Compare commits

..
70 Commits
Author SHA1 Message Date
randy efddc55ad0 refactor: enhance Overpass API handling with multiple endpoints and improved error reporting
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
2026-10-09 22:07:45 +02:00
randy 33be95ef59 Send a User-Agent to Overpass, overpass-api.de 406s Ruby's default
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 13s
The Camper POIs plugin died with "plugin exited with status 1": Net::HTTP's
bare "Ruby" User-Agent now gets a 406 Not Acceptable from overpass-api.de,
any identifying one goes through.
2026-10-09 21:51:24 +02:00
randy ede21454a2 Draw offline hiking trails sharing a path as parallel lanes
CI / check (push) Successful in 22s
CI / deploy (push) Successful in 12s
Several marked routes running down one path drew as a single line, only
the topmost color visible. maplibre-gl-lanes puts each route in its own
lane; OSM relations already share exact vertices, which is all it needs.
One LaneLayer across every picked country; click hit-testing and the
highlight move to queryLane/setHighlight.
2026-10-09 21:46:05 +02:00
randy 9c80a20f3e compose: enable IPv6 on default network (fixes broken container DNS on brick)
CI / check (push) Successful in 38s
CI / deploy (push) Successful in 12s
2026-10-08 12:28:05 +02:00
randy 43785a074b Note the NPS arrowhead logo's official Pantone/RAL colors
CI / check (push) Successful in 38s
CI / deploy (push) Successful in 12s
Unrelated to anything the app renders, but the doc already collects
NPS-ecosystem color facts and this came up while sourcing colors
for a 3D print of the shield.
2026-09-21 18:08:35 +02:00
randy b0c93d10d7 Detect a LiveTrack session ending the moment Garmin confirms it, not 30min later
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 12s
Watched the session from the last two commits actually get saved:
Garmin updated its `end` field from the future 24h placeholder to
within a second of the last point's own time, immediately — much
faster than the recency-of-last-point fallback alone would have
noticed (that only flips after ACTIVE_WITHIN with no new points, so a
just-saved session with a last point seconds old would still read as
active for up to 30 more minutes).

Now checks end-already-in-the-past first (authoritative and near
instant when Garmin has set it), falling back to point recency only
when end still shows the future placeholder — the case an abandoned,
never-saved session never updates.
2026-09-20 16:55:57 +02:00
randy 3106f8c103 Draw a LiveTrack session's planned course alongside the actual track
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 13s
Garmin's own LiveTrack page already shows both when the activity was
started from a saved course — the session payload carries the course's
geoPoints right alongside the track points (LiveTrackClient found it
nested under the same React Query cache the session/points come from).

Rendered as a second, dashed LineString distinct from the actual
track's own line ("kind": "actual" vs "planned" on each GeoJSON
feature) — muted gray rather than matching Garmin's own purple-for-
planned, since this app's actual track is already purple and reusing
it would make the two indistinguishable.
2026-09-20 16:54:24 +02:00
randy 8eada07869 Drop subject:LiveTrack from the Apps Script's Gmail search, it finds nothing
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 12s
Documented as matching regardless of language, on the assumption
Gmail's subject: operator does a substring match — empirically false
against a real, currently-arriving LiveTrack email whose subject does
contain the word ("Sledujte LiveTrack používateľa ...", confirmed
2026-09-20): with the filter, the search returned zero threads;
removing it and rerunning found the email immediately. Left out
entirely rather than replaced — from:noreply@garmin.com plus the
session-link regex already narrows this down to real LiveTrack emails
with something usable in them.
2026-09-20 16:44:20 +02:00
randy 5d86cb1d04 Detect a live LiveTrack session from its last point, not session["end"]
CI / check (push) Successful in 40s
CI / deploy (push) Successful in 13s
session["end"].nil? was the active signal since this was first built,
on the assumption Garmin leaves it unset until the activity actually
stops. Checked directly against a real still-recording session today
(2026-09-20): end was already non-nil, set to exactly start + 24h — the
share link's own expiry, not an end time — so every currently-active
session was showing as "Ended". A genuinely ended session's end does
hold its real end time (confirmed against the 2026-09-06 session this
was originally, and only, verified against), which is what made the
bug easy to miss.

Recency of the last track point is a directly-observable signal
instead of reverse-engineering what a given `end` value means: Garmin
posts one roughly every postTrackPointFrequency seconds (~15, observed)
while genuinely still recording.
2026-09-20 16:42:52 +02:00
randy b7618edc25 Move the session-skip from a before filter to real Rack middleware
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
The before filter added in the previous commit never ran for static
assets: Sinatra serves public_folder files (every JS/CSS/font/vendor
file) through its own built-in shortcut, bypassing route and filter
dispatch entirely — confirmed live, curl still got a fresh set-cookie
on /js/map.js after that commit deployed, and the user hit the exact
same logout on refresh again. A HAR from that retest shows the same
clobbering pattern as before, just via a different response each time.

SkipSessionWrite is a plain Rack middleware instead, positioned (via
`use`, after `set :sessions`) inside Rack::Session::Cookie's own
wrapping — it sees the same env static dispatch does, so every response
is covered, not just Sinatra-routed ones.
2026-09-16 18:03:28 +02:00
randy bd46b72144 Skip re-writing the session cookie on every response, not just the three that change it
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
Rack::Session::Cookie re-serializes and re-sets the cookie on every
response that even reads session — Rack::Protection's CSRF check does,
on nearly every request — reflecting whatever cookie that particular
request happened to arrive with, whether or not anything actually
changed. A HAR capture caught the real failure mode: seconds after a
successful login, offline_data.js's _checkUpdate (an unrelated
background poll fired the instant the page loads) was still carrying
the browser's pre-login cookie — a client-side timing race across the
OAuth redirect chain, not something this server controls — and its own
re-issued Set-Cookie landed after the login's, silently overwriting
session[:user] with nothing logged anywhere to explain it.

Only /login, /auth/callback and /logout ever actually write to
session. A before filter now skips the write everywhere else, removing
every other response from that race — a stale request can still read a
stale cookie, but it can no longer clobber a fresher one.

Also drops the earlier except: [:session_hijacking] from the previous
commit: rack-protection 4.2.1 only enables SessionHijacking when
explicitly opted into via :use, so Sinatra's default setup never turned
it on in the first place — the exclusion was a no-op, not the fix.
2026-09-16 17:56:44 +02:00
randy 6126b5055b Disable Rack::Protection::SessionHijacking, it silently logs everyone out
CI / check (push) Successful in 37s
CI / deploy (push) Successful in 19s
Sinatra turns this on by default once sessions are enabled: it pins a
session to the User-Agent header seen on whichever request first wrote
to it, and empties the session — no exception, nothing logged — on any
later request where that header doesn't match. That's exactly what
production hit behind its reverse proxy: OAuth login set session[:user]
fine, then the very next page load (even a plain refresh) came back
logged out with nothing in the logs to explain why.

Its own docs concede every header it checks is spoofable anyway ("will
not prevent determined hijacking attempts"), so losing it is a minor
tradeoff for a login that actually stays logged in. Every other
Rack::Protection check stays on.
2026-09-16 16:11:01 +02:00
randy 9edc2d9534 Show clicked hiking trail info in a docked panel, not a popup
CI / check (push) Successful in 22s
CI / deploy (push) Successful in 12s
A map-anchored Popup on a trail click routinely overlapped the very
line it described. Follows the same click -> docked panel pattern
gpx.js already uses for a track line click (TrackProfilePanel) rather
than inventing a new one: a fixed #trail-panel docks to the right edge
on desktop and the bottom edge on narrow viewports (this file's first
@media breakpoint), so the trail stays fully visible either way.

routeHtml()'s own markup is reused unchanged for the panel body —
.trail-panel__body's padding matches .maplibregl-popup-content's own
(15px 10px) so the header's canceling negative margin still lines up.
The popup-identity staleness guard for the async Overpass lookup is
replaced with a plain request token, since there's no longer a fresh
Popup instance to key off of.
2026-09-15 12:52:54 +02:00
randy eada464fb3 Highlight trails in their own color instead of recoloring to yellow
CI / check (push) Successful in 22s
CI / deploy (push) Successful in 12s
2026-09-13 20:17:48 +02:00
randy ca7772dd72 Highlight the clicked trail(s) on the map, both online and offline
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
The real app had no visual feedback for which line a click actually
matched, unlike the earlier SVG prototype's hover highlight — just a
popup. Draws the matched route's real geometry (from the offline
vector feature, or reassembled from the online Overpass response's own
member-way geometry) as a separate yellow highlight layer, cleared
when the popup closes.

Guarded against a stale popup's closeOnClick "close" event clearing a
highlight that a newer click already set in the same event dispatch —
tracks which popup is currently "active" and only clears on its own
close, not a predecessor's delayed one.
2026-09-13 19:28:51 +02:00
randy 4c95927231 Pin check/renovate back to brick-build now that its image has Node
CI / check (push) Successful in 38s
CI / deploy (push) Successful in 12s
The runner's label-to-image mapping got repointed at Gitea's own
official runner image (docker.gitea.com/runner-images:ubuntu-latest,
has Node out of the box) — the earlier opensuse/leap:16.0 mapping for
brick-build specifically was what broke check/renovate on 2026-09-12,
not brick-build the label itself. Back to pinning both to this
machine specifically instead of whichever runner answers to the
shared ubuntu-latest label.
2026-09-13 12:32:14 +02:00
randy c6e4a455b6 Split offline hiking trails per country, add a picker instead of one big file
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 11s
data/hiking_trails.geojson is now data/hiking_trails/<CODE>.geojson —
one file per country instead of a merge, generated per-source
(PmtilesBuilder.extract_hiking_trails), cached the same way
geofabrik_cache is (skipped once a country's own extract is already
newer than its source .osm.pbf, so growing the selection by one
country only re-extracts that one). GET /api/hiking-trails now lists
available country codes instead of a single file's status.

The Overlays panel's "Hiking trails" item becomes an expandable
per-country checklist once any offline extract exists (same shape as
Radar's own expand), replacing the plain on/off checkbox — nobody
covering a dozen countries' worth of offline map data needs all of
their hiking trails loaded and parsed at once, so each ticked country
fetches its own GeoJSON independently instead.

HikingTrails.extract_merged (server-side multi-country merge, no
longer needed now that the client picks per country) removed again.
Verified the new extract_hiking_trails + caching skip live on brick
against a real cached country PBF.
2026-09-13 11:17:05 +02:00
randy 079ac37787 Wire offline hiking trails into the real build, API, and renderer
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
data/hiking_trails.geojson now generates automatically as the last
step of every offline.pmtiles build (PmtilesBuilder.extract_hiking_trails,
best-effort — a failure here doesn't fail the whole build), merged
across all selected countries with cross-border dedup by OSM relation
id (HikingTrails.extract_merged). Served at GET /tiles/hiking_trails.geojson
and GET /api/hiking-trails, fetched client-side only once the overlay
is actually toggled on.

hiking_trails.js now prefers this local file whenever it exists,
regardless of which base map (online/offline) is active — same
"prefer local when available" story as HillshadeControl already uses
for terrain. Offline click hit-tests the real vector layer directly
(queryRenderedFeatures) instead of querying Overpass — instant, no
network. Coordinates are rounded to 5dp to keep the combined file
smaller; colour tag values are normalized against a known-safe set
before ever reaching a style paint property.

Verified extract_merged (new: combines multiple countries' extracts,
dedupes overlapping relations) against two real cached country PBFs
on brick directly, not just fixtures.
2026-09-13 10:31:59 +02:00
randy b35ace9734 Fix hiking trails extraction: add-locations-to-ways was missing
Verified live on brick against real cached extracts: a plain Geofabrik
PBF's ways only carry bare node references, not coordinates, so
locations_on_ways on the final `cat -f opl` had nothing to embed —
every way came out as e.g. n123xy, empty x/y, and every relation got
dropped for having no usable geometry (0 routes for Macedonia).

osmium add-locations-to-ways between the tags-filter and cat steps
resolves those references first. Re-verified after the fix: 45 routes
for Macedonia, 3009 for Slovakia, with real colour/ref/operator/
distance (326 red, 782 yellow, 510 blue, 902 green KST-style routes).
2026-09-13 09:26:22 +02:00
randy 714795d52b Prototype: extract offline hiking trails from the Geofabrik PBF we already have
CI / check (push) Successful in 43s
CI / deploy (push) Successful in 13s
No custom Planetiler profile needed after all — Maptool::HikingTrails
runs osmium tags-filter r/route=hiking against the same .osm.pbf
PmtilesBuilder already downloads per country for offline.pmtiles, then
osmium cat -f opl (locations_on_ways=true) to get member-way geometry
back as plain text, no separate node-id lookup table to build.

No general OSM tool stitches a route relation into one line (routes
are a semantic grouping, not a geometry type) — parse_opl does that
walk itself, same as the Overpass-based click handler already does
client-side. Kept pure/testable apart from the two osmium subprocess
calls, mirroring OverpassPois.fetch vs .to_geojson.

rake hiking:trails[pbf,out] to try it by hand against a real cached
extract. Not wired into the actual offline build or rendering yet —
still just a prototype, per TODO.adoc.
2026-09-13 09:18:50 +02:00
randy cc3d8ca5f3 Race 3 Overpass mirrors for speed, show more per-route detail
CI / check (push) Successful in 2m18s
CI / deploy (push) Successful in 11s
The single shared overpass-api.de instance varies wildly in load —
Promise.any against three independent public mirrors (also CORS-open,
verified) means only the slowest one determines wait time, not
whichever happens to be hit.

Also show the description tag (falls back to it for the title when a
route has no name, e.g. many KST route relations), the ref number, and
a "View on OpenStreetMap" link — the popup was skipping data that was
already in the response for free.
2026-09-13 08:53:48 +02:00
randy 6f1577f957 Click a hiking trail to see its name, color, network and distance
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
The Waymarked Trails raster overlay has no queryable features (just
pixels), so this answers "what trail is this" with a one-off Overpass
query on click instead: way(around:40m) finds candidate ways, then
rel(bw) walks backward to their parent route=hiking relations. Overpass
has permissive CORS, so this runs straight from the browser, no server
proxy needed — verified against a real KST route in the Tatras.

Route length isn't tagged directly, so it's summed from each member
way's own geometry (measure.js's existing haversine lineLength, now
exported and reused rather than duplicated). The colour tag is only
ever used as a CSS background after matching a strict hex/word regex —
untrusted free text otherwise.
2026-09-12 23:32:56 +02:00
randy 9c45e7275c Add a Hiking trails overlay (Waymarked Trails, online only)
CI / check (push) Successful in 2m16s
CI / deploy (push) Successful in 11s
New Overlays panel toggle, same plain on/off shape as Contour lines —
raster tiles from waymarkedtrails.org, which already color each route
by its OSM colour tag (the KST/KČT paint-blaze colors used in
Slovakia/Czechia, plain red elsewhere), so no styling work needed here.

Online only for now; offline (baked into offline.pmtiles) needs a
custom Planetiler profile for route relations, deferred until there's
a route planner/router to pair it with.
2026-09-12 22:36:10 +02:00
randy c894706140 Revert check/renovate to ubuntu-latest — brick-build's image has no Node
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 12s
brick-build's job container (opensuse/leap:16.0) has no Node.js, and
every step in check/renovate (checkout, setup-ruby, setup-node) is a
JS-based Action that needs it just to start — both runs since the
brick-build switch failed immediately, and deploy never ran because it
depends on check passing. ubuntu-latest is shared with "Gitea Runner on
Cube" (not pinned to this specific machine), but that's fine: only
deploy (a unique label held by just brick's native runner) actually
needs to land on a specific box.
2026-09-12 21:39:21 +02:00
randy 21988c61da Lower offline terrain to z13, not z15 — raster DEM storage explodes with zoom
CI / check (push) Failing after 4s
CI / deploy (push) Skipped
The 12-country selection's terrain.pmtiles was headed well past the
181GB an earlier 5-country build already produced at z15. Raster DEM
tiles don't compress away like vector ones (every tile is a real
image, even over empty terrain), so each extra zoom level roughly
quadruples storage. z13 is each Mapterhorn regional chunk's own native
minzoom — one raster level per chunk instead of a 15-13=2-level
pyramid on top — still enough for the hillshade toggle and the planned
slope readout. Online hillshade keeps its own z15 cap; this only
affects the offline copy.
2026-09-12 20:18:36 +02:00
randy f18e5c19d3 Truncate noisy build-failure output, fix the recurring terrain death
CI / check (push) Failing after 4s
CI / deploy (push) Skipped
The web UI was showing a raw ~500k-character stdout/stderr dump on a
failed build — go-pmtiles' extract logs one progress line per chunk,
which is fine on a terminal (it overwrites itself) but not captured
non-interactively. command_output now keeps just the tail, and the
status line renders it with preserved line breaks in a scrollable box
instead of one unreadable blob.

Root cause of the actual failure (terrain build dying ~11 minutes in
with an HTTP/2 stream reset): go-pmtiles' default --overfetch=0.05
merged this multi-country planet.pmtiles extract into a single ~60GB
request with no way to resume after a dropped stream. --overfetch=0
splits it into many smaller requests instead.
2026-09-12 19:41:45 +02:00
randy 237e5b2e54 Move check/renovate onto brick-build, deploy stays brick-host
Renovate no longer needs Docker access for a one-shot docker-run job:
switched to setup-node + npm install -g renovate (matching labelprint's
workflow), so it fits the same brick-build runner as check instead of
the stale "deploy" label. Also note down the build-log readability/API
gaps flagged from a real failed terrain build.
2026-09-12 19:36:08 +02:00
randy 3f9c46e846 Skip rebuilding offline/terrain data when nothing actually changed
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 12s
2026-09-12 13:32:02 +02:00
randy 17d1adedbc Guard syncTerrain against running before the style has loaded
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 12s
2026-09-12 13:27:53 +02:00
randy cd02b03502 Retry pmtiles extract/merge on failure, not just on the first try
CI / check (push) Successful in 39s
CI / deploy (push) Successful in 12s
2026-09-12 12:50:09 +02:00
randy 5c3151c2f5 Add a 3D pitch toggle, with raised terrain following it 2026-09-12 12:50:09 +02:00
randy f5eb9a4a8d Tell Planetiler explicitly how many CPUs it has
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 12s
Its own autodetection was leaving most of a 16-core host idle
(observed in production) — likely a container cgroup/JVM detection
quirk. Etc.nprocessors asks the OS directly instead.
2026-09-12 01:34:29 +02:00
randy f09b5de6c2 Relabel data bind mounts for SELinux (:Z)
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 12s
App crashed on startup on brick (Permission denied @ dir_s_mkdir -
/app/data/gpx) — brick has SELinux enforcing, unlike cube, and Podman
denies a container write access to a bind-mounted host directory
without a relabel regardless of Unix permissions. Applied to both
maptool's ./data mount and kiwix's ZIM mount (same underlying cause,
would have hit this too once a real ZIM is placed there).
2026-09-12 01:27:23 +02:00
randy 0080ac6170 Deploy to brick-host instead of cube-host
CI / check (push) Successful in 19s
CI / deploy (push) Failing after 5m1s
cube.local and brick.local swapped IPs and brick is now the deploy
target — rename the native runner label everywhere it's referenced.
2026-09-12 01:15:09 +02:00
randy 78362d51f2 Show what's actually cached per-country, with a delete button per row
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 13s
The only visibility into downloaded data was the whole offline.pmtiles
row, and the only delete was "everything" (whole file + whole cache).
GET /api/offline-tiles/cache lists what's really in
data/geofabrik_cache/ right now — distinct from offline.pmtiles' own
meta.sources, which only reflects the last successful build and can
disagree with both the current selection and the cache (ticked-but-
not-Applied, or built-from-but-since-unticked).

The region picker now shows each cached country's size next to its
checkbox, with its own delete button (DELETE
/api/offline-tiles/cache/:code, already added but unused by the UI
until now) — removing just that entry rather than clearing the whole
cache.
2026-09-11 23:39:43 +02:00
randy d09d953cbf Pass --download to Planetiler for its auxiliary OpenMapTiles sources
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 13s
Real build failure once the tmp-filename fix got past the format-
detection error: "data/sources/lake_centerline.shp.zip does not exist.
Run with --download to fetch it". The OpenMapTiles profile needs a few
small non-OSM sources (water polygons, Natural Earth, lake
centerlines) alongside the .osm.pbf itself, and Planetiler only
fetches them when told to. They land under its own default
data/sources/, gitignored alongside the rest of data/ — fetched once,
reused by every later build.
2026-09-11 23:28:07 +02:00
randy b54020e23b Fix Planetiler tmp-file format detection, add build progress and delete
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 13s
Root cause of "planetiler exited with status 1: ... Unsupported format
tmp" (and likely the earlier unexplained "pmtiles exited with status
1"): Planetiler/go-pmtiles pick their output archive format from the
file extension, and the atomic-rename tmp files were named
"offline.pmtiles.tmp"/"terrain.pmtiles.tmp" — ".tmp" as the extension,
not ".pmtiles". Renamed to "offline.tmp.pmtiles"/"terrain.tmp.pmtiles".

Also, since build-status only ever said "running since X" with no way
to tell what it was actually doing:
- GET .../build-status now includes a `step` string ("fetching PL
  (3/4) — done: SK, CZ — pending: HU", "tiling with planetiler", ...)
  while a build is in progress.
- DELETE /api/offline-tiles / /api/terrain-tiles clear a built file,
  its sidecar meta, and (for offline) the whole per-country download
  cache — a manual "reclaim space" / "start over" action the region
  picker's Apply never did on its own. DELETE
  /api/offline-tiles/cache/:code clears just one country's cached
  extract. All three are 409 while that kind's build is running, and
  surfaced as a small "Delete" button on each row in the Offline data
  panel.
2026-09-11 23:23:46 +02:00
randy 2878270187 Include stdout in run_*! build error messages, not just stderr
Real cause of "pmtiles exited with status 1:" with nothing after the
colon: go-pmtiles (like some other CLIs) can write its actual error to
stdout, which was being silently discarded. Applies the same fix to
osmium/planetiler for consistency.
2026-09-11 23:07:49 +02:00
randy 007dee7341 Cache per-country Geofabrik extracts so Apply doesn't redownload everything
Every region-selection change re-downloaded every selected country's
.osm.pbf from scratch, even ones already fetched by a prior build —
painful for multi-GB countries. Extracts now persist in
data/geofabrik_cache/, keyed by country, and are only re-fetched when
Geofabrik's Last-Modified for that country actually moved on (checked
with a cheap HEAD) or the file isn't cached yet.
2026-09-11 22:52:16 +02:00
randy 077b44ece1 Note OpenTopoMap-style forest/meadow fill patterns as a TODO idea
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 2m54s
2026-09-11 22:13:30 +02:00
randy 619f2293df Move Docker base image to Debian trixie for Java 21
Planetiler 0.10.2 needs Java 21+; bookworm's default-jre-headless (and
its backports) only goes up to 17, causing a real build to fail with
UnsupportedClassVersionError. trixie's default-jre-headless is Java 21.
2026-09-11 22:13:30 +02:00
randy a84e6ae22f Follow Geofabrik's 302 redirects on extract download and Last-Modified check
CI / check (push) Successful in 29s
CI / deploy (push) Successful in 16s
Geofabrik redirects europe/<slug>-latest.osm.pbf to a dated filename
(confirmed live: .../europe/austria-latest.osm.pbf -> .../europe/austria-
260910.osm.pbf) — a real build hit this immediately. Net::HTTP doesn't
follow redirects on its own; both the streaming download and the HEAD-based
update-check now do, up to a small limit.
2026-09-11 20:57:22 +02:00
randy 920a1d2d36 Stream Geofabrik extract downloads instead of buffering in memory
Net::HTTP's plain #get loads the whole response body before returning —
fine for the small JSON manifests elsewhere in this file, but a real risk
for a multi-GB country .osm.pbf (Germany, Russia, ...). Streams to disk
via read_body instead.
2026-09-11 20:56:06 +02:00
randy d08ed41185 Replace offline basemap pipeline with Planetiler, unify online/offline styling
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 2m43s
Offline tiles were built from Protomaps' own basemap schema, needing a
completely separate hand-tuned theme (protomaps-themes-base + OFFLINE_THEME)
from the online Liberty style's ONLINE_STYLE_PATCH — the two could never be
made to match exactly no matter how much either side got tuned (today's
session found and fixed several mismatches: building opacity, park
boundaries, road casings, road widths).

Offline.pmtiles is now built with Planetiler's default OpenMapTiles profile
against Geofabrik per-country OSM extracts (osmium-merged when more than one
country is selected), the same vector schema Liberty already expects.
offlineStyle() now clones the same Liberty JSON (vendored as a local
snapshot for reliability with no internet) and applies the identical
ONLINE_STYLE_PATCH, just repointed at local PMTiles/glyphs/sprite — one
palette instead of two, correct on both sides by construction.

Also vendors Liberty's icon sprite for the first time (offline had none
before), adds Java/osmium-tool/planetiler.jar to the Docker image, and
removes the now-unused protomaps-themes-base dependency and the
theme-comparison debug page that found today's bugs.
2026-09-11 20:52:11 +02:00
randy 0874e8f8ed Add a debug page for comparing offline base-map themes side by side
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 13s
Renders the bare protomaps-themes-base themes next to the app's actual
offline/online styles (each real app instance, embedded), with pan/zoom
kept in sync via the existing URL-hash mechanism, to make it easy to see
where the hand-tuned offline palette diverges from the online one.
2026-09-11 16:58:32 +02:00
randy 9223410b40 Swap flag emoji for real (square, bordered) SVG flags
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 16s
Emoji flags are font glyphs, not shapes — no way to resize, border, or
tone them down to fit this app's controlled, hand-drawn-icon look
(same reasoning the topbar readout already settled earlier this
session). Vendored flag-icons' pre-cropped square SVGs (MIT) instead,
framed in a small rounded-square badge — the same convention
icons.js's MAP_MARKER_ICON already uses for framing a glyph, not a new
visual language.
2026-09-11 13:31:36 +02:00
randy 4a3862189f Track what was actually built, and flag when it's stale
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 16s
The .pmtiles files themselves carry no record of what upstream
snapshot they came from — only a local mtime. Each successful build
now writes a small sidecar (offline.pmtiles.meta.json: the Protomaps
build date used; terrain.pmtiles.meta.json: the Mapterhorn source
names/md5sums) recording that, surfaced in the panel next to the
existing size/updated line.

New GET .../check-update routes compare the recorded build against
the live upstream index/manifest — true/false/null, null meaning
"don't know" (no local build yet, or the check itself failed) rather
than a false "no". Kept separate from the plain status endpoints
(which map.js's own topbar dot hits on every page load) since this
one reaches an external service; only called once when the Offline
data panel is actually opened.

Also: flag emoji next to each country in the picker (derived from the
ISO code via Unicode regional indicators, no image assets needed).
2026-09-11 13:18:32 +02:00
randy 53c9a82af2 Add a country-based region picker for offline data
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 12s
Replaces "regenerate for the current viewport" with what Locus/OsmAnd-
style apps give you: tick which countries you want offline (a Natural
Earth-derived checklist, public domain, vendored), Apply rebuilds
offline.pmtiles/terrain.pmtiles to cover the whole selection at once.
Multiple regions can be offline simultaneously (e.g. Slovakia + Austria
for a trip that crosses the border) — the selection itself is the
source of truth (data/offline_regions.json), not a set of per-country
files, so "remove a country" and "refresh a country" are both just
"change the selection, then Apply."

Confirmed against go-pmtiles' own source (not just its docs) before
committing to this shape: `extract --region=<geojson>` treats a
FeatureCollection's features as their union already (no dissolve/union
math needed on our side — pmtiles/region.go's UnmarshalRegion just
concatenates every feature's rings), and `merge` hard-errors on any
overlapping tile id between inputs (merge.go's prepareInputs). That
second fact rules out "extract each country separately, merge them
all" — adjacent countries' region-extracts would share border tiles,
since extraction copies whole tiles rather than clipping vector
content to the polygon. The actual fix: extract per *source archive*
(the Protomaps build, each intersecting Mapterhorn chunk), each call
covering the whole current selection via one --region= file — the
terrain pipeline already extracted-then-merged per source this way, it
just needed a multi-country region instead of a single bbox.
2026-09-11 12:55:56 +02:00
randy f89dea5805 Build terrain.pmtiles from Mapterhorn instead of fetching tiles ourselves
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 1m52s
Mapterhorn (mapterhorn.com, Protomaps-affiliated) publishes pre-built
Terrarium-encoded terrain as PMTiles: a global planet.pmtiles (z0-12)
plus ~450 regional chunks (z13+). Same pmtiles extract --bbox=...
mechanism offline.pmtiles already uses, just against their files —
confirmed live against their real manifest and the documented
Interlaken example. Replaces fetching every individual tile ourselves
(even after this session's earlier concurrency fix, still tens of
minutes for a real region) with 1-3 extract calls plus a merge when
the bbox spans more than one chunk: seconds, not minutes.

Removes CONCURRENT_TILE_FETCHES, write_terrarium_mbtiles, tile_range,
lng/lat_to_tile_x/y, fetch_terrarium_tile, and the sqlite3 gem
entirely — none of it is needed once the tiles arrive pre-packaged.

dem.js's point-elevation lookup (shadow.js's terrain-shadow overlay)
hardcoded a 256px PNG tile assumption that Mapterhorn's 512px WebP
tiles would have silently broken — now reads the actually-decoded
tile's own size and mime type instead of assuming either.
2026-09-11 12:26:03 +02:00
randy d041c21270 Fetch terrain tiles concurrently with persistent connections
CI / check (push) Successful in 20s
CI / deploy (push) Successful in 13s
Serial fetches (no connection reuse) measured ~650ms/tile, almost all
TCP+TLS handshake — a real bbox at z0..15 is tens of thousands of
tiles, so this was hours for a modest region. Now: 16 worker threads
share a queue (I/O-bound, so the GVL doesn't get in the way), each
keeping its own persistent connection across every tile it fetches.
Measured live against the real Terrarium source: ~40ms/tile, ~16x
faster.

write_terrarium_mbtiles's SQLite writes and progress updates stay on
one thread (draining a results Queue the workers feed), so nothing
needed its own locking beyond the two Queues themselves.
2026-09-11 12:04:13 +02:00
randy bbe9f38bda Show build progress instead of a static "Building…"
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 13s
terrain.pmtiles can take minutes (tiles fetched one at a time) with no
way to tell a slow build from a stuck one. Now reports tiles_done/
tiles_total into the same status Hash the frontend already polls, and
the panel shows elapsed time (both files) plus tile counts (terrain
only) so the numbers visibly move every ~3s poll instead of sitting on
one static string.
2026-09-11 11:57:43 +02:00
randy d72f8a3c94 Bundle go-pmtiles into the Docker image
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 2m23s
The "Regenerate for current view" build was failing in production with
"could not run 'pmtiles'" — go-pmtiles was never actually installed
anywhere, just documented as something a developer runs by hand
locally (docs/development.adoc). Installs the static Linux binary from
go-pmtiles' own GitHub releases, architecture-matched via dpkg rather
than hardcoded amd64.

Verified the exact download/extract/exec sequence end-to-end outside
Docker (no daemon available in this sandbox to run a real build).
2026-09-11 11:47:31 +02:00
randy cd56ebe0a1 Add in-app regeneration of offline.pmtiles and terrain.pmtiles
CI / check (push) Successful in 20s
CI / deploy (push) Successful in 1m52s
A logged-in user can now trigger, from a new "Offline data" sidebar
panel, what was previously a fully manual go-pmtiles workflow
(docs/development.adoc): regenerate either file for the current map
viewport, no area-picker (same "visible area is the area" convention
the Overpass POI plugin already uses).

- lib/maptool/pmtiles_builder.rb: two independent background jobs
  (Thread + Mutex-guarded status Hash, same shape as app.rb's own GNSS
  poller), each writing to a .tmp path and atomically renaming into
  place so a build never serves a partially-written file mid-run.
  offline.pmtiles is a single `pmtiles extract` against Protomaps'
  public daily basemap build (byte-range extraction, not a full
  ~120GB download). terrain.pmtiles has no equivalent public
  Terrarium-encoded archive to extract from, so it fetches individual
  Terrarium tiles itself, packs them into an intermediate MBTiles
  (new sqlite3 gem), then the same `pmtiles convert`.
- 4 new app.rb routes (POST .../build, GET .../build-status) plus
  size/mtime added to the existing availability endpoints.
- public/js/offline_data.js: new sidebar panel, polling build-status
  the same self-rescheduling-setTimeout shape livetrack.js already
  uses for its own refresh loop.
- Both network-heavy pieces (the Protomaps build index, per-tile
  fetches) are injectable, so test/test_pmtiles_builder.rb exercises
  the actual build pipeline against a fake `pmtiles` binary without
  hitting a real network, matching this suite's existing
  keep-it-offline convention for external services.
2026-09-11 01:25:08 +02:00
randy fba8ca10a2 Add contour lines to the Overlays panel via maplibre-contour
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 12s
5th Overlays item, generated client-side from the same online Terrarium
DEM tiles Terrain shading already fetches (no offline fallback — the
plugin's DemSource has no public hook to route through the pmtiles://
protocol the offline basemap uses, so this quietly renders nothing
without a network, same as Radar/the Wikipedia layer).

A plain vector source/layer under MapLibre's own tile lifecycle, no
manual refetch-on-move code needed. Thresholds are the plugin's own
generic starting point, not yet tuned against real terrain. Labels
skipped for now (font-name matching across the online/offline styles'
own glyph sets adds real risk for a nice-to-have, not the actual ask).
2026-09-11 00:12:32 +02:00
randy b4a1c6c0cc Add a search box, matching NPS's Park Tiles viewer
CI / check (push) Successful in 19s
CI / deploy (push) Successful in 13s
Top-left map control (topmost, above Home/zoom) with a text input:
"lat, lng" is parsed and jumped to directly (same plain-text shape the
topbar's coordinate readout already copies to the clipboard), anything
else is geocoded via Nominatim — OSM's own geocoder, free, no API key,
matching the online basemap's own OSM-derived style. Online only, same
story as this app's other non-basemap online features (Radar, the
Wikipedia layer) — no offline geocoding index to fall back to.
2026-09-10 23:59:35 +02:00
randy 23a84ac185 Bump the topbar wordmark to 20px
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 12s
2026-09-10 23:53:32 +02:00
randy c8b6dd2fb1 Use the National Park typeface for the topbar wordmark
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 12s
Self-hosted Bold woff2 only (the one weight .topbar__brand renders at)
under public/vendor/national-park-typeface/ — a router-carved-signage
display face for the "MAPTOOL" heading, matching the uppercase/
letter-spaced treatment it already had. Everything else stays on the
system font stack; this is a wordmark swap, not a body-text one.

docs/nps-resources.adoc/third-party-notices.adoc updated to record it.
2026-09-10 23:50:19 +02:00
randy a5adb39770 Add monoline icons to the topbar coordinates/sun readout
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 13s
WAYPOINT_ICON (coords) and PENDING_ICON (countdown) reused as-is; two new
hand-drawn SUNRISE_ICON/SUNSET_ICON since NPS's symbol set has no match —
same currentColor stroke style as the rest of icons.js, so they render
monochrome in the dark topbar rather than colored emoji.

sun.js's formatSunInfo now returns {sunrise, sunset, countdown} instead of
one joined string, so map.js can place its own icon in front of each
piece instead of the module owning any markup.
2026-09-10 22:54:18 +02:00
randy b5466b53e4 Widen the side panels so the hourly forecast strip fits
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 12s
260px -> 300px on the shared .basemap-panel class (Overlays/GPX/Livetrack
all use it) — the next-8-hours row was getting clipped.
2026-09-10 22:47:35 +02:00
randy 6911e67c81 Add a next-8-hours strip to the Radar panel's forecast
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 13s
Same Open-Meteo call and cache as the daily outlook, just with an hourly
field added — a rolling window from now rather than "rest of today", so
it doesn't shrink to nothing right before midnight.
2026-09-10 22:39:51 +02:00
randy 254e9b4c61 Add a scale bar to the map
CI / check (push) Successful in 53s
CI / deploy (push) Successful in 12s
MapLibre's own ScaleControl, bottom-left (attribution already owns
bottom-right) — no custom NPS-style bar needed, the built-in one covers it.
2026-09-10 22:14:40 +02:00
randy c9561a5489 Add an offline terrain shadow overlay to the Overlays panel
Shades the visible map by whether each point is currently blocked from
the sun by surrounding terrain (shademap.app-style), computed fully
offline via the same Terrarium DEM tiles the Terrain shading layer
already renders, plus this session's sun-position math and Radar's
checkbox+expandable UI shape.

dem.js pulls elevation-at-a-point out on its own since TODO.adoc's
slope readout and solar calculator will need the same lookup.
2026-09-10 21:43:51 +02:00
randy a8d58abaca Move Radar and Terrain shading into the Overlays panel
Both used to be their own separate sidebar icons; neither has
anything to manage beyond on/off, same as Wikipedia already got its
own Overlays entry for. Terrain shading is a plain checkbox item now.
Radar keeps its scrub/forecast controls, but behind an explicit
expand (clicking its name), separate from the checkbox — checking it
on shouldn't force the details open, and expanding to scrub/check the
forecast shouldn't also flip the layer on.

Both controls now build their own <li> (RadarControl.mount(li),
HillshadeControl.mount(li) — the latter moved into overlays.js
entirely, it has no reason to live in map.js once it's not building
its own sidebar icon) rather than a whole sidebar-item + flyout the
way they used to.

Also gave each Overlays item a leading icon: Wikipedia and Terrain
shading reuse existing NPS pictograms (WIKI_ICON, and WILDERNESS_ICON
newly exported from icons.js's forest entry — NPS has no dedicated
mountain/terrain glyph, wilderness-22.svg is the closest honest
match). Radar's is hand-drawn (a rain cloud) since NPS's set has
nothing weather-related to reuse.
2026-09-10 21:23:51 +02:00
randy dce5fd87da Add a Locus Pro-style toolbar readout: DMS coords + sunrise/sunset
The topbar's coordinate display was plain decimal degrees ("48.14611,
17.17722") — switched to DMS (48°08'46"N 17°10'38"E), the more
GPS-unit-like format Locus Pro itself uses, and made it click-to-copy
(plain decimal, since that's what pastes cleanly into another map/GPS
app — not the DMS text actually shown).

Added sunrise/sunset + a countdown to whichever's next, for the same
map-center point the coordinates already describe. Vendored SunCalc
(BSD-2-Clause, github.com/mourner/suncalc) rather than hand-rolling
the astronomical math — small, dependency-free, and correctness here
has real pitfalls (equation of time, refraction, polar day/night) a
first-pass reimplementation would risk getting subtly wrong.

Also added a test asserting every public/js/*.js module has a
versioned importmap entry in layout.erb — caught that overlays.js
(from the previous commit) was missing one, which would have quietly
skipped ASSET_VERSION's cache-busting for that file after every
deploy.
2026-09-10 21:08:02 +02:00
randy c5b4a9ffb2 Move the Wikipedia layer into its own "Overlays" panel
Wikipedia doesn't belong inside "Tracks & Points" — that panel is for
files you add/remove/reorder, while Wikipedia is a live layer with
nothing to manage, just on/off. Gave it its own sidebar panel
(#overlays-panel, same shell as LiveTrackPanelControl) instead, sized
to hold more such checklist-only layers later without a redesign.

Moved all the wiki layer logic out of GpxPanelControl into a new
OverlaysControl (public/js/overlays.js). svgToImageData/badgeImageId/
escapeHtml are now exported from gpx.js so it doesn't have to
reimplement them — the same three helpers a second overlay would also
need.
2026-09-10 20:54:40 +02:00
randy 787df5478d Replace the per-popup "Nearby on Wikipedia" link with a live Wikipedia layer
The old link only ever surfaced one nearby article at a time, buried
in whatever POI's popup you happened to open, and only for uploaded
files with a category. Replaced it with a standalone "Wikipedia"
toggle under the renamed "Tracks & Points" panel: enabling it adds a
live map layer that refetches GET /api/kiwix/nearby on every pan/zoom
and plots every nearby article as its own point.

kiwix-serve's own geo search never echoes back a result's coordinates
(confirmed live against the real production ZIM, both its HTML and
?format=xml output) — it only filters by distance from a point. So
Maptool::Kiwix.nearby fetches each matching article's own page too and
reads its <meta name="geo.position"> tag, which every geotagged
Wikipedia article carries. Tried parallelizing those per-article
fetches with threads first; measured no improvement against the real
ZIM (kiwix-serve appears to serialize requests regardless), so it
stayed sequential — ~1.6s for a 30-result page, fine for a
refetch-on-pan-end layer.

Clicking a point opens a small popup linking to the article (proxied
through the existing /kiwix/* passthrough). New WIKI_ICON (NPS's
bookstore-22.svg) marks these, kept out of POI_ICONS since that set
doubles as notes.js's icon-picker option list and this fixed-glyph
layer has no per-feature category to key off of.
2026-09-10 20:45:41 +02:00
randy 47e3f9c7e8 Update TODO.adoc for this session's work
CI / check (push) Successful in 41s
CI / deploy (push) Successful in 13s
Marks Park4Night (private plugin), the category-filter polygon/line
bug, and the overpass-pois icon/description work as done.
2026-09-09 18:22:28 +02:00
randy ea9cd5fc40 Fix three bugs found reviewing this session's changes
- notes.js's ICON_LABELS wasn't extended for the shop_supermarket/
  shop_convenience keys icons.js added — the note icon picker showed
  the raw internal key as the title instead of a real label.
- Plugins.run mislabeled a malformed plugin.json as "plugin did not
  print valid JSON" — a message written for bad script stdout, not a
  bad manifest, since both paths shared one rescue.
- overpass_pois.rb's description_for joined raw OSM tag values with
  "\n" — an operator/opening_hours tag with an embedded newline could
  inject an extra line into the popup that gpx.js's formatDescription
  (built for p4n2gpx's own well-formed lines) would render as its own
  independent, legitimate-looking link. Tag values are stripped of
  embedded newlines before joining.
2026-09-09 18:21:47 +02:00
randy 7c664578af Fix category filter not hiding polygon/line notes, only points
TODO.adoc had this flagged: toggling a category off in the legend only
re-sliced the points source (_applyCategoryFilter), so a polygon or
line note (Measure's "Save as note") sharing that category stayed
visible. Added _linesByPath alongside the existing _pointsByPath,
tracked/cleaned up the same way, and reused for both the legend's
category scan and the filter's re-slice.
2026-09-09 18:21:39 +02:00
randy d9cc468478 Show the actual plugin run error instead of a generic message
A failed run showed "Failed — see server log" regardless of why —
misleading, since a Plugins::RunError never actually reaches any log
(app.rb turns it straight into a 502 JSON response). Found this the
hard way debugging the p4n2gpx plugin, having to SSH in and reproduce
the run by hand each time just to see why it failed. Now shows the
response's actual error message (e.g. "plugin exited with status 1"),
wrapping instead of overflowing the flyout for a longer one.
2026-09-09 18:17:24 +02:00
98 changed files with 15098 additions and 485 deletions

No files matched your search

+12 -5
View File
@@ -6,7 +6,14 @@ on:
jobs: jobs:
check: check:
runs-on: ubuntu-latest # brick-build's job container now points at Gitea's own official
# runner image (docker.gitea.com/runner-images:ubuntu-latest, has
# Node out of the box) — the earlier opensuse/leap:16.0 image that
# failed here on 2026-09-12 (no Node, every step below is a JS-based
# Action) got repointed. Pinned to brick specifically again, rather
# than the shared ubuntu-latest label "Gitea Runner on Cube" also
# answers to.
runs-on: brick-build
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -34,13 +41,13 @@ jobs:
run: find public/js -name "*.js" -print0 | xargs -0 -n1 node --check run: find public/js -name "*.js" -print0 | xargs -0 -n1 node --check
# Handled by a separate, native (non-Docker) runner registered with the # Handled by a separate, native (non-Docker) runner registered with the
# single "cube-host" label (see docs/deployment.adoc) — it runs directly # single "brick-host" label (see docs/deployment.adoc) — it runs directly
# on the server, so it has podman/git/etc. from the host, no socket/bind # on the server, so it has podman/git/etc. from the host, no socket/bind
# mount tricks needed. The Docker-based runner keeps handling `check`. # mount tricks needed. The Docker-based runner keeps handling `check`.
deploy: deploy:
needs: check needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: cube-host runs-on: brick-host
steps: steps:
# Not actions/checkout@v4: keeping this plain-git checkout from when # Not actions/checkout@v4: keeping this plain-git checkout from when
# the job ran in a Node-less container — no reason to add a Node # the job ran in a Node-less container — no reason to add a Node
@@ -114,14 +121,14 @@ jobs:
# layers (apt-get/bundle install) as cache — unlike explicitly # layers (apt-get/bundle install) as cache — unlike explicitly
# removing the image first, which would delete that cache along with # removing the image first, which would delete that cache along with
# it. sudo is needed since the unit is root's, not gitea-runner's own # it. sudo is needed since the unit is root's, not gitea-runner's own
# (gitea-runner has passwordless sudo for this on cube-host). # (gitea-runner has passwordless sudo for this on brick-host).
- name: Deploy - name: Deploy
run: sudo systemctl restart podman-compose@maptool.service run: sudo systemctl restart podman-compose@maptool.service
# systemctl restart above only confirms the process forked # systemctl restart above only confirms the process forked
# (Type=simple, no readiness protocol) — not that `podman compose up # (Type=simple, no readiness protocol) — not that `podman compose up
# --build` actually finished and Puma is answering. Polls the health # --build` actually finished and Puma is answering. Polls the health
# endpoint (cube-host itself, same machine the container's port is # endpoint (brick-host itself, same machine the container's port is
# published on) instead of trusting that — checking for this # published on) instead of trusting that — checking for this
# deploy's own revision, not just any 200, so a stuck old container # deploy's own revision, not just any 200, so a stuck old container
# that never got torn down can't pass as a successful deploy of the # that never got torn down can't pass as a successful deploy of the
+20 -12
View File
@@ -8,17 +8,25 @@ on:
jobs: jobs:
renovate: renovate:
# The native deploy runner (see docs/deployment.adoc) already has # Same reasoning as check's runs-on in ci.yml: brick-build's job
# Docker access — Renovate itself is a one-shot batch job, not a # container now has Node (repointed at Gitea's own official runner
# long-running service, so `docker run`, not docker-compose. # image), which setup-node/npm below need.
runs-on: deploy runs-on: brick-build
steps: steps:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install Renovate
run: npm install -g renovate
- name: Run Renovate - name: Run Renovate
run: | run: renovate
docker run --rm \ env:
-e RENOVATE_PLATFORM=gitea \ RENOVATE_PLATFORM: gitea
-e RENOVATE_ENDPOINT="${{ gitea.server_url }}/api/v1/" \ RENOVATE_ENDPOINT: "${{ gitea.server_url }}/api/v1/"
-e RENOVATE_TOKEN="${{ secrets.RENOVATE_TOKEN }}" \ RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
-e RENOVATE_REPOSITORIES="${{ gitea.repository }}" \ RENOVATE_REPOSITORIES: "${{ gitea.repository }}"
-e RENOVATE_ONBOARDING=false \ RENOVATE_ONBOARDING: "false"
renovate/renovate GITHUB_COM_TOKEN: ${{ secrets.GITHUB_COM_TOKEN }}
+11
View File
@@ -12,6 +12,17 @@
# see docs/development.adoc) # see docs/development.adoc)
/data/*.pmtiles /data/*.pmtiles
# Per-country .osm.pbf cache for offline.pmtiles rebuilds (also large,
# also local-only — see PmtilesBuilder.cached_extract)
/data/geofabrik_cache/
# Planetiler's own auxiliary-source cache and scratch working directory
# (water polygons/Natural Earth/lake centerlines, --download; temp feature
# DBs, --tmpdir) — relative to the app's CWD, which lands them here
# alongside the rest of data/ (see extract_offline's --download comment)
/data/sources/
/data/tmp/
# Offline Wikipedia ZIM for kiwix-serve (also large, also local-only — # Offline Wikipedia ZIM for kiwix-serve (also large, also local-only —
# same story, see docs/development.adoc) # same story, see docs/development.adoc)
/data/kiwix/* /data/kiwix/*
+40 -2
View File
@@ -1,11 +1,49 @@
FROM ruby:4.0.6-slim-bookworm # trixie, not bookworm: Planetiler needs Java 21+ (UnsupportedClassVersionError
# otherwise) and bookworm's own default-jre-headless is only Java 17 —
# bookworm-backports doesn't carry a newer one either, only trixie's does.
FROM ruby:4.0.6-slim-trixie
# build-essential: puma has a native (C) extension; harmless if the # build-essential: puma has a native (C) extension; harmless if the
# platform-specific gem in Gemfile.lock already has a precompiled binary # platform-specific gem in Gemfile.lock already has a precompiled binary
# matching this base image, but keeps the build working if it doesn't. # matching this base image, but keeps the build working if it doesn't.
RUN apt-get update && apt-get install --no-install-recommends -y build-essential \ # ca-certificates/curl: needed for the go-pmtiles/planetiler.jar downloads
# below (also handy for anyone debugging inside a running container).
# default-jre-headless: Planetiler is a JVM tool (see PLANETILER_VERSION
# below) — headless since this never touches a display; trixie's version
# of this package is Java 21, see above.
# osmium-tool: merges multiple countries' OSM extracts into one file before
# handing them to Planetiler (lib/maptool/pmtiles_builder.rb) — a normal
# Debian package, no separate download/pinning needed like the other two.
RUN apt-get update && apt-get install --no-install-recommends -y build-essential ca-certificates curl default-jre-headless osmium-tool \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# The `pmtiles` CLI (lib/maptool/pmtiles_builder.rb's "Regenerate for
# current view" — see docs/development.adoc) — no Debian package exists,
# so this pulls the static binary straight from its own GitHub releases.
# Architecture-matched (Debian's dpkg arch name translated to the release
# asset's own naming) rather than hardcoded amd64, so this still works
# building on an arm64 host. Pinned, like every other dependency in this
# repo (Gemfile, docs/third-party-notices.adoc) — bump PMTILES_VERSION
# deliberately, not via `latest`.
ARG PMTILES_VERSION=1.31.2
RUN set -eux; \
case "$(dpkg --print-architecture)" in \
amd64) pmtiles_arch=x86_64 ;; \
arm64) pmtiles_arch=arm64 ;; \
*) echo "unsupported architecture for go-pmtiles: $(dpkg --print-architecture)" >&2; exit 1 ;; \
esac; \
curl -fsSL "https://github.com/protomaps/go-pmtiles/releases/download/v${PMTILES_VERSION}/go-pmtiles_${PMTILES_VERSION}_Linux_${pmtiles_arch}.tar.gz" \
| tar xz -C /usr/local/bin pmtiles
# Planetiler (lib/maptool/pmtiles_builder.rb's offline.pmtiles build,
# replacing an earlier Protomaps-CDN-based pipeline — see
# docs/development.adoc) — a single JVM fat jar, no per-arch variant needed
# unlike go-pmtiles above. Default PLANETILER_JAR path (pmtiles_builder.rb)
# expects it here.
ARG PLANETILER_VERSION=0.10.2
RUN curl -fsSL "https://github.com/onthegomap/planetiler/releases/download/v${PLANETILER_VERSION}/planetiler.jar" \
-o /usr/local/lib/planetiler.jar
WORKDIR /app WORKDIR /app
COPY Gemfile Gemfile.lock ./ COPY Gemfile Gemfile.lock ./
+16
View File
@@ -24,6 +24,22 @@ namespace :overpass do
end end
end end
namespace :hiking do
desc "Extract route=hiking relations (name/colour/network/operator, full geometry) from a local " \
".osm.pbf (e.g. one of PmtilesBuilder's own data/geofabrik_cache/*.osm.pbf) and write GeoJSON. " \
"Prototype — see lib/maptool/hiking_trails.rb. Usage: rake hiking:trails[input.osm.pbf,out.geojson]"
task :trails, %i[pbf out] do |_t, args|
%i[pbf out].each { |key| abort("usage: rake hiking:trails[input.osm.pbf,out.geojson]") unless args[key] }
require_relative "lib/maptool/hiking_trails"
puts "Extracting hiking routes from #{args[:pbf]}..."
geojson = Maptool::HikingTrails.extract(args[:pbf])
File.write(args[:out], JSON.generate(geojson))
puts "Wrote #{geojson["features"].size} route(s) to #{args[:out]}"
end
end
namespace :gpx do namespace :gpx do
desc "Backfill properties.id on Note-category features saved before edit/delete (and its id targeting) existed" desc "Backfill properties.id on Note-category features saved before edit/delete (and its id targeting) existed"
task :backfill_note_ids do task :backfill_note_ids do
+19 -5
View File
@@ -8,14 +8,18 @@ independence from connectivity.
== Offline data and cartography == Offline data and cartography
* [ ] Offline vector tiles (PMTiles/MBTiles) for Europe/world via Planetiler or MapTiler * [x] Offline vector tiles (PMTiles/MBTiles) for Europe/world via Planetiler or MapTiler — Geofabrik per-country extracts, `osmium merge`, Planetiler's default OpenMapTiles profile straight to PMTiles (`lib/maptool/pmtiles_builder.rb`); offline now shares the exact same style/palette as online (`ONLINE_STYLE_PATCH`) instead of a separate protomaps-schema theme
* [x] Shaded relief / DEM layer — hillshade toggle in the sidebar, sourced from public Terrarium tiles online; reads `data/terrain.pmtiles` for offline if one's been generated (see docs/development.adoc), otherwise the toggle is online-only for now * [x] Shaded relief / DEM layer — hillshade toggle in the sidebar, sourced from public Terrarium tiles online; reads `data/terrain.pmtiles` for offline if one's been generated (see docs/development.adoc), otherwise the toggle is online-only for now
** [ ] Terrain slope readout for parking (numeric, not just visual shading) ** [ ] Terrain slope readout for parking (numeric, not just visual shading)
* [ ] Contour lines from OSM for hikes and mountain passes * [ ] Contour lines from OSM for hikes and mountain passes
* [x] Overpass/OSM scripts to extract POIs (drinking water, dump stations, LPG, public toilets, laundromats, shops, springs) — `rake overpass:pois`, see docs/development.adoc; writes a `.geojson` to drop in `data/gpx/` or upload. Also runnable from inside the app itself now, see the import plugins entry below (`plugins/overpass-pois` wraps this same query) * [x] Hiking trail overlay — Waymarked Trails raster tiles online (`public/js/hiking_trails.js`), colored per-route by the OSM `colour` tag they already render (the KST/KČT paint-blaze colors in Slovakia/Czechia, plain red elsewhere). Click queries a one-off Overpass `around` search (racing 3 public mirrors) for the route's name/colour/network/operator/distance
** [x] Offline copy wired in for real, no custom Planetiler profile needed — `Maptool::HikingTrails` (`lib/maptool/hiking_trails.rb`) runs `osmium tags-filter r/route=hiking` + `add-locations-to-ways` + `cat -f opl` against the *same* per-country Geofabrik `.osm.pbf` already downloaded for `offline.pmtiles`, no new download. One `data/hiking_trails/<CODE>.geojson` per country (not one combined file), generated as the last step of every offline build and cached like `geofabrik_cache` (skipped once already newer than its source PBF) — a many-country selection no longer means one giant file, since `GET /api/hiking-trails` lists available countries and the Overlays panel's own checklist only fetches `GET /tiles/hiking_trails/<CODE>.geojson` for whichever ones get ticked. Offline click uses a local `queryRenderedFeatures` hit-test instead of Overpass — instant, no network. See docs/development.adoc
* [x] Overpass/OSM scripts to extract POIs (drinking water, dump stations, LPG, public toilets, laundromats, shops, springs) — `rake overpass:pois`, see docs/development.adoc; writes a `.geojson` to drop in `data/gpx/` or upload. Also runnable from inside the app itself now, see the import plugins entry below (`plugins/overpass-pois` wraps this same query). Each category now carries a real `POI_ICONS` glyph and color (not p4n2gpx's plain red marker) plus a popup description built from opening_hours/operator/website tags
* [x] Import plugins: a pluggable way to add more data sources (Park4Night, iOverlander, Freecampsites, ...) without their scrapers necessarily living in this (public) repo — sidebar icon (logged in only) lists whatever's under `plugins/`, "Run for this area" uses the current map viewport as the bbox, no separate area-picker needed. Contract: a `plugin.json` manifest + a script that reads `--south`/`--west`/`--north`/`--east` and prints a GeoJSON `FeatureCollection` to stdout (`lib/maptool/plugins.rb`, `GET /api/plugins`, `POST /api/plugins/:name/run`) — output saved the same way an upload would be. `plugins/overpass-pois` ships as the reference example; a private/ToS-sensitive scraper just drops its own directory in locally and is never `git add`ed * [x] Import plugins: a pluggable way to add more data sources (Park4Night, iOverlander, Freecampsites, ...) without their scrapers necessarily living in this (public) repo — sidebar icon (logged in only) lists whatever's under `plugins/`, "Run for this area" uses the current map viewport as the bbox, no separate area-picker needed. Contract: a `plugin.json` manifest + a script that reads `--south`/`--west`/`--north`/`--east` and prints a GeoJSON `FeatureCollection` to stdout (`lib/maptool/plugins.rb`, `GET /api/plugins`, `POST /api/plugins/:name/run`) — output saved the same way an upload would be. `plugins/overpass-pois` ships as the reference example; a private/ToS-sensitive scraper just drops its own directory in locally and is never `git add`ed
** [ ] Park4Night / iOverlander / Freecampsites data itself — no plugin written yet for any of these, the infra above just makes adding one (in a private repo if needed) straightforward whenever that happens ** [x] Park4Night — private plugin (`data/plugins/p4n2gpx/`, outside this repo per the "ToS-sensitive scraper" note above); iOverlander/Freecampsites still open, same story
* [x] Kiwix/ZIM offline Wikipedia linked to POI GPS coordinates — a "Nearby on Wikipedia" popup link to kiwix-serve's own geo search (`GET /kiwix/search?latitude=&longitude=&distance=`), proxied through this app (`GET /kiwix/*` in app.rb) rather than a direct link to kiwix-serve — production sits behind a reverse proxy that only forwards to this app, so no address exists for a browser to reach kiwix-serve at directly; kiwix-serve runs with `--urlRootLocation=/kiwix` so its own emitted links already match. `KIWIX_URL` + `GET /api/kiwix` reachability check, same optional-integration pattern as offline.pmtiles/terrain.pmtiles; `docker-compose.yaml` now runs a `kiwix` service, ZIM not included (see docs/development.adoc for where to get one) ** [x] A private plugin's own dependencies now have somewhere to live too: `data/plugins/` is under the persistent `data/` bind mount (survives a redeploy, unlike `build/`), and `docs/deployment.adoc` documents the convention
* [x] Kiwix/ZIM offline Wikipedia linked to POI GPS coordinates — proxied through this app (`GET /kiwix/*` in app.rb) rather than a direct link to kiwix-serve — production sits behind a reverse proxy that only forwards to this app, so no address exists for a browser to reach kiwix-serve at directly; kiwix-serve runs with `--urlRootLocation=/kiwix` so its own emitted links already match. `KIWIX_URL` + `GET /api/kiwix` reachability check, same optional-integration pattern as offline.pmtiles/terrain.pmtiles; `docker-compose.yaml` now runs a `kiwix` service, ZIM not included (see docs/development.adoc for where to get one)
** [x] Replaced the old per-popup "Nearby on Wikipedia" link (one POI at a time, easy to miss) with a standalone "Wikipedia" layer under Tracks & Points — a live `GET /api/kiwix/nearby` refetch on every pan/zoom, not a saved file. kiwix-serve's own geo search never echoes back a result's coordinates, so `Maptool::Kiwix.nearby` fetches each matching article's own page too and reads its `<meta name="geo.position">` tag — verified against the real production ZIM (~1.6s for a 30-result page; tried parallelizing those fetches with threads, no measured gain, kept sequential)
* [x] Cache weather forecast when temporarily connected — Open-Meteo 3-day forecast in the Radar flyout, last successful fetch cached in localStorage as a fallback when offline * [x] Cache weather forecast when temporarily connected — Open-Meteo 3-day forecast in the Radar flyout, last successful fetch cached in localStorage as a fallback when offline
** [x] Cache the radar tile *images* too — `public/sw.js`, network-falling-back-to-cache for RainViewer tile requests, registered from layout.erb ** [x] Cache the radar tile *images* too — `public/sw.js`, network-falling-back-to-cache for RainViewer tile requests, registered from layout.erb
@@ -28,6 +32,7 @@ independence from connectivity.
** [x] Filter panel as a "Unigrid Matrix" — an icon grid to toggle categories on/off, styled like the legend on an official NPS paper map, instead of plain dropdowns ** [x] Filter panel as a "Unigrid Matrix" — an icon grid to toggle categories on/off, styled like the legend on an official NPS paper map, instead of plain dropdowns
* [ ] Custom vector style (MVT/MapLibre) using the NPS color palette — muted natural tones for forests, water, and contours — so POIs and routes stand out at high zoom * [ ] Custom vector style (MVT/MapLibre) using the NPS color palette — muted natural tones for forests, water, and contours — so POIs and routes stand out at high zoom
** [ ] Landuse & protections: distinct styling for national parks, nature reserves (translucent green/gray-green), military zones, and private land (important for wild camping) ** [ ] Landuse & protections: distinct styling for national parks, nature reserves (translucent green/gray-green), military zones, and private land (important for wild camping)
** [ ] OpenTopoMap-style `fill-pattern` texture for forest (tree icons) and meadow/pasture (grass texture) landcover, instead of flat fill colors — Liberty's own sprite (`public/vendor/liberty-sprite/`) only has a `wetland` dot pattern, no tree/grass, so this needs custom pattern images added to the sprite sheet; works offline and online alike since it's just a style/sprite change, unlike pulling in OpenTopoMap's own raster tiles (online-only, restrictive tile-usage policy, wouldn't match the NPS palette)
** [x] Elevation-tinted terrain — a `color-relief` layer (hypsometric tint, this app's own green→brown→gray palette) under the hillshade layer, same "Terrain shading" toggle ** [x] Elevation-tinted terrain — a `color-relief` layer (hypsometric tint, this app's own green→brown→gray palette) under the hillshade layer, same "Terrain shading" toggle
** [x] Road hierarchy: clearly distinguish paved roads from unpaved/gravel/track — critical for knowing if the camper can get there ** [x] Road hierarchy: clearly distinguish paved roads from unpaved/gravel/track — critical for knowing if the camper can get there
@@ -51,7 +56,7 @@ independence from connectivity.
** [x] The clicked point stays marked on the map while the note form is open (a plain `Marker`, removed on save/cancel/deactivate) — it was disappearing back into the flat map with nothing showing where the note would land ** [x] The clicked point stays marked on the map while the note form is open (a plain `Marker`, removed on save/cancel/deactivate) — it was disappearing back into the flat map with nothing showing where the note would land
** [x] Icon picker for point notes (a grid in the save form) — extends `POI_ICONS` with more of the symbols `PARK4NIGHT_TAGS` already vendors (water, dump station, toilets, playground, viewpoint, swimming, fishing, climbing, hiking, …), reusable as real marker-badge glyphs, not just inline tag icons. Hidden for a Measure-drawn line/polygon note — no single spot for a badge to sit at ** [x] Icon picker for point notes (a grid in the save form) — extends `POI_ICONS` with more of the symbols `PARK4NIGHT_TAGS` already vendors (water, dump station, toilets, playground, viewpoint, swimming, fishing, climbing, hiking, …), reusable as real marker-badge glyphs, not just inline tag icons. Hidden for a Measure-drawn line/polygon note — no single spot for a badge to sit at
** [x] Fixed: the Save button's own blue background was losing to `.notes-panel__actions button`'s gray — same class-vs-class+element specificity trap as `.gpx-panel__action[hidden]` already has a comment about elsewhere in this file, just not yet applied here ** [x] Fixed: the Save button's own blue background was losing to `.notes-panel__actions button`'s gray — same class-vs-class+element specificity trap as `.gpx-panel__action[hidden]` already has a comment about elsewhere in this file, just not yet applied here
** [ ] The Unigrid Matrix category filter only re-slices the *points* source (`_applyCategoryFilter`) — toggling the "Note" category off hides point notes but not polygon/line ones sharing that category ** [x] Fixed: the Unigrid Matrix category filter only re-sliced the *points* source (`_applyCategoryFilter`) — toggling the "Note" category off hid point notes but not polygon/line ones sharing that category. `_linesByPath` now tracks those alongside `_pointsByPath`, re-sliced/scanned the same way
* [ ] Offline satellite tile caching (e.g. ESRI) for a selected route area — key for judging surface quality and obstacles when wild camping * [ ] Offline satellite tile caching (e.g. ESRI) for a selected route area — key for judging surface quality and obstacles when wild camping
== Nice to have (later) == Nice to have (later)
@@ -68,3 +73,12 @@ Deprioritized for now — revisit after the sections above land.
* [ ] Level gauge from IMU (ESP32/RPi) — camper tilt, wheel-chock leveling calculation * [ ] Level gauge from IMU (ESP32/RPi) — camper tilt, wheel-chock leveling calculation
* [ ] Victron/Home Assistant via MQTT or WebSocket — battery SoC, tank levels, temperatures * [ ] Victron/Home Assistant via MQTT or WebSocket — battery SoC, tank levels, temperatures
* [ ] Plot GPS track with historical battery/speed data * [ ] Plot GPS track with historical battery/speed data
== Admin / ops tooling
* [ ] Offline build log readability — a failed pmtiles/terrain build currently surfaces as one
~500k-character line; format it for the web UI instead (e.g. per-country/state name + status,
not the raw combined stdout/stderr dump)
* [ ] Expose the build log via an API endpoint, not just embedded in the admin page
* [ ] Document all API endpoints somewhere and make them easy to browse (status of each) —
maybe a status page, login-gated
+235 -20
View File
@@ -12,8 +12,43 @@ require_relative "lib/maptool/livetrack_store"
require_relative "lib/maptool/livetrack_client" require_relative "lib/maptool/livetrack_client"
require_relative "lib/maptool/kiwix" require_relative "lib/maptool/kiwix"
require_relative "lib/maptool/plugins" require_relative "lib/maptool/plugins"
require_relative "lib/maptool/pmtiles_builder"
module Maptool module Maptool
# Only /login, /auth/callback and /logout ever write to session
# (oauth_state, user, session.clear) — everything else only ever reads
# it, if it touches session at all. That still matters:
# Rack::Session::Cookie re-serializes and re-sets the cookie on *every*
# response that so much as read session (Rack::Protection's own CSRF
# check does, on nearly every request) — reflecting whatever cookie
# that particular request happened to arrive with, whether or not
# anything changed. A HAR capture caught the actual failure mode this
# caused: seconds after a successful login, a background poll fired the
# instant the page loads was still carrying the browser's pre-login
# cookie (a client-side timing race across the OAuth redirect chain,
# not something this server controls), and its own re-issued Set-Cookie
# landed *after* the login's, silently overwriting session[:user] with
# nothing logged anywhere to explain it.
#
# A plain Rack middleware, not a Sinatra `before` filter: static assets
# (public_folder — every JS/CSS/font/vendor file) are served by
# Sinatra's own built-in static-file shortcut, which bypasses route and
# filter dispatch entirely but is still wrapped by this, same as every
# other response — each one was independently capable of winning the
# clobbering race a `before` filter alone left uncovered.
class SkipSessionWrite
WRITE_PATHS = %w[/login /auth/callback /logout].freeze
def initialize(app)
@app = app
end
def call(env)
env["rack.session.options"][:skip] = true unless WRITE_PATHS.include?(env["PATH_INFO"])
@app.call(env)
end
end
class App < Sinatra::Base class App < Sinatra::Base
set :public_folder, File.join(__dir__, "public") set :public_folder, File.join(__dir__, "public")
set :views, File.join(__dir__, "views") set :views, File.join(__dir__, "views")
@@ -32,6 +67,7 @@ module Maptool
# don't survive a restart there, which is fine outside production. # don't survive a restart there, which is fine outside production.
set :sessions, httponly: true, secure: production?, same_site: :lax, set :sessions, httponly: true, secure: production?, same_site: :lax,
secret: ENV.fetch("SESSION_SECRET") { SecureRandom.hex(32) } secret: ENV.fetch("SESSION_SECRET") { SecureRandom.hex(32) }
use SkipSessionWrite
# Cache-busts /js and /css requests: browsers (and a caching proxy in # Cache-busts /js and /css requests: browsers (and a caching proxy in
# front of production, see docs/deployment.adoc) hang onto static # front of production, see docs/deployment.adoc) hang onto static
@@ -125,6 +161,13 @@ module Maptool
halt 401, json(error: "login required", login_url: "/login") unless logged_in? halt 401, json(error: "login required", login_url: "/login") unless logged_in?
end end
def pmtiles_file_status(path, meta)
return { available: false } unless File.exist?(path)
status = { available: true, size: File.size(path), mtime: File.mtime(path).utc.iso8601 }
meta ? status.merge(meta: meta) : status
end
def callback_url def callback_url
"#{request.base_url}/auth/callback" "#{request.base_url}/auth/callback"
end end
@@ -204,7 +247,7 @@ module Maptool
end end
get "/api/offline-tiles" do get "/api/offline-tiles" do
json available: File.exist?(OFFLINE_PMTILES_PATH) json pmtiles_file_status(OFFLINE_PMTILES_PATH, PmtilesBuilder.offline_meta(DATA_DIR))
end end
# Served as a plain range-request-capable file, not decoded server-side: # Served as a plain range-request-capable file, not decoded server-side:
@@ -216,11 +259,100 @@ module Maptool
send_file OFFLINE_PMTILES_PATH, type: "application/octet-stream" send_file OFFLINE_PMTILES_PATH, type: "application/octet-stream"
end end
# Compares the recorded build (offline.pmtiles.meta.json) against the
# current latest Protomaps build without rebuilding anything —
# update_available is null (not true/false) if there's no local build
# yet, or the live index couldn't be reached, so the frontend can tell
# "don't know" apart from "no, up to date". Not folded into GET
# /api/offline-tiles above: that route is on the hot path (map.js's own
# topbar-dot check on every page load), this one only gets called when
# the Offline data panel is actually opened.
get "/api/offline-tiles/check-update" do
json update_available: PmtilesBuilder.offline_update_available?(DATA_DIR)
end
# Lists the selectable countries and which of them are currently
# selected (data/offline_regions.json) — read-only, no login needed,
# same tier as the plain availability checks above.
get "/api/offline-regions" do
json available: PmtilesBuilder.available_regions, selected: PmtilesBuilder.selected_regions(DATA_DIR)
end
# Just updates the selection — does not itself start a build, so
# ticking several countries doesn't fire off several rebuilds. The
# frontend follows this with the two build routes below once the user
# is done choosing.
post "/api/offline-regions" do
require_login!
codes = Array(params[:codes])
begin
PmtilesBuilder.set_selected_regions(DATA_DIR, codes)
rescue PmtilesBuilder::BuildError => e
halt 400, json(error: e.message)
end
json ok: true
end
# Regenerates offline.pmtiles for the currently selected regions (see
# POST /api/offline-regions above) by downloading each from Geofabrik
# and tiling them with Planetiler — see lib/maptool/pmtiles_builder.rb.
# Runs in the background; poll build-status below for progress.
post "/api/offline-tiles/build" do
require_login!
begin
PmtilesBuilder.build_offline(data_dir: DATA_DIR)
rescue PmtilesBuilder::BuildError => e
halt 409, json(error: e.message)
end
json status: "started"
end
get "/api/offline-tiles/build-status" do
json PmtilesBuilder.offline_status
end
# Deletes offline.pmtiles plus its whole per-country download cache —
# a manual "reclaim the space"/"start over" action, distinct from
# unticking countries in the region picker (which only changes what a
# future build covers, doesn't free anything already on disk).
delete "/api/offline-tiles" do
require_login!
begin
PmtilesBuilder.delete_offline_data(DATA_DIR)
rescue PmtilesBuilder::BuildError => e
halt 409, json(error: e.message)
end
json ok: true
end
# What's actually in the per-country download cache right now (not the
# same as offline.pmtiles' own meta.sources — see cached_extracts) —
# read-only, same tier as GET /api/offline-regions above, so the region
# picker can show a size + delete affordance next to each cached
# country regardless of whether it's still selected or already built.
get "/api/offline-tiles/cache" do
json extracts: PmtilesBuilder.cached_extracts(DATA_DIR)
end
# Deletes one country's cached Geofabrik extract only — e.g. to force a
# clean re-download of just that one, or reclaim space for a country
# that's no longer selected (the cache otherwise keeps it indefinitely).
delete "/api/offline-tiles/cache/:code" do
require_login!
begin
PmtilesBuilder.delete_cached_extract(DATA_DIR, params[:code])
rescue PmtilesBuilder::BuildError => e
halt 409, json(error: e.message)
end
json ok: true
end
# Same optional-local-file story as offline.pmtiles above — see # Same optional-local-file story as offline.pmtiles above — see
# docs/development.adoc for how to generate one, until then hillshade # docs/development.adoc for how to generate one, until then hillshade
# just stays unavailable while the offline basemap is active. # just stays unavailable while the offline basemap is active.
get "/api/terrain-tiles" do get "/api/terrain-tiles" do
json available: File.exist?(TERRAIN_PMTILES_PATH) json pmtiles_file_status(TERRAIN_PMTILES_PATH, PmtilesBuilder.terrain_meta(DATA_DIR))
end end
get "/tiles/terrain.pmtiles" do get "/tiles/terrain.pmtiles" do
@@ -229,6 +361,64 @@ module Maptool
send_file TERRAIN_PMTILES_PATH, type: "application/octet-stream" send_file TERRAIN_PMTILES_PATH, type: "application/octet-stream"
end end
# Same reasoning as GET /api/offline-tiles/check-update above.
get "/api/terrain-tiles/check-update" do
json update_available: PmtilesBuilder.terrain_update_available?(DATA_DIR)
end
post "/api/terrain-tiles/build" do
require_login!
begin
PmtilesBuilder.build_terrain(data_dir: DATA_DIR)
rescue PmtilesBuilder::BuildError => e
halt 409, json(error: e.message)
end
json status: "started"
end
get "/api/terrain-tiles/build-status" do
json PmtilesBuilder.terrain_status
end
# Same reasoning as DELETE /api/offline-tiles above.
delete "/api/terrain-tiles" do
require_login!
begin
PmtilesBuilder.delete_terrain_data(DATA_DIR)
rescue PmtilesBuilder::BuildError => e
halt 409, json(error: e.message)
end
json ok: true
end
# Generated as a step inside POST /api/offline-tiles/build (see
# PmtilesBuilder.extract_hiking_trails) rather than its own separate
# build — it's derived from the exact same per-country .osm.pbf cache
# offline.pmtiles itself already downloads, so there's nothing of its
# own to trigger; DELETE /api/offline-tiles removes it too. One file
# per country (not one combined file) so a viewer only ever fetches
# the country they actually pick in the Hiking trails overlay's own
# checklist (public/js/hiking_trails.js), not every selected country's
# trails at once — `countries` lists which ones exist to build that
# checklist from.
get "/api/hiking-trails" do
json countries: PmtilesBuilder.hiking_trails_countries(DATA_DIR)
end
# Fetched client-side only for a country the viewer actually ticks in
# the Hiking trails checklist, not on page load. :code is constrained
# to exactly what hiking_trails_countries can ever return (two
# uppercase letters) — otherwise this would accept an arbitrary
# ".."-laden path segment.
get "/tiles/hiking_trails/:code.geojson" do
halt 404 unless params[:code].match?(/\A[A-Z]{2}\z/)
path = PmtilesBuilder.hiking_trails_country_path(DATA_DIR, params[:code])
halt 404 unless File.exist?(path)
send_file path, type: "application/geo+json"
end
# Pinged once per page load (public/js/map.js) — url is a same-origin # Pinged once per page load (public/js/map.js) — url is a same-origin
# path (not kiwix-serve's own address, which the browser can't reach # path (not kiwix-serve's own address, which the browser can't reach
# directly behind the reverse proxy in front of this app), proxied by # directly behind the reverse proxy in front of this app), proxied by
@@ -238,6 +428,19 @@ module Maptool
json available: reachable, url: (reachable ? "/kiwix" : "") json available: reachable, url: (reachable ? "/kiwix" : "")
end end
# Backs the "Wikipedia" map layer (public/js/gpx.js) — replaces the old
# per-popup "Nearby on Wikipedia" link, which only ever pointed at one
# POI at a time. Read-only and login-free, same as the tree/plugin
# listing routes below: viewing the map needs no account, only writing
# to it does.
get "/api/kiwix/nearby" do
halt 404 if KIWIX_URL.empty?
bbox = %w[south west north east].map { |key| params[key].to_s }
halt 400, json(error: "south, west, north, east are required") if bbox.any?(&:empty?)
json Kiwix.nearby(KIWIX_URL, bbox.map(&:to_f))
end
# Proxies kiwix-serve's own search UI and content pages through this # Proxies kiwix-serve's own search UI and content pages through this
# app instead of linking the browser to it directly (see KIWIX_URL) — # app instead of linking the browser to it directly (see KIWIX_URL) —
# kiwix-serve is started with --urlRootLocation=/kiwix # kiwix-serve is started with --urlRootLocation=/kiwix
@@ -540,24 +743,36 @@ module Maptool
halt 502, json(error: "track has no positions") if coordinates.empty? halt 502, json(error: "track has no positions") if coordinates.empty?
last = points.last last = points.last
json( features = [
type: "FeatureCollection", { type: "Feature", geometry: { type: "LineString", coordinates: coordinates }, properties: { "kind" => "actual" } },
features: [ {
{ type: "Feature", geometry: { type: "LineString", coordinates: coordinates }, properties: {} }, type: "Feature",
{ geometry: { type: "Point", coordinates: coordinates.last },
type: "Feature", # Merges the session summary in too — the point layer's hover
geometry: { type: "Point", coordinates: coordinates.last }, # popup (public/js/livetrack.js) reads it straight off here
# Merges the session summary in too — the point layer's hover # rather than needing a second fetch against /sessions.
# popup (public/js/livetrack.js) reads it straight off here properties: summary.merge(
# rather than needing a second fetch against /sessions. "time" => last["dateTime"],
properties: summary.merge( "speed_meters_per_sec" => last["speedMetersPerSec"],
"time" => last["dateTime"], "heart_rate_beats_per_min" => last["heartRateBeatsPerMin"]
"speed_meters_per_sec" => last["speedMetersPerSec"], ),
"heart_rate_beats_per_min" => last["heartRateBeatsPerMin"] },
), ]
},
] # Only present when the activity was started from a saved Garmin
) # course (see LiveTrackClient#fetch) — plotted separately from the
# actual track ("kind" distinguishes the two LineStrings) since it's
# the planned route, not where the person has actually been.
course_coordinates = result.course&.[]("geoPoints")&.filter_map { |p| p.dig("position", "lon") && p.dig("position", "lat") && [p["position"]["lon"], p["position"]["lat"]] }
if course_coordinates&.any?
features << {
type: "Feature",
geometry: { type: "LineString", coordinates: course_coordinates },
properties: { "kind" => "planned", "name" => result.course["name"] },
}
end
json(type: "FeatureCollection", features: features)
end end
get "/api/gnss/position" do get "/api/gnss/position" do
+17 -3
View File
@@ -60,8 +60,17 @@ services:
# docs/development.adoc for how the deploy job treats this # docs/development.adoc for how the deploy job treats this
# directory). A private plugin (app.rb's PLUGINS_DIRS) also belongs # directory). A private plugin (app.rb's PLUGINS_DIRS) also belongs
# under here, at data/plugins/<name>/ — nothing further to mount, # under here, at data/plugins/<name>/ — nothing further to mount,
# this one bind already covers the whole data/ tree. # this one bind already covers the whole data/ tree. An offline.pmtiles
- ./data:/app/data # build's own scratch work (Dir.mktmpdir, lib/maptool/pmtiles_builder.rb)
# happens outside this mount, in the container's own filesystem — a
# large country's .osm.pbf plus Planetiler's working set can be
# several GB, worth knowing if the host is ever short on space there.
# :Z (not :z — this path is only ever mounted into this one
# container) relabels the host directory for SELinux, since Podman
# on an SELinux-enforcing host otherwise denies the container write
# access to a bind-mounted directory regardless of Unix permissions
# — harmless/ignored on a host without SELinux.
- ./data:/app/data:Z
# Fakes a GNSS fix (a loop around Bratislava, see # Fakes a GNSS fix (a loop around Bratislava, see
# bin/gnss_simulator.rb/docs/development.adoc) since there's no real # bin/gnss_simulator.rb/docs/development.adoc) since there's no real
@@ -114,4 +123,9 @@ services:
# ever talks to this container directly. # ever talks to this container directly.
command: ["--urlRootLocation=/kiwix", "/data/wikipedia.zim"] command: ["--urlRootLocation=/kiwix", "/data/wikipedia.zim"]
volumes: volumes:
- ./data/kiwix/wikipedia.zim:/data/wikipedia.zim # :Z, same SELinux reasoning as maptool's own ./data mount above.
- ./data/kiwix/wikipedia.zim:/data/wikipedia.zim:Z
networks:
default:
enable_ipv6: true
+70 -62
View File
@@ -44,18 +44,33 @@ opened in a browser.
Map data is vector, not raster: the app ships Map data is vector, not raster: the app ships
https://maplibre.org[MapLibre GL JS] (vendored, see below) and the map https://maplibre.org[MapLibre GL JS] (vendored, see below) and the map
view (`public/js/map.js`) offers two styles: view (`public/js/map.js`) offers two styles, both **the same
OpenMapTiles-schema Liberty style** (https://tiles.openfreemap.org/styles/liberty[OpenFreeMap
"liberty"]), just pointed at different vector sources:
* *Online* — a full hosted vector style, * *Online* — Liberty's own hosted tiles, `tiles.openfreemap.org`,
https://tiles.openfreemap.org/styles/liberty[OpenFreeMap "liberty"],
requires internet access. requires internet access.
* *Offline* — built at runtime from a local * *Offline* — a local https://github.com/protomaps/PMTiles[PMTiles]
https://github.com/protomaps/PMTiles[PMTiles] archive at archive at `data/offline.pmtiles`, built by
`data/offline.pmtiles`, served as-is (see below) at https://github.com/onthegomap/planetiler[Planetiler] from an OSM
`/tiles/offline.pmtiles`, decoded client-side by extract (see `docs/development.adoc`), served as-is at
`/tiles/offline.pmtiles` and decoded client-side by
https://github.com/protomaps/PMTiles[pmtiles.js] registered as a https://github.com/protomaps/PMTiles[pmtiles.js] registered as a
`pmtiles://` protocol handler, and styled with base layers from `pmtiles://` protocol handler.
https://github.com/protomaps/basemaps[protomaps-themes-base].
Using the same schema both ways is deliberate: an earlier version of
this app built offline tiles from Protomaps' own basemap schema
instead, which needed a *separate* hand-tuned theme
(`protomaps-themes-base` + a since-removed `OFFLINE_THEME`) to
approximate the online look — two independently-maintained palettes
that drifted out of sync (building opacity, road casings, park
boundaries) every time one side changed. Planetiler's default profile
*is* OpenMapTiles, so `offlineStyle()` now just clones the same parsed
Liberty JSON `ONLINE_STYLE` already fetched, repoints its `sources`,
`glyphs`, and `sprite` at local copies, and applies the exact same
`ONLINE_STYLE_PATCH` recolor (below) — one palette, one place to tune
it, correct on both sides by construction rather than by re-checking
every time something changes.
`GET /api/offline-tiles` reports whether `data/offline.pmtiles` is `GET /api/offline-tiles` reports whether `data/offline.pmtiles` is
present (`{"available": true|false}`); the frontend uses it once, on present (`{"available": true|false}`); the frontend uses it once, on
@@ -72,28 +87,35 @@ support) just returns byte ranges of the file; pmtiles.js in the
browser reads the archive's own directory and requests only the bytes browser reads the archive's own directory and requests only the bytes
it needs for the tiles on screen. it needs for the tiles on screen.
=== Offline text labels and color palette No PMTiles file ships with the repository — it is built locally or
in-app (see `docs/development.adoc`) and stays gitignored because such
extracts are typically large binary files. Geofabrik's per-country
extracts are OSM data, ODbL-licensed, same attribution
(`© OpenStreetMap contributors`) the map already shows either way — no
separate licensing story the way the old Protomaps-basemap pipeline's
NPS-Park-Tiles color research needed.
=== Offline text labels, sprite, and color palette
Label rendering in MapLibre needs a "glyphs" server serving font PBF Label rendering in MapLibre needs a "glyphs" server serving font PBF
ranges (256 Unicode code points each); `data/offline.pmtiles`'s fields ranges (256 Unicode code points each). The glyphs Liberty's style
(`name`, `name:en`, `script`, ...) match what references are vendored under `public/vendor/fonts/` — Noto Sans
`protomaps-themes-base`'s `layers(source, theme, { lang: "en" })` Regular/Italic/Medium, ranges `0-255` and `256-511` (Basic Latin +
expects for its label layers (verified against the actual PMTiles Latin-1 Supplement + Latin Extended-A, SIL OFL licensed, from
vector layer schema), so passing that option turns labels on. The
glyphs themselves are vendored under `public/vendor/fonts/` — Noto
Sans Regular/Italic/Medium, ranges `0-255` and `256-511`
(Basic Latin + Latin-1 Supplement + Latin Extended-A, SIL OFL
licensed, from
https://github.com/protomaps/basemaps-assets[protomaps/basemaps-assets]), https://github.com/protomaps/basemaps-assets[protomaps/basemaps-assets]),
covering accented Latin place names (Slovak, Czech, Polish, German, covering accented Latin place names (Slovak, Czech, Polish, German,
...). Labels in other scripts (Cyrillic, Greek, CJK, ...) won't render ...). Labels in other scripts (Cyrillic, Greek, CJK, ...) won't render
offline, since those glyph ranges aren't vendored. offline, since those glyph ranges aren't vendored. Liberty's icon
sprite (shields, POI icons) is likewise vendored as a one-time
snapshot under `public/vendor/liberty-sprite/` — both `offlineStyle()`
and the online style otherwise share identical `glyphs`/`sprite`
roles, just resolved to local vs. `tiles.openfreemap.org` URLs.
`public/js/map.js`'s `OFFLINE_THEME` nudges a couple dozen color roles `public/js/map.js`'s `ONLINE_STYLE_PATCH` — applied to whichever
from `protomaps-themes-base`'s stock "light" theme towards the source is loaded, online or offline (see above) — nudges Liberty's
National Park Service's own "Park Tiles" cartography — a warm, OpenMapTiles layer ids towards a warm, National Park Service
Harpers-Ferry-Center-derived look. A few of these values are the "Park Tiles"-inspired look. A few of these values are the genuine
genuine paint colors pulled from a public Park Tiles style JSON paint colors pulled from a public Park Tiles style JSON
(`nps.gov/lib/npmap.js/style/ck72fwp2642dv07o7tbqinvz4.json`, the (`nps.gov/lib/npmap.js/style/ck72fwp2642dv07o7tbqinvz4.json`, the
"Imagery" variant — the only one of NPS's four styles served from a "Imagery" variant — the only one of NPS's four styles served from a
plain, unauthenticated URL; "Standard"/"Slate"/"Light" resolve through plain, unauthenticated URL; "Standard"/"Slate"/"Light" resolve through
@@ -101,50 +123,36 @@ Mapbox Studio's private API with an access token, which isn't
something to piggyback on even though it's technically visible in something to piggyback on even though it's technically visible in
their own JS bundle) — background, the park/protected-area green, the their own JS bundle) — background, the park/protected-area green, the
two road-importance tiers, and the park-boundary line color. The rest two road-importance tiers, and the park-boundary line color. The rest
are reasoned to match that same warm family (olive greens, muted blue) are reasoned to match that same warm family (olive greens, muted blue,
rather than sourced. This is deliberately partial — labels, casings, brown road casings) rather than sourced. This is deliberately
and roles with no clean real-color anchor are left at theme partial — labels, halos, rail, and minor overlays (cemetery/hospital/
defaults — not the kind of full per-layer recolor a previous iteration school/pitch/track shading) are left at Liberty's own defaults.
of this app did (and removed, along with any dependency on OsmAnd's
own tile server, which this app cannot use per OsmAnd's terms
restricting tile use to "personal, non-commercial use within OsmAnd
applications and platforms").
+ +
Recreating the *color choices* is fine — that's not the kind of thing Recreating the *color choices* is fine — that's not the kind of thing
a data license restricts — but the actual Park Tiles *service* stays a data license restricts — but the actual Park Tiles *service* stays
off-limits: its OSM/terrain layers are licensed through Mapbox Atlas off-limits: its OSM/terrain layers are licensed through Mapbox Atlas
(a commercial, non-sublicensable data service scoped to NPS), and it's (a commercial, non-sublicensable data service scoped to NPS), and it's
built for park visitors specifically (park-unit and trailhead built for park visitors specifically (park-unit and trailhead
labeling prioritized), not general-purpose worldwide cartography. labeling prioritized), not general-purpose worldwide cartography —
irrelevant to this app either way now, since it never talks to NPS's
tile service, only ever OpenFreeMap/Geofabrik.
No PMTiles file ships with the repository — it is expected to be `loadOnlineStyle` (`public/js/map.js`) fetches Liberty's live
supplied locally (see `docs/development.adoc`) and stays gitignored `style.json` once, patches it in place, and uses the resulting object
because such extracts are typically large binary files. everywhere `ONLINE_STYLE` used to be a plain URL string — MapLibre
accepts a style object anywhere it accepts a style URL, so
The Online map (OpenFreeMap Liberty) gets the same treatment via `applyStyle`'s `map.setStyle(... : ONLINE_STYLE)` needed no change.
`ONLINE_STYLE_PATCH`, a separate hand-built table — Liberty is an That fetch runs before the map (and the rest of the page) is
OpenMapTiles-schema style, with its own layer ids (`landcover_wood`, constructed — a top-level `await` — so there's no flash of Liberty's
`road_secondary_tertiary`, `boundary_2`, ...) that don't correspond to own colors before the patch lands; a 3s timeout and a catch-all fall
protomaps' theme roles, so the two palettes can't share one object, back to the plain URL (letting MapLibre fetch and render it unpatched)
only the same underlying color choices. `loadOnlineStyle` rather than block the whole page's startup on a remote style server.
(`public/js/map.js`) fetches Liberty's `style.json` once, patches it Offline mode can't rely on that same live fetch succeeding — the field
in place, and uses the resulting object everywhere `ONLINE_STYLE` used usage this app is built for is exactly when it won't — so
to be a plain URL string — MapLibre accepts a style object anywhere it `offlineStyle()` instead clones a vendored, point-in-time snapshot of
accepts a style URL, so `applyStyle`'s `map.setStyle(... : ONLINE_STYLE)` the same Liberty JSON (`public/vendor/liberty-style/liberty.json`),
needed no change. That fetch runs before the map (and the rest of the patches it the same way, and repoints it at the local PMTiles/glyphs/
page) is constructed — a top-level `await` — so there's no flash of sprite described above.
Liberty's own colors before the patch lands; a 3s timeout and a
catch-all fall back to the plain URL (letting MapLibre fetch and
render it unpatched) rather than block the whole page's startup on a
remote style server, since this app is meant to keep working offline
in the field, independent of whether the online map itself loads.
Only the layers that dominate the map's overall color read are
patched (land cover, water, parks, roads, boundaries, buildings) —
same deliberately-partial scope as `OFFLINE_THEME`, not a full
per-layer recolor. Road casings are the one addition with no
protomaps-role equivalent to reuse, since offline roads don't get a
separate outline color there; `#c9a66b` is reasoned to fit the family
rather than sourced from anywhere real.
== Map permalink == Map permalink
+32 -10
View File
@@ -7,11 +7,21 @@ MapTool deploys as a Podman container (via `podman compose`, driven by a
the Gitea Actions runners run on. Two separate runners are registered the Gitea Actions runners run on. Two separate runners are registered
against the Gitea instance: against the Gitea instance:
* The existing Docker-based runner (`gitea/runner:3`), labels * The existing Docker-based runner (`gitea/runner:3`), label
`ubuntu-latest`/`ubuntu-24.04`/`ubuntu-22.04` — runs the `check` job `brick-build` — runs the `check` and `renovate` jobs in a fresh,
in a fresh, isolated container per job, same as before. isolated container per job. Every label on this runner (including
`ubuntu-latest`/`ubuntu-24.04`/`ubuntu-22.04`, still registered but no
longer what `check`/`renovate` pin to) now maps to Gitea's own
official runner image (`docker.gitea.com/runner-images:ubuntu-latest`,
has Node out of the box) — an earlier `opensuse/leap:16.0` mapping for
`brick-build` specifically had no Node, which every step in
`check`/`renovate` needs (they're JS-based Actions), and briefly broke
both jobs (2026-09-12) until reverted to the shared `ubuntu-latest`
label; now that the image itself is fixed, both are pinned back to
`brick-build` — this machine specifically, not whichever runner
happens to answer to the shared label (e.g. "Gitea Runner on Cube").
* A dedicated native runner, installed directly on the host (no Docker * A dedicated native runner, installed directly on the host (no Docker
around it), label `cube-host` — runs only the `deploy` job. around it), label `brick-host` — runs only the `deploy` job.
They're separate because the Docker-based runner's jobs (whether in They're separate because the Docker-based runner's jobs (whether in
its usual isolated per-job containers, or tried directly inside its its usual isolated per-job containers, or tried directly inside its
@@ -55,6 +65,13 @@ expect this to flip the other way, `/srv/docker` symlinked to
only holds what's checked into this public repo, and gets wiped and only holds what's checked into this public repo, and gets wiped and
replaced by the checkout sync on every deploy same as the rest of replaced by the checkout sync on every deploy same as the rest of
`build/`. `build/`.
`data/` (and the kiwix ZIM path) carries a `:Z` SELinux relabel in
`docker-compose.yaml`'s bind mounts — without it, Podman on an
SELinux-enforcing host denies the container write access to the
bind-mounted directory regardless of its Unix permissions (surfaces
as a plain `Permission denied` in the app's own logs, nothing
SELinux-labeled unless you go looking in `audit.log`); harmless/
ignored on a host where SELinux isn't enforcing.
* `.env` — regenerated every deploy (see below), lives here rather * `.env` — regenerated every deploy (see below), lives here rather
than in `build/` for the same reason as the symlink: `docker than in `build/` for the same reason as the symlink: `docker
compose`/`podman compose` auto-load `.env` from the compose file's compose`/`podman compose` auto-load `.env` from the compose file's
@@ -84,7 +101,7 @@ label:
---- ----
runner: runner:
labels: labels:
- "cube-host" - "brick-host"
---- ----
The `host` scheme means jobs run directly via the runner process's own The `host` scheme means jobs run directly via the runner process's own
@@ -195,7 +212,7 @@ const PROCESSED_LABEL = "livetrack-processed";
function checkLiveTrackEmail() { function checkLiveTrackEmail() {
const label = GmailApp.getUserLabelByName(PROCESSED_LABEL) || GmailApp.createLabel(PROCESSED_LABEL); const label = GmailApp.getUserLabelByName(PROCESSED_LABEL) || GmailApp.createLabel(PROCESSED_LABEL);
const threads = GmailApp.search(`from:noreply@garmin.com subject:LiveTrack -label:${PROCESSED_LABEL}`); const threads = GmailApp.search(`from:noreply@garmin.com -label:${PROCESSED_LABEL}`);
threads.forEach(thread => { threads.forEach(thread => {
// getBody() (raw HTML), not getPlainBody(): the email has no text/plain // getBody() (raw HTML), not getPlainBody(): the email has no text/plain
@@ -218,9 +235,14 @@ function checkLiveTrackEmail() {
The Gmail label (rather than marking the message read/unread) tracks which The Gmail label (rather than marking the message read/unread) tracks which
threads are already handled without touching the user's own inbox state. threads are already handled without touching the user's own inbox state.
`subject:LiveTrack` matches regardless of the account's language — Garmin No `subject:LiveTrack` filter on the search — despite Gmail's own
keeps "LiveTrack" as-is in the subject even in a localized substring-match documentation for `subject:`, that filter empirically
("Sledujte LiveTrack používateľa ...") one. returned zero threads against a real, currently-arriving LiveTrack email
whose subject does contain the word ("Sledujte LiveTrack používateľa
...", confirmed 2026-09-20), so it's left out entirely — the regex match
below is what actually narrows this down to real LiveTrack emails with a
usable session link, `from:noreply@garmin.com` alone is deliberately
broad.
Set the trigger from the editor: clock icon on the left (Triggers) → *Add Set the trigger from the editor: clock icon on the left (Triggers) → *Add
Trigger* → function `checkLiveTrackEmail`, time-driven, "Minutes timer", Trigger* → function `checkLiveTrackEmail`, time-driven, "Minutes timer",
@@ -237,7 +259,7 @@ webhook simply won't receive that session; nothing breaks.
== CI deploy job == CI deploy job
`.gitea/workflows/ci.yml`'s `deploy` job runs after `check` passes, only `.gitea/workflows/ci.yml`'s `deploy` job runs after `check` passes, only
on a push to `main`, with `runs-on: cube-host`: on a push to `main`, with `runs-on: brick-host`:
. Checks out the repo with plain `git` (`fetch` + `checkout`, . Checks out the repo with plain `git` (`fetch` + `checkout`,
authenticated via a one-off `http.extraHeader`) rather than authenticated via a one-off `http.extraHeader`) rather than
+206 -19
View File
@@ -27,18 +27,27 @@ reload (e.g. Ctrl/Cmd+Shift+R) if that happens.
To enable the offline style, place a PMTiles file at To enable the offline style, place a PMTiles file at
`data/offline.pmtiles` (see `docs/architecture.adoc` for how it is `data/offline.pmtiles` (see `docs/architecture.adoc` for how it is
served and styled). It is not part of the repository — obtain or served and styled — the same OpenMapTiles schema/style the online map
build one locally: uses, just from local data). Logged-in users can generate this from
the app itself — see "In-app regeneration" below — or it can be built
by hand:
* The simplest source is a pre-built extract from * Download one or more country extracts from
https://app.protomaps.com[protomaps.com's extract builder] — pick a https://download.geofabrik.de[Geofabrik] (`.osm.pbf`, ODbL/OSM
region and download it as `.pmtiles`. Its schema is what attribution) — e.g.
`protomaps-themes-base` (the styling library used for the offline `curl -O https://download.geofabrik.de/europe/slovakia-latest.osm.pbf`.
style) expects. * If more than one country, merge them with
* To convert some other vector tile source (e.g. an `.mbtiles` file) https://github.com/osmcode/osmium-tool[osmium-tool]:
into PMTiles, use the `osmium merge slovakia-latest.osm.pbf austria-latest.osm.pbf -o merged.osm.pbf`
https://github.com/protomaps/go-pmtiles[go-pmtiles] CLI: (each Geofabrik file is already clipped to that country's real
`pmtiles convert in.mbtiles data/offline.pmtiles`. border, so no separate bbox/polygon clip step is needed either
before or after — skip this step entirely for a single country).
* Tile it with https://github.com/onthegomap/planetiler[Planetiler]'s
default profile, which *is* OpenMapTiles — no `--profile` flag
needed — writing PMTiles directly:
`java -jar planetiler.jar --osm-path=merged.osm.pbf --output=data/offline.pmtiles`
(needs Java 21+; `planetiler.jar` from
https://github.com/onthegomap/planetiler/releases/latest[the latest release]).
Once the file is in place, `GET /api/offline-tiles` reports Once the file is in place, `GET /api/offline-tiles` reports
`available: true` and the map defaults to the offline style. `available: true` and the map defaults to the offline style.
@@ -50,14 +59,192 @@ The sidebar's "Terrain shading" toggle works online out of the box
no API key). For it to work offline too, place a PMTiles archive of no API key). For it to work offline too, place a PMTiles archive of
Terrarium-encoded `raster-dem` tiles at `data/terrain.pmtiles` Terrarium-encoded `raster-dem` tiles at `data/terrain.pmtiles`
(`GET /api/terrain-tiles` reports `available: true` once it's there, (`GET /api/terrain-tiles` reports `available: true` once it's there,
same pattern as offline map data above). It is not part of the same pattern as offline map data above). Also generatable in-app (see
repository — build one locally from a DEM source (e.g. Copernicus below) or by hand, the same `go-pmtiles extract` mechanism as
GLO-30 or SRTM) with a tool like `data/offline.pmtiles` above — but from
https://github.com/mapbox/rio-rgbify[rio-rgbify] (GeoTIFF -> https://mapterhorn.com[Mapterhorn], a Protomaps-affiliated project
Terrarium-encoded PNG tiles in an `.mbtiles`), then publishing pre-built Terrarium-encoded terrain as PMTiles (a global
`pmtiles convert out.mbtiles data/terrain.pmtiles` with `planet.pmtiles`, z0-12, plus ~450 regional chunks covering z13 and up
https://github.com/protomaps/go-pmtiles[go-pmtiles], same as — see https://mapterhorn.com/data-access/[their own data-access page]
`data/offline.pmtiles`. for the full manifest and per-source license/attribution details, CC-BY-4.0
and similar, varying by region):
[source,shell]
----
pmtiles extract https://download.mapterhorn.com/planet.pmtiles planet-extract.pmtiles --bbox=west,south,east,north --maxzoom=13 --overfetch=0
# only if a regional chunk covers the bbox too (see download_urls.json) —
# most bboxes hit exactly one, a bbox straddling a chunk boundary hits two:
pmtiles extract https://download.mapterhorn.com/<chunk>.pmtiles chunk-extract.pmtiles --bbox=west,south,east,north --maxzoom=13 --overfetch=0
pmtiles merge planet-extract.pmtiles chunk-extract.pmtiles data/terrain.pmtiles
----
`--overfetch=0`: the default (0.05) merges nearby byte ranges into fewer,
bigger requests to save round-trips — for `planet.pmtiles` (global z0-12,
tens of GB) over a bbox spanning several countries this can collapse the
whole extract into a *single* multi-GB request with no way to resume after
a mid-transfer HTTP/2 stream reset. `--overfetch=0` trades that one
fragile mega-request for many small ones instead (spread over
`--download-threads`, default 4) — slower end to end, but a dropped
connection only loses one small request's worth of progress, not the
whole extract.
`--maxzoom=13`: raster DEM tiles don't compress away like vector ones do
(every tile is a real image, even over flat/empty terrain), so each extra
zoom level roughly quadruples storage — the app's own build
(`PmtilesBuilder::TERRAIN_MAX_ZOOM`) caps at 13, each regional chunk's
own native minzoom, rather than the online hillshade layer's z15. A
12-country selection at z15 was headed well past 181GB; z13 is still
plenty for the hillshade toggle and the planned slope readout.
Tiles are 512px WebP, not the 256px PNG this app's *online* Terrarium
source uses — `public/js/dem.js`'s own point-elevation lookups
(`shadow.js`'s terrain-shadow overlay) read the actual decoded tile
size rather than assuming one, so this isn't a problem either way.
== Offline hiking trails
The sidebar's "Hiking trails" overlay works online out of the box
(waymarkedtrails.org raster tiles, `public/js/hiking_trails.js`) —
clicking a trail fires a one-off Overpass query for its name/colour/
network/distance. For it to work offline too, one `data/hiking_trails/
<CODE>.geojson` per country is generated automatically as the last step
of every `offline.pmtiles` build (`Maptool::HikingTrails`,
`lib/maptool/hiking_trails.rb`), straight from the *same* per-country
`.osm.pbf` files already downloaded for `offline.pmtiles` itself — no
separate download, no third-party service:
[source,shell]
----
osmium tags-filter country.osm.pbf r/route=hiking -o hiking.osm.pbf
osmium add-locations-to-ways hiking.osm.pbf -o hiking-located.osm.pbf
osmium cat hiking-located.osm.pbf -f opl,locations_on_ways=true,add_metadata=false -o hiking.opl
----
`add-locations-to-ways` matters: a plain Geofabrik PBF's ways only carry
bare node *references*, not coordinates, so skipping this step silently
produces ways with empty x/y in the OPL output and every route gets
dropped for having no usable geometry — not an error, just wrong output
(verified against a real extract before this was caught). No general OSM
tool stitches a route relation's member ways into one line for you
(routes are a semantic grouping, not a geometry type), so
`HikingTrails.parse_opl` does that walk itself — the same thing
waymarkedtrails.org and this app's own online Overpass click handler
already do.
Best-effort per country: an extraction problem on one country's PBF logs
a warning and is skipped rather than failing the whole `offline.pmtiles`
build, or even the other countries in the same selection, over what's a
bonus overlay, not the core map (`PmtilesBuilder.extract_hiking_trails`).
Cached the same way `geofabrik_cache` is (skipped once a country's own
extract is already newer than its source `.osm.pbf`), so growing the
region selection by one country only extracts that one. Coordinates are
rounded to 5 decimal places (~1m, plenty for a rendered line) — Slovakia
alone is ~3000 routes, ~30MB unrounded.
One file per country rather than one combined file: `GET /api/hiking-trails`
lists which country codes have an extract (`countries: [...]`), and the
Overlays panel's "Hiking trails" item turns that into a checklist —
each ticked country fetches its own `GET /tiles/hiking_trails/<CODE>.geojson`
only then, not on page load and not for countries never ticked. Offline
click hit-tests the loaded vector layer(s) directly
(`queryRenderedFeatures`) instead of querying Overpass, so it resolves
instantly with no network at all. `rake hiking:trails[pbf,out]` runs the
extraction by hand against one `.osm.pbf` for testing.
`DELETE /api/offline-tiles` removes the whole `data/hiking_trails/`
directory along with `offline.pmtiles` and the Geofabrik cache — there's
no separate delete/rebuild route for it, since it's not an independent
build.
== In-app regeneration
A logged-in user can also generate both files above from the app
itself, no local tooling needed — the sidebar's "Offline data" icon
(Offline hub group) shows a checklist of European countries
(`lib/maptool/data/europe_countries.geojson`, Natural Earth 110m
admin-0, public domain) plus each file's status (size, last updated).
Ticking countries and clicking "Apply changes" saves the selection
(`data/offline_regions.json`, an array of ISO A2 codes) and rebuilds
both files to cover it.
There's no standalone per-country *built* file — `offline.pmtiles`/
`terrain.pmtiles` are always one combined extract for the *whole
current selection*. "Remove a country" is just "change the selection,
then Apply" (unticking a country and applying rebuilds without it).
terrain.pmtiles re-extracts its selection from scratch on
every Apply — fine given that extraction is byte-range requests
against a remote archive, not full downloads. offline.pmtiles, by
contrast, downloads a whole country's `.osm.pbf` from Geofabrik (can be
multi-GB), so it keeps a per-country cache under
`data/geofabrik_cache/` instead of re-fetching on every Apply — a
country already cached is only re-downloaded if Geofabrik's own
`Last-Modified` for it has moved on since (checked with a cheap `HEAD`,
see `PmtilesBuilder.cached_extract`), so growing or shrinking the
selection only touches whatever's actually new to it.
This wraps exactly the manual workflows documented above
(`lib/maptool/pmtiles_builder.rb`). offline.pmtiles downloads each
selected country's Geofabrik extract, `osmium merge`s them if there's
more than one, then tiles the result with Planetiler — the server
needs `osmium` and `java` + `planetiler.jar` installed and on `PATH`
(override with the `OSMIUM_BIN`/`JAVA_BIN`/`PLANETILER_JAR` env vars if
they live somewhere else; the Docker image installs all three, see
`Dockerfile`). terrain.pmtiles still uses `go-pmtiles`
(`PMTILES_BIN`) against Mapterhorn, unrelated to this — it extracts
`planet.pmtiles` (always) plus whichever Mapterhorn regional chunks
intersect the selection, `merge`s them if there was more than one —
always safe regardless of how many/which countries are selected, since
each chunk is Mapterhorn's own pre-partitioned, non-overlapping source
file (extracting the *same* source once per country and merging those,
by contrast, would fail — adjacent countries' region-extracts share
border tiles, since extraction copies whole tiles rather than clipping
vector content to the polygon, and `pmtiles merge` refuses any
overlapping tile id between inputs).
A build runs in the background (`GET /api/offline-regions` to list/see
the current selection, `POST` to change it, then `POST
/api/offline-tiles/build` / `/api/terrain-tiles/build`, polled via the
matching `*/build-status` route) — only one of each kind at a time;
starting a second while one's running gets a 409, as does starting
either with nothing selected yet. While running, `build-status` also
includes a `step` string (e.g. `"fetching PL (3/4) — done: SK, CZ —
pending: HU"`, or `"tiling with planetiler"`) — see
`PmtilesBuilder.run_job`'s `report_step`.
`DELETE /api/offline-tiles` removes offline.pmtiles, its sidecar meta,
and the whole per-country download cache (`data/geofabrik_cache/`) —
`DELETE /api/terrain-tiles` does the same for terrain.pmtiles. Both are
a manual "reclaim the space" / "start over" action (409 while that
kind's build is running), separate from the region picker's Apply,
which only changes what a *future* build covers and never frees
anything already on disk. `DELETE
/api/offline-tiles/cache/:code` removes just one country's cached
extract, e.g. to force a clean re-download of it without touching the
rest of the cache. `GET /api/offline-tiles/cache` lists what's
actually in that cache right now (code/size/Last-Modified per file) —
not the same thing as offline.pmtiles' own `meta.sources`, which only
reflects the last successful build: a country can be cached without
ever having been built (ticked, not yet Applied) or built from without
still being selected (unticked since). The region picker shows this
next to each country's checkbox, with its own delete button, rather
than only offering "delete everything" on the whole offline.pmtiles
row.
Each successful build also writes a small sidecar (`offline.pmtiles.
meta.json` — each source country's code/Geofabrik slug/Last-Modified;
`terrain.pmtiles.meta.json` — the Mapterhorn source names/md5sums)
recording what was actually built, since the `.pmtiles` file itself
carries no such record. `GET /api/offline-tiles`/`/api/terrain-tiles`
include it as `meta`, shown in the panel next to the existing
size/updated line. `GET .../check-update` compares that recorded build
against each source's live upstream state (a Geofabrik `HEAD` per
country for offline, the Mapterhorn manifest for terrain — without
rebuilding anything) and reports `update_available: true/false/null` —
`null` means "don't know" (no local build yet, or a check itself
failed), kept as its own route rather than folded into the plain
status endpoints above since those are on the page-load hot path (the
topbar's offline-status dot) and this one reaches an external service,
only worth doing when the Offline data panel is actually open.
== Camper-relevant POIs (Overpass) == Camper-relevant POIs (Overpass)
+64 -6
View File
@@ -24,8 +24,8 @@ checking here before searching from zero next time.
| MIT (per `package.json`); the whole `npmap5-plugins` repo is separately | MIT (per `package.json`); the whole `npmap5-plugins` repo is separately
public domain as a US government work public domain as a US government work
| Park Tiles color palette (offline + online basemap themes) | Park Tiles color palette (shared offline + online basemap theme)
| `public/js/map.js`'s `OFFLINE_THEME` and `ONLINE_STYLE_PATCH` | `public/js/map.js`'s `ONLINE_STYLE_PATCH`
| Colors only — see <<park-tiles>> for why the actual tile service isn't used | Colors only — see <<park-tiles>> for why the actual tile service isn't used
| `overview` (top-right minimap/inset control) | `overview` (top-right minimap/inset control)
@@ -187,7 +187,7 @@ why this app uses OpenFreeMap Liberty as its actual basemap, not Park
Tiles — see `docs/architecture.adoc`'s "Offline maps" section. Tiles — see `docs/architecture.adoc`'s "Offline maps" section.
Recreating the *color choices* is a different matter — not the kind of Recreating the *color choices* is a different matter — not the kind of
thing a data license restricts — and is what `OFFLINE_THEME` in thing a data license restricts — and is what `ONLINE_STYLE_PATCH` in
`public/js/map.js` actually does (see `docs/architecture.adoc`). `public/js/map.js` actually does (see `docs/architecture.adoc`).
=== Public style JSON (source for the real colors we did pull) === Public style JSON (source for the real colors we did pull)
@@ -219,11 +219,11 @@ embedded credentials, even when technically reachable:
|=== |===
Colors actually pulled from the Imagery style JSON and used in Colors actually pulled from the Imagery style JSON and used in
`OFFLINE_THEME`: background (`hsl(40, 37%, 97%)`, a warm cream), the `ONLINE_STYLE_PATCH`: background (`hsl(40, 37%, 97%)`, a warm cream), the
park/protected-area green (`#b3e378`), the two road-importance tiers park/protected-area green (`#b3e378`), the two road-importance tiers
(`#fdefbe`/`#fef7dd`), and the park-boundary line color (`#546619`, dark (`#fdefbe`/`#fef7dd`), and the park-boundary line color (`#546619`, dark
olive). The rest of `OFFLINE_THEME` extends that same warm/olive family olive). The rest of `ONLINE_STYLE_PATCH` extends that same warm/olive
by eye rather than from a sourced value. family by eye rather than from a sourced value.
=== Legend / cartographic hierarchy === Legend / cartographic hierarchy
@@ -259,6 +259,20 @@ own work (a map viewer, not a publisher of NPS-branded content):
own UI chrome (currently plain system fonts, `public/css/style.css`) — own UI chrome (currently plain system fonts, `public/css/style.css`) —
no current use case, but no licensing blocker either, worth no current use case, but no licensing blocker either, worth
remembering if that comes up. remembering if that comes up.
* *Another open alternative, for display use*: https://nationalparktypeface.com[National Park]
— a separate, unrelated project (Ben Hoepner, Andrea Herstowski, Jeremy
Shellhorn / DO Studio), also SIL OFL licensed, also original ("inspired
by" park signage, not a Rawlinson reproduction). Ships as variable
`.ttf`, `.woff`/`.woff2`, and `.otf` — self-hostable via `@font-face`
the same way. Where it differs from Sequoia Sans above: it's a
router-carved-signage *display* face (the chunky all-caps look of
actual trailhead signs), not a general body/UI sans — best fit would be
a wordmark/heading use (e.g. the topbar's "MAPTOOL" text) alongside a
more legible sans for body copy, not a full typeface swap. Now used for
exactly that: `.topbar__brand` only (`public/css/style.css`), Bold
weight vendored under `public/vendor/national-park-typeface/` — see
`docs/third-party-notices.adoc`. Body copy elsewhere still plain system
fonts, unchanged.
* *Hawaiian typography*: NPS Rawlinson includes vowels with Hawaiian * *Hawaiian typography*: NPS Rawlinson includes vowels with Hawaiian
macrons (ā, ē, ī, ō, ū) and the okina (ʻ); the regular weight of macrons (ā, ē, ī, ō, ū) and the okina (ʻ); the regular weight of
Frutiger doesn't, so NPS uses a separate "HFrutiger" weight for Frutiger doesn't, so NPS uses a separate "HFrutiger" weight for
@@ -272,6 +286,50 @@ own work (a map viewer, not a publisher of NPS-branded content):
resource — included here only because it was shared alongside the resource — included here only because it was shared alongside the
typography notes above. typography notes above.
== Arrowhead (shield) logo colors
Unrelated to anything this app renders — noted here only because it came up
in conversation (wanting exact colors to 3D-print the NPS arrowhead) and
this doc is already the dumping ground for NPS-ecosystem color/graphic-identity
facts. Per NPS's own graphic identity standards (Harpers Ferry Center), the
arrowhead uses three official Pantone spot colors plus white:
[cols="2,2,1,1"]
|===
| Element | Pantone | Hex (converted) | Nearest RAL Classic
| Mountain
| Dark Rust, PMS 1615 C
| `#8b4720`
| RAL 2013 Pearl orange (`#923E25`)
| Sky
| Medium Rust, PMS 1605 C
| `#a65523`
| RAL 8023 Orange brown (`#A45729`)
| Trees / grass
| Dark Green, PMS 553 C
| `#284734`
| RAL 6012 Black green (`#2F3D3A`)
| Bison, lake, snowfield, type
| White
| `#ffffff`
| RAL 9016 Traffic white (`#F1F1EA`) — RAL has no pure white
|===
Hex values are third-party Pantone→RGB conversions, not something NPS
publishes directly (their style guide only specifies PMS codes). RAL codes
are the closest match by RGB distance against the RAL Classic chart — same
caveat, not an official NPS-endorsed mapping, just the nearest available
code for filament/paint shopping. Source: NPS Graphic Identity and Style
Guides (https://www.nps.gov/subjects/hfc/nps-graphic-identity-and-style-guides.htm),
FAQs About Arrowhead Artwork
(https://www.nps.gov/subjects/hfc/faqs-about-arrowhead-artwork.htm). Use of
the arrowhead symbol itself is controlled by law/regulation/policy — see
the FAQ page before printing/distributing anything bearing it.
== NPMap4 (the older, Leaflet-based generation) == NPMap4 (the older, Leaflet-based generation)
https://www.nps.gov/lib/npmap.js/4.2.1/ — predecessor to npmap5, https://www.nps.gov/lib/npmap.js/4.2.1/ — predecessor to npmap5,
+95 -8
View File
@@ -18,9 +18,9 @@ BSD-3-Clause licensed.
| 4.5.0 | 4.5.0
| https://github.com/protomaps/PMTiles | https://github.com/protomaps/PMTiles
| protomaps-themes-base | maplibre-contour (Overlays panel's "Contour lines", `public/js/contour.js`)
| 4.5.0 | 0.1.0
| https://github.com/protomaps/basemaps | https://github.com/onthegomap/maplibre-contour
|=== |===
Also vendored under `public/vendor/`, but not BSD-3-Clause like the Also vendored under `public/vendor/`, but not BSD-3-Clause like the
@@ -51,6 +51,34 @@ overview's polar pan limit, but MapLibre 6's `Map` no longer exposes
`.transform` at all — guarded to skip that (cosmetic) adjustment `.transform` at all — guarded to skip that (cosmetic) adjustment
instead of throwing when it's absent. instead of throwing when it's absent.
Also vendored under `public/vendor/` — MIT-licensed, attribution
required (`LICENSE` included alongside):
[cols="1,1,2"]
|===
| Library | Version | Source
| maplibre-gl-lanes (offline hiking trails, `public/js/hiking_trails.js`
— trails sharing a path drawn as parallel lanes); its `dist/*.mjs`
build, minus the trailing `sourceMappingURL` comment
| 1.3.0
| https://github.com/c0nsumer/maplibre-gl-lanes
|===
Also vendored under `public/vendor/` — MIT-licensed (© 2013 Panayiotis
Lipiridis), attribution required (this notice satisfies that):
[cols="1,1,2"]
|===
| Library | Version | Source
| flag-icons (just the square `1x1/` crop, only the 39 European
countries `lib/maptool/data/europe_countries.geojson` lists — see
`public/js/offline_data.js`)
| 7.5.0
| https://github.com/lipis/flag-icons
|===
Offline map text labels need font glyphs too (see Offline map text labels need font glyphs too (see
`docs/architecture.adoc`), vendored under `public/vendor/fonts/` — `docs/architecture.adoc`), vendored under `public/vendor/fonts/` —
SIL Open Font License, not BSD-3-Clause like the above: SIL Open Font License, not BSD-3-Clause like the above:
@@ -63,13 +91,72 @@ SIL Open Font License, not BSD-3-Clause like the above:
`256-511` only) `256-511` only)
| https://github.com/protomaps/basemaps-assets[protomaps/basemaps-assets] | https://github.com/protomaps/basemaps-assets[protomaps/basemaps-assets]
(`fonts/`, `OFL.txt` included alongside) (`fonts/`, `OFL.txt` included alongside)
| National Park (Bold weight only, `.woff2`), topbar wordmark
(`.topbar__brand`, `public/css/style.css`) — see
`docs/nps-resources.adoc`
| https://nationalparktypeface.com[nationalparktypeface.com] (Ben
Hoepner, Andrea Herstowski, Jeremy Shellhorn / DO Studio) — "published
for free use under the SIL Open Font License" per the site; no
`OFL.txt` ships in its own download zips, unlike Noto Sans above
|=== |===
The online map style (not vendored, fetched at runtime when the The map style — https://tiles.openfreemap.org/styles/liberty[OpenFreeMap
"Online" layer is selected) is "liberty"], built from https://www.openstreetmap.org[OpenStreetMap]
https://tiles.openfreemap.org/styles/liberty[OpenFreeMap "liberty"], data — is used two ways (see `docs/architecture.adoc`): fetched live
built from https://www.openstreetmap.org[OpenStreetMap] data. at runtime for the "Online" layer, and as a one-time vendored snapshot
(`public/vendor/liberty-style/liberty.json`) that the "Offline" layer
repoints at local data instead. Its icon sprite is vendored the same
way, under `public/vendor/liberty-sprite/` (`ofm.json`/`ofm.png` +
`@2x` variants, from `tiles.openfreemap.org/sprites/`). Neither has
been re-checked against a specific upstream version pin the way the
table above does — re-fetch both from `tiles.openfreemap.org` if
Liberty's own style/sprite ever visibly drifts from what's vendored.
`data/offline.pmtiles` (`lib/maptool/pmtiles_builder.rb`'s in-app
regeneration, or the manual recipe in `docs/development.adoc`) is
built at build time, not vendored, from
https://download.geofabrik.de[Geofabrik]'s per-country OpenStreetMap
extracts (ODbL).
`terrain.pmtiles` (same regeneration mechanism) is extracted at build
time, not vendored, from https://mapterhorn.com[Mapterhorn] — a
Protomaps-affiliated project publishing pre-built Terrarium-encoded
terrain as PMTiles. Per-region source licenses vary (CC-BY-4.0 and
similar); see
https://download.mapterhorn.com/attribution.json[their own attribution
list] for the full provenance.
Not a frontend asset like the above, but also baked into this app's own
Docker image (not a separate container the way kiwix-serve/gpsd are —
see `docs/deployment.adoc`/`docs/development.adoc`): three CLI tools
`lib/maptool/pmtiles_builder.rb` shells out to for offline.pmtiles/
terrain.pmtiles regeneration.
[cols="1,1,1,2"]
|===
| Tool | Version | License | Source
| go-pmtiles (`pmtiles` CLI)
| 1.31.2
| BSD-3-Clause
| https://github.com/protomaps/go-pmtiles
| Planetiler (`planetiler.jar`)
| 0.10.2
| Apache-2.0
| https://github.com/onthegomap/planetiler
| osmium-tool (`osmium`, installed via the `osmium-tool` Debian package)
| (Debian trixie's packaged version)
| GPL-3.0
| https://github.com/osmcode/osmium-tool
|===
To update a vendored library: download its package (e.g. To update a vendored library: download its package (e.g.
`npm pack <name>@<version>`), replace the corresponding files under `npm pack <name>@<version>`), replace the corresponding files under
`public/vendor/<name>/`, and update the version in this file. `public/vendor/<name>/`, and update the version in this file. To
update go-pmtiles or Planetiler, bump `PMTILES_VERSION`/
`PLANETILER_VERSION` in the `Dockerfile` and the version here —
osmium-tool tracks whatever version Debian trixie currently packages,
nothing to pin on this side.
@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"name":"Albania","iso_a2":"AL","iso_a3":"ALB"},"geometry":{"type":"Polygon","coordinates":[[[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.371768,41.877551],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855409],[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727]]]}},{"type":"Feature","properties":{"name":"Austria","iso_a2":"AT","iso_a3":"AUT"},"geometry":{"type":"Polygon","coordinates":[[[16.979667,48.123497],[16.903754,47.714866],[16.340584,47.712902],[16.534268,47.496171],[16.202298,46.852386],[16.011664,46.683611],[15.137092,46.658703],[14.632472,46.431817],[13.806475,46.509306],[12.376485,46.767559],[12.153088,47.115393],[11.164828,46.941579],[11.048556,46.751359],[10.442701,46.893546],[9.932448,46.920728],[9.47997,47.10281],[9.632932,47.347601],[9.594226,47.525058],[9.896068,47.580197],[10.402084,47.302488],[10.544504,47.566399],[11.426414,47.523766],[12.141357,47.703083],[12.62076,47.672388],[12.932627,47.467646],[13.025851,47.637584],[12.884103,48.289146],[13.243357,48.416115],[13.595946,48.877172],[14.338898,48.555305],[14.901447,48.964402],[15.253416,49.039074],[16.029647,48.733899],[16.499283,48.785808],[16.960288,48.596982],[16.879983,48.470013],[16.979667,48.123497]]]}},{"type":"Feature","properties":{"name":"Belarus","iso_a2":"BY","iso_a3":"BLR"},"geometry":{"type":"Polygon","coordinates":[[[28.176709,56.16913],[29.229513,55.918344],[29.371572,55.670091],[29.896294,55.789463],[30.873909,55.550976],[30.971836,55.081548],[30.757534,54.811771],[31.384472,54.157056],[31.791424,53.974639],[31.731273,53.794029],[32.405599,53.618045],[32.693643,53.351421],[32.304519,53.132726],[31.49764,53.16743],[31.305201,53.073996],[31.540018,52.742052],[31.78597,52.10168],[31.785992,52.101678],[30.927549,52.042353],[30.619454,51.822806],[30.555117,51.319503],[30.157364,51.416138],[29.254938,51.368234],[28.992835,51.602044],[28.617613,51.427714],[28.241615,51.572227],[27.454066,51.592303],[26.337959,51.832289],[25.327788,51.910656],[24.553106,51.888461],[24.005078,51.617444],[23.527071,51.578454],[23.508002,52.023647],[23.199494,52.486977],[23.799199,52.691099],[23.804935,53.089731],[23.527536,53.470122],[23.484128,53.912498],[24.450684,53.905702],[25.536354,54.282423],[25.768433,54.846963],[26.588279,55.167176],[26.494331,55.615107],[27.10246,55.783314],[28.176709,56.16913]]]}},{"type":"Feature","properties":{"name":"Belgium","iso_a2":"BE","iso_a3":"BEL"},"geometry":{"type":"Polygon","coordinates":[[[6.156658,50.803721],[6.043073,50.128052],[5.782417,50.090328],[5.674052,49.529484],[4.799222,49.985373],[4.286023,49.907497],[3.588184,50.378992],[3.123252,50.780363],[2.658422,50.796848],[2.513573,51.148506],[3.314971,51.345781],[3.315011,51.345777],[3.314971,51.345755],[4.047071,51.267259],[4.973991,51.475024],[5.606976,51.037298],[6.156658,50.803721]]]}},{"type":"Feature","properties":{"name":"Bosnia and Herz.","iso_a2":"BA","iso_a3":"BIH"},"geometry":{"type":"Polygon","coordinates":[[[18.56,42.65],[17.674922,43.028563],[17.297373,43.446341],[16.916156,43.667722],[16.456443,44.04124],[16.23966,44.351143],[15.750026,44.818712],[15.959367,45.233777],[16.318157,45.004127],[16.534939,45.211608],[17.002146,45.233777],[17.861783,45.06774],[18.553214,45.08159],[19.005485,44.860234],[19.00548,44.86023],[19.36803,44.863],[19.11761,44.42307],[19.59976,44.03847],[19.454,43.5681],[19.21852,43.52384],[19.03165,43.43253],[18.70648,43.20011],[18.56,42.65]]]}},{"type":"Feature","properties":{"name":"Bulgaria","iso_a2":"BG","iso_a3":"BGR"},"geometry":{"type":"Polygon","coordinates":[[[22.65715,44.234923],[22.944832,43.823785],[23.332302,43.897011],[24.100679,43.741051],[25.569272,43.688445],[26.065159,43.943494],[27.2424,44.175986],[27.970107,43.812468],[28.558081,43.707462],[28.039095,43.293172],[27.673898,42.577892],[27.99672,42.007359],[27.135739,42.141485],[26.117042,41.826905],[26.106138,41.328899],[25.197201,41.234486],[24.492645,41.583896],[23.692074,41.309081],[22.952377,41.337994],[22.881374,41.999297],[22.380526,42.32026],[22.545012,42.461362],[22.436595,42.580321],[22.604801,42.898519],[22.986019,43.211161],[22.500157,43.642814],[22.410446,44.008063],[22.65715,44.234923]]]}},{"type":"Feature","properties":{"name":"Croatia","iso_a2":"HR","iso_a3":"HRV"},"geometry":{"type":"Polygon","coordinates":[[[16.564808,46.503751],[16.882515,46.380632],[17.630066,45.951769],[18.456062,45.759481],[18.829825,45.908872],[19.072769,45.521511],[19.390476,45.236516],[19.005485,44.860234],[18.553214,45.08159],[17.861783,45.06774],[17.002146,45.233777],[16.534939,45.211608],[16.318157,45.004127],[15.959367,45.233777],[15.750026,44.818712],[Line truncated
+181
View File
@@ -0,0 +1,181 @@
require "open3"
require "tmpdir"
require "json"
require_relative "track_stats"
module Maptool
# Prototype: extracts route=hiking relations (name/colour/network/operator,
# full member-way geometry) straight out of a Geofabrik .osm.pbf — the same
# file PmtilesBuilder already downloads per country for offline.pmtiles, so
# this needs no separate download or third-party service. Three osmium
# subprocess steps do the heavy lifting (osmium is already a server
# dependency, see PmtilesBuilder/docs/development.adoc):
#
# 1. `osmium tags-filter FILE r/route=hiking -o hiking.osm.pbf` — pulls
# out just the hiking relations *and* (by default, no -R) every way/
# node they reference, so the output is small and self-contained.
# 2. `osmium add-locations-to-ways` — a plain Geofabrik PBF's ways only
# carry bare node *references* (ids), not coordinates; this resolves
# each way's node ids against the node objects in the same file and
# rewrites the way with real coordinates attached. Verified against a
# real extract (Macedonia) that skipping this step silently produces
# ways with empty x/y (`n123xy`, no numbers) in the OPL output below —
# not an error, just wrong output, so easy to miss without checking.
# 3. `osmium cat hiking.osm.pbf -f opl,locations_on_ways=true,add_metadata=false`
# — OPL is a plain-text, one-object-per-line format (see
# https://osmcode.org/opl-file-format/); locations_on_ways (now that
# step 2 actually populated them) embeds each way's node coordinates
# directly in its own line, so no separate node-id -> [lon,lat] table
# needs building, just two passes over text.
#
# No general OSM tool stitches a route relation's member ways into one
# line for you (routes are a semantic grouping, not a geometry type) —
# every hiking-trail renderer, including this app's own Overpass-based
# click handler (public/js/hiking_trails.js) and waymarkedtrails.org
# itself, does this same member-way walk by hand.
module HikingTrails
class ExtractError < StandardError; end
ROUTE_TAGS = %w[name description ref colour network operator].freeze
def self.osmium_bin
ENV.fetch("OSMIUM_BIN", "osmium")
end
# pbf_path: a Geofabrik-style .osm.pbf, e.g. one of
# PmtilesBuilder.cached_extracts' own files. Returns a GeoJSON
# FeatureCollection (MultiLineString per relation, one sub-line per
# member way — not one big concatenated LineString, since a relation's
# ways aren't always contiguous/ordered and jumping across a gap would
# draw a spurious straight connector).
def self.extract(pbf_path)
Dir.mktmpdir("maptool-hiking-trails") do |dir|
filtered = File.join(dir, "hiking.osm.pbf")
run!(osmium_bin, "tags-filter", pbf_path, "r/route=hiking", "-o", filtered)
located = File.join(dir, "hiking-located.osm.pbf")
run!(osmium_bin, "add-locations-to-ways", filtered, "-o", located)
opl_path = File.join(dir, "hiking.opl")
run!(osmium_bin, "cat", located, "-f", "opl,locations_on_ways=true,add_metadata=false", "-o", opl_path)
parse_opl(File.read(opl_path))
end
end
# Pure — no subprocess — so it's cheaply testable against a canned OPL
# fixture, same "keep the real logic testable without the real tool
# installed" reasoning OverpassPois.to_geojson already gives for staying
# separate from OverpassPois.fetch.
def self.parse_opl(text)
ways = {} # way id -> [[lon, lat], ...]
relations = [] # { tags:, members: [{type:, id:}, ...] }
text.each_line do |line|
fields = line.chomp.split(" ")
head = fields.first
next unless head
case head[0]
when "w"
ways[head[1..].to_i] = way_coordinates(fields)
when "r"
tags = tags_field(fields)
relations << { id: head[1..].to_i, tags: tags, members: members_field(fields) } if tags["route"] == "hiking"
end
end
features = relations.filter_map { |relation| relation_feature(relation, ways) }
{ "type" => "FeatureCollection", "features" => features }
end
def self.relation_feature(relation, ways)
lines = relation[:members]
.select { |member| member[:type] == "w" }
.filter_map { |member| ways[member[:id]] }
.select { |coords| coords.size >= 2 }
return nil if lines.empty?
distance = lines.sum { |coords| TrackStats.compute(coords)["distance_meters"] }
tags = relation[:tags]
properties = {
"id" => relation[:id],
"name" => tags["name"] || tags["description"],
"distance_meters" => distance.round(1),
}
ROUTE_TAGS.each { |key| properties[key] ||= tags[key] }
{
"type" => "Feature",
"properties" => properties.compact,
# Rounded to 5 decimal places (~1m) — full OSM float precision
# (~1cm) is pointless for a rendered line and roughly doubles the
# size of every coordinate pair once this many-country GeoJSON is
# actually served to a browser.
"geometry" => { "type" => "MultiLineString", "coordinates" => round_coordinates(lines) },
}
end
private_class_method :relation_feature
def self.round_coordinates(lines)
lines.map { |coords| coords.map { |lon, lat| [lon.round(5), lat.round(5)] } }
end
private_class_method :round_coordinates
def self.field(fields, letter)
fields.find { |f| f.start_with?(letter) }
end
private_class_method :field
def self.tags_field(fields)
raw = field(fields, "T")&.delete_prefix("T") || ""
return {} if raw.empty?
raw.split(",").to_h { |pair| pair.split("=", 2).map { |part| unescape(part.to_s) } }
end
private_class_method :tags_field
def self.members_field(fields)
raw = field(fields, "M")&.delete_prefix("M") || ""
return [] if raw.empty?
raw.split(",").map do |member|
{ type: member[0], id: member[1..].split("@", 2).first.to_i }
end
end
private_class_method :members_field
# "n123x14.123y49.456,n124x14.130y49.460,..." -> [[14.123, 49.456], ...].
# A member id with no embedded location (shouldn't happen with
# locations_on_ways=true and -R omitted, but a way with a dangling/
# missing node reference is possible in real-world data) is skipped
# rather than raising — one bad node shouldn't drop the whole route.
def self.way_coordinates(fields)
raw = field(fields, "N")&.delete_prefix("N") || ""
return [] if raw.empty?
raw.split(",").filter_map do |node_ref|
match = node_ref.match(/\An\d+x(?<lon>-?[\d.]+)y(?<lat>-?[\d.]+)\z/)
[match[:lon].to_f, match[:lat].to_f] if match
end
end
private_class_method :way_coordinates
# OPL escapes space/newline/comma/equals/@/% as %<hex>% (see
# https://osmcode.org/opl-file-format/#_encoding) — only tag keys/
# values and usernames can contain them; the field-letter prefixes and
# id/comma/@ separators we split on above are never themselves escaped.
def self.unescape(str)
str.gsub(/%([0-9a-fA-F]{1,6})%/) { [Regexp.last_match(1).to_i(16)].pack("U") }
end
private_class_method :unescape
def self.run!(*args)
stdout, stderr, status = Open3.capture3(*args)
raise ExtractError, "#{args.first} exited with status #{status.exitstatus}: #{[stdout, stderr].map(&:strip).reject(&:empty?).join("\n")}" unless status.success?
rescue Errno::ENOENT, Errno::EACCES => e
raise ExtractError, "could not run '#{args.first}' (#{e.message}) — is osmium-tool installed and on PATH?"
end
private_class_method :run!
end
end
+85
View File
@@ -1,4 +1,5 @@
require "net/http" require "net/http"
require "rexml/document"
module Maptool module Maptool
# A reachability check plus a passthrough proxy for an external # A reachability check plus a passthrough proxy for an external
@@ -10,6 +11,13 @@ module Maptool
# *this* app, leaving no address a browser could reach kiwix-serve at # *this* app, leaving no address a browser could reach kiwix-serve at
# directly. GET /kiwix/* in app.rb calls .proxy below instead. # directly. GET /kiwix/* in app.rb calls .proxy below instead.
module Kiwix module Kiwix
# <meta name="geo.position" content="LAT;LON"> — a standard tag
# Wikipedia's own MediaWiki geo extension puts on every geotagged
# article's page (confirmed live against a real ZIM), semicolon rather
# than comma-separated. Far simpler to match than the human-facing
# "48°08′46″N 17°10′38″E / 48.14611°N 17.17722°E" markup the article
# body itself renders (DMS *and* decimal, in varying wrapper markup).
GEO_POSITION_PATTERN = /<meta name="geo\.position" content="(-?[\d.]+);(-?[\d.]+)"/
def self.reachable?(url, open_timeout: 1, read_timeout: 2) def self.reachable?(url, open_timeout: 1, read_timeout: 2)
return false if url.to_s.strip.empty? return false if url.to_s.strip.empty?
@@ -41,5 +49,82 @@ module Maptool
rescue StandardError rescue StandardError
nil nil
end end
# bbox: [south, west, north, east] — the "Wikipedia" map layer
# (public/js/gpx.js) refetches this on every pan/zoom for whatever's
# currently on screen. kiwix-serve's own geo search only *filters* by
# distance from a point, it doesn't echo each result's own coordinates
# back (confirmed live: neither its HTML nor its ?format=xml output
# carries one) — so every matching article's page is fetched too, and
# its coordinates read from GEO_POSITION_PATTERN above. Cheap since
# kiwix-serve serves its ZIM locally with no network round trip, unlike
# this same fetch-every-result shape would be against live Wikipedia.
def self.nearby(base_url, bbox, limit: 30, open_timeout: 2, read_timeout: 10)
south, west, north, east = bbox
lat = (south + north) / 2.0
lng = (west + east) / 2.0
radius_m = (haversine_km(lat, lng, north, east) * 1000).round
query = URI.encode_www_form(pattern: "", latitude: lat, longitude: lng, distance: radius_m, pageLength: limit, format: "xml")
response = proxy(base_url, "/kiwix/search", query, open_timeout: open_timeout, read_timeout: read_timeout)
return empty_collection unless response&.code == "200"
# One request per result — tried running these in parallel threads
# first (each is blocking I/O, so MRI's GIL releases during the
# actual wait), but measured no improvement against the real
# production ZIM (kiwix-serve itself appears to serialize incoming
# requests regardless), so plain sequential stayed. ~1.6s for a
# 30-result page on that box — fine for a refetch-on-pan-end layer,
# not for anything tighter; lower `limit` first if that's ever not
# enough headroom, before reaching for something more involved.
features = parse_search_results(response.body).filter_map { |result| point_feature(base_url, result, open_timeout, read_timeout) }
{ "type" => "FeatureCollection", "features" => features }
end
def self.empty_collection
{ "type" => "FeatureCollection", "features" => [] }
end
private_class_method :empty_collection
def self.parse_search_results(xml)
doc = REXML::Document.new(xml)
(doc.root&.get_elements("channel/item") || []).filter_map do |item|
title = item.elements["title"]&.text
link = item.elements["link"]&.text
{ title: title, link: link } if title && link
end
rescue REXML::ParseException
[]
end
private_class_method :parse_search_results
# result[:link] arrives already percent-encoded, straight out of
# kiwix-serve's own XML (e.g. "/kiwix/content/wikipedia/%C5%A0tadi%C3%B3n_Rapid")
# — decoded first so .proxy's own escaping (built for Sinatra's
# already-*decoded* splat, see its own comment above) doesn't double-
# encode the "%" and 404 the request.
def self.point_feature(base_url, result, open_timeout, read_timeout)
response = proxy(base_url, URI::RFC2396_PARSER.unescape(result[:link]), nil, open_timeout: open_timeout, read_timeout: read_timeout)
return nil unless response&.code == "200"
match = response.body.match(GEO_POSITION_PATTERN)
return nil unless match
{
"type" => "Feature",
"properties" => { "name" => result[:title], "link" => result[:link] },
"geometry" => { "type" => "Point", "coordinates" => [match[2].to_f, match[1].to_f] },
}
end
private_class_method :point_feature
def self.haversine_km(lat1, lng1, lat2, lng2)
earth_radius_km = 6371.0
dlat = (lat2 - lat1) * Math::PI / 180
dlng = (lng2 - lng1) * Math::PI / 180
a = (Math.sin(dlat / 2)**2) + (Math.cos(lat1 * Math::PI / 180) * Math.cos(lat2 * Math::PI / 180) * (Math.sin(dlng / 2)**2))
2 * earth_radius_km * Math.asin(Math.sqrt(a))
end
private_class_method :haversine_km
end end
end end
+47 -7
View File
@@ -1,5 +1,6 @@
require "net/http" require "net/http"
require "json" require "json"
require "time"
module Maptool module Maptool
# Fetches a Garmin LiveTrack session's track and metadata. # Fetches a Garmin LiveTrack session's track and metadata.
@@ -18,7 +19,7 @@ module Maptool
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
PUSH_PATTERN = /self\.__next_f\.push\((\[.*?\])\)<\/script>/m PUSH_PATTERN = /self\.__next_f\.push\((\[.*?\])\)<\/script>/m
Result = Struct.new(:points, :session, keyword_init: true) Result = Struct.new(:points, :session, :course, keyword_init: true)
def self.fetch(session_id, token) def self.fetch(session_id, token)
new.fetch(session_id, token) new.fetch(session_id, token)
@@ -29,6 +30,10 @@ module Maptool
# if that part wasn't found even though points were (seen in practice: # if that part wasn't found even though points were (seen in practice:
# session details are consistently present alongside points, but nothing # session details are consistently present alongside points, but nothing
# here depends on that holding forever). # here depends on that holding forever).
# `course`: the planned route (name/geoPoints), or nil — only present
# when the activity was actually started from a saved Garmin course
# (session["courseIds"] is how the page itself knows to fetch one; a
# plain unplanned activity has neither).
def fetch(session_id, token) def fetch(session_id, token)
payloads = parse_payloads(fetch_html(session_id, token)) payloads = parse_payloads(fetch_html(session_id, token))
@@ -36,18 +41,42 @@ module Maptool
raise FetchError, "no track points found (session invalid or expired?)" unless points raise FetchError, "no track points found (session invalid or expired?)" unless points
session = search(payloads) { |node| node if node["sessionName"] } session = search(payloads) { |node| node if node["sessionName"] }
# First of possibly several — a LiveTrack session only ever follows
# the one course it was started from.
course = search(payloads) { |node| node["courses"].first if node["courses"].is_a?(Array) && !node["courses"].empty? }
Result.new(points: clean(points), session: clean(session)) Result.new(points: clean(points), session: clean(session), course: clean(course))
end end
# Distance/duration/activity type come from the last track point (its # Distance/duration/activity type come from the last track point (its
# totals are cumulative for the whole session), name/type from the # totals are cumulative for the whole session), name/type from the
# session details — nil-safe throughout since either can be missing. # session details — nil-safe throughout since either can be missing.
# #
# "active": Garmin doesn't set a session's `end` until the activity is # "active": originally `session["end"].nil?`, on the assumption Garmin
# actually stopped, so its absence means the session is still recording # doesn't set `end` until the activity actually stops — disproven
# — inferred from the one (already-ended) session this was built # against a real still-recording session (2026-09-20): `end` was
# against, not verified against a session actually caught mid-activity. # already non-nil, set to exactly `start + 24h`, the share link's own
# expiry, not an actual end time. Two signals now, both verified live
# against that same session:
#
# * `end` itself, once it's actually in the past — watched the same
# session update it from that 24h placeholder to within a second of
# the last point's own time, immediately after the activity was
# saved on the watch. Authoritative and near-instant when present,
# so this is checked first.
# * failing that (still showing the future placeholder — an abandoned
# session never gets saved, so `end` never moves), recency of the
# last track point: Garmin posts one roughly every
# `postTrackPointFrequency` seconds (~15, observed) while genuinely
# still recording, so one older than ACTIVE_WITHIN means signal was
# lost a while ago (same "looks ended until it isn't" behavior
# Garmin's own LiveTrack page shows in that case).
#
# Both must agree the session is live — `end` already in the past
# means stopped even if the last point is only seconds old (exactly
# the save-immediately-after-the-last-point case above).
ACTIVE_WITHIN = 30 * 60 # seconds
def self.summarize(result) def self.summarize(result)
last = result.points.last || {} last = result.points.last || {}
session = result.session || {} session = result.session || {}
@@ -56,12 +85,23 @@ module Maptool
"activity_type" => last["activityType"], "activity_type" => last["activityType"],
"distance_meters" => last["totalDistanceMeters"], "distance_meters" => last["totalDistanceMeters"],
"duration_secs" => last["totalDurationSecs"], "duration_secs" => last["totalDurationSecs"],
"active" => session["end"].nil?, "active" => active?(last["dateTime"], session["end"]),
"start" => session["start"], "start" => session["start"],
"end" => session["end"], "end" => session["end"],
} }
end end
def self.active?(last_point_time, session_end)
return false unless last_point_time
now = Time.now.utc
return false if session_end && Time.parse(session_end) <= now
Time.parse(last_point_time) > now - ACTIVE_WITHIN
rescue ArgumentError, TypeError
false
end
private private
# The RSC payload serializes an absent field as the literal string # The RSC payload serializes an absent field as the literal string
+39 -5
View File
@@ -12,7 +12,15 @@ module Maptool
module OverpassPois module OverpassPois
class FetchError < StandardError; end class FetchError < StandardError; end
OVERPASS_API = "https://overpass-api.de/api/interpreter".freeze # Same three public mirrors public/js/hiking_trails.js races — tried in
# turn here instead, falling through to the next on any failure: the
# shared free instances regularly 504/429 under load, and a plugin run
# is a one-off click, not worth threads to race them.
OVERPASS_APIS = [
"https://overpass-api.de/api/interpreter",
"https://overpass.kumi.systems/api/interpreter",
"https://overpass.private.coffee/api/interpreter",
].freeze
# Each entry: an Overpass tag filter -> { label:, icon:, color: }, used # Each entry: an Overpass tag filter -> { label:, icon:, color: }, used
# both to build the query (unioning every filter) and to label/style # both to build the query (unioning every filter) and to label/style
@@ -53,8 +61,23 @@ module Maptool
# bbox: [south, west, north, east] (Overpass's own axis order). # bbox: [south, west, north, east] (Overpass's own axis order).
def self.fetch(bbox, timeout: 60) def self.fetch(bbox, timeout: 60)
query = build_query(bbox, timeout: timeout) query = build_query(bbox, timeout: timeout)
uri = URI(OVERPASS_API) errors = OVERPASS_APIS.map do |url|
response = Net::HTTP.post_form(uri, "data" => query) return post(URI(url), query, timeout)
rescue FetchError, Net::OpenTimeout, Net::ReadTimeout, SystemCallError, SocketError, OpenSSL::SSL::SSLError, JSON::ParserError => e
"#{URI(url).host}: #{e.message}"
end
raise FetchError, errors.join("; ")
end
def self.post(uri, query, timeout)
# overpass-api.de answers Net::HTTP's default "Ruby" User-Agent with
# a 406 (verified 2026-10-09) — any identifying UA gets through.
request = Net::HTTP::Post.new(uri, "User-Agent" => "maptool (Overpass camper POIs plugin)")
request.set_form_data("data" => query)
# read_timeout past the query's own [timeout:] — Net::HTTP's default
# 60s equals it, so a big viewport could hit Net::ReadTimeout just as
# Overpass was about to answer (a country-sized bbox measured ~57s).
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: timeout + 30) { |http| http.request(request) }
raise FetchError, "unexpected response #{response.code}" unless response.is_a?(Net::HTTPSuccess) raise FetchError, "unexpected response #{response.code}" unless response.is_a?(Net::HTTPSuccess)
JSON.parse(response.body) JSON.parse(response.body)
@@ -106,11 +129,22 @@ module Maptool
private_class_method :category_for private_class_method :category_for
def self.description_for(tags) def self.description_for(tags)
lines = DESCRIPTION_TAGS.filter_map { |tag, label| "#{label}: #{tags[tag]}" if tags[tag] } lines = DESCRIPTION_TAGS.filter_map { |tag, label| "#{label}: #{clean_tag(tags[tag])}" if tags[tag] }
website = tags["website"] || tags["contact:website"] website = tags["website"] || tags["contact:website"]
lines << website if website lines << clean_tag(website) if website
lines.empty? ? nil : lines.join("\n") lines.empty? ? nil : lines.join("\n")
end end
private_class_method :description_for private_class_method :description_for
# An OSM tag value is free text from a world-editable map — an embedded
# newline could otherwise inject an extra "line" of its own into the
# description, e.g. an operator tag ending in "\nhttps://evil.example"
# would render as its own separate, legitimate-looking link line once
# gpx.js's formatDescription (written for p4n2gpx's own well-formed
# "Label: value" lines) splits on "\n".
def self.clean_tag(value)
value.to_s.tr("\n\r", " ").strip
end
private_class_method :clean_tag
end end
end end
+12 -3
View File
@@ -55,14 +55,23 @@ module Maptool
manifest_path = File.join(plugin_dir, "plugin.json") manifest_path = File.join(plugin_dir, "plugin.json")
command = JSON.parse(File.read(manifest_path))["command"] command = begin
JSON.parse(File.read(manifest_path))["command"]
rescue JSON::ParserError
raise RunError, "plugin has an invalid plugin.json"
end
raise RunError, "plugin has no command" unless command raise RunError, "plugin has no command" unless command
south, west, north, east = bbox south, west, north, east = bbox
args = ["--south=#{south}", "--west=#{west}", "--north=#{north}", "--east=#{east}"] args = ["--south=#{south}", "--west=#{west}", "--north=#{north}", "--east=#{east}"]
stdout, _stderr, status = Open3.capture3(command, *args, chdir: plugin_dir) stdout, stderr, status = Open3.capture3(command, *args, chdir: plugin_dir)
raise RunError, "plugin exited with status #{status.exitstatus}" unless status.success? # First stderr line too — for a Ruby plugin that's the exception
# message (e.g. "...: unexpected response 406"), the only clue to why.
unless status.success?
reason = stderr.lines.first&.strip
raise RunError, ["plugin exited with status #{status.exitstatus}", reason].compact.reject(&:empty?).join(": ")
end
geojson = JSON.parse(stdout) geojson = JSON.parse(stdout)
raise RunError, "plugin did not print a GeoJSON FeatureCollection" unless geojson.is_a?(Hash) && geojson["type"] == "FeatureCollection" raise RunError, "plugin did not print a GeoJSON FeatureCollection" unless geojson.is_a?(Hash) && geojson["type"] == "FeatureCollection"
+745
View File
@@ -0,0 +1,745 @@
require "open3"
require "net/http"
require "uri"
require "json"
require "tmpdir"
require "fileutils"
require "etc"
require_relative "hiking_trails"
module Maptool
# Regenerates data/offline.pmtiles and data/terrain.pmtiles for the
# currently *selected* regions (data/offline_regions.json, an array of
# Natural Earth iso_a2 country codes — see available_regions/
# selected_regions/set_selected_regions below). No job queue: each build
# runs on its own background Thread and reports into a Mutex-guarded
# status Hash a route can poll, the same shape app.rb's own GNSS poller
# (GNSS_LATEST/GNSS_LATEST_MUTEX) already uses.
module PmtilesBuilder
class BuildError < StandardError; end
# Natural Earth (public domain, no attribution required) 110m admin-0
# countries, filtered to Europe and trimmed to name/iso_a2/iso_a3 —
# see the plan this was built from for the exact filter. 110m is
# deliberately coarse (a km or so of border precision, not
# survey-grade) — fine for "which country to grab," a same-schema swap
# to Natural Earth's 50m/10m cut if that ever needs to be tighter.
REGIONS_FILE = File.join(__dir__, "data", "europe_countries.geojson")
# Every iso_a2 code in REGIONS_FILE mapped to its Geofabrik download
# slug (https://download.geofabrik.de/<slug>-latest.osm.pbf) — Geofabrik
# names regions, not ISO codes, and doesn't always split the way a
# country border does, so this table exists rather than deriving the
# slug from the country name. Two known quirks worth knowing about,
# not worth working around:
# - RU has no "europe/" prefix (Russia is its own top-level index,
# and unlike every other entry here is a single ~4GB file covering
# the whole country rather than a European-only slice).
# - GB's "great-britain" slug is England/Scotland/Wales only —
# Northern Ireland lives in "ireland-and-northern-ireland" instead
# (bundled with IE, not GB). Selecting GB alone won't include NI.
GEOFABRIK_SLUGS = {
"AL" => "europe/albania",
"AT" => "europe/austria",
"BY" => "europe/belarus",
"BE" => "europe/belgium",
"BA" => "europe/bosnia-herzegovina",
"BG" => "europe/bulgaria",
"HR" => "europe/croatia",
"CZ" => "europe/czech-republic",
"DK" => "europe/denmark",
"EE" => "europe/estonia",
"FI" => "europe/finland",
"FR" => "europe/france",
"DE" => "europe/germany",
"GR" => "europe/greece",
"HU" => "europe/hungary",
"IS" => "europe/iceland",
"IE" => "europe/ireland-and-northern-ireland",
"IT" => "europe/italy",
"XK" => "europe/kosovo",
"LV" => "europe/latvia",
"LT" => "europe/lithuania",
"LU" => "europe/luxembourg",
"MD" => "europe/moldova",
"ME" => "europe/montenegro",
"NL" => "europe/netherlands",
"MK" => "europe/macedonia",
"NO" => "europe/norway",
"PL" => "europe/poland",
"PT" => "europe/portugal",
"RO" => "europe/romania",
"RU" => "russia",
"RS" => "europe/serbia",
"SK" => "europe/slovakia",
"SI" => "europe/slovenia",
"ES" => "europe/spain",
"SE" => "europe/sweden",
"CH" => "europe/switzerland",
"UA" => "europe/ukraine",
"GB" => "europe/great-britain",
}.freeze
# Mapterhorn (a Protomaps-affiliated project) publishes Terrarium-
# encoded terrain as pre-built PMTiles archives: one global
# planet.pmtiles (z0-12) plus ~450 regional chunks (z13 up to a
# per-region max, 13-18) — extract_offline below no longer touches
# Protomaps at all (see GEOFABRIK_SLUGS above), but terrain stays on
# this same "extract only the region's byte ranges from a remote
# archive" story via go-pmtiles, unrelated to the basemap swap.
MAPTERHORN_MANIFEST_URL = "https://download.mapterhorn.com/download_urls.json"
MAPTERHORN_PLANET_NAME = "planet.pmtiles"
# Deliberately *below* the online hillshade layer's own cap
# (public/js/overlays.js's ONLINE_TERRAIN_TILES source, maxzoom: 15) —
# raster DEM tiles don't compress away like vector ones do, so every
# extra zoom level roughly quadruples storage. z13 is each regional
# chunk's own native minzoom (see the comment above), so this is the
# cheapest useful cut: one raster level per chunk, not a 15-13=2-level
# pyramid on top of it. Bumped into a 181GB terrain.pmtiles for a
# 5-country selection before this was lowered — see docs/development.adoc.
TERRAIN_MAX_ZOOM = 13
OFFLINE_STATUS = { state: "idle" }
TERRAIN_STATUS = { state: "idle" }
OFFLINE_MUTEX = Mutex.new
TERRAIN_MUTEX = Mutex.new
def self.offline_status
OFFLINE_MUTEX.synchronize { OFFLINE_STATUS.dup }
end
def self.terrain_status
TERRAIN_MUTEX.synchronize { TERRAIN_STATUS.dup }
end
# Test-only: the status hashes are process-wide state (same caveat as
# GPX_STORE/LIVETRACK_STORE in app.rb), so the suite needs a way to
# reset them between tests the way AppTestHelper#teardown already does
# for those.
def self.reset!
OFFLINE_MUTEX.synchronize { OFFLINE_STATUS.replace(state: "idle") }
TERRAIN_MUTEX.synchronize { TERRAIN_STATUS.replace(state: "idle") }
end
# --- region selection ---
def self.available_regions
region_features.map { |f| { code: f["properties"]["iso_a2"], name: f["properties"]["name"] } }.sort_by { |r| r[:name] }
end
def self.selected_regions(data_dir)
path = selected_regions_path(data_dir)
return [] unless File.exist?(path)
JSON.parse(File.read(path))
rescue JSON::ParserError
[]
end
def self.set_selected_regions(data_dir, codes)
unknown = codes - available_regions.map { |r| r[:code] }
raise BuildError, "unknown region code(s): #{unknown.join(", ")}" unless unknown.empty?
File.write(selected_regions_path(data_dir), JSON.generate(codes))
end
def self.selected_regions_path(data_dir)
File.join(data_dir, "offline_regions.json")
end
private_class_method :selected_regions_path
def self.region_features
@region_features ||= JSON.parse(File.read(REGIONS_FILE))["features"]
end
private_class_method :region_features
# Still used by terrain (below) to compute the aggregate bbox Mapterhorn
# chunk intersection needs — offline.pmtiles no longer needs this at
# all, since each Geofabrik country file is already clipped to that
# country's real border (more accurate than this 110m polygon, not
# less), so there's no separate "--region=" precision step for it
# anymore.
def self.region_bbox(codes)
selected = region_features.select { |f| codes.include?(f["properties"]["iso_a2"]) }
raise BuildError, "no matching regions for #{codes.join(", ")}" if selected.empty?
coords = selected.flat_map { |f| ring_coordinates(f["geometry"]) }
lons = coords.map { |lng, _lat| lng }
lats = coords.map { |_lng, lat| lat }
[lats.min, lons.min, lats.max, lons.max]
end
private_class_method :region_bbox
def self.ring_coordinates(geometry)
case geometry["type"]
when "Polygon" then geometry["coordinates"].flatten(1)
when "MultiPolygon" then geometry["coordinates"].flatten(2)
else []
end
end
private_class_method :ring_coordinates
# --- builds ---
# Returns once the background Thread has started, not once the build
# finishes — raises BuildError (the caller turns this into an error
# response) if nothing's selected yet, or one's already running.
#
# download_extract/fetch_last_modified exist as injectable seams so
# tests can exercise the rest of this (scratch dir, osmium/planetiler
# invocation, atomic rename, the per-country cache below) without a
# real network call — same reasoning test_helper.rb already gives for
# keeping /api/plugins route tests off the real Overpass API.
def self.build_offline(data_dir:, download_extract: method(:download_extract), fetch_last_modified: method(:fetch_geofabrik_last_modified))
codes = selected_regions(data_dir)
raise BuildError, "select at least one region first" if codes.empty?
begin_run!(OFFLINE_MUTEX, OFFLINE_STATUS)
Thread.new do
run_job(OFFLINE_MUTEX, OFFLINE_STATUS) do |report_step|
extract_offline(codes, data_dir, download_extract, fetch_last_modified, report_step)
end
end
end
# fetch_manifest: same injectable-seam reasoning as download_extract
# above, for the one Mapterhorn manifest lookup this needs.
def self.build_terrain(data_dir:, fetch_manifest: method(:fetch_mapterhorn_manifest))
codes = selected_regions(data_dir)
raise BuildError, "select at least one region first" if codes.empty?
begin_run!(TERRAIN_MUTEX, TERRAIN_STATUS)
Thread.new do
run_job(TERRAIN_MUTEX, TERRAIN_STATUS) do |report_step|
build_terrain_pmtiles(codes, data_dir, fetch_manifest, report_step)
end
end
end
def self.begin_run!(mutex, status)
mutex.synchronize do
raise BuildError, "a build is already running" if status[:state] == "running"
status.replace(state: "running", started_at: Time.now.utc.iso8601)
end
end
private_class_method :begin_run!
# Yields a report_step proc the build can call with a short
# human-readable description of what it's doing right now ("downloading
# SK (1/2)", "tiling with planetiler", ...) — surfaced as status[:step]
# so GET .../build-status can show progress beyond just "running since
# X" (see docs/development.adoc).
def self.run_job(mutex, status)
report_step = ->(text) { mutex.synchronize { status[:step] = text } }
yield report_step
mutex.synchronize { status.merge!(state: "done", finished_at: Time.now.utc.iso8601).delete(:message) }
rescue => e
mutex.synchronize { status.merge!(state: "error", finished_at: Time.now.utc.iso8601, message: e.message) }
ensure
mutex.synchronize { status.delete(:step) }
end
private_class_method :run_job
# --- offline.pmtiles ---
# One Geofabrik .osm.pbf per selected country (already clipped to that
# country's real border), osmium-merged into one file if more than one
# was selected (skipped entirely for a single country — nothing to
# merge), then tiled by Planetiler using its default OpenMapTiles
# profile straight to PMTiles — the same vector schema
# tiles.openfreemap.org's Liberty style already expects, so
# public/js/map.js's offlineStyle() can reuse ONLINE_STYLE_PATCH
# unchanged instead of a separate protomaps-themes-base palette.
def self.extract_offline(codes, data_dir, download_extract, fetch_last_modified, report_step)
scratch_dir = Dir.mktmpdir("maptool-offline")
sources = codes.each_with_index.map do |code, i|
slug = GEOFABRIK_SLUGS.fetch(code) { raise BuildError, "no Geofabrik source known for region #{code}" }
report_step.call(progress_step("fetching #{code}", codes, i))
cached_extract(code, slug, data_dir, download_extract, fetch_last_modified)
end
osm_path =
if sources.size == 1
sources.first["path"]
else
report_step.call("merging with osmium")
merged_path = File.join(scratch_dir, "merged.osm.pbf")
run_osmium!("merge", *sources.map { |s| s["path"] }, "-o", merged_path)
merged_path
end
report_step.call("tiling with planetiler")
# .tmp.pmtiles, not .pmtiles.tmp — Planetiler picks its output archive
# format (pmtiles vs mbtiles) from the file extension, so a ".tmp"
# suffix made it fail with "Unsupported format tmp" instead of
# actually tiling anything.
tmp_path = File.join(data_dir, "offline.tmp.pmtiles")
# --download: the OpenMapTiles profile also needs a few small auxiliary
# sources (water polygons, Natural Earth, lake centerlines) alongside
# the OSM extract itself — without this flag Planetiler expects them
# to already exist on disk and fails instead of fetching them. They
# land under Planetiler's own default "data/sources/", relative to
# this process's working directory (the app root, so in practice
# that's data_dir's own "sources" subdirectory) — fetched once,
# reused by every later build.
# --threads: Planetiler's own core-count autodetection can come up
# short in a container (cgroup/JVM detection quirks vary by runtime),
# leaving most of a many-core host idle — Etc.nprocessors asks the
# OS directly (sched_getaffinity on Linux), which correctly reflects
# whatever's actually usable by this process even under a container.
run_planetiler!("--osm-path=#{osm_path}", "--output=#{tmp_path}", "--download", "--threads=#{Etc.nprocessors}")
File.rename(tmp_path, File.join(data_dir, "offline.pmtiles"))
File.write(offline_meta_path(data_dir), JSON.generate(sources: sources.map { |s| s.slice("code", "slug", "last_modified") }))
report_step.call("extracting hiking trails")
extract_hiking_trails(sources, data_dir)
ensure
FileUtils.remove_entry(scratch_dir) if scratch_dir && Dir.exist?(scratch_dir)
File.delete(tmp_path) if tmp_path && File.exist?(tmp_path)
end
private_class_method :extract_offline
# One GeoJSON per country (data/hiking_trails/<CODE>.geojson), not one
# merged file — the frontend only ever needs to show a country at a
# time the viewer actually picks (see HikingTrailsControl's country
# checklist), and a combined multi-country file was needlessly making
# every viewer fetch every selected country's trails regardless.
#
# Runs over the same per-country .osm.pbf files `sources` above already
# downloaded/cached for offline.pmtiles itself — no separate download.
# Cached the same way geofabrik_cache is: skipped for a country whose
# extract is already newer than its source .osm.pbf, so growing the
# region selection by one country only extracts that one, not every
# already-covered country all over again.
#
# Best-effort per country: an extraction problem (a malformed PBF
# region, an osmium hiccup) shouldn't fail the whole offline.pmtiles
# build, or even the other countries in this same selection, over
# what's a bonus overlay, not the core map.
def self.extract_hiking_trails(sources, data_dir)
FileUtils.mkdir_p(hiking_trails_dir(data_dir))
sources.each do |source|
out_path = hiking_trails_country_path(data_dir, source["code"])
next if File.exist?(out_path) && File.mtime(out_path) >= File.mtime(source["path"])
tmp_path = "#{out_path}.tmp"
begin
File.write(tmp_path, JSON.generate(HikingTrails.extract(source["path"])))
File.rename(tmp_path, out_path)
rescue HikingTrails::ExtractError => e
File.delete(tmp_path) if File.exist?(tmp_path)
warn "hiking trails extraction failed for #{source["code"]}, skipping: #{e.message}"
end
end
end
private_class_method :extract_hiking_trails
def self.hiking_trails_dir(data_dir)
File.join(data_dir, "hiking_trails")
end
def self.hiking_trails_country_path(data_dir, code)
File.join(hiking_trails_dir(data_dir), "#{code}.geojson")
end
# Country codes with an already-extracted offline hiking-trails file,
# sorted — not necessarily the same as the *current* region selection
# (accumulates like geofabrik_cache does; a country deselected after
# being built once is left alone rather than swept up here).
def self.hiking_trails_countries(data_dir)
Dir.glob(File.join(hiking_trails_dir(data_dir), "*.geojson")).map { |path| File.basename(path, ".geojson") }.sort
end
# Renders a report_step string like "fetching PL (3/4) — done: SK, CZ —
# pending: HU" — so GET .../build-status shows not just "it's running"
# but which of the current build's sources is in flight and which are
# already behind/ahead of it.
def self.progress_step(action, labels, index)
done = labels[0...index]
pending = labels[(index + 1)..] || []
parts = ["#{action} (#{index + 1}/#{labels.size})"]
parts << "done: #{done.join(", ")}" unless done.empty?
parts << "pending: #{pending.join(", ")}" unless pending.empty?
parts.join(" — ")
end
private_class_method :progress_step
def self.geofabrik_url(slug)
"https://download.geofabrik.de/#{slug}-latest.osm.pbf"
end
private_class_method :geofabrik_url
# Per-country .osm.pbf cache, kept across builds under data_dir (not
# scratch_dir, which is wiped every run) — changing the region
# selection re-downloads only the countries that are new to it, and
# re-running the same selection re-downloads nothing unless Geofabrik's
# own Last-Modified for that country moved on. A stamp file next to
# each cached extract records the Last-Modified it was fetched with;
# a HEAD check (cheap) decides whether the multi-GB GET (not cheap) is
# skippable.
def self.cached_extract(code, slug, data_dir, download_extract, fetch_last_modified)
cache_dir = geofabrik_cache_dir(data_dir)
FileUtils.mkdir_p(cache_dir)
path = File.join(cache_dir, "#{code}.osm.pbf")
stamp_path = "#{path}.last_modified"
url = geofabrik_url(slug)
if File.exist?(path) && File.exist?(stamp_path)
current = begin
fetch_last_modified.call(url)
rescue BuildError
nil
end
return { "code" => code, "slug" => slug, "last_modified" => current, "path" => path } if current && current == File.read(stamp_path)
end
last_modified = download_extract.call(url, path)
File.write(stamp_path, last_modified.to_s)
{ "code" => code, "slug" => slug, "last_modified" => last_modified, "path" => path }
end
private_class_method :cached_extract
def self.geofabrik_cache_dir(data_dir)
File.join(data_dir, "geofabrik_cache")
end
private_class_method :geofabrik_cache_dir
# Real network implementation of the download_extract seam above —
# streams the extract to dest (some of these are multi-GB — Net::HTTP's
# plain #get buffers the whole response body in memory first, which
# read_body's block form avoids) and returns its Last-Modified header
# (free off the same GET, no separate HEAD needed) for the sidecar
# meta / update-check below. Geofabrik 302s (e.g. .../europe/austria
# redirecting to wherever actually serves it) — followed manually
# since request_get's block form doesn't do this itself.
def self.download_extract(url, dest, limit: 5)
raise BuildError, "too many redirects downloading #{url}" if limit.zero?
uri = URI(url)
last_modified = nil
redirect_to = nil
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request_get(uri) do |response|
case response
when Net::HTTPSuccess
last_modified = response["last-modified"]
File.open(dest, "wb") { |file| response.read_body { |chunk| file.write(chunk) } }
when Net::HTTPRedirection
redirect_to = URI.join(url, response["location"]).to_s
else
raise BuildError, "could not download #{url} (HTTP #{response.code})"
end
end
end
return download_extract(redirect_to, dest, limit: limit - 1) if redirect_to
last_modified
end
# Sidecar recording which Geofabrik sources (country code, slug, and
# that request's Last-Modified) the on-disk offline.pmtiles was
# actually built from — the file itself carries no such record. nil if
# never built (or built before this existed).
def self.offline_meta(data_dir)
read_json(offline_meta_path(data_dir))
end
# Compares each currently-selected country's live Geofabrik
# Last-Modified against what's recorded, without downloading anything
# — nil (not true/false) if there's nothing to compare yet (no local
# build, nothing selected) or a check couldn't be completed, so the
# caller can tell "don't know" apart from "no". Also naturally reports
# "yes" if the selection changed since the last build (a country now
# relevant that wasn't recorded, or vice versa) — same reasoning
# terrain_update_available? below already uses.
def self.offline_update_available?(data_dir, fetch_last_modified: method(:fetch_geofabrik_last_modified))
meta = offline_meta(data_dir)
return nil unless meta
codes = selected_regions(data_dir)
return nil if codes.empty?
current = codes.map do |code|
slug = GEOFABRIK_SLUGS[code]
return nil unless slug
{ "code" => code, "last_modified" => fetch_last_modified.call(geofabrik_url(slug)) }
end.sort_by { |s| s["code"] }
recorded = (meta["sources"] || []).map { |s| s.slice("code", "last_modified") }.sort_by { |s| s["code"] }
current != recorded
rescue BuildError
nil
end
def self.fetch_geofabrik_last_modified(url, limit: 5)
raise BuildError, "too many redirects reaching #{url}" if limit.zero?
uri = URI(url)
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.head(uri) }
case response
when Net::HTTPSuccess then response["last-modified"]
when Net::HTTPRedirection then fetch_geofabrik_last_modified(URI.join(url, response["location"]).to_s, limit: limit - 1)
else raise BuildError, "could not reach #{url}"
end
end
def self.offline_meta_path(data_dir)
File.join(data_dir, "offline.pmtiles.meta.json")
end
private_class_method :offline_meta_path
# Deletes offline.pmtiles, its sidecar meta, and the whole per-country
# download cache (data/geofabrik_cache/) — "start over" for the offline
# basemap. The current selection (data/offline_regions.json) is left
# alone, so a following build just re-downloads it fresh.
def self.delete_offline_data(data_dir)
OFFLINE_MUTEX.synchronize { raise BuildError, "a build is already running" if OFFLINE_STATUS[:state] == "running" }
File.delete(File.join(data_dir, "offline.pmtiles")) if File.exist?(File.join(data_dir, "offline.pmtiles"))
File.delete(offline_meta_path(data_dir)) if File.exist?(offline_meta_path(data_dir))
FileUtils.remove_entry(hiking_trails_dir(data_dir)) if Dir.exist?(hiking_trails_dir(data_dir))
cache_dir = geofabrik_cache_dir(data_dir)
FileUtils.remove_entry(cache_dir) if Dir.exist?(cache_dir)
end
# Deletes just one country's cached .osm.pbf (and its Last-Modified
# stamp) — e.g. to force a fresh download without touching the others,
# or to reclaim space for a country no longer selected.
def self.delete_cached_extract(data_dir, code)
OFFLINE_MUTEX.synchronize { raise BuildError, "a build is already running" if OFFLINE_STATUS[:state] == "running" }
cache_dir = geofabrik_cache_dir(data_dir)
path = File.join(cache_dir, "#{code}.osm.pbf")
File.delete(path) if File.exist?(path)
File.delete("#{path}.last_modified") if File.exist?("#{path}.last_modified")
end
# What's actually sitting in the per-country download cache right now —
# not the same thing as offline_meta's "sources", which only reflects
# whatever the *last successful build* used. A country can be cached
# without ever having been built (selected, then deselected again
# before Apply) or built from without still being selected, and this is
# what lets the region picker show/clear either case.
def self.cached_extracts(data_dir)
cache_dir = geofabrik_cache_dir(data_dir)
return [] unless Dir.exist?(cache_dir)
Dir.glob(File.join(cache_dir, "*.osm.pbf")).map do |path|
stamp_path = "#{path}.last_modified"
{
"code" => File.basename(path, ".osm.pbf"),
"size" => File.size(path),
"last_modified" => File.exist?(stamp_path) ? File.read(stamp_path) : nil,
}
end.sort_by { |s| s["code"] }
end
# --- terrain.pmtiles ---
# planet.pmtiles (z0-12, always) plus whichever regional chunks
# intersect the selection's aggregate bbox (z13+, zero or more) are
# each `extract`ed on their own using the *same* --region= file, then
# combined with `pmtiles merge` if there was more than one. Always
# safe to merge regardless of how many countries are selected or
# whether they're adjacent: each Mapterhorn chunk is its own
# pre-partitioned, non-overlapping source grid cell, so two different
# chunks' extracts can never share a tile id — the only thing `merge`
# refuses (see pmtiles/merge.go's own disjointness check). What would
# NOT be safe is extracting the *same* source once per country and
# merging those — adjacent countries' region-extracts would share
# border tiles (extraction copies whole tiles, it doesn't clip vector
# content to the polygon) — so this always does one extract per
# *source archive*, never one per country.
def self.build_terrain_pmtiles(codes, data_dir, fetch_manifest, report_step)
report_step.call("fetching mapterhorn manifest")
items = fetch_manifest.call
planet = items.find { |item| item["name"] == MAPTERHORN_PLANET_NAME }
raise BuildError, "mapterhorn manifest is missing #{MAPTERHORN_PLANET_NAME}" unless planet
scratch_dir = Dir.mktmpdir("maptool-terrain")
south, west, north, east = region_bbox(codes)
region_path = File.join(scratch_dir, "region.geojson")
selected = region_features.select { |f| codes.include?(f["properties"]["iso_a2"]) }
File.write(region_path, JSON.generate(type: "FeatureCollection", features: selected))
chunks = items.select { |item| item["name"] != MAPTERHORN_PLANET_NAME && bbox_intersects?(item, south, west, north, east) }
# .tmp.pmtiles, not .pmtiles.tmp — same reasoning as offline.pmtiles'
# own tmp_path above, in case go-pmtiles also derives format from
# the extension for its "merge" output.
tmp_path = File.join(data_dir, "terrain.tmp.pmtiles")
sources = [planet, *chunks]
labels = sources.map { |item| item["name"] }
extracts = sources.each_with_index.map do |item, i|
report_step.call(progress_step("extracting #{item["name"]}", labels, i))
extract_path = File.join(scratch_dir, "extract-#{i}.pmtiles")
# --overfetch=0: go-pmtiles' default (0.05) merges nearby byte
# ranges into fewer, bigger HTTP requests to save round-trips —
# for planet.pmtiles (global z0-12, tens of GB) covering a
# multi-country selection that collapsed the whole extract into
# a *single* ~60GB request, which then had no way to resume after
# an HTTP/2 stream reset 11 minutes in (see the "died again"
# 2026-09-12 build). Disabling merging trades that one fragile,
# unresumable mega-request for many small ones spread over
# --download-threads (default 4) — slower on a clean run, but no
# longer one dropped stream away from losing 20 minutes of transfer.
run_pmtiles!("extract", item["url"], extract_path, "--region=#{region_path}", "--maxzoom=#{TERRAIN_MAX_ZOOM}", "--overfetch=0")
extract_path
end
if extracts.size == 1
File.rename(extracts.first, tmp_path)
else
report_step.call("merging with pmtiles")
run_pmtiles!("merge", *extracts, tmp_path)
end
File.rename(tmp_path, File.join(data_dir, "terrain.pmtiles"))
File.write(terrain_meta_path(data_dir), JSON.generate(sources: [planet, *chunks].map { |i| { name: i["name"], md5sum: i["md5sum"] } }))
ensure
FileUtils.remove_entry(scratch_dir) if scratch_dir && Dir.exist?(scratch_dir)
File.delete(tmp_path) if tmp_path && File.exist?(tmp_path)
end
private_class_method :build_terrain_pmtiles
def self.bbox_intersects?(item, south, west, north, east)
item["min_lon"] < east && item["max_lon"] > west && item["min_lat"] < north && item["max_lat"] > south
end
private_class_method :bbox_intersects?
def self.fetch_mapterhorn_manifest
response = Net::HTTP.get_response(URI(MAPTERHORN_MANIFEST_URL))
raise BuildError, "could not reach #{MAPTERHORN_MANIFEST_URL}" unless response.is_a?(Net::HTTPSuccess)
JSON.parse(response.body).fetch("items")
rescue JSON::ParserError
raise BuildError, "mapterhorn manifest was not valid JSON"
end
private_class_method :fetch_mapterhorn_manifest
# Sidecar recording which Mapterhorn sources (name + md5sum) the on-disk
# terrain.pmtiles was actually extracted+merged from. nil if never built.
def self.terrain_meta(data_dir)
read_json(terrain_meta_path(data_dir))
end
# Same "don't know" vs. "no" distinction as offline_update_available?
# above. Recomputes which sources *would* be selected for the current
# selection (same manifest + bbox_intersects? this build itself uses)
# and compares their md5sums against what's recorded — so this also
# correctly says "yes" if the selection changed since the last build
# (a chunk now relevant that wasn't recorded), not just if Mapterhorn's
# own data moved on.
def self.terrain_update_available?(data_dir, fetch_manifest: method(:fetch_mapterhorn_manifest))
meta = terrain_meta(data_dir)
return nil unless meta
codes = selected_regions(data_dir)
return nil if codes.empty?
items = fetch_manifest.call
planet = items.find { |item| item["name"] == MAPTERHORN_PLANET_NAME }
return nil unless planet
south, west, north, east = region_bbox(codes)
chunks = items.select { |item| item["name"] != MAPTERHORN_PLANET_NAME && bbox_intersects?(item, south, west, north, east) }
current = [planet, *chunks].map { |i| { "name" => i["name"], "md5sum" => i["md5sum"] } }.sort_by { |s| s["name"] }
current != (meta["sources"] || []).sort_by { |s| s["name"] }
rescue BuildError
nil
end
def self.terrain_meta_path(data_dir)
File.join(data_dir, "terrain.pmtiles.meta.json")
end
private_class_method :terrain_meta_path
# Deletes terrain.pmtiles and its sidecar meta — same "start over"
# reasoning as delete_offline_data above. No download cache to clean up
# here: terrain.pmtiles' pmtiles-extract step reads its sources over
# HTTP range requests rather than downloading a whole archive first.
def self.delete_terrain_data(data_dir)
TERRAIN_MUTEX.synchronize { raise BuildError, "a build is already running" if TERRAIN_STATUS[:state] == "running" }
File.delete(File.join(data_dir, "terrain.pmtiles")) if File.exist?(File.join(data_dir, "terrain.pmtiles"))
File.delete(terrain_meta_path(data_dir)) if File.exist?(terrain_meta_path(data_dir))
end
# --- shared ---
def self.read_json(path)
File.exist?(path) ? JSON.parse(File.read(path)) : nil
rescue JSON::ParserError
nil
end
private_class_method :read_json
# ENV read lazily (not load-time constants) so tests can point these at
# fake binaries without a require-order dance.
def self.pmtiles_bin
ENV.fetch("PMTILES_BIN", "pmtiles")
end
def self.osmium_bin
ENV.fetch("OSMIUM_BIN", "osmium")
end
def self.java_bin
ENV.fetch("JAVA_BIN", "java")
end
def self.planetiler_jar
ENV.fetch("PLANETILER_JAR", "/usr/local/lib/planetiler.jar")
end
# go-pmtiles' extract logs one progress line per chunk fetched
# ("fetching N dirs, M chunks, K requests") — fine on a terminal where
# it overwrites itself, but captured non-interactively that's one line
# per chunk, easily hundreds of thousands of characters for a big
# region. The actual failure is always at the end, so keep only the
# tail — same story for osmium/planetiler's own chatty stdout.
OUTPUT_TAIL_LINES = 40
# Some CLI tools (go-pmtiles included) write their actual error to
# stdout rather than stderr — combine both rather than risk raising a
# BuildError with nothing useful in it.
def self.command_output(stdout, stderr)
lines = [stdout, stderr].map(&:strip).reject(&:empty?).join("\n").lines
return lines.join if lines.size <= OUTPUT_TAIL_LINES
omitted = lines.size - OUTPUT_TAIL_LINES
"(#{omitted} earlier lines omitted)\n#{lines.last(OUTPUT_TAIL_LINES).join}"
end
private_class_method :command_output
# extract/merge stream tens of GB over HTTP range requests; retry a
# transient failure (e.g. a mid-transfer HTTP/2 stream reset from the
# remote) a couple times before giving up, rather than forcing a full
# re-download on every hiccup.
def self.run_pmtiles!(*args, attempts: 3)
attempts.times do |i|
stdout, stderr, status = Open3.capture3(pmtiles_bin, *args)
return if status.success?
raise BuildError, "pmtiles exited with status #{status.exitstatus}: #{command_output(stdout, stderr)}" if i == attempts - 1
end
rescue Errno::ENOENT, Errno::EACCES => e
raise BuildError, "could not run '#{pmtiles_bin}' (#{e.message}) — is go-pmtiles installed and on PATH? See docs/development.adoc."
end
private_class_method :run_pmtiles!
def self.run_osmium!(*args)
stdout, stderr, status = Open3.capture3(osmium_bin, *args)
raise BuildError, "osmium exited with status #{status.exitstatus}: #{command_output(stdout, stderr)}" unless status.success?
rescue Errno::ENOENT, Errno::EACCES => e
raise BuildError, "could not run '#{osmium_bin}' (#{e.message}) — is osmium-tool installed and on PATH? See docs/development.adoc."
end
private_class_method :run_osmium!
def self.run_planetiler!(*args)
stdout, stderr, status = Open3.capture3(java_bin, "-jar", planetiler_jar, *args)
raise BuildError, "planetiler exited with status #{status.exitstatus}: #{command_output(stdout, stderr)}" unless status.success?
rescue Errno::ENOENT, Errno::EACCES => e
raise BuildError, "could not run '#{java_bin} -jar #{planetiler_jar}' (#{e.message}) — is Java and planetiler.jar installed? See docs/development.adoc."
end
private_class_method :run_planetiler!
end
end
+495 -3
View File
@@ -4,6 +4,18 @@ html, body {
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
} }
/* Router-carved-signage display face (docs/nps-resources.adoc), used only
for the topbar wordmark below — a full body-text swap is a separate,
bigger decision this isn't. Bold is the only weight vendored since
.topbar__brand only ever renders at font-weight 700. */
@font-face {
font-family: "National Park";
src: url("/vendor/national-park-typeface/NationalPark-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
#app { #app {
height: 100%; height: 100%;
display: flex; display: flex;
@@ -20,20 +32,65 @@ html, body {
} }
.topbar__brand { .topbar__brand {
font-size: 14px; font-family: "National Park", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 20px;
font-weight: 700; font-weight: 700;
letter-spacing: 0.12em; letter-spacing: 0.12em;
text-transform: uppercase; text-transform: uppercase;
color: #fff; color: #fff;
} }
.topbar__coords { .topbar__center {
margin: 0 auto; margin: 0 auto;
display: flex;
align-items: center;
gap: 14px;
}
.topbar__coords,
.topbar__sun {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px; font-size: 12px;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
color: #bbb; color: #bbb;
} }
.topbar__sun {
gap: 14px;
}
.topbar__group {
display: flex;
align-items: center;
gap: 5px;
}
.topbar__icon {
display: flex;
width: 13px;
height: 13px;
flex-shrink: 0;
}
.topbar__icon svg {
width: 100%;
height: 100%;
}
.topbar__coords {
cursor: pointer;
}
.topbar__coords:hover {
color: #fff;
}
.topbar__sun[hidden] {
display: none;
}
.topbar__offline-status { .topbar__offline-status {
width: 8px; width: 8px;
height: 8px; height: 8px;
@@ -199,6 +256,29 @@ html, body {
border-top: 1px solid #eee; border-top: 1px solid #eee;
} }
.radar-control__forecast-hours {
display: flex;
gap: 10px;
overflow-x: auto;
padding-bottom: 6px;
margin-bottom: 4px;
border-bottom: 1px solid #eee;
}
.radar-control__forecast-hour {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
flex: 0 0 auto;
font-variant-numeric: tabular-nums;
}
.radar-control__forecast-hour span:first-child {
color: #888;
font-size: 11px;
}
.radar-control__forecast-day { .radar-control__forecast-day {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -223,11 +303,32 @@ html, body {
font-size: 11px; font-size: 11px;
} }
.shadow-control__time {
display: flex;
align-items: center;
gap: 6px;
}
.shadow-control__slider {
width: 110px;
}
.shadow-control__clock {
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.shadow-control__hint {
margin: 6px 0 0;
color: #888;
font-size: 11px;
}
.basemap-panel { .basemap-panel {
position: fixed; position: fixed;
top: 0; top: 0;
left: 52px; left: 52px;
width: 260px; width: 300px;
max-width: 90vw; max-width: 90vw;
height: 100%; height: 100%;
display: flex; display: flex;
@@ -296,6 +397,225 @@ html, body {
visibility: visible; visibility: visible;
} }
/* Docked detail panel for a clicked hiking trail — replaces a map-anchored
Popup (which routinely covered up the very trail line it described) with
a fixed side panel, same "click info moves off the map" idea
track_profile.js already established for clicking a GPX track line
(TrackProfilePanel). .trail-panel__body gets the exact padding
.maplibregl-popup-content used (15px 10px) so routeHtml()'s own output —
built for that popup, including its header's canceling negative margin —
renders here unchanged; no separate panel-flavored template needed. */
.trail-panel {
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: 300px;
max-width: 90vw;
background: #fff;
border-left: 1px solid #ddd;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
z-index: 10;
}
.trail-panel__body {
height: 100%;
box-sizing: border-box;
overflow-y: auto;
padding: 15px 10px;
font-size: 13px;
}
.trail-panel__close {
position: absolute;
top: 6px;
right: 6px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.85);
font-size: 18px;
line-height: 1;
padding: 2px 7px;
cursor: pointer;
}
/* First breakpoint in this file. Below it the same panel docks to the
bottom edge instead of the right one — a fixed-width side panel would
otherwise eat most of a phone-width map, the exact "popup dominates the
screen" problem this panel was meant to fix in the first place. */
@media (max-width: 640px) {
.trail-panel {
top: auto;
left: 0;
right: 0;
bottom: 0;
width: auto;
max-width: none;
max-height: 50vh;
border-left: none;
border-top: 1px solid #ddd;
border-radius: 8px 8px 0 0;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
}
}
.offline-data-panel__body {
padding: 8px 12px;
}
.offline-data-panel__regions-label {
margin: 0 0 4px;
font-weight: 600;
}
.offline-data-panel__region-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 220px;
overflow-y: auto;
border: 1px solid #eee;
border-radius: 4px;
}
.offline-data-panel__region-list li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.offline-data-panel__region-list label {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
min-width: 0;
flex: 1;
cursor: pointer;
}
.offline-data-panel__region-list label:hover {
background: #f0f0f0;
}
.offline-data-panel__cache-info {
display: flex;
align-items: center;
gap: 4px;
padding-right: 8px;
color: #888;
font-size: 11px;
white-space: nowrap;
}
.offline-data-panel__cache-delete {
border: none;
background: none;
padding: 0 2px;
color: #a33;
font-size: 11px;
cursor: pointer;
}
.offline-data-panel__cache-delete:hover {
text-decoration: underline;
}
.offline-data-panel__cache-delete:disabled {
opacity: 0.5;
cursor: default;
}
/* Same rounded-square badge ratio as icons.js's own MAP_MARKER_ICON
(rx 5 of a 22-wide square) — a consistent frame rather than however
each flag's own artwork happens to fill an unbordered square. */
.offline-data-panel__flag {
flex-shrink: 0;
width: 18px;
height: 18px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.15);
object-fit: cover;
}
.offline-data-panel__region-status {
padding: 6px 8px;
color: #888;
}
.offline-data-panel__apply {
margin-top: 8px;
width: 100%;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
padding: 6px 8px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.offline-data-panel__apply:hover {
background: #f0f0f0;
}
.offline-data-panel__apply:disabled {
opacity: 0.5;
cursor: default;
}
.offline-data-panel__row {
margin-top: 14px;
padding-top: 10px;
border-top: 1px solid #eee;
}
.offline-data-panel__row-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.offline-data-panel__delete {
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
padding: 2px 8px;
font: inherit;
font-size: 11px;
color: #a33;
cursor: pointer;
}
.offline-data-panel__delete:hover {
background: #fdecec;
}
.offline-data-panel__delete:disabled {
opacity: 0.5;
cursor: default;
}
.offline-data-panel__status {
margin: 6px 0 0;
color: #666;
font-size: 12px;
}
.offline-data-panel__status--error {
white-space: pre-wrap;
word-break: break-word;
max-height: 200px;
overflow-y: auto;
color: #b3261e;
}
.offline-data-panel__update-badge {
color: #b8860b;
}
.gpx-panel { .gpx-panel {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -778,6 +1098,72 @@ html, body {
color: #333; color: #333;
} }
.search-ctrl {
position: relative;
}
.search-ctrl__button svg {
display: block;
margin: 0 auto;
width: 18px;
height: 18px;
}
.search-ctrl__button.active {
background: #dbe9ff;
}
.search-panel {
position: absolute;
top: 0;
left: 100%;
margin-left: 8px;
z-index: 20;
width: 240px;
background: #fff;
border: 1px solid #ddd;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 6px 8px;
font-size: 13px;
}
.search-panel[hidden] {
display: none;
}
.search-panel__input {
width: 100%;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px 7px;
font: inherit;
}
.search-panel__results {
list-style: none;
margin: 6px 0 0;
padding: 0;
max-height: 220px;
overflow-y: auto;
}
.search-panel__result {
padding: 6px 4px;
border-radius: 4px;
cursor: pointer;
}
.search-panel__result:hover {
background: #f0f0f0;
}
.search-panel__status {
padding: 6px 4px;
color: #888;
}
.notes-ctrl { .notes-ctrl {
position: relative; position: relative;
} }
@@ -1008,6 +1394,17 @@ html, body {
user-select: none; user-select: none;
} }
.pitch-toggle-ctrl__button {
width: 29px;
height: 29px;
font-size: 11px;
font-weight: 600;
}
.pitch-toggle-ctrl__button.active {
background: #dbe9ff;
}
.gpx-popup__header { .gpx-popup__header {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1019,6 +1416,14 @@ html, body {
color: #fff; color: #fff;
} }
.hiking-trail-popup__swatch {
flex-shrink: 0;
width: 14px;
height: 14px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.6);
}
.gpx-popup__header-icon { .gpx-popup__header-icon {
flex-shrink: 0; flex-shrink: 0;
width: 18px; width: 18px;
@@ -1174,6 +1579,84 @@ html, body {
box-shadow: 0 0 0 2px rgba(42, 157, 143, 0.4); box-shadow: 0 0 0 2px rgba(42, 157, 143, 0.4);
} }
.overlays-panel__list {
flex: 1;
overflow-y: auto;
list-style: none;
margin: 0;
padding: 8px 12px;
}
.overlays-panel__item label {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 4px;
border-radius: 4px;
cursor: pointer;
}
.overlays-panel__item label:hover {
background: #f0f0f0;
}
.overlays-panel__icon {
display: flex;
width: 16px;
height: 16px;
flex-shrink: 0;
color: #555;
}
.overlays-panel__icon svg {
width: 100%;
height: 100%;
}
.overlays-panel__row {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 4px;
}
.overlays-panel__name {
border: none;
background: none;
padding: 0;
font: inherit;
color: inherit;
text-align: left;
cursor: pointer;
}
.overlays-panel__name:hover {
text-decoration: underline;
}
.overlays-panel__details {
margin: 0 4px 6px 26px;
padding-top: 6px;
border-top: 1px solid #eee;
font-size: 12px;
}
.hiking-trail-countries {
display: flex;
flex-wrap: wrap;
gap: 4px 12px;
list-style: none;
margin: 0;
padding: 0;
}
.hiking-trail-countries__item label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.livetrack-panel__list { .livetrack-panel__list {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
@@ -1487,8 +1970,17 @@ html, body {
.plugins-panel__status { .plugins-panel__status {
min-width: 3.5em; min-width: 3.5em;
max-width: 200px;
color: #888; color: #888;
font-size: 11px; font-size: 11px;
/* Overrides the parent's nowrap: "Running…"/"Done" never wrap either
way, but a real RunError message (e.g. "plugin exited with status 1")
needs to, or it just overflows the flyout unreadably. */
white-space: normal;
}
.plugins-panel__status--error {
color: #c62828;
} }
.plugins-panel__empty { .plugins-panel__empty {
+99
View File
@@ -0,0 +1,99 @@
import { addProtocol } from "/vendor/maplibre-gl/maplibre-gl.mjs";
import { ONLINE_TERRAIN_TILES } from "/js/overlays.js";
import { firstSymbolLayerId } from "/js/radar.js";
import { CONTOUR_ICON } from "/js/icons.js";
const SOURCE_ID = "contour-lines";
const LINE_LAYER_ID = "contour-lines-line";
// Topographic contour lines, generated client-side by the vendored
// maplibre-contour plugin (public/vendor/maplibre-contour/) from the same
// online Terrarium DEM tiles overlays.js's HillshadeControl already
// fetches. Online only, unlike Terrain shading/Terrain shadow: the
// plugin's DemSource has no public hook to route its own tile fetch
// through the pmtiles:// protocol HillshadeControl uses for the offline
// basemap, so — same story as the Wikipedia layer and Radar — this just
// quietly renders nothing without a network.
export class ContourControl {
constructor(map) {
this._map = map;
this._active = false;
this._demSource = new window.mlcontour.DemSource({
url: ONLINE_TERRAIN_TILES[0],
encoding: "terrarium",
maxzoom: 15,
worker: true,
cacheSize: 100,
});
this._demSource.setupMaplibre({ addProtocol });
// setStyle() (switching Online/Offline) tears down all custom sources/
// layers — put contour lines back if they were on, same as every other
// Overlays control.
map.on("style.load", () => {
if (this._active) this._addLayer();
});
}
mount(li) {
li.innerHTML = `
<label>
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${CONTOUR_ICON}</span>
<span>Contour lines</span>
</label>
`;
this._checkbox = li.querySelector(".overlays-panel__toggle");
this._checkbox.addEventListener("change", (event) => this._toggle(event.target.checked));
}
_toggle(active) {
this._active = active;
if (active) this._addLayer();
else this._removeLayer();
}
_addLayer() {
if (this._map.getSource(SOURCE_ID)) return;
// Starting-point thresholds (README's own generic example) — meters,
// more/tighter contours at higher zoom. Expect to eyeball-tune against
// real Tatras-scale terrain.
const tiles = [
this._demSource.contourProtocolUrl({
thresholds: {
11: [200, 1000],
12: [100, 500],
14: [50, 200],
15: [20, 100],
},
contourLayer: "contours",
elevationKey: "ele",
levelKey: "level",
}),
];
this._map.addSource(SOURCE_ID, { type: "vector", tiles, maxzoom: 15 });
this._map.addLayer(
{
id: LINE_LAYER_ID,
type: "line",
source: SOURCE_ID,
"source-layer": "contours",
paint: {
// Roughly the same brown as the mid-slope band of overlays.js's
// own ELEVATION_COLOR_RAMP, so this reads as part of the same
// terrain treatment rather than a bolted-on layer.
"line-color": "#8a6d3b",
"line-width": ["match", ["get", "level"], 1, 1.2, 0.6],
"line-opacity": 0.6,
},
},
firstSymbolLayerId(this._map),
);
}
_removeLayer() {
if (this._map.getLayer(LINE_LAYER_ID)) this._map.removeLayer(LINE_LAYER_ID);
if (this._map.getSource(SOURCE_ID)) this._map.removeSource(SOURCE_ID);
}
}
+74
View File
@@ -0,0 +1,74 @@
// Elevation lookups against the same Terrarium-encoded terrain tiles
// overlays.js's HillshadeControl already renders — pulled into its own file
// since TODO.adoc lists two more near-future features (slope readout, solar
// calculator) that will need the same "elevation at a point" primitive,
// shadow.js's raymarch being the first real consumer.
import { ONLINE_TERRAIN_TILES } from "/js/overlays.js";
const DEM_ZOOM = 10; // fixed lookup zoom, independent of the map's own display zoom
const tileCache = new Map(); // "z/x/y" -> Promise<{elevations,size}|null>, so concurrent lookups on the same not-yet-fetched tile share one fetch
let offlinePmtiles = null;
// fracX/fracY (not pixel coordinates): the tile hasn't been fetched yet at
// this point, so its actual pixel size isn't known — online tiles are
// 256px PNG, offline ones (Mapterhorn, see lib/maptool/pmtiles_builder.rb)
// are 512px WebP. elevationAt scales these once the real size is known.
function lngLatToTile(lng, lat, zoom) {
const n = 2 ** zoom;
const x = ((lng + 180) / 360) * n;
const latRad = (lat * Math.PI) / 180;
const y = ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * n;
const tileX = Math.floor(x);
const tileY = Math.floor(y);
return { tileX, tileY, fracX: x - tileX, fracY: y - tileY };
}
async function decodeTerrarium(bytes, mime) {
const bitmap = await createImageBitmap(new Blob([bytes], { type: mime }));
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bitmap, 0, 0);
const { data } = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
const elevations = new Int16Array(bitmap.width * bitmap.height);
for (let i = 0; i < elevations.length; i++) {
elevations[i] = data[i * 4] * 256 + data[i * 4 + 1] + data[i * 4 + 2] / 256 - 32768;
}
return { elevations, size: bitmap.width };
}
async function fetchTileBytes(z, x, y, offlineAvailable) {
if (offlineAvailable) {
if (!offlinePmtiles) offlinePmtiles = new window.pmtiles.PMTiles(`${window.location.origin}/tiles/terrain.pmtiles`);
const result = await offlinePmtiles.getZxy(z, x, y);
return result ? { bytes: new Uint8Array(result.data), mime: "image/webp" } : null;
}
const url = ONLINE_TERRAIN_TILES[0].replace("{z}", z).replace("{x}", x).replace("{y}", y);
const response = await fetch(url);
return response.ok ? { bytes: new Uint8Array(await response.arrayBuffer()), mime: "image/png" } : null;
}
function fetchTile(z, x, y, offlineAvailable) {
const key = `${z}/${x}/${y}`;
let cached = tileCache.get(key);
if (cached) return cached;
const promise = fetchTileBytes(z, x, y, offlineAvailable)
.then((result) => (result ? decodeTerrarium(result.bytes, result.mime) : null))
.catch(() => null);
tileCache.set(key, promise);
return promise;
}
// -> Promise<number|null> — null on a fetch/decode failure, or a point
// falling outside a partial offline extract's zoom range.
export async function elevationAt(lng, lat, offlineAvailable) {
const { tileX, tileY, fracX, fracY } = lngLatToTile(lng, lat, DEM_ZOOM);
const tile = await fetchTile(DEM_ZOOM, tileX, tileY, offlineAvailable);
if (!tile) return null;
const px = Math.min(tile.size - 1, Math.floor(fracX * tile.size));
const py = Math.min(tile.size - 1, Math.floor(fracY * tile.size));
return tile.elevations[py * tile.size + px];
}
+27 -27
View File
@@ -43,6 +43,7 @@ export class GpxPanelControl {
this._visible = new Set(); // file paths currently shown on the map this._visible = new Set(); // file paths currently shown on the map
this._interactiveLayers = new Set(); // point layer ids with click/hover wired up this._interactiveLayers = new Set(); // point layer ids with click/hover wired up
this._pointsByPath = new Map(); // path -> its waypoints FeatureCollection, for the category legend this._pointsByPath = new Map(); // path -> its waypoints FeatureCollection, for the category legend
this._linesByPath = new Map(); // path -> its lines/polygons FeatureCollection, same reason (a polygon/line note also carries a category)
this._hiddenCategories = new Set(); // category names toggled off in the legend this._hiddenCategories = new Set(); // category names toggled off in the legend
this._openDownloadMenu = null; // the one gpx-tree__download-menu currently open, if any this._openDownloadMenu = null; // the one gpx-tree__download-menu currently open, if any
@@ -116,7 +117,7 @@ export class GpxPanelControl {
mount(sidebar) { mount(sidebar) {
this._container = document.createElement("div"); this._container = document.createElement("div");
this._container.className = "sidebar-item"; this._container.className = "sidebar-item";
this._container.innerHTML = `<button type="button" class="sidebar-icon" title="Tracks" aria-label="Tracks">${GPX_ICON}</button>`; this._container.innerHTML = `<button type="button" class="sidebar-icon" title="Tracks &amp; Points" aria-label="Tracks &amp; Points">${GPX_ICON}</button>`;
this._icon = this._container.querySelector(".sidebar-icon"); this._icon = this._container.querySelector(".sidebar-icon");
this._icon.addEventListener("click", () => this._togglePanel()); this._icon.addEventListener("click", () => this._togglePanel());
@@ -597,6 +598,7 @@ export class GpxPanelControl {
this._addClusterLayers(pointsId, path); this._addClusterLayers(pointsId, path);
if (points.features.length > 0) this._pointsByPath.set(path, points); if (points.features.length > 0) this._pointsByPath.set(path, points);
if (lines.features.length > 0) this._linesByPath.set(path, lines);
this._updateLegend(); this._updateLegend();
this._applyCategoryFilter(); this._applyCategoryFilter();
@@ -711,7 +713,7 @@ export class GpxPanelControl {
const editable = feature.properties.category === "Note" && !!feature.properties.id && isLoggedIn(); const editable = feature.properties.category === "Note" && !!feature.properties.id && isLoggedIn();
const popup = new Popup({ closeButton: true, maxWidth: "260px" }) const popup = new Popup({ closeButton: true, maxWidth: "260px" })
.setLngLat(event.lngLat) .setLngLat(event.lngLat)
.setHTML(popupHtml(feature.properties, { editable, lngLat: event.lngLat })) .setHTML(popupHtml(feature.properties, { editable }))
.addTo(this._map); .addTo(this._map);
if (editable) this._wireNoteActions(popup, path, feature); if (editable) this._wireNoteActions(popup, path, feature);
}); });
@@ -763,7 +765,7 @@ export class GpxPanelControl {
const bodyInput = content.querySelector(".gpx-popup__edit-body"); const bodyInput = content.querySelector(".gpx-popup__edit-body");
titleInput.focus(); titleInput.focus();
content.querySelector(".gpx-popup__edit-cancel").addEventListener("click", () => { content.querySelector(".gpx-popup__edit-cancel").addEventListener("click", () => {
content.innerHTML = popupHtml(feature.properties, { editable: true, lngLat: popup.getLngLat() }); content.innerHTML = popupHtml(feature.properties, { editable: true });
this._wireNoteActions(popup, path, feature); this._wireNoteActions(popup, path, feature);
}); });
content.querySelector(".gpx-popup__edit-save").addEventListener("click", async () => { content.querySelector(".gpx-popup__edit-save").addEventListener("click", async () => {
@@ -843,6 +845,7 @@ export class GpxPanelControl {
if (this._map.getSource(pointsId)) this._map.removeSource(pointsId); if (this._map.getSource(pointsId)) this._map.removeSource(pointsId);
this._pointsByPath.delete(path); this._pointsByPath.delete(path);
this._linesByPath.delete(path);
this._updateLegend(); this._updateLegend();
} }
@@ -853,8 +856,8 @@ export class GpxPanelControl {
// on the map, not just a read-only color key. // on the map, not just a read-only color key.
_updateLegend() { _updateLegend() {
const categories = new Map(); // category name -> { color, icon } const categories = new Map(); // category name -> { color, icon }
for (const points of this._pointsByPath.values()) { for (const collection of [...this._pointsByPath.values(), ...this._linesByPath.values()]) {
for (const feature of points.features) { for (const feature of collection.features) {
const category = feature.properties.category; const category = feature.properties.category;
if (category && !categories.has(category)) { if (category && !categories.has(category)) {
categories.set(category, { color: feature.properties.color || TRACK_COLOR, icon: feature.properties.icon }); categories.set(category, { color: feature.properties.color || TRACK_COLOR, icon: feature.properties.icon });
@@ -909,6 +912,16 @@ export class GpxPanelControl {
const visible = points.features.filter((f) => !this._hiddenCategories.has(f.properties.category)); const visible = points.features.filter((f) => !this._hiddenCategories.has(f.properties.category));
source.setData({ type: "FeatureCollection", features: visible }); source.setData({ type: "FeatureCollection", features: visible });
} }
// Same re-slicing for polygon/line notes sharing the "lines" source with
// plain tracks/routes — those have no `category` of their own, so
// `_hiddenCategories.has(undefined)` is always false and they're never
// affected by this, only a hidden Note (or other categorized) shape is.
for (const [path, lines] of this._linesByPath) {
const source = this._map.getSource(linesSourceId(path));
if (!source) continue;
const visible = lines.features.filter((f) => !this._hiddenCategories.has(f.properties.category));
source.setData({ type: "FeatureCollection", features: visible });
}
} }
async _uploadFiles(fileList, folder) { async _uploadFiles(fileList, folder) {
@@ -1047,7 +1060,11 @@ function uploadFile(formData, onUploadComplete) {
// fixed square size (the source SVGs have no explicit width/height, just a // fixed square size (the source SVGs have no explicit width/height, just a
// viewBox, so one is injected — otherwise an <img> falls back to the CSS // viewBox, so one is injected — otherwise an <img> falls back to the CSS
// default replaced-element size instead of the viewBox's aspect). // default replaced-element size instead of the viewBox's aspect).
async function svgToImageData(svgMarkup, size) { // Exported: overlays.js's own live layers (Wikipedia, more to come per its
// own doc comment) need the same SVG-to-map-image/badge-id/escaping
// helpers this file already has, and reimplementing them there would just
// be the same three functions typed twice.
export async function svgToImageData(svgMarkup, size) {
// xmlns is required here even though the other hand-drawn icons in this // xmlns is required here even though the other hand-drawn icons in this
// app get away without it — those are inlined via innerHTML, where the // app get away without it — those are inlined via innerHTML, where the
// HTML parser assigns the SVG namespace from context, but a standalone // HTML parser assigns the SVG namespace from context, but a standalone
@@ -1070,7 +1087,7 @@ async function svgToImageData(svgMarkup, size) {
} }
} }
function badgeImageId(color) { export function badgeImageId(color) {
return `${BADGE_IMAGE_PREFIX}${color}`; return `${BADGE_IMAGE_PREFIX}${color}`;
} }
@@ -1086,26 +1103,13 @@ function gpxFileUrl(path) {
return `/api/gpx/file/${path.split("/").map(encodeURIComponent).join("/")}`; return `/api/gpx/file/${path.split("/").map(encodeURIComponent).join("/")}`;
} }
// Set once from map.js after its one-time GET /api/kiwix check (same
// fetch-once-at-load story as the offline-tiles/terrain-tiles checks) —
// module-level rather than threaded through every popupHtml call, since
// it's the same answer for the whole page's lifetime.
let kiwixConfig = { available: false, url: "" };
export function setKiwixConfig(config) {
kiwixConfig = config;
}
// properties come straight from an uploaded GPX file — untrusted input — // properties come straight from an uploaded GPX file — untrusted input —
// so every value is escaped before landing in the popup's HTML. // so every value is escaped before landing in the popup's HTML.
// editable (only ever true for a "Note" feature the viewer is logged in // editable (only ever true for a "Note" feature the viewer is logged in
// for — see _registerPointInteractivity) adds Edit/Delete buttons; the // for — see _registerPointInteractivity) adds Edit/Delete buttons; the
// click handling for them lives in _wireNoteActions, not here, since this // click handling for them lives in _wireNoteActions, not here, since this
// only builds markup, it has no map/store access of its own. lngLat (the // only builds markup, it has no map/store access of its own.
// popup's own anchor point) drives the "Nearby on Wikipedia" link — kiwix- function popupHtml({ name, description, time, category, photos, icon }, { editable = false } = {}) {
// serve's own geo search, linked to directly rather than proxied/embedded
// (it already has a full search UI of its own, see docs/development.adoc).
function popupHtml({ name, description, time, category, photos, icon }, { editable = false, lngLat = null } = {}) {
const parts = []; const parts = [];
if (name) { if (name) {
const glyph = icon && POI_ICONS[icon] ? `<span class="gpx-popup__header-icon">${POI_ICONS[icon]}</span>` : ""; const glyph = icon && POI_ICONS[icon] ? `<span class="gpx-popup__header-icon">${POI_ICONS[icon]}</span>` : "";
@@ -1126,10 +1130,6 @@ function popupHtml({ name, description, time, category, photos, icon }, { editab
parts.push(`<p class="gpx-popup__time">${escapeHtml(parsed.toLocaleString())}</p>`); parts.push(`<p class="gpx-popup__time">${escapeHtml(parsed.toLocaleString())}</p>`);
} }
} }
if (kiwixConfig.available && lngLat) {
const wikiUrl = `${kiwixConfig.url}/search?pattern=&latitude=${lngLat.lat}&longitude=${lngLat.lng}&distance=2000`;
parts.push(`<p class="gpx-popup__wiki"><a href="${escapeHtml(wikiUrl)}" target="_blank" rel="noopener noreferrer">Nearby on Wikipedia →</a></p>`);
}
if (editable) { if (editable) {
parts.push( parts.push(
`<div class="gpx-popup__note-actions"><button type="button" class="gpx-popup__edit">Edit</button><button type="button" class="gpx-popup__delete">Delete</button></div>`, `<div class="gpx-popup__note-actions"><button type="button" class="gpx-popup__edit">Edit</button><button type="button" class="gpx-popup__delete">Delete</button></div>`,
@@ -1160,7 +1160,7 @@ function formatDuration(totalSeconds) {
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`; return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
} }
function escapeHtml(text) { export function escapeHtml(text) {
const div = document.createElement("div"); const div = document.createElement("div");
div.textContent = text; div.textContent = text;
return div.innerHTML; return div.innerHTML;
+442
View File
@@ -0,0 +1,442 @@
import { WALKING_ICON } from "/js/icons.js";
import { firstSymbolLayerId } from "/js/radar.js";
import { escapeHtml } from "/js/gpx.js";
import { lineLength, formatDistance } from "/js/measure.js";
import { buildLineGraph, orderLanesAsync, LaneLayer } from "/vendor/maplibre-gl-lanes/maplibre-gl-lanes.mjs";
// Overpass has permissive CORS (verified: Access-Control-Allow-Origin: *),
// so this queries it directly from the browser — no server-side proxy
// needed, same "public API, no key" story as the Nominatim search already
// hits from here. One query per click (not per pan/move like the
// Wikipedia layer's live refetch), so this stays well within Overpass's
// shared rate limits.
//
// Three independent public mirrors (also CORS-open, verified), raced with
// Promise.any rather than called one at a time — the shared free instances
// vary a lot in load from one moment to the next, and a multi-second wait
// on a single one was the main complaint. Whichever answers first wins;
// the others are simply left to finish and get GC'd, not worth the extra
// AbortController plumbing for a single one-off per-click request.
const OVERPASS_APIS = [
"https://overpass-api.de/api/interpreter",
"https://overpass.kumi.systems/api/interpreter",
"https://overpass.private.coffee/api/interpreter",
];
const CLICK_RADIUS_M = 40;
// OSM colour tag values are free text (untrusted) — only used as a CSS
// background if they look like an actual color (hex or a plain word),
// never interpolated unchecked into a style attribute.
const SAFE_COLOR = /^(#[0-9a-fA-F]{3,8}|[a-zA-Z]+)$/;
// Pre-rendered raster tiles, not vector — waymarkedtrails.org already
// colors each route by its OSM `colour` tag (the KST/KČT-style paint-blaze
// colors used in Slovakia/Czechia), falling back to plain red where a route
// has none, so there's no styling to do here ourselves. Used whenever no
// offline extract is available at all — same "quietly renders nothing
// without a network" story as the Wikipedia/Radar/Contour lines overlays.
const ONLINE_SOURCE_ID = "hiking-trails-online";
const ONLINE_LAYER_ID = "hiking-trails-online-line";
const TILE_URL = "https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png";
const ATTRIBUTION =
'Trails &copy; <a href="https://waymarkedtrails.org" target="_blank">Waymarked Trails</a> (CC-BY-SA), map data &copy; OpenStreetMap contributors';
const OPACITY = 0.85;
// Offline: real vector trails for each country the viewer actually picks,
// GeoJSON generated at build time by Maptool::HikingTrails straight
// from the same Geofabrik .osm.pbf offline.pmtiles itself is built from
// (see lib/maptool/hiking_trails.rb, PmtilesBuilder.extract_hiking_trails)
// — no network needed, and no waiting on Overpass for click details either
// (hit-tested locally via LaneLayer.queryLane instead, see _handleClick
// below). One file per country rather than one combined file: a viewer
// covering a dozen countries' worth of offline map data rarely wants all
// of their hiking trails loaded at once, so this is opt-in per country
// instead.
//
// Drawn with maplibre-gl-lanes rather than a plain line layer: a stretch
// of path several marked routes share (very common with KST blazes) is
// otherwise one line drawn over and over, only the topmost color visible.
// Lanes puts each route in its own parallel lane instead. It needs routes
// sharing a path to share exact vertices, which OSM relations over shared
// ways already do. One LaneLayer over every picked country, not one per
// country, so lanes at a border stay ordered together. Doesn't drape over
// 3D terrain (sinks under raised ground, warns on the console) — a known
// limitation of the library, not worth working around for an overlay.
const LANES_LAYER_ID = "hiking-trails-lanes";
const LANE_SIZES = () => ({ spacing: 3.5, width: 3, casingWidth: 0.5 });
function offlineUrl(code) {
return `/tiles/hiking_trails/${code}.geojson`;
}
// waymarkedtrails.org's own fallback for an untagged route.
const DEFAULT_TRAIL_COLOR = "#c1272d";
// Real-world `colour` values seen in production data (see the 2026-09-13
// extraction against SK.osm.pbf) — anything outside this set (typos like
// "yelllow", multi-value combos like "blue, yellow", stray hex codes) falls
// back to DEFAULT_TRAIL_COLOR rather than being trusted as a CSS value
// straight from OSM tag text.
const KNOWN_TRAIL_COLORS = new Set(["red", "yellow", "blue", "green", "orange", "purple", "brown", "black", "white", "grey", "gray", "darkblue"]);
function normalizedTrailColor(raw) {
if (!raw) return DEFAULT_TRAIL_COLOR;
const trimmed = raw.trim();
if (KNOWN_TRAIL_COLORS.has(trimmed.toLowerCase())) return trimmed.toLowerCase();
return /^#[0-9a-fA-F]{3,8}$/.test(trimmed) ? trimmed : DEFAULT_TRAIL_COLOR;
}
// Online mode's highlight outline for whichever route(s) the last click
// matched — drawn as its own separate source/layer (real geometry, from the
// Overpass response), since the raster tiles have no paintable features to
// highlight at all. Offline lanes use LaneLayer.setHighlight instead. Same "yellow outline on a picked
// route" treatment the earlier SVG prototype visualization used.
const HIGHLIGHT_SOURCE_ID = "hiking-trails-highlight";
const HIGHLIGHT_LAYER_ID = "hiking-trails-highlight-line";
// An Overlays panel item — a plain on/off checkbox (raster overlay) until
// offline extracts exist for at least one country, at which point it
// becomes an expandable per-country checklist instead (same expand shape
// as RadarControl), since a viewer with a dozen countries' worth of
// offline map data rarely wants all of their hiking trails loaded, let
// alone parsed by the browser, at once.
export class HikingTrailsControl {
constructor(map) {
this._map = map;
this._active = false; // the online-mode checkbox's own state
this._offlineCountries = []; // codes with an extract available on the server
this._selectedCountries = new Set(); // codes currently shown, offline mode
this._trailData = new Map(); // code -> fetched GeoJSON, kept across toggles/style swaps
this._routeInfo = new Map(); // String(relation id) -> feature properties, for the panel
this._lanes = null; // the current LaneLayer, null when no country is shown
map.on("click", (event) => this._handleClick(event));
this._panel = document.getElementById("trail-panel");
this._panelBody = this._panel.querySelector(".trail-panel__body");
this._panel.querySelector(".trail-panel__close").addEventListener("click", () => this._hidePanel());
// setStyle() (switching Online/Offline) tears down all custom sources/
// layers — put whatever was showing back, same as every other
// Overlays control. The highlight is transient (tied to whichever
// trail is showing in the panel, if any) — simpler to just drop it
// here than try to carry it across a style swap.
map.on("style.load", () => {
if (this._active) this._addOnlineLayer();
this._clearHighlight();
if (this._selectedCountries.size > 0) this._rebuildLanes();
});
}
mount(li) {
li.innerHTML = `
<div class="overlays-panel__row">
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${WALKING_ICON}</span>
<button type="button" class="overlays-panel__name">Hiking trails</button>
</div>
<div class="overlays-panel__details" hidden>
<ul class="hiking-trail-countries"></ul>
</div>
`;
this._checkbox = li.querySelector(".overlays-panel__toggle");
this._nameButton = li.querySelector(".overlays-panel__name");
this._details = li.querySelector(".overlays-panel__details");
this._countryList = li.querySelector(".hiking-trail-countries");
this._checkbox.addEventListener("change", (event) => this._toggleOnline(event.target.checked));
this._nameButton.addEventListener("click", () => {
if (this._offlineCountries.length === 0) return; // nothing to expand into yet
const expanding = this._details.hidden;
this._details.hidden = !expanding;
this._nameButton.setAttribute("aria-expanded", String(expanding));
});
}
// Called once map.js's GET /api/hiking-trails check resolves. Switches
// this item from "online raster, plain on/off" to "offline vector,
// pick your countries" — the two are mutually exclusive rather than
// both offered at once, since once a real local extract exists there's
// no reason to still fall back to a third-party tile server for any of
// it.
setOfflineCountries(countries) {
this._offlineCountries = countries;
if (countries.length === 0) return;
if (this._active) this._toggleOnline(false);
this._checkbox.hidden = true;
this._countryList.innerHTML = countries
.map(
(code) => `
<li class="hiking-trail-countries__item">
<label>
<input type="checkbox" class="hiking-trail-countries__toggle" value="${code}">
${code}
</label>
</li>
`,
)
.join("");
this._countryList.querySelectorAll(".hiking-trail-countries__toggle").forEach((input) => {
input.addEventListener("change", (event) => this._toggleCountry(event.target.value, event.target.checked));
});
}
_toggleOnline(active) {
this._active = active;
if (active) this._addOnlineLayer();
else this._removeOnlineLayer();
}
async _toggleCountry(code, active) {
if (!active) {
this._selectedCountries.delete(code);
this._rebuildLanes();
return;
}
this._selectedCountries.add(code);
if (!this._trailData.has(code)) {
let geojson;
try {
geojson = await (await fetch(offlineUrl(code))).json();
} catch {
return; // degrade quietly, same story as every other optional layer here
}
// Normalized once here so an untrustworthy `colour` tag value never
// reaches the layer's color directly — same reasoning gpx.js's own
// feature.properties.color ||= TRACK_COLOR normalization gives.
for (const feature of geojson.features) {
feature.properties.color = normalizedTrailColor(feature.properties.colour);
}
this._trailData.set(code, geojson);
if (!this._selectedCountries.has(code)) return; // toggled back off while fetching
}
this._rebuildLanes();
}
// Rebuilt from scratch on every country toggle and style swap (setStyle
// drops custom layers too) — lane order depends on every route drawn, so
// there's no cheaper incremental path. Ordering runs in a worker; a newer
// rebuild started meanwhile wins, same token guard as _handleOnlineClick.
async _rebuildLanes() {
const build = (this._lanesBuild = {});
const features = [...this._selectedCountries].flatMap((code) => this._trailData.get(code)?.features || []);
let graph = null;
if (features.length > 0) {
graph = buildLineGraph(features, { routeProperty: "id" });
await orderLanesAsync(graph);
}
if (this._lanesBuild !== build) return;
this._hidePanel();
if (this._map.getLayer(LANES_LAYER_ID)) this._map.removeLayer(LANES_LAYER_ID);
this._lanes = null;
this._routeInfo = new Map(features.map((feature) => [String(feature.properties.id), feature.properties]));
if (!graph) return;
this._lanes = new LaneLayer({ id: LANES_LAYER_ID, graph, sizes: LANE_SIZES, casingColor: "rgba(255, 255, 255, 0.8)" });
// Below labels (same firstSymbolLayerId spot contour.js/radar.js use),
// so place names/POI icons stay readable on top of the trail lines.
this._map.addLayer(this._lanes, firstSymbolLayerId(this._map));
}
_handleClick(event) {
if (this._selectedCountries.size > 0) this._handleOfflineClick(event);
else if (this._active) this._handleOnlineClick(event);
}
// The lanes have real geometry to hit-test directly — no network
// round-trip, so this resolves instantly. Each route has its own lane, so
// a click picks exactly one, highlighted by the layer itself. A genuine
// miss just closes an already-open panel rather than showing a "not
// found" message, same as clicking empty space anywhere else on the map.
_handleOfflineClick(event) {
const hit = this._lanes?.queryLane(event.point, 8);
const info = hit && this._routeInfo.get(String(hit.route));
if (!info) {
this._hidePanel();
return;
}
this._lanes.setHighlight(hit.route);
this._showPanel(routeHtml(info));
}
_showPanel(html) {
this._panelBody.innerHTML = html;
this._panel.hidden = false;
}
_hidePanel() {
if (this._panel.hidden) return;
this._panel.hidden = true;
this._clearHighlight();
}
// The raster tiles have no queryable features (they're pixels), so
// "what trail is this" is answered with a one-off Overpass query instead
// of a MapLibre feature hit-test — an `around` search rather than an
// exact point match, since a click rarely lands exactly on the (already
// pixel-thick) rendered line.
async _handleOnlineClick(event) {
const { lng, lat } = event.lngLat;
this._showPanel(`<p class="gpx-popup__category">Looking for trails…</p>`);
// A newer click's request replaces this one while Overpass is still
// answering — same "in-flight response arrives after it's already
// stale" guard the old Popup-identity check gave, just keyed off a
// fresh token each click instead of a fresh Popup instance.
const request = (this._onlineRequest = {});
let relations;
try {
relations = await fetchNearbyRoutes(lng, lat);
} catch {
if (this._onlineRequest === request) this._panelBody.innerHTML = `<p class="gpx-popup__category">Could not reach Overpass.</p>`;
return;
}
if (this._onlineRequest !== request) return;
if (relations.length === 0) {
this._panelBody.innerHTML = `<p class="gpx-popup__category">No hiking trail found here.</p>`;
return;
}
this._panelBody.innerHTML = relations.map((relation) => routeHtml(overpassRouteInfo(relation))).join("<hr>");
this._setHighlight(relations.map((relation) => ({ geometry: routeGeometry(relation), color: normalizedTrailColor(relation.tags?.colour) })));
}
_addOnlineLayer() {
if (this._map.getSource(ONLINE_SOURCE_ID)) return;
this._map.addSource(ONLINE_SOURCE_ID, {
type: "raster",
tiles: [TILE_URL],
tileSize: 256,
maxzoom: 17,
attribution: ATTRIBUTION,
});
// Below labels (same firstSymbolLayerId spot contour.js/radar.js use),
// so place names/POI icons stay readable on top of the trail lines.
this._map.addLayer(
{ id: ONLINE_LAYER_ID, type: "raster", source: ONLINE_SOURCE_ID, paint: { "raster-opacity": OPACITY } },
firstSymbolLayerId(this._map),
);
}
_removeOnlineLayer() {
if (this._map.getLayer(ONLINE_LAYER_ID)) this._map.removeLayer(ONLINE_LAYER_ID);
if (this._map.getSource(ONLINE_SOURCE_ID)) this._map.removeSource(ONLINE_SOURCE_ID);
}
// features: an array of { geometry, color } — geometry a GeoJSON
// LineString or MultiLineString (mixed is fine), color the trail's own
// (already-normalized) colour — everything the last click matched, drawn
// as one highlight layer (a wider outline in the trail's own color,
// rather than a fixed color that would repaint it) on top of the base
// trail rendering.
_setHighlight(features) {
const data = {
type: "FeatureCollection",
features: features.map(({ geometry, color }) => ({ type: "Feature", properties: { color }, geometry })),
};
const source = this._map.getSource(HIGHLIGHT_SOURCE_ID);
if (source) {
source.setData(data);
return;
}
this._map.addSource(HIGHLIGHT_SOURCE_ID, { type: "geojson", data });
this._map.addLayer({
id: HIGHLIGHT_LAYER_ID,
type: "line",
source: HIGHLIGHT_SOURCE_ID,
layout: { "line-cap": "round", "line-join": "round" },
paint: { "line-color": ["get", "color"], "line-width": 5, "line-opacity": 0.9 },
});
}
_clearHighlight() {
this._lanes?.setHighlight(null);
if (this._map.getLayer(HIGHLIGHT_LAYER_ID)) this._map.removeLayer(HIGHLIGHT_LAYER_ID);
if (this._map.getSource(HIGHLIGHT_SOURCE_ID)) this._map.removeSource(HIGHLIGHT_SOURCE_ID);
}
}
// way(around:...) finds candidate ways near the click, then rel(bw...)
// walks backward from those to their parent relations — the same
// find-the-way-then-its-relation shape as clicking a route on
// waymarkedtrails.org's own site. `out geom` inlines each member way's
// full coordinate list right on the relation, so no separate stitching
// step is needed to get real geometry back.
async function queryOverpass(url, query) {
const response = await fetch(url, { method: "POST", body: `data=${encodeURIComponent(query)}` });
if (!response.ok) throw new Error(`overpass ${response.status}`);
return response.json();
}
async function fetchNearbyRoutes(lng, lat) {
const query = `[out:json][timeout:15];way(around:${CLICK_RADIUS_M},${lat},${lng})->.w;rel(bw.w)["route"="hiking"];out geom;`;
const data = await Promise.any(OVERPASS_APIS.map((url) => queryOverpass(url, query)));
return (data.elements || []).filter((element) => element.type === "relation");
}
// OSM doesn't tag a route's length directly — summed from each member
// way's own geometry instead, same haversine-sum measure.js's own Measure
// tool uses for a hand-drawn line. Shared endpoints between adjacent ways
// get walked twice, same as how a real hike over those ways would. Mirrors
// Maptool::HikingTrails.relation_feature's own distance calc server-side
// (lib/maptool/hiking_trails.rb) — this is the online, no-server-file path.
function routeDistance(relation) {
return (relation.members || [])
.filter((member) => member.type === "way" && member.geometry)
.reduce((total, member) => total + lineLength(member.geometry.map((point) => [point.lon, point.lat])), 0);
}
// Same member-way walk as routeDistance above, collecting coordinates
// instead of summing them — the online click highlight's geometry source,
// since there's no vector feature to read it back from the way the offline
// layer's own queryRenderedFeatures result already carries one.
function routeGeometry(relation) {
const lines = (relation.members || [])
.filter((member) => member.type === "way" && member.geometry)
.map((member) => member.geometry.map((point) => [point.lon, point.lat]));
return { type: "MultiLineString", coordinates: lines };
}
// Adapts a raw Overpass relation into the same shape offline GeoJSON
// features' properties already carry (lib/maptool/hiking_trails.rb's own
// ROUTE_TAGS + distance_meters + id) — one HTML builder (routeHtml) below
// serves both instead of two near-identical ones.
function overpassRouteInfo(relation) {
const tags = relation.tags || {};
return {
id: relation.id,
name: tags.name,
description: tags.description,
colour: tags.colour,
network: tags.network,
operator: tags.operator,
ref: tags.ref,
distance_meters: routeDistance(relation),
};
}
function routeHtml(info) {
const name = info.name || info.description || "Hiking trail";
const swatch = SAFE_COLOR.test(info.colour || "")
? `<span class="hiking-trail-popup__swatch" style="background:${info.colour}"></span>`
: "";
const meta = [formatDistance(info.distance_meters || 0)];
if (info.ref) meta.push(`#${info.ref}`);
if (info.network) meta.push(info.network);
if (info.operator) meta.push(escapeHtml(info.operator));
const parts = [
`<div class="gpx-popup__header"><strong>${escapeHtml(name)}</strong>${swatch}</div>`,
`<p class="gpx-popup__category">${meta.filter(Boolean).join(" · ")}</p>`,
];
// description already used as the title above when there's no name — only
// shown again here as its own line if there both is a name *and* a
// description to add, so it's never rendered twice for the common
// no-name case.
if (info.name && info.description) parts.push(`<p class="gpx-popup__description">${escapeHtml(info.description)}</p>`);
parts.push(
`<p class="gpx-popup__link"><a href="https://www.openstreetmap.org/relation/${info.id}" target="_blank" rel="noopener noreferrer">View on OpenStreetMap →</a></p>`,
);
return parts.join("");
}
+106 -11
View File
@@ -7,10 +7,14 @@ export const LAYERS_ICON = `
<path d="M2 14l10 6 10-6"/> <path d="M2 14l10 6 10-6"/>
</svg>`; </svg>`;
export const HILLSHADE_ICON = ` // Two overlapping circles — distinct from LAYERS_ICON's stacked-planes
// look (that one picks a *base* map; this one is for the sidebar flyout
// toggling live overlay layers, e.g. Wikipedia, on top of whichever base
// map is showing) while still reading as "layers combining."
export const OVERLAYS_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round">
<path d="M2 18 9 7l3.5 5.5L15 9l7 9z"/> <circle cx="9" cy="12" r="6"/>
<path d="M2 18h20"/> <circle cx="15" cy="12" r="6"/>
</svg>`; </svg>`;
export const NOTE_PIN_ICON = ` export const NOTE_PIN_ICON = `
@@ -26,11 +30,6 @@ export const GPX_ICON = `
<path d="M6.5 7.5C10 11 8 13 12 13s2 5 6.5 5"/> <path d="M6.5 7.5C10 11 8 13 12 13s2 5 6.5 5"/>
</svg>`; </svg>`;
export const RADAR_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round">
<path d="M7 18a4 4 0 1 1 .7-7.94A5 5 0 0 1 17 9a4 4 0 0 1 0 8H7z"/>
</svg>`;
export const GNSS_ICON = ` export const GNSS_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round">
<circle cx="12" cy="12" r="5.5"/> <circle cx="12" cy="12" r="5.5"/>
@@ -44,6 +43,31 @@ export const RULER_ICON = `
<path d="M9 10l1.5 1.5M12 7l1.5 1.5M15 4l1.5 1.5M6 13l1.5 1.5"/> <path d="M9 10l1.5 1.5M12 7l1.5 1.5M15 4l1.5 1.5M6 13l1.5 1.5"/>
</svg>`; </svg>`;
// Toolbar readout glyphs (public/js/sun.js's text, assembled with these by
// map.js) — same monoline style as the sidebar icons above, no NPS source
// for either (that set has no sunrise/sunset pictogram).
export const SUNRISE_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 18h18"/>
<path d="M6 18a6 6 0 0 1 12 0"/>
<path d="M12 13V5"/>
<path d="M9 8l3-3 3 3"/>
</svg>`;
export const SUNSET_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 18h18"/>
<path d="M6 18a6 6 0 0 1 12 0"/>
<path d="M12 5v8"/>
<path d="M9 10l3 3 3-3"/>
</svg>`;
export const SEARCH_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="7"/>
<path d="M20 20l-4.35-4.35"/>
</svg>`;
export const NEW_FOLDER_ICON = ` export const NEW_FOLDER_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/> <path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/>
@@ -141,6 +165,46 @@ export const MAP_MARKER_ICON = `
// tinted at render time, see gpx.js's _registerCategoryGlyphs) so only the // tinted at render time, see gpx.js's _registerCategoryGlyphs) so only the
// alpha shape matters, not the actual `currentColor` value. // alpha shape matters, not the actual `currentColor` value.
// src/wilderness-22.svg — exported (unlike BICYCLE_TRAIL_ICON below, this
// one's also used outside this file) as the Overlays panel's Terrain
// shading glyph (overlays.js): the closest honest match in NPS's set for
// "mountains/terrain" — it has no weather or elevation-specific pictogram.
export const WILDERNESS_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><polygon points="18,18 13,15 17,15 13,11 16,11 12,7 15,7 12,4 14,4 11,0 8,4 10,4 7,7 10,7 6,11 9,11 5,15 9,15 4,18 10,18 10,22 12,22 12,18 "/></svg>`;
// Hand-drawn, not NPS-sourced — that set has no weather/precipitation
// pictogram to reuse for the Overlays panel's Radar item, unlike Wikipedia
// (WIKI_ICON) and Terrain shading (WILDERNESS_ICON) above.
export const RADAR_CLOUD_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor">
<path d="M17,10.1c-0.4-3-2.9-5.3-6-5.3c-2.5,0-4.7,1.5-5.6,3.8C3.4,8.9,1,11.1,1,13.9C1,16.7,3.3,19,6.1,19h10.3
c2.5,0,4.6-2,4.6-4.5C21,12.2,19.3,10.4,17,10.1z"/>
<rect x="6" y="20" width="1.6" height="2"/>
<rect x="10.2" y="20" width="1.6" height="2"/>
<rect x="14.4" y="20" width="1.6" height="2"/>
</svg>`;
// Hand-drawn, not NPS-sourced — same story as RADAR_CLOUD_ICON above, no
// terrain-shadow/sun-behind-a-ridge pictogram in that set either. Used as
// the Overlays panel's Terrain shadow glyph (shadow.js via overlays.js).
export const TERRAIN_SHADOW_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor">
<circle cx="16" cy="6" r="3"/>
<polygon points="0,19 6,9 9,13 13,6 22,19"/>
</svg>`;
// Hand-drawn, not NPS-sourced — no contour-line pictogram in that set
// either. Concentric rings via fill-rule evenodd, same solid-glyph
// convention as WILDERNESS_ICON/RADAR_CLOUD_ICON/TERRAIN_SHADOW_ICON above —
// used as the Overlays panel's Contour lines glyph (contour.js).
export const CONTOUR_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor" fill-rule="evenodd">
<path d="M21,11 A10,10 0 1,0 1,11 A10,10 0 1,0 21,11 Z
M18.5,11 A7.5,7.5 0 1,0 3.5,11 A7.5,7.5 0 1,0 18.5,11 Z
M16,11 A5,5 0 1,0 6,11 A5,5 0 1,0 16,11 Z
M13.5,11 A2.5,2.5 0 1,0 8.5,11 A2.5,2.5 0 1,0 13.5,11 Z"/>
</svg>`;
// src/bicycle-trail-22.svg — pulled out to its own constant since it's also // src/bicycle-trail-22.svg — pulled out to its own constant since it's also
// used unrasterized, inline, as PARK4NIGHT_TAGS.vtt below. // used unrasterized, inline, as PARK4NIGHT_TAGS.vtt below.
const BICYCLE_TRAIL_ICON = ` const BICYCLE_TRAIL_ICON = `
@@ -203,8 +267,7 @@ export const POI_ICONS = {
</svg>`, </svg>`,
// src/wilderness-22.svg // src/wilderness-22.svg
forest: ` forest: WILDERNESS_ICON,
<svg viewBox="0 0 22 22" fill="currentColor"><polygon points="18,18 13,15 17,15 13,11 16,11 12,7 15,7 12,4 14,4 11,0 8,4 10,4 7,7 10,7 6,11 9,11 5,15 9,15 4,18 10,18 10,22 12,22 12,18 "/></svg>`,
// src/bicycle-trail-22.svg // src/bicycle-trail-22.svg
sport_cycling: BICYCLE_TRAIL_ICON, sport_cycling: BICYCLE_TRAIL_ICON,
@@ -265,7 +328,11 @@ const FISHING_ICON = `
const SCENIC_VIEWPOINT_ICON = ` const SCENIC_VIEWPOINT_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g transform="translate(4.000000, 0.000000)"><path d="M3.2,11c0,1.2-0.9,2.2-2.1,2.2c-1.2,0-2.1-1-2.1-2.2 c0-1.2,0.9-2.2,2.1-2.2C2.2,8.8,3.2,9.8,3.2,11"/><path d="M4.6,10.4v1.3l10.6,0.8v-3L4.6,10.4"/><path d="M2.3,7.5l1.1,0.7L7.7,2L5.3,0.5L2.3,7.5"/><path d="M4.4,9.8l7.9-3.8l-1.4-2.6L3.8,8.6L4.4,9.8z"/><path d="M2.3,14.5l1.1-0.7l4.3,6.2l-2.4,1.5L2.3,14.5"/><path d="M3.8,13.4l0.6-1.2l7.9,3.8l-1.4,2.6L3.8,13.4"/></g></svg>`; <svg viewBox="0 0 22 22" fill="currentColor"><g transform="translate(4.000000, 0.000000)"><path d="M3.2,11c0,1.2-0.9,2.2-2.1,2.2c-1.2,0-2.1-1-2.1-2.2 c0-1.2,0.9-2.2,2.1-2.2C2.2,8.8,3.2,9.8,3.2,11"/><path d="M4.6,10.4v1.3l10.6,0.8v-3L4.6,10.4"/><path d="M2.3,7.5l1.1,0.7L7.7,2L5.3,0.5L2.3,7.5"/><path d="M4.4,9.8l7.9-3.8l-1.4-2.6L3.8,8.6L4.4,9.8z"/><path d="M2.3,14.5l1.1-0.7l4.3,6.2l-2.4,1.5L2.3,14.5"/><path d="M3.8,13.4l0.6-1.2l7.9,3.8l-1.4,2.6L3.8,13.4"/></g></svg>`;
const WALKING_ICON = ` // Exported (unlike the other POI glyphs around it) — also used as the
// Overlays panel's Hiking trails glyph (hiking_trails.js), same "reuse an
// existing NPS-sourced pictogram" story as WILDERNESS_ICON above rather
// than hand-drawing another one.
export const WALKING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M10.4,3.6c1,0,1.8-0.8,1.7-1.8c0-1-0.8-1.8-1.8-1.7c-1,0-1.8,0.8-1.7,1.8C8.6,2.8,9.5,3.6,10.4,3.6z"/><path d="M16.5,9.5L13.9,8l-2.2-3.4c-0.3-0.4-0.9-0.8-1.5-0.7C9.7,4,9.3,4.2,9,4.4L5.8,7.7C5.7,7.8,5.6,8,5.6,8.1l-0.4,3.6 c0,0,0,0.1,0,0.1c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.7-0.4,0.8-0.8l0.3-3.1l1.1-1.1l-1,8.6l-1.9,4.1c-0.1,0.1-0.1,0.3-0.1,0.5 c0,0.7,0.6,1.2,1.2,1.2c0.5,0,0.9-0.3,1.1-0.7l2-4.4c0-0.1,0.2-0.4,0.2-0.5l0.2-2.1l2.1,6.9c0.2,0.5,0.6,0.8,1.2,0.8 c0.7,0,1.2-0.6,1.2-1.2c0-0.1,0-0.1,0-0.2L11.5,11l0.4-3.1l0.8,1.3c0.1,0.1,0.1,0.2,0.2,0.2l2.8,1.6c0.1,0,0.2,0.1,0.4,0.1 c0.4,0,0.8-0.4,0.8-0.8C16.9,9.8,16.7,9.6,16.5,9.5z"/></svg>`; <svg viewBox="0 0 22 22" fill="currentColor"><path d="M10.4,3.6c1,0,1.8-0.8,1.7-1.8c0-1-0.8-1.8-1.8-1.7c-1,0-1.8,0.8-1.7,1.8C8.6,2.8,9.5,3.6,10.4,3.6z"/><path d="M16.5,9.5L13.9,8l-2.2-3.4c-0.3-0.4-0.9-0.8-1.5-0.7C9.7,4,9.3,4.2,9,4.4L5.8,7.7C5.7,7.8,5.6,8,5.6,8.1l-0.4,3.6 c0,0,0,0.1,0,0.1c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.7-0.4,0.8-0.8l0.3-3.1l1.1-1.1l-1,8.6l-1.9,4.1c-0.1,0.1-0.1,0.3-0.1,0.5 c0,0.7,0.6,1.2,1.2,1.2c0.5,0,0.9-0.3,1.1-0.7l2-4.4c0-0.1,0.2-0.4,0.2-0.5l0.2-2.1l2.1,6.9c0.2,0.5,0.6,0.8,1.2,0.8 c0.7,0,1.2-0.6,1.2-1.2c0-0.1,0-0.1,0-0.2L11.5,11l0.4-3.1l0.8,1.3c0.1,0.1,0.1,0.2,0.2,0.2l2.8,1.6c0.1,0,0.2,0.1,0.4,0.1 c0.4,0,0.8-0.4,0.8-0.8C16.9,9.8,16.7,9.6,16.5,9.5z"/></svg>`;
const GUIDED_TOURS_ICON = ` const GUIDED_TOURS_ICON = `
@@ -353,6 +420,34 @@ Object.assign(POI_ICONS, {
shop_convenience: STORE_ICON, shop_convenience: STORE_ICON,
}); });
// Hand-drawn, not NPS-sourced — no "download offline map data" pictogram
// in that set either. A tray with a downward arrow into it, same solid
// 22x22 currentColor convention as CONTOUR_ICON/TERRAIN_SHADOW_ICON above
// — used for the "Offline data" sidebar icon (offline_data.js).
export const OFFLINE_DATA_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor">
<path d="M10,0h2v10.5l3-3l1.4,1.4L11,14.3L5.6,8.9L7,7.5l3,3V0z"/>
<path d="M2,15h18v2H2z M2,18h18v2H2z"/>
</svg>`;
// src/bookstore-22.svg (NPS symbol library, same source/license as
// POI_ICONS above) — used as the fixed marker glyph for the "Wikipedia"
// map layer (gpx.js), not part of POI_ICONS itself: that set is keyed by
// OSM/p4n2gpx category and doubles as the note icon-picker's option list
// (notes.js), neither of which applies here — every point on this layer
// gets the same one glyph, there's no per-feature category to key it by.
export const WIKI_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor">
<path d="M16.6,20.7c-0.6,0-0.9-0.4-0.9-0.9c0-0.5,0.3-0.9,0.9-0.9c0.3,0,0.5-0.2,0.5-0.5
S16.9,18,16.6,18c-1.1,0-1.9,0.8-1.9,1.9c0,1,0.7,1.9,1.9,1.9c0.3,0,0.5-0.2,0.5-0.5S16.9,20.7,16.6,20.7L16.6,20.7z"/>
<path d="M5,18c0,0,0,0,0,0c0,3.1,0.5,4,2.7,4l8.9,0c0.3,0,0.5-0.2,0.5-0.5
c0-0.3-0.2-0.5-0.5-0.5H8c-1.5,0-2-0.5-2-3c0,0,0,0,0-0.1c0-0.3-0.2-0.5-0.5-0.5S5,17.7,5,18L5,18z"/>
<path d="M5,8c0-0.6,5-5,5-5h5h1c0.6,0,1,0.4,1,1v14c0,0.6-0.4,1-1,1H6c-0.6,0-1-0.4-1-1
V8z M9,4L6,18c0,0,10,0,10,0c0,0,0-14,0-14C16,4,9,4,9,4z"/>
<path d="M5,6.8c0-0.5,0.4-1.3,0.8-1.6l6.4-4.7C12.7,0.2,13,0.4,13,1v10
c0,0.6-0.3,1.7-0.7,2.1l-6.5,5.3C5.4,18.8,5,18.6,5,18V6.8z"/>
</svg>`;
export const LIVETRACK_ICON = ` export const LIVETRACK_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 21s7-7.58 7-12a7 7 0 0 0-14 0c0 4.42 7 12 7 12z"/> <path d="M12 21s7-7.58 7-12a7 7 0 0 0-14 0c0 4.42 7 12 7 12z"/>
+17 -1
View File
@@ -5,6 +5,11 @@ import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
const SESSIONS_URL = "/api/livetrack/sessions"; const SESSIONS_URL = "/api/livetrack/sessions";
const TRACK_SOURCE = "livetrack-track"; const TRACK_SOURCE = "livetrack-track";
const TRACK_COLOR = "#7209b7"; const TRACK_COLOR = "#7209b7";
// The planned route (only present when the activity was started from a
// saved Garmin course, see LiveTrackClient#fetch) — dashed and muted so it
// reads as a reference line, not more recorded data next to the actual
// track's own color.
const PLANNED_COLOR = "#888";
// Garmin devices post a new point roughly every 15s (postTrackPointFrequency // Garmin devices post a new point roughly every 15s (postTrackPointFrequency
// in the session data) — polling twice that often would just re-fetch the // in the session data) — polling twice that often would just re-fetch the
// same 400+KB page for nothing most of the time. // same 400+KB page for nothing most of the time.
@@ -171,11 +176,22 @@ export class LiveTrackPanelControl {
if (this._map.getSource(TRACK_SOURCE)) return; if (this._map.getSource(TRACK_SOURCE)) return;
this._map.addSource(TRACK_SOURCE, { type: "geojson", data: geojson }); this._map.addSource(TRACK_SOURCE, { type: "geojson", data: geojson });
// Added before the actual track's own line layer so it paints
// underneath — the recorded track is the thing that actually matters
// when the two overlap.
this._map.addLayer({
id: `${TRACK_SOURCE}-planned`,
type: "line",
source: TRACK_SOURCE,
filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", ["get", "kind"], "planned"]],
layout: { "line-cap": "round", "line-join": "round" },
paint: { "line-color": PLANNED_COLOR, "line-width": 2, "line-dasharray": [2, 2] },
});
this._map.addLayer({ this._map.addLayer({
id: `${TRACK_SOURCE}-line`, id: `${TRACK_SOURCE}-line`,
type: "line", type: "line",
source: TRACK_SOURCE, source: TRACK_SOURCE,
filter: ["==", ["geometry-type"], "LineString"], filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", ["get", "kind"], "actual"]],
layout: { "line-cap": "round", "line-join": "round" }, layout: { "line-cap": "round", "line-join": "round" },
paint: { "line-color": TRACK_COLOR, "line-width": 3 }, paint: { "line-color": TRACK_COLOR, "line-width": 3 },
}); });
+214 -215
View File
@@ -2,10 +2,11 @@ import {
Map as MaplibreMap, Map as MaplibreMap,
NavigationControl, NavigationControl,
Popup, Popup,
ScaleControl,
addProtocol, addProtocol,
} from "/vendor/maplibre-gl/maplibre-gl.mjs"; } from "/vendor/maplibre-gl/maplibre-gl.mjs";
import { RadarControl } from "/js/radar.js"; import { GpxPanelControl } from "/js/gpx.js";
import { GpxPanelControl, setKiwixConfig } from "/js/gpx.js"; import { OverlaysControl } from "/js/overlays.js";
import { GnssControl } from "/js/gnss.js"; import { GnssControl } from "/js/gnss.js";
import { LiveTrackPanelControl } from "/js/livetrack.js"; import { LiveTrackPanelControl } from "/js/livetrack.js";
import { TrackProfilePanel } from "/js/track_profile.js"; import { TrackProfilePanel } from "/js/track_profile.js";
@@ -13,7 +14,10 @@ import { MeasureControl } from "/js/measure.js";
import { NotesControl } from "/js/notes.js"; import { NotesControl } from "/js/notes.js";
import { PluginsControl } from "/js/plugins.js"; import { PluginsControl } from "/js/plugins.js";
import { LocateControl } from "/js/locate.js"; import { LocateControl } from "/js/locate.js";
import { LAYERS_ICON, HILLSHADE_ICON } from "/js/icons.js"; import { SearchControl } from "/js/search.js";
import { OfflineDataControl } from "/js/offline_data.js";
import { LAYERS_ICON, WAYPOINT_ICON, SUNRISE_ICON, SUNSET_ICON, PENDING_ICON } from "/js/icons.js";
import { formatCoords, formatSunInfo } from "/js/sun.js";
import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js"; import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
import { isLoggedIn } from "/js/auth.js"; import { isLoggedIn } from "/js/auth.js";
import HomeButton from "/vendor/npmap-home-button/home-button.esm.js"; import HomeButton from "/vendor/npmap-home-button/home-button.esm.js";
@@ -24,29 +28,46 @@ const CENTER = [17.1077, 48.1486];
const ZOOM = 13; const ZOOM = 13;
const ONLINE_STYLE_URL = "https://tiles.openfreemap.org/styles/liberty"; const ONLINE_STYLE_URL = "https://tiles.openfreemap.org/styles/liberty";
// Recolors OpenFreeMap Liberty to the same NPS-inspired palette as // Recolors OpenFreeMap Liberty to a warm, NPS-Park-Tiles-inspired palette —
// OFFLINE_THEME below — warm cream ground, muted roads/boundaries — mapped // cream ground, muted roads/boundaries — mapped onto Liberty's OpenMapTiles
// onto Liberty's OpenMapTiles layer ids instead of protomaps' theme roles // layer ids. Applied identically regardless of which PMTiles data actually
// (the two schemas don't share layer names, so this is a separate // backs the "openmaptiles" source (see offlineStyle() below): offline tiles
// hand-built table, not a shared object). Only the layers that dominate the // are built by Planetiler's own OpenMapTiles profile (docs/development.adoc),
// map's overall color read are touched; labels, halos, rail, and minor // the same schema Liberty already expects, so one patch serves both —
// overlays (cemetery/hospital/school/pitch/track shading, building // no separate offline theme/palette to keep in sync. Only the layers that
// outlines) are left at Liberty's own defaults — same "not a full // dominate the map's overall color read are touched; labels, halos, rail,
// recolor" scope as OFFLINE_THEME. Road casings have no protomaps-role // and minor overlays (cemetery/hospital/school/pitch/track shading,
// equivalent to reuse (offline roads don't get a separate outline), so // building outlines) are left at Liberty's own defaults.
// #c9a66b there is reasoned to fit the family rather than sourced from
// anywhere real.
// //
// wood/grass are deliberately left unpatched — Liberty's own green reads // wood/grass are deliberately left unpatched — Liberty's own green (a
// better than this app's earlier brown/khaki recolor, and OFFLINE_THEME's // translucent landcover_wood/landcover_grass wash) already reads well over
// wood_a/scrub_a below were changed to match Liberty's green instead. // the patched cream background.
const ONLINE_STYLE_PATCH = { const ONLINE_STYLE_PATCH = {
background: { "background-color": "#f9f6ef" }, background: { "background-color": "#f9f6ef" },
// Liberty washes every built-up area in a translucent gray-tan below
// z12 (landuse_residential, maxzoom 12 — gone by this app's default
// z13) that the offline schema has no equivalent of; protomaps just
// leaves that ground as plain background/earth. Matched to background
// here rather than left alone, so a zoomed-out view doesn't read as
// visibly dingier online than offline.
landuse_residential: { "fill-color": "#f9f6ef" },
landcover_sand: { "fill-color": "#f0e4c4" }, landcover_sand: { "fill-color": "#f0e4c4" },
water: { "fill-color": "#a3cedf" }, water: { "fill-color": "#a3cedf" },
park: { "fill-color": "#b3e378" }, // fill-outline-color isn't touched by a plain fill-color patch — Liberty
park_outline: { "line-color": "#546619" }, // draws it separately, defaulting to a bright green that reads as a
building: { "fill-color": "#ded7cc" }, // distinct border around named conservation areas (e.g. the Malé
// Karpaty CHKO). Matched to the fill itself, and park_outline's own
// dashed line hidden below, so a reserve blends in as ordinary forest —
// offline has no equivalent stroke at all (see park_a/park_b below),
// and the point was never to call reserves out as visibly different,
// on either side.
park: { "fill-color": "#b3e378", "fill-outline-color": "#b3e378" },
park_outline: { "line-opacity": 0 },
// fill-opacity: 0.5 matches the offline theme's own "buildings" layer —
// without it, Liberty's dense individual building footprints (each with
// its own outline) paint fully opaque and read much heavier/grayer than
// offline's softer, half-transparent fill over the same cream ground.
building: { "fill-color": "#ded7cc", "fill-opacity": 0.5 },
boundary_2: { "line-color": "#546619" }, boundary_2: { "line-color": "#546619" },
boundary_3: { "line-color": "#546619" }, boundary_3: { "line-color": "#546619" },
@@ -54,19 +75,30 @@ const ONLINE_STYLE_PATCH = {
// minor (residential/service/track), pedestrian — each repeated for its // minor (residential/service/track), pedestrian — each repeated for its
// tunnel_/bridge_ counterpart so a road doesn't change color when it // tunnel_/bridge_ counterpart so a road doesn't change color when it
// dips into a tunnel or crosses a bridge. Rail is left alone. // dips into a tunnel or crosses a bridge. Rail is left alone.
road_motorway: { "line-color": "#fdefbe" }, //
road_trunk_primary: { "line-color": "#fdefbe" }, // line-width below is a flat number, not Liberty's own zoom-interpolated
road_secondary_tertiary: { "line-color": "#fef7dd" }, // expression — picked to match the offline theme's actual rendered width
road_link: { "line-color": "#fef7dd" }, // at this app's default zoom (13; verified live via queryRenderedFeatures
road_motorway_link: { "line-color": "#fef7dd" }, // on both sides), since without it Liberty's own curve draws surface
// roads roughly twice as thick as offline at street level, reading as a
// much bolder road network online.
// ponytail: tuned for z13 only, not a matching curve across all zooms —
// will drift back apart at very different zoom levels. Upgrade path:
// sample offline's width at a few more zooms and replace with a matching
// ["interpolate", ["linear"], ["zoom"], ...] expression.
road_motorway: { "line-color": "#fdefbe", "line-width": 2.26 },
road_trunk_primary: { "line-color": "#fdefbe", "line-width": 2.26 },
road_secondary_tertiary: { "line-color": "#fef7dd", "line-width": 1.87 },
road_link: { "line-color": "#fef7dd", "line-width": 1 },
road_motorway_link: { "line-color": "#fef7dd", "line-width": 1 },
road_minor: { "line-color": "#fffdf8" }, road_minor: { "line-color": "#fffdf8" },
road_service_track: { "line-color": "#fffdf8" }, road_service_track: { "line-color": "#fffdf8" },
road_path_pedestrian: { "line-color": "#e9e2d8" }, road_path_pedestrian: { "line-color": "#e9e2d8" },
road_motorway_casing: { "line-color": "#c9a66b" }, road_motorway_casing: { "line-color": "#c9a66b", "line-width": 1.48 },
road_trunk_primary_casing: { "line-color": "#c9a66b" }, road_trunk_primary_casing: { "line-color": "#c9a66b", "line-width": 1.48 },
road_secondary_tertiary_casing: { "line-color": "#c9a66b" }, road_secondary_tertiary_casing: { "line-color": "#c9a66b", "line-width": 1 },
road_link_casing: { "line-color": "#c9a66b" }, road_link_casing: { "line-color": "#c9a66b", "line-width": 1.5 },
road_motorway_link_casing: { "line-color": "#c9a66b" }, road_motorway_link_casing: { "line-color": "#c9a66b", "line-width": 1.5 },
road_minor_casing: { "line-color": "#c9a66b" }, road_minor_casing: { "line-color": "#c9a66b" },
road_service_track_casing: { "line-color": "#c9a66b" }, road_service_track_casing: { "line-color": "#c9a66b" },
@@ -157,95 +189,45 @@ async function loadOnlineStyle() {
const ONLINE_STYLE = await loadOnlineStyle(); const ONLINE_STYLE = await loadOnlineStyle();
// A vendored copy of the same Liberty style ONLINE_STYLE fetches live
// (public/vendor/liberty-style/liberty.json — a point-in-time snapshot, not
// auto-updated) that offlineStyle() below patches+repoints the exact same
// way. Offline mode can't depend on ONLINE_STYLE_URL actually being
// reachable — that fetch failing/timing out is the normal case in the
// field, not an edge case — so this is a local, same-origin fetch instead
// (fast and always available, since it's served by this app itself).
const OFFLINE_STYLE_TEMPLATE = await fetch("/vendor/liberty-style/liberty.json").then((response) => response.json());
const protocol = new pmtiles.Protocol(); const protocol = new pmtiles.Protocol();
addProtocol("pmtiles", protocol.tile); addProtocol("pmtiles", protocol.tile);
// Nudged from protomaps-themes-base's stock "light" theme towards the // Builds the offline style from OFFLINE_STYLE_TEMPLATE — the identical
// National Park Service's own "Park Tiles" cartography — a warm, // Liberty JSON + ONLINE_STYLE_PATCH recolor as the online map, just
// Harpers-Ferry-Center-derived look. Not their actual map: a handful of // repointed at a local PMTiles source instead of tiles.openfreemap.org.
// values below (marked) are the genuine paint colors pulled from a public // This works because data/offline.pmtiles is now built by Planetiler's own
// Park Tiles style JSON // OpenMapTiles profile (see docs/development.adoc), the same vector schema
// (nps.gov/lib/npmap.js/style/ck72fwp2642dv07o7tbqinvz4.json, the "Imagery" // Liberty expects — before that, offline tiles used a different schema
// variant — the only one of their four styles served from a plain, // (protomaps' own basemaps) needing a completely separate hand-tuned
// unauthenticated URL; "Standard"/"Slate"/"Light" resolve through Mapbox // theme (protomaps-themes-base + OFFLINE_THEME) that could never be made
// Studio's private API with an access token, which isn't something to // to match ONLINE_STYLE_PATCH's look exactly, no matter how much either
// piggyback on even though it's technically visible in their own JS bundle). // side got tuned.
// The rest are reasoned to match that same family (warm cream ground, olive
// greens, muted blue) rather than sourced. Not a full recolor: labels,
// casings, and roles with no clean real-color anchor are left at theme
// defaults.
// //
// Recreating the color choices is legitimate — that's not the kind of thing // glyphs/sprite point at vendored local copies (public/vendor/fonts,
// a data license restricts (see docs/architecture.adoc's "Offline maps" // public/vendor/liberty-sprite) rather than tiles.openfreemap.org's —
// section) — what stays off-limits is their actual tile *service*: its // glyphs are Latin-only (see docs/architecture.adoc: labels in other
// OSM/terrain layers are licensed through Mapbox Atlas, a commercial, // scripts won't render offline), sprite is a one-time snapshot like
// non-sublicensable data service scoped to NPS. // OFFLINE_STYLE_TEMPLATE itself. ne2_shaded (Liberty's low-zoom shaded-
const OFFLINE_THEME = { // relief backdrop, z0-6 only) is left pointing at its remote URL
...protomaps_themes_base.namedTheme("light"), // unchanged — offline usage is regional, not whole-earth zoomed out, so
background: "#f9f6ef", // real: Park Tiles' own background, hsl(40, 37%, 97%) // this only ever silently fails to load at a zoom level offline users
earth: "#f9f6ef", // aren't in, the same graceful-degradation story as this app's other
water: "#a3cedf", // online-only extras (Wikipedia, live radar).
// Matches OpenFreeMap Liberty's own wood/grass green (its landcover_wood
// hsla(98,61%,72%,0.7) and landcover_grass rgba(176,213,154,1) at 0.3
// opacity, blended over the cream background) — the online map's actual
// look whenever ONLINE_STYLE_PATCH's brown/khaki recolor fails to apply
// (see loadOnlineStyle's fetch fallback) turned out to be the look this
// app's users actually want, so offline was changed to match it instead
// of chasing the patch's own reliability.
wood_a: "#c3e8ab",
wood_b: "#a8d888",
scrub_a: "#dcecc9",
scrub_b: "#cfe3b8",
// Close to wood_b, not the more saturated Park Tiles green: protomaps'
// theme routes "forest"/"nature_reserve"/"protected_area" (e.g. the Malé
// Karpaty CHKO boundary) through this same park_a/park_b pair as actual
// named parks — Liberty's online style doesn't make that reserve-boundary
// distinction at all, so a visibly different green here reads as an
// offline-only blotch rather than a deliberate distinction.
park_a: "#c3e8ab",
park_b: "#a8d888",
sand: "#f0e4c4",
beach: "#f0e4c4",
buildings: "#ded7cc",
pedestrian: "#e9e2d8",
highway: "#fdefbe", // real: Park Tiles' primary/highway road color
major: "#fef7dd", // real: Park Tiles' secondary road color
// "minor" (singular) is never actually read by protomaps' own paint
// expressions — only the zoom-interpolated minor_a/minor_b pair is, so a
// plain "minor" key here was dead: ordinary streets were silently
// falling back to the base theme's default pale yellow instead of this
// app's cream.
minor_a: "#fffdf8",
minor_b: "#fffdf8",
link: "#fef7dd",
// Distinct brown, not cream: protomaps' "other"/"path" kind is what a
// camper actually needs called out — unpaved tracks and footpaths,
// separate from paved minor_road/service (minor_service, left at the
// base theme's default since it's real driveways/parking aisles, not
// unpaved). Already rendered dashed by the theme itself; this just picks
// a real "dirt" tone instead of unstyled white.
other: "#c9a66b",
boundaries: "#546619", // real: Park Tiles' park-boundary line color
};
function offlineStyle() { function offlineStyle() {
const pmtilesUrl = `${window.location.origin}/tiles/offline.pmtiles`; const style = patchOnlineStyle(structuredClone(OFFLINE_STYLE_TEMPLATE));
style.sources.openmaptiles.url = `pmtiles://${window.location.origin}/tiles/offline.pmtiles`;
return { style.glyphs = `${window.location.origin}/vendor/fonts/{fontstack}/{range}.pbf`;
version: 8, style.sprite = `${window.location.origin}/vendor/liberty-sprite/ofm`;
glyphs: `${window.location.origin}/vendor/fonts/{fontstack}/{range}.pbf`, return style;
sources: {
offline: {
type: "vector",
url: `pmtiles://${pmtilesUrl}`,
attribution: "&copy; OpenStreetMap contributors",
},
},
// lang: "en" pulls in labels_layers (see docs/architecture.adoc) — only
// the Latin-script glyph ranges are vendored, so labels using other
// scripts won't render offline.
layers: protomaps_themes_base.layers("offline", OFFLINE_THEME, { lang: "en" }),
};
} }
// A sidebar icon that opens a full-height side panel (like the GPX one) // A sidebar icon that opens a full-height side panel (like the GPX one)
@@ -308,100 +290,6 @@ class LayerToggleControl {
} }
} }
const HILLSHADE_ID = "hillshade-dem";
const COLOR_RELIEF_ID = "elevation-tint";
// Hypsometric tint reusing this app's own palette (wood_a's green at
// valley floor, the road casing brown through the mid-slopes) rather than
// a generic rainbow ramp, so it reads as part of the same map rather than
// a bolted-on overlay. Stops picked for Central European terrain — the
// Tatras top out around 2600m — not a general-purpose world ramp.
const ELEVATION_COLOR_RAMP = [
"interpolate",
["linear"],
["raster-value"],
0, "#c3e8ab",
400, "#dcd08f",
900, "#c9a66b",
1600, "#a89a86",
2600, "#f9f6ef",
];
// Public, unauthenticated, no API key — the same open (SRTM/ArcticDEM-derived)
// elevation tiles OpenStreetMap's own hillshade layer and many other free
// tools use. Only reachable online; while the offline basemap is active this
// is skipped in favor of a local terrain.pmtiles if one's been generated
// (see docs/development.adoc), same optional-local-file story as
// offline.pmtiles itself.
const ONLINE_TERRAIN_TILES = ["https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"];
// A simple on/off sidebar toggle, not a panel — there's nothing to choose,
// just whether the shading shows.
class HillshadeControl {
constructor(map) {
this._map = map;
this._active = false;
this._offlineAvailable = false;
// setStyle() (switching Online/Offline) tears down all custom
// sources/layers — put hillshade back if it was on.
map.on("style.load", () => {
if (this._active) this._addLayer();
});
}
mount(sidebar) {
this._container = document.createElement("div");
this._container.className = "sidebar-item";
this._container.innerHTML = `<button type="button" class="sidebar-icon" title="Terrain shading" aria-label="Terrain shading" aria-pressed="false">${HILLSHADE_ICON}</button>`;
this._button = this._container.querySelector(".sidebar-icon");
this._button.addEventListener("click", () => this._toggle());
sidebar.appendChild(this._container);
}
setOfflineAvailable(available) {
this._offlineAvailable = available;
}
_toggle() {
this._active = !this._active;
this._button.classList.toggle("active", this._active);
this._button.setAttribute("aria-pressed", String(this._active));
if (this._active) this._addLayer();
else this._removeLayer();
}
_addLayer() {
if (this._map.getSource(HILLSHADE_ID)) return;
const source = this._offlineAvailable
? { type: "raster-dem", url: `pmtiles://${window.location.origin}/tiles/terrain.pmtiles`, encoding: "terrarium" }
: {
type: "raster-dem",
tiles: ONLINE_TERRAIN_TILES,
tileSize: 256,
encoding: "terrarium",
maxzoom: 15,
attribution: "Terrain tiles: Mapzen, SRTM, ArcticDEM",
};
this._map.addSource(HILLSHADE_ID, source);
// Elevation tint under, hillshade on top: the tint sets the base color
// per elevation band, hillshade multiplies slope-shading over it — the
// combined "characteristic relief look" the TODO asked for, not two
// independent layers.
this._map.addLayer({
id: COLOR_RELIEF_ID,
type: "color-relief",
source: HILLSHADE_ID,
paint: { "color-relief-color": ELEVATION_COLOR_RAMP, "color-relief-opacity": 0.7 },
});
this._map.addLayer({ id: HILLSHADE_ID, type: "hillshade", source: HILLSHADE_ID, paint: { "hillshade-exaggeration": 0.4 } });
}
_removeLayer() {
if (this._map.getLayer(HILLSHADE_ID)) this._map.removeLayer(HILLSHADE_ID);
if (this._map.getLayer(COLOR_RELIEF_ID)) this._map.removeLayer(COLOR_RELIEF_ID);
if (this._map.getSource(HILLSHADE_ID)) this._map.removeSource(HILLSHADE_ID);
}
}
// A small map-corner button (grouped with Home/zoom, see below) that's a // A small map-corner button (grouped with Home/zoom, see below) that's a
// second entry point to the same panel LayerToggleControl already manages // second entry point to the same panel LayerToggleControl already manages
// in the page's own sidebar — matching the National Park Service's own // in the page's own sidebar — matching the National Park Service's own
@@ -467,6 +355,65 @@ class ZoomLevelControl {
} }
} }
// Tilts the camera between a flat top-down view and a fixed 3D-perspective
// pitch — bearing stays untouched (this map never rotates, see the
// NavigationControl comment below), so this is purely "look at it from an
// angle", not a rotate/compass control. Independent of the "Terrain
// shading" overlay toggle (overlays.js) — that adds a raster-dem hillshade/
// color-relief layer, this just moves the camera; combining both gives a
// real sense of relief but neither depends on the other.
const PITCH_3D = 60;
class PitchToggleControl {
onAdd(map) {
this._map = map;
this._container = document.createElement("div");
this._container.className = "maplibregl-ctrl maplibregl-ctrl-group pitch-toggle-ctrl";
this._button = document.createElement("button");
this._button.type = "button";
this._button.className = "pitch-toggle-ctrl__button";
this._button.addEventListener("click", () => this._toggle());
this._container.appendChild(this._button);
this._update = () => this._render();
map.on("pitchend", this._update);
this._render();
return this._container;
}
onRemove() {
this._map.off("pitchend", this._update);
this._container.remove();
}
getDefaultPosition() {
return "top-left";
}
// Set once overlaysControl exists (it's built later in this file) —
// lets raised terrain (OverlaysControl.syncTerrain, see overlays.js)
// follow the pitch toggle without this control needing to know anything
// about hillshade/DEM sources itself.
setOverlaysControl(overlaysControl) {
this._overlaysControl = overlaysControl;
this._overlaysControl.syncTerrain(this._map.getPitch() > 0);
}
_toggle() {
this._map.easeTo({ pitch: this._map.getPitch() > 0 ? 0 : PITCH_3D, duration: 300 });
}
_render() {
const is3d = this._map.getPitch() > 0;
this._button.textContent = is3d ? "2D" : "3D";
this._button.title = is3d ? "Switch to 2D view" : "Switch to 3D view";
this._button.setAttribute("aria-label", this._button.title);
this._button.classList.toggle("active", is3d);
this._overlaysControl?.syncTerrain(is3d);
}
}
const map = new MaplibreMap({ const map = new MaplibreMap({
container: "map", container: "map",
style: ONLINE_STYLE, style: ONLINE_STYLE,
@@ -497,11 +444,14 @@ const SLOVAKIA_BOUNDS = [
[16.83, 47.73], [16.83, 47.73],
[22.57, 49.61], [22.57, 49.61],
]; ];
map.addControl(new SearchControl(), "top-left");
map.addControl(new HomeButton({ bounds: SLOVAKIA_BOUNDS }), "top-left"); map.addControl(new HomeButton({ bounds: SLOVAKIA_BOUNDS }), "top-left");
// showCompass: false — this map never rotates, so a bearing-reset button // showCompass: false — this map never rotates, so a bearing-reset button
// (NavigationControl's default third button) would just be dead weight. // (NavigationControl's default third button) would just be dead weight.
map.addControl(new NavigationControl({ showCompass: false }), "top-left"); map.addControl(new NavigationControl({ showCompass: false }), "top-left");
map.addControl(new ZoomLevelControl(), "top-left"); map.addControl(new ZoomLevelControl(), "top-left");
const pitchToggle = new PitchToggleControl();
map.addControl(pitchToggle, "top-left");
map.addControl(new MeasureControl(), "top-left"); map.addControl(new MeasureControl(), "top-left");
// Writing a note needs a login (POST /api/gpx/notes) — no point adding a // Writing a note needs a login (POST /api/gpx/notes) — no point adding a
// button that can only ever end in a 401 for a logged-out viewer, same // button that can only ever end in a 401 for a logged-out viewer, same
@@ -520,6 +470,7 @@ const gnssControl = new GnssControl(map);
// the "GNSS" sidebar icon actually turns it on — not just whenever a // the "GNSS" sidebar icon actually turns it on — not just whenever a
// receiver happens to have a fix. See locate.js. // receiver happens to have a fix. See locate.js.
map.addControl(new LocateControl(gnssControl), "top-left"); map.addControl(new LocateControl(gnssControl), "top-left");
map.addControl(new ScaleControl({ unit: "metric" }), "bottom-left");
// A small inset map showing where the current view sits in context — NPS's // A small inset map showing where the current view sits in context — NPS's
// own overview plugin (docs/nps-resources.adoc's "overview" entry, still on // own overview plugin (docs/nps-resources.adoc's "overview" entry, still on
@@ -539,11 +490,50 @@ map.once("style.load", () => {
const coordsLabel = document.querySelector(".topbar__coords"); const coordsLabel = document.querySelector(".topbar__coords");
const updateCoords = () => { const updateCoords = () => {
const { lng, lat } = map.getCenter(); const { lng, lat } = map.getCenter();
coordsLabel.textContent = `${lat.toFixed(5)}, ${lng.toFixed(5)}`; coordsLabel.innerHTML = `<span class="topbar__icon">${WAYPOINT_ICON}</span><span>${formatCoords(lat, lng)}</span>`;
}; };
updateCoords(); updateCoords();
map.on("move", updateCoords); map.on("move", updateCoords);
// Plain decimal degrees, not the DMS text actually shown — that's what
// pastes cleanly into another map/GPS app, which is the whole point of
// copying a coordinate in the first place.
coordsLabel.title = "Click to copy coordinates";
coordsLabel.addEventListener("click", async () => {
const { lng, lat } = map.getCenter();
try {
await navigator.clipboard.writeText(`${lat.toFixed(6)}, ${lng.toFixed(6)}`);
} catch {
return; // clipboard API unavailable (e.g. an insecure context) — nothing to fall back to
}
coordsLabel.textContent = "Copied!";
setTimeout(updateCoords, 1000);
});
// Sunrise/sunset for the same center point the coords above already show —
// on moveend (not every "move" frame, unlike updateCoords: the sun position
// barely moves between two nearby spots, not worth recomputing constantly)
// plus a periodic tick so the "Xh Ym to sunset" countdown keeps counting
// down even while the map sits still.
const sunLabel = document.querySelector(".topbar__sun");
const updateSun = () => {
const { lng, lat } = map.getCenter();
const info = formatSunInfo(lat, lng);
sunLabel.hidden = info === null;
if (!info) return;
const countdownGroup = info.countdown
? `<span class="topbar__group"><span class="topbar__icon">${PENDING_ICON}</span><span>${info.countdown}</span></span>`
: "";
sunLabel.innerHTML =
`<span class="topbar__group"><span class="topbar__icon">${SUNRISE_ICON}</span><span>${info.sunrise}</span></span>` +
`<span class="topbar__group"><span class="topbar__icon">${SUNSET_ICON}</span><span>${info.sunset}</span></span>` +
countdownGroup;
};
updateSun();
map.on("moveend", updateSun);
setInterval(updateSun, 60000);
const sidebar = document.getElementById("sidebar"); const sidebar = document.getElementById("sidebar");
// Grouped into the TODO's planned 4-module menu (Explorer / Offline hub / // Grouped into the TODO's planned 4-module menu (Explorer / Offline hub /
@@ -556,8 +546,9 @@ profilePanel.mount();
sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Explorer"></div>`); sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Explorer"></div>`);
new GpxPanelControl(map, profilePanel).mount(sidebar); new GpxPanelControl(map, profilePanel).mount(sidebar);
const hillshadeControl = new HillshadeControl(map); const overlaysControl = new OverlaysControl(map);
hillshadeControl.mount(sidebar); overlaysControl.mount(sidebar);
pitchToggle.setOverlaysControl(overlaysControl);
// Running a plugin needs a login (POST /api/plugins/:name/run) — same // Running a plugin needs a login (POST /api/plugins/:name/run) — same
// "hide the write-only affordance" story as NotesControl above. // "hide the write-only affordance" story as NotesControl above.
if (isLoggedIn()) new PluginsControl(map).mount(sidebar); if (isLoggedIn()) new PluginsControl(map).mount(sidebar);
@@ -565,7 +556,7 @@ if (isLoggedIn()) new PluginsControl(map).mount(sidebar);
sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Offline hub"></div>`); sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Offline hub"></div>`);
layerToggle.mount(sidebar); layerToggle.mount(sidebar);
layerToggle.setActive("online"); layerToggle.setActive("online");
new RadarControl(map).mount(sidebar); new OfflineDataControl().mount(sidebar);
sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Navigation"></div>`); sidebar.insertAdjacentHTML("beforeend", `<div class="sidebar-group-label" title="Navigation"></div>`);
gnssControl.mount(sidebar); gnssControl.mount(sidebar);
@@ -610,12 +601,20 @@ fetch("/api/offline-tiles")
fetch("/api/terrain-tiles") fetch("/api/terrain-tiles")
.then((response) => response.json()) .then((response) => response.json())
.then(({ available }) => hillshadeControl.setOfflineAvailable(available)) .then(({ available }) => {
overlaysControl.setHillshadeOfflineAvailable(available);
overlaysControl.setShadowOfflineAvailable(available);
})
.catch(() => {});
fetch("/api/hiking-trails")
.then((response) => response.json())
.then(({ countries }) => overlaysControl.setHikingTrailsOfflineCountries(countries))
.catch(() => {}); .catch(() => {});
fetch("/api/kiwix") fetch("/api/kiwix")
.then((response) => response.json()) .then((response) => response.json())
.then(setKiwixConfig) .then(({ available }) => overlaysControl.setWikiAvailable(available))
.catch(() => {}); .catch(() => {});
// Alt+click anywhere shows every rendered feature at that point — base map // Alt+click anywhere shows every rendered feature at that point — base map
+5 -2
View File
@@ -317,7 +317,9 @@ function haversine([lng1, lat1], [lng2, lat2]) {
return 2 * EARTH_RADIUS_M * Math.asin(Math.sqrt(a)); return 2 * EARTH_RADIUS_M * Math.asin(Math.sqrt(a));
} }
function lineLength(points) { // Exported — also used by hiking_trails.js to total up an OSM route
// relation's member ways into one distance, same haversine-sum approach.
export function lineLength(points) {
let total = 0; let total = 0;
for (let i = 1; i < points.length; i++) total += haversine(points[i - 1], points[i]); for (let i = 1; i < points.length; i++) total += haversine(points[i - 1], points[i]);
return total; return total;
@@ -347,7 +349,8 @@ function polygonArea(points) {
return Math.abs((area * EARTH_RADIUS_M * EARTH_RADIUS_M) / 2); return Math.abs((area * EARTH_RADIUS_M * EARTH_RADIUS_M) / 2);
} }
function formatDistance(meters) { // Exported — also used by hiking_trails.js's route-details popup.
export function formatDistance(meters) {
if (meters === 0) return ""; if (meters === 0) return "";
return meters >= 1000 ? `${(meters / 1000).toFixed(2)} km` : `${Math.round(meters)} m`; return meters >= 1000 ? `${(meters / 1000).toFixed(2)} km` : `${Math.round(meters)} m`;
} }
+2
View File
@@ -24,6 +24,8 @@ const ICON_LABELS = {
leisure_fishing: "Fishing", leisure_fishing: "Fishing",
sport_climbing: "Climbing", sport_climbing: "Climbing",
route_hiking: "Hiking", route_hiking: "Hiking",
shop_supermarket: "Supermarket",
shop_convenience: "Convenience store",
}; };
const NEW_LAYER_VALUE = "__new__"; const NEW_LAYER_VALUE = "__new__";
+400
View File
@@ -0,0 +1,400 @@
import { OFFLINE_DATA_ICON } from "/js/icons.js";
import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
import { isLoggedIn } from "/js/auth.js";
import { escapeHtml } from "/js/gpx.js";
const POLL_INTERVAL_MS = 3000;
function formatBytes(bytes) {
const mb = bytes / (1024 * 1024);
return mb >= 1024 ? `${(mb / 1024).toFixed(1)} GB` : `${mb.toFixed(1)} MB`;
}
function formatElapsed(ms) {
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
}
// "Building…" alone gave no way to tell a slow build from a stuck one, or
// which of the current selection's sources it's actually working on right
// now — elapsed time answers "is it alive", status.step (see
// PmtilesBuilder.run_job) answers "doing what, and what's left".
function formatRunning(status) {
const elapsed = status.started_at ? ` (${formatElapsed(Date.now() - new Date(status.started_at).getTime())})` : "";
const step = status.step ? `: ${status.step}` : "";
return `Building…${elapsed}${step}`;
}
// Vendored square-cropped SVGs (public/vendor/flag-icons/, MIT, see
// docs/third-party-notices.adoc) rather than Unicode flag emoji — emoji
// are font glyphs, not shapes, so they can't be resized/bordered/muted to
// fit this app's own controlled, hand-drawn-icon look the way a real SVG
// can (same reasoning the topbar readout's icons already settled on
// earlier — see icons.js). Framed in a small rounded-square badge in the
// markup below, the same convention icons.js's MAP_MARKER_ICON already
// uses for framing a category glyph, rather than a new visual language.
function flagIconUrl(code) {
return `/vendor/flag-icons/1x1/${code.toLowerCase()}.svg`;
}
// meta.sources is one entry per country the current build covers (see
// lib/maptool/pmtiles_builder.rb) — listing the codes is more useful here
// than a count, since they're exactly what RegionPicker's checklist above
// already shows.
function formatOfflineMeta(meta) {
if (!meta) return "";
return ` · ${meta.sources.map((s) => s.code).join(", ")}`;
}
function formatTerrainMeta(meta) {
if (!meta) return "";
const count = meta.sources.length;
return ` · ${count} source${count === 1 ? "" : "s"}`;
}
// The checklist of selectable countries (GET /api/offline-regions) plus
// the "Apply changes" button that saves the ticked set (POST, same URL)
// and hands it off — doesn't trigger a build itself, OfflineDataControl
// does that across both files once the selection is saved.
class RegionPicker {
constructor(onApply) {
this._onApply = onApply;
}
async mount(container) {
const section = document.createElement("div");
section.className = "offline-data-panel__regions";
section.innerHTML = `
<p class="offline-data-panel__regions-label">Regions</p>
<ul class="offline-data-panel__region-list"><li class="offline-data-panel__region-status">Loading…</li></ul>
<button type="button" class="offline-data-panel__apply" hidden>Apply changes</button>
`;
this._list = section.querySelector(".offline-data-panel__region-list");
this._applyButton = section.querySelector(".offline-data-panel__apply");
// Viewing/ticking works for anyone; only saving needs a login — same
// finer-grained gating measure.js/gpx.js use for their own
// write-only affordances, rather than hiding this whole control.
this._applyButton.hidden = !isLoggedIn();
this._applyButton.addEventListener("click", () => this._apply());
container.appendChild(section);
let data;
let cache;
try {
[data, cache] = await Promise.all([
fetch("/api/offline-regions").then((r) => r.json()),
fetch("/api/offline-tiles/cache").then((r) => r.json()),
]);
} catch {
this._list.innerHTML = `<li class="offline-data-panel__region-status">Could not load the region list.</li>`;
return;
}
const selected = new Set(data.selected);
const cached = new Map(cache.extracts.map((e) => [e.code, e]));
this._list.innerHTML = data.available
.map((region) => {
const extract = cached.get(region.code);
// Downloaded-but-not-(yet)-built is a real state (ticked, not
// Applied yet) as is built-but-no-longer-selected (unticked since)
// — this badge reflects the cache on disk, independent of either.
const cacheInfo = extract
? `
<span class="offline-data-panel__cache-info">
${formatBytes(extract.size)}
${
isLoggedIn()
? `<button type="button" class="offline-data-panel__cache-delete" data-code="${escapeHtml(region.code)}" data-name="${escapeHtml(region.name)}" title="Delete downloaded data for ${escapeHtml(region.name)}">✕</button>`
: ""
}
</span>`
: "";
return `
<li>
<label>
<input type="checkbox" value="${escapeHtml(region.code)}" ${selected.has(region.code) ? "checked" : ""}>
<img class="offline-data-panel__flag" src="${flagIconUrl(region.code)}" alt="" width="18" height="18">
${escapeHtml(region.name)}
</label>
${cacheInfo}
</li>`;
})
.join("");
this._list.querySelectorAll(".offline-data-panel__cache-delete").forEach((button) => {
button.addEventListener("click", () => this._deleteCache(button));
});
}
// Removes just this row's cache badge on success, rather than re-fetching
// and re-rendering the whole list — that would also overwrite whatever
// unticked/ticked-but-not-yet-Applied state the user has pending.
async _deleteCache(button) {
const code = button.dataset.code;
if (!window.confirm(`Delete downloaded data for ${button.dataset.name}? This can't be undone.`)) return;
button.disabled = true;
try {
const response = await fetch(`/api/offline-tiles/cache/${code}`, { method: "DELETE" });
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.error || `could not delete: ${response.status}`);
} catch (err) {
window.alert(err.message);
button.disabled = false;
return;
}
button.closest(".offline-data-panel__cache-info").remove();
}
async _apply() {
const codes = [...this._list.querySelectorAll("input:checked")].map((el) => el.value);
const body = new URLSearchParams();
codes.forEach((code) => body.append("codes[]", code));
this._applyButton.disabled = true;
try {
const response = await fetch("/api/offline-regions", { method: "POST", body });
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.error || `could not save the selection: ${response.status}`);
} catch (err) {
window.alert(err.message);
return;
} finally {
this._applyButton.disabled = false;
}
this._onApply();
}
}
// One row of the Offline data panel — just a status readout for either
// offline.pmtiles or terrain.pmtiles, both driven by the same selection
// (RegionPicker above). Both files behave identically from here (same
// status/build/build-status/check-update endpoint shape, see
// lib/maptool/pmtiles_builder.rb), just against different URLs/labels and
// a different way of describing what was actually built (formatMeta), so
// one class covers both rather than writing the same fetch/poll logic
// twice.
class DataFileRow {
constructor({ label, statusUrl, deleteUrl, buildUrl, buildStatusUrl, checkUpdateUrl, formatMeta }) {
this._label = label;
this._statusUrl = statusUrl;
this._deleteUrl = deleteUrl;
this._buildUrl = buildUrl;
this._buildStatusUrl = buildStatusUrl;
this._checkUpdateUrl = checkUpdateUrl;
this._formatMeta = formatMeta;
}
mount(container) {
const row = document.createElement("div");
row.className = "offline-data-panel__row";
row.innerHTML = `
<div class="offline-data-panel__row-head">
<strong>${this._label}</strong>
<button type="button" class="offline-data-panel__delete" hidden>Delete</button>
</div>
<p class="offline-data-panel__status">Checking…</p>
`;
this._statusEl = row.querySelector(".offline-data-panel__status");
this._deleteButton = row.querySelector(".offline-data-panel__delete");
this._deleteButton.addEventListener("click", () => this._delete());
container.appendChild(row);
this._refreshStatus();
}
// Single place that writes to _statusEl, so the error styling (pre-wrap,
// scrollable, red — see style.css) never stays stuck on once a build's
// failure message has scrolled past into a normal status line.
_setStatus(text, isError = false) {
this._statusEl.textContent = text;
this._statusEl.classList.toggle("offline-data-panel__status--error", isError);
}
async _refreshStatus() {
let data;
try {
data = await (await fetch(this._statusUrl)).json();
} catch {
this._setStatus("Could not check status.");
return;
}
if (!data.available) {
this._setStatus("Not generated yet.");
this._deleteButton.hidden = true;
return;
}
const updated = new Date(data.mtime).toLocaleString([], { dateStyle: "short", timeStyle: "short" });
this._setStatus(`${formatBytes(data.size)} · updated ${updated}${this._formatMeta(data.meta)}`);
this._deleteButton.hidden = !isLoggedIn();
this._checkUpdate();
}
// Deletes the built file (and, for offline.pmtiles, its whole per-country
// download cache — see PmtilesBuilder.delete_offline_data) so the row
// goes back to "Not generated yet." — a manual way to reclaim space or
// force a completely fresh rebuild, since Apply alone never removes
// anything already on disk.
async _delete() {
if (!window.confirm(`Delete ${this._label.toLowerCase()}? This can't be undone.`)) return;
this._deleteButton.disabled = true;
try {
const response = await fetch(this._deleteUrl, { method: "DELETE" });
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.error || `could not delete: ${response.status}`);
} catch (err) {
window.alert(err.message);
return;
} finally {
this._deleteButton.disabled = false;
}
this._refreshStatus();
}
// Separate from _refreshStatus's own fetch (and only called once, right
// after — not polled): this one reaches an external service (Geofabrik
// or Mapterhorn's manifest, see lib/maptool/pmtiles_builder.rb), so it's
// kept off the hot status path
// and just silently skipped if it fails, same as everywhere else this
// app degrades gracefully when an optional network check doesn't answer.
async _checkUpdate() {
let data;
try {
data = await (await fetch(this._checkUpdateUrl)).json();
} catch {
return;
}
if (!data.update_available) return;
const badge = document.createElement("span");
badge.className = "offline-data-panel__update-badge";
badge.textContent = " · a newer version is available";
this._statusEl.appendChild(badge);
}
// Called by OfflineDataControl once RegionPicker's Apply has saved a
// new selection — not tied to a button of this row's own.
async triggerBuild() {
this._setStatus("Checking…");
try {
// false (not null/true — see offline_update_available? in
// pmtiles_builder.rb) means the just-saved selection matches what's
// already on disk byte-for-byte: same countries, nothing upstream
// changed. Rebuilding would just burn bandwidth/CPU to reproduce
// the same file, so skip it. null (never built, or can't tell)
// still falls through to building below.
const check = await (await fetch(this._checkUpdateUrl)).json();
if (check.update_available === false) {
this._refreshStatus();
return;
}
} catch {
// couldn't tell — fall through and build rather than silently skip
}
this._setStatus("Starting…");
try {
const response = await fetch(this._buildUrl, { method: "POST" });
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.error || `build failed: ${response.status}`);
} catch (err) {
this._setStatus(err.message, true);
return;
}
this._setStatus("Building…");
this._poll();
}
// Self-rescheduling setTimeout loop, same shape as livetrack.js's
// _scheduleRefresh — check status, only reschedule while still running.
_poll() {
setTimeout(async () => {
let status;
try {
status = await (await fetch(this._buildStatusUrl)).json();
} catch {
this._poll(); // transient failure — try again next tick
return;
}
if (status.state === "running") {
this._setStatus(formatRunning(status));
this._poll();
return;
}
if (status.state === "error") {
this._setStatus(`Build failed: ${status.message}`, true);
} else {
this._refreshStatus();
}
}, POLL_INTERVAL_MS);
}
}
// A sidebar panel wrapping the manual go-pmtiles workflow
// docs/development.adoc documents — a region picker (which countries
// should be available offline) plus, on "Apply changes", a rebuild of
// offline.pmtiles/terrain.pmtiles covering the whole current selection
// (see lib/maptool/pmtiles_builder.rb — there's no standalone per-country
// file to individually delete; "remove a country" and "refresh a
// country" are both just "change the selection, then Apply").
export class OfflineDataControl {
constructor() {
this._rows = [
new DataFileRow({
label: "Offline map data",
statusUrl: "/api/offline-tiles",
deleteUrl: "/api/offline-tiles",
buildUrl: "/api/offline-tiles/build",
buildStatusUrl: "/api/offline-tiles/build-status",
checkUpdateUrl: "/api/offline-tiles/check-update",
formatMeta: formatOfflineMeta,
}),
new DataFileRow({
label: "Offline terrain data",
statusUrl: "/api/terrain-tiles",
deleteUrl: "/api/terrain-tiles",
buildUrl: "/api/terrain-tiles/build",
buildStatusUrl: "/api/terrain-tiles/build-status",
checkUpdateUrl: "/api/terrain-tiles/check-update",
formatMeta: formatTerrainMeta,
}),
];
this._regionPicker = new RegionPicker(() => this._rows.forEach((row) => row.triggerBuild()));
}
mount(sidebar) {
this._container = document.createElement("div");
this._container.className = "sidebar-item";
this._container.innerHTML = `<button type="button" class="sidebar-icon" title="Offline data" aria-label="Offline data">${OFFLINE_DATA_ICON}</button>`;
this._icon = this._container.querySelector(".sidebar-icon");
this._icon.addEventListener("click", () => this._togglePanel());
this._panel = document.getElementById("offline-data-panel");
this._panel.querySelector(".basemap-panel__close").addEventListener("click", () => this._togglePanel(false));
const body = this._panel.querySelector(".offline-data-panel__body");
this._regionPicker.mount(body);
this._rows.forEach((row) => row.mount(body));
onlyOneSidePanelOpen(this._panel, () => this._icon.classList.remove("active"));
sidebar.appendChild(this._container);
}
_togglePanel(force) {
const open = force === undefined ? this._panel.hidden : force;
this._panel.hidden = !open;
this._icon.classList.toggle("active", open);
if (open) notifySidePanelOpen(this._panel);
}
}
+408
View File
@@ -0,0 +1,408 @@
import { Popup } from "/vendor/maplibre-gl/maplibre-gl.mjs";
import { OVERLAYS_ICON, WIKI_ICON, WILDERNESS_ICON, MAP_MARKER_ICON } from "/js/icons.js";
import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
import { svgToImageData, badgeImageId, escapeHtml } from "/js/gpx.js";
import { RadarControl } from "/js/radar.js";
import { ShadowControl } from "/js/shadow.js";
import { ContourControl } from "/js/contour.js";
import { HikingTrailsControl } from "/js/hiking_trails.js";
const ICON_IMAGE_SIZE = 48; // rasterized once at this size, rendered much smaller by icon-size below
const ICON_SIZE = 0.3; // 48 * 0.3 = ~14px, sized to sit inside the marker badge below
const MARKER_SIZE = 0.55;
const WIKI_SOURCE_ID = "wiki-points";
const WIKI_ICON_ID = "wiki-icon";
const WIKI_BADGE_COLOR = "#795548";
const HILLSHADE_ID = "hillshade-dem";
const COLOR_RELIEF_ID = "elevation-tint";
// MapLibre's own default light ({anchor:"viewport", intensity:0.5,
// position:[1.15,210,30]}) sits the sun almost overhead (polar 30°) — fine
// for flat 2D, but it barely rakes across 3D terrain at all, which is
// most of why real relief still looked flat/washed out even once the mesh
// itself was raised. Lower polar angle = lower sun = longer, more visible
// slope shadows; azimuth 315 is the classic NW hillshade convention.
// anchor:"map" (not "viewport") keeps the sun fixed to geography rather
// than spinning with the camera as you look around a tilted view.
const TERRAIN_LIGHT = { anchor: "map", color: "#fff", intensity: 0.6, position: [1.5, 315, 65] };
const DEFAULT_LIGHT = { anchor: "viewport", color: "#fff", intensity: 0.5, position: [1.15, 210, 30] };
// Hypsometric tint reusing this app's own palette (wood_a's green at
// valley floor, the road casing brown through the mid-slopes) rather than
// a generic rainbow ramp, so it reads as part of the same map rather than
// a bolted-on overlay. Stops picked for Central European terrain — the
// Tatras top out around 2600m — not a general-purpose world ramp.
const ELEVATION_COLOR_RAMP = [
"interpolate",
["linear"],
["raster-value"],
0, "#c3e8ab",
400, "#dcd08f",
900, "#c9a66b",
1600, "#a89a86",
2600, "#f9f6ef",
];
// Public, unauthenticated, no API key — the same open (SRTM/ArcticDEM-derived)
// elevation tiles OpenStreetMap's own hillshade layer and many other free
// tools use. Only reachable online; while the offline basemap is active this
// is skipped in favor of a local terrain.pmtiles if one's been generated
// (see docs/development.adoc), same optional-local-file story as
// offline.pmtiles itself.
// Exported: dem.js's elevation lookups (shadow.js's raymarch) hit the same
// online tile source, rather than duplicating the URL template.
export const ONLINE_TERRAIN_TILES = ["https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"];
// A plain on/off Overlays item — there's nothing to choose, just whether
// the shading shows, unlike Radar's expandable scrub/forecast.
class HillshadeControl {
constructor(map) {
this._map = map;
this._active = false;
this._offlineAvailable = false;
// setStyle() (switching Online/Offline) tears down all custom
// sources/layers — put hillshade back if it was on.
map.on("style.load", () => {
if (this._active) this._addLayer();
this.syncTerrain(this._map.getPitch() > 0);
});
}
mount(li) {
li.innerHTML = `
<label>
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${WILDERNESS_ICON}</span>
<span>Terrain shading</span>
</label>
`;
this._checkbox = li.querySelector(".overlays-panel__toggle");
this._checkbox.addEventListener("change", (event) => this._toggle(event.target.checked));
}
setOfflineAvailable(available) {
this._offlineAvailable = available;
}
_toggle(active) {
this._active = active;
// Clear first: setTerrain may still be pointing at HILLSHADE_ID from
// before, and removing that source out from under it is worth avoiding
// even for the instant before syncTerrain below re-evaluates it.
if (this._map.getTerrain()) this._map.setTerrain(null);
if (active) this._addLayer();
else this._removeLayer();
this.syncTerrain(this._map.getPitch() > 0);
}
// Real terrain displacement (map.setTerrain), not just the flat
// hillshade/color-relief paint above — called both when this checkbox
// changes and when the 3D pitch toggle changes (map.js's
// PitchToggleControl, via OverlaysControl.syncTerrain below), since
// showing raised terrain needs both "tilted enough to see it" and "the
// DEM source this checkbox controls is actually loaded".
syncTerrain(pitched) {
// setLight/setTerrain throw if the style isn't loaded yet — called
// synchronously from map.js's setOverlaysControl during page init,
// before the first style.load; that listener re-invokes this once it
// actually is loaded, so bailing here just skips the premature call.
if (!this._map.isStyleLoaded()) return;
const terrainOn = pitched && this._active && this._map.getSource(HILLSHADE_ID);
if (terrainOn) {
this._map.setTerrain({ source: HILLSHADE_ID, exaggeration: 3 });
} else if (this._map.getTerrain()) {
this._map.setTerrain(null);
}
this._map.setLight(terrainOn ? TERRAIN_LIGHT : DEFAULT_LIGHT);
// The raster hillshade layer fakes relief by shading a flat texture —
// once real 3D geometry is doing that job (with its own lit shading
// for free), the raster version just looks like a blurry, low-res
// texture pasted on top of it. It's the only way to suggest relief in
// flat 2D though, so only hide it once real terrain is actually up.
// The color-relief elevation tint stays regardless — that's
// colour-by-height information, not a shading trick.
if (this._map.getLayer(HILLSHADE_ID)) {
this._map.setLayoutProperty(HILLSHADE_ID, "visibility", terrainOn ? "none" : "visible");
}
}
_addLayer() {
if (this._map.getSource(HILLSHADE_ID)) return;
// terrain.pmtiles is built from Mapterhorn (mapterhorn.com — see
// lib/maptool/pmtiles_builder.rb) whether generated in-app or by hand
// (docs/development.adoc), so this credits them regardless of how it
// got there — their own site lists the full per-region source
// provenance (CC-BY-4.0 and similar), not practical to reproduce here.
const source = this._offlineAvailable
? {
type: "raster-dem",
url: `pmtiles://${window.location.origin}/tiles/terrain.pmtiles`,
encoding: "terrarium",
attribution: "Terrain: Mapterhorn",
}
: {
type: "raster-dem",
tiles: ONLINE_TERRAIN_TILES,
tileSize: 256,
encoding: "terrarium",
maxzoom: 15,
attribution: "Terrain tiles: Mapzen, SRTM, ArcticDEM",
};
this._map.addSource(HILLSHADE_ID, source);
// Elevation tint under, hillshade on top: the tint sets the base color
// per elevation band, hillshade multiplies slope-shading over it — the
// combined "characteristic relief look" the TODO asked for, not two
// independent layers.
this._map.addLayer({
id: COLOR_RELIEF_ID,
type: "color-relief",
source: HILLSHADE_ID,
paint: { "color-relief-color": ELEVATION_COLOR_RAMP, "color-relief-opacity": 0.7 },
});
this._map.addLayer({ id: HILLSHADE_ID, type: "hillshade", source: HILLSHADE_ID, paint: { "hillshade-exaggeration": 0.4 } });
}
_removeLayer() {
if (this._map.getLayer(HILLSHADE_ID)) this._map.removeLayer(HILLSHADE_ID);
if (this._map.getLayer(COLOR_RELIEF_ID)) this._map.removeLayer(COLOR_RELIEF_ID);
if (this._map.getSource(HILLSHADE_ID)) this._map.removeSource(HILLSHADE_ID);
}
}
// A sidebar side panel (same shell as LiveTrackPanelControl/#livetrack-panel)
// listing live map layers that aren't a base map or a Tracks & Points file —
// each one refetched/toggled live rather than something to add/remove/
// reorder, so this is a plain checklist, its items built here (like
// PluginsControl's own list) rather than as static markup. Radar and
// Terrain shading used to be their own separate sidebar icons; both moved
// here since neither has anything to "manage," just on/off (Radar's own
// scrub/forecast controls live behind expanding its item, not a factor in
// where it's mounted).
export class OverlaysControl {
constructor(map) {
this._map = map;
this._hillshade = new HillshadeControl(map);
this._radar = new RadarControl(map);
this._shadow = new ShadowControl(map);
this._contour = new ContourControl(map);
this._hikingTrails = new HikingTrailsControl(map);
this._wikiEnabled = false; // the checkbox's own state
this._wikiMoveHandler = null; // the moveend listener driving its live refetch, while enabled
this._wikiRequestSeq = 0; // ignores an in-flight refetch a newer one has already superseded
this._interactivityRegistered = false; // guards against double-registering the click/hover handlers below across enable/disable cycles
// setStyle() (switching the Online/Offline base map) tears down all
// custom sources/layers/images — put the wiki layer back afterwards,
// same as GpxPanelControl does for visible GPX files. Hillshade/Radar
// handle their own the same way, from their own constructors above.
map.on("style.load", () => {
if (this._wikiEnabled) this._addWikiLayers().then(() => this._refreshWikiLayer());
});
}
mount(sidebar) {
this._container = document.createElement("div");
this._container.className = "sidebar-item";
this._container.innerHTML = `<button type="button" class="sidebar-icon" title="Overlays" aria-label="Overlays">${OVERLAYS_ICON}</button>`;
this._icon = this._container.querySelector(".sidebar-icon");
this._icon.addEventListener("click", () => this._togglePanel());
this._panel = document.getElementById("overlays-panel");
this._panel
.querySelector(".basemap-panel__close")
.addEventListener("click", () => this._togglePanel(false));
const list = this._panel.querySelector(".overlays-panel__list");
list.innerHTML = `
<li class="overlays-panel__item"></li>
<li class="overlays-panel__item"></li>
<li class="overlays-panel__item overlays-panel__item--expandable"></li>
<li class="overlays-panel__item overlays-panel__item--expandable"></li>
<li class="overlays-panel__item"></li>
<li class="overlays-panel__item overlays-panel__item--expandable"></li>
`;
const [hillshadeItem, wikiItem, radarItem, shadowItem, contourItem, hikingTrailsItem] = list.children;
this._hillshade.mount(hillshadeItem);
this._mountWiki(wikiItem);
this._radar.mount(radarItem);
this._shadow.mount(shadowItem);
this._contour.mount(contourItem);
this._hikingTrails.mount(hikingTrailsItem);
onlyOneSidePanelOpen(this._panel, () => this._icon.classList.remove("active"));
sidebar.appendChild(this._container);
}
_togglePanel(force) {
const open = force === undefined ? this._panel.hidden : force;
this._panel.hidden = !open;
this._icon.classList.toggle("active", open);
if (open) notifySidePanelOpen(this._panel);
}
setHillshadeOfflineAvailable(available) {
this._hillshade.setOfflineAvailable(available);
}
setHikingTrailsOfflineCountries(countries) {
this._hikingTrails.setOfflineCountries(countries);
}
// Called by map.js's PitchToggleControl whenever the 3D pitch toggle
// changes, so raised terrain follows the camera tilt rather than needing
// the "Terrain shading" checkbox re-clicked to notice.
syncTerrain(pitched) {
this._hillshade.syncTerrain(pitched);
}
setShadowOfflineAvailable(available) {
this._shadow.setOfflineAvailable(available);
}
_mountWiki(li) {
li.innerHTML = `
<label>
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${WIKI_ICON}</span>
<span>Wikipedia</span>
</label>
`;
this._wikiItem = li;
this._wikiToggle = li.querySelector(".overlays-panel__toggle");
this._wikiToggle.addEventListener("change", (event) => {
if (event.target.checked) this._enableWikiLayer();
else this._disableWikiLayer();
});
}
// Called once map.js's one-time GET /api/kiwix check resolves — disables
// (rather than hides) the checkbox rather than the whole panel/icon,
// since the other overlays are still usable with no kiwix-serve
// configured at all.
setWikiAvailable(available) {
this._wikiToggle.disabled = !available;
if (!available) this._wikiItem.title = "No kiwix-serve configured";
}
async _enableWikiLayer() {
this._wikiEnabled = true;
await this._addWikiLayers();
this._wikiMoveHandler = () => this._refreshWikiLayer();
this._map.on("moveend", this._wikiMoveHandler);
this._refreshWikiLayer();
}
_disableWikiLayer() {
this._wikiEnabled = false;
if (this._wikiMoveHandler) {
this._map.off("moveend", this._wikiMoveHandler);
this._wikiMoveHandler = null;
}
this._removeWikiLayers();
}
async _registerWikiIcon() {
if (this._map.hasImage(WIKI_ICON_ID)) return;
const whiteSvg = WIKI_ICON.replace(/currentColor/g, "#fff");
const imageData = await svgToImageData(whiteSvg, ICON_IMAGE_SIZE);
if (!this._map.hasImage(WIKI_ICON_ID)) this._map.addImage(WIKI_ICON_ID, imageData);
}
// Same one-image-per-color story as gpx.js's own _registerBadgeColors,
// just for this layer's single fixed color rather than a set of them
// discovered from a file.
async _registerWikiBadge() {
const id = badgeImageId(WIKI_BADGE_COLOR);
if (this._map.hasImage(id)) return;
const coloredSvg = MAP_MARKER_ICON.replace(/currentColor/g, WIKI_BADGE_COLOR);
const imageData = await svgToImageData(coloredSvg, ICON_IMAGE_SIZE);
if (!this._map.hasImage(id)) this._map.addImage(id, imageData);
}
async _addWikiLayers() {
if (this._map.getSource(WIKI_SOURCE_ID)) return;
await Promise.all([this._registerWikiIcon(), this._registerWikiBadge()]);
if (this._map.getSource(WIKI_SOURCE_ID)) return; // style.load raced this while awaiting
this._map.addSource(WIKI_SOURCE_ID, { type: "geojson", data: { type: "FeatureCollection", features: [] } });
this._map.addLayer({
id: `${WIKI_SOURCE_ID}-badge`,
type: "symbol",
source: WIKI_SOURCE_ID,
layout: {
"icon-image": badgeImageId(WIKI_BADGE_COLOR),
"icon-size": MARKER_SIZE,
"icon-allow-overlap": true,
"icon-ignore-placement": true,
},
});
this._map.addLayer({
id: `${WIKI_SOURCE_ID}-icon`,
type: "symbol",
source: WIKI_SOURCE_ID,
layout: { "icon-image": WIKI_ICON_ID, "icon-size": ICON_SIZE, "icon-allow-overlap": true, "icon-ignore-placement": true },
});
this._registerWikiInteractivity(`${WIKI_SOURCE_ID}-badge`);
}
_removeWikiLayers() {
[`${WIKI_SOURCE_ID}-badge`, `${WIKI_SOURCE_ID}-icon`].forEach((layerId) => {
if (this._map.getLayer(layerId)) this._map.removeLayer(layerId);
});
if (this._map.getSource(WIKI_SOURCE_ID)) this._map.removeSource(WIKI_SOURCE_ID);
}
_registerWikiInteractivity(layerId) {
if (this._interactivityRegistered) return;
this._interactivityRegistered = true;
this._map.on("click", layerId, (event) => {
const feature = event.features[0];
if (!feature) return;
new Popup({ closeButton: true, maxWidth: "260px" }).setLngLat(event.lngLat).setHTML(wikiPopupHtml(feature.properties)).addTo(this._map);
});
this._map.on("mouseenter", layerId, () => {
this._map.getCanvas().style.cursor = "pointer";
});
this._map.on("mouseleave", layerId, () => {
this._map.getCanvas().style.cursor = "";
});
}
// seq guards against an out-of-order response — a fast second pan's
// fetch resolving before a slower first one's — overwriting newer data
// with stale data; not a full abort-in-flight setup, just the ordering
// guarantee this actually needs.
async _refreshWikiLayer() {
const seq = ++this._wikiRequestSeq;
const bounds = this._map.getBounds();
const params = new URLSearchParams({
south: bounds.getSouth(),
west: bounds.getWest(),
north: bounds.getNorth(),
east: bounds.getEast(),
});
let geojson;
try {
geojson = await (await fetch(`/api/kiwix/nearby?${params}`)).json();
} catch {
return;
}
if (seq !== this._wikiRequestSeq) return;
const source = this._map.getSource(WIKI_SOURCE_ID);
if (source) source.setData(geojson);
}
}
// properties come from GET /api/kiwix/nearby — kiwix-serve's own ZIM
// content, not an uploaded file, but still untrusted-by-convention like
// every other popup builder in this app (escaped all the same). link
// already carries the /kiwix prefix this app proxies (lib/maptool/kiwix.rb).
function wikiPopupHtml({ name, link }) {
const parts = [`<div class="gpx-popup__header"><strong>${escapeHtml(name)}</strong></div>`];
parts.push(`<p class="gpx-popup__wiki"><a href="${escapeHtml(link)}" target="_blank" rel="noopener noreferrer">Read on Wikipedia →</a></p>`);
return parts.join("");
}
+15 -4
View File
@@ -83,12 +83,23 @@ export class PluginsControl {
east: bounds.getEast(), east: bounds.getEast(),
}); });
const response = await fetch(`/api/plugins/${encodeURIComponent(plugin.name)}/run`, { method: "POST", body }); const response = await fetch(`/api/plugins/${encodeURIComponent(plugin.name)}/run`, { method: "POST", body });
if (!response.ok) throw new Error(`run failed: ${response.status}`); // Every app.rb response on this route is JSON, success or error —
const { path } = await response.json(); // the catch below is only for an infra-level failure in front of it
notifyGpxStoreChanged(path); // (a proxy's own HTML error page, a dropped connection) that never
// reached app.rb at all.
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.error || `run failed: ${response.status}`);
notifyGpxStoreChanged(data.path);
status.textContent = "Done"; status.textContent = "Done";
status.classList.remove("plugins-panel__status--error");
} catch (err) { } catch (err) {
status.textContent = "Failed — see server log"; // Plugins::RunError's message (e.g. "plugin exited with status 1")
// reaches here as `data.error` above — nothing about a plugin run
// failure gets logged server-side (app.rb turns it into a plain 502
// JSON response, not an exception), so showing that message directly
// is the only way to see why, short of SSHing in to reproduce it.
status.textContent = err.message;
status.classList.add("plugins-panel__status--error");
} finally { } finally {
button.disabled = false; button.disabled = false;
} }
+76 -33
View File
@@ -1,4 +1,4 @@
import { RADAR_ICON } from "/js/icons.js"; import { RADAR_CLOUD_ICON } from "/js/icons.js";
const RAINVIEWER_API = "https://api.rainviewer.com/public/weather-maps.json"; const RAINVIEWER_API = "https://api.rainviewer.com/public/weather-maps.json";
@@ -12,6 +12,7 @@ const RAINVIEWER_API = "https://api.rainviewer.com/public/weather-maps.json";
// radar animation above can't. // radar animation above can't.
const FORECAST_API = "https://api.open-meteo.com/v1/forecast"; const FORECAST_API = "https://api.open-meteo.com/v1/forecast";
const FORECAST_DAYS = 3; const FORECAST_DAYS = 3;
const HOURLY_FORECAST_COUNT = 8;
const FORECAST_CACHE_KEY = "maptool:forecast"; const FORECAST_CACHE_KEY = "maptool:forecast";
// A small subset of WMO weather codes (open-meteo's `weather_code`) mapped // A small subset of WMO weather codes (open-meteo's `weather_code`) mapped
@@ -30,6 +31,21 @@ function weatherEmoji(code) {
return WEATHER_EMOJI.find(([codes]) => codes.includes(code))?.[1] ?? "🌡️"; return WEATHER_EMOJI.find(([codes]) => codes.includes(code))?.[1] ?? "🌡️";
} }
// A rolling window from "now" rather than "rest of today" — the latter
// shrinks to nothing right before midnight and balloons to ~24 entries just
// after, for no good reason.
function nextHours(hourly, count) {
const now = new Date();
return hourly.time
.map((time, i) => ({
time,
code: hourly.weather_code[i],
temp: Math.round(hourly.temperature_2m[i]),
}))
.filter((hour) => new Date(hour.time) >= now)
.slice(0, count);
}
// Matches the default window used by github.com/jpettitt/weather-radar-card // Matches the default window used by github.com/jpettitt/weather-radar-card
// (60 minutes of history at RainViewer's native 10-minute cadence = 7 // (60 minutes of history at RainViewer's native 10-minute cadence = 7
// frames, the last one being "now"). // frames, the last one being "now").
@@ -39,10 +55,10 @@ const OPACITY = 0.7;
const ATTRIBUTION = const ATTRIBUTION =
'Weather data &copy; <a href="https://www.rainviewer.com" target="_blank">RainViewer</a>'; 'Weather data &copy; <a href="https://www.rainviewer.com" target="_blank">RainViewer</a>';
// A sidebar icon that fetches RainViewer's recent precipitation frames on // An Overlays panel item (see overlays.js) that fetches RainViewer's recent
// demand and animates them as a raster overlay, with play/pause and a scrub // precipitation frames on demand and animates them as a raster overlay,
// slider in a flyout. No API key needed — see docs/architecture.adoc for // with play/pause and a scrub slider revealed by expanding the item. No API
// RainViewer's usage terms. // key needed — see docs/architecture.adoc for RainViewer's usage terms.
export class RadarControl { export class RadarControl {
constructor(map) { constructor(map) {
this._map = map; this._map = map;
@@ -59,12 +75,19 @@ export class RadarControl {
}); });
} }
mount(sidebar) { // li: the <li class="overlays-panel__item"> this item owns (see
this._container = document.createElement("div"); // overlays.js) — built here rather than in the static markup since this
this._container.className = "sidebar-item"; // control is the only thing that knows its own internal structure
this._container.innerHTML = ` // (playback/forecast), same as before when it built its own sidebar
<button type="button" class="sidebar-icon" title="Radar" aria-label="Radar">${RADAR_ICON}</button> // flyout.
<div class="sidebar-flyout" hidden> mount(li) {
li.innerHTML = `
<div class="overlays-panel__row">
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${RADAR_CLOUD_ICON}</span>
<button type="button" class="overlays-panel__name">Radar</button>
</div>
<div class="overlays-panel__details" hidden>
<div class="radar-control__playback"> <div class="radar-control__playback">
<button type="button" class="radar-control__play" aria-label="Play">&#9654;</button> <button type="button" class="radar-control__play" aria-label="Play">&#9654;</button>
<input type="range" class="radar-control__scrub" min="0" max="0" step="1" value="0"> <input type="range" class="radar-control__scrub" min="0" max="0" step="1" value="0">
@@ -74,27 +97,34 @@ export class RadarControl {
</div> </div>
`; `;
this._icon = this._container.querySelector(".sidebar-icon"); this._checkbox = li.querySelector(".overlays-panel__toggle");
this._flyout = this._container.querySelector(".sidebar-flyout"); this._nameButton = li.querySelector(".overlays-panel__name");
this._playback = this._container.querySelector(".radar-control__playback"); this._details = li.querySelector(".overlays-panel__details");
this._playButton = this._container.querySelector(".radar-control__play"); this._playback = li.querySelector(".radar-control__playback");
this._scrub = this._container.querySelector(".radar-control__scrub"); this._playButton = li.querySelector(".radar-control__play");
this._time = this._container.querySelector(".radar-control__time"); this._scrub = li.querySelector(".radar-control__scrub");
this._forecastEl = this._container.querySelector(".radar-control__forecast"); this._time = li.querySelector(".radar-control__time");
this._forecastEl = li.querySelector(".radar-control__forecast");
this._icon.addEventListener("click", () => this._setEnabled(!this._enabled)); this._checkbox.addEventListener("change", (event) => this._setEnabled(event.target.checked));
// Separate from the checkbox on purpose — expanding to see the scrub/
// forecast shouldn't also flip the layer on, and turning the layer on
// shouldn't force the details open (a viewer who never cares to scrub
// history/see the forecast keeps a one-line item either way).
this._nameButton.addEventListener("click", () => {
const expanding = this._details.hidden;
this._details.hidden = !expanding;
this._nameButton.setAttribute("aria-expanded", String(expanding));
});
this._playButton.addEventListener("click", () => this._togglePlayback()); this._playButton.addEventListener("click", () => this._togglePlayback());
this._scrub.addEventListener("input", () => { this._scrub.addEventListener("input", () => {
this._stopAnimation(); this._stopAnimation();
this._showFrame(Number(this._scrub.value)); this._showFrame(Number(this._scrub.value));
}); });
sidebar.appendChild(this._container);
} }
async _setEnabled(enabled) { async _setEnabled(enabled) {
this._enabled = enabled; this._enabled = enabled;
this._icon.classList.toggle("active", enabled);
if (!enabled) { if (!enabled) {
this._stopAnimation(); this._stopAnimation();
this._removeLayers(); this._removeLayers();
@@ -104,15 +134,15 @@ export class RadarControl {
return; return;
} }
this._icon.disabled = true; this._checkbox.disabled = true;
// Independent fetches: RainViewer being unreachable shouldn't hide a // Independent fetches: RainViewer being unreachable shouldn't hide a
// forecast that Open-Meteo still answered, or vice versa — especially // forecast that Open-Meteo still answered, or vice versa — especially
// since only the forecast has a cached fallback to fall back to. // since only the forecast has a cached fallback to fall back to.
const [framesResult, forecastResult] = await Promise.allSettled([fetchFrames(), this._loadForecast()]); const [framesResult, forecastResult] = await Promise.allSettled([fetchFrames(), this._loadForecast()]);
this._icon.disabled = false; this._checkbox.disabled = false;
// The icon may have been clicked again (disabling) while the fetches // The checkbox may have been unchecked again (disabling) while the
// were in flight. // fetches were in flight.
if (!this._enabled) return; if (!this._enabled) return;
const frames = framesResult.status === "fulfilled" ? framesResult.value : []; const frames = framesResult.status === "fulfilled" ? framesResult.value : [];
@@ -123,7 +153,7 @@ export class RadarControl {
// own failure. // own failure.
if (frames.length === 0 && !forecast) { if (frames.length === 0 && !forecast) {
this._enabled = false; this._enabled = false;
this._icon.classList.remove("active"); this._checkbox.checked = false;
} else if (frames.length > 0) { } else if (frames.length > 0) {
this._frames = frames; this._frames = frames;
this._frameIndex = frames.length - 1; this._frameIndex = frames.length - 1;
@@ -166,7 +196,17 @@ export class RadarControl {
precip: data.daily.precipitation_probability_max[i], precip: data.daily.precipitation_probability_max[i],
})); }));
const rows = days const hourRows = nextHours(data.hourly, HOURLY_FORECAST_COUNT)
.map(
(hour) => `
<div class="radar-control__forecast-hour">
<span>${new Date(hour.time).toLocaleTimeString([], { hour: "2-digit" })}</span>
<span>${weatherEmoji(hour.code)}</span>
<span>${hour.temp}°</span>
</div>`,
)
.join("");
const dayRows = days
.map( .map(
(day) => ` (day) => `
<div class="radar-control__forecast-day"> <div class="radar-control__forecast-day">
@@ -180,7 +220,7 @@ export class RadarControl {
const staleNote = cachedAt const staleNote = cachedAt
? `<p class="radar-control__forecast-stale">Cached from ${new Date(cachedAt).toLocaleString([], { dateStyle: "short", timeStyle: "short" })}</p>` ? `<p class="radar-control__forecast-stale">Cached from ${new Date(cachedAt).toLocaleString([], { dateStyle: "short", timeStyle: "short" })}</p>`
: ""; : "";
this._forecastEl.innerHTML = rows + staleNote; this._forecastEl.innerHTML = `<div class="radar-control__forecast-hours">${hourRows}</div>` + dayRows + staleNote;
} }
_addLayers() { _addLayers() {
@@ -252,9 +292,6 @@ export class RadarControl {
} }
_updateFrameUI() { _updateFrameUI() {
// Shown whenever there's radar frames, a forecast, or both — either
// one on its own is still worth showing the flyout for.
this._flyout.hidden = !this._enabled;
this._playback.hidden = this._frames.length === 0; this._playback.hidden = this._frames.length === 0;
this._scrub.max = String(Math.max(0, this._frames.length - 1)); this._scrub.max = String(Math.max(0, this._frames.length - 1));
this._scrub.value = String(this._frameIndex); this._scrub.value = String(this._frameIndex);
@@ -288,6 +325,10 @@ async function fetchForecast(lat, lon) {
url.searchParams.set("latitude", lat); url.searchParams.set("latitude", lat);
url.searchParams.set("longitude", lon); url.searchParams.set("longitude", lon);
url.searchParams.set("daily", "weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max"); url.searchParams.set("daily", "weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max");
// Same call, same cached JSON (writeCachedForecast below) — the next-few-
// hours strip alongside the multi-day outlook rather than a second
// request/cache entry.
url.searchParams.set("hourly", "weather_code,temperature_2m");
url.searchParams.set("timezone", "auto"); url.searchParams.set("timezone", "auto");
url.searchParams.set("forecast_days", String(FORECAST_DAYS)); url.searchParams.set("forecast_days", String(FORECAST_DAYS));
const response = await fetch(url); const response = await fetch(url);
@@ -316,7 +357,9 @@ function readCachedForecast() {
} }
} }
function firstSymbolLayerId(map) { // Exported: contour.js's own contour-line layers want the same
// "sit above base fill/roads, below place labels" placement.
export function firstSymbolLayerId(map) {
const layers = map.getStyle()?.layers || []; const layers = map.getStyle()?.layers || [];
const symbolLayer = layers.find((layer) => layer.type === "symbol"); const symbolLayer = layers.find((layer) => layer.type === "symbol");
return symbolLayer ? symbolLayer.id : undefined; return symbolLayer ? symbolLayer.id : undefined;
+111
View File
@@ -0,0 +1,111 @@
import { SEARCH_ICON } from "/js/icons.js";
import { escapeHtml } from "/js/gpx.js";
const NOMINATIM_API = "https://nominatim.openstreetmap.org/search";
// "lat, lng" / "lat lng" decimal degrees — the same plain-text shape the
// topbar's own coordinate readout copies to the clipboard (map.js). Tried
// before hitting the geocoder, so pasting a coordinate pair jumps straight
// there with no network round-trip.
const COORD_PATTERN = /^\s*(-?\d+(?:\.\d+)?)\s*[, ]\s*(-?\d+(?:\.\d+)?)\s*$/;
// A place-name/coordinate search box, matching NPS's own Park Tiles viewer.
// Online only, like this app's other non-basemap online features (Radar,
// the Wikipedia layer) — no offline geocoding index to fall back to.
// Nominatim: OpenStreetMap's own geocoder, free, no API key, matching the
// online basemap's own OSM-derived style already used here.
export class SearchControl {
constructor() {
this._results = [];
}
onAdd(map) {
this._map = map;
this._container = document.createElement("div");
this._container.className = "maplibregl-ctrl maplibregl-ctrl-group search-ctrl";
this._container.innerHTML = `
<button type="button" class="search-ctrl__button" title="Search" aria-label="Search">${SEARCH_ICON}</button>
<div class="search-panel" hidden>
<form class="search-panel__form">
<input type="text" class="search-panel__input" placeholder="Search, or lat, lng">
</form>
<ul class="search-panel__results"></ul>
</div>
`;
this._button = this._container.querySelector(".search-ctrl__button");
this._panel = this._container.querySelector(".search-panel");
this._input = this._container.querySelector(".search-panel__input");
this._resultsEl = this._container.querySelector(".search-panel__results");
this._button.addEventListener("click", () => this._toggle());
this._container.querySelector(".search-panel__form").addEventListener("submit", (event) => {
event.preventDefault();
this._search();
});
return this._container;
}
onRemove() {
this._container.remove();
}
getDefaultPosition() {
return "top-left";
}
_toggle() {
const opening = this._panel.hidden;
this._panel.hidden = !opening;
this._button.classList.toggle("active", opening);
if (opening) this._input.focus();
}
async _search() {
const query = this._input.value.trim();
if (!query) return;
const coords = query.match(COORD_PATTERN);
if (coords) {
this._resultsEl.innerHTML = "";
this._jumpTo(Number(coords[1]), Number(coords[2]));
return;
}
this._resultsEl.innerHTML = `<li class="search-panel__status">Searching…</li>`;
let places;
try {
const url = new URL(NOMINATIM_API);
url.searchParams.set("q", query);
url.searchParams.set("format", "jsonv2");
url.searchParams.set("limit", "5");
places = await (await fetch(url)).json();
} catch {
this._resultsEl.innerHTML = `<li class="search-panel__status">Search unavailable — check your connection.</li>`;
return;
}
this._renderResults(places);
}
_renderResults(places) {
this._results = places;
if (places.length === 0) {
this._resultsEl.innerHTML = `<li class="search-panel__status">No results</li>`;
return;
}
this._resultsEl.innerHTML = places
.map((place, i) => `<li class="search-panel__result" data-index="${i}">${escapeHtml(place.display_name)}</li>`)
.join("");
this._resultsEl.querySelectorAll(".search-panel__result").forEach((el) => {
el.addEventListener("click", () => {
const place = this._results[Number(el.dataset.index)];
this._jumpTo(Number(place.lat), Number(place.lon));
});
});
}
_jumpTo(lat, lng) {
this._map.flyTo({ center: [lng, lat], zoom: Math.max(this._map.getZoom(), 14) });
this._panel.hidden = true;
this._button.classList.remove("active");
}
}
+237
View File
@@ -0,0 +1,237 @@
import { getPosition } from "/vendor/suncalc/suncalc.js";
import { elevationAt } from "/js/dem.js";
import { TERRAIN_SHADOW_ICON } from "/js/icons.js";
const MIN_ZOOM = 11; // below this, a screen is roughly the same scale as the 40km raymarch — "one screen, one shadow-casting neighborhood" stops holding
const GRID_COLS = 96;
const GRID_ROWS = 64;
const RAYMARCH_STEPS = 16;
const RAYMARCH_START_M = 150;
const RAYMARCH_MAX_M = 40000;
const RAYMARCH_RATIO = (RAYMARCH_MAX_M / RAYMARCH_START_M) ** (1 / (RAYMARCH_STEPS - 1)); // geometric spacing: dense near the point, coarse far out
const EARTH_RADIUS_M = 6371000;
const SHADOW_RGBA = [20, 20, 50, Math.round(0.45 * 255)];
const SLIDER_DEBOUNCE_MS = 300;
const IMAGE_SOURCE_ID = "terrain-shadow";
// An Overlays panel item (see overlays.js) shading the parts of the visible
// map currently blocked from the sun by surrounding terrain — offline
// equivalent of shademap.app, built on the same Terrarium DEM tiles
// HillshadeControl already renders (via dem.js) and the same sun-position
// math the toolbar readout uses (public/js/sun.js). No Web Worker: a
// recompute is dozens of small awaited fetches (mostly cache hits after the
// first pan), not sustained sync CPU work, so it already yields to the
// event loop on its own between grid rows without blocking a pan/zoom.
export class ShadowControl {
constructor(map) {
this._map = map;
this._enabled = false;
this._offlineAvailable = false;
this._seq = 0; // bumped on every recompute/disable so a superseded in-flight one (a fast second pan, or turning the layer off mid-compute) discards its result instead of overwriting newer data
this._moveHandler = null;
this._sliderMinutes = currentMinutesOfDay();
this._sliderTimer = null;
// setStyle() (switching Online/Offline) tears down all custom sources/
// layers — recompute puts the shadow layer back if it was on, same as
// every other Overlays control.
map.on("style.load", () => {
if (this._enabled) this._recompute();
});
}
mount(li) {
li.innerHTML = `
<div class="overlays-panel__row">
<input type="checkbox" class="overlays-panel__toggle">
<span class="overlays-panel__icon">${TERRAIN_SHADOW_ICON}</span>
<button type="button" class="overlays-panel__name">Terrain shadow</button>
</div>
<div class="overlays-panel__details" hidden>
<div class="shadow-control__time">
<input type="range" class="shadow-control__slider" min="0" max="1439" step="5" value="${this._sliderMinutes}">
<span class="shadow-control__clock">${formatMinutes(this._sliderMinutes)}</span>
</div>
<p class="shadow-control__hint" hidden>Zoom in further to compute terrain shadow.</p>
</div>
`;
this._checkbox = li.querySelector(".overlays-panel__toggle");
this._nameButton = li.querySelector(".overlays-panel__name");
this._details = li.querySelector(".overlays-panel__details");
this._slider = li.querySelector(".shadow-control__slider");
this._clock = li.querySelector(".shadow-control__clock");
this._hint = li.querySelector(".shadow-control__hint");
this._checkbox.addEventListener("change", (event) => this._setEnabled(event.target.checked));
this._nameButton.addEventListener("click", () => {
const expanding = this._details.hidden;
this._details.hidden = !expanding;
this._nameButton.setAttribute("aria-expanded", String(expanding));
});
this._slider.addEventListener("input", () => {
this._sliderMinutes = Number(this._slider.value);
this._clock.textContent = formatMinutes(this._sliderMinutes);
clearTimeout(this._sliderTimer);
this._sliderTimer = setTimeout(() => this._recompute(), SLIDER_DEBOUNCE_MS);
});
}
setOfflineAvailable(available) {
this._offlineAvailable = available;
}
_setEnabled(enabled) {
this._enabled = enabled;
if (enabled) {
this._moveHandler = () => this._recompute();
this._map.on("moveend", this._moveHandler);
this._recompute();
return;
}
this._seq++;
if (this._moveHandler) {
this._map.off("moveend", this._moveHandler);
this._moveHandler = null;
}
this._hint.hidden = true;
this._removeLayer();
}
async _recompute() {
const seq = ++this._seq;
if (this._map.getZoom() < MIN_ZOOM) {
this._hint.hidden = false;
this._removeLayer();
return;
}
this._hint.hidden = true;
const bounds = this._map.getBounds();
const center = this._map.getCenter();
const date = this._selectedDate();
// One sun position for the whole viewport — azimuth/altitude vary
// negligibly across it next to the raymarch's own 150m-step coarseness.
const sun = getPosition(date, center.lat, center.lng);
const west = bounds.getWest();
const east = bounds.getEast();
const south = bounds.getSouth();
const north = bounds.getNorth();
const canvas = document.createElement("canvas");
canvas.width = GRID_COLS;
canvas.height = GRID_ROWS;
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(GRID_COLS, GRID_ROWS);
if (sun.altitude <= 0) {
// Sun's below the horizon everywhere in view — no point raymarching.
fillShadow(imageData);
} else {
for (let row = 0; row < GRID_ROWS; row++) {
const lat = north - ((row + 0.5) / GRID_ROWS) * (north - south);
const rowWork = [];
for (let col = 0; col < GRID_COLS; col++) {
const lng = west + ((col + 0.5) / GRID_COLS) * (east - west);
rowWork.push(
this._isShadowed(lng, lat, sun.azimuth, sun.altitude).then((shadowed) => setPixel(imageData, col, row, shadowed)),
);
}
await Promise.all(rowWork); // yields once per row, so a fast pan/zoom's newer recompute can cut in
if (seq !== this._seq) return;
}
}
ctx.putImageData(imageData, 0, 0);
if (seq !== this._seq) return;
this._publish(canvas.toDataURL(), bounds);
}
async _isShadowed(lng, lat, sunAzimuth, sunAltitude) {
const pointElev = await elevationAt(lng, lat, this._offlineAvailable);
if (pointElev === null) return false; // no DEM data here — render sunlit rather than guess
let dist = RAYMARCH_START_M;
const samples = [];
for (let i = 0; i < RAYMARCH_STEPS; i++) {
samples.push(this._horizonSample(lng, lat, pointElev, sunAzimuth, dist));
dist *= RAYMARCH_RATIO;
}
const angles = (await Promise.all(samples)).filter((a) => a !== null);
const horizonMax = angles.length ? Math.max(...angles) : -90;
return sunAltitude <= horizonMax;
}
async _horizonSample(lng, lat, pointElev, sunAzimuth, dist) {
const sample = destinationPoint(lng, lat, sunAzimuth, dist);
const sampleElev = await elevationAt(sample.lng, sample.lat, this._offlineAvailable);
if (sampleElev === null) return null;
const curvatureDrop = (dist * dist) / (2 * EARTH_RADIUS_M);
return (Math.atan2(sampleElev - pointElev - curvatureDrop, dist) * 180) / Math.PI;
}
_selectedDate() {
const date = new Date();
date.setHours(0, this._sliderMinutes, 0, 0);
return date;
}
// First image-source usage in this app — addSource/addLayer once, then
// updateImage on every later recompute rather than remove+re-add.
_publish(dataUrl, bounds) {
const coordinates = [
[bounds.getWest(), bounds.getNorth()],
[bounds.getEast(), bounds.getNorth()],
[bounds.getEast(), bounds.getSouth()],
[bounds.getWest(), bounds.getSouth()],
];
const source = this._map.getSource(IMAGE_SOURCE_ID);
if (source) {
source.updateImage({ url: dataUrl, coordinates });
} else {
this._map.addSource(IMAGE_SOURCE_ID, { type: "image", url: dataUrl, coordinates });
this._map.addLayer({ id: IMAGE_SOURCE_ID, type: "raster", source: IMAGE_SOURCE_ID });
}
}
_removeLayer() {
if (this._map.getLayer(IMAGE_SOURCE_ID)) this._map.removeLayer(IMAGE_SOURCE_ID);
if (this._map.getSource(IMAGE_SOURCE_ID)) this._map.removeSource(IMAGE_SOURCE_ID);
}
}
// Flat-earth destination point (bearing/distance -> lng/lat) — fine under
// 40km, and nothing in this codebase already does lng/lat offset math.
function destinationPoint(lng, lat, bearingDeg, distanceM) {
const bearing = (bearingDeg * Math.PI) / 180;
const latRad = (lat * Math.PI) / 180;
const dLat = (distanceM * Math.cos(bearing)) / EARTH_RADIUS_M;
const dLng = (distanceM * Math.sin(bearing)) / (EARTH_RADIUS_M * Math.cos(latRad));
return { lng: lng + (dLng * 180) / Math.PI, lat: lat + (dLat * 180) / Math.PI };
}
function setPixel(imageData, col, row, shadowed) {
if (!shadowed) return; // ImageData starts fully transparent — nothing to do for a sunlit cell
const i = (row * imageData.width + col) * 4;
imageData.data.set(SHADOW_RGBA, i);
}
function fillShadow(imageData) {
for (let i = 0; i < imageData.data.length; i += 4) imageData.data.set(SHADOW_RGBA, i);
}
function formatMinutes(minutes) {
const h = Math.floor(minutes / 60);
const m = minutes % 60;
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
}
function currentMinutesOfDay() {
const now = new Date();
return now.getHours() * 60 + now.getMinutes();
}
+71
View File
@@ -0,0 +1,71 @@
import { getTimes } from "/vendor/suncalc/suncalc.js";
// A Locus Pro-style toolbar readout: DMS coordinates (plain decimal degrees
// read less like a "GPS unit" and are harder to scan at a glance) plus
// sunrise/sunset and a countdown to whichever comes next — both computed
// for the map's current center, the same point the coordinates already
// describe, not a GNSS fix (this app's coordinate readout was never tied
// to having one).
function toDMS(value, positiveLabel, negativeLabel) {
const label = value >= 0 ? positiveLabel : negativeLabel;
const abs = Math.abs(value);
const degrees = Math.floor(abs);
const minutesFull = (abs - degrees) * 60;
let minutes = Math.floor(minutesFull);
let seconds = Math.round((minutesFull - minutes) * 60);
// A seconds value that rounds up to 60 has to carry into minutes (and
// from there, in the rare case, into degrees) rather than print "60".
if (seconds === 60) {
seconds = 0;
minutes += 1;
}
const carriedDegrees = minutes === 60 ? degrees + 1 : degrees;
const carriedMinutes = minutes === 60 ? 0 : minutes;
return `${carriedDegrees}°${String(carriedMinutes).padStart(2, "0")}'${String(seconds).padStart(2, "0")}"${label}`;
}
export function formatCoords(lat, lng) {
return `${toDMS(lat, "N", "S")} ${toDMS(lng, "E", "W")}`;
}
function formatTime(date) {
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
}
function formatCountdown(ms) {
const totalMinutes = Math.max(0, Math.round(ms / 60000));
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
}
const DAY_MS = 24 * 60 * 60 * 1000;
// null when there's no sunrise/sunset to show at all — getTimes returns
// null (not Invalid Date) for both on a polar day/night, per its own
// comment in suncalc.js — the caller just hides the readout in that case
// rather than showing something misleading. Returns the three pieces
// separately (not one joined string) so the caller can put its own icon in
// front of each rather than this module owning any markup.
export function formatSunInfo(lat, lng, now = new Date()) {
const today = getTimes(now, lat, lng);
if (today.sunrise === null || today.sunset === null) return null;
let next;
if (now < today.sunrise) {
next = { label: "sunrise", at: today.sunrise };
} else if (now < today.sunset) {
next = { label: "sunset", at: today.sunset };
} else {
// Already past today's sunset — the next event is tomorrow's sunrise.
const tomorrow = getTimes(new Date(now.getTime() + DAY_MS), lat, lng);
next = tomorrow.sunrise === null ? null : { label: "sunrise", at: tomorrow.sunrise };
}
return {
sunrise: formatTime(today.sunrise),
sunset: formatTime(today.sunset),
countdown: next ? `${formatCountdown(next.at - now)} to ${next.label}` : null,
};
}
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-al" viewBox="0 0 512 512">
<path fill="red" d="M0 0h512v512H0z"/>
<path id="al-a" fill="#000001" d="M204.9 99.5c-5 0-13.2 1.6-13 5.4-14-2.3-15.4 3.4-14.6 8.5q2.3-3 4.2-3.3 2.8-.4 5.8 1.5a23 23 0 0 1 5 4.4c-4.8 1.1-8.6.4-12.4-.3a18 18 0 0 1-6.1-2.5c-1.6-1.1-2.1-2.1-4.6-4.7-2.9-3-6-2.1-5 2.5 2.2 4.3 6 6.3 10.7 7 2.2.4 5.6 1.2 9.4 1.2s8.1-.5 10.5 0c-1.4.8-3 2.4-6.2 3s-8-2-11-2.6c.4 2.5 3.5 4.8 9.7 6 10.2 2.2 18.7 4 24.3 7s9.1 6.8 11.6 9.8c5 6 5.3 10.5 5.6 11.5 1 9.5-2.2 14.8-8.4 16.4-3 .8-8.5-.7-10.5-3-2-2.4-4-6.4-3.4-12.7.5-2.5 3.4-9 1-10.3a292 292 0 0 0-34.4-16c-2.7-1.1-5 2.5-5.8 4A54 54 0 0 1 129 107c-4.6-8.1-12.1 0-10.9 7.7 2.1 8.6 8.6 14.8 16.5 19.2 8 4.5 18.1 8.8 28.3 8.6 5.5 1 5.5 8.2-1.1 9.5-13 0-23.2-.2-32.9-9.6-7.4-6.7-11.5 1.3-9.4 5.8 3.6 14 23.6 18 43.8 13.4 7.8-1.3 3.1 7 .9 7.2-8.4 6-23.5 12-36.8-.1-6.1-4.7-10.2-.7-8 6 6 17.5 28.5 13.8 44 5.2 4-2.2 7.6 3 2.7 6.9-19.2 13.4-28.9 13.6-37.6 8.4-10.8-4.3-11.8 7.8-5.3 11.8 7.2 4.4 25.4 1 38.9-7.4 5.7-4.2 6 2.4 2.3 5-15.9 13.8-22.2 17.5-38.8 15.2-8.2-.6-8 9.5-1.6 13.5 8.8 5.4 26.1-3.6 39.5-14.7 5.6-3 6.6 2 3.8 7.8a57 57 0 0 1-23.3 19.2 29 29 0 0 1-19.5.7c-6.2-2.2-7 4.2-3.6 10 2 3.5 10.6 4.7 19.7 1.4 9.2-3.2 19-10.8 25.7-19.8 6-5.1 5.2 1.8 2.5 6.7-13.5 21.3-25.9 29.2-42.1 27.9-7.3-1.2-8.9 4.4-4.3 9.6 8 6.7 18.2 6.4 27-.2a751 751 0 0 0 30.8-32.6c5.5-4.4 7.3 0 5.7 9-1.5 5.1-5.2 10.5-15.3 14.5-7 4-1.8 9.4 3.4 9.5 2.9 0 8.7-3.3 13-8.3 5.9-6.5 6.2-11 9.5-21.1 3-5 8.4-2.7 8.4 2.5-2.6 10.2-4.8 12-10 16.2-5.1 4.7 3.4 6.3 6.3 4.4 8.3-5.6 11.3-12.8 14.1-19.4 2-4.8 7.8-2.5 5.1 5.3-6.4 18.5-17 25.8-35.5 29.6-1.9.3-3 1.4-2.4 3.6l7.5 7.5c-11.5 3.3-20.8 5.2-32.2 8.5L142 300.6c-1.5-3.4-2.2-8.7-10.4-5-5.7-2.6-8.2-1.6-11.4 1 4.5.1 6.5 1.3 8.3 3.4 2.3 6 7.6 6.6 13 5 3.5 2.9 5.4 5.2 9 8.2l-17.8-.6c-6.3-6.7-11.3-6.3-15.8-1-3.5.5-5 .5-7.3 4.7 3.7-1.5 6-2 7.7-.3 6.6 3.9 11 3 14.3 0l18.7 1.1c-2.3 2-5.6 3.1-8 5.2-9.7-2.8-14.7 1-16.4 8.8a18 18 0 0 0-1.4 10c1-3.2 2.5-5.9 5.3-7.6 8.6 2.2 11.8-1.3 12.3-6.5 4.2-3.4 10.5-4.1 14.6-7.6 4.9 1.6 7.2 2.6 12.1 4.1q2.5 8 12 6c7.7.3 6.3 3.4 7 5.9 2-3.6 2-7-2.8-10.3-1.7-4.6-5.5-6.7-10.4-4-4.7-1.3-5.9-3.2-10.5-4.6 11.7-3.7 20-4.5 31.8-8.3 3 2.8 5.2 4.8 8.2 7.2q2.5 1.6 4 0c7.3-10.6 10.6-20 17.4-27 2.6-2.9 6-6.8 9.6-7.8 1.8-.4 4-.2 5.5 1.4 1.4 1.6 2.6 4.4 2 8.7-.6 6.2-2 8.2-3.8 11.8s-3.9 6-6 8.8c-4.4 5.7-10.1 9-13.5 11.2-6.8 4.4-9.7 2.5-15 2.2-6.7.8-8.5 4.1-3 8.7a21 21 0 0 0 13.7 2.3c3.3-.6 7-4.8 9.8-7 3-3.6 8.1.6 4.7 4.7-6.3 7.5-12.6 12.4-20.3 12.3-8.2 1-6.7 5.7-1.3 7.9 9.8 4 18.6-3.5 23-8.5 3.5-3.7 6-3.9 5.3 2-3.4 10.5-8.1 14.6-15.7 15.1-6.2-.5-6.3 4.2-1.7 7.5 10.3 7 17.7-5 21.2-12.4 2.5-6.6 6.3-3.5 6.7 2 0 7.3-3.2 13.2-12 20.7 6.7 10.7 14.5 21.7 21.3 32.5l20.5-228.2-20.5-36c-2.1-2-9.3-10.5-11.2-11.7-.7-.7-1.1-1.2-.1-1.6s3.2-.8 4.8-1c-4.4-4.4-8-5.8-16.3-8.2 2-.8 4-.3 9.9-.6a32 32 0 0 0-14.4-11c4.5-3 5.3-3.3 9.8-7-7.7-.6-14.3-2-20.8-4a41 41 0 0 0-12.8-3.7m.7 9c4 0 6.6 1.4 6.6 3s-2.5 3.1-6.6 3.1-6.6-1.5-6.6-3.2 2.6-3 6.6-3z"/>
<use xlink:href="#al-a" width="100%" height="100%" transform="matrix(-1 0 0 1 512 0)"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-at" viewBox="0 0 512 512">
<path fill="#fff" d="M0 170.7h512v170.6H0z"/>
<path fill="#c8102e" d="M0 0h512v170.7H0zm0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

+12
View File
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ba" viewBox="0 0 512 512">
<defs>
<clipPath id="ba-a">
<path fill-opacity=".7" d="M0 0h512v512H0z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#ba-a)">
<path fill="#009" d="M0 0h512v512H0z"/>
<path fill="#fc0" d="m77 0 437 437V0z"/>
<path fill="#FFF" d="m461.4 470.4-26.1-19.1-26.9 19 10.2-31.2-26.4-19.2h32.7l10.2-31 10 31.1 32.8.1-26.2 19.4zm76.7 10.4h-32.7l-10-31.2-10.2 31.1h-32.8l26.4 19.2-10.1 31.2 26.8-19 26.2 19-9.8-30.9zM391.8 379.6l26.2-19.4h-32.7L375.2 329 365 360h-32.7l26.4 19.3-10.1 31.1 26.8-19 26.1 19.1zm-60.3-60.4 26.2-19.4-32.8-.1-10-31.2-10.2 31.2-32.7-.1 26.4 19.2-10.2 31.2 26.9-19 26.1 19.1zm-59.7-59.7 26.2-19.4h-32.7l-10.1-31.2L245 240h-32.7l26.4 19.2-10.1 31.2 26.8-19 26.1 19zm-60.4-60.3 26.2-19.3-32.8-.1-10-31.2-10.2 31.2-32.7-.1 26.4 19.2-10.2 31.2 26.9-19 26.1 19-9.7-30.8zm-59.7-59.9L178 120l-32.7-.1-10-31.2-10.3 31.1H92.2l26.4 19.2-10.1 31.2 26.8-19 26.1 19zm-60-60L118 60l-32.7-.1-10-31.2L65 59.8H32.2L58.6 79l-10.1 31.2 26.8-19 26.2 19zm-60-60L58 0 25.2-.1l-10-31.2L4.8-.2h-32.7L-1.4 19l-10.1 31.2 26.8-19 26.1 19z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-be" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#000001" d="M0 0h170.7v512H0z"/>
<path fill="#ffd90c" d="M170.7 0h170.6v512H170.7z"/>
<path fill="#f31830" d="M341.3 0H512v512H341.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 302 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-bg" viewBox="0 0 512 512">
<path fill="#fff" d="M0 0h512v170.7H0z"/>
<path fill="#00966e" d="M0 170.7h512v170.6H0z"/>
<path fill="#d62612" d="M0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" id="flag-icons-by" viewBox="0 0 512 512">
<defs>
<clipPath id="by-a">
<path d="M0 0h200v608h8v284l-8 8H0Z"/>
</clipPath>
</defs>
<path fill="#ce1720" d="M0 0h512v512H0Z"/>
<g fill="#fff" clip-path="url(#by-a)" transform="matrix(.5625 0 0 .56889 5 0)">
<g id="by-c">
<path id="by-b" d="M36 0v14h-9v14H16v16H8v13H-8V24H8V6H-8V0Zm26 77v15h-8v12h-8V92h-8V77h-8V57h8V42h8V30h8v12h8v15h8v20Zm-17-1h10V58H45ZM19 183h8v-18h-8zm54 0h8v-18h-8ZM-8 305H6v13h6v16h9v15h12v-15h9v-16h8v-13H38v-15h21v10h13v17h11v19h-8v14h-7v13h-6v14h-9v12h-7v11h-9v14H24v-15h-9v-14H8v-9H-8v-23H8v-20H-8Z"/>
<use xlink:href="#by-b" transform="matrix(-1 0 0 1 200 0)"/>
<path d="M96 0v32h8V0h32v14h-8v14h-12v16h-8v13H92V44h-8V28H72V14h-8V0Zm-2 274v-11h-6v-13h-7v-14h-8v-14h-8v-10h-9v-14H44v14h-9v10h-7v14h-8v14h-6v13H8v17H-8v-44H8v-20H-8v-33H8v14h10v14h10v-14h10v-14h8v-18h-8v-14H28v-14H18v14H8v14H-8v-41H8v-19H-8V77H8v13h8v16h11v13h9v15h7v12h14v-12h7v-15h9v-13h11V90h8V77h16v13h8v16h11v13h9v15h7v12h14v-12h7v-15h9v-13h11V90h8V77h16v28h-16v19h16v41h-16v-14h-10v-14h-10v14h-10v14h-8v18h8v14h10v14h10v-14h10v-14h16v33h-16v20h16v44h-16v-17h-6v-13h-6v-14h-8v-14h-7v-10h-9v-14h-12v14h-9v10h-8v14h-8v14h-7v13h-6v11zm2-167v27h8v-27zm-4 58v-14H82v-14H72v14H62v14h-8v18h8v14h10v14h10v-14h10v-14h16v14h10v14h10v-14h10v-14h8v-18h-8v-14h-10v-14h-10v14h-10v14zm4 46v27h8v-27z"/>
</g>
<use xlink:href="#by-c" transform="matrix(1 0 0 -1 0 900)"/>
<path d="M-8 408H8v14h7v8h8v14h7v12h-7v14h-8v8H8v14H-8Zm216 0v84h-16v-14h-7v-8h-8v-14h-7v-12h7v-14h8v-8h7v-14ZM62 459h8v-18h-8zm76 0v-18h-8v18zm-42-59h8v-18h-8zm0 100v18h8v-18Zm-50-75h14v-11h10v-10h5v-10h6v-14h8v-14h4v-13h14v13h4v14h8v14h6v10h5v10h10v11h14v50h-14v11h-10v10h-5v10h-6v14h-8v14h-4v13H93v-13h-4v-14h-8v-14h-6v-10h-5v-10H60v-11H46Zm50 9v-15h-8v-10h-8v25h8v9h5v14h-5v9h-8v25h8v-10h8v-15h8v15h8v10h8v-25h-8v-9h-5v-14h5v-9h8v-25h-8v10h-8v15z"/>
</g>
<path fill="#007c30" d="M117.3 341.3h395V512h-395z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ch" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="red" d="M0 0h512v512H0z"/>
<g fill="#fff">
<path d="M96 208h320v96H96z"/>
<path d="M208 96h96v320h-96z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 288 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-cz" viewBox="0 0 512 512">
<path fill="#fff" d="M0 0h512v256H0z"/>
<path fill="#d7141a" d="M0 256h512v256H0z"/>
<path fill="#11457e" d="M300 256 0 56v400z"/>
</svg>

After

Width:  |  Height:  |  Size: 226 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 512 512">
<path fill="#fc0" d="M0 341.3h512V512H0z"/>
<path fill="#000001" d="M0 0h512v170.7H0z"/>
<path fill="red" d="M0 170.7h512v170.6H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 229 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-dk" viewBox="0 0 512 512">
<path fill="#c8102e" d="M0 0h512.1v512H0z"/>
<path fill="#fff" d="M144 0h73.1v512H144z"/>
<path fill="#fff" d="M0 219.4h512.1v73.2H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 232 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ee" viewBox="0 0 512 512">
<path fill="#1791ff" d="M0 0h512v170.7H0z"/>
<path fill="#000001" d="M0 170.7h512v170.6H0z"/>
<path fill="#fff" d="M0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

+547
View File
@@ -0,0 +1,547 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-es" viewBox="0 0 512 512">
<path fill="#AA151B" d="M0 0h512v512H0z"/>
<path fill="#F1BF00" d="M0 128h512v256H0z"/>
<path fill="#ad1519" d="M171.7 227.6s-.5 0-.8-.2l-1.1-1-.7-.5-.7-.9s-.7-1.2-.4-2q.7-1.3 1.5-1.6c.5-.3 1.6-.6 1.6-.6l1.2-.5 1.3-.3.6-.3.9-.1 1.1-.3 1.7.1h5.1l3.6 1.2c.6.1 1.9.3 2.4.6q.9.6 1.3 1.1l.4 1.1v1.1l-.5.9-.6 1-.8.7s-.6.5-1.1.5-5.1-.9-8.2-.9-7.8.9-7.8.9"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M171.7 227.6s-.5 0-.8-.2l-1.1-1-.7-.5-.7-.9s-.7-1.2-.4-2q.7-1.3 1.5-1.6c.5-.3 1.6-.6 1.6-.6l1.2-.5 1.3-.3.6-.3.9-.1 1.1-.3 1.7.1h5.1l3.6 1.2c.6.1 1.9.3 2.4.6q.9.6 1.3 1.1l.4 1.1v1.1l-.5.9-.6 1-.8.7s-.6.5-1.1.5-5.1-.9-8.2-.9-7.8.9-7.8.9z"/>
<path fill="#c8b100" d="M178.2 220.9q.1-2.4 1.4-2.6 1.3.2 1.4 2.6-.1 2.3-1.4 2.5-1.3-.2-1.4-2.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M178.2 220.9q.1-2.4 1.4-2.6 1.3.2 1.4 2.6-.1 2.3-1.4 2.5-1.3-.2-1.4-2.5z"/>
<path fill="#c8b100" d="M179 220.9c0-1.3.3-2.4.6-2.4q.7.2.7 2.4t-.7 2.3q-.5-.2-.6-2.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179 220.9c0-1.3.3-2.4.6-2.4q.7.2.7 2.4t-.7 2.3q-.5-.2-.6-2.3z"/>
<path fill="#c8b100" d="M178.7 218.2q0-.9.9-1 1.2 0 1 1c-.2 1-.5.9-1 .9a1 1 0 0 1-1-1"/>
<path fill="#c8b100" d="M180.3 217.8v.6h-1.5v-.6h.5v-1.3h-.7v-.6h.7v-.6h.6v.6h.6v.6h-.6v1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M180.3 217.8v.6h-1.5v-.6h.5v-1.3h-.7v-.6h.7v-.6h.6v.6h.6v.6h-.6v1.3h.4"/>
<path fill="#c8b100" d="M181 217.8v.6h-2.7v-.6h1v-1.3h-.7v-.6h.7v-.6h.6v.6h.6v.6h-.6v1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M181 217.8v.6h-2.7v-.6h1v-1.3h-.7v-.6h.7v-.6h.6v.6h.6v.6h-.6v1.3h1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179.9 217.3a1 1 0 0 1 .6.9q0 .8-.9.9-1.2 0-1-1 .1-.6.8-.8"/>
<path fill="#c8b100" d="M179.6 227.4h-5v-1.2l-.3-1.2-.2-1.6c-1.4-1.8-2.6-3-3-2.7q0-.6.4-.8c1.2-.7 3.7 1 5.6 3.9l.5.7h4l.5-.7c1.9-2.9 4.4-4.6 5.6-3.9l.5.8c-.5-.3-1.7.9-3 2.7l-.3 1.6-.2 1.2-.1 1.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179.6 227.4h-5v-1.2l-.3-1.2-.2-1.6c-1.4-1.8-2.6-3-3-2.7q0-.6.4-.8c1.2-.7 3.7 1 5.6 3.9l.5.7h4l.5-.7c1.9-2.9 4.4-4.6 5.6-3.9l.5.8c-.5-.3-1.7.9-3 2.7l-.3 1.6-.2 1.2-.1 1.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M171.3 220.6c1-.5 3 1.2 4.8 3.8m11.9-3.8c-1-.5-3.1 1.2-4.9 3.8"/>
<path fill="#c8b100" d="m172.3 229.6-.6-1q3.1-1 7.9-1c3 0 5.9.3 7.9 1l-.6.9-.3.8q-2.7-.9-7-.8c-2.9 0-5.6.3-7 .8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m172.3 229.6-.6-1q3.1-1 7.9-1c3 0 5.9.3 7.9 1l-.6.9-.3.8q-2.7-.9-7-.8c-2.9 0-5.6.3-7 .8l-.3-.7"/>
<path fill="#c8b100" d="M179.6 232.2a27 27 0 0 0 6.2-.7q1.1-.3 1-.8 0-.3-.3-.4a26 26 0 0 0-7-.9c-2.6 0-5.3.4-6.8.9q-.3 0-.4.4 0 .5 1 .8c1 .3 3.8.7 6.3.7"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179.6 232.2a27 27 0 0 0 6.2-.7q1.1-.3 1-.8 0-.3-.3-.4a26 26 0 0 0-7-.9c-2.6 0-5.3.4-6.8.9q-.3 0-.4.4 0 .5 1 .8c1 .3 3.8.7 6.3.7z"/>
<path fill="#c8b100" d="m187.6 227.4-.6-.5s-.6.3-1.3.2-1-1-1-1-.8.7-1.5.6-1-.6-1-.6-.8.5-1.4.5c-.7 0-1.3-.9-1.3-.9s-.6.9-1.2 1-1.2-.6-1.2-.6-.3.6-1 .7c-.9.1-1.6-.6-1.6-.6s-.5.7-1 1c-.6.1-1.3-.4-1.3-.4l-.2.5-.3.2.2.4a33 33 0 0 1 15.5.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m187.6 227.4-.6-.5s-.6.3-1.3.2-1-1-1-1-.8.7-1.5.6-1-.6-1-.6-.8.5-1.4.5c-.7 0-1.3-.9-1.3-.9s-.6.9-1.2 1-1.2-.6-1.2-.6-.3.6-1 .7c-.9.1-1.6-.6-1.6-.6s-.5.7-1 1c-.6.1-1.3-.4-1.3-.4l-.2.5-.3.2.2.4a33 33 0 0 1 15.5.1z"/>
<path fill="#c8b100" d="M179.6 224.8h.3a1 1 0 0 0 1 1.5q.9 0 1.2-.8l.1-.4v.5q.2.8 1.2.9a1 1 0 0 0 1.1-1.1v-.1l.4-.4.2.4-.1.5q.1 1 1 1 .7 0 1-.4l.2-.3v.4q0 .5.5.7l1-.4.8-.8v.5s-.5.8-1 1.1l-1 .3q-.4-.2-.7-.7l-.8.3q-1 0-1.4-1a2 2 0 0 1-1.2.6 2 2 0 0 1-1.3-.6l-1.1.4q-1 0-1.4-.7-.5.7-1.4.7l-1-.4q-.6.5-1.4.6a2 2 0 0 1-1.2-.5q-.4.8-1.4.8-.5 0-.8-.2-.2.5-.7.7l-1-.3-1-1.1v-.5l.9.8q.8.5.9.4.5-.1.5-.7v-.4l.2.3q.3.5 1 .5.9-.1 1-1v-.6l.1-.4.4.4q.1 1.1 1 1.2 1.1 0 1.2-1v-.3l.2.3q.3.8 1.1.8 1.1-.1 1.2-1.1l-.1-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179.6 224.8h.3a1 1 0 0 0 1 1.5q.9 0 1.2-.8l.1-.4v.5q.2.8 1.2.9a1 1 0 0 0 1.1-1.1v-.1l.4-.4.2.4-.1.5q.1 1 1 1 .7 0 1-.4l.2-.3v.4q0 .5.5.7l1-.4.8-.8v.5s-.5.8-1 1.1l-1 .3q-.4-.2-.7-.7l-.8.3q-1 0-1.4-1a2 2 0 0 1-1.2.6 2 2 0 0 1-1.3-.6l-1.1.4q-1 0-1.4-.7-.5.7-1.4.7l-1-.4q-.6.5-1.4.6a2 2 0 0 1-1.2-.5q-.4.8-1.4.8-.5 0-.8-.2-.2.5-.7.7l-1-.3-1-1.1v-.5l.9.8q.8.5.9.4.5-.1.5-.7v-.4l.2.3q.3.5 1 .5.9-.1 1-1v-.6l.1-.4.4.4q.1 1.1 1 1.2 1.1 0 1.2-1v-.3l.2.3q.3.8 1.1.8 1.1-.1 1.2-1.1l-.1-.4z"/>
<path fill="#c8b100" d="M179.6 227.6q-4.8 0-7.9 1l-.3-.2q0-.3.2-.4 3.1-1 8-1c4.9 0 6 .4 8 1l.2.3q0 .3-.3.2-3.1-1-8-1"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M179.6 227.6q-4.8 0-7.9 1l-.3-.2q0-.3.2-.4 3.1-1 8-1c4.9 0 6 .4 8 1l.2.3q0 .3-.3.2-3.1-1-8-1z"/>
<path fill="#fff" d="M176.6 228.7q0-.5.5-.5.3 0 .4.5 0 .3-.5.4-.6 0-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M176.6 228.7q0-.5.5-.5.3 0 .4.5 0 .3-.5.4-.6 0-.4-.4z"/>
<path fill="#ad1519" d="M179.6 228.8h-1l-.3-.3q0-.2.3-.3h2a.3.3 0 0 1 .4.3.3.3 0 0 1-.4.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179.6 228.8h-1l-.3-.3q0-.2.3-.3h2a.3.3 0 0 1 .4.3.3.3 0 0 1-.4.3h-1"/>
<path fill="#058e6e" d="M174.7 229.2h-.7q-.3.1-.4-.2l.2-.3.7-.1.8-.2q.3 0 .4.3 0 .3-.3.4h-.8"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M174.7 229.2h-.7q-.3.1-.4-.2l.2-.3.7-.1.8-.2q.3 0 .4.3 0 .3-.3.4h-.8"/>
<path fill="#ad1519" d="m171.8 229.7.3-.5.7.1-.4.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m171.8 229.7.3-.5.7.1-.4.6-.6-.2"/>
<path fill="#fff" d="M181.7 228.7q0-.5.4-.5t.5.5q0 .3-.5.4-.6 0-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M181.7 228.7q0-.5.4-.5t.5.5q0 .3-.5.4-.6 0-.4-.4z"/>
<path fill="#058e6e" d="M184.5 229.2h.8q.2.1.3-.2l-.2-.3-.8-.1-.7-.2q-.3 0-.4.3 0 .3.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M184.5 229.2h.8q.2.1.3-.2l-.2-.3-.8-.1-.7-.2q-.3 0-.4.3 0 .3.3.4h.7"/>
<path fill="#ad1519" d="m187.3 229.7-.2-.5h-.7l.3.6h.7"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m187.3 229.7-.2-.5h-.7l.3.6h.7"/>
<path fill="#ad1519" d="M179.6 231.6q-3.9 0-6.5-.7a27 27 0 0 1 6.5-.7q3.8 0 6.5.7-2.7.7-6.5.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M179.6 231.6q-3.9 0-6.5-.7a27 27 0 0 1 6.5-.7q3.8 0 6.5.7-2.7.7-6.5.7z"/>
<path fill="#c8b100" d="M187.4 226.2v-.4q-.3 0-.5.2 0 .3.2.5.2 0 .3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M187.4 226.2v-.4q-.3 0-.5.2 0 .3.2.5.2 0 .3-.3z"/>
<path fill="#c8b100" d="M182.5 225.2q0-.3-.2-.4-.3 0-.3.4t.2.4.3-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M182.5 225.2q0-.3-.2-.4-.3 0-.3.4t.2.4.3-.4z"/>
<path fill="#c8b100" d="M176.7 225.2q0-.3.2-.4.3 0 .3.4t-.2.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M176.7 225.2q0-.3.2-.4.3 0 .3.4t-.2.4z"/>
<path fill="#c8b100" d="M171.8 226.2q-.1-.3.1-.4t.4.2q0 .3-.2.5-.2 0-.3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M171.8 226.2q-.1-.3.1-.4t.4.2q0 .3-.2.5-.2 0-.3-.3z"/>
<path fill="#c8b100" d="m179.6 222.4-.9.5.7 1.4.2.2.2-.2.7-1.4-1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m179.6 222.4-.9.5.7 1.4.2.2.2-.2.7-1.4-1-.5"/>
<path fill="#c8b100" d="m177.7 224.5.4.6 1.3-.4.2-.2-.2-.2-1.3-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m177.7 224.5.4.6 1.3-.4.2-.2-.2-.2-1.3-.4-.4.6"/>
<path fill="#c8b100" d="m181.5 224.5-.4.6-1.3-.4-.2-.2.1-.2 1.4-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m181.5 224.5-.4.6-1.3-.4-.2-.2.1-.2 1.4-.4.4.6"/>
<path fill="#c8b100" d="m173.9 223-.7.6.9 1.2.2.1.2-.2.3-1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m173.9 223-.7.6.9 1.2.2.1.2-.2.3-1.4-1-.3"/>
<path fill="#c8b100" d="m172.4 225.3.5.5 1.3-.7v-.4h-1.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m172.4 225.3.5.5 1.3-.7v-.4h-1.5l-.3.6"/>
<path fill="#c8b100" d="m176.2 224.6-.3.6-1.4-.1-.2-.2.1-.2 1.3-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m176.2 224.6-.3.6-1.4-.1-.2-.2.1-.2 1.3-.7.5.6"/>
<path fill="#c8b100" d="M171 225.5v.7l-1.5.1h-.2v-.3l1.1-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M171 225.5v.7l-1.5.1h-.2v-.3l1.1-1 .6.5"/>
<path fill="#c8b100" d="M173.8 225q0-.6.5-.6t.5.5l-.5.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M173.8 225q0-.6.5-.6t.5.5l-.5.5z"/>
<path fill="#c8b100" d="m185.3 223 .7.6-.9 1.2-.2.1-.2-.2-.3-1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m185.3 223 .7.6-.9 1.2-.2.1-.2-.2-.3-1.4 1-.3"/>
<path fill="#c8b100" d="m186.8 225.3-.6.5-1.2-.7-.1-.2.2-.2h1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m186.8 225.3-.6.5-1.2-.7-.1-.2.2-.2h1.4l.3.6"/>
<path fill="#c8b100" d="m183 224.6.3.6 1.4-.1.2-.2-.1-.2-1.3-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m183 224.6.3.6 1.4-.1.2-.2-.1-.2-1.3-.7-.5.6"/>
<path fill="#c8b100" d="M188 225.5v.7l1.5.1h.2v-.3l-1.1-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M188 225.5v.7l1.5.1h.2v-.3l-1.1-1-.6.5"/>
<path fill="#c8b100" d="M179 224.5a.5.5 0 0 1 .6-.5q.5 0 .5.5l-.5.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M179 224.5a.5.5 0 0 1 .6-.5q.5 0 .5.5l-.5.4z"/>
<path fill="#c8b100" d="M184.4 225a.5.5 0 0 1 .5-.6l.5.5-.5.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M184.4 225a.5.5 0 0 1 .5-.6l.5.5-.5.5z"/>
<path fill="#c8b100" d="m169.1 226.3-.7-.8-.7-.3s.3-.3.6-.3l.5.2v-.2s.3 0 .4.4v1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m169.1 226.3-.7-.8-.7-.3s.3-.3.6-.3l.5.2v-.2s.3 0 .4.4v1z"/>
<path fill="#c8b100" d="m169.1 226 .6.1q.3.3 0 .5-.4.2-.4 0-.3-.3-.2-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m169.1 226 .6.1q.3.3 0 .5-.4.2-.4 0-.3-.3-.2-.5z"/>
<path fill="#c8b100" d="m189.9 226.3.7-.8.7-.3s-.3-.3-.6-.3l-.5.2v-.2s-.3 0-.4.4v.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m189.9 226.3.7-.8.7-.3s-.3-.3-.6-.3l-.5.2v-.2s-.3 0-.4.4v.7z"/>
<path fill="#c8b100" d="m189.9 226-.5.1q-.3.3-.2.5h.6q.3-.3.1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m189.9 226-.5.1q-.3.3-.2.5h.6q.3-.3.1-.5z"/>
<path fill="#c8b100" d="M168.2 238h22.9v-6h-22.9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M168.2 238h22.9v-6h-22.9z"/>
<path fill="#c8b100" d="m170.6 242 .5-.1h17.5q-1-.3-1-1.3.1-1 1-1.4l-.5.1h-17.5q1 .3 1 1.3t-1 1.3"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m170.6 242 .5-.1h17.5q-1-.3-1-1.3.1-1 1-1.4l-.5.1h-17.5q1 .3 1 1.3t-1 1.3z"/>
<path fill="#c8b100" d="M171 241.9h17.2q.9 0 1 .8-.1.7-1 .8H171q-1-.1-1.1-.8.1-.8 1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M171 241.9h17.2q.9 0 1 .8-.1.7-1 .8H171q-1-.1-1.1-.8.1-.8 1-.8z"/>
<path fill="#c8b100" d="M171 238h17.2q.9 0 1 .6-.1.7-1 .7H171q-1 0-1-.7t1-.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M171 238h17.2q.9 0 1 .6-.1.7-1 .7H171q-1 0-1-.7t1-.7z"/>
<path fill="#005bbf" d="M195.6 338.6a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8q-2.5 0-4 .8a9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8q-1.6 1-4 1v2.4a9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.9 9 9 0 0 0 4 .9 9 9 0 0 0 4-.9 9 9 0 0 1 4-.9q2.4 0 4 .9a9 9 0 0 0 4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M195.6 338.6a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8q-2.5 0-4 .8a9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8q-1.6 1-4 1v2.4a9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.9 9 9 0 0 0 4 .9 9 9 0 0 0 4-.9 9 9 0 0 1 4-.9q2.4 0 4 .9a9 9 0 0 0 4 .9z"/>
<path fill="#ccc" d="M195.6 341a9 9 0 0 1-4-.8 9 9 0 0 0-4-.8q-2.5 0-4 .8a9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.3 0-3.9.9a9 9 0 0 1-4 .9v2.4a9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8 9 9 0 0 0 4 1 9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 4 .9 9 9 0 0 0 4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M195.6 341a9 9 0 0 1-4-.8 9 9 0 0 0-4-.8q-2.5 0-4 .8a9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.3 0-3.9.9a9 9 0 0 1-4 .9v2.4a9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8 9 9 0 0 0 4 1 9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 4 .9 9 9 0 0 0 4 .9V341"/>
<path fill="#005bbf" d="M195.6 343.6a9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1v2.4q2.4 0 4-.9a9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8 9 9 0 0 0 8 0 9 9 0 0 1 4-.8q2.4 0 4 .8 1.5 1 4 1v-2.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M195.6 343.6a9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1v2.4q2.4 0 4-.9a9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8 9 9 0 0 0 8 0 9 9 0 0 1 4-.8q2.4 0 4 .8 1.5 1 4 1v-2.6"/>
<path fill="#ccc" d="M195.6 348.5a9 9 0 0 1-4-1 9 9 0 0 0-4-.7 9 9 0 0 0-4 .8 9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.7 9 9 0 0 0-3.9.8 9 9 0 0 1-4 .9V346a9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8q1.5 1 4 1a9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 4 .8q1.5 1 4 1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M195.6 348.5a9 9 0 0 1-4-1 9 9 0 0 0-4-.7 9 9 0 0 0-4 .8 9 9 0 0 1-4 .9 9 9 0 0 1-4-1 9 9 0 0 0-4-.7 9 9 0 0 0-3.9.8 9 9 0 0 1-4 .9V346a9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 3.9.8q1.5 1 4 1a9 9 0 0 0 4-1 9 9 0 0 1 4-.8 9 9 0 0 1 4 .8q1.5 1 4 1v2.4"/>
<path fill="#005bbf" d="M195.6 351a9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .8 9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8q-1.6 1-4 1v-2.5q2.4 0 4-1a9 9 0 0 1 4-.7 9 9 0 0 1 3.9.8 9 9 0 0 0 4 .9 9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 4 .8 9 9 0 0 0 4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M195.6 351a9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .8 9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8q-1.6 1-4 1v-2.5q2.4 0 4-1a9 9 0 0 1 4-.7 9 9 0 0 1 3.9.8 9 9 0 0 0 4 .9 9 9 0 0 0 4-.9 9 9 0 0 1 4-.8 9 9 0 0 1 4 .8 9 9 0 0 0 4 .9z"/>
<path fill="#c8b100" d="m170.6 328.5.2.6c0 1.5-1.3 2.7-3 2.7h23.6c-1.6 0-2.9-1.2-2.9-2.7l.1-.6h-.5z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m170.6 328.5.2.6c0 1.5-1.3 2.7-3 2.7h23.6c-1.6 0-2.9-1.2-2.9-2.7l.1-.6h-.5z"/>
<path fill="#c8b100" d="M171 327h17.2q.9 0 1 .7-.1.8-1 .8H171q-1 0-1.1-.8.1-.7 1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M171 327h17.2q.9 0 1 .7-.1.8-1 .8H171q-1 0-1.1-.8.1-.7 1-.8z"/>
<path fill="#c8b100" d="M168 337.8h23.3v-6H168z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M168 337.8h23.3v-6H168z"/>
<path fill="#ad1519" d="M166 305.8c-2.2 1.3-3.8 2.7-3.5 3.4q.1.8 1.9 1.8c1.6 1.1 2.6 3.2 1.8 4.1a6 6 0 0 0-.1-9.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M166 305.8c-2.2 1.3-3.8 2.7-3.5 3.4q.1.8 1.9 1.8c1.6 1.1 2.6 3.2 1.8 4.1a6 6 0 0 0-.1-9.3z"/>
<path fill="#ccc" d="M171.3 326h16.6v-81.6h-16.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M183.1 244.5V326m2-81.4V326m-13.8 0h16.6v-81.5h-16.6z"/>
<path fill="#ad1519" d="M205 275a53 53 0 0 0-17-3 52 52 0 0 0-8 .8c-9.9 1.7-17.5 5.6-16.9 8.9v.2l-3.7-8.7c-.7-3.6 7.7-8 18.8-9.8a57 57 0 0 1 9.8-.8c7 0 13.2.9 16.9 2.3v10"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M205 275a53 53 0 0 0-17-3 52 52 0 0 0-8 .8c-9.9 1.7-17.5 5.6-16.9 8.9v.2l-3.7-8.7c-.7-3.6 7.7-8 18.8-9.8a57 57 0 0 1 9.8-.8c7 0 13.2.9 16.9 2.3v10"/>
<path fill="#ad1519" d="M171.3 285.1c-4.7-.3-7.8-1.5-8.2-3.5-.3-1.5 1.3-3.2 4-4.7l4.2.3v8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M171.3 285.1c-4.7-.3-7.8-1.5-8.2-3.5-.3-1.5 1.3-3.2 4-4.7l4.2.3v8"/>
<path fill="#ad1519" d="M188 279q4.4.5 6 2l.2.1c.5 1-2 3.3-6.3 5.8v-8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M188 279q4.4.5 6 2l.2.1c.5 1-2 3.3-6.3 5.8v-8"/>
<path fill="#ad1519" d="M160.9 300.9c-.4-1.3 4-4 10.4-6.3 3-1 5.3-2.1 8.3-3.4 8.9-4 15.4-8.4 14.6-10l-.1-.2c.5.4 1.2 8.4 1.2 8.4.8 1.5-5.2 6-13.3 9.8-2.6 1.3-8.1 3.3-10.7 4.2-4.7 1.6-9.3 4.7-8.9 5.8z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M160.9 300.9c-.4-1.3 4-4 10.4-6.3 3-1 5.3-2.1 8.3-3.4 8.9-4 15.4-8.4 14.6-10l-.1-.2c.5.4 1.2 8.4 1.2 8.4.8 1.5-5.2 6-13.3 9.8-2.6 1.3-8.1 3.3-10.7 4.2-4.7 1.6-9.3 4.7-8.9 5.8z"/>
<path fill="#c8b100" d="M170.1 271c2-.7 3.4-1.6 2.7-3.2q-.6-1.5-3-.7l-2.8 1 2.5 6.2.9-.3.8-.3zm-1.2-3 .7-.2q1-.4 1.5.8.5.8-.5 1.6l-.7.3zm7.7-2.6-.8.3h-1l1.5 6.5 4.5-.9-.2-.4v-.4l-2.7.7zm9 5.6 2.9-6.8h-1.1l-2 5-2.4-4.7-1 .1h-1.1l3.7 6.5.5-.1zm9.4-5 .5-.9a4 4 0 0 0-1.9-.6q-2.7-.1-3 1.7c-.2 2.3 3.4 2.1 3.3 3.7q-.3 1-1.6.8-1.3 0-1.5-1.2h-.2l-.5 1.2q.8.4 1.9.6c1.8.2 3.3-.6 3.4-1.8.2-2.2-3.4-2.3-3.3-3.6q0-1 1.4-.8 1 0 1.3 1z"/>
<path fill="#ad1519" d="M332.4 225.7s-.8.8-1.3 1c-.6 0-1.3-.6-1.3-.6s-.5.5-1.1.7-1.4-.7-1.4-.7-.6.8-1.2 1-1.1-.2-1.1-.2-.2.4-.7.6h-.5l-.6-.5-.7-.7-.6-.2-.3-1.1-.1-.6q0-1.1 2.4-1.8 1.2-.2 2 0a6 6 0 0 1 3.3-.8 6 6 0 0 1 3.2.7 6 6 0 0 1 3-.7q2.3 0 3.3.8a4 4 0 0 1 2.1 0q2.4.7 2.4 1.8v.6l-.4 1-.6.3-.7.8-.6.3s-.3.2-.5.1q-.7-.4-.7-.6c0-.2-.6.4-1.2.2s-1-1-1-1-.9.8-1.5.7q-1-.5-1.1-.7c-.1-.2-.7.6-1.2.5s-1.4-.9-1.4-.9"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.4 225.7s-.8.8-1.3 1c-.6 0-1.3-.6-1.3-.6s-.5.5-1.1.7-1.4-.7-1.4-.7-.6.8-1.2 1-1.1-.2-1.1-.2-.2.4-.7.6h-.5l-.6-.5-.7-.7-.6-.2-.3-1.1-.1-.6q0-1.1 2.4-1.8 1.2-.2 2 0a6 6 0 0 1 3.3-.8 6 6 0 0 1 3.2.7 6 6 0 0 1 3-.7q2.3 0 3.3.8a4 4 0 0 1 2.1 0q2.4.7 2.4 1.8v.6l-.4 1-.6.3-.7.8-.6.3s-.3.2-.5.1q-.7-.4-.7-.6c0-.2-.6.4-1.2.2s-1-1-1-1-.9.8-1.5.7q-1-.5-1.1-.7c-.1-.2-.7.6-1.2.5s-1.4-.9-1.4-.9z"/>
<path fill="#c8b100" d="M331 221.4q.1-1.8 1.3-2 1.3.2 1.4 2c.1 1.8-.6 2-1.4 2q-1.3-.2-1.4-2"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M331 221.4q.1-1.8 1.3-2 1.3.2 1.4 2c.1 1.8-.6 2-1.4 2q-1.3-.2-1.4-2z"/>
<path fill="#c8b100" d="M331.7 221.4c0-1 .3-1.9.7-1.9s.6.9.6 1.9q0 1.7-.7 1.8-.5-.2-.6-1.8"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M331.7 221.4c0-1 .3-1.9.7-1.9s.6.9.6 1.9q0 1.7-.7 1.8-.5-.2-.6-1.8z"/>
<path fill="#c8b100" d="m325 229.6-.5-1q3-1 7.8-1t8 1l-.6.9-.4.8q-2.7-.9-7-.8c-2.8 0-5.6.3-7 .8l-.2-.7"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m325 229.6-.5-1q3-1 7.8-1t8 1l-.6.9-.4.8q-2.7-.9-7-.8c-2.8 0-5.6.3-7 .8l-.2-.7"/>
<path fill="#c8b100" d="M332.3 232.2c2.5 0 5.3-.4 6.3-.7q1-.3 1-.8 0-.3-.4-.4a26 26 0 0 0-6.9-.9 26 26 0 0 0-6.8.9q-.4 0-.4.4t1 .8c1 .3 3.7.7 6.2.7"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.3 232.2c2.5 0 5.3-.4 6.3-.7q1-.3 1-.8 0-.3-.4-.4a26 26 0 0 0-6.9-.9 26 26 0 0 0-6.8.9q-.4 0-.4.4t1 .8c1 .3 3.7.7 6.2.7z"/>
<path fill="#fff" d="m338.4 222.3.4-.5q.5 0 .5.5 0 .3-.5.4z"/>
<path fill="none" stroke="#000" stroke-width=".2" d="m338.4 222.3.4-.5q.5 0 .5.5 0 .3-.5.4zm-.3-1.6.5-.4q.3 0 .4.4 0 .5-.4.5l-.5-.4zm-1.1-1 .4-.3q.5 0 .5.4t-.5.4l-.4-.4zm-1.5-.4q0-.3.5-.4l.4.4-.4.5q-.5 0-.5-.5zm-1.5 0 .5-.4q.5 0 .5.5t-.5.4z"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".3" d="m343 225.3.2-1.1a3 3 0 0 0-3-3l-1.3.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m337.8 223.2.3-.9c0-1.2-1.3-2.1-2.7-2.1q-1 0-1.7.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M343.4 224q0-.5.4-.5.6 0 .5.4-.1.6-.5.5t-.4-.5zm-.2-1.7q0-.3.5-.4.3 0 .4.4 0 .5-.5.4t-.4-.4zm-1-1.2.4-.5q.5 0 .5.5t-.5.4l-.5-.4zm-1.5-.7q.1-.3.5-.4.5 0 .5.4l-.5.5-.4-.5zm-1.4 0q0-.3.4-.4.5 0 .5.5t-.5.4q-.6-.2-.4-.4z"/>
<path fill="#c8b100" d="m340.3 227.4-.6-.5s-.6.3-1.3.2-1-1-1-1-.7.7-1.4.6-1.1-.6-1.1-.6-.7.5-1.4.5-1.2-.9-1.2-.9-.7.9-1.3 1-1.1-.6-1.1-.6-.3.6-1.1.7-1.5-.6-1.5-.6-.5.7-1 1c-.6.1-1.3-.4-1.3-.4l-.2.5-.4.2.2.4a33 33 0 0 1 15.5.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m340.3 227.4-.6-.5s-.6.3-1.3.2-1-1-1-1-.7.7-1.4.6-1.1-.6-1.1-.6-.7.5-1.4.5-1.2-.9-1.2-.9-.7.9-1.3 1-1.1-.6-1.1-.6-.3.6-1.1.7-1.5-.6-1.5-.6-.5.7-1 1c-.6.1-1.3-.4-1.3-.4l-.2.5-.4.2.2.4a33 33 0 0 1 15.5.1z"/>
<path fill="#fff" d="m325.3 222.3.5-.5.4.5-.4.4q-.5 0-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="m325.3 222.3.5-.5.4.5-.4.4q-.5 0-.5-.4zm.3-1.6.4-.4.5.4-.5.5a.4.4 0 0 1-.4-.4zm1.1-1q0-.2.5-.3t.5.4q-.1.6-.5.4l-.5-.4zm1.5-.4q0-.3.4-.4.5 0 .5.4t-.5.5q-.4 0-.4-.5zm1.5 0 .4-.4.5.5q0 .3-.5.4z"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".3" d="m321.7 225.3-.3-1.1a3 3 0 0 1 3-3q.7 0 1.3.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m326.9 223.2-.3-.9c0-1.2 1.2-2.1 2.7-2.1q1 0 1.6.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M320.3 224q0-.5.5-.5t.5.4l-.5.5q-.5 0-.5-.5zm.2-1.7q0-.3.5-.4.6 0 .4.4t-.4.4zm1-1.2q.1-.5.5-.5l.5.5-.5.4-.4-.4zm1.5-.7.4-.4q.5 0 .5.4l-.5.5-.5-.5zm1.4 0q0-.3.5-.4l.4.5q0 .3-.4.4-.5 0-.5-.4z"/>
<path fill="#c8b100" d="M332.4 224.8h.2v.4q.1 1 1.1 1 .8 0 1.1-.7l.2-.4v.5q.2.8 1.1.9a1 1 0 0 0 1.2-1.1v-.1l.3-.4.2.4v.5q0 1 1 1a1 1 0 0 0 1-.4l.1-.3v.4q0 .5.5.7l1-.4.8-.8v.5s-.5.8-1 1.1l-1 .3q-.5-.2-.6-.7l-.8.3q-1.1 0-1.5-1a2 2 0 0 1-1.2.6 2 2 0 0 1-1.3-.6q-.5.4-1 .4a2 2 0 0 1-1.5-.7 2 2 0 0 1-2.4.3 2 2 0 0 1-1.3.6q-.8 0-1.2-.5-.4.8-1.5.8l-.8-.2q-.1.5-.7.7l-1-.3-1-1.1v-.5l1 .8q.7.5.8.4.5-.1.5-.7v-.4l.3.3q.3.5.9.5 1-.1 1-1v-.6l.2-.4.3.4q.1 1.1 1.1 1.2t1.2-1v-.3l.2.3q.3.8 1 .8a1.1 1.1 0 0 0 1.2-1.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.4 224.8h.2v.4q.1 1 1.1 1 .8 0 1.1-.7l.2-.4v.5q.2.8 1.1.9a1 1 0 0 0 1.2-1.1v-.1l.3-.4.2.4v.5q0 1 1 1a1 1 0 0 0 1-.4l.1-.3v.4q0 .5.5.7l1-.4.8-.8v.5s-.5.8-1 1.1l-1 .3q-.5-.2-.6-.7l-.8.3q-1.1 0-1.5-1a2 2 0 0 1-1.2.6 2 2 0 0 1-1.3-.6q-.5.4-1 .4a2 2 0 0 1-1.5-.7 2 2 0 0 1-2.4.3 2 2 0 0 1-1.3.6q-.8 0-1.2-.5-.4.8-1.5.8l-.8-.2q-.1.5-.7.7l-1-.3-1-1.1v-.5l1 .8q.7.5.8.4.5-.1.5-.7v-.4l.3.3q.3.5.9.5 1-.1 1-1v-.6l.2-.4.3.4q.1 1.1 1.1 1.2t1.2-1v-.3l.2.3q.3.8 1 .8a1.1 1.1 0 0 0 1.2-1.5z"/>
<path fill="#c8b100" d="M332.3 227.6q-4.6 0-7.8 1l-.4-.2.2-.4q3.2-1 8-1c4.8 0 6 .4 8 1q.3 0 .2.3t-.3.2q-3.1-1-7.9-1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.3 227.6q-4.6 0-7.8 1l-.4-.2.2-.4q3.2-1 8-1c4.8 0 6 .4 8 1q.3 0 .2.3t-.3.2q-3.1-1-7.9-1z"/>
<path fill="#fff" d="M329.4 228.7q0-.5.4-.5t.5.5q-.1.3-.5.4-.5 0-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M329.4 228.7q0-.5.4-.5t.5.5q-.1.3-.5.4-.5 0-.5-.4z"/>
<path fill="#ad1519" d="M332.4 228.8h-1q-.4 0-.4-.3t.4-.3h2l.3.3q0 .3-.3.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.4 228.8h-1q-.4 0-.4-.3t.4-.3h2l.3.3q0 .3-.3.3h-1"/>
<path fill="#058e6e" d="M327.4 229.2h-.7a.3.3 0 0 1-.4-.2l.3-.3.7-.1.7-.2q.3 0 .4.3 0 .3-.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M327.4 229.2h-.7a.3.3 0 0 1-.4-.2l.3-.3.7-.1.7-.2q.3 0 .4.3 0 .3-.3.4h-.7"/>
<path fill="#ad1519" d="m324.5 229.7.4-.5.6.1-.4.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m324.5 229.7.4-.5.6.1-.4.6-.6-.2"/>
<path fill="#fff" d="M334.4 228.7q0-.5.5-.5t.4.5q0 .3-.4.4-.5 0-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M334.4 228.7q0-.5.5-.5t.4.5q0 .3-.4.4-.5 0-.5-.4z"/>
<path fill="#058e6e" d="M337.3 229.2h.7q.3.1.4-.2l-.3-.3-.7-.1-.7-.2q-.3 0-.4.3 0 .3.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M337.3 229.2h.7q.3.1.4-.2l-.3-.3-.7-.1-.7-.2q-.3 0-.4.3 0 .3.3.4h.7"/>
<path fill="#ad1519" d="m340.1 229.7-.3-.5h-.7l.4.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m340.1 229.7-.3-.5h-.7l.4.6h.6"/>
<path fill="#ad1519" d="M332.3 231.6a27 27 0 0 1-6.4-.7 28 28 0 0 1 13 0 27 27 0 0 1-6.6.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M332.3 231.6a27 27 0 0 1-6.4-.7 28 28 0 0 1 13 0 27 27 0 0 1-6.6.7z"/>
<path fill="#c8b100" d="m340.2 226.2-.1-.4q-.3 0-.4.2l.1.5q.3 0 .4-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m340.2 226.2-.1-.4q-.3 0-.4.2l.1.5q.3 0 .4-.3z"/>
<path fill="#c8b100" d="M335.2 225.2q0-.3-.2-.4-.2 0-.3.4t.2.4.3-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M335.2 225.2q0-.3-.2-.4-.2 0-.3.4t.2.4.3-.4z"/>
<path fill="#c8b100" d="M329.5 225.2q0-.3.2-.4.2 0 .3.4t-.2.4-.3-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M329.5 225.2q0-.3.2-.4.2 0 .3.4t-.2.4-.3-.4z"/>
<path fill="#c8b100" d="m324.5 226.2.1-.4q.3 0 .4.2l-.1.5q-.3 0-.4-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m324.5 226.2.1-.4q.3 0 .4.2l-.1.5q-.3 0-.4-.3z"/>
<path fill="#c8b100" d="m332.3 222.4-.8.5.6 1.4.2.2.3-.2.6-1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m332.3 222.4-.8.5.6 1.4.2.2.3-.2.6-1.4-.9-.5"/>
<path fill="#c8b100" d="m330.4 224.5.4.6 1.4-.4.1-.2-.1-.2-1.4-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m330.4 224.5.4.6 1.4-.4.1-.2-.1-.2-1.4-.4-.4.6"/>
<path fill="#c8b100" d="m334.3 224.5-.4.6-1.4-.4-.1-.2.1-.2 1.4-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m334.3 224.5-.4.6-1.4-.4-.1-.2.1-.2 1.4-.4.4.6"/>
<path fill="#c8b100" d="m326.6 223-.7.6 1 1.2.2.1.1-.2.3-1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m326.6 223-.7.6 1 1.2.2.1.1-.2.3-1.4-.9-.3"/>
<path fill="#c8b100" d="m325.2 225.3.5.5 1.2-.7.1-.2-.1-.2h-1.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m325.2 225.3.5.5 1.2-.7.1-.2-.1-.2h-1.5l-.2.6"/>
<path fill="#c8b100" d="m329 224.6-.3.6-1.4-.1-.2-.2v-.2l1.3-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m329 224.6-.3.6-1.4-.1-.2-.2v-.2l1.3-.7.6.6"/>
<path fill="#c8b100" d="m323.8 225.5-.1.7-1.5.1h-.2v-.3l1.2-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m323.8 225.5-.1.7-1.5.1h-.2v-.3l1.2-1 .6.5"/>
<path fill="#c8b100" d="M326.6 225a.5.5 0 0 1 .5-.6l.5.5-.5.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M326.6 225a.5.5 0 0 1 .5-.6l.5.5-.5.5z"/>
<path fill="#c8b100" d="m338 223 .8.6-1 1.2-.2.1-.1-.2-.3-1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m338 223 .8.6-1 1.2-.2.1-.1-.2-.3-1.4.9-.3"/>
<path fill="#c8b100" d="m339.5 225.3-.5.5-1.3-.7v-.2l.1-.2h1.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m339.5 225.3-.5.5-1.3-.7v-.2l.1-.2h1.5l.2.6"/>
<path fill="#c8b100" d="m335.7 224.6.3.6 1.4-.1.2-.2v-.2l-1.3-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m335.7 224.6.3.6 1.4-.1.2-.2v-.2l-1.3-.7-.6.6"/>
<path fill="#c8b100" d="m340.7 225.5.1.7 1.4.1h.3v-.3l-1.2-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m340.7 225.5.1.7 1.4.1h.3v-.3l-1.2-1-.6.5"/>
<path fill="#c8b100" d="M331.8 224.5q.1-.5.5-.5.6 0 .5.5 0 .3-.5.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M331.8 224.5q.1-.5.5-.5.6 0 .5.5 0 .3-.5.4z"/>
<path fill="#c8b100" d="M337.1 225a.5.5 0 0 1 .5-.6q.5 0 .5.5t-.5.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M337.1 225a.5.5 0 0 1 .5-.6q.5 0 .5.5t-.5.5z"/>
<path fill="#c8b100" d="M331.4 219q0-.7 1-.8c1-.1.9.4.9.9s-.4.9-1 .9a1 1 0 0 1-.9-1"/>
<path fill="#c8b100" d="M333 218.8v.6h-1.5v-.6h.5v-1.4h-.6v-.6h.6v-.5h.7v.5h.6v.6h-.6v1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M333 218.8v.6h-1.5v-.6h.5v-1.4h-.6v-.6h.6v-.5h.7v.5h.6v.6h-.6v1.4z"/>
<path fill="#c8b100" d="M333.7 218.8v.6H331v-.6h1v-1.4h-.7v-.6h.6v-.5h.7v.5h.6v.6h-.6v1.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M332.6 218.2a1 1 0 0 1 .7.9q0 .8-1 .9a1 1 0 0 1-.9-1q0-.6.7-.8"/>
<path fill="#c8b100" d="m321.9 226.3-.7-.8-.7-.3s.3-.3.6-.3l.5.2v-.2s.2 0 .3.4v.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m321.9 226.3-.7-.8-.7-.3s.3-.3.6-.3l.5.2v-.2s.2 0 .3.4v.7z"/>
<path fill="#c8b100" d="m321.9 226 .5.1q.4.3.1.5h-.5q-.3-.3-.1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m321.9 226 .5.1q.4.3.1.5h-.5q-.3-.3-.1-.5z"/>
<path fill="#c8b100" d="m342.6 226.3.7-.8.7-.3s-.3-.3-.6-.3l-.5.2v-.2s-.3 0-.4.4v.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m342.6 226.3.7-.8.7-.3s-.3-.3-.6-.3l-.5.2v-.2s-.3 0-.4.4v.7z"/>
<path fill="#c8b100" d="m342.6 226-.5.1q-.3.3-.1.5t.5 0q.4-.3.1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m342.6 226-.5.1q-.3.3-.1.5t.5 0q.4-.3.1-.5z"/>
<path fill="#c8b100" d="M321 238h22.8v-6h-22.9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M321 238h22.8v-6h-22.9z"/>
<path fill="#c8b100" d="m341.4 242-.4-.1h-17.6q1-.3 1-1.3-.1-1-1-1.4l.4.1h17.6q-1 .3-1 1.3t1 1.3"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m341.4 242-.4-.1h-17.6q1-.3 1-1.3-.1-1-1-1.4l.4.1h17.6q-1 .3-1 1.3t1 1.3z"/>
<path fill="#c8b100" d="M323.9 241.9h17q1 0 1.1.8-.1.7-1 .8h-17.1q-1-.1-1-.8t1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M323.9 241.9h17q1 0 1.1.8-.1.7-1 .8h-17.1q-1-.1-1-.8t1-.8z"/>
<path fill="#c8b100" d="M323.9 238h17q1 0 1.1.6-.1.7-1 .7h-17.1q-1 0-1.1-.7.1-.7 1-.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M323.9 238h17q1 0 1.1.6-.1.7-1 .7h-17.1q-1 0-1.1-.7.1-.7 1-.7z"/>
<path fill="#005bbf" d="M316.4 338.6q2.5 0 4-.9a9 9 0 0 1 4-.8q2.4 0 4 .8 1.6 1 4 1a9 9 0 0 0 4-1 9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v2.4a9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.2 0-3.9.9a9 9 0 0 1-4 .9 9 9 0 0 1-4-.9 9 9 0 0 0-4-.9q-2.4 0-4 .9a9 9 0 0 1-4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M316.4 338.6q2.5 0 4-.9a9 9 0 0 1 4-.8q2.4 0 4 .8 1.6 1 4 1a9 9 0 0 0 4-1 9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v2.4a9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.2 0-3.9.9a9 9 0 0 1-4 .9 9 9 0 0 1-4-.9 9 9 0 0 0-4-.9q-2.4 0-4 .9a9 9 0 0 1-4 .9z"/>
<path fill="#ccc" d="M316.4 341a9 9 0 0 0 4-.8 9 9 0 0 1 4-.8q2.4 0 4 .8a9 9 0 0 0 4 .9q2.5 0 4-1a9 9 0 0 1 4-.8 8 8 0 0 1 3.9.9 9 9 0 0 0 4 .9v2.4a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8q-2.2 0-3.9.8a9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M316.4 341a9 9 0 0 0 4-.8 9 9 0 0 1 4-.8q2.4 0 4 .8a9 9 0 0 0 4 .9q2.5 0 4-1a9 9 0 0 1 4-.8 8 8 0 0 1 3.9.9 9 9 0 0 0 4 .9v2.4a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8q-2.2 0-3.9.8a9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .9 9 9 0 0 1-4 .9V341"/>
<path fill="#005bbf" d="M316.4 343.6q2.5 0 4-1a9 9 0 0 1 4-.8 9 9 0 0 1 4 .9q1.6.8 4 .9 2.5 0 4-1a9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v2.4a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.4 0-4 .8a9 9 0 0 1-4 1v-2.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M316.4 343.6q2.5 0 4-1a9 9 0 0 1 4-.8 9 9 0 0 1 4 .9q1.6.8 4 .9 2.5 0 4-1a9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v2.4a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8q-2.4 0-4 .8a9 9 0 0 1-4 1v-2.6"/>
<path fill="#ccc" d="M316.4 348.5a9 9 0 0 0 4-1 9 9 0 0 1 4-.7 9 9 0 0 1 4 .8q1.6.8 4 .9 2.5 0 4-1a9 9 0 0 1 4-.7q2.3 0 3.9.8t4 .9V346a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .8 9 9 0 0 1-4 1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M316.4 348.5a9 9 0 0 0 4-1 9 9 0 0 1 4-.7 9 9 0 0 1 4 .8q1.6.8 4 .9 2.5 0 4-1a9 9 0 0 1 4-.7q2.3 0 3.9.8t4 .9V346a9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-3.9.8 9 9 0 0 1-4 1 9 9 0 0 1-4-1 9 9 0 0 0-4-.8 9 9 0 0 0-4 .8 9 9 0 0 1-4 1v2.4"/>
<path fill="#005bbf" d="M316.4 351q2.5 0 4-1a9 9 0 0 1 4-.8 9 9 0 0 1 4 .9q1.6.8 4 .8 2.5 0 4-.9a9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v-2.5a9 9 0 0 1-4-1 9 9 0 0 0-4-.7q-2.2 0-3.9.8a9 9 0 0 1-4 .9 9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-4 .8 9 9 0 0 1-4 .9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M316.4 351q2.5 0 4-1a9 9 0 0 1 4-.8 9 9 0 0 1 4 .9q1.6.8 4 .8 2.5 0 4-.9a9 9 0 0 1 4-.8q2.3 0 3.9.8 1.6 1 4 1v-2.5a9 9 0 0 1-4-1 9 9 0 0 0-4-.7q-2.2 0-3.9.8a9 9 0 0 1-4 .9 9 9 0 0 1-4-.9 9 9 0 0 0-4-.8 9 9 0 0 0-4 .8 9 9 0 0 1-4 .9z"/>
<path fill="#c8b100" d="m341.4 328.5-.2.6c0 1.5 1.3 2.7 3 2.7h-23.6c1.6 0 2.9-1.2 2.9-2.7v-.6z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m341.4 328.5-.2.6c0 1.5 1.3 2.7 3 2.7h-23.6c1.6 0 2.9-1.2 2.9-2.7v-.6z"/>
<path fill="#c8b100" d="M323.9 327h17q1 0 1.1.7-.1.8-1 .8h-17.1q-1 0-1-.8 0-.7 1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M323.9 327h17q1 0 1.1.7-.1.8-1 .8h-17.1q-1 0-1-.8 0-.7 1-.8z"/>
<path fill="#c8b100" d="M320.7 337.8H344v-6h-23.4z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M320.7 337.8H344v-6h-23.4z"/>
<path fill="#ad1519" d="M346 305.8c2.2 1.3 3.8 2.7 3.6 3.4q-.4.8-2 1.8c-1.6 1.1-2.6 3.2-1.8 4.1a6 6 0 0 1 .1-9.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M346 305.8c2.2 1.3 3.8 2.7 3.6 3.4q-.4.8-2 1.8c-1.6 1.1-2.6 3.2-1.8 4.1a6 6 0 0 1 .1-9.3z"/>
<path fill="#ccc" d="M324 326h16.7v-81.6h-16.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M336.2 244.4v81.4m1.8-81.4v81.4m-14 .2h16.7v-81.6h-16.6z"/>
<path fill="#ad1519" d="M307 275a53 53 0 0 1 25-2.2c9.9 1.7 17.5 5.6 16.9 8.9v.2s3.7-8.4 3.7-8.7c.7-3.6-7.7-8-18.8-9.8a57 57 0 0 0-9.8-.8c-7 0-13.2.9-16.9 2.3v10"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M307 275a53 53 0 0 1 25-2.2c9.9 1.7 17.5 5.6 16.9 8.9v.2s3.7-8.4 3.7-8.7c.7-3.6-7.7-8-18.8-9.8a57 57 0 0 0-9.8-.8c-7 0-13.2.9-16.9 2.3v10"/>
<path fill="#ad1519" d="M340.8 285.1c4.6-.3 7.7-1.5 8-3.5q.4-2.4-4-4.7l-4 .3v8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M340.8 285.1c4.6-.3 7.7-1.5 8-3.5q.4-2.4-4-4.7l-4 .3v8"/>
<path fill="#ad1519" d="M324 279q-4.4.5-6 2l-.2.1c-.5 1 2 3.3 6.3 5.8v-8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M324 279q-4.4.5-6 2l-.2.1c-.5 1 2 3.3 6.3 5.8v-8"/>
<path fill="#ad1519" d="M351.1 300.9c.4-1.3-4-4-10.4-6.3-3-1-5.3-2.1-8.3-3.4-8.8-4-15.4-8.4-14.6-10l.1-.2c-.4.4-1.2 8.4-1.2 8.4-.8 1.5 5.2 6 13.3 9.8 2.6 1.3 8.1 3.3 10.7 4.2 4.7 1.6 9.3 4.7 8.9 5.8z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M351.1 300.9c.4-1.3-4-4-10.4-6.3-3-1-5.3-2.1-8.3-3.4-8.8-4-15.4-8.4-14.6-10l.1-.2c-.4.4-1.2 8.4-1.2 8.4-.8 1.5 5.2 6 13.3 9.8 2.6 1.3 8.1 3.3 10.7 4.2 4.7 1.6 9.3 4.7 8.9 5.8z"/>
<path fill="#c8b100" d="M317.5 271.3q1-3.6 2.2-7l-.5.1h-.6q-.6 2.6-1.5 5.1l-2.9-4.4-1 .2-1 .1 4.2 6h1.1m6.3-7H322l-.2 6.5h4.6v-.8l-2.7.1zm7.3 1.1 2.1.3v-.4l.1-.4-6.2-.5v.8h2.2l-.6 6h1l.8.1zm2.5 6.5 1 .2.8.2.7-3h.1l.5 1.2 1 2.3 1 .2 1.1.2-.3-.6-1.5-3.1q1.8 0 2.3-1.3.2-1-.7-1.6l-1.9-.6-2.5-.5zm3.2-5.6c.8.2 1.7.3 1.7 1.1v.5q-.4 1.5-2.3 1zm8.7 7.6-.3 2.1.9.5.9.5.6-7.4-.8-.4-6.6 4.1.6.3.4.3 1.8-1.4zm-1.9-1.7 2.2-1.4-.3 2.4z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M230.3 205.3c0-1.2 1-2.1 2.2-2.1q2.1.2 2.3 2c0 1.2-1 2.2-2.3 2.2a2 2 0 0 1-2.2-2.1z"/>
<path fill="#ad1519" stroke="#000" stroke-width=".3" d="M255.3 187.1c6.8 0 13 1 16.8 2.6a32 32 0 0 0 8.6 2.2q4 .5 7 .2c2.9 0 6.9.8 11 2.6a29 29 0 0 1 8 5l-1.7 1.5-.4 4-4.4 5-2.2 2-5.3 4-2.6.3-.8 2.3-33.7-4-33.8 4-.9-2.3-2.6-.2-5.2-4.2-2.2-1.9-4.4-5-.5-4-1.6-1.5a30 30 0 0 1 8-5c4-1.8 8-2.6 10.9-2.6q3 .3 7-.2a32 32 0 0 0 8.6-2.2q6.1-2.5 16.4-2.6z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M255.9 231.6c-12.6 0-23.8-1.5-31.9-4q-1-.2-.8-1.2a1 1 0 0 1 .8-1.2 120 120 0 0 1 31.9-4c12.5.1 23.8 1.6 31.8 4q.9.4.9 1.2 0 1-1 1.3a119 119 0 0 1-31.7 4"/>
<path fill="#ad1519" d="M255.8 230a121 121 0 0 1-29.3-3.4c7.8-2 18-3.2 29.3-3.2a123 123 0 0 1 29.5 3.2c-7.8 2-18.1 3.4-29.5 3.4"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M256.7 230v-6.7m-1.9 6.7v-6.7"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M253.1 230v-6.7m-1.7 6.7v-6.7"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M249.9 230v-6.7m-2.9 6.4v-6.2m1.4 6.2v-6.4"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M244.3 229.4v-5.7m1.4 5.9v-6m-5 5.4v-4.9m1.2 5V224m1.2 5.4V224"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M239.4 229v-4.8m-1.1 4.6v-4.5"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M237 228.7v-4.2m-2.6 3.8V225m1.4 3.5v-3.7"/>
<path fill="none" stroke="#000" stroke-width=".7" d="M233.1 228v-2.9m-1.2 2.7v-2.4"/>
<path fill="none" stroke="#000" stroke-width=".8" d="M230.6 227.5v-2m-1.4 1.8v-1.4"/>
<path fill="none" stroke="#000" stroke-width=".9" d="M227.8 227v-.7"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M264 229.7v-6.2m-3.2 6.4v-6.6m-2.2 6.6v-6.6"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M255.8 221.3a115 115 0 0 0-32.2 4c.7-.3.6-1-.2-3.2-1-2.6-2.6-2.5-2.6-2.5a130 130 0 0 1 35-4.3c13.8 0 26.2 1.7 35.1 4.3 0 0-1.5-.1-2.5 2.5q-1.3 3-.3 3.3c-8-2.5-19.6-4.1-32.3-4.1"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M255.8 215.3a131 131 0 0 0-35 4.3q-1 .3-1.4-.6a1 1 0 0 1 .8-1.4c8.9-2.7 21.6-4.4 35.6-4.4s26.9 1.7 35.8 4.4q1 .5.7 1.4t-1.4.6c-8.9-2.6-21.3-4.2-35-4.3"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M255.8 230a121 121 0 0 1-29.3-3.4c7.8-2 18-3.2 29.3-3.2a123 123 0 0 1 29.5 3.2c-7.8 2-18.1 3.4-29.5 3.4z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M246 218.5q.1-1 1.1-1.1 1.1.1 1.2 1t-1.2 1.1a1 1 0 0 1-1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M255.9 219.3h-3.4q-1-.1-1.1-1 .1-1 1-1.1h6.9a1 1 0 0 1 1.1 1q-.1 1-1.1 1.1h-3.4"/>
<path fill="#058e6e" stroke="#000" stroke-width=".4" d="m239 220.2-2.5.3q-1 0-1.2-1a1 1 0 0 1 1-1.1l2.4-.3 2.5-.3a1 1 0 0 1 1.2 1q0 .9-1 1.1l-2.5.3"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M229.1 220.4q.1-.9 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1.1a1 1 0 0 1-1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="m221.6 222.4 1.3-1.7 3.4.5-2.7 2-2-.8"/>
<path fill="#058e6e" stroke="#000" stroke-width=".4" d="m272.8 220.2 2.4.3q1 0 1.3-1a1 1 0 0 0-1-1.1l-2.5-.3-2.4-.3q-1.1 0-1.3 1 0 .9 1 1.1l2.5.3"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M263.4 218.5q.2-1 1.2-1.1c1-.1 1.1.5 1.1 1q-.1 1-1.1 1.1a1 1 0 0 1-1.2-1m17 2q.1-1 1-1.1a1 1 0 0 1 1.2 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="m290 222.4-1.2-1.7-3.4.5 2.8 2 1.9-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M225.1 227c8-2.3 18.7-3.6 30.7-3.7 12 0 22.9 1.4 30.8 3.7"/>
<path fill="#c8b100" d="m230.4 196 1.5 1.1 2.1-3.4a8 8 0 0 1-3.8-7.6c.2-4.5 5.6-8.1 12.5-8.1q5.4.1 9 2.5l.2-1.9a19 19 0 0 0-9.2-2.3c-8 0-14.1 4.4-14.4 9.8a10 10 0 0 0 3.3 8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="m230.4 196 1.5 1.1 2.1-3.4a8 8 0 0 1-3.8-7.6c.2-4.5 5.6-8.1 12.5-8.1q5.4.1 9 2.5l.2-1.9a19 19 0 0 0-9.2-2.3c-8 0-14.1 4.4-14.4 9.8a10 10 0 0 0 3.3 8l-1.2 2"/>
<path fill="#c8b100" d="M230.5 196a10 10 0 0 1-4.3-7.7c0-3.5 2.2-6.6 5.7-8.6a9 9 0 0 0-3.6 6.4 10 10 0 0 0 3.3 8l-1 2"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M230.5 196a10 10 0 0 1-4.3-7.7c0-3.5 2.2-6.6 5.7-8.6a9 9 0 0 0-3.6 6.4 10 10 0 0 0 3.3 8l-1 2"/>
<path fill="#c8b100" d="M206.8 199.6a9 9 0 0 1-2.5-6.3q0-2.2 1-4c2.1-4.5 9-7.8 17.1-7.8q3.4 0 6.3.7-.7.8-1 1.5a27 27 0 0 0-5.3-.5c-7.4 0-13.6 3-15.4 6.8a8 8 0 0 0-.8 3.3 8 8 0 0 0 2.9 6l-2.7 4.4-1.5-1.2z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M206.8 199.6a9 9 0 0 1-2.5-6.3q0-2.2 1-4c2.1-4.5 9-7.8 17.1-7.8q3.4 0 6.3.7-.7.8-1 1.5a27 27 0 0 0-5.3-.5c-7.4 0-13.6 3-15.4 6.8a8 8 0 0 0-.8 3.3 8 8 0 0 0 2.9 6l-2.7 4.4-1.5-1.2z"/>
<path fill="#c8b100" d="M209.5 184.8a11 11 0 0 0-4.2 4.4 9 9 0 0 0-1 4.1q.1 3.7 2.5 6.3l-1.7 2.6a11 11 0 0 1-2.4-6.8c0-4.3 2.7-8.1 6.8-10.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M209.5 184.8a11 11 0 0 0-4.2 4.4 9 9 0 0 0-1 4.1q.1 3.7 2.5 6.3l-1.7 2.6a11 11 0 0 1-2.4-6.8c0-4.3 2.7-8.1 6.8-10.6z"/>
<path fill="#c8b100" d="M255.8 175.4c1.7 0 3.3 1.2 3.7 2.8q.3 2.1.4 4.7v1.2q.1 5.5 1.3 8.7l-5.5 5.2-5.5-5.2q1.1-3.2 1.3-8.7v-1.2q0-2.6.5-4.7c.3-1.6 2-2.8 3.7-2.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M255.8 175.4c1.7 0 3.3 1.2 3.7 2.8q.3 2.1.4 4.7v1.2q.1 5.5 1.3 8.7l-5.5 5.2-5.5-5.2q1.1-3.2 1.3-8.7v-1.2q0-2.6.5-4.7c.3-1.6 2-2.8 3.7-2.8z"/>
<path fill="#c8b100" d="M255.8 177a2 2 0 0 1 1.9 1.5l.4 4.5v1.1c0 3.4.5 6.4 1.2 8.2l-3.6 3.5-3.6-3.5c.7-1.8 1.2-4.8 1.2-8.2V183q0-2.4.5-4.5.4-1.3 2-1.4"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M255.8 177a2 2 0 0 1 1.9 1.5l.4 4.5v1.1c0 3.4.5 6.4 1.2 8.2l-3.6 3.5-3.6-3.5c.7-1.8 1.2-4.8 1.2-8.2V183q0-2.4.5-4.5.4-1.3 2-1.4z"/>
<path fill="#c8b100" d="m281 196-1.4 1.1-2.2-3.4a8 8 0 0 0 3.9-7.6c-.2-4.5-5.7-8.1-12.5-8.1a16 16 0 0 0-9 2.5l-.3-1.9a19 19 0 0 1 9.3-2.3c7.9 0 14 4.4 14.4 9.8a10 10 0 0 1-3.3 8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="m281 196-1.4 1.1-2.2-3.4a8 8 0 0 0 3.9-7.6c-.2-4.5-5.7-8.1-12.5-8.1a16 16 0 0 0-9 2.5l-.3-1.9a19 19 0 0 1 9.3-2.3c7.9 0 14 4.4 14.4 9.8a10 10 0 0 1-3.3 8l1.1 1.9"/>
<path fill="#c8b100" d="M280.9 196q4.2-3.1 4.4-7.7c0-3.5-2.3-6.6-5.7-8.6a9 9 0 0 1 3.6 7.3q-.2 4.3-3.3 7.2l1 1.9"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M280.9 196q4.2-3.1 4.4-7.7c0-3.5-2.3-6.6-5.7-8.6a9 9 0 0 1 3.6 7.3q-.2 4.3-3.3 7.2l1 1.9"/>
<path fill="#c8b100" d="M304.7 199.6a9 9 0 0 0 1.5-10.3c-2.2-4.5-9-7.8-17.2-7.8a28 28 0 0 0-6.3.7l1.1 1.5a27 27 0 0 1 5.2-.5c7.5 0 13.7 3 15.5 6.8q.8 1.5.8 3.3a8 8 0 0 1-3 6l2.8 4.4 1.4-1.2z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M304.7 199.6a9 9 0 0 0 1.5-10.3c-2.2-4.5-9-7.8-17.2-7.8a28 28 0 0 0-6.3.7l1.1 1.5a27 27 0 0 1 5.2-.5c7.5 0 13.7 3 15.5 6.8q.8 1.5.8 3.3a8 8 0 0 1-3 6l2.8 4.4 1.4-1.2z"/>
<path fill="#c8b100" d="M302 184.8a11 11 0 0 1 4.2 4.4 9 9 0 0 1 1 4.1q-.1 3.7-2.5 6.3l1.6 2.6a11 11 0 0 0 2.5-6.8c0-4.3-2.7-8.1-6.9-10.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M302 184.8a11 11 0 0 1 4.2 4.4 9 9 0 0 1 1 4.1q-.1 3.7-2.5 6.3l1.6 2.6a11 11 0 0 0 2.5-6.8c0-4.3-2.7-8.1-6.9-10.6z"/>
<path fill="#fff" d="M253.8 193.5c0-1 .9-1.9 2-1.9 1 0 1.9.9 1.9 1.9a2 2 0 0 1-2 1.8 2 2 0 0 1-2-1.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M253.8 193.5c0-1 .9-1.9 2-1.9 1 0 1.9.9 1.9 1.9a2 2 0 0 1-2 1.8 2 2 0 0 1-2-1.8z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M253.8 189.8a2 2 0 0 1 2-1.8 2 2 0 0 1 1.9 1.9 2 2 0 0 1-2 1.8 2 2 0 0 1-2-1.8m.5-4q.1-1.3 1.5-1.4 1.5 0 1.6 1.5-.1 1.3-1.6 1.4-1.4-.1-1.5-1.4m.4-3.6q.1-.9 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1.1a1 1 0 0 1-1-1m.1-3.1q0-.8 1-.9c1-.1.8.4.8.9s-.4.8-.9.8-.9-.3-.9-.8"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="m255.9 204.6 1.3.2a5 5 0 0 0 4.8 6.4 5 5 0 0 0 4.8-3.3s.4-1.7.6-1.7.2 1.9.3 1.9c.3 2.4 2.5 4 5 4a5 5 0 0 0 5-5.4l1.5-1.5.9 2a4 4 0 0 0-.5 2 4.7 4.7 0 0 0 4.8 4.6 5 5 0 0 0 4-2l1-1.3v1.6c0 1.5.7 3 2.2 3.2 0 0 1.8.1 4.1-1.7a28 28 0 0 0 3.7-3.4l.2 1.9s-2 3-4 4.2c-1.2.7-3 1.4-4.4 1.1s-2.6-1.4-3.1-2.7a7 7 0 0 1-3.6 1 7 7 0 0 1-6.5-4 7.5 7.5 0 0 1-11-.3 7 7 0 0 1-5 1.9 7 7 0 0 1-6.1-3.1 7 7 0 0 1-6.1 3 7 7 0 0 1-5-1.8 7.5 7.5 0 0 1-11 .3 7 7 0 0 1-6.5 4 7 7 0 0 1-3.6-1q-.9 2.2-3.1 2.8c-1.4.2-3.2-.5-4.3-1.2-2.2-1.2-4.1-4.3-4.1-4.3l.2-1.8s1.3 1.5 3.6 3.4c2.4 1.8 4.2 1.7 4.2 1.7 1.5-.2 2.2-1.7 2.2-3.2v-1.6l1 1.3a5 5 0 0 0 4 2c2.6 0 4.8-2 4.8-4.6q0-1-.5-2l.8-2 1.6 1.5v.6a5 5 0 0 0 5 4.8c2.5 0 4.7-1.7 5-4 0 0 0-1.9.2-1.9s.7 1.8.7 1.7a5 5 0 0 0 4.8 3.3 5 5 0 0 0 4.8-6.4l1.3-.2"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M290.5 210.8c.3-.8 0-1.7-.6-1.8q-1.1-.2-1.6 1.2c-.3.8 0 1.7.5 1.8q1 .2 1.7-1.2m-22-4.2q.2-1.4-.9-1.7-1 0-1.3 1.4-.1 1.6 1 1.8 1 0 1.3-1.5m-25.4 0q-.1-1.4 1-1.7 1 0 1.3 1.4.1 1.6-1 1.8-1 0-1.3-1.5m-22 4.3q-.3-1.4.7-2 1-.1 1.6 1.3.4 1.4-.6 1.9-1 0-1.6-1.2"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M230.8 196.3a6 6 0 0 1 2.4 3s0-.2.7-.6q1-.4 1.1-.3l-.2 1.4c-.1.3-.1 1.4-.4 2.3l-.6 1.8a2 2 0 0 0-1.6-.5 2 2 0 0 0-1.4 1s-.7-.6-1.2-1.4l-1.2-2.2-.7-1.2h1.1l1 .3a5 5 0 0 1 1-3.7"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M231.3 206.7a2 2 0 0 1-.7-1.1 2 2 0 0 1 .2-1.3l-1.9-.7c-.7-.2-2-.2-2.4-.2h-1.2l.3.6.5.7a5 5 0 0 0-3 2.2 6 6 0 0 0 3.6 1l-.2.8v.7l1-.4c.4-.2 1.6-.6 2.2-1l1.6-1.3m2.9-.5.2-1.2a2 2 0 0 0-.7-1.1s.7-.8 1.5-1.3l2.3-1 1-.4v.6l-.2.9a6 6 0 0 1 3.7 1 5 5 0 0 1-3.1 2.1l.8 1.4h-1.2c-.4 0-1.7 0-2.4-.3-1-.2-2-.7-2-.7"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M230.3 205.3c0-1.2 1-2.1 2.2-2.1q2.1.2 2.3 2c0 1.2-1 2.2-2.3 2.2a2 2 0 0 1-2.2-2.1"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M255.8 192.9a6 6 0 0 1 2 3.9s.2-.3 1-.6 1.2-.1 1.2-.1l-.5 1.4c-.2.4-.4 1.6-.9 2.6l-1 1.8a2 2 0 0 0-1.8-.7q-1 0-1.7.7-.2 0-1-1.8c-.5-1-.7-2.2-.9-2.6l-.6-1.4s.6-.1 1.3.2q1.1.4 1 .5a6 6 0 0 1 2-4"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M254.2 204.6a2 2 0 0 1-.6-1.3q0-.8.6-1.4s-1-.7-2-1.1c-.8-.4-2.2-.6-2.6-.7l-1.3-.3.1.7.4 1q-2.3.2-3.9 1.8a6 6 0 0 0 4 1.8l-.5 1-.2.6 1.4-.2 2.6-.7 2-1.2m3.3 0q.5-.5.5-1.3a2 2 0 0 0-.5-1.4s1-.7 2-1.1c.8-.4 2.2-.6 2.6-.7l1.3-.3-.2.7-.4 1a6 6 0 0 1 4 1.8 6 6 0 0 1-4 1.8l.4 1 .2.6-1.3-.2-2.6-.7-2-1.2m23.5-8.4a6 6 0 0 0-2.4 3.1l-.7-.6q-1-.4-1.1-.3l.2 1.4.3 2.3.7 1.8a2 2 0 0 1 1.6-.5 2 2 0 0 1 1.4 1l1.2-1.4 1.2-2.2.7-1.2h-1.2q-1 .2-.9.3a5 5 0 0 0-1-3.7"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M280.5 206.7q.5-.5.7-1.1a2 2 0 0 0-.2-1.3l1.8-.7c.8-.2 2-.2 2.5-.2h1.1l-.2.6-.6.7a5 5 0 0 1 3.1 2.2 6 6 0 0 1-3.6 1l.2.8v.7l-1-.4-2.3-1-1.5-1.3m-2.9-.5-.3-1.2q.2-.8.7-1.1s-.7-.8-1.5-1.3c-.6-.4-1.8-.9-2.2-1l-1-.4v.6l.2.9a6 6 0 0 0-3.7 1q1.1 1.6 3 2.1l-.4.8-.3.6h1.2c.4 0 1.7 0 2.4-.3 1-.2 1.9-.7 1.9-.7"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M277 205.3q.2-2 2.2-2.1 2.1.2 2.3 2c0 1.2-1 2.2-2.3 2.2a2 2 0 0 1-2.2-2.1m24.8 4.6c-.5-.5-1.6-.4-2.4.3s-1 1.7-.5 2.2 1.6.5 2.4-.2q1.2-1.2.5-2.3"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M298.7 211.3q.1-.6.7-1.1c.8-.7 2-.8 2.4-.3l.2.3s1.2-2.1 2.5-2.9 3.6-.5 3.6-.5a3 3 0 0 0-3.1-3q-1.6 0-2.5 1.2l-.3-1.1s-1.4.3-2 1.8c-.6 1.7 0 4 0 4s-.3-1-.8-1.7a9 9 0 0 0-2.6-1.7l-1.4-.8v1.5a8 8 0 0 0-4 .5 5 5 0 0 0 2.7 2.3l-.8.8-.5.5 1.4.2c.4 0 1.8.3 2.7.2l1.8-.2m-85.6 0a2 2 0 0 0-.8-1.1c-.8-.7-1.9-.8-2.4-.3l-.2.3s-1-2.1-2.4-2.9-3.6-.5-3.6-.5a3 3 0 0 1 3-3q1.6 0 2.6 1.2l.2-1.1s1.4.3 2 1.9 0 3.9 0 3.9.4-1 .9-1.7 1.8-1.4 2.5-1.7l1.4-.8v1.5a8 8 0 0 1 4 .5 5 5 0 0 1-2.7 2.3l.9.8.4.5-1.3.2c-.4 0-1.9.3-2.7.2l-1.8-.2"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M210 210q1-.9 2.3.2c.9.7 1 1.7.6 2.2q-1 .9-2.4-.2t-.6-2.3m43.7-6.6q.2-2 2.3-2.1c2.1-.1 2.2.9 2.2 2s-1 2.2-2.3 2.2a2 2 0 0 1-2.2-2.1"/>
<path fill="#005bbf" stroke="#000" stroke-width=".3" d="M251.2 171.3c0-2.4 2-4.3 4.6-4.3s4.5 2 4.5 4.3a4.4 4.4 0 0 1-4.5 4.3 4.4 4.4 0 0 1-4.6-4.3"/>
<path fill="#c8b100" stroke="#000" stroke-width=".3" d="M254.6 159.3v2.3H252v2.3h2.5v6.8h-3.2l-.2.6q0 1 .4 1.7h8.3l.4-1.7-.2-.6h-3V164h2.4v-2.3H257v-2.3z"/>
<path fill="#ccc" d="M256.2 352.6a87 87 0 0 1-37.8-8.8 24 24 0 0 1-13.7-21.6V288h102.8v34c0 9.5-5.4 17.7-13.7 21.8a86 86 0 0 1-37.6 8.7"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M256.2 352.6a87 87 0 0 1-37.8-8.8 24 24 0 0 1-13.7-21.6V288h102.8v34c0 9.5-5.4 17.7-13.7 21.8a86 86 0 0 1-37.6 8.7z"/>
<path fill="#ccc" d="M256 288h51.5v-57H256z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M256 288h51.5v-57H256z"/>
<path fill="#ad1519" d="M256 322.1a25 25 0 0 1-25.6 24.4c-14.2 0-25.8-11-25.8-24.4V288H256v34"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M215.8 342.3c1.7.8 3.9 2.2 6.2 2.8l-.1-58.3h-6v55.4z"/>
<path fill="#c8b100" stroke="#000" stroke-linejoin="round" stroke-width=".5" d="M204.5 321.7a26 26 0 0 0 6 16v-50.6h-5.9z"/>
<path fill="#c7b500" stroke="#000" stroke-width=".5" d="M227.3 346.4c2.4.2 4.1.2 6 0v-59.6h-6z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M238.6 345a20 20 0 0 0 6.2-2.6v-55.6h-6z"/>
<path fill="#ad1519" d="M204.6 288H256v-57h-51.4z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M204.6 288H256v-57h-51.4z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M250.4 337.1c2.5-2.2 4.9-7.3 5.7-13l.2-37.3h-6V337z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M256 322.1a25 25 0 0 1-25.6 24.4c-14.2 0-25.8-11-25.8-24.4V288H256v34"/>
<path fill="#ad1519" d="M307.6 288v34a25 25 0 0 1-25.8 24.4c-14.2 0-25.8-11-25.8-24.4v-34z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M307.6 288v34a25 25 0 0 1-25.8 24.4c-14.2 0-25.8-11-25.8-24.4v-34h51.6"/>
<path fill="#c8b100" d="m265.4 313.7.1.6q0 1-1.2 1.1-1-.1-1.2-1.1 0-.3.2-.5h-1.7a3 3 0 0 0 1.9 3v4.1h1.7v-4.1a3 3 0 0 0 1.8-1.7h4.7v-1.4zm23.1 0v1.4h-4.2l-.3.6 5 5.6-1.4 1-4.9-5.6-.2.1v9.3h-1.8v-9.3l-.2-.1-5 5.6-1.4-1 5.1-5.7-.2-.5h-4.4v-1.4h14zm2.8 0v1.4h4.8q.4 1.2 1.7 1.7v4.1h1.8v-4.1a3 3 0 0 0 2-2.5l-.2-.6h-1.6a1.2 1.2 0 0 1-1 1.7q-1.2-.1-1.3-1.1l.1-.5zm-7 23.5a17 17 0 0 0 3.8-1l.9 1.4a19 19 0 0 1-4.6 1.3 3 3 0 0 1-2.8 2 3 3 0 0 1-2.7-2 19 19 0 0 1-4.8-1.3l.8-1.5q2 .9 4.2 1.2a3 3 0 0 1 1.6-1.5v-7.1h1.8v7.1a3 3 0 0 1 1.7 1.4zm-11.8-2.4-.9 1.5a18 18 0 0 1-3.9-3.3 3 3 0 0 1-2.6-.5 2.5 2.5 0 0 1-.4-3.7l.2-.2a16 16 0 0 1-1.4-5.1h1.8a14 14 0 0 0 1.1 4.4l1.5.1 4.4-4.8 1.3 1-4.4 4.9a3 3 0 0 1 0 3 16 16 0 0 0 3.2 2.7zm-6.5-5a1.2 1.2 0 0 1 1.7-.2 1 1 0 0 1 0 1.6 1.3 1.3 0 0 1-1.6.2 1 1 0 0 1-.1-1.6m-2.2-4.8-1.8-.4-.3-4.6 1.8-.6v2.6l.2 3zm1.4-5.7 1.8.4v2.3c0-.8.3 2.3.3 2.3l-1.8.6-.3-2.9zm6 14.6a17 17 0 0 0 5.1 2.7l.4-1.7a15 15 0 0 1-4.3-2.1zm-1 1.6a19 19 0 0 0 5.2 2.7l-1.3 1.3a20 20 0 0 1-4.2-2.2zm2.3-10 1.7.7 3.2-3.5-1-1.5zm-1.3-1-1-1.5 3.1-3.5 1.7.7zm19.3 10.5.9 1.5a18 18 0 0 0 3.8-3.3q1.6.4 2.7-.5a2.5 2.5 0 0 0 .3-3.7l-.1-.2q1.1-2.4 1.3-5.1h-1.7a14 14 0 0 1-1.2 4.4l-1.5.1-4.3-4.8-1.3 1 4.3 4.9a3 3 0 0 0 .1 3 16 16 0 0 1-3.3 2.7m6.5-5a1.2 1.2 0 0 0-1.7-.2 1 1 0 0 0-.1 1.6q.8.8 1.7.1a1 1 0 0 0 0-1.6zm2.2-4.8 1.8-.5.2-4.5-1.7-.6v2.6zm-1.5-5.7-1.8.4v2.3c0-.8-.3 2.3-.3 2.3l1.9.6.2-2.9zm-6 14.6a17 17 0 0 1-5 2.7l-.5-1.7a15 15 0 0 0 4.3-2.1zm.9 1.6a19 19 0 0 1-5.2 2.7l1.3 1.3a20 20 0 0 0 4.3-2.2zm-2.4-10-1.7.7-3.1-3.5 1-1.5zm1.4-1 1-1.5-3.2-3.5-1.7.7 3.8 4.3m-21.4-9.3.5 1.7h4.8l.6-1.7zm22.5 0-.5 1.7h-4.8l-.5-1.7zm-12.4 23.3q0-1 1.2-1.1 1 .1 1.1 1.1 0 1.1-1.2 1.2c-1.2.1-1.2-.6-1.2-1.2zm2-8.2 1.8-.5v-5l-1.8-.5zm-1.8 0-1.8-.5v-5l1.8-.5z"/>
<path fill="#c8b100" d="M261.6 313.8q.4-1.5 1.9-2V306h1.7v5.7q1.4.5 1.8 1.7h4.7v.3h-6.3a1 1 0 0 0-1-.6 1 1 0 0 0-1.1.6h-1.7m13 0v-.3h4.4l.2-.4-5.4-6 1.3-1 5.3 5.8h.3v-8h1.7v7.9h.3l5.2-5.8 1.3 1-5.2 5.9.3.6h4.2v.3zm23 0a1 1 0 0 1 1.1-.6q.8 0 1 .6h1.7a3 3 0 0 0-1.8-2V306h-1.8v5.7a3 3 0 0 0-1.8 1.7h-4.6v.3zm-32.2-16 6.5 7.3 1.3-1-6.5-7.3.3-.6h4.7v-1.6H267a3 3 0 0 0-2.7-1.9 2.7 2.7 0 0 0-2.8 2.7q.1 1.8 1.9 2.5v5.6h1.7v-5.6zm34 .1v5.6h-1.7v-5.6l-.4-.2-6.3 7.4-1.3-1 6.5-7.5-.1-.3h-4.8v-1.6h4.8a3 3 0 0 1 2.6-1.9c1.6 0 2.8 1.2 2.8 2.7q-.1 2-2 2.5zm-17 0v3.4h-1.8V298a3 3 0 0 1-1.8-1.7h-4.3v-1.6h4.3a3 3 0 0 1 2.6-1.9q2 .1 2.7 1.9h4.3v1.6h-4.3a3 3 0 0 1-1.7 1.7zm-19 4.1-1.8.5v4.6l1.8.5V302m1.8 0 1.8.5v4.6l-1.8.5zm32.5 0-1.8.5v4.6l1.8.5zm1.8 0 1.8.5v4.6l-1.8.5zm-27.3 1 1.7-.8 3.2 3.5-1 1.5zm-1.3 1-1 1.5 3 3.5 1.8-.7zm19.7-1.2-1.8-.7-3 3.5 1 1.5zm1.3 1 1 1.6-3 3.5-1.8-.7 3.8-4.3m-21.7 9.6.5-1.7h4.8l.6 1.7zm-7-18.1q.1-1 1.1-1.2 1.1 0 1.2 1.2c.1 1.2-.5 1.1-1.2 1.1q-1-.1-1.2-1.1zm12.8.8-.5 1.7h-4.8l-.5-1.7zm0-1.7-.5-1.7h-4.8l-.5 1.7zm16.7 19-.5-1.7h-4.8l-.5 1.7zm4.6-18.1q.2-1 1.2-1.2 1.1 0 1.2 1.2c.1 1.2-.5 1.1-1.2 1.1q-1-.1-1.2-1.1m-17.2 0q.2-1 1.2-1.2c1-.2 1.2.5 1.2 1.2s-.5 1.1-1.2 1.1q-1-.1-1.2-1.1m6.7.8.6 1.7h4.8l.5-1.7zm0-1.6.5-1.8h4.9l.5 1.7H287m-6.3 5.4-1.8.5v4.6l1.8.5zm1.7 0 1.8.5v4.6l-1.8.5z"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="M284.2 337.3a17 17 0 0 0 4-1l.8 1.4a19 19 0 0 1-4.6 1.3 3 3 0 0 1-2.8 2 3 3 0 0 1-2.7-2 19 19 0 0 1-4.8-1.3l.8-1.5q2 1 4.2 1.1a3 3 0 0 1 1.6-1.4v-7.2h1.8v7.2a3 3 0 0 1 1.7 1.4zm-5-21.7-.2-.5h-4.4v-1.6h4.4q0-.3.2-.4l-5.4-6 1.3-1 5.3 5.8.3-.1V304h1.7v7.7l.3.1 5.2-5.9 1.3 1-5.2 6 .3.6h4.2v1.6h-4.2l-.3.6 5 5.6-1.4 1-4.9-5.6-.2.1v9.3h-1.8v-9.3l-.2-.1-5 5.6-1.4-1 5.1-5.7m-13.7-17.7 6.5 7.2 1.3-1-6.5-7.3.3-.6h4.7v-1.7h-4.7a3 3 0 0 0-2.7-1.8 2.7 2.7 0 0 0-2.8 2.7q.1 1.8 1.9 2.5v5.5h1.7V298h.3zm7 37-.9 1.5a18 18 0 0 1-3.9-3.3q-1.4.4-2.6-.5a2.5 2.5 0 0 1-.3-3.7v-.2a16 16 0 0 1-1.3-5.1h1.8a14 14 0 0 0 1.1 4.4l1.5.1 4.4-4.8 1.3 1-4.3 4.9a3 3 0 0 1-.1 3 16 16 0 0 0 3.3 2.7zm-9-14v-4a3 3 0 0 1-2-2.6q.2-1.9 2-2.6v-5.6h1.7v5.7q1.4.4 1.8 1.7h4.7v1.6H267a3 3 0 0 1-1.8 1.7v4.1h-1.7m2.5 9a1.2 1.2 0 0 1 1.7-.2q.7.7 0 1.6a1.3 1.3 0 0 1-1.6.1 1 1 0 0 1-.1-1.6zm-2.2-4.9-1.8-.4-.3-4.5 1.8-.6v2.6q0 1.5.3 3zm1.5-5.6 1.7.4.3 4.6-1.8.6-.3-2.9v-2.7zm5.9 14.6a17 17 0 0 0 5.1 2.7l.4-1.7a15 15 0 0 1-4.3-2.2l-1.2 1.2m-1 1.5a19 19 0 0 0 5.3 2.7l-1.4 1.2a20 20 0 0 1-4.2-2.1l.4-1.8"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="m272.6 325.4 1.7.8 3.2-3.5-1-1.5-3.9 4.2m-1.3-1-1-1.5 3.1-3.5 1.7.7-3.8 4.3m-8.2-10.1q.2-1.1 1.2-1.2 1.1 0 1.2 1.2 0 1-1.2 1.1-1-.1-1.2-1.1zm27.5 20.6.8 1.5a18 18 0 0 0 4-3.3q1.3.4 2.6-.5a2.5 2.5 0 0 0 .3-3.7l-.2-.2q1.1-2.4 1.4-5.1h-1.7a14 14 0 0 1-1.2 4.4l-1.5.1-4.3-4.8-1.4 1 4.4 4.9a3 3 0 0 0 .1 3 16 16 0 0 1-3.3 2.7zm9-14v-4.1a3 3 0 0 0 2-2.5 3 3 0 0 0-2-2.6v-5.6h-1.8v5.7q-1.3.4-1.8 1.7h-4.7v1.6h4.8a3 3 0 0 0 1.7 1.7v4.1zm-2.5 9a1.2 1.2 0 0 0-1.7-.2 1 1 0 0 0-.1 1.6q.7.8 1.7.1a1 1 0 0 0 0-1.6zm2.2-4.8 1.8-.4.2-4.6-1.7-.6v2.6zm-1.5-5.7-1.8.4v2.3c0-.8-.3 2.3-.3 2.3l1.9.6.2-2.9v-2.7m1.8-21.5v5.5h-1.8V298l-.4-.2-6.4 7.2-1.3-1.1 6.5-7.4-.1-.3h-4.8v-1.7h4.8a3 3 0 0 1 2.6-1.8c1.6 0 2.8 1.2 2.8 2.7q-.1 2-2 2.5zm-17.2 0v3.4h-1.7v-3.4a3 3 0 0 1-1.8-1.7h-4.3v-1.7h4.3a3 3 0 0 1 2.6-1.8q2 .2 2.7 1.8h4.3v1.7h-4.3a3 3 0 0 1-1.7 1.7zm9.5 36a17 17 0 0 1-5.2 2.8l-.4-1.7a15 15 0 0 0 4.3-2.1l1.3 1m.9 1.6a19 19 0 0 1-5.2 2.7l1.3 1.3a20 20 0 0 0 4.3-2.2l-.4-1.8M263.5 302l-1.8.5v4.6l1.8.5V302m1.8 0 1.8.5v4.6l-1.8.5V302m32.5 0-1.8.5v4.6l1.8.5V302"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="m299.6 302 1.8.5v4.6l-1.8.5V302m-9.2 23.4-1.7.8-3.1-3.5 1-1.5 3.8 4.2m1.3-1 1-1.5-3-3.5-1.8.7 3.8 4.3m-19.4-21.5 1.7-.7 3.1 3.5-1 1.5-3.8-4.3M271 304l-1 1.5 3 3.5 1.8-.8-3.8-4.2m19.7-1.2-1.8-.7-3 3.5 1 1.5 3.8-4.3m1.3 1 1 1.5-3 3.5-1.8-.7 3.8-4.2m-21.7 9.6.5-1.7h4.8l.6 1.7h-5.9m0 1.6.5 1.7h4.8l.6-1.7h-5.9m-7-19.7q.1-1.1 1.1-1.2 1.1 0 1.2 1.2c.1 1.2-.5 1.1-1.2 1.1q-1-.1-1.2-1.1zm12.8.8-.5 1.7h-4.8l-.5-1.7h5.8m0-1.7-.5-1.7h-4.8l-.5 1.7h5.8m21.4 19.8q0-1 1.2-1.2c1.2-.2 1.2.5 1.2 1.2q0 1-1.2 1.1c-1.2.1-1.2-.5-1.2-1.1zm-4.7-.8-.5-1.7h-4.8l-.5 1.7h5.9m0 1.6-.6 1.7h-4.8l-.5-1.7h5.9m-12.4 23.3q0-1 1.2-1.1 1 .1 1.1 1.1 0 1.1-1.2 1.2c-1.2.1-1.2-.5-1.2-1.2zm2-8.2 1.8-.5V325l-1.8-.5v5.6m-1.8 0-1.8-.5V325l1.8-.5v5.6m16.8-34.8q.2-1.1 1.2-1.2 1.1 0 1.2 1.2c.1 1.2-.5 1.1-1.2 1.1q-1-.1-1.2-1.1zm-17.2 0q.2-1.1 1.2-1.2 1.1 0 1.2 1.2c.1 1.2-.5 1.1-1.2 1.1q-1-.1-1.2-1.1zm6.7.8.6 1.7h4.8l.5-1.7H287m0-1.7.6-1.7h4.8l.5 1.7H287m-6.3 5.4-1.8.5v4.5l1.8.5V300m1.7 0 1.8.5v4.5l-1.8.5V300"/>
<path fill="#058e6e" d="M278.8 314.3a2.7 2.7 0 0 1 2.8-2.6q2.6.2 2.8 2.6a2.7 2.7 0 0 1-2.8 2.7 2.7 2.7 0 0 1-2.8-2.7"/>
<path fill="#db4446" d="M282.3 245v-.6l.1-.4s-1.6.2-2.5 0a6 6 0 0 1-2.5-1.4q-1-1-1.7-1.2c-1.4-.2-2.4.4-2.4.4s1 .4 1.8 1.4a5 5 0 0 0 2 1.5c.6.2 2.7 0 3.3.1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M282.3 245v-.6l.1-.4s-1.6.2-2.5 0a6 6 0 0 1-2.5-1.4q-1-1-1.7-1.2c-1.4-.2-2.4.4-2.4.4s1 .4 1.8 1.4a5 5 0 0 0 2 1.5c.6.2 2.7 0 3.3.1z"/>
<path fill="#ed72aa" stroke="#000" stroke-width=".4" d="M290 242.7v1.4c.1.7-.2 1.3 0 1.7l.2 1c.2.2.3.9.3.9l-.8-.6-.6-.4v1l.6 1.3q.6.6 1 1.4c.3.4.2 1.4.2 1.4s-.4-.7-.9-.9l-1.3-.6s.9.8.9 1.6c0 .7-.4 1.6-.4 1.6s-.3-.7-.8-1.1l-1.2-1s.5 1.2.5 2v2.6s-.5-.7-1-1l-1-.8c0-.2.5.7.6 1.2s.3 2.4 2 4.8c1 1.4 2.4 3.8 5.6 3s2-5 1.3-7-1-4.3-1-5c.1-.8.7-3 .6-3.5a9 9 0 0 1 .1-3.4q.6-1.8 1-2.4l.5-1.4v-1.4l.8 1.5.1 1.5s.1-1.1 1-1.7 2-1.2 2.2-1.5l.4-.5s-.1 2-.7 2.7l-1.8 2.2s.8-.3 1.3-.3h.9s-.7.5-1.5 1.6-.5 1.3-1 2.3c-.7 1-1.1 1-1.9 1.6-1 1-.5 4.5-.3 5 .1.5 2 4.9 2 6 .1 1 .3 3.3-1.6 4.9-1.2 1-3.1 1-3.6 1.2-.4.3-1.3 1.2-1.3 3s.7 2.1 1.2 2.6 1.2.2 1.3.5l.5.8q.4.3.3.8c0 .4-1 1.3-1.2 2-.3.6-1 2.2-1 2.5s0 1 .3 1.4c0 0 .9 1 .2 1.3-.4.1-.8-.3-1-.2l-1 .4q-.4 0-.4-.8l-.1-.7q-.4 0-.4.5c0 .2 0 .9-.3.9s-.6-.5-.9-.6c-.2 0-.8-.2-.9-.5q.1-.6.8-1 .8-.1.5-.5-.4-.3-.7 0-.6.3-.8-.3c-.2-.6.1-.7 0-.8 0-.2-.4-.6.2-.9s.8.3 1.4.2q1-.1 1.1-.7.4-.6-.2-1.5c-.6-.9-.8-.5-1-.8l-.3-1v2.4l-.8-.9c-.3-.3-.6-1.4-.6-1.4v1.4c0 .4.4.7.2.9-.1.1-.8-.8-1-.9q-.5-.3-1-1l-.6-1.5c0-.2-.2-1.4 0-1.6l.4-1.2h-1.4c-.8 0-1.3-.2-1.6.3q-.4.8.2 3 .6 2 .5 2.2l-.8 1-1-.1q-.2-.2-1.2-.3h-1.4c-.3 0-1-.4-1.2-.3q-.7 0-.7.7.1.9-.5.7l-1-.2c-.3-.1-.9 0-.8-.4q0-.5.4-.8.4-.5 0-.5h-.7q-.4.5-.7.5-.5-.2-.5-1.1c0-.7-.7-1.3 0-1.3s1.4.5 1.5.2q.2-.5-.3-.8c-.3-.3-.7-.5-.3-.8l.8-.6c.2-.1.4-.8.7-.6.7.3 0 .7.7 1.4s1 1 2.1.9q1.4-.1 1.4-.6l-.2-1 .2-1s-.5.3-.6.6l-.5.8v-2.1l-.2-.9-.3 1-.1 1s-.7-.5-.5-1.5v-2.1c.3-.4.9-1.7 2.2-1.8h2.8l2.1-.3s-3-1.5-3.7-2-1.8-1.7-2.1-2.2l-.7-1.6q-.1 0-1 .3l-1.3 1-.7 1v-2s-.4 1.3-1 1.8l-1.5 1.2v-1l.2-1s-.5.8-1.2 1-1.9 0-2 .5v1.4q-.3.6-.5.6l-.8-.4c-.6-.2-.7.2-.7.2s-.4-.4-.2-.7c0-.3.7-.7.5-.9l-.9.2c-.3.2-.9.3-.8-.2q.2-.6 0-1-.1-.5.2-.6c.2-.1 1.2 0 1.3-.2q0-.4-.9-.7t-.6-.8l.6-.6c.1-.3.2-.8.7-.6.6.3.5.9 1 1 .6.3 2 0 2.3-.1l1.5-1 1.7-1.2-1.1-.8-1-1.1-2-.7c-.5 0-1.8-.5-1.8-.5l.8-.3q.4-.4.9-.6h.3-1.5c-.3-.2-1-.6-1.3-.6h-.9s.8-.4 1.4-.5l1.2-.1s-1-.3-1.3-.6l-.6-1q-.2-.5-.6-.6c-.4-.1-.9.4-1.2.3q-.4 0-.6-.7v-.5c-.2-.3-.7-.8-.2-1h1.4c.1-.2-.5-.7-.8-1-.4-.1-1-.4-.7-.7l.8-.6c.2-.3.4-1 .8-.7.3.2.9 1.3 1.1 1.2s.4-.9.3-1.2c0-.3 0-1 .3-.9s.5.5 1 .5 1.1-.1 1 .2q0 .6-.6 1.1a2 2 0 0 0-.2 1.6 4 4 0 0 0 1.2 1.5c.4.3 1.3.5 1.8.8s1.9 1.4 2.3 1.5l.9.3s.5-.2 1.1-.2 2.2.1 2.8-.1 1.3-.7 1.1-1.2-1.5-1-1.4-1.4q.2-.5 1.4-.5c.8 0 1.8.2 2-1 .2-1 .3-1.6-.8-1.9q-1.8-.1-2.1-1t-.2-1.3 1.5-.3 2-.3l.6-1 1.5-.3s1.5.7 3 1.7c1.2 1 2.3 2.3 2.3 2.3"/>
<path d="m279.3 242-.2-.7-.1-.3s.9 0 .9.2q-.1.4-.4.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m279.3 242-.2-.7-.1-.3s.9 0 .9.2q-.1.4-.4.4z"/>
<path d="M283.4 240.5v-.5s.7 0 1.1.3c.6.4 1 1 1 1-.2.2-.6-.2-1-.3h-.4q-.3 0-.4-.2v-.3z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M283.4 240.5v-.5s.7 0 1.1.3c.6.4 1 1 1 1-.2.2-.6-.2-1-.3h-.4q-.3 0-.4-.2v-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m289 246.7-.4-.7-.2-.5"/>
<path fill="#db4446" d="M267.9 241.8s.5.3.8.3l.9.1s.2-.5.1-1c-.2-1.2-1.3-1.4-1.3-1.4s.3.7.1 1q-.4 1-.6 1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M267.9 241.8s.5.3.8.3l.9.1s.2-.5.1-1c-.2-1.2-1.3-1.4-1.3-1.4s.3.7.1 1q-.4 1-.6 1z"/>
<path fill="#db4446" d="M265.5 242.8s-.4-.8-1.3-.7-1.6.9-1.6.9h1.3c.4.3.5 1 .5 1l.7-.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M265.5 242.8s-.4-.8-1.3-.7-1.6.9-1.6.9h1.3c.4.3.5 1 .5 1l.7-.6z"/>
<path fill="#db4446" d="M264.4 246s-.8.1-1.2.6c-.5.5-.4 1.5-.4 1.5s.5-.6 1-.6l1.2.2-.3-.9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M264.4 246s-.8.1-1.2.6c-.5.5-.4 1.5-.4 1.5s.5-.6 1-.6l1.2.2-.3-.9z"/>
<path d="m279.3 245.9.4-.5.3.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m279.3 245.9.4-.5.3.5h-.7"/>
<path d="m280.2 245.9.4-.6.4.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m280.2 245.9.4-.6.4.5h-.8"/>
<path d="m279.8 242.5.8.3-.7.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m279.8 242.5.8.3-.7.4-.1-.7"/>
<path d="m280.8 242.8.8.2-.6.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m280.8 242.8.8.2-.6.4-.2-.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M275.2 249.2s-.9.3-1.2.7q-.4 1-.3 1.1c.1.1.7-.6 1.6-.3l1.3.3c.4 0 1.3-.4 1.3-.4s-.7.9-.6 1.5l.1 1.2c0 .7-.5 1.6-.5 1.6l1-.3a5 5 0 0 0 1.8-.9l1-1s-.2 1 0 1.5l.2 1.7s.4-.5.8-.7c.2 0 .8-.4 1-.7l.3-1s0 .8.4 1.3c.3.4.7 1.8.7 1.8s.3-.9.6-1.3q.6-.6.7-1v-1l.4 1m-11.7.6s.5-.9 1-1.2l1.2-.8 1-.4m1 5.3 1.4-.8 1.2-1.2"/>
<path fill="#db4446" d="M267 256.4s-.4-.5-1.2-.3c-.7 0-1.2 1-1.2 1l1-.2.6.5.5-.4z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M267 256.4s-.4-.5-1.2-.3c-.7 0-1.2 1-1.2 1l1-.2.6.5.5-.4z"/>
<path fill="#db4446" d="M266.2 259.4s-.7 0-1.2.4-.6 1.2-.6 1.2.5-.4 1-.3l.8.2.1-.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M266.2 259.4s-.7 0-1.2.4-.6 1.2-.6 1.2.5-.4 1-.3l.8.2.1-.6z"/>
<path fill="#db4446" d="M267.6 262.2s0 .8.4 1.3 1.2.6 1.2.6l-.3-.8c-.1-.4.3-.7.3-.7s-.4-.4-.7-.4z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M267.6 262.2s0 .8.4 1.3 1.2.6 1.2.6l-.3-.8c-.1-.4.3-.7.3-.7s-.4-.4-.7-.4zm17.1 1.4s2.1 1.3 2 2.4c0 1-1.1 2.4-1.1 2.4"/>
<path fill="#db4446" d="M275.2 269.4s-.5-.6-1.3-.6c-.7 0-1.5.7-1.5.7s1 0 1.2.2l.4.7.6-.3z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M275.2 269.4s-.5-.6-1.3-.6c-.7 0-1.5.7-1.5.7s1 0 1.2.2l.4.7.6-.3z"/>
<path fill="#db4446" d="M273 272.3s-1-.1-1.5.4-.4 1.4-.4 1.4.6-.7 1-.6q1 .1 1.2.3l-.2-.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M273 272.3s-1-.1-1.5.4-.4 1.4-.4 1.4.6-.7 1-.6q1 .1 1.2.3l-.2-.8z"/>
<path fill="#db4446" d="M275 275.4s-.5.6-.1 1.1c.3.6 1 .8 1 .8s-.2-.4-.1-.8.7-.8.7-.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M275 275.4s-.5.6-.1 1.1c.3.6 1 .8 1 .8s-.2-.4-.1-.8.7-.8.7-.8z"/>
<path fill="#db4446" d="M287.7 276.6s-.8-.2-1.3 0-.8 1.5-.8 1.5.7-.6 1.3-.6l1 .3v-.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M287.7 276.6s-.8-.2-1.3 0-.8 1.5-.8 1.5.7-.6 1.3-.6l1 .3v-.8z"/>
<path fill="#db4446" d="M288.1 279.7s-.6.6-.4 1.2l.6 1s0-.7.3-1 1-.3 1-.3l-.7-.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M288.1 279.7s-.6.6-.4 1.2l.6 1s0-.7.3-1 1-.3 1-.3l-.7-.6z"/>
<path fill="#db4446" d="M291.3 280.6s-.3.8.3 1.3q1 .8 1.1.6c.1-.2-.5-.8-.3-1.3q.3-.6.5-.7l-.8-.2z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M291.3 280.6s-.3.8.3 1.3q1 .8 1.1.6c.1-.2-.5-.8-.3-1.3q.3-.6.5-.7l-.8-.2z"/>
<path fill="#ffd691" stroke="#000" stroke-width=".5" d="M258.7 337.5q3.2 1.1 3.2 4c0 2.5-2.3 4.3-5.4 4.3-3 0-5.6-1.8-5.6-4.2 0-1.9 1-4 3.2-4l-.3-.6-.6-.7h1.3l.7.5.6-.7.7-.6.6.7.3.6s.4-.4.8-.5l.9-.3-.3.7-.1.8"/>
<path fill="#058e6e" stroke="#000" stroke-width=".5" d="M256 348.5s-4-2.8-5.8-3.1c-2.3-.5-4.8-.1-5.9-.2l1.9 1.5a11 11 0 0 0 3.5 2c3.3.8 6.3-.2 6.3-.2m1.2.2s2.6-2.7 5.3-3c3.3-.5 5.4.2 6.6.5 0 0-1 .5-1.6 1-.5.3-2 1.5-4.2 1.6-2.2 0-4.7-.3-5.1-.2l-1 .1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".5" d="M256.4 345.4a5.2 5.2 0 0 1 0-7.6 5 5 0 0 1 1.7 3.8 5 5 0 0 1-1.7 3.8"/>
<path fill="#058e6e" stroke="#000" stroke-width=".5" d="M255.4 351s.6-1.6.7-3l-.2-2.2h.8s.4 1.2.4 2.2l-.2 2.5-.7.1-.8.3"/>
<path fill="#fff" d="M307 203.4q0-1 1-1 1.1 0 1.2 1c.1 1-.5 1.1-1.2 1.1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M307 203.4q0-1 1-1 1.1 0 1.2 1c.1 1-.5 1.1-1.2 1.1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M308.4 200.7q.2-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M308.4 200.7q.2-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M309.5 197.6q.1-1 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M309.5 197.6q.1-1 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M309.6 194.2q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1-1.1 0-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M309.6 194.2q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1-1.1 0-1.2-1z"/>
<path fill="#fff" d="M308.8 190.9q.1-1 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M308.8 190.9q.1-1 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M307 187.8q0-.9 1.2-1a1 1 0 0 1 1 1q0 1-1 1.1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M307 187.8q0-.9 1.2-1a1 1 0 0 1 1 1q0 1-1 1.1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M304.7 185.4q0-1 1.2-1a1 1 0 0 1 1 1q0 .9-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M304.7 185.4q0-1 1.2-1a1 1 0 0 1 1 1q0 .9-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M302 183.3q.2-1 1.2-1 1.3 0 1.1 1c-.2 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M302 183.3q.2-1 1.2-1 1.3 0 1.1 1c-.2 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M298.9 181.6q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M298.9 181.6q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M295.5 180.4q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M295.5 180.4q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M291.8 179.8q.1-1 1.1-1.1 1.1.1 1.2 1t-1.2 1.1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M291.8 179.8q.1-1 1.1-1.1 1.1.1 1.2 1t-1.2 1.1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M288.3 179.6q.1-1 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M288.3 179.6q.1-1 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M284.9 179.7q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M284.9 179.7q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M281.4 179.7q.1-1 1.1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M281.4 179.7q.1-1 1.1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M283.1 182.7q.1-1 1.1-1.1 1.1.1 1.2 1-.1 1-1.1 1.1a1 1 0 0 1-1.2-1m.7 3.2q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1m.2 3.3q.1-1 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1m-1 3q0-1 1-1.1a1 1 0 0 1 1.2 1q-.1 1-1.1 1.1a1 1 0 0 1-1.2-1m-1.9 2.7q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="#fff" d="M278.8 177.6q.1-.9 1.1-1t1.1 1c.1 1-.5 1.1-1 1.1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M278.8 177.6q.1-.9 1.1-1t1.1 1c.1 1-.5 1.1-1 1.1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M275.7 176q.1-1 1.1-1.1 1.1 0 1.2 1c.1 1-.5 1.1-1.1 1.1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M275.7 176q.1-1 1.1-1.1 1.1 0 1.2 1c.1 1-.5 1.1-1.1 1.1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M272.3 175q0-1 1.2-1.1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M272.3 175q0-1 1.2-1.1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M268.8 174.3q.1-1 1.1-1 1.1 0 1.2 1c.1 1-.5 1-1.2 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M268.8 174.3q.1-1 1.1-1 1.1 0 1.2 1c.1 1-.5 1-1.2 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M265.4 174.4q.1-1 1.1-1t1.1 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M265.4 174.4q.1-1 1.1-1t1.1 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M261.8 175q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M261.8 175q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M258.5 176.1q.1-1 1.1-1t1.1 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M258.5 176.1q.1-1 1.1-1t1.1 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M202.3 203.4q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1.1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M202.3 203.4q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1.1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M200.8 200.7q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M200.8 200.7q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M199.7 197.6q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M199.7 197.6q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M199.6 194.2q.1-1 1.1-1 1.1 0 1.2 1c.1 1-.5 1-1.2 1q-1 0-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M199.6 194.2q.1-1 1.1-1 1.1 0 1.2 1c.1 1-.5 1-1.2 1q-1 0-1-1z"/>
<path fill="#fff" d="M200.4 190.9q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M200.4 190.9q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M202.2 187.8q.1-.9 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M202.2 187.8q.1-.9 1-1a1 1 0 0 1 1.2 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M204.5 185.4q.1-1 1.1-1t1.1 1q-.1.9-1.1 1t-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M204.5 185.4q.1-1 1.1-1t1.1 1q-.1.9-1.1 1t-1.1-1z"/>
<path fill="#fff" d="M207.2 183.3q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M207.2 183.3q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M210.3 181.6q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M210.3 181.6q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M213.7 180.4q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M213.7 180.4q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M217.4 179.8q.1-1 1.1-1.1a1 1 0 0 1 1.1 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M217.4 179.8q.1-1 1.1-1.1a1 1 0 0 1 1.1 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M220.9 179.6q.1-1 1.1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M220.9 179.6q.1-1 1.1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M224.3 179.7q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M224.3 179.7q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M227.8 179.7q.1-1 1.1-1t1.2 1c.2 1-.5 1-1.2 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M227.8 179.7q.1-1 1.1-1t1.2 1c.2 1-.5 1-1.2 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M226 182.7q.1-1 1.2-1.1c1.1-.1 1.1.5 1.1 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1m-.7 3.2q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1q-1 0-1.1-1m-.2 3.3q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1m1 3q.2-1 1.2-1.1a1 1 0 0 1 1.1 1q-.1 1-1.1 1.1a1 1 0 0 1-1.1-1m1.9 2.7q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="#fff" d="M230.4 177.6q.1-.9 1.1-1 1.1 0 1.2 1c.1 1-.5 1.1-1.2 1.1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M230.4 177.6q.1-.9 1.1-1 1.1 0 1.2 1c.1 1-.5 1.1-1.2 1.1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M233.5 176q.1-1 1.1-1.1 1 0 1.1 1c.1 1-.5 1.1-1 1.1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M233.5 176q.1-1 1.1-1.1 1 0 1.1 1c.1 1-.5 1.1-1 1.1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M236.9 175q.1-1 1-1.1 1.3 0 1.2 1c-.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M236.9 175q.1-1 1-1.1 1.3 0 1.2 1c-.1 1-.5 1-1.1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M240.4 174.3q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M240.4 174.3q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M243.8 174.4q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M243.8 174.4q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#fff" d="M247.4 175q.1-1 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M247.4 175q.1-1 1.1-1a1 1 0 0 1 1.2 1q0 1-1.2 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M250.7 176.1q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M250.7 176.1q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M222.3 244.1h-1v-1h-1.6v3.9h1.6v2.6h-3.5v7.5h1.9v15.2h-3.8v7.8h29v-7.8h-3.7v-15.2h1.9v-7.5h-3.6V247h1.7v-3.8h-1.7v1h-1v-1H237v1h-1.2v-1h-1.6v3.8h1.6v2.6h-3.5v-8.3h1.9v-3.8h-1.9v1h-1v-1h-1.6v1h-1v-1h-1.9v3.8h2v8.3H225V247h1.7v-3.8H225v1h-1v-1h-1.8v1zm-6.4 36h29m-29-1.9h29m-29-1.9h29m-29-1.9h29m-29-2.1h29m-25.2-1.7h21.5m-21.5-1.9h21.5m-21.5-2h21.5m-21.5-2h21.5m-21.5-1.9h21.5m-21.5-1.9h21.5m-21.5-1.9h21.5m-23.4-1.9H243m-25.3-1.8H243m-25.3-2H243m-25.3-1.8H243m-21.8-2h18.2m-10.9-1.8h3.6m-3.5-2h3.5m-3.5-1.8h3.5m-3.5-1.9h3.5m-5.4-2.4h7.3m-12.8 8h3.8m-5.4-2.3h7m-7 34.8v-1.9m0-1.9v-1.9m-2 2v1.8m3.6 0v-1.9m2 3.8v-1.9m0-1.9v-1.9m0-2.1v-1.7m0-1.9v-2m-2 7.7v-2.1m-3.5 2.1v-2.1m7.3 0v2.1m1.7-2.1v-1.7m-5.5-1.9v2m3.8-2v2m3.5-2v2m-1.8-2v-2m1.8-2v2m0-5.8v2m-1.8-3.9v2m1.8-3.8v1.8m-3.5-1.9v2m-3.8-2v2m-1.6-3.8v1.9m3.5-2v2m3.6-2v2m1.9-3.8v1.9m-3.6-2v2m-3.8-2v2m-1.6-3.8v1.9m7-2v2m-3.5-5.7v1.9m16.3-2h-3.7m5.4-2.3H234m7 34.8v-1.9m0-1.9v-1.9m2 2v1.8m-3.6 0v-1.9m-1.9 3.8v-1.9m0-1.9v-1.9m0-2.1v-1.7m0-1.9v-2m2 7.7v-2.1m3.5 2.1v-2.1m-7.3 0v2.1m-1.7-2.1v-1.7m5.4-1.9v2m-3.7-2v2m-3.6-2v2m1.9-2v-2m-1.9-2v2m0-5.8v2m1.9-3.9v2m-1.9-3.8v1.8m3.6-1.9v2m3.8-2v2m1.6-3.8v1.9m-3.5-2v2m-3.6-2v2m-1.9-3.8v1.9m3.6-2v2m3.7-2v2m1.7-3.8v1.9m-7.1-2v2m3.6-5.7v1.9m-7.4 19.1v-2m0-5.8V259m0 5.7v-1.9m0-5.7v-1.8m0-2v-1.8m0-3.8v-2m0-1.8V242m-9 5h3.8m3.6-5.7h3.5m3.6 5.7h3.7"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M235.3 280.1v-5c0-1-.5-3.8-5-3.8-4.3 0-4.7 2.9-4.7 3.8v5z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="m227.2 275.4-2.3-.3c0-1 .2-2.3 1-2.8l2 1.7c-.2.2-.7.9-.7 1.4zm6.4 0 2.4-.3c0-1-.3-2.3-1-2.8l-2 1.7c.1.2.6.9.6 1.4zm-2.4-2.4 1.2-2.1-2-.5q-1 0-2 .5l1.2 2.1zm-4.4-6v-5c0-1.4-1-2.6-2.6-2.6s-2.6 1.2-2.6 2.6v5.2h5.2zm7.3 0v-5c0-1.4 1-2.6 2.6-2.6s2.6 1.2 2.6 2.6v5.2h-5.2zm-1.9-12.7.5-4.7h-4.5l.2 4.7zm3.5 0-.4-4.7h4.7l-.5 4.7zm-10.6 0 .2-4.7h-4.5l.5 4.7z"/>
<path fill="#0039f0" d="M233.6 280.1v-4.3c0-.7-.4-2.8-3.3-2.8a3 3 0 0 0-3 2.8v4.3zm-7.3-13.5v-4.5c0-1.2-.7-2.4-2.1-2.4s-2.2 1.2-2.2 2.4v4.5zm8.3 0v-4.5c0-1.2.7-2.4 2.1-2.4s2.1 1.2 2.1 2.4v4.5z"/>
<path fill="#ad1519" d="M239.5 287.8c0-10.4 7.5-18.8 16.6-18.8s16.7 8.4 16.7 18.7c0 10.4-7.5 18.8-16.7 18.8s-16.6-8.4-16.6-18.7"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M239.5 287.8c0-10.4 7.5-18.8 16.6-18.8s16.7 8.4 16.7 18.7c0 10.4-7.5 18.8-16.7 18.8s-16.6-8.4-16.6-18.7z"/>
<path fill="#005bbf" d="M244.4 287.7c0-7.6 5.3-13.7 11.7-13.7 6.5 0 11.8 6.1 11.8 13.7s-5.3 13.7-11.7 13.7c-6.5 0-11.8-6.1-11.8-13.7"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M244.4 287.7c0-7.6 5.3-13.7 11.7-13.7 6.5 0 11.8 6.1 11.8 13.7s-5.3 13.7-11.7 13.7c-6.5 0-11.8-6.1-11.8-13.7z"/>
<path fill="#c8b100" d="M250.6 278.3s-1.4 1.5-1.4 2.9.6 2.6.6 2.6a2 2 0 0 0-1.4-1 1.5 1.5 0 0 0-1.6 1.5l.3.8.5 1q.3-.6 1-.6 1 .1 1 1v.3h-1.2v1h1.1l-.8 1.7 1-.4 1 1 .8-1 1 .4-.7-1.6h1v-1.1h-1.2v-.3q.1-.9 1-1 1.2 0 1 .6l.6-1 .2-.8q0-1.4-1.5-1.5-1 0-1.4 1s.5-1.2.5-2.6-1.4-3-1.4-3"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M250.6 278.3s-1.4 1.5-1.4 2.9.6 2.6.6 2.6a2 2 0 0 0-1.4-1 1.5 1.5 0 0 0-1.6 1.5l.3.8.5 1q.3-.6 1-.6 1 .1 1 1v.3h-1.2v1h1.1l-.8 1.7 1-.4 1 1 .8-1 1 .4-.7-1.6h1v-1.1h-1.2v-.3q.1-.9 1-1 1.2 0 1 .6l.6-1 .2-.8q0-1.4-1.5-1.5-1 0-1.4 1s.5-1.2.5-2.6-1.4-3-1.4-3z"/>
<path fill="#c8b100" d="M248.4 287.9h4.5v-1.1h-4.5v1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M248.4 287.9h4.5v-1.1h-4.5v1z"/>
<path fill="#c8b100" d="M261.5 278.3s-1.4 1.5-1.4 2.9.6 2.6.6 2.6q-.4-1-1.4-1-1.5.2-1.6 1.5l.3.8.5 1q.2-.6 1-.6a1 1 0 0 1 1 1v.3h-1.2v1h1.1l-.8 1.7 1-.4.9 1 .9-1 1 .4-.8-1.6h1.1v-1.1h-1.2v-.3q.1-.9 1-1c.9-.1.9.2 1 .6l.6-1 .2-.8q-.1-1.4-1.5-1.5-1.1 0-1.5 1s.6-1.2.6-2.6-1.4-3-1.4-3"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M261.5 278.3s-1.4 1.5-1.4 2.9.6 2.6.6 2.6q-.4-1-1.4-1-1.5.2-1.6 1.5l.3.8.5 1q.2-.6 1-.6a1 1 0 0 1 1 1v.3h-1.2v1h1.1l-.8 1.7 1-.4.9 1 .9-1 1 .4-.8-1.6h1.1v-1.1h-1.2v-.3q.1-.9 1-1c.9-.1.9.2 1 .6l.6-1 .2-.8q-.1-1.4-1.5-1.5-1.1 0-1.5 1s.6-1.2.6-2.6-1.4-3-1.4-3z"/>
<path fill="#c8b100" d="M259.3 287.9h4.5v-1.1h-4.5v1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M259.3 287.9h4.5v-1.1h-4.5v1z"/>
<path fill="#c8b100" d="M256 287.6s-1.3 1.5-1.3 3c0 1.4.6 2.6.6 2.6a2 2 0 0 0-1.5-1 1.5 1.5 0 0 0-1.5 1.4l.2.8.5 1q.3-.5 1-.5 1 0 1.1 1v.3h-1.2v1h1l-.8 1.7 1.1-.5.9 1 .8-1 1.1.5-.8-1.7h1.1v-1h-1.2v-.3q0-1 1-1 .8 0 1 .5l.5-1 .3-.8q-.1-1.3-1.6-1.4a2 2 0 0 0-1.4 1s.6-1.2.6-2.7c0-1.4-1.4-2.9-1.4-2.9"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M256 287.6s-1.3 1.5-1.3 3c0 1.4.6 2.6.6 2.6a2 2 0 0 0-1.5-1 1.5 1.5 0 0 0-1.5 1.4l.2.8.5 1q.3-.5 1-.5 1 0 1.1 1v.3h-1.2v1h1l-.8 1.7 1.1-.5.9 1 .8-1 1.1.5-.8-1.7h1.1v-1h-1.2v-.3q0-1 1-1 .8 0 1 .5l.5-1 .3-.8q-.1-1.3-1.6-1.4a2 2 0 0 0-1.4 1s.6-1.2.6-2.7c0-1.4-1.4-2.9-1.4-2.9z"/>
<path fill="#c8b100" d="M253.8 297.2h4.5v-1h-4.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M253.8 297.2h4.5v-1h-4.5z"/>
<path fill="#c8b100" d="M289.4 238.3h-.3l-.3.4q-.5.4-.9 0l-.1-.4h-.5q-.5-.2-.2-.8l.1-.2v-.3h-.3l-.1.3q-.5.4-.8.1l-.1-.2h-.2c-.5.2-.7-1-.8-1.3l-.1.3v1.3l-.3 1.2q1.4.3 3 1.7a10 10 0 0 1 2.5 2.5l1.2-.6c.6-.2 1.4-.2 1.4-.2l.2-.2c-.3 0-1.6.1-1.6-.4l.1-.2h-.3q-.3-.3 0-.8h.2v-.4h-.3l-.2.1q-.5.5-.9 0v-.4h-.6a1 1 0 0 1 0-.9l.2-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M289.4 238.3h-.3l-.3.4q-.5.4-.9 0l-.1-.4h-.5q-.5-.2-.2-.8l.1-.2v-.3h-.3l-.1.3q-.5.4-.8.1l-.1-.2h-.2c-.5.2-.7-1-.8-1.3l-.1.3v1.3l-.3 1.2q1.4.3 3 1.7a10 10 0 0 1 2.5 2.5l1.2-.6c.6-.2 1.4-.2 1.4-.2l.2-.2c-.3 0-1.6.1-1.6-.4l.1-.2h-.3q-.3-.3 0-.8h.2v-.4h-.3l-.2.1q-.5.5-.9 0v-.4h-.6a1 1 0 0 1 0-.9l.2-.3z"/>
<path d="M287 239h.3s.1.2 0 .2h-.2v-.2"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M287 239h.3s.1.2 0 .2h-.2v-.2z"/>
<path d="m288 239.8-.3-.2v-.2h.2l.3.3.3.2s.1.1 0 .2h-.1z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m288 239.8-.3-.2v-.2h.2l.3.3.3.2s.1.1 0 .2h-.1l-.4-.3"/>
<path d="m286.3 238.6-.3-.2s-.1 0 0-.1h.1l.3.1.2.2.1.1h-.2z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m286.3 238.6-.3-.2s-.1 0 0-.1h.1l.3.1.2.2.1.1h-.2l-.2-.1"/>
<path d="M285.2 237.9h.2l.1.2h-.2z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M285.2 237.9h.2l.1.2h-.2z"/>
<path d="M289.1 240.6v-.3h-.3v.3z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M289.1 240.6v-.3h-.3v.3z"/>
<path d="m289.7 241.2.2.2q.1.1.2 0l-.2-.3-.2-.2h-.2v.1z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m289.7 241.2.2.2q.1.1.2 0l-.2-.3-.2-.2h-.2v.1l.2.2"/>
<path d="M290.7 242v-.2h-.3v.3h.3"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M290.7 242v-.2h-.3v.3h.3z"/>
<path fill="#c8b100" d="M287.9 235.9h-.6l-.2.9.1.1h.2l.7-.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M287.9 235.9h-.6l-.2.9.1.1h.2l.7-.5-.2-.5"/>
<path fill="#c8b100" d="M286.2 236.4v.5l1 .2v-.3l-.5-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M286.2 236.4v.5l1 .2v-.3l-.5-.7-.5.3"/>
<path fill="#c8b100" d="m288.2 237.5-.5.3-.6-.8v-.1h1.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m288.2 237.5-.5.3-.6-.8v-.1h1.1v.6"/>
<path fill="#c8b100" d="m287 236.8.3-.1.1.4h-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m287 236.8.3-.1.1.4h-.3z"/>
<path fill="#c8b100" d="m284.8 235.9-.3-.8-.4-.4s.4-.2.9.1 0 .9 0 .9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m284.8 235.9-.3-.8-.4-.4s.4-.2.9.1 0 .9 0 .9z"/>
<path fill="#c8b100" d="m285.8 236.2-.4.4-.7-.6v-.3h1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m285.8 236.2-.4.4-.7-.6v-.3h1l.1.5"/>
<path fill="#c8b100" d="m284.6 235.8.3-.2q.2 0 .1.3 0 .3-.3.3v-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m284.6 235.8.3-.2q.2 0 .1.3 0 .3-.3.3v-.4z"/>
<path fill="#c8b100" d="M290.2 237.3h-.6l-.3.8v.2h.2l.9-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M290.2 237.3h-.6l-.3.8v.2h.2l.9-.4-.2-.6"/>
<path fill="#c8b100" d="m288.5 237.6-.1.6.9.2h.1v-.2l-.4-.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m288.5 237.6-.1.6.9.2h.1v-.2l-.4-.8-.5.2"/>
<path fill="#c8b100" d="m290.3 239-.6.2-.4-.8v-.2h.1l1 .2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m290.3 239-.6.2-.4-.8v-.2h.1l1 .2-.1.6"/>
<path fill="#c8b100" d="M289.1 238.1q.2-.2.4 0l.1.4h-.4v-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M289.1 238.1q.2-.2.4 0l.1.4h-.4v-.4z"/>
<path fill="#c8b100" d="m292.2 239.2.1.6-.9.3h-.1v-.2l.3-.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m292.2 239.2.1.6-.9.3h-.1v-.2l.3-.8.6.1"/>
<path fill="#c8b100" d="m292 240.8-.5.2-.3-.9v-.1h.2l.8.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m292 240.8-.5.2-.3-.9v-.1h.2l.8.3-.1.5"/>
<path fill="#c8b100" d="m290.5 239.3-.2.5.9.3h.1v-.1l-.2-.9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m290.5 239.3-.2.5.9.3h.1v-.1l-.2-.9-.6.2"/>
<path fill="#c8b100" d="M291.5 240.3v-.4a.3.3 0 0 0-.5 0v.3h.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M291.5 240.3v-.4a.3.3 0 0 0-.5 0v.3h.4z"/>
<path fill="#c8b100" d="m292.9 242.1.8.1.5.3s.1-.5-.3-.8-.9.2-.9.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m292.9 242.1.8.1.5.3s.1-.5-.3-.8-.9.2-.9.2z"/>
<path fill="#c8b100" d="m292.3 241.2-.3.5.8.5v-.1h.2l-.1-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m292.3 241.2-.3.5.8.5v-.1h.2l-.1-1h-.6"/>
<path fill="#c8b100" d="M293 242.2s.2-.2.1-.3h-.4q-.2 0-.1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M293 242.2s.2-.2.1-.3h-.4q-.2 0-.1.3zm40.7-23.4v.6H331v-.6h1v-1.4h-.7v-.6h.6v-.5h.7v.5h.6v.6h-.6v1.4h1"/>
<path fill="none" d="M179.3 231.6v-1.3m-.3 1.3v-1.3m-.3 1.3v-1.3m-.4 1.3v-1.3"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M178 231.6v-1.3m-.5 1.2v-1.2m.2 1.2v-1.2m-.7 1.2v-1.1m.2 1v-1m-.9 1v-1m.2 1v-1m.2 1v-1m-.7 1v-1m-.2.9v-.8m-.2.8v-.8m-.6.8v-.7m.3.7v-.7m-.5.6v-.6m-.2.5v-.4m-.3.3v-.3m-.3.3v-.2"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M173.8 231v-.1"/>
<path fill="none" d="M180.8 231.5v-1.2m-.7 1.2v-1.2m-.4 1.3v-1.3m152.6 1.3v-1.3m-.3 1.3v-1.3m-.4 1.3v-1.3m-.4 1.3v-1.3"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M331 231.6v-1.3m-.6 1.2v-1.1m.3 1.1v-1.2m-.8 1.2v-1.1m.2 1.1v-1.2m-.9 1.1v-1m.2 1v-1m.3 1v-1m-.7 1v-1m-.3 1v-.9m-.2.8v-.8m-.5.8v-.7m.2.7v-.7m-.5.6v-.5m-.2.5v-.5m-.3.4v-.4m-.2.3v-.2"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M326.7 231v-.1"/>
<path fill="none" d="M333.7 231.5v-1.2m-.6 1.3v-1.3m-.5 1.3v-1.3"/>
</svg>

After

Width:  |  Height:  |  Size: 80 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-fi" viewBox="0 0 512 512">
<path fill="#fff" d="M0 0h512v512H0z"/>
<path fill="#002f6c" d="M0 186.2h512v139.6H0z"/>
<path fill="#002f6c" d="M123.2 0h139.6v512H123.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 237 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-fr" viewBox="0 0 512 512">
<path fill="#fff" d="M0 0h512v512H0z"/>
<path fill="#000091" d="M0 0h170.7v512H0z"/>
<path fill="#e1000f" d="M341.3 0H512v512H341.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 231 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 512 512">
<path fill="#012169" d="M0 0h512v512H0z"/>
<path fill="#FFF" d="M512 0v64L322 256l190 187v69h-67L254 324 68 512H0v-68l186-187L0 74V0h62l192 188L440 0z"/>
<path fill="#C8102E" d="m184 324 11 34L42 512H0v-3zm124-12 54 8 150 147v45zM512 0 320 196l-4-44L466 0zM0 1l193 189-59-8L0 49z"/>
<path fill="#FFF" d="M176 0v512h160V0zM0 176v160h512V176z"/>
<path fill="#C8102E" d="M0 208v96h512v-96zM208 0v512h96V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 505 B

+16
View File
@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gr" viewBox="0 0 512 512">
<path fill="#0d5eaf" fill-rule="evenodd" d="M0 0h512v57H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 57h512v57H0z"/>
<path fill="#0d5eaf" fill-rule="evenodd" d="M0 114h512v57H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 171h512v57H0z"/>
<path fill="#0d5eaf" fill-rule="evenodd" d="M0 228h512v56.9H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 284.9h512v57H0z"/>
<path fill="#0d5eaf" fill-rule="evenodd" d="M0 341.9h512v57H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 398.9h512v57H0z"/>
<path fill="#0d5eaf" d="M0 0h284.9v284.9H0z"/>
<g fill="#fff" fill-rule="evenodd" stroke-width="1.3">
<path d="M114 0h57v284.9h-57z"/>
<path d="M0 114h284.9v57H0z"/>
</g>
<path fill="#0d5eaf" fill-rule="evenodd" d="M0 455h512v57H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 861 B

+56
View File
@@ -0,0 +1,56 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-hr" viewBox="0 0 512 512">
<path fill="#171796" d="M0 0h512v512H0z"/>
<path fill="#fff" d="M0 0h512v341.3H0z"/>
<path fill="red" d="M0 0h512v170.7H0z"/>
<path fill="red" d="M256 388.4a100 100 0 0 0 99.6-99.5V170.6H156.5V289a100 100 0 0 0 99.5 99.5z"/>
<path fill="#fff" d="M256 386.8a98 98 0 0 0 97.7-97.6V172.5H158.4v116.7a98 98 0 0 0 97.6 97.6"/>
<path fill="red" d="M199.6 176.2H162v41.3h37.6zm0 82.6h37.6v-41.3h-37.6zM162 289q0 5.6.7 11h36.9v-41.2H162zm112.8-30.2h-37.6V300h37.6zm0 82.5h37.5V300h-37.6zm37.5 22.6a95 95 0 0 0 21.5-22.6h-21.5V364zm-112.7-22.6h37.6V300h-37.6zm-21.5 0a95 95 0 0 0 21.5 22.6v-22.6zm84.4 41.3a93 93 0 0 0 12.2-1.7v-39.6h-37.5V381a93 93 0 0 0 12.2 1.7q6.6.4 13.1 0zm86.7-82.6q.6-5.4.7-11v-30.2h-37.6V300zm-74.4-41.2h37.5v-41.3h-37.6zm0-82.6h-37.6v41.3h37.6zm75 41.3v-41.3h-37.5v41.3z"/>
<path fill="#fff" d="m352 169.4 23.3-52.8L357.5 88l-29.4 11-20.6-23.7-27.2 15.6L256 71l-24.3 19.8-27.2-15.6L184 98.8 154.5 88l-17.8 28.7 23.3 52.8a232 232 0 0 1 96-20.6c34.2 0 66.7 7.4 96 20.6z"/>
<path fill="#0093dd" d="m184.5 101.2-29.2-10.8-16.3 26.4 6.3 14.2 15.8 36a234 234 0 0 1 36.9-12.8z"/>
<path fill="#fff" stroke="#000" stroke-width=".3" d="M182.9 127.2a14 14 0 0 1 1.6 6.6c0 7.9-6.4 14.3-14.3 14.3a14 14 0 0 1-14-11.5 14 14 0 0 0 12.5 7.3 14.3 14.3 0 0 0 14.2-16.7z"/>
<path d="M169.8 107.5v.2l-1 5.2 3.9 3.4.2.1-.3.1-4.9 1.7-1 5.1v.3l-.2-.2-4-3.4-4.9 1.7h-.2v-.2l1-5.1-3.9-3.4-.2-.2h.2l5-1.8 1-5v-.3l.2.1 4 3.5 4.9-1.7z"/>
<path fill="#f7db17" d="m163.9 114.8 3.7 3.2 4.6-1.6zm0-.2 8.3 1.6-3.7-3.2zm-8.9-1.7 8.3 1.6-3.7-3.2zm8.3 1.7L155 113l3.6 3.2zm.4-.3 5.6-6.3-4.6 1.5zm-6 6.9 5.6-6.4-4.6 1.6zm5.8-6.3-5.6 6.4 4.6-1.6zm5.9-6.8-5.5 6.3 4.6-1.5zm-6 6.3-2.7-8-1 4.7zm3 8.5-2.8-8-1 4.8zm-2.6-8 2.7 8 1-4.8zm-3-8.6 2.8 8 .9-4.8z"/>
<path fill="#171796" d="m232 93.2-27-15.4-20.5 23.4 13.5 53a234 234 0 0 1 38.5-6.5z"/>
<path fill="red" d="M194.7 141a248 248 0 0 1 40.7-6.8l-1.1-13.8a262 262 0 0 0-43 7.2zm-6.8-26.6a275 275 0 0 1 45.2-7.6L232 93.3a289 289 0 0 0-47.5 8l3.4 13z"/>
<g transform="translate(-256)scale(.00253)">
<path fill="#0093dd" d="m212105 36890-23 13-9517-7794-9497 7778 1788 21560c2543-210 5113-322 7709-322 2608 0 5190 113 7744 325l1795-21560z"/>
<g id="hr-a">
<path d="M202545 46585c-18-2-44 10-69 45-186 250-359 469-545 720-195 61-242 180-167 348-261-26-291 193-302 432-250-379-522-482-814-307-11-230-187-338-439-392-180-10-319-65-436-145-60-42-110-64-170-106-126-88-226-5-172 74 267 434 535 868 802 1302-14 80 6 151 88 204 47 133 93 265 140 397-11 38-21 75-32 113-221-105-443-118-664-133-170-8-287-50-361-137-54-63-91-26-92 82-3 534 162 1014 599 1492-231 4-462 11-694 21-79 6-95 39-73 104 126 304 339 579 822 766-208 112-327 285-357 520-9 224-75 382-212 455-60 32-81 65-24 106 253 185 565 193 895 112-157 270-226 553-198 850 208 56 412 15 614-52-29 61-44 175-52 309-7 115-41 229-104 343-32 33-65 84 4 102 336 91 648 52 915-47 0 243 2 487 76 727 18 58 70 102 125 26 155-214 322-396 527-517 31 90 75 168 156 215 96 55 147 170 153 343 0 30-2 60 35 90 149 7 514-380 589-597 206 121 284 246 439 461 55 76 99 29 128-25 62-243 67-481 66-724 267 99 579 138 915 47 69-19 36-70 4-102-62-114-105-250-113-365-9-133-14-226-43-287 202 68 405 108 614 52 29-297-53-579-211-850 330 80 655 73 908-112 57-41 35-74-24-106-136-73-203-231-212-455-30-235-149-409-357-520 483-187 696-463 822-766 22-66 6-99-73-104-231-10-480-24-711-27 437-478 606-961 604-1495-1-108-38-146-92-82-74 87-179 137-348 146-222 15-435 24-656 128-11-38-21-75-32-113 46-132 106-260 153-393 82-53 102-123 88-204 267-434 513-868 781-1302 54-79-46-162-171-74-60 42-110 64-170 106-117 80-257 134-437 145-251 54-417 167-428 397-293-175-564-73-814 307-11-239-41-457-302-432 75-168 17-291-178-352-186-250-458-470-644-720-31-35-51-47-69-45z"/>
<g fill="#f7db17">
<path d="M205075 47978c-51-26-124 17-162 95s-33 170 19 196c40 20 84-6 119-56l22-36c2-3 4-6 5-9 38-78 49-163-2-188zm-5008 0c52-26 124 17 162 95s39 165-13 191-103-24-141-102-60-158-9-184zm4539 905c-32 0-59 27-59 59s26 59 59 59 59-26 59-59c0-32-26-59-59-59m-4032 0a59.1 59.1 0 1 0 .2 118.2 59.1 59.1 0 0 0-.2-118.2m4294-304c-754-91-1506-133-2260-133s-1509 41-2269 115c-26 8-21 90 14 86 756-73 1507-113 2256-113 743 0 1485 40 2228 129 39 4 54-80 32-84z"/>
<path d="M200319 48495c768-75 1530-117 2289-116 754 0 1507 42 2261 133l111-184c-32 10-62 9-90-5-76-38-92-161-36-274 56-114 164-175 240-138 39 19 62 62 68 114l446-739c-204 130-328 214-581 252-281 41-409 139-368 307 38 156-57 133-201 54-314-171-541 71-652 353-73 186-159 181-168-13-4-70 0-131-7-200-21-223-89-286-216-224-161 78-175 25-137-58 28-60 86-128 66-221-9-67-66-92-151-98-182-244-467-483-649-727-182 244-374 483-556 727-86 5-142 30-152 98-20 93 52 157 80 217 38 82 23 135-137 57-127-61-186-3-207 220-7 69-10 139-13 209-9 194-95 199-168 13-111-282-352-524-667-353-145 79-203 102-182-54 23-172-107-266-388-307-253-37-377-122-581-252l419 682c12-25 29-45 53-57 76-38 184 24 240 138 56 113 40 237-36 274-10 5-21 8-32 10l100 163zm4389 911c-7 3-7 4-24 11-46 19-80 66-134 124-57 60-128 125-211 188-12 10-25 19-44-6s-7-35 6-44c80-62 149-124 204-182 30-32 56-63 77-92-95-11-190-21-284-30-79 24-157 55-222 95-59 35-107 77-137 125-8 14-16 27-44 11-27-16-19-30-11-44 35-58 91-107 158-147 33-20 69-38 106-54-107-9-214-18-321-25-22 13-42 29-61 47-20 19-39 42-56 67-9 13-18 26-44 8s-18-31-8-44c19-29 41-54 64-77l9-9c-80-5-161-10-241-14-2 2-5 5-8 7a340 340 0 0 0-93 126c-6 15-12 29-41 18-29-12-23-26-17-41 12-29 27-55 45-81 8-11 18-22 27-33-115-5-230-9-344-12-4 5-9 8-14 11q-37.5 22.5-66 51c-28.5 28.5-35 40-48 63-8 14-16 28-43 12-28-16-20-29-12-43 16-28 35-54 59-77 7-7 14-13 21-19-122-2-244-4-365-4q-180 0-360 3c8 7 15 13 22 20 23 23 42 49 59 77 8 14 16 27-12 43s-35 2-44-12q-19.5-34.5-48-63c-28.5-28.5-41-36-66-51-6-3-12-7-15-12-115 2-230 6-345 11 11 11 20 23 29 35 19 25 33 52 45 81 6 15 12 29-17 41s-35-3-41-18c-9-24-22-46-38-67-15-21-34-41-55-59-4-3-7-6-10-10-81 4-162 8-243 13 4 4 9 8 13 12 24 23 45 48 64 77 9 13 18 26-8 44s-35 5-44-8c-18-26-36-48-56-67s-41-35-64-49c-1-1-3-2-5-3-110 7-220 14-330 23 43 18 85 38 122 61 67 40 124 89 158 147 8 14 16 27-11 44-27 16-35 3-44-11-29-48-78-90-137-125-72-44-159-77-246-102h-2c-90 7-179 15-268 24 22 33 51 68 86 106 55 58 124 120 204 182 13 9 25 19 6 44s-32 15-44 6c-83-64-155-128-211-188-37-38-99-111-135-140-196-90-354-127-575-147-153-14-318-9-458-79 36 85 75 164 126 229 53 68 120 121 209 147 8 2 21 16 22 25 28 157 84 286 169 386 52 60 114 110 188 149-75-81-132-166-172-251-67-142-90-286-77-420 1-16 3-32 34-29 32 3 30 19 29 35-11 123 9 256 72 387 56 118 159 237 291 346 24 19 0 63-29 55-154-44-290-123-383-231-89-104-149-237-180-397-94-32-165-90-222-164-47-60-85-131-118-205 28 428 182 801 456 1137 61 75 165 182 255 216 92 35 95 100-20 101-34 1-69 1-105 1 84 31 164 66 233 105 127 73 217 162 224 273 1 16 2 32-29 34-32 2-33-14-34-29-6-86-82-160-192-223-113-65-259-117-402-160-154 0-312-1-459 3 39 28 80 57 131 84 82 44 188 86 343 122 89 21 166 52 233 91 71 42 130 93 177 150 10 12 20 25-5 45s-34 8-45-5c-42-52-95-98-159-135q-91.5-54-216-84c-161-38-272-81-358-128-75-40-131-82-184-123 180 393 450 573 835 689 23 7 43 13 61 19 3 1 6 1 9 2 86 21 175 40 266 55 92 15 166 28 261 37 16 1 32 3 29 34-3 32-19 30-34 29-99-9-174-22-266-38q-87-15-171-33c-26 6-64 9-107 12-232 14-420 225-435 494 0 5 0 11-1 16 88-80 179-157 273-212 117-68 239-103 364-69 15 4 31 8 22 39-8 31-23 27-39 22-106-28-212 3-316 63-108 63-213 158-315 253-24 147-82 285-205 377 61 34 104 65 163 45 86-39 172-78 261-108 91-31 184-52 282-57 16-1 32-1 33 31s-14 32-31 33c-91 4-179 24-264 53-75 26-149 58-222 91 221 47 460-1 667-79 60-22 105-42 133-41 51-30 112-53 172-79 66-28 132-51 182-57 16-2 32-4 35 28 4 32-12 33-28 35-112 13-127 21-222 79 0 21-66 57-126 96-36 24-70 52-87 67-95 86-144 181-188 287-29 70-52 145-68 224 55-108 121-211 201-303 94-108 208-201 345-265 14-7 29-13 42 15 13 29-1 35-15 42-129 60-236 147-324 250-90 103-161 222-219 345-31 64-8 1-42 86 110-122 212-224 323-307 132-100 283-157 418-133 15 3 31 6 26 37s-21 28-37 26c-116-21-250 32-369 121-121 92-244 223-366 361 184 26 366-26 542-85 91-30 183-135 239-152 19-24 38-46 57-67 33-37 67-71 102-100 12-10 24-20 45 4s8 34-4 45c-33 28-65 60-96 94-32 35-62 73-92 113-6 8-13 17-24 16-60 70-151 162-172 240-57 210-25 370-122 576 71-38 128-81 175-134 53-60 94-135 128-230 37-104 95-195 167-270 75-77 165-136 261-172 15-5 30-11 41 19s-4 35-19 41c-87 32-169 86-238 157-66 68-119 151-153 247-37 102-81 183-141 250-44 50-95 91-156 127 52-3 78-10 121-7 79-6 211-66 279-119 66-51 116-120 154-206 6-15 13-29 42-16s23 27 16 42c-42 96-99 174-173 231-56 43-121 75-196 93 161-5 311-42 467-100 65-24 87-168 127-208 32-58 66-112 105-158 47-56 101-101 164-127 15-6 29-12 41 18 12 29-3 35-17 41-52 21-98 60-139 108-36 42-68 93-98 147 10 73-51 228-53 305-7 205-2 409 53 612 53-71 107-134 162-192 0-5 0-10 1-15 18-106 33-219 40-332 7-112 7-223-6-329-2-16-4-32 27-35 32-4 34 12 35 28 14 111 14 226 7 340-6 90-16 180-30 269 54-51 53-51 77-103 37-80 59-159 67-237 9-80 5-157-13-230-4-15-7-31 24-38s35 8 38 24c19 80 25 165 14 252-8 65-24 132-49 199 56-42 114-82 178-122-4-75-5-153-3-227 2-68 7-134 18-190 4-20 7-40 47-33s37 27 33 48c-9 50-14 111-16 177-2 78 0 162 4 243 5 82 49 185 125 230 103 62 158 163 186 274 16-145 17-280 3-400-17-143-55-26Line truncated
<path d="M204649 49231c-680-88-1359-113-2041-114-684 0-1369 40-2058 112-20 6-15 33-14 46 2 28 37 35 121 27 643-60 1285-93 1932-93 674 0 1351 21 2038 102 33 9 77-85 22-81z"/>
<path fill-rule="evenodd" d="M200570 49160c683-71 1362-110 2038-110 675 0 1349 40 2025 127l31-127c-17 9-37 15-58 15a123 123 0 1 1 113-171l60-170c-724-84-1446-122-2171-122-729 0-1459 38-2193 107l58 164c22-32 59-54 101-54a123 123 0 1 1 0 246c-12 0-25-2-36-6l33 94-2 7zm3067-416a123 123 0 1 0 0 246 123 123 0 1 0 0-246m0 64a59.1 59.1 0 0 0 0 118c32 0 59-27 59-59s-26-59-59-59m-1082-91a123 123 0 1 0 123 123c0-68-55-123-123-123m0 64a59.1 59.1 0 1 0 .2 118.2 59.1 59.1 0 0 0-.2-118.2m-1064-40a123 123 0 1 0-.2 245.8 123 123 0 0 0 .2-245.8m0 64c-33 0-59 26-59 59s26 59 59 59a59.1 59.1 0 0 0 0-118"/>
</g>
<path d="M202601 47974c-14-68-49-129-100-175-51-47-116-78-187-88-33-4-39-58-7-68 60-20 114-67 157-133 45-69 79-157 95-256 5-34 64-35 69-1 15 84 51 153 97 208 55 66 125 112 193 138 31 12 25 63-8 68-59 9-105 42-141 87-50 62-81 145-100 221-8 33-62 31-69-2zm33-118c20-52 47-103 81-146 28-34 60-64 99-84-51-30-100-70-143-120-28-34-53-73-73-116-19 59-45 112-75 158-31 47-67 86-108 116 50 19 95 47 134 82 34 31 63 68 85 110m799 5115-515 206c-17 7-35 14-48-21-14-34 4-41 21-48l515-206c17-7 35-14 48 21 14 34-4 41-21 48m59-326-604 328c-16 9-33 18-51-15s-1-42 15-51l604-328c16-9 33-18 51 15s1 42-15 51m-1826-65 604 328c16 9 33 18 15 51s-34 24-51 15l-604-328c-16-9-33-18-15-51s34-24 51-15m51 322 515 206c18 7 35 14 21 48-14 35-31 28-49 21l-515-206c-17-7-34-14-21-48 14-35 31-28 48-21zm224 434c137 33 261 48 358 31 88-16 155-60 191-146v-493c-107-1-212-15-303-41-109-31-170-98-201-178-41-107-27-235-4-329 5-18 9-36 45-27s32 27 27 45c-20 82-33 194 1 284 23 60 69 110 152 133 91 25 198 38 307 38 107 0 214-13 304-40 82-24 148-69 192-123s65-117 57-176c-5-36-24-62-49-80-34-24-82-35-128-37-47-2-94 7-142 16-25 5-50 9-77 13-19 2-37 5-42-32s14-40 32-42c23-3 48-8 73-12 52-10 105-20 159-18 60 2 121 18 168 51 42 29 72 72 80 131 11 80-16 163-73 233-53 65-131 119-229 147-83 24-178 38-274 42v483c3 5 3 11 2 16 37 82 102 125 188 141 97 18 221 2 358-31 18-5 36-9 45 27 8 37-9 41-28 45-146 35-279 51-388 32-92-17-165-58-215-132-49 74-124 115-215 132-109 20-242 4-388-32-18-4-37-8-28-45 8-36 27-32 45-27zm356 210 402-9c19 0 38-1 38 37 1 38-18 38-37 38l-402 9c-19 0-37 1-38-37s18-38 37-38m593-3082c151-125 293-227 423-297 133-72 254-111 359-106 19 1 37 1 36 39-1 37-20 37-39 36-92-4-200 32-322 97-125 67-263 166-410 289-14 12-29 24-53-5s-9-41 5-53zm-605 56c-141-130-298-240-445-314-139-71-268-108-363-100-19 2-37 4-40-34-4-37 15-39 34-40 110-10 252 31 404 107 152 77 315 191 461 325 14 13 28 25 2 53-25 27-39 15-53 2zm-213 1004c37-83 83-155 136-219 53-63 112-119 174-170 14-12 29-24 52 5 24 29 9 41-5 53-59 48-114 101-164 160-49 59-91 125-125 201-8 17-15 34-49 19s-27-32-19-49m371-1734c49 66 88 139 114 223 26 82 40 175 39 279 5 80 6 165-7 249-13 86-42 170-97 246-43 60-101 97-165 113-53 13-109 10-164-7 29 100 51 208 6 308-8 18-33 27-51 18-43-22-86-43-128-62s-84-36-127-51l-1-1c-95-37-173-73-236-112-65-39-115-80-150-124l1 2c-44-49-72-106-88-170-14-55-20-114-22-174-72-39-138-78-194-116-64-43-118-87-161-131-13-14-26-27 1-53s40-12 53 1c39 40 89 80 150 121 60 40 128 81 204 121 124 13 247 51 370 109 106 50 211 115 317 192 13 7 19 16 27 20 8 6 16 13 25 19 51 22 104 28 152 16 47-11 90-39 122-84 48-66 72-139 84-214 12-77 11-157 6-234v-2c1-97-12-183-35-258-24-76-58-142-102-201-11-15-22-30 7-52s41-7 52 7zm-375 1047c-104-77-207-141-311-190-105-49-210-83-314-98q3 72 18 135c13 52 35 99 71 138l1 1c30 37 73 72 130 107 60 36 134 71 225 106l-1-1c45 16 89 34 133 54 31 14 61 28 93 44 19-83-10-179-37-267-2-8-5-15-9-29zm776-1003c-44 59-79 125-102 201-24 76-36 161-35 258v2c-5 77-6 158 6 234 12 75 37 148 84 214 32 45 75 72 122 84 48 12 101 6 152-16l25-19c6-4 13-12 27-20 105-77 211-143 317-192 123-58 246-95 370-109 75-40 144-80 204-121s111-81 149-121c13-13 26-27 53-1s14 39 0 53c-43 44-97 88-161 131-57 38-122 77-194 116-2 61-8 119-22 174-16 63-44 121-88 170l1-2c-35 44-85 85-150 124-63 38-141 75-237 112l-1 1c-43 15-85 32-127 51-43 19-85 40-128 62-18 9-43 0-51-18-45-100-23-208 6-308-55 18-111 20-164 7-64-15-122-53-165-113-55-76-84-160-97-246-13-85-12-169-7-249-1-104 13-196 39-279 26-84 65-158 114-223 11-15 22-30 52-7 30 22 19 37 7 52zm940 715c-105 15-209 49-314 98-104 49-207 113-311 190-4 13-6 21-8 29-27 88-56 184-37 267 31-15 62-30 93-44 44-20 87-38 133-54l-1 1c91-35 165-70 225-106 58-34 100-70 131-107l1-1c35-39 57-86 71-138 11-42 16-87 19-135z"/>
<path fill-rule="evenodd" d="M203459 50602a216.4 216.4 0 0 0 0 433 216.6 216.6 0 0 0 0-433m0 69c-81 0-147 66-147 147s66 147 147 147 147-66 147-147-66-147-147-147m0 60a87 87 0 1 0 .1 174.1 87 87 0 0 0-.1-174.1m-1697-124c119 0 217 97 217 216s-97 217-217 217a216.6 216.6 0 0 1 0-433m0 69c81 0 147 66 147 147s-66 147-147 147-147-66-147-147 66-147 147-147m0 60a87 87 0 1 1-.1 174.1 87 87 0 0 1 .1-174.1"/>
</g>
<use xlink:href="#hr-a" width="100%" height="100%" transform="rotate(-2.2 -55532.8 156275.8)"/>
<use xlink:href="#hr-a" width="100%" height="100%" transform="rotate(2.2 459865.8 156275.8)"/>
</g>
<path fill="#171796" d="M328.1 101.3 307.6 78l-27 15.4-4.5 54.6a235 235 0 0 1 38.5 6.4l13.5-53z"/>
<path d="M286 104.7q1.4-.5 2.7.2.6.3.9.7l1.2-1 2-1a8 8 0 0 1 2.9-.9l3-.1q1.3 0 2.5.7l2.1 1.1 2.3 1.1a10 10 0 0 0 4.8.4q.9 0 .2.5c-3.5 2.2-6.3.2-8.6-.6l2.2 1.8q1.3 1.3 3.7 2.5a9 9 0 0 0 3.8 1l1.7-.2q.4 0 .4.2l-.2.4a5 5 0 0 1-2.7.8q-2.9 0-5.6-1.4-2-1.2-3.5-2.6a8 8 0 0 0-3.2-1.5 6 6 0 0 0-3.1.1q.4 0 .6.4.8.4 2 .5.8.1-.3.8-.7.8-1.8.8c-1 .7-1.4.2-1.8-.2l-.2.8v.5l.2.8.2.4.4.7q0 .3.2.5l.7.6q.9.4 1 1.3.7.2.8.9.4.1.6.5h2.8q1.3.3 2.1 1.4h1.3q.6-.3 1.4-.4h2.5l1.3.8q.8.7 1.9.3a3 3 0 0 1 2.5 0q.6-.3 1.2-.2.4-.5 1-.5 1-.2 1 .9-.1.4-.4.4-.5.9-1.4.7l-.5 1q.4 1.3-.7 1.2-.3.5-.7.4l-1 .6.3.6c.5.7-.2 1-.8 1q.2.8.1 1.4 1 .6.1 1.2.7 1.2-.5 1.8.1.7-.4 1t-.3.6q.6 1.1-.2 1.3l-.2.1q0 .2-.2.2l-1.6 1.1h-.3q-.5 1.3-1.8 2.5a2 2 0 0 1-1 1.3q0 .3-.4.3.6.6.2 1a3 3 0 0 1-1.4 1q-1.6.4-2.2-.2-.4-.5 0-.7-1 0-.8-.8 0-.2.4-.2l1-.3.5-.6q.2-1 1.2-1.4.8-.3 1.5-1.3l1-1.5-.3-.9-.6-.5q-.7 0-.8-.5h-.3l-.7.2-1.2 1q-.2.5-.8.5c-1 .2-1.5 1.3-2 1.8l-.5.8q0 1-.5 1.1h-.7q.4.4 0 1-.3.3-.9.4l-1.7-.3q-.7-.3-.5-.7-.6-.3-.4-.5 0-.4.5-.3l.6-.2a6 6 0 0 1 2-1.5l.7-.7q0-.8 1-1.5l.2-.6v-.3q-.3-.6-.2-1.2v-.6q-1.6.9-1.7 0-.6.5-1 0h-1.3l-.8.3-.6.7a3 3 0 0 1-.5 1.5l-.7 1.7-.1.5q0 1-.5 1.1l-.5.8v.2q.3.6-.2 1.2l-1 .5q-.5.2-1 0c-.6-.2-.5-.4-.5-.6h-.5l-.4-.3q-.2-.3.2-.5l.9-.6q0-.3.4-.5.3-.6.7-.7l.7-1.6q.2-.2.1-.5l.1-.3q.2 0 0-.2a1 1 0 0 1 .3-1.2c.3-.5.6-1.6.3-2.1l-.1-1h-.3q-.4-.2-.6.3l-.4.6q-.4 1.4-1 1.7l-.2 1.2q0 .8-.2.8t-.5.6l-.1.3q.4.5 0 1-1.2.9-2.5.3-.9-.3-.7-.7c-.9-.1-.7-.8 0-1a4 4 0 0 0 2-1.5q0-1.4.7-1.5.1-.8.5-1.5t.3-1.4q-.7-.5 0-1l-.1-.3q-.4 0-.3-.6 0-.2-.2-.2c-.7 0-.5-.4-.2-.7q.2-.2 0-.4l-.1-.5q-.6-.4 0-1-.4-.2-.4-.7-1.2-.1-.7-1l.7-.7q.4-.5.3-.9c-.2-.5.5-1 1-1.4v-.4q-.5-.5 0-1v-.4q-1.4.2-.5-1.1-.8-.4.5-1.6l.1-.4-1 .4q-.6.2-1 0l-.5-.4-.2-.7c-1 0-1.2-.7-.6-1.1l.6-.9q.6-.9 1.4-1.6l.1-.6-.8-.3q0-.4-.6-.6-.3-.2-.1-.5-.6-.7-.3-1z"/>
<g fill="#f7db17">
<path d="M304 130.2h-.3l-.8.8c-.2-.3-.6 0-.2.1l-.2.2q-.2.4-.6.5h-.2v-.4l.1-.4v-.1l-.2 1c-.8.2-1.3 1.1-2 1.7l-.4.9c0 .2 0 1-.3 1h-.2q-.1-.3-.4 0h-1c-.3.2.3.1.6.3q.2.2.2.5c-.3.7-2 .5-2.5.2q-.4-.2-.3-.5h.4l.6-.2v-.2q0-.2-.2 0l-.5.1h-.5q-.2 0-.1-.1h.2l.7-.3a6 6 0 0 1 2-1.5l.8-.7q.2-.9 1-1.5 0-.4.3-.8v-.4q-.3-.6-.2-1v-1l.9-.6v.8l-.2.3v.3-.2l.3-.3q.4 0 .8-.4l.7-.7v1.2l-.6.7v.1h.1l.5-.7h.1l.1-.2.9-.4v1z"/>
<path d="m287.4 120.6.2-.2.4-.6.5-.7h.1v.1l-.5.6-.4.6v.2q0 .5.3.8l.3-.5.5-.4.3-.3.2-.3h.1l-.2.4-.4.3-.4.4-.3.3q.2.2 0 .4c-.1.3-.4.5 0 .8l.2.5q.2.3-.1.7c-.3.3 0 .2.1.2h.1l.3-.4.3-.4.4-.5.2-.6h.1l-.2.6-.4.6-.3.4-.3.4q.3.3.2.8h.2l.2-.3.2-.4h.1l-.2.5-.2.2q.3.3 0 .7-.6.4 0 .6v1.7-.4l-.1.1q-.5 1-.7 1.9c-.7.1-.6.8-.6 1.4a4 4 0 0 1-2 1.6q-.4 0-.6.3.1.3.4.2.8-.6.2.2v.3c.6.4 1.8.6 2.4.2q.7-.3.2-.9 0-.9.8-1.3.2-.1 0-.4 0-.8.2-1.4l.2-.2q.4-.1.7-1.3l.1-.4q0-.3.3-.1l.1-.4q.4-.6 1-.4l-.2-.9.1-1 .1-.2.3-.8.4-.8v.1l-.3.8-.3.8v.1a3 3 0 0 0 .2 2v.1q.3 0 .2.4v.6c.4.6 0 1.8-.3 2.3q-.2.4-.2 1 .2.2 0 .5v.1l-.3.6-.7 1.7q-.6.2-.7.8-.4 0-.4.3l-1 .7q-.2.1.3.4.3 0 .6-.4t.2 0l-.2.7c0 .2.6.3.7.3q.8-.2 1.3-.6t.1-.8h-.5l-.6-.3q0-.2 0 0l.7.1h.5v-.2l.5-.9q.5-.2.4-1l.1-.5q.5-.7.8-1.7.5-.8.5-1.5 0-.4.3-.4 0-.2.3-.5l.2-.4.1-.2v-1l-.2-.2v-.2h.1v.2l.1.1.2.6v.5l-.2.2-.1.4.7-.2.4-.5.2-.6h.1l-.2.7-.3.4 1.1-.2v-1.2l.4-.8-.2.8v1.2q.1.5.5.3l.4-.3v-.1l2-1.6-1.7 1.5v.3q-.1.7.6.4l.6-.3.3-.2 1-.7.2-.6.8-1.3.7-.6.5-.7-.4.8-.7.6a4 4 0 0 0-.9 1.8v1l.7-.4.8-.7q0-.5.3-.6l.4-.4v.2l-.4.3-.2.5v1.2l1-.6h.1v1.1q0 .5.4.8l.5-.6.4-.3.2-.2.2-.3h.1v.1l-.2.2-.3.3-.3.2v.5h.7q-.1.7.5.6v-.5l.1-.5h.1v1q.2.5.6.7l.2-.5.1-.5h.1v.5l-.3.6v.5l.2-.3.3-.4h.1l-.3.4-.2.4.2.2c-.7 1.1-1.4 2.7-2.6 3.2q-1 .3-1.1 1.3l-.6.6q-.6.3-1.3.4 0 .4.4.4l.8-.3s-.1.5-.6.8v.2c.4.6 1.4.4 2 .2.4 0 1.1-.6 1.3-1q.1-.3-.2-.6c-.8.1-.7-.6 0-.3q.3.1.3-.4 1-.2 1-1.1l1.8-2.2.1-.6.2-.4.1-.5h.1v.5l-.2.4q0 .2.2.2.7-.7 1.6-1.1 0-.4.5-.4l.1-.1q.2-.5-.2-.9 0-.5.4-.7.4-.5.3-1.1v-.2l-.1-.3.2.3v.1q.5-.2.7-.6l-.3-.9-.6.3v-.1l.7-.4.3-.2c.2-.3-.3-.6-.5-.7l-.8-.7v-.1h.1l.7.7v-1.3l-.2-.4-.5-.4-.2-.5h.1l.2.5.4.3q.5.2 1 0c.4-.2-.1-.7-.2-1l-.2-.5h.1l-.1-.2h-.2v-.1h.2l.2.3h.1q.5-.1.8-.6l-.4-.4-.4-.4.5.3.5.5q.4 0 .5-.3l-.2-.5-.3-.4q.3 0 .4.4l.2.4h.5c.3-.2 0-.8 0-1l-.2-.2h-.2v-.2h.1l.2.1.2.1.4-.6c0-.4.1-.2.5-.3q.6.1 1-.7.2 0 .3-.3.1-.5-.4-.5-.7-.1-1.1.4v.2q.2 0 0 .1l-.1-.2-1.3.2h-.1l.2.3.1.4v.1h-.1l-.1-.5-.3-.4-1-.2.1.1.2.2v.1l-.3-.2-.2-.2-.9.2-.6.2.4.5.2.6v.1h-.1l-.2-.6-.4-.5h-.6l.2.3-.3-.4-.5-.2.2 1.6h-.1v-1l-.2-.7-.3-.2-.6-.5-.2.8.1-.8-1.1-.3.6 1 .3.4v.5l-.1-.5-.3-.4-.3-.5-.4-.5h-.4.1l.1.3h-.1v-.2l-.2-.1h-.8a6 6 0 0 1 1 2.4v.1l-.5-1.3-.7-1.2-.7.2.5.4-.3-.2-.3-.2-.3.1h-.2l1.1 1.2-.6-.6-.6-.5h-.7l.5 1.9-.3-1-.4-.8h-.4l.4 1.7h-.1l-.2-1-.2-.8a4 4 0 0 0-1.4-1.1 2 2 0 0 1 .4 1.2h-.1l-.1-.7-.4-.6h-.4l-1-.1q.4.3.4.7h-.1q0-.3-.5-.7h-.7l.3.3.3.5q0 .4.2.6 0 .3.4.5l-.5-.4-.3-.7-.2-.4-.3-.5h-.7l.2 1v1l-.1-1-.3-1-.5-.4-.2.3v-.4q-.2-.7-.7-.9v.5l-.2.5q-.2 0 0 0v-.6l.1-.4q-.1-1-1-1.3v.7h-.1v-.9l-.5-.5-.1.8-.4.9h-.1l.4-1v-.8l-.1-.3-.1.3h-.1v-.3l.1-.1v-.1l-.3-.5-.3.5.2-.6-.1-.3-.1.2-.3.2.2-.3v-.1q.2 0 0-.2.1-.4-.1-.8h-.4l-.5.4v-.1q.3-.3.5-.3l.4-.1v-.3l-1.2.3h-.8v-.2l.8.1 1.2-.3.2-.6h-.6l.6-.1v-.2q.1-.4.5 0 .3 0 .5.3.4.2.9-.2h-.3v-.1h.4q.8.1 1.5-.5h-1l-.7-.2v-.1l.7.2h1l.2-.1.4-.3-2-.6-.8-.4a2 2 0 0 0-1.4.3l-.3-.3-.5-.1h-.7v-.1h.7l.6.1 1.9-.4 1.8-.2v-.2q.4-.4.3-1-.2-.4-.8-.7h-1.4l-.9.1q.4.2.6.5.4.4.5 1v.1h-.2q0-.7-.4-1t-.8-.5l-1.4.3q-.5 0-.8.3l1 .1q.3.2.6.8v.1h-.2q-.2-.6-.5-.7c-.5-.2-1.4 0-1.8.2l-.6.7h-.2v-.2l.7-.7.7-.3 1-.6 1.5-.2-.6-.6h-.7l-1.7.8h.6v.2h-.9s0 .1 0 0l-1.6 1.3q-.9 0-1 .2-.2.2-.2.6l-.2.8q-.9.7-1.4 1.6l-.3.6h.3v.1-.3l.3-.4-.1.4v.6h-.2l-.1-.3h-.3l-.4.3-.2.3q0 .4.6.3c.6 0 .1.4.5.7l.4.5h.6q0-.7.2-.7l.2-.4v.3l-.1.2v.4l1-.3q1.2-1 .5 0l-.1.2-.2.7c-.2.1-1 .8-.8 1.1q.4 0 .3.2c0 .2-.4.7-.3 1h.3l.8-.7.6-.8v-.1l.3-.8.3-.8h.1l.3.3.2.1h.3l.3-.3s0-.1 0 0l-.2.3-.4.1h-.3l-.2-.3-.3.7-.2.7v.2l-.7.9-.6.5.2.6 1-1 .9-1.1-.3.6-.1.4v.8l-.1-.8v-.2l-1.3 1.4q-.7.3-.3.7l.4-.3.4-.3h.1v.1l-.4.3-.4.3.1.4 1-.6-.8.6-.1.2c-.3.2-1.3.8-1.2 1.2q.4 0 .7-.3l.7-.5v.1l-.6.5v.3h-.2v-.2l-.5.2q0 .6-.4 1c0 .2-1 1-.7 1.2h.4z"/>
<path d="m287.9 106.9-.8-.3-.5-.6q-.3 0-.1-.3l.3-.1h.3l.2.2-.2-.3h-.8l-.2-.6.4.1.2.2-.2-.3h-.2a3 3 0 0 1 2.3.3q.5.2.8.6l-.2.2q-.8 0-1.1.2-.2.2-.3.7z"/>
</g>
<path d="m304.4 130-.4.2.4-.6zm2.1 4.5-.4.4-.5.4.5-.5zm1.4-1.7-.2.3q-.2 0 0 0zm-.7 0a1 1 0 0 1-.7.7l.4-.4.2-.4zm1.3-1.7-.1.2-.1.2h-.1l.2-.5zm1.1 0-.1.5-.2.5h-.1l.2-.5.1-.5zm-.5-6.4q.5.7.5 1.5l.1-.5a2 2 0 0 0 .3-1.3h.1v.7l-.3.6-.2 1v.1h-.2v-.1q.1-1-.4-2zm-.8 1v.3h-.1v-.2s0-.1 0 0zm-.2 1.5.2 1.5.4-.4h.1v.1l-.3.3-.2.3-.2-.9-.2-.8h.2zm1.1.6v1.2zs-.1 0 0 0m1-.2v1.8l.1.6-.3-.6v-.7l.2-.5zm.5-1.7v.3l-.1.3s0 .1-.1 0v-.3zm-.5-2.8.1.3h-.1zs-.1 0 0-.1m.3-2v.8l-.3.4v.6h-.1v-.7l.3-.4zm-2.1-.8.8.7q.3.4.3 1h-.1l-.6-.2q.2 1.2 0 2a2 2 0 0 1-1 1l-.1-.1a4 4 0 0 0 .1-1.8h.1v1.7q.5-.3.8-1 .2-.6 0-1.9l.7.2-.3-.8-.8-.7zm5.5-1zh-.1v-.2zm-1.3-.1.2.2.2.2-.2-.2-.3-.1v-.1zm-2 .5.3.6v.6s0 .1 0 0v-.6zm-6.4.7v.4h-.1zm1.2-.7v.3l.1.1v.1l-.1-.1zm2.5 1.7v.3h-.1zm.8-1.9.1.2v.2h-.1v-.3q-.2 0 0-.1m-5.9.3.1.4.2.3.2.5v.4-.4l-.3-.4-.2-.4zm2.4 1.6.2.7v.8q0 .5.2.8t.7.7l-.2-1 .4-1 .3-.4v-.5h.1v.5l-.3.5-.3 1 .1 1v.1l-1-.8a1 1 0 0 1-.1-1v-.7zm-1 1.5-.1.8-.5.6.3-.1.7-.6.4-.3-.3.4-.3.3.1.3v.1h-.1v-.4l-.5.3-.4.1s-.1 0 0 0l.5-.8zm-1.1-.1v.2h-.1v-.2z"/>
<path d="M302.4 121.3q.2.6.1 1 0 .6-.4 1l-.6.7-.1.5.2-.2q0-.2.5-.3l1.2-1h.1l-.6.6-.7.5-.5.3-.3.4h-.1l.2-.9.6-.7.4-.8-.1-1zm2.3 3.8.2.4.1.2q.3.3.3.8l-.1 1h-.1l-.4-1-.9-.7 1 .7.3.7v-.7l-.2-.8v-.1q-.4-.2-.3-.5zm1 0 .2.1v.1q.2 0 0 .1l-.1-.1zm1.3.4-.3.7-.2.7s0 .1 0 0h-.1l.2-.7.3-.8zm.7 4.1v.2h-.1v-.2zm-.8-1.2v.4l-.2.4v-.5zm-1 0v.2l-.1-.1v-.1zm-1.3-.6-.1.4h-.1l.1-.4s0-.1.1 0m-1.8 1.9-.1.2v.3h-.1v-.3zm-5 5-.1.1-.2.2h-.1v-.1l.3-.3zm.8-.5-.1.2h-.2s0 .1 0 0v-.1l.2-.1zm1-.6-.2.2v-.1l.1-.2zm1.6-4.1v.8l-.1.1-.1-.5.1-.5zm1-11v.3l-.3.4v-.1l.2-.3v-.4zm-1-1 .2.5v.5h-.1v-.5l-.2-.5zm-1.5 1.8-.4-.5-.1-.4q.2 0 0 0l.2.4zq0 .1 0 0m1.6 2.7v.3zm-2-2q.4.3.6 1v.8h.1q.2 0 .3-.4l.2-.9.2-.6.1-.6h.1l.5.9.3 1h-.1l-.3-1-.4-.7v.5l-.3.5q0 .7-.2 1 0 .3-.3.4h-.1v.5h-.1l-.1-1.3-.6-1v-.2zm1.2 5.1v.5l-.2.4v-.9zm-2.4 1 .1.4h-.1zm2-2.6-.1.6-.4.6-.3.7v.7l-.1-.8q0-.3.3-.7a2 2 0 0 0 .6-1.2zm-.5-.8v.6l-.5.6-.4.5-.3.6.2-.6.5-.6.4-.5v-.6zm-2.5 1.2q0 .6-.2 1-.2.6-.7 1-.2-.1 0-.2.4-.3.6-.8.3-.4.2-1zm-3.3 1.8v.2l.2.2h-.1l-.1-.1-.1-.2v-.1zm-1.3 0v.8q0 .3.4.7l.2.9.4-.6.1-.6h.1l-.1.7-.5.6h-.1v-.5l-.2-.5-.3-.7-.1-.8zm.2 4.5.1-.8h.1l-.1.8zm-.2 1.2v-.5h.1v.5zm-.9 2.1.3-.3.2-.3h.1l-.2.4-.3.3v-.1zm-1.2-7v.2zm-2 4.8v-.3zm5.2-8.5-.2 1q0 .6.3.8.4.3.5.7l.1-.4v-.4h.1l-.1 1h-.1l-.1-.2q-.1-.4-.5-.6-.3-.3-.3-.8t.2-1.1zm-.4-1.4v.8l-.3.8-.2 1q0 .6.3 1.1v.1a3 3 0 0 1-.2-2.2l.3-.8zm-2.4 3.5v.5l.1.5-.3-.5v-.6zm-1-.4-.2.6v.3l.1.3-.2-.3v-.4l.1-.2v-.3zm6.1-9.2a3 3 0 0 0 .3 1.6l-.4-.8zm-.8.7a4 4 0 0 1 .6 1.9h-.1a4 4 0 0 0-.6-1.8h.1zm-3.8.2q-.6.8-.6 1.3t.2 1a3 3 0 0 1 0 1.7h-.1l.2-.9-.2-.8-.2-1q0-.6.6-1.4.2 0 0 0zm3.6-1.6v.5l-.4.5-.4.5v.7q-.2-.4 0-.8l.3-.5.3-.4v-.4zm-3.8.4v.7l-.4.6q-.4.3-.4.7l-.2.8v-.9l.5-.6.4-.7zm2.1 0v.3l.1.3-.2-.2v-.3h.1zm0-.5-.5.4-.2.8.2.6.1.7h-.1v-.7l-.3-.5q-.6.6-.6 1.3a3 3 0 0 0 .5 1.6 3 3 0 0 1-.6-1.6q0-.8.7-1.4 0-.5.2-.8zm-.7-.8v.1q0 .5-.2.8l-.3.3v.4h-.1v-.4l.3-.4.2-.7zm6 6.5.3.8h-.1v-.4zm0 2v.6l-.2.7-.2.6h-.1l.2-.6.3-.7zm-1.2-2a3 3 0 0 1 .7 2.8h-.1a3 3 0 0 0-.7-2.6v-.1h.1zm0 2.3-1 1.4-.6 1-.2-.5-.2-.5q-.3-.4-.2-.9t.3-.9l.3-.6v-.6h.1l-.3 1.3-.3.8.2.9.3.7.5-.7zm-3.4-1v.7l.4.7h-.1l-.3-.6zm-1.9 1v.3zm.7-1.7-.4 1q-.2 0 0 0l.2-.5v-.5zM291 124a4 4 0 0 1-.3-1.9l.6-1.1.5-.7v-1h.1v1l-.6.8-.5 1q-.2.7.3 1.9zm-1-1.3v.8s.1 0 0 0l-.2-.4v-.4zm4.8-3.4.3.9-.2 1q-.2 0 0-.1l.1-.9-.3-.8zm1.5-1.1a3 3 0 0 1 0 1.7l-.2-.7-.5-.6q0-.1.1 0l.6 1v-.6zm.9-1.3.4.8.1 1.1v.5l.1.5-.2-.4v-.6l-.1-1q0-.5-.4-.8zm-2.4-.2-.2.6v.6h-.1v-.7l.2-.6h.1zm-4.6 3.1-.4 1.2-.8.7h-.1l.8-.8.4-1v-.1zm-.1-1.6v.5l-.3.5.1-.5v-.4l.1-.1zm3.1-.2v1.1h-.1V118zm.8-1.4q-.4.4-.4.8l.2.8q.4.6.3 1.2 0 .7-.3 1.3h-.1l.3-1.3-.3-1.2-.2-.8q0-.5.4-.9v.1zm-5-8.7 1.2-.5q.6 0 1.2.3h.8-.8v.1l-1.2.7a1 1 0 0 1-.9-.2v-.1l.9.2 1-.6-1-.2q-.5 0-1 .4z"/>
<path d="M290.2 107.8a.3.3 0 0 1 0 .6l-.3-.3q0-.3.3-.3m1.9 1.7.5-.3.3-.4q.2 0 0 0l-.2.5zm4-1.6h.2l.4.1v.1h-.1l-.3-.1zm-2.3 0 .4-.4.7-.1v.1l-.7.1zm.2.4.2-.2.3-.2v.1l-.3.1v.2zm-3.7 1.3-.2.3v.3h-.1l.2-.7q.2 0 0 0zm1 .5v.1zm-.6-1.2h.7l-.4.1h-.4v-.1zm.7-2h-.3l-.1.2h-.1l.2-.3zm-1.2 0-.2.2-.1.1v-.2zm-.7 0-.1.2-.1.2h-.1v-.2zm-.8.8-.4.4h-.1l.4-.5zm0 1-.1.1-.2.2.1-.2zm-.9.2v.4zm1.2 1.5v.6s0 .1-.1 0v-.6q.2 0 0 0zm-.7-.5v.4h-.1v-.4zm-.6-4.8.5.5v.1h-.1l-.2-.3-.2-.2s-.1 0 0 0zm5.5 7.2a2 2 0 0 1 0 1.5v-.8l-.1-.6v-.1zm-1.8 2 .3-.6.5-.6a2 2 0 0 0 .3-1.5v-.1h.1v.9l-.3.7-.5.6-.3.7q-.2 0 0 0zm-2.3-.5.2-.3.3-.2v.1l-.3.2zm-.5-.2.3-.3.4-.2-.3.3-.3.3zm2.5-2.3h.5-.6zm-1.3-.2v.2h-.1zm25.8 7.4.2.2v.2l-.1-.1-.2-.2h.1zM287 132l.4.2.5.1v.1h-.5l-.5-.4z"/>
<g fill="red">
<path d="M308.6 106.3h1.8q-1 .5-2 .7l.2-.3zm-1.5-.1 1.3.1v.3l-.3.3v.2h-1.3l.3-.5zm-11.2-3.3h2.2q.3 0 .5.4.2.3.1.8l.1.1h.1l-.1-1-.3-.3h.2q1.2 0 2.3.6l.2.6-.4.6q.2.2.2 0 .3-.2.4-.6v-.4l1.8 1v.5l-.5.3s-.1 0 0 .1q0 .2 0 0 .4 0 .7-.3v-.5l1.3.6v.4q0 .3-.4.4v.1q0 .1.1 0 .4 0 .5-.4v-.3l.5.1 1.4.4.1.4-.2.4v.2c-1.5-.2-3-1-4.4-1.5l-.5-.2-1.7-.7h-.2v-.1l-1.5-.4h-1.8q0-.5-.2-.8z"/>
<path d="m293.3 103.6 2.2-.6q.6.2.7.5.2.3.1.7a5 5 0 0 0-2 .2q-.2-.4-.6-.7h-.4zm8 4-.6-.3a7 7 0 0 0-3.3-.9q.6-.6.4-1.2l-.7-.8 1.1.1 2 .5q.2.6.1 1-.2.5-.6.7 0 .3.1.1.6-.3.7-.8v-.9l1 .7 1.2.8q0 .7-.4.9h-1z"/>
<path d="m303.4 109.3-2-1.5q.6 0 1-.2t.5-.8l1.7 1.5v.7l-.3.2z"/>
<path d="m305.7 110.8-.7-.4-1.4-1h.7l.4-.2.2-.4v-.3l2.2 1.6q0 .4-.3.4l-1 .2h-.1z"/>
<path d="m308.1 111.8-2.2-.9q.6 0 1-.2.5 0 .5-.5l1.3.6v.4q-.1.3-.5.4v.1z"/>
<path d="M313.2 111.2h.2q-2.2 1.4-5.1.6.5-.2.6-.5t0-.4l1.8.4-.1.2-.5.4s-.1 0 0 0v.2l.7-.4.1-.4zM302.9 137l-.3.4-1.3.3q0 .4.4.4l.8-.3s-.1.5-.6.8v.2c.4.6 1.4.4 2 .2.4 0 1.1-.5 1.3-1q.1-.2-.2-.6-.7 0-.6-.3-.7-.2-1.5-.1m-16.2-5.1-1.1.6q-.4 0-.6.3.1.3.4.2 1-.6.2.2v.3c.6.5 1.8.6 2.4.3q.7-.4.2-1v-.2l-.6-.1-.4-.3zm10.7 3.7h-.2c-.2.2.4.1.7.3q.3.2.2.5c-.3.7-2 .5-2.5.2q-.4-.2-.3-.5l1-.2v-.2h-.2l-.5.1h-.5q-.2 0-.1-.1h.2l.8-.2v-.2q.9 0 1.4.3m-7.7-.5-.2.3-1 .7q-.2.1.3.4.3 0 .6-.4t.2 0l-.2.7c0 .2.6.3.8.3q.7-.2 1.2-.6t.1-.8h-.5l-.6-.3v-.1zm3-28.1h.3l.7.1 1.8-.4 1.8-.2v-.2q.4-.4.3-1-.1-.3-.8-.7h-1.4q-.5 0-.9.2.4 0 .7.4.3.3.4 1v.1h-.2l-.4-1q-.5-.4-.8-.4-.8 0-1.4.2-.4 0-.8.3h1q.4.3.6.9v.1h-.2q-.2-.6-.5-.7c-.5-.2-1.4 0-1.7.2l-.7.7h-.1q1.2 0 2.3.4m-2.4-.5.7-.7.7-.3 1-.6 1.5-.2-.6-.6h-.7l-1.7.8h.6v.2h-.9s0 .1 0 0l-1.4 1.1z"/>
</g>
<path fill="#0093dd" d="m351.4 167 22.2-50.1-16.4-26.4-29 10.8-13.6 53a234 234 0 0 1 36.8 12.8z"/>
<path fill="#fff" d="M323.2 120.6a269 269 0 0 1 42.3 14.7l-8.6 19.3a248 248 0 0 0-38.9-13.4z"/>
<path fill="red" d="m358.3 151.5 5.7-12.9a265 265 0 0 0-41.7-14.4l-3.4 13.2a252 252 0 0 1 39.6 13.7z"/>
<path d="M326.4 134.2q.6-.2 1.2 0 .5-.2 1.1-.2l.7-.2v-.3l-.2-.4a3 3 0 0 1-1.1-1.6q-.6 0-.6-.3h-.6q-1-.1-1.3-.5l-.7-.5-.2-.3q-.4-.6.2-.7h.7q.4-.2 1-.2.4 0 .9-.5v-.5q0-.2.2-.1.6 0 .9.5.8.1 1.3.8 1.1.1 1.2.5t-.5.7v.1l.4.5 1.1.7q1.6-.1 5.1 2.1 2.3.4 4.4 1.3h1q3.8-.7 7.2 1.7 1.2.2 2.4.7l1.7.4q1.3 0 2.7.7 1.6.3 2.6 1.3.7.8-.2 1.2-.5 1-2 .4-1 0-2-1a10 10 0 0 1-2.7-1.4l-1.3-.6h-.9v.2q.4.3.5.8v1q0 .7.7 1l1 .5q.4 0 .4.3l.4 1.8.4.4q.8.8-.3 1-.7.8-1.4.9t-.7-.2l-.5-.3c-.5-.4 0-1 1.1-.7q0-.2.3-.2v-1l-.4-.2-1.1-.6q-1-.8-2.5-1.6-1 0-2-.6h-.7q-.4-.1-.6.1-.3.4-.8.2h-1.2l-.5.1q-.8 0-1.1.2t-.7.2l-.4-.3-.6-.2q-.7-.3-.6-.4c-.6-.2-.3-.7.1-.7l1.5.1q.7.1 1.2-.2l.6-.6a4 4 0 0 1-2-.5q-1.8-1.2-3-.3-.3.3-.6.1h-.6l-1.3-.1a4 4 0 0 1-2 0q-.9.5-1.2.1l-.7-.4q-.5 0-.5-.2-.6-.2-.5-.4-.2-.4 0-.6.5-.2 1.1 0l.5.4h.7q.4-.2 1-.2l.8-.2v-.2q-.7-.2-.9-.8a3 3 0 0 1-1.7-.6q-.6 0-1-.2H328l-.9-.1q-.4.5-1 .2l-.9-.5q-.4-.1-.4-.4-.5-.3 0-.6.4-.1 1.1.1z"/>
<path fill="#fff" d="M343.1 139.3s-.1-.2-.3 0q-1.3.1-2.4-.5t-2.2-.7l.9-.5h1.5q1.2.1 2 .5 1 .3 1.1.7h.1l.9.8h-.8z"/>
<path d="M345 101.1v.2l1 5.2-4 3.5-.2.1.3.1 5 1.7 1 5.2v.2l.2-.2 4-3.4 5 1.7h.1v-.1l-1-5.2 4-3.5.2-.1-.3-.1-5-1.7-1-5.2v-.2l-.2.1-4 3.5-5-1.7z"/>
<path fill="#f7db17" d="m342.5 109.9 3.7-3.2 4.7 1.6zm9-1.7 3.7-3.3 4.6 1.6zm8.4-1.5-3.7 3.2-4.7-1.6zm-9 1.7-3.7 3.3-4.7-1.6zm-5.4-6.8 4.6 1.6 1 4.8zm6 6.9 4.6 1.6 1 4.8zm5.4 6.5-4.6-1.6-1-4.8zm-6-6.9-4.6-1.6-1-4.8zm3.2-8.1 1 4.8-3.7 3.2zm-3 8.6 1 4.9-3.7 3.2zm-2.9 8-1-4.8 3.8-3.2zm3-8.6-1-4.9 3.8-3.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-hu" viewBox="0 0 512 512">
<g fill-rule="evenodd">
<path fill="#fff" d="M512 512H0V0h512z"/>
<path fill="#388d00" d="M512 512H0V341.3h512z"/>
<path fill="#d43516" d="M512 170.8H0V.1h512z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 273 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ie" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h512v512H0z"/>
<path fill="#009A49" d="M0 0h170.7v512H0z"/>
<path fill="#FF7900" d="M341.3 0H512v512H341.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 289 B

+12
View File
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-is" viewBox="0 0 512 512">
<defs>
<clipPath id="is-a">
<path fill-opacity=".7" d="M85.4 0h486v486h-486z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="0" clip-path="url(#is-a)" transform="translate(-90)scale(1.0535)">
<path fill="#003897" d="M0 0h675v486H0z"/>
<path fill="#fff" d="M0 189h189V0h108v189h378v108H297v189H189V297H0z"/>
<path fill="#d72828" d="M0 216h216V0h54v216h405v54H270v216h-54V270H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 520 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-it" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h512v512H0z"/>
<path fill="#009246" d="M0 0h170.7v512H0z"/>
<path fill="#ce2b37" d="M341.3 0H512v512H341.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 289 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-lt" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt" transform="scale(.51314 1.0322)">
<rect width="1063" height="708.7" fill="#006a44" rx="0" ry="0" transform="scale(.93865 .69686)"/>
<rect width="1063" height="236.2" y="475.6" fill="#c1272d" rx="0" ry="0" transform="scale(.93865 .69686)"/>
<path fill="#fdb913" d="M0 0h997.8v164.6H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 439 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-lu" viewBox="0 0 512 512">
<path fill="#ed2939" d="M0 0h512v170.7H0z"/>
<path fill="#fff" d="M0 170.7h512v170.6H0z"/>
<path fill="#00a1de" d="M0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-lv" viewBox="0 0 512 512">
<g fill-rule="evenodd">
<path fill="#fff" d="M0 0h512v512H0z"/>
<path fill="#981e32" d="M0 0h512v204.8H0zm0 307.2h512V512H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 234 B

+71
View File
@@ -0,0 +1,71 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-md" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#00319c" d="M0 0h170.7v512H0z"/>
<path fill="#ffde00" d="M170.7 0h170.6v512H170.7z"/>
<path fill="#de2110" d="M341.3 0H512v512H341.3z"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M173.6 691v63.7c0 3.6 0 7.1-3.5 7.1-3.6 0-7.1 7-7.1 10.6 0 7.1 3.5 10.7 7 10.7h32c3.5 0 7-3.6 7-10.7-.2-3.7-3.5-10.6-7-10.6-3.6 0-3.6-3.5-3.6-7v-63.9" transform="matrix(.46695 -.29552 .30926 .4462 -5.1 64.5)"/>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="3.1" d="M177.2 696.3c0 24.4-9.6 44.3-21.3 44.3s-21.3-19.9-21.3-44.3S144.3 652 156 652s21.3 19.8 21.3 44.3z" transform="matrix(.42858 -.34452 .36055 .40954 -29.7 77.3)"/>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M173.6 691v63.7c0 3.6 0 7.1-3.5 7.1-3.6 0-7.1 7-7.1 10.6 0 7.1 3.5 10.7 7 10.7h32c3.5 0 7-3.6 7-10.7-.2-3.7-3.5-10.6-7-10.6-3.6 0-3.6-3.5-3.6-7v-63.9" transform="matrix(.48902 .26089 -.27302 .46728 313.4 -58.3)"/>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="3.1" d="M421.6 701.6h-28.3c-1 1-9 54-17.7 74.4-10.6 24.8-42.5 46.7-42.5 46.7s19 6 24.8 10c1.4-.4 31.8-7.1 35.4-46 3.5 38.9-21.3 56.6-21.3 56.6s35.5 10.6 35.5 35.5c0-24.8 35.4-35.5 35.4-35.5s-17.8-17.7-21.3-56.7c7 39 34 45.7 35.5 46 5.7-3.9 24.7-10 24.7-10S450 800.9 439.4 776c-8.7-20.3-16.8-73.5-17.7-74.4z" transform="matrix(.56007 0 0 .53518 29.7 -86.3)"/>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="3.1" d="m336.6 467.7 53.2 17.7a38 38 0 0 0 35.4-35.4c.6-39.4-14.2-53.2-17.7-53.2l7-7L404 379s3.6-18.4 39-17.7 35.4 17.7 35.4 35.4c0 17.8-17.7 17.8-17.7 53.2a38 38 0 0 0 35.5 35.4l53.1-17.7V645H336.6z" transform="matrix(.56007 0 0 .53518 9.8 -56)"/>
<g fill="none" stroke="#000">
<path stroke-width=".6" d="m302.2 292.2 14.6 15 14.7-15 14.6 15 29.3-15 14.3 15.8 15-15.8 21.6 15.8 22.3-15.8 14.6 15 14.7-15" transform="matrix(.1355 0 0 .38018 203 87)"/>
<path stroke-width=".3" d="m432.3 435.8-7.1 39m10.6-39v39m7.1-39v39m7.1-39 3.5 39m3.6-39 3.5 39" transform="matrix(.56007 0 0 .53518 9.8 -56)"/>
</g>
<g stroke="#000" stroke-width="3.1">
<path fill="#a77b3b" fill-rule="evenodd" d="M318.9 361.4c35.4 17.7 35.4 53.2 35.4 70.9-1.2 16.1 0 354.3 0 354.3s-2.3-20-17.7-35.4L319 733.5l-17.7-17.7c-13-11.8-17.7-35.5-17.7-53.2v-248s0-17.8 35.4-53.2z" transform="matrix(-.56007 0 0 .53518 357.1 -56)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(-.56007 0 0 .53518 357.1 -131.8)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(-.56007 0 0 -.53518 357.1 492)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(-.56007 0 0 .53518 357.1 -94)"/>
<path fill="none" d="m301.4 557-.2 158.8" transform="matrix(-.56007 0 0 1.01529 357.1 -399.6)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(-.56007 0 0 .91125 347.3 -316)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(-.56007 0 0 1.0208 337.4 -384.6)"/>
</g>
<g stroke="#000" stroke-width="3.1">
<path fill="#a77b3b" fill-rule="evenodd" d="M318.9 361.4c35.4 17.7 35.4 53.2 35.4 70.9-1.2 16.1 0 354.3 0 354.3s-2.3-20-17.7-35.4L319 733.5l-17.7-17.7c-13-11.8-17.7-35.5-17.7-53.2v-248s0-17.8 35.4-53.2z" transform="matrix(.56007 0 0 .53518 158.7 -56)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(.56007 0 0 .53518 158.7 -131.8)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(.56007 0 0 -.53518 158.7 492)"/>
<path fill="none" d="M283.5 574c.4 0 17.7-17.7 17.7-17.7l17.7 17.7 17.7-17.7 17.7 17.7" transform="matrix(.56007 0 0 .53518 158.7 -94)"/>
<path fill="none" d="m301.4 557-.2 158.8" transform="matrix(.56007 0 0 1.01529 158.7 -399.6)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(.56007 0 0 .91125 168.5 -316)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(.56007 0 0 1.0208 178.4 -384.6)"/>
</g>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="3.1" d="M177.2 696.3c0 24.4-9.6 44.3-21.3 44.3s-21.3-19.9-21.3-44.3S144.3 652 156 652s21.3 19.8 21.3 44.3z" transform="matrix(.51699 .20584 -.21541 .49401 290.2 -73.2)"/>
<path fill="red" fill-rule="evenodd" d="M198.4 203.8h119v56.9h-119z"/>
<path fill="#564dff" fill-rule="evenodd" d="M198.4 260.7h119V289c0 19-29.7 19-59.5 38-29.8-19-59.5-19-59.5-38z"/>
<path fill="none" stroke="#ff0" stroke-width="2.5" d="M336.6 485.4h212.6V645c0 35.4-53.1 35.4-106.3 70.9-53.1-35.5-106.3-35.5-106.3-71V485.5z" transform="matrix(.56007 0 0 .53518 9.8 -56)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M385.6 129.9S335 185.4 335 238.5s53.1 70.9 53.1 70.9-17.7 0-17.7 70.8c0 35.5 53.1 17.8 53.1 35.5s-.7 60.5 0 88.6c0 17.7-35.4 0-35.4 17.7 0 8.8 26.6 53.1 53.2 53.1s53.1-44.3 53.1-53.1c0-17.7-35.4 0-35.4-17.7v-88.6c0-17.7 53.1 0 53.1-35.5 0-70.8-17.7-70.8-17.7-70.8s53.2-17.7 53.2-70.9-50.7-108.6-50.7-108.6 28.5 73.2 28.5 108.6c0 17.7-13.3 53.2-48.7 53.2 0 0-9-17.8-17.7 0 0 0-10.2-17.8-17.7 0-2.6 6-7.2-17.8-17.8 0-4.4 4.7-8-17.8-17.7 0-17.7 0-53.1-17.8-53.1-53.2s32.9-108.6 32.9-108.6z" transform="matrix(.39205 0 0 .2513 84.9 175)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M382.7 248c-3.6 3.6 4.6 61.3 7 63.8 3.6 3.5 24.9 3.5 28.4 0 2.5-2.5 0-56.7-3.5-60.2-3.6-3.6-29.4-6-32-3.6z" transform="matrix(.39205 0 0 .2513 86.4 225)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M382.7 248c-3.6 3.6 4.6 61.3 7 63.8 3.6 3.5 24.9 3.5 28.4 0 2.5-2.5 0-56.7-3.5-60.2-3.6-3.6-29.4-6-32-3.6z" transform="matrix(-.39205 0 0 .2513 429.4 225)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M414.6 228.5a16 16 0 1 1-32 0 16 16 0 0 1 32 0z" transform="matrix(.45063 0 0 .31327 63.9 210.4)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M414.6 228.5a16 16 0 1 1-32 0 16 16 0 0 1 32 0z" transform="matrix(.45063 0 0 .31328 92.6 210.4)"/>
<path fill-rule="evenodd" d="M270.4 311c0 1.2-1.6 2.2-3.5 2.2s-3.4-1-3.4-2.2 1.5-2.2 3.4-2.2 3.5 1 3.5 2.2m-18 0c0 1.2-1.6 2.2-3.5 2.2s-3.5-1-3.5-2.2 1.6-2.2 3.5-2.2 3.4 1 3.4 2.2z"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="m439.4 550-7.8-10.3-12.8 1.7 1.8-12.8-10.3-7.7 10.3-7.8-1.8-12.8 12.8 1.8 7.8-10.3 7.8 10.3 12.7-1.8-1.7 12.8 10.2 7.8-10.2 7.8 1.7 12.7-12.8-1.7z" transform="matrix(.54641 0 0 .52213 17.8 -47.3)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="m496 591.7 21.3 10.7 21.3-10.7-21.3-10.6z" transform="matrix(.56007 0 0 .53518 9.8 -56)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="m496 591.7 21.3 10.7 21.3-10.7-21.3-10.6z" transform="matrix(.56007 0 0 .53518 -75.5 -56)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M535 659c-3.5-7-14.1-10.6-21.2-10.6s-14.2 0-21.3 10.7c0-14.2 10.5-21.3 21.3-21.3a23 23 0 0 1 21.2 21.3z" transform="matrix(.48568 -.26652 .27892 .4641 -137.5 131.4)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 7.9 -65.5)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 3.9 -52.2)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 1.9 -59.8)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 11.8 -52.2)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 13.8 -59.8)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.56007 0 0 .53518 7.9 -57.9)"/>
<g fill-rule="evenodd">
<path fill="#da4500" d="M238.7 159c-10 0-11.3 1.5-15.8 3 0-4.3 11.1-9.4 21.1-9.4 0 3.2-2.6 6.3-5.3 6.3z"/>
<path fill="#cac9c7" d="M254 148.8c0 2.1-1.8 3.8-4 3.8s-4-1.7-4-3.8 1.8-3.8 4-3.8 4 1.7 4 3.8"/>
<path fill="#ff0" stroke="#000" stroke-width="1pt" d="M361.4 357.9v10.6h17.7v53.1h10.7v-53.1h17.7v-10.6h-17.7v-14.2H379v14.2z" transform="matrix(.56007 0 0 .53518 9.8 -56)"/>
<path fill="#da4500" d="M244 152.6a64 64 0 0 0-23.8 3.2c0-5.9 6.8-12.7 18.5-12.7 2.7 0 5.3 6.3 5.3 9.5"/>
</g>
<g fill-rule="evenodd">
<path fill="#008500" stroke="#000" stroke-width=".9" d="M67.3 613c-14.2-14.2-16.6-21.4 3.6-10.1l283.7 247.5-3.4 7L67.4 613z" transform="matrix(.31554 .24102 -.11439 .19257 189.7 119)"/>
<path fill="#008f00" d="M182.3 329.6c.7-8.6-15-17.9-15.7-17.3s1 5.6 4 8.2c-5.8-2.9-11.8-4.7-12-3.4-.4 1.7 9 5.8 9.8 7.4 0 1.1-7 1-6.8 2.1.1 1.2 19.7 11 20.7 3m-15.7-25c.6-8.6-15.1-18-15.8-17.3-.6.6 1 5.6 4 8.2-5.7-3-11.7-4.8-11.9-3.5-.4 1.7 9 5.8 9.7 7.4.1 1.2-6.9 1-6.7 2.2.1 1.1 19.7 11 20.7 3m-12-22.8c.6-8.6-15.2-17.9-15.8-17.3s1 5.6 4 8.2c-5.8-3-11.8-4.7-11.9-3.4-.5 1.7 9 5.8 9.7 7.4.1 1.1-6.9 1-6.8 2.1s19.7 11 20.7 3zm-12-24.6c.7-8.6-15-18-15.7-17.3s1 5.6 4 8.2c-5.8-3-11.8-4.8-11.9-3.5-.5 1.7 8.9 5.8 9.7 7.4.1 1.2-6.9 1-6.8 2.2.1 1 19.7 11 20.7 3"/>
<path fill="#008f00" d="M146.7 256c7.7-4.6 6.2-22.2 5.3-22.3s-4.2 4-4.6 7.9c-1-6.2-3-11.9-4-11.2-1.8.7.3 10.3-.6 11.9-.9.7-5-4.7-5.8-4-.9.8 2.4 21.7 9.7 17.7m11 21.7c8.9-2 13.6-19 12.9-19.4-.8-.4-5.4 2.5-7.1 6 1.3-6.1 1.3-12.1 0-11.9-1.8.1-3.3 9.8-4.6 11-1.1.5-3-6-4.1-5.5s-5.3 21.2 3 19.8zm13.3 22.1c8-4 8.2-21.6 7.3-21.8s-4.5 3.7-5.3 7.5c-.4-6.2-1.8-12-3-11.5-1.8.5-.7 10.3-1.7 11.8-1 .6-4.4-5.2-5.4-4.5s.4 21.9 8 18.5zm14.2 29c8.4-3.2 10.5-20.8 9.6-21-.8-.4-4.9 3.1-6 6.8.3-6.2-.6-12.1-1.8-11.7-1.9.4-1.7 10.2-2.9 11.6-1 .6-3.9-5.5-4.9-5-1 .7-2 21.8 6 19.2z"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M209 776c0 7.8-10.2 14.2-23 14.2s-23-6.4-23-14.2 10.3-14.2 23-14.2 23 6.4 23 14.2z" transform="matrix(.48902 .26089 -.27302 .46728 313.4 -58.3)"/>
<g fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width=".6">
<path d="M156 414.6v-7.1h7v-7.1h7v7h7.2v7.2H170v17.7h-7v-17.7zm7 60.2h7v205.5h-7z" transform="matrix(.40549 .1277 -.14842 .38308 363.6 58)"/>
<path d="M159.4 676.8h14.2v56.7h-14.2zM156 439.4h21.3c14.1 0-3.6 39-3.6 39h-14.1s-17.8-39-3.6-39zm17.6-5.4c0 3-3.1 5.4-7 5.4s-7.1-2.4-7.1-5.3 3.1-5.4 7-5.4 7.1 2.4 7.1 5.4z" transform="matrix(.40549 .1277 -.14842 .38308 363.6 58)"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1pt" d="M209 776c0 7.8-10.2 14.2-23 14.2s-23-6.4-23-14.2 10.3-14.2 23-14.2 23 6.4 23 14.2z" transform="matrix(.46695 -.29552 .30926 .4462 -5.1 64.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+118
View File
@@ -0,0 +1,118 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-me" viewBox="0 0 512 512">
<path fill="#d3ae3b" d="M0 0h512v512H0z"/>
<path fill="#c40308" d="M25.6 25.6h460.8v460.8H25.6z"/>
<path fill="#b96b29" d="M367.4 251.8c3-.8 13.5-3.9 20.8-9.8s8.8-10.7 9.4-13.3.8-4.6-1-3.2c-2 1.5-7.7 6-11.6 8.2a43 43 0 0 1-9.6 3.7c-2 .6-3.8 1-1.7.2s8.6-3.1 13.7-8.2c5.1-5.2 9.2-12.8 9.3-17.6 0-4.7-.3-4.3-1.8-3.1s-6.9 7.8-10 10.3a96 96 0 0 1-10.3 7.3c-2.4 1.4-5.8 3-1-.2s13.8-11 17.5-21 3-16 3-17c-.1-1-1-3-1.8-1.1s-6.9 14-11.8 19.2c-4.8 5.2-3.7 4.2-4.8 5s-3 2.3-.5-.6c2.3-3 7.2-9.7 9.7-16.5a55 55 0 0 0 3.3-17.3c0-3.9-1.6-9.2-2.4-10.8s-1.5-2.8-1.7-1-4.2 13.8-6.3 19.6a75 75 0 0 1-7.1 13.7c-.8 1.2-3 3.8-.7-.6a83 83 0 0 0 7.5-29.4 47 47 0 0 0-2.8-22.3c-1.6-3.3-3.1-4.5-3.1-4.5s.3 8.3-2.2 20.3-6.1 18.2-6.1 18.2-1.5 1.8-.5-1.1c.7-2 2.2-6.2 2.8-13.4.7-7.5.8-16.1-1.4-23.4-2.4-8-6.5-12-6.5-12-1.2 11.4-1 26-5.2 38-.7-.7-1.8-2-2.2.7a67 67 0 0 1-7.4 20.6c-5.2 9.4-7.3 10.6-7.3 10.6l-1.6-3.5c-.7-1.3-1-2-2-.2-.8 1.7-3.7 8.3-10 15.6s-9.8 10.8-9.6 16.2c.2 5.5 3 8.3 6.9 10.7 3.8 2.5 4.5 3.9 3.9 6.8-.6 3-4 4-7 4.1s-6.7-.1-8 .6c-1.3.8-1.4 2.7-2.8 4-1.3 1.4-1.9 2.1-3.1 2.3l-1 .2c-.4-14.4 5.2-20.8 5.2-20.8L291 217.7l-2.4 1.1-.4-.7a12 12 0 0 1-2.8-8.3c.8.1 2.3.7 3.8.4 1.4-.3.3-1.7-.1-3a28 28 0 0 1-.2-10.3s2.3 2.4 3.7 2.4 1.3-1.4 1.5-3a12 12 0 0 1 3.2-6.9s2.5 1.7 5.3 1.7c2.9 0 6.7-.7 9.7-3.6s3-3.6 3-3.6 5.6 2 10 2.2c4.2.2 8.8-.5 11-5s-.6-8.8-1.1-9.5-2-1.3-2 0 .1 4.7-2.5 6.4-4.6 1.6-7.1.8a22 22 0 0 0-14-.3c.2-.2 3.4-3.5 6.6-4.5s5.1.4 6.2 1.6c1 1.3 2 3.4 2.8.4.9-3 .4-6.4-1-8.6a8 8 0 0 0-7.7-3.2q-4.5.6-4.3.8c.2.2-1.6-4.6-5.7-5.4-5-1-10.5.8-14.8 3.2 0 0-3.4.8-6.8-.2a22 22 0 0 1-5.9-2.9l1.2-3.7 2-.7c1-.4.8-.8 1-1.6s-2-2.4-2-2.4-.3-2.6 3.1-4.3l3-1.5s2 .3 2.9-.2q1-1 .8-1c-.2 0 1.5-.5 2-1.4q.9-1.6.3-3.3c-.4-1-1-1.3-.4-1.8a5 5 0 0 0 1.2-2.5v-1s1.2-.5 1.1-2.3-1-2.4-1.6-2.6l-.9-.1s0-1.1-1-2.5l-1.5-2c-.1 0 .7-.5.3-1.7a3 3 0 0 0-2-2.1c-.7-.3-1.6 0-1.6 0l-4-2.3-1.5-.8s0-1.1-.5-1.7-1.5-.9-2-.9l-1.2.3c-.2.1-4.5-1.1-6.4-1.4s-2-.5-2-.5-4-1.9-5.5-2.2l-2-.5s1.6-2 1.7-4.2a6 6 0 0 0-3-5.4l-1.7-.8V99l3 .3.2-4.8-3 .4.5-3h-6.2l.5 2.9-3-.2.3 4.8 3-.4-.2 1.8s-1.1.2-2.1.9a6 6 0 0 0-2.9 5.1 9 9 0 0 0 1.5 4.3l-2 .4c-1.5.3-5.6 2.2-5.6 2.2s0 .2-2 .5l-6.3 1.4s-.6-.3-1.2-.3-1.6.3-2 .9c-.5.6-.5 1.7-.5 1.7l-1.6.8-4 2.3s-.8-.3-1.6 0a3 3 0 0 0-2 2.1c-.3 1.2.3 1.7.3 1.7s-.6.6-1.5 2-1 2.5-1 2.5l-.9.1c-.5.2-1.5.9-1.5 2.6-.1 1.8 1 2.2 1 2.2l.1 1q.2 1.7 1.1 2.6c.6.5 0 .7-.3 1.8a4 4 0 0 0 .2 3.3c.6.9 2 1.4 2 1.4s0 .4.9 1 2.8.2 2.8.2-.4-.3 3 1.4c3.4 1.8 3.1 4.4 3.1 4.4s-2 1.5-2 2.4c.2.8 0 1.2 1 1.6l2 .7 1.2 3.7c-1.3.9-3.5 2.3-5.8 3-3.4 1-6.8.1-6.8.1a23 23 0 0 0-14.8-3.2c-4.1.8-5.8 5.4-5.8 5.4s-1.1-.5-4.3-.8a8 8 0 0 0-7.6 3.2 11 11 0 0 0-1 8.6c.8 3 1.7 1 2.8-.4 1-1.2 3-2.6 6.2-1.6a19 19 0 0 1 6.5 4.5s-.5-.4-4-.9c-4-.5-7.4.4-10 1.2-2.4.8-4.4.9-7-.8-2.7-1.7-2.6-5.1-2.6-6.4s-1.4-.7-1.9 0-3.3 4.9-1.1 9.4 6.8 5.3 11 5c4.4-.2 10-2.2 10-2.2s0 .8 3 3.7 6.8 3.6 9.6 3.6 5.3-1.7 5.3-1.7a12 12 0 0 1 3.3 7c.2 1.5 0 3 1.5 3s3.6-2.5 3.6-2.5l.4 2.4c.2 1.7-.1 6.5-.5 7.8-.4 1.4-1.5 2.8 0 3 1.3.4 2.9-.2 3.7-.3a12 12 0 0 1-2.8 8.3l-.4.7-2.3-1.1-15.7 18.3s5.6 6.4 5.3 20.9v-.1l-1.2-.2c-1.2-.2-1.8-1-3.1-2.3s-1.4-3.2-2.8-4-5-.5-8-.6-6.4-1.1-7-4c-.5-3 .1-4.4 4-6.9 3.8-2.4 6.6-5.2 6.8-10.7s-3.3-9-9.6-16.2c-6.3-7.3-9.2-13.9-10-15.6-1-1.7-1.3-1-2 .2l-1.6 3.5s-2-1.2-7.2-10.6a67 67 0 0 1-7.5-20.7c-.4-2.6-1.4-1.3-2.2-.6-4.2-12-4-26.7-5.1-38 0 0-4.2 4-6.6 12a64 64 0 0 0-1.4 23.4c.6 7.2 2.1 11.5 2.8 13.4 1 2.9-.5 1.1-.5 1.1s-3.6-6.3-6-18.2c-2.6-12-2.3-20.3-2.3-20.3s-1.4 1.2-3.1 4.5a47 47 0 0 0-2.8 22.3c.6 12.2 5.1 25 7.5 29.4s.2 1.8-.7.5c-.8-1.2-5-7.8-7.1-13.6A242 242 0 0 1 128 165c-.3-1.8-1-.6-1.8 1s-2.4 7-2.4 10.8a55 55 0 0 0 3.3 17.3 68 68 0 0 0 9.8 16.5c2.3 3 .5 1.4-.6.6s0 .1-4.8-5a98 98 0 0 1-11.8-19.2c-.8-1.9-1.7.1-1.7 1.2-.1 1-.8 6.9 2.9 16.9a49 49 0 0 0 17.6 21c4.8 3.2 1.3 1.6-1.1.2A96 96 0 0 1 127 219c-3-2.5-8.4-9.2-9.9-10.3s-1.8-1.6-1.7 3.2c0 4.7 4 12.3 9.2 17.4 5 5.2 11.7 7.5 13.7 8.3s.4.4-1.7-.2a43 43 0 0 1-9.6-3.7c-3.9-2.2-9.6-6.7-11.5-8.2s-1.6.6-1 3.2c.5 2.5 2 7.3 9.3 13.3s17.8 9 20.8 9.8.9.9.9.9l-7.3-1.5c-6-1.4-15.3-6.2-17.8-7.8s-2.8-2.1-2.5 1.2 3.9 10.3 9.5 13.6a53 53 0 0 0 18.3 5.1c2.6.2 2 .5.8.6a55 55 0 0 1-20.6-3.5l-.5-2.1c-.2-1.3-.9-1-1.3-.8-.4.3-.6.5-2 1.2s-1.5-.1-1.5-.6l-.4-3.1q0-1.3-1.2-.7l-5 2c-1 .6 0 1.5.6 2.6l1.5 2.8-3.8 1c-1 .2-.7.9-.4 1.5l2 4.9c.4 1 1.2.4 1.2.4l3-1.8 1 4c-.1 0-1.4.3-2.9 1.3s-1 2.4.7 2.9c1.8.5 3.8 1.4 6.1 5.6s2.9 11.6 4.3 23.4 9 17.8 10 18.5c1 .8.4 1.4.4 1.4s-1 1.5-.6 3.7a6 6 0 0 0 3.5 4c1 .4.8 1.5.8 1.5s-.6 2.5 0 7c.5 4.7 4.2 9.9 5.3 10.8 1 .8 1 1.3 1 1.7-.2.3-.9 0-2.1 1q-2.1 1.5-3.6 4c-.9 1.6-.6 2.5-.3 4.7s3.6 4.3 3.6 4.3-.6.1-.8 1.8q0 2.6 2.6 4.6c1.4 1.2 4.2 1.8 4.2 1.8l.4 2.4c.2 1 2 2 3.2 2.6s3.5.3 4.4-.2q1.5-.5 2.2 1c.6 1 .4 4.7.4 6.2s-.9 3-.4 3.8 2 .2 3.4-.6 1.7-.2 3 .6 3.3 1.6 3.8 1.5c.5 0 .3-1.4.3-2.5s.2-3.7.7-4c.2-.2 2.4-.4 4-1q1.5-.4 2-1.5c0-.7-1.1-.7-3.6-2-2.4-1.3-2.8-2-4.4-3-1.5-1-.8-1.4-.8-1.9s.7-1 1-1.5.2-2 .2-2l1.8-1.9 2.2-2.4c.8-.6 2-3.8 2.9-4.9.8-1 2.6-2.1 3.7-3.5s4-3.7 5.8-4.8c1.7-1.2 6.3-3.3 6.3-3.3s-.1 2.7 1 4c1.3 1.4 2.8-1 8-4.7s9.3-5 9.3-5l2.5 3.5c1.4 1.7 2.7-.1 9.1-9 5-7 11.8-13.4 14.4-15.8l1.4.6-.4 1.2c-.6 1.8-1.5 3-2.8 5.6s-3.2 3.1-5.4 6.6Line truncated
<path fill="#d4af3a" d="M300 253.6c0-10.9 5-17.7 5-17.7l-13.7-15.6c-12.9 5-24.8 5.3-35.3 0-10.6 5.3-22.4 5-35.4 0L207 236s5 6.8 5 17.7-2 19-3.3 24.2-2.2 15 .6 22c2.7 7 5.7 13.2 17.8 18.2 12 5.1 18.4 7.7 22.6 11.2 4.2 3.6 6.3 6.4 6.3 6.4s2.1-2.9 6.3-6.4c4.2-3.4 10.5-6 22.6-11.2 12-5 15-11.2 17.9-18.2s1.7-16.8.5-22-3.3-13.3-3.3-24.2z"/>
<path fill="#b96b29" d="M295.7 258a30 30 0 0 1 4.3-19.4L288.3 225c-9 5-21.4 4.3-32.3-.7-10.8 5-23.3 5.7-32.2.7L212 238.6s6 9.1 4.3 19.5-5.4 26.8-4.5 33.4c1 6.7 5.7 14.5 10.5 18.5a61 61 0 0 0 17.2 9.4 49 49 0 0 1 12.5 7.2c2.4 1.8 4 3.4 4 3.4s1.5-1.6 3.9-3.4c2.3-1.8 7.3-5.4 12.4-7.2a62 62 0 0 0 17.3-9.3 33 33 0 0 0 10.4-18.6c1-6.7-2.7-23-4.4-33.4z"/>
<path fill="#1d5e91" d="M294.6 259.3a35 35 0 0 1 3.3-20.2l-10.5-11.7c-8.8 4.6-19.6 3.4-31.4-.7-11.7 4-22.5 5.3-31.4.7l-10.5 11.7s5 9 3.4 20.2-4.3 23.9-3.8 31.3l.2 1.6h9.5s.6-.7 1.6-1.1l1.4-.8c-.1 0 .2-2 1.4-2.3q1.5-.5 2.1-.4c.6.1.8-.1 1.4-.7s2-2 2.7-3.9 1-2.7 1-4 .5-3.2.5-3.2-.2-.6-1.2-.3c-1 .4-1.1-.6-1.2-1.6s0-2.9.5-3.8l.8-1.2-.4-1.6s-6.1 1.3-7.2-3.5l-1.4-7.5q0-.7-1.1-.7c-1.1 0-2.5-.5-3.8-3-1.2-2.4-1.3-3.4-1-4.4s1-.6 1.3-.3c.2.3 1.8 2.7 2.5 2 .6-.6.3-.6-.6-2.5-1-1.8-.6-2.4-.5-3.3 0-1-.3-2.2-.3-2.7s.5-.9 1-.6l1.4 1c.2.2.3.7.9.4.5-.3.2-2 .2-2.5q.1-1 1.4-.6a2 2 0 0 1 1.2 2q-.1 1 .4 1.1c.3 0 .7-.9.7-1.5s.4-1.5 1-.7.7 1.8.5 3 .2.6-.2 2.6-.6 3-.5 4.5 0 2.4.8 4.2c.8 2 1.4 4.1 2.8 4.2h1.8s1.6-3 2.8-3.8 3-.8 4.2-1q1.7-.1 1.7-.7.2-.4-1.9-.6c-1.4-.2-2.7-.6-2.7-1.7s.2-2.1-.7-2.2c-1 0-3.4-.8-4.2-2.4s-.7-1.9-.6-2.7 1.2-2.6 2-1 1.1 1.8 1.7 2.2c.6.3 1.4.4 1-.7a5 5 0 0 1-.5-2.6q.1-1.2 1.3-1.3l3.6-1 1.1-.3s.1-.7 1.4-1.1a10 10 0 0 1 3.2-.5c1 0 1.6.2 2.9-.4a6 6 0 0 1 3-.7 4 4 0 0 1 2.1.8c.6.4.4 1 .4 1.5s.1.8 1 .8q1.2-.1 2.4.4c1.2.5 2 .7 1.2 1.6-.7.8-.7 1.8-1.2 1.8q-.4.1-.6.7-.4.5.2.9c.5.3.5.6 1.2 1.3s1.8 1.3.7 2.4c-1 1-3 2-3 2s-1 .4.2 1.2c1.3.6 1.8 2.1 2.3 2.4l1.2.8c.3.1.2 1.2.2 1.2l-1.5.8q-1.5.6-2.8.7-1.3-.2-1 .8l1.2 2.2.4 1.3s1.4.3 2.6.3 1.9 0 4.6-.3 3.6-.4 6.3 0c2.7.2 3.1.1 3.8.4l1.4.6s3.8 0 6-.5c2-.5 2.5-1.8 2.2-2.5s-.3-1.8-3.9-2.3-5.1-.3-8-.9c-2.9-.5-7.4-2-8.2-5.6s2.1-5.2 3.6-5.9 4.6-1.8 6.1-2.4a14 14 0 0 1 7-.8c2.8.4 4.5 1.2 5.6.5s2-.2 1.8.9c-.2 1-1.3 3-4.5 4s-7 1.1-9.1.5c-2.1-.5-3.7-1.3-5-.9s-2.6 2-1.3 4c1.4 1.8 8.4 2.4 10.3 2.6s8.8-.3 10.8 3.1c2 3.5.4 7.3-2 8.2a30 30 0 0 1-6 1.7l-1.8.2s.8 1.9.8 2.6-.1 2.2.8 2.8 2 1 3.5.8 3.3.8 3.4 2.2c0 1.3.5 3.1.6 5.8.2 2.6 0 4.8 0 4.8s0 1-.4 2l-2.2 4.7h10.2l.1-1.6c.5-7.5-2.1-20.2-3.8-31.4z"/>
<path fill="#6d8c3e" d="M215 297.1c2 5.8 6.2 11.5 12.5 15.3 8.7 5.3 12.4 4.6 20.7 10 8.2 5.3 7.7 6.2 7.8 6.2s-.4-1 7.8-6.3c8.3-5.3 12-4.6 20.7-9.9a29 29 0 0 0 12.4-15.3z"/>
<path fill="#d4af3a" d="M222.2 253c1 1 1.6 1.3 3 1.1 1.5-.1 1.8 1 2 2 .3 1.2.3 3.9 1.4 6.3 1 2.5 4.4 2.6 4.4 2.6q.3-2 1-4c.2-.3.6-1.4-.6-1.4s-1.2-.5-2-1.7a23 23 0 0 1-2.4-6.3c-.4-2 .3-5 .6-6.1q.5-1.8 0-2c-.4-.3-1.2 0-1.2 0s.3.9 0 1c-.4.2-.7-.5-1-1.2-.2-.7-.5-.6-1-.5q-.7.1-.1 1c.3.5.1 1.4.1 1.4l-1.5-1c-.4-.2-1.5.2-1.6.8 0 .6.4 2 1.4 2.9s1 2.4.6 3.1c-.3.8-1.7.7-2 .3a1 1 0 0 0-1.2 0c-.4.5-.8.7.1 1.7m-.5-2.6-.8-.7-.6-.8v.9c0 .6.6 1.2.6 1.2zm2-6.8q.8-.2.5-.8l-1-1.2h-.6l.4 1.4q.1.9.7.6m2.6-2.1q-.1.7.4.4.7-.5.4-1.1c-.2-.4-.7-.7-.7-.7l-.3-.2zm3.7.6-.2-1s0 .5-.2.9q-.4.3 0 .5.3.3.4-.4m5.8 25c.7 1.7 2.5-1 4.2-3 1.7-2.2 1.6-.6 1.4 1.2a8 8 0 0 0 1.1 4.7c.7 1.2 1.3.5 1.5-.3.3-.8.5-2.6 1-3.4.7-.9 1.2-.1 1.3.6a8 8 0 0 0 1.7 3.3c.9 1 1.3-.3 1.5-1.5l.9-3.7c.2-.7 1-.1 1-.1l1.7 1.6c1 1 1.2-.6 1.3-1.4.1-.9-.2-2.7.2-3 .3-.3 1.2.4 2.7 1.1s1.4-.7 1.2-1.4a8 8 0 0 0-2.1-3.6c-1.2-1-1.4-2.2-1-2.7s1.8 0 2.7-.2q1.1 0 2-.3c.7-.1.7-.8.7-.8s-.5-.8-1.5-1.7-1.5-1.5-2.7-1.8c-1.1-.2-2.7 0-2.7 0l-.2-.3q-.4-.4-.7 0l-1.3 1.2q-.6.6-1 .4c-.3-.1.3-.5.3-.5s.6-.5 1.1-1.6q.9-1.8.3-2.7c-.4-.5-1.7 0-2.3 0q-1 .1-1.2-.2c0-.4.7-.2 1.5-.3s1.4-1.1 1.7-2q.3-1 1-.2c.7.8 1 .4 1 0l1.1-2c.6-.8 1-2 .3-2.7s-2.1-.7-2.7-.2q-.9.6-1.2 2.2c-.1 1.3-.5.6-.5.2s-.5-1-.3-1.5 0-1 0-1 0-1.1-1.2-1.4a9 9 0 0 0-3.7.4c-1.2.3-1 1.3-1 1.3l-1.8.1-3 .8q-1.3.2-1.2.7l.2 1.2s.7 0 1 .2q.6.5-.2.8-.6.1-.6.8 0 .9.7.7.7-.1 2.5.5c1 .5 1.4 1.5 1.3 2-.2.6-1.7 1.3-2.5 1.3s-1 .2-1 .7 1 .5 1 .5l2.7.2c1.9.3 2.2.6 3 1.4s.2.9-.2 1q-.7.6-1.6 1-.8.6-1.6.8c-.5.1-2 0-4.5 1s-3.4 4.5-3.4 4.5l1.5-.8 4-2.1c1.3-.8 1.3.4.6 1.6s.6 1 1 .6.9-.7 2.2-1.4.8.4.8.4-.4.8-.5 2.7c-.1 1.8 1.9 0 2.7-1.1 1-1.2 1.3 0 .7 1.3s-2 2.4-2.7 2.4-1.3-.7-1.4-1.2.3-1.8-.2-2.1-1.7 0-2.7.6-1.1.1-1.4-.4.4-.7.2-1q-.5-.3-1.2.2l-3 1.5c-1.7 1-1.6 1.4-1.6 2.7s.3 1.6 1 3.3z"/>
<path fill="#d4af3a" d="m252.5 239.1 1-.5 1.6-.3c.8 0 .6-.5.5-.9q-.2-.5-1.5-.5c-1.3 0-1.6.5-2.1.7-.6.3-1.3.4-1.3.4l.5.7q.7.9 1.3.4m4 4 .9-.2 1.3-.6c.7-.3 1-.4 1.4-1.1s-.5-.8-1.6-1.1q-1.7-.4-1.9.5.1.6-.5 1.8c-.6 1.2 0 .7.4.7m-.3 1c-1.1 0-2.4 1.7-2.9 2s-.2.6-.2.6a7 7 0 0 0 .7 3.1h.8q.8 0 1.7-.7a4 4 0 0 1 2-.8 6 6 0 0 0 2.3-.9c1-.5-.5-1.4-1.6-2.3a4 4 0 0 0-2.8-1m-15 3.9q1.5-.3 1.4-1 0-1-1.6-1c-2.8.3-5.5-.7-6-3.5 0 0-1 1.7.8 3.7 1.7 1.9 4.6 1.9 5.5 1.8zm-9.4 43.7.5.5h4.3q.9-.5.7-1.3l1-5c.7-2.8 1.7-4.8 2.1-6 .4-1.1-.4-1.5-.7-2.2s-.8-2-.9-2.6-1.1-.6-1.7-.5-.6 1-.7 1.7c0 .9-.5 3.4-.9 5.5-.4 2.2-2.8 5-3.5 5.7q-1 .9-1.7.9-1.1 0-2 .3-1.2.5-1 1.7c0 .6 1 1 1 1l1-.9q.6-.4 1.7-.6 1 0 .5.5c-.3.3-.2.4.3 1.3m-6.5-.2q-.5.1-.7.7h2.8l-.3-.7c-.3-.4-1.2-.2-1.8 0"/>
<path fill="#d4af3a" d="m229 291.4-.9.8h3q.3-.5-.2-1c-.7-.5-1.4 0-1.8.2zm26-1.2q-.4 0-1 .6t-.7 1.2v.2h1.3v-.2q0-.5.7-1.2c.6-.4 0-.6-.2-.6zm2.5 1.3q0-.6-.4-.7c-.3 0-.8.6-1.2.8l-.5.6h2z"/>
<path fill="#d4af3a" d="M269 277.7c1-.6.8-.8.9-1.5q0-1-.3-1.1c-.4-.3-1.8-.9-3-.9s-3.6.3-4.6 1c-.9.5-1.3 2.8-1.3 4q0 1.3.5 1.2c.5-.1.9-.8.9-.8l1.9 2.7c.7 1 .4 1.5 0 2.3-.3.8-1.4 3-2.4 3.5s-1.6 0-2.8-.1q-1.4-.3-2.3.8c-.7.6 0 1.1.3 1.1q.7-.1 1.4.5c.3.4 0 1.3 0 1.8h5.5c.9-.4 1.6-1.7 1.8-2.4.3-.8 1.5-1.9 2-2.4.7-.6.7-1 .7-2.1a5 5 0 0 0-.7-2.7 5 5 0 0 1-.7-2.8c.1-.7 1.3-1.5 2.2-2.1"/>
<path fill="#d4af3a" d="M260 264q.7 1-.5 1.2-1.1.3-3 .2c-1.9-.1-1.1 0-1.2.7a5 5 0 0 1-.8 2.9c-.8 1-1.7.5-2.2.2q-.9-.6-1 .4l-1.2 2.2c-.6 1-1 1-1.4 1-.4-.2-.8-.8-1.2-.8q-.7-.2-1.7-1.9c-.6-1-1 .1-1.4 1q-.5 1.4-1.6 1.4c-.8-.1-1.7-1.6-2.1-2.5q-.6-1.7-1.4-1.7c-.7 0-1.9 1.4-2.6 1.8q-1 .6-1.3-.2-.6-.6-1.3 1.3-.5 1.8.7 2.4c.6.3 1.8-.6 2.7-1.1.8-.6 1.2-.4 2-.4s.8 1.4 1.2 3.4c.4 2.1 1.6 2.4 1.6 2.4s.4.3 1.3-1.2c1-1.6 1-3.2 2-3.2s1 .2 1.8 1.5c1 1.2 1.7 2.1 2.2 1.2s1-2.3 1.6-2.8c.5-.5 1-.6.5.3s-1 1.8.4 1.5c1.2-.4 6-2.4 8-2.8l5-.5 4.4-.3c1 0 1 0 1.1-.8 0-.7-.5-.5-2.4-.5q-2.8 0-4.6.4-2.1.3-1.8-.2c.3-.3.7-.6 2.2-.8l6.4-.4c1.7 0 1.1.4 1 1.6-.1 1 .1 2.7.2 3.7s.2 1.4 1 2.8c1 1.5 4 2.4 5 2.8.9.5.7 0 .7-.8q.1-1.2 1.5-.4 1.4.6 3 1c1 .3 1 .5 1 1l.3 2.5c.1 1 0 1.3-.4 2l-2.1 2.4s-.8 0-2.5.4-2.2 1.2-2.4 1.9.3.9.4 1.1v.9h.4l.6-1c.6-.7 2-.9 3-.4q.9.6.6 1.4h3.9l2-4.2c.5-.9.7-1.5.7-2.8s.2-6.2 0-6.4 0-1.1 0-2.5c-.1-1.4 0-2.4-2.6-2.7-2.5-.2-3.3-1.3-4.2-2.2-.9-.8-.6-2.8-1.3-5.2s-2.4-2.8-5.4-3.4a25 25 0 0 0-7.5-.5c-2.2.2-5 .6-6.6.4s-1.2.3-.7 1.2z"/>
<path fill="#d4af3a" d="m274.5 291.6-.6.6h2.6l-.2-.8q-.7-.6-1.8.2m4.7-.1-1 .7h2.7v-.3c0-.4-.9-.9-1.7-.4M214 293l.6 2.5h82.8l.6-2.5zm55.8-39.6c2.7 1.8 11 1.9 14.9 2.6s3.6 3.8 2.4 5.4c-1.2 1.5-6.7 1.7-7.7 1.8.6.4 1.4 1.6 1.4 1.6l2.2-.2a12 12 0 0 0 6.5-2.2c2-1.7 1.9-4.3.1-6.1s-4.6-2-7.6-2.3l-8.5-1.1q-5-1-4.9-3.8c0-2 1.8-3 3-3.3 1.4-.4 3.4-1 5.3-.3s4 .8 7.6.3 4.4-3 4.4-3-.4.3-2.3.3-3.1-.7-6.5-.8c-3.3 0-4.5 1.2-6 1.7-1.6.6-5.8 1-7 3.7s.1 4 2.8 5.7z"/>
<ellipse cx="248.1" cy="241" fill="#b96b29" rx="1.6" ry="1.5"/>
<path fill="#b96b29" d="M281.6 243.7c-2-.2-4.8.3-4.8.3s.6.4 1.3.4q.9-.1 3.9.3c2 .3 3.5-.6 3.5-.6z"/>
<path fill="#1d5e91" d="M262 282.2q-.6-1-1.2-.2c-.6.5-1.2-1.2-1.3-2.3l.4-4.4s-2.8.5-4.3 1-3 .7-4.2 1q-1.7.6-2.5.4l-3.1-.2c-1.1 0-1 .2-2.1 1.3-1.1 1-1.4 1.7-2.3 3.7q-1 2.8-1.4 6.4-.3 2.4-.7 3.3h13l.5-1.4a3 3 0 0 1 1.5-1.4q.9-.2 1.2-.8.6-.9 1.6-1.4 1.2-.4 3 .2c1 .3 1.4-.5 1.7-.9l1-1.7c.6-.8.2-1.4.2-1.4s-.5-.4-1-1.2m20.8.4q-.1-1-.5-1-.8 0-1.9-.5c-.6-.3-.6 1.5-.6 1.5s-1.5.1-3.2 0c-1.7-.3-3.8-1.8-4.5-2.3-.7-.6-1.5-1.7-1.5-1.7l-1 .8q-1 .6-1 2.5c-.1 1 1.2 2.4 1.3 3.5.1 1-1.2 3.3-1.8 4.1-.4.6-1 2-1.3 2.7h6.2l.7-1c.6-.7 1.7-.9 1.7-.9l.3-.8q.2-1 2.4-1.9c1.6-.6 3.4-.7 3.4-.7s.8-.6 1.3-1.5.2-2 0-2.8"/>
<path fill="#d4af3a" d="M230.6 351.6c1.2.4 3.4-.4 4.5-1.8 1-1.3 1.4-3.3 2.2-4.5l1.4-2s-.2 2-1.2 4.5a17 17 0 0 0-1 7.3c.5 2.8 2.4 3.3 3.5 3q1.3-.7 3.2-3.6l1-2c.7-1.5 1.4-3.8 1.7-3.7.7.1.3 1.7 0 2.8-.6 2.2-1.6 5.4-1.7 7.3-.1 2 .1 5.5 2 5.7 2 .1 3-1.6 4.1-5.6s1.3-5.2 1.8-5.2.5 2.7.5 3.9a20 20 0 0 0 1.7 7.9c1 .8 1.7.7 1.7.7s.8.1 1.7-.7c1-.9 1.7-6.7 1.7-7.9s0-3.9.5-3.9.7 1.3 1.8 5.2c1 4 2 5.7 4 5.6s2.2-3.7 2.1-5.6c0-2-1.1-5.2-1.7-7.4q-.6-3.3 1.7.8l1.4 2.7q1.5 2.4 2.8 3c1.1.3 3-.2 3.5-3 .4-3-1.7-11.2-1.7-11.2l.9 1.4c.8 1.2 1.2 3.2 2.2 4.5 1.1 1.4 3.3 2.2 4.5 1.8s1.3-2.5.4-6.4-4.3-6.1-6.8-9.2c-2.3-2.9-4-8-4.3-8.7a29 29 0 0 0-8.4 6l-6.3 6.6s-1-1.3-6.2-6.7a29 29 0 0 0-8.5-6c-.2.7-2.4 5.6-4.7 8.4-1.1 1.5-2.7 2.6-3.9 4a13 13 0 0 0-2.5 5.6q-1.3 5.8.5 6.4z"/>
<path fill="#d4af3a" d="m230.5 364.8 2.3-2s1-1 .8.8c-.3 1.7-.9 5.3-.5 6.8.4 1.6.6 3 2.5 1.8s2.3-1.9 3.2-2.8c1-1 2-1.4 1.7.1-.2 1.5-1 3.8-.6 6.9q.6 4.6 3.8.9a34 34 0 0 0 3.7-6.3c.6-1.3 1.8-2.4 1.8 0 0 5.1 1.2 12.6 6.8 14.5 5.7-2 6.8-9.4 6.8-14.5 0-2.4 1.2-1.3 1.8 0a35 35 0 0 0 3.7 6.3q3.2 3.8 3.8-1c.4-3-.3-5.3-.6-6.8s.8-1 1.7-.1 1.4 1.6 3.3 2.8 2-.2 2.4-1.8c.4-1.5-.2-5-.5-6.8s.9-.8.9-.8l2.2 2c1.5 1.3 3.6 1.4 3.6 1.4s1-5.3-1.3-8.5l-3-4.3s-1.3 0-2.2-.4l-2-1.1v3l3.4 3.3c.7.8 2 2.1 0 .9l-3.3-2.6s-.4 2.7-1.2 3.4-1.9.8-2.3.6l-.5-.2-.1 1s1.7 3.2 2 4.5q.2 1.8-1-.5c-.8-1.7-2.3-3.8-3-5-.7-1-1.7-1.8-1.6-.4.1 1.5-.5 4.1-1 5l-.8 1.4 1.2 4.4c.1.6 0 1.4-.6.5s-2.2-4.5-2.2-4.5-2.3 0-3-.7c-.6-.6-1.6-3.1-1.6-3.1s-.9 3.1-1.6 4.1-2.6 2-2.6 2l-.2 6.5q0 1.4-.6 1.4t-.6-1.4l-.2-6.4s-1.8-1-2.5-2c-.8-1-1.7-4.2-1.7-4.2s-1 2.5-1.6 3.1-3 .7-3 .7-1.6 3.7-2.2 4.5c-.6 1-.7.1-.5-.6 0-.6 1.1-4.3 1.1-4.3l-.8-1.3a13 13 0 0 1-1-5c.1-1.5-1-.8-1.6.4-.7 1.1-2.2 3.2-3 4.9q-1.1 2.4-.9.5c.3-1.3 2-4.5 2-4.5l-.2-1-.4.2c-.5.2-1.6 0-2.4-.6s-1.1-3.4-1.1-3.4l-3.4 2.6c-2 1.2-.6 0 0-.9l3.4-3.3v-3s-1 .7-2 1l-2.2.5-3 4.3c-2.2 3.2-1.3 8.5-1.3 8.5s2.1 0 3.6-1.4m27.5 35.7-2-2.5q.3 0-1.9 2.5c-1.4 1.7-4.8 4.3-4.8 4.3A17 17 0 0 0 256 417c2.2-1.4 3.5-3.3 4.9-5.3 1.6-2.4 1.9-6.9 1.9-6.9s-3.4-2.6-4.8-4.3"/>
<path fill="#b96b29" d="M255.3 406c.1 1 0 3.6.7 3.6s.5-2.6.7-3.6l.1-3.5h-1.7s0 2.4.2 3.5"/>
<path fill="#d4af3a" d="M256 161.4a54 54 0 0 1-17-2.2l-4.2 1a55 55 0 0 0 21.2 3.4c9.3-.1 14.7-1 21.1-3.5l-4.2-.9a54 54 0 0 1-17 2.2zm21.9-5-4-2.7c-.7-.4-.1-1.4 0-1.8a18 18 0 0 1 5-1.7c1.6-.4 1.4-.8 1.4-.8s-1-1.1-7.2-1.8-17.1-.5-17.1-.5-11-.2-17.1.5-7.2 1.8-7.2 1.8-.3.4 1.4.8 4.8 1.3 5 1.7c0 .4.6 1.4 0 1.8l-4 2.7c-.5.4-.5 1 .2 1 .8 0 .8 0 3-.4a101 101 0 0 1 37.5 0c2 .5 2.1.5 2.9.4s.7-.6.2-1"/>
<path fill="#d4af3a" d="M270.8 159c-4.2-.5-7-1-14.8-1s-10.6.5-14.8 1c2.6 1 7 1.6 14.8 1.6s12.2-.5 14.8-1.6m-19.4-61 3.5-.4-.2 3 3.2-.2-.3-2.9 3.5.5.2-2.8-3.7.3.6-3h-4l.7 3-3.5-.3zm10.4 7.2h-4.4v-3.7l-1.3-.1H255v3.8h-4.5a5 5 0 0 0-.2 2.1H262v-.6l-.3-1.5z"/>
<path fill="#1d5e91" d="M256.1 112c2.6 0 4.8-1.5 5.6-3.6h-11.2c.8 2.1 3 3.6 5.6 3.6m2.4-10.2v2.6h3a6 6 0 0 0-3-2.6m-4.8 0a6 6 0 0 0-2.9 2.6h3z"/>
<path fill="#b96b29" d="m259.5 149.3-3.5-1.6-3.5 1.8c-1.3.6-1.1.4-1.5 1.4q-.5 1.1.6 1.4l4.4 2.3 4.9-2.3c.9-.4.7-.9.5-1.6q-.3-.8-2-1.4z"/>
<path fill="#d4af3a" d="m259.9 150.4-4-1.8-3.7 1.8 3.9 2zm-4-15s-.3 1.4-.7 2q-.7.7-.6 1.8c.1.7.9 1.1 1.4 1.1s1.2-.4 1.4-1.1q0-1-.7-1.8c-.4-.6-.7-2-.7-2z"/>
<circle cx="256" cy="141.7" r=".7" fill="#d4af3a"/>
<path fill="#d4af3a" d="M253.4 138s1.3-1.4 1.7-2.3l.9-1.6.8 1.6c.5.9 1.7 2.3 1.7 2.3s1.8-1 2.8-4.3c1-3.4-.3-7-.7-10.8l-.8-10.4a8 8 0 0 1-3.8 1.1 8 8 0 0 1-3.8-1.1s-.4 6.6-.9 10.4-1.7 7.4-.7 10.8 2.8 4.3 2.8 4.3"/>
<ellipse cx="256" cy="130.4" fill="#b96b29" rx="3" ry="2.9"/>
<ellipse cx="256" cy="123.5" fill="#b96b29" rx="2.9" ry="2.8"/>
<ellipse cx="256" cy="117" fill="#b96b29" rx="2.8" ry="2.7"/>
<circle cx="256" cy="129.9" r="1.6" fill="#d4af3a"/>
<ellipse cx="256" cy="123" fill="#d4af3a" rx="1.6" ry="1.5"/>
<ellipse cx="256" cy="116.5" fill="#d4af3a" rx="1.4" ry="1.3"/>
<path fill="#b96b29" d="M255.9 358.5q.3 0 .2-1.4l.4-4.3c.2-1.2-.3-1.2-.7-1-.3 0-.5 1-.5 1.5l.2 4.4q0 .8.4.8"/>
<path fill="#d4af3a" d="M224.4 383c1.4-.8 2.6-1.7 5.6-3.8a13 13 0 0 0 4.2-4.2h-1q-1.1-.2-1.2-1.7l.2-4.4c.2-2-.3-3.5-.3-3.5s-1.3 1.2-3.3 2.3-2.8.5-2.8.5l-.5-2-2 4c-1.2 2-4.5 4.4-6.8 6.2a31 31 0 0 1-6.9 4.2c-1.9.7-.8 1.6-.8 1.6s2 2 7 2.8c5 1 7.3-1 8.6-2"/>
<path fill="#d4af3a" d="M241.9 381.1c-1.8-.2-2.9-.8-3.2-2.8-.2-1.2 0-6 0-6l-1.2 1.2c-.7.9-2.2 1-2.2 1s-.2.7-.9 1.8a10 10 0 0 1-3.2 3.5l-5 3.4a30 30 0 0 1-4.5 3.3 8 8 0 0 0-3.8 5q-1 3.6 1.8 3.2c2-.2 2.2 0 5.4-.8s7.7-4.4 11-6.5c3.4-2 5.8-6.3 5.8-6.3"/>
<path fill="#d4af3a" d="M250.7 384s-1.3-1.7-1.8-3.6l-1.5-5.8-1.5 3.5c-1 2.3-3 2.9-3 2.9s0 1-1.5 2.9a23 23 0 0 1-6 5 23 23 0 0 0-7 5.6c-1.6 2.3-1.7 4.7-1 5.7.8 1 1.9-.2 3.4-1 1.5-.7 5.9-1.4 12-4.6s7.9-10.6 7.9-10.6"/>
<path fill="#d4af3a" d="M253.3 397.5c1.7-2.5 2-8.5 2-8.5l-1.5-1-2.3-3.1-2 4a16 16 0 0 1-4 4.6c-2.2 2.1-5.2 4.1-6.5 6.3s-.6 6.7.2 8.1c.9 1.5 2 1 3.3-.2l3.9-3.4c2.2-1.9 5.3-4.3 7-6.8z"/>
<path fill="#b96b29" d="M233.6 347a23 23 0 0 1 2.7-5l1.3-2.5s-2.1 2-3 3.4a15 15 0 0 0-1.7 4.2q-.6 2.4.7 0zm8.5 1.6.5-2s.4-1.2.2-1.3q-.5 0-1.7 2.7c-.6 2-1.5 4.8-1.3 5 .1 0 2-3.2 2.3-4.4m6.7 5.4.4-1s.7-2.1.7-2.6-.2-1-.8.2q-.7 1.8-.7 2.7c-.2.5-.8 2.2-.6 2.4s.6-.9 1-1.7M228 374.4l-.9-.5-1.3 1.5-2.2 2a9 9 0 0 1-2.8 2.4q-2.1 1-1.7 1.1c.3.3 1.2-.3 1.2-.3s.4 0 2.1-1 2-1.6 3.1-2.5c1.1-1 2.5-2.7 2.5-2.7m7.3 8.7c.7-.7 1.3-1.2 1-1.5q-.6-.6-1.4.1l-3 2.8-2.2 1.7c-.7.6-2.4 1.5-3.2 1.9-.8.5 0 .5 0 .5l1.3-.5 2.6-1.1 1.8-1.1zm8.4 5.8c.6-1.1 0-.8-1-.4-.8.3-2.8 2.5-4 3.3l-3.1 1.7c-1 .5-1 .7-1 1q.2.3.9 0l1.8-1 1-.2q.8-.1 2.6-1.7 2-1.3 2.8-2.7m4 7.7c-1.6.9-4.1 2.9-4.8 4.2q-1.1 2.1-.9 2.6c.2.2 1-.3 1.3-.8l1-1.4s2.5-2.5 3.4-3a9 9 0 0 0 2.9-3.3c.3-1 0-1.5 0-1.5s-1.5 2.3-3 3.2z"/>
<path fill="#c52126" d="M208 181.3a24 24 0 0 0-19-1.6q-3.4 1-6 .2c-2.8-.7-5-3.2-5.7-6.3 0 0-2.6 3.3 0 6.8a8 8 0 0 0 4.6 3.2c5.5 1 10.8-2.3 15.3-2.6 4.4-.2 7.8 2.5 7.8 2.5s1 .9 2.5 0q2.3-1.2.5-2.3z"/>
<path fill="#d4af3a" d="m192.3 338.7-1.3-.2-2.8-.9q-.9-.4-1.7.3c-.8.7-4.4 2.9-4.4 2.9h6.5c2.2 0 3.7-2.2 3.7-2.2z"/>
<path fill="#d4af3a" d="M210.5 337.6v-.7l-.8-1.8s-1 .6-2 1.6c-1.3 1.3-2.6 3.2-3.2 4-1.1 1.3-2.6 2.6-1.3.4s1.7-3.2 2.7-4.6l1.7-2.2-3.7-.6c-2.7-.3-4.4-1.4-5.6-2.1l-3.5-2s-4.6 2-6.3 2.4c-1.8.5-2.8.7-1.1 1.5s3.1 1.1 4 1.6c.7.6 3.3 1.4 4.8 1.4 1.6 0 4.2 0 3.2 1q-1.6 1.8-3.3 6.4a22 22 0 0 0-1.2 7.4l.2 2.7s1.9-2.1 7-5.3 7-4.3 7.9-4.7 1.4-.4 1.2-1.6z"/>
<path fill="#d4af3a" d="m230 322.1-1-.4h-.2c-.6.4-3.7 2.4-5 4.5s-2.5 2.2-1.1 0a19 19 0 0 1 3.5-4.4l1-1a56 56 0 0 1-7.8-4.5c-.3.7-1 2-1.7 2.7-1 1.1-3.5 2.1-3.5 2.1s2-2 2.6-3.5l.8-2.6a34 34 0 0 1-5.1-4.9 19 19 0 0 1-4.4 7.9c-4 4.5-12 6.7-12 6.7s-2.8.3-.5 1c2.2.5 3.8 1.5 8.3.8 4.4-.8 4.7-1 5.3-1.2s2.2 0 0 .8-7.4 1.6-9.3 1.4c-2-.1-4.7-.6-1.9 1s4 2 5.7 2 2.5.6 4.2.6 2.1-.3 3.3-.6q1.9-.2 2 .7c.1.9-1.6 1.8-1.6 5q0 5 .6 6.4c.3 1 1 2.5 1.5 3.4.5 1 .7 1.8 1.1 0 .5-2 1.6-4.7 2-6.6s.7-1.9.8-2.9 1.2-1.6.8.1l-1.4 5.5c-.4 1.5-1.2 4.4.2 2.1 1.5-2.2 6.8-8.3 7.7-9.3q1.4-1.6 2.3-3c.6-.9 2.5-2 3.4-3.4.8-1.2 3.2-3.5 4.2-4.3L230 322zm-74.2-137.7c.1 3.3 3.8 9.6 5.8 13 2.1 3.3 2.7 4.6 2.3 4.8-.3.3-1.2-1.5-2.3-3l-5.2-7.6c-1.9-2.6-4.6-9-5-9.5-.3-.6-.5-1-1.4-.2-1 .7-2.7 7.6-.6 12.3 2.2 4.8 5.4 8.2 6.7 9.8 1.4 1.7 3.2 4.2 2.9 4.5s-.8-.5-2.4-2.2l-6.2-6.8a49 49 0 0 1-4-6.5q-.5-.7-1-.1c-.5.6-1.4 2-1 5.7.5 5.2 3.3 8.2 5.7 10.9 2.4 2.6 4.8 4.8 4.5 5.2-.4.3-2.9-1.9-4.6-3.5s-6-5.7-6.8-6.9c-1-1.1-2.3-.6-2.2 4 0 4.4 5 9 7 11.5 2.2 2.5 4.7 4 4.5 4.5s-2.3-.8-3.6-1.7c-1.4-1-6.4-4.2-7.4-5-1-.6-1.5.7-.3 5 1.3 4.3 5 7.7 7.2 9.3s5.8 3.5 5.6 4q-.5.6-3.8-1.1c-2-1-4.5-2-6.3-2.5s-1.8.8-1.8.8-.4 3.7 2.3 6.6a14 14 0 0 0 8.5 4.6c2.8.5 3.3.6 3.2 1q0 .6-1 .5c-.7 0-3.3.3-5.7.3s-3.7.6-3.7 1.7c0 1-.1 2.5 3.5 4.9 3.7 2.4 7.7 2.8 9.5 3.1s1.7.5 1.4 1-1.2.5-3.9.8c-2.7.2-4.3 1.3-4.7 2-.4.8.2 2.9 2.4 4.4q3.1 2.2 9.4 1.7c4.3-.3 5.3-.3 5.5.4.1.7-1.3.9-3.4 1.3-2.2.3-3.8.3-5 1.3s-.3 3 1.7 4.6c2.1 1.5 7.2 1.5 9.4 1.3 2.1-.1 5.4-.9 5.6-.3s-1.5 1.4-3.8 2.2-2.8 1.3-3.2 2.2-.1 1.8 2.5 2.8c2.7 1 6.5 1 8.5 0q3-1.3 3.5-.8.3.4-.5 1.2a10 10 0 0 1-4.3 2.4c-2.2.6-2.5.6-3.2 2.4s1.2 3.2 5.2 3.3c4 .2 9.3-1.4 11-2.4q2.4-1.4 2.7-.9.6.5-.7 1.2c-.8.7-3.5 2.6-4.9 3s-1.5 1.3-1.5 2.3-.4 2.8 3.2 3.6 7.8.2 11.6-2.5a9 9 0 0 0 2.4-2.2c-.3-3.7-.1-6.2.3-8.5a14 14 0 0 1-3.6-.9c-2.2-.8-3-2.8-3.5-4q-.8-1.3-2.1-.7-1.2.6-3 .6a8 8 0 0 1-5.6-3c-1.8-2.2-1.5-4.3-1.6-5.4 0-1-1.7-.3-2.6-.3s-3.3-.3-5.6-1.9-2.3-3.7-2.5-4.6-1.2-1.4-1.9-1.7-1.8-.6-3.4-1.8-2.1-3.8-2.2-4.7c0-.8-1-1-4.2-2.6s-3-5-2.8-6.4c.1-1.4 1.6-2.5 1.6-3s-1-.6-2.1-1.3c-1-.6-3.8-1.7-3.9-5.7 0-4 2.6-4.4 2.6-4.4l-1.6-.9c-1-.6-4-2.6-4-5s1.1-3.4 1.9-4 3-1.1 3-1.1-1.4-1-2-2.8-.8-4.1.6-5.8c1.3-1.6 3.6-1.6 4.8-1.6s2.3.7 2.7.3.5-.6.1-1.6-.5-3.2-.2-4c.4-.8.5-2.3 3.5-3.3s6.3 1 7.3 1.6.6-.8.6-1.8-2-3.5-4.4-7a78 78 0 0 1-10.8-25.1c-.4-1.8-1-1-1-1s-.8.3-2.4 4.5a27 27 0 0 0-1.8 11.2"/>
<path fill="#d4af3a" d="M209.6 257.5a8 8 0 0 1-6-3.8c-1.9-2.7-4.6-3.3-9.4-3.2s-7-2.2-8.2-5.5c-1.2-3.4.2-7.1 5-9.5 5-2.4 7.1-5.1 7.3-9.2.2-4.2-4.9-9.3-9.9-15.1s-9-14-9-14-1.5 1.9-1.5 3.8c0 2 .8 4 0 4.5-.7.6-1-.8-4-2s-5.3-1.5-7.1.6 0 5.8 1.7 7.5 3 3 2.5 3.7-3-1-3.7-1.6c-.9-.5-2.8-2.6-5.7-2.4-2.8.3-3.8 1.1-4 3.5-.4 2.4 2.5 4.6 4.3 5.5q2.9 1.4 2.1 2c-.4.5-.9 0-1.7-.2s-3.1-.5-4.6 0-3.9 2.6-1.7 5c2.2 2.6 6.8 3.5 7.8 3.7s1.3-.1 1.4.6c.1.8-1.9.8-4.4.9s-3.2 2.4-2.8 4.3c.3 1.8 4.5 3.8 6.8 4.2s3 0 3.3.7q.3.8-1.6 1.2c-1.3.4-3.8.7-4.2 2.7s-.6 3 1.4 4.7c1.9 1.8 5 1.5 6 1.3s2-1.2 2.9-.3c.9 1-.8 1.7-2.4 2.8s-1.5 3.2 1.2 4.7a8 8 0 0 0 6.4.5c1.1-.3 2.6-2 3.3-1 .8 1.1-2.3 1.5-3.6 3-1.2 1.6-.8 4.2 1.8 5.8s6.8.4 7.5-.2q1.3-1.2 2.2-.4c.6.6-.2 2-.8 3.3-.5 1.3-1.5 5 2.3 6.6 3.9 1.4 7.4-1.9 8-2.5q1-1 1.8-.5c.6.3.3.8-.1 1.5a4 4 0 0 0 1.3 5.3 7 7 0 0 0 5 1q1-3 2-7.6h-.5c-.4 0-.4.5-.4.7q0 .4-.3.4c-.3 0-.3.2-.8.6q-.8.6-1.2.5c-.2-.2 1-.8 1.3-1.2q.8-.7 1-1.5 0-.6 1-1.3l.5-.5q.7-3.2 1.3-7.7l.4-5.9zM131.1 200c4 8.3 9.9 13 9.9 13s-.8-2.1-1.1-3.5-.1-3-.1-3-1.5-2-4.1-6c-2.7-4.2-4.2-10.3-4.2-10.3s2.5 6.1 4.5 9.3a50 50 0 0 0 4.2 6s.8-3 1.3-3q.8-.2 1.5.9.8.7.9.5l-1.7-3q-.9-1-1.6-2.3c-1.6-2.4-4.9-7-6.6-11.6s-5.4-16.1-6-19.5-1.3-1.6-2 1a29 29 0 0 0-.9 11.3 57 57 0 0 0 6 20.2m5.3 22.4 3.8 2.7-.6-1.9-.3-3-3.1-2.7a44 44 0 0 1-6-6.4c-3-3.8-.6-1.5 2 1.3s7.4 6.8 7.4 6.8v-1q0-1.2.7-2c.5-.6.3-.6-2.5-2.7a80 80 0 0 1-18.7-26.1s-1.6 9.5 4.2 20 11.5 13.9 13 15zm30.4 60c3.2-1.2 2.3-4.3 2.3-4.4l-3.7 1.3c-1.8.7-6.2 2-8 2s-1.7-.2-1.8-.4c0-.3 1.4 0 3.9-.5s6.9-2.8 7.7-3.3q1.1-.7-.5-.7a5 5 0 0 1-2.5-1c-.4-.3-1.6.2-4.5.9a53 53 0 0 1-23 .8c-.6-.2-1.4.2.8 3.9s10 5.7 15.4 5.6c5.3 0 10.7-3 13.9-4.1zm-14.3-7.6c5.5 0 7.6-1.6 7.6-1.6s0-.3-.7-1.4c-.8-1.1-.6-3-.6-3l-3.7.5q-3.5.8-8 .2c-2.9-.6-1.6-.9.7-.6 2.4.3 7-.6 8.4-1 1.4-.6.8-1-.2-1.4s-3.6-2-3.6-2-6.8.8-13 .1-10.2-2.3-11.5-2.5-1.2.3-1.2.3-.4.6 1.8 4a18 18 0 0 0 10 6.6 54 54 0 0 0 14 1.8m-1.4-13-.6-1.4c-.3-1.2.6-2 .6-2s-1.3-.4-5.4-1c-4.1-.4-4.4-.8-7-1.3-2.4-.6-4.1-1.3-4.1-1.6q0-.3 2.4.7c1.4.5 7.8 1.2 10.5 1.6q4 .6 4.6.4c.5-.3 2.2-.7 2.2-.7l-2.6-.6a9 9 0 0 1-3.4-1.4c-.8-.5-1-.6-4.4-1s-6.1-1.5-12.2-3.7-10.6-4.8-11.8-5.8-1.2-.3-.8 1c.3 1.2.3 2.8 3.3 6.4s4.6 5.1 12.3 8c7.7 2.8 16.4 2.4 16.4 2.4M128.3 243a43 43 0 0 0 16.5 6.8c.3-.4-.4-2 0-3 .4-1.2 1.1-1.2 2.7-1.3 1.7 0 .6-.4.2-.4l-3.2-.8c-2.7-.7-5.5-1-9.2-2.5-3.8-1.4-5-2.5-4.8-2.7 0-.3 1.7.9 5.2 2.2s7.5 1.8 9.7 2.3.3-.5-.7-1.1-2.5-2.9-2.5-2.9-2.5-.4-6.5-1.6q-5.8-1.7-10.6-4.5l-3.5-2.3-5.5-4c-1.3-1-1-.2-.8.4s1.1 3.5 3.3 6.6 2.8 4 9.7 8.8m-1.2-13.5c6.8 5.3 12.5 7 13.5 7.4s1-.1.7-.7c-.2-.6-.3-2.4-.3-2.4l-5-3c-2.8-1.5-5.7-3.6-7.7-5s-.8-1.2-.2-.7 3.4 2 6 3.8l7 4.1s0-.9.6-1.4c.6-.6 2-.4 2-.4l-1.1-1.4c-.5-.7-2.7-1.4-5.2-2.6a62 62 0 0 1-10.2-7.2c-3.3-2.8-9.7-9.8-10.1-10.3q-.7-.9-.8.2c0 .7-.3 1.2.4 4.4s3.7 9.9 10.4 15.2m23.8-50.3 2.2 3.8 1.5 2.5V184a12 12 0 0 1 0-3.7q.3-1.3.2-2.4-.1-1.4-.4-2c-.2-1-1.2-4.9-1.7-7.7a62 62 0 0 1-.9-10.2c0-3.5.5-1.7.5 0s.2 4.3.8 8.3c.5 4 2.2 10 2.2 10s0-.9.6-2.2l2-4.5c1.1-2.4.4-2 .4-2s-2.4-11.3-3-16.6-1.6-19.4-1.6-19.4c-3.5 3.5-6 12.6-6.5 21.3-.3 6.5.7 12.5 1 15a38 38 0 0 0 2.7 11.3m-7.6 17.8q0-4 2.3-5s-1.4-2.6-2.7-6.3a49 49 0 0 1-2-8.2c-.9-6-1.6-12.3.2-2 1.8 10.2 4 13.8 4 13.8l2.6 4.3a18 18 0 0 1-.3-5.3c0-2 .3-4.8 1-7 0 0-3.2-6.3-5.2-13.9a87 87 0 0 1-3.4-24.1s-3.3 2.4-4.4 14.8a74 74 0 0 0 3.8 28.8l4 10zm35.4 94.5-2-1.3c-1.5-1-1-4-1-4l-1.6 1.1q-1.7 1.3-4 2.3L167 291q-1.4.4 0-.5c.9-.5 1.1-.8 3.2-1.6 2-.8 6.1-4.4 6.1-4.4l-2.6-.1c-1.7-.1-3.9-1-3.9-1s-1.7 1.6-8.5 4.5c-6.8 3-12.6 3.7-12.6 3.7s.7 3.5 4 4.7c2.7 1.1 6.5 1.9 13.8.1a47 47 0 0 0 12.3-4.8zm9 7c.6-2.9-1-4.9-1-4.9l-1.4 1.1c-1 1-2.6 2.7-5.4 4.1-2.9 1.5-4 1.4-1.2-.1s6.9-5.7 6.9-5.7l-1.1-.4c-1-.4-3 .3-10.6 4.9-7.7 4.6-12.2 4.8-12.2 4.8s3.4 4.1 12.7 3.8c9.4-.3 12.7-4.7 13.3-7.6m17.6-3.4s-1 1.5-2.4 2.4c-1.4 1-5 1.5-5 1.5l-1 1.3a15 15 0 0 1-3.9 4.3c-1.6 1.2-4.2 2-4.3 1.7-.2-.3 1.5-1 3-2 1.7-.9 3-2.7 4.2-4 1-1.2-.5-1-.5-1h-3.7c-1.8-.2-1.5 0-2 .8q-.6 1.1-2.6 3.2l-4.7 5.4c-1 1.3-.2 1.6-.2 1.6s.4.8 4.6 1.3c2.5.2 5.3 0 8.2-1.8a25 25 0 0 0 5.6-4.8c5-5.4 4.7-9.9 4.7-9.9m2.2 11.3-1.5 1.9s.7-2.2 1.3-3.3l1-1.4a28 28 0 0 1-1.7-5c-.5 1.5-1.9 5.8-3.7 8.6-2.1 3.5-4 4.7-2.8 5.6q2.2 1.7 6.4-.2a7 7 0 0 0 3.7-5.3l-1.3-2.3q-.5.3-1.3 1.4z"/>
<ellipse cx="206.9" cy="169.2" fill="#d4af3a" rx="1.9" ry="1.8"/>
<path fill="#d4af3a" d="M203 165.9c1.7 0 2.6-.9 4.5-.7a28 28 0 0 1 9.1 3.6 16 16 0 0 0 10.5.9q1.5-.6 3-1.5c4.6-2.5 3.5-7.6 3.5-7.6a18 18 0 0 1-11.4 3.5l1 .8c.6.7 1.8 1.5 1.4 1.8s-2-1.3-3.4-2.3a21 21 0 0 0-7-3.4c-3.4-1-7.4-1.1-10 .3-2.7 1.5-2.9 3.3-2.5 4q.4.7 1.2.6zm49.2 37c-.8-.2-2.2-.5-4-2.2a76 76 0 0 1-6.7-7.3q-.9-1.5 1-2.3c1.2-.5 2-2 2-2s-2.7-1.1-4.5-2.4-6-5.2-6.6-6.1c-.6-1-.3-1.1.5-1.4 1-.3 1.1-1 1.1-1s-2-1.1-3.8-2.6l-4.3-4c-2.7 1.2-7 .7-9.3.3-4.1-.7-7.1-2.4-7.1-2.4l-.7 1.9c-.5 1.3-2.4 2-4.3.7-2-1.2-1.5-4-1.5-4s-1.5-.9-4.7-2q-4.6-1.9-8.2-.2c-2.6 1.2-3.8 4.7-3.8 7.3 0 2.7 1.4 1.6 1.6 1.2s1-2.3 4.1-2.8c3.3-.5 6.2 1 9.8 4s4.5 3.3 6.3 5.2c1.9 1.9.4 3-2.7 4-3.2 1-7.2-1-7.4-1-.6.3 1.4 3.1 4.1 4.2 2.7 1 6.2.3 8.9-.7 1.8-.8 2.2 0 2.7.5 3 3.2 3.7 6 4.2 9.7a4 4 0 0 0 2.8-3.3c.1-.6.3-1.9 1.3-1 1 1 1.9 6.3 1.9 8.7 0 2.5-.8 5.7-.8 5.7l.8-.1q.7-.2 2.5-1.2c1.1-.7 1.2 0 1.2 1.4s.4 3.8 2.2 8.3c1.7 4.4 4.3 5.9 4.3 5.9s.5-.9 1-2.7c.5-1.7 1-5.2 1-8.2s-.9-9.4-.9-9.4 3.7 4.4 7.5 5.5 7.6.5 9.2-1.4.1-2.6-.7-2.8"/>
<path fill="#d4af3a" d="M227.2 214.5s-1.2 2.5-1.8 3.4c-.6 1-.9 1-.9 1s1.2 1 2.8 1.6l3.2.8s-1.9-2.6-2.5-4.4zm22.9-1.5-1-2.3a16 16 0 0 1-8.7-1.8s1.8 3.6 3.5 4.8c1.8 1.3 2 1.3 2 1.3s-1.7.3-4-2c-2.4-2.3-3.5-5-3.5-5s.6 6.2-.4 10l-1.1 4.2s5 1 10.5-.6 6-2 6-2 .6-.2-.3-1.4a30 30 0 0 1-3-5.2"/>
<path fill="#b96b29" d="m240 199.4-1.9-2.4s-.6.6 0 1.4 2.9 3 4.5 4a12 12 0 0 0 3.3 1.2v-.3s-4.2-2-5.9-3.9m-6.8 12.9-.4-9c0-1-.7-.8-.7-.8l-.8.4 1.6 11.3c.2 1.2.8 1.1 1.1 1-.6-.2-.7-2.6-.8-3zm-10.4-33.6c-.6-.6-2-1-.7.5s3.2 3.4 3.7 5 .8 3.5 0 3c-1-.3-1.6-.3-2.5-1.1-1-.8-2.8-2.1-3.5-2.3-.7-.3-2 0-1 1.1a26 26 0 0 0 4.2 3.7l4.2 2.9 2.1 1.4s-1-6.4-2.7-9.1a28 28 0 0 0-3.8-5zm-34.1 105.9c-1.3 1-3 1.4-3 1.4s-.8 0-.8.2 1 0 1.9-.1c1-.2 2.3-1.2 2.8-1.5s1.2-.8 1-1.5c-.3-.6-.7.5-2 1.5zm11 5.9-2.4 2-1.4.6q-1 .6-.9.7 0 .2 1 0c1-.2 2.9-1.8 3.6-2.5q1.1-.8.7-1-.3-.4-.6.2M155 211.1q.7 1 1 1 .4-.3-.5-1.1l-.8-1-1.2-1.6c-.4-.4-1.1-.7-.3.4zm-3.4 7.4 1.5 1.4q.8 1.1 1.2 1c.3-.4-.8-1-1.3-1.7l-1.5-1.5-1.4-.8zm1.3 12c.8 1 2 1.2 2.2.8q.3-.4-.8-1a24 24 0 0 1-4-2.7q-.6-.7-1-.6c0 .1 1.2 1.6 1.6 1.8zm2.4 9.7q.2-.6-.6-.6l-1.7-.2s-1.4-.5-1.5-.3c0 .3 1.2.8 1.7 1a3 3 0 0 1 1.5.4q.5.2.6-.3m1 10.4c-1.4 0-2.7-.3-2.8-.1s.7.6 1.4.8c.8.3 2.2.4 3.2.4s1.1-.1 1-.6c0-.5-1.2-.5-2.7-.5zm11.8 11.5c-.2-.3-.7 0-1.6 0-.8.3-2.2.5-3 .5l-2.5-.1-1 .2q.2.3 3.2.4c1.8.2 3-.2 4-.3q1.3-.3 1-.7zm4.9 8.1q-1.4.5-2.3.4l-2.4.3s-.8.2-.7.5q.2.3 1.1-.1h2.5q1.7-.1 2.7-.6c.8-.1 1.6-.5 1.2-1s-1.2.1-2.1.5m9.5 6.2-2.6 1.9-1.2.7c-.8.4-.7.6-.6.8s.6-.3 1-.5 2-1.2 2.7-1.8 1.7-1 1.6-1.5q-.2-.6-1 .4zm-15.8-79.8 1.8 2.5c1.6 2 .8 0 .4-.5s-2.8-3.9-3.7-4.8c-.8-1-.9-.5-.5.1zm-3.4 10.5q.3-.1-1.3-1.6l-2.8-3.6s-.5-1.1-.7-1 .7 1.9 1.4 2.6c.8.8 1.8 2.3 2.3 2.9q.7 1 1 .7zm4.5 19.7c0-.6-1.9-.5-2.7-.7l-2.3-.5s-1.8-.3-1.8 0q-.1.3 1.3.6l3.5.8c1.1.1 2 .4 2-.2m-4.5-10.7 2.6 1.9c.7.3 1.2.8 1.6.4q.4-.5-1.4-1.1l-1.5-1-1.2-.5c-.5-.3-1.4-.6-.1.3m10.5-7-1.4-1s-1-.8-1-.5c-.3.3 1 1.1 1.5 1.7s1.7 2 2.2 1.7-.5-1.2-1.3-1.9m-6.9 27.5-1.7.4c-.6.2-.7.5.5.5s1.7-.3 3-.5q1.6-.3 1.3-.8t-1.3-.1zm1.9 10c-.1.1.8 0 1.4-.3l2.7-1.1c.8-.2.8-.4.8-.8s-.8-.1-1.8.2-2.2 1.3-2.2 1.3zm12.8 6q1.2-.9.7-1.3-.3-.4-1.3.4l-2 1.5-1.3.6q-1.1.5-1 1c.2.2 1.3 0 1.9-.6.5-.4 2-1.1 3-1.7zm6 8c.3-.4 1.4-.8 1.9-1.1s.8-1.4.4-1.6c-.4-.1-.6.5-1.5 1.1l-3.7 2.5-.7.3q-1 .6.2.5c.7-.1 1-.1 1.9-.7q1.1-.9 1.5-1m9 7.5c.7-.3 1.8-1.3 2.3-1.8s1.2-1.1.8-1.4q-.6-.4-1.4.7c-.7.5-2.3 2.2-2.3 2.2l-1.1.8c-.9.7 0 .6.3.3q.4-.4 1.3-.8z"/>
<path fill="#d4af3a" d="m232.3 150.9-2.7 2.2 3.3 1.4 3.6-2.5z"/>
<ellipse cx="244.4" cy="151.6" fill="#b96b29" rx="3.4" ry="3.3"/>
<ellipse cx="244.4" cy="150.8" fill="#d4af3a" rx="2.1" ry="2"/>
<path fill="#d4af3a" d="M237.3 140.2c.9.3 2 .2 2 .2s.4-1.6-.5-2.3l-2-1.8-.7-.6v2.1c-.1.8.3 2 1.2 2.4"/>
<circle cx="239.1" cy="141.8" r=".6" fill="#d4af3a"/>
<path fill="#d4af3a" d="m234.4 141.2-.8 1s.7 0 1.2.4q1 .7 2 .7a1 1 0 0 0 .9-1.2q-.1-.8-1.6-1.3c-1.1-.4-1.4 0-1.7.4"/>
<path fill="#d4af3a" d="M236.8 145.2c-2.1.2-5-2.2-5-2.2q-.4 3-3.3 3.3h.2s1.7 1 3.7 1.2q1.3.2 2.7-.2a10 10 0 0 0 3.2-1c1.3-.8.9-2.9.9-2.9s-.2 1.6-2.4 1.8m-8.4-2.1c-.9-.4-2.2-1-2.8-1s-1.3-.2-1.2.8 1.2 1.5 2.2 1.6q1.4-.1 2.2-.7 1-.4-.4-.7m-6.7-3.7c-1-.7-1.9-.9-1.9-.9s-.6 1.7-.2 2.7 1 1.3 2 1.3 2.3-.5 1.9-1.2a5 5 0 0 0-1.8-2z"/>
<ellipse cx="222.5" cy="144.1" fill="#d4af3a" rx=".8" ry=".5"/>
<path fill="#d4af3a" d="M224.6 135.8c-.6-2.9-.5-6 2.2-9.9s7.4-5.5 7.4-5.5l1.6-1.9c1.5-1.6 4.5-3.6 4.5-3.6l-5.5 1.5v.1a2.4 2.4 0 0 1-2.4 2.5 2 2 0 0 1-1.4-.4 24 24 0 0 0-5.6 3.1q.4.6.4 1.3a2.4 2.4 0 0 1-2.5 2.5q-.6 0-1-.3l-.4.5-1.7 4a3 3 0 0 1 1 2.1c0 1.4-.9 2.5-2 2.5v1.6q-.2 1.2 1.7 1.7c1.4.4 3.3 2.8 3.3 2.8.1 0 1.1-1.8.4-4.6"/>
<path fill="#d4af3a" d="M233.2 117.6q.8-.7.2-1.4-.8-.5-1.7.1c-.9.6-.6 1-.2 1.4q.8.5 1.7-.1m-9 6.1q.8-.9.2-1.8-.9-.7-1.9.2-1 1-.2 1.7c.4.5 1.3.4 1.9 0z"/>
<circle cx="218.8" cy="131.4" r="1.4" fill="#d4af3a"/>
<path fill="#d4af3a" d="M248.9 112q-1.2.2-4.1 1.7c1.4 0 2.5.9 2.5 2s-1.3 2.4-2.6 2.4a2.4 2.4 0 0 1-2.4-2.3q0-.5.2-.8l-1.9 1.2c-4.4 3-5.5 4.5-7.7 8.5s-3 8.6-2 11.2c1 2.7 4.4 3.2 4.5 3.2q-.2 0-.5-.9l.3-1.7v-2.2s.8-.1 1.3.4l1.5 1.8 1.7 1.6s.7-1 .7-2.2-.8-2.5-1-4.6 1-5.8 3-8.6a24 24 0 0 1 7-5.7 2 2 0 0 0 1.4-2l.3-2c0-1.3-1-1.2-2.2-1"/>
<path fill="#d4af3a" d="M244.5 116.8q1.4-.2 1.5-1.4-.1-1-1.4-1.1-1.3.1-1.4 1.1 0 1.2 1.3 1.4m6.6 26c1 .4 2.2.2 2.8-.2q1-.8 0-1.9a2 2 0 0 0-2.2-.2l-2 1.4s.3.5 1.4.9"/>
<path fill="#d4af3a" d="M254.5 144c-.4.6-.7 1-1.9 1s-2.5 0-3.2-.8l-1.3-1.9h-1.4s-.4 1.9-1 2.4-2.7.6-3.6 0c-.9-.8-1.7-1.6-1.9-1.5q-.3.3 1 1.6c1.3 1.3 1.5 1.3 3 1.3h8.5s1.8.1 2.3-1q.7-1.5.2-1.9-.2-.2-.7.8"/>
<path fill="#d4af3a" d="M241.5 142.6c1.3.7 2 .2 2.5-.3l.5-.9s-1.2-.4-1.8-.8q-.8-.5-1.5-.6l-.7.2s-.2 1.6 1 2.4"/>
<circle cx="235.4" cy="130" r="2.8" fill="#b96b29"/>
<path fill="#b96b29" d="M240.3 120.2c-1.1-.7-2.8-.4-3.7.7s-.8 2.7.4 3.5c1.1.7 2.8.4 3.7-.7s.7-2.7-.4-3.5"/>
<circle cx="235.3" cy="129.6" r="1.6" fill="#d4af3a"/>
<path fill="#d4af3a" d="M239.1 120.5q-1.2-.2-2 .8-.6 1.2 1 1.6c1.8.3 2.6-2 1-2.4m-43.4 219.8s-4.3 2-5.7 2.4l-1.8.4s-3 2.7-5.1 4a19 19 0 0 1-7.6 2.7c-3.5.3-12.9.2-17.1 0-4.2 0-6.8 1-8.7 3s-2.2 5 0 7.2c2.4 2.3 5 1.1 5.8-.2.9-1.4.4-3.2 0-4-.3-.8.8-1 2-1.1a26 26 0 0 1 11.4 2.5c1.4.7.6.8-.4.6l-3.7-1.2a15 15 0 0 0-4.3-1c-1 0-.7.6-.4 1l1 1.7-.4 2 .5 1 .9-1.5c.2-.6 0-1.6.2-2q.4-1 1.8.2a7 7 0 0 1 1.8 3.8c.4 2-.3 3.5-.3 3.5s.9.7 1 1.2c.2.5-.2 2.4-.2 2.4s.6.4 1.2-.7l1.3-3.8c.2-.7.6-2 1.5-1a8 8 0 0 1 1.6 2.7l.2 1s1.9-1.8 2.5-2.9a18 18 0 0 1 3.8-4.2c1.8-1.5 5-4.5 6.5-5.5l6.8-4c1.2-.7 1.4-.7 1.6-2.8s1.3-4 1.8-5.4zm-70.1-78.7c0-.3-.8-1.6-1-2.2s-.8-.3-.8-.3-1.4.8-2.4 1.1q-1.3.3-1.3-.6l-.5-3.4c-.2-1.2-.4-1.1-.9-1l-2.6 1c-.9.5-.6.9-.3 1.4l1.8 3.4c.5.7 0 1-.5 1.3l-2.4.7c-1 .4-1 .6-.9 1l1.2 2.5c.4.6.8.2.8.2s1.1-1 2-1.5c1-.5 1.1-.3 1.4.4l1.5 5.2q.3.9 1.1.4l1.8-.5c.5-.2.7-1.3.7-1.3l-2.6-4.8c-.6-1 0-1.2.4-1.2l2.7-.5c1-.3.8-1 .8-1.3m23.8 103c-.2-.9 0-1.8-.7-2.5a9 9 0 0 1-2-3.2c-.2-1.1.4-1.7.3-2.3q-.3-1-1.9 0c-.8.7-2 1.4-2 3.4s.8 4.5 3.6 5.3c2.9.9 2.8.3 2.7-.7m2.8-1s-.5-.3-.8.2-.2.8.5 2 1.2 1.5 2.1 2 .3-.5.2-.9q0-.5-.3-1-.3-.2-.3-1.4c0-1.2-1.4-.8-1.4-.8z"/>
<path fill="#d4af3a" d="M155.7 373.3c.6-.8 1-1 2.3-1.6s3.7-.9 4.5-1 2.6-1.6 2.3-3.2q-.7-2.2-2.5-2.4-1.6-.5-2.2.2a4 4 0 0 1-2 1.5c-1.2.4-1.3.8-2 1s-.2.6-.8 1.1-1.4 0-2.3-.2-1-.5-2-.5l-3.6-.2h-.7q-.7-.2-1 .7c-.2.6.1 2.2.8 3 .6.8 2.7 1.2 4 1.6l3.3.8c1.3.2 1.4 0 2-.8zm13 1.3c1.1-.7.5-2 .2-2.8s-1.8-1-2.6-1-1.2 0-1.7.4q-.5.6-2.1 1.3c-1.2.3-2.1.8-3.7 1.4s-2.1 1.7-2.8 2.2c-.7.4-2 0-2 0s-1-.3-1.3 0q-.4.4.6 1.5c1 1.1 1.9.6 3.3 1s1.6.1 2.2-.2l1.9-1.8c1-.6 3-.7 4-.8 1.1-.1 3-.4 4-1.2m0-4.9c.5.5 1 1 1.5.3s1-2.1.6-3l-1-1.6-1.2 3.1c-.4.7-.3.8.1 1.2m-4.3-7.4c0-1.4-1-3-1-3s-.3 1.2-.9 2.3l-1.2 1.7 2 .4c.5 0 1.1 0 1.1-1.4m-8 3q2.1-.6 3-1.5c.5-.5 1-2 .5-2.7s-1.5-1.6-2-1.6h-.7s-.5 1.4-1 2.1-1.3 1.2-1.3 2.1 0 1.9 1.5 1.6m3-6.8q0-1.1-.7-2l-.8-.8-.2 1.7-.2.7.8.6c.4.1 1 .5 1-.2zM141 320.4l1-6.3c-.7-6.3-4-12.3-6.6-16.5a79 79 0 0 1-6.5-14 40 40 0 0 1-2-10.7c0-2.5 1-3.8 1-3.8s.5-.6 0-1c-.3-.5-1 0-1.7.6a8 8 0 0 0-1.6 2.8l-.3 1.2s-1.4-.3-3.5-.2q-3.2.2-3.3.8c0 .5.3.9 1.9 1.5s2.5 1 3.3 2c.8.8 2.8 5.5 3.4 7.7a109 109 0 0 1 2.4 13 52 52 0 0 0 4.4 15.3c.7 1.6 4.7 9.1 8.1 7.6m4 11.1c-1.5.8-1.5 6.8-.8 10 .6 3 3.7 6.9 4.6 8 1 1 1.1 1 1.6.7l.7-.3s1.3-.4 2.3-1.2.6-2.6.4-4.3-1.4-5-3.8-9-3.6-4.7-5-3.9m1.5-4.8c0-2-1.7-3.4-3.6-3.4q-1.5 0-2.2.6c-2 1.8-.8 6.3 2 6.3 1 0 2.3-.2 3-1 .7-.5.9-1.6.9-2.5zm28.3 56.2a13 13 0 0 1-5.7-4.4c-.3-.8-.3-.8-1.4-.5s-2.7.4-3.8.4c-1.2 0 .1 1.4 1 2.6.7 1.3.5 5.4.5 5.4l-.2 1q-.2.9.3 1.2.3.2 1.3-1.1c.8-1 2.3-3.5 2.3-3.5l1.6.3c1.3.3 3.3.2 4.4-.1 1-.4.5-1.1-.2-1.3z"/>
<path fill="#d4af3a" d="M169.8 386.1q-.7-.1-1 .3l-.7 1.4q-.1.6.3 1l.7.6s.5.5 1 .7.5-.5.5-1l.3-2q-.1-.9-1-1z"/>
<path fill="#1d5e91" d="M378.6 345.8c-1.4-.3-2-.9-2-.9s-.5 0-1 .8-2.1 2-2.1 2-2.4.6-2.8 2.2.9 2.3 2 2c1.2-.1 4-.7 5.2-2.2 1.2-1.4 1.4-2.3 1.7-2.8.2-.6.3-.8-1-1.1m3.8-13.2c-1-3.7-3.3-8.3-7.9-10.6l-2.2 8c-.5 2-.5 1.9 1 2.2l7 1.7c1 .3 2.6 1 2-1.4zm-22-3.5c1.2.2 1-.2 1.4-1.7l2.3-8.7s-4.8-.2-9 1.8-6.1 5.8-6.1 5.8 1 .5 4 1.1zm-11.2 10-2.3-.5s1 4.2 2.8 6.5 3.8 3.4 7.5 3.5c3.7 0 6.5-2 7-3.6l1.2-2.8s-6.7-.9-9.5-1.5q-4.1-1.1-6.7-1.6"/>
<path fill="#d4af3a" d="m348 336.5 16.5 3.3q1.9.5 2.5.2c.3-.4.5-.6 1.7-1s1.1-1.7 1.1-1.7l.5-2.3c0-1.2.7-1.5 2 0 1.4 1.3 1.7 3.4 2 5.3.2 2 3.3 2.5 4.5 2.7s1.8-.2 2.5-1.3a16 16 0 0 0 1.3-5c.2-1.4 0-1.3-1.5-1.7l-9.9-2.3c-1.2-.3-1-1.1-.8-2l3-9.9a21 21 0 0 0-8.1-2.4s-2 9.2-2.5 10.6-1.3 1.4-2.7 1.2l-12-2.8-1 2a17 17 0 0 0-1.1 5.6c0 1.2.4 1.2 2 1.5"/>
<path fill="#d4af3a" d="M384.3 344.8c-1.4-.9-2.4-1.2-3.2.6-.7 1.7-.8 4.2-2.6 6s-4.4 3.6-7 3.4-4-.8-3.4-3.7 4.5-3.9 5.5-5.4a3.4 3.4 0 0 0-.7-4.5c-1.4-1.2-3.3-1.4-3.9-1a8 8 0 0 0-2.9 3.9c-.8 2.2-1.3 4-3 5.4a13 13 0 0 1-9.5 2c-3.4-.8-4.7-1.8-6-5.2s-2.7-9-3.4-9.6-1.6-1-3 0c-1.5 1-1.6 2.6-1.8 3.8s0 2.6 1.5 4.4c1.4 1.8 3 3.5 3.5 5.4.6 1.8 2.2 4.6-.1 4.7s-3.4.2-5.1-1.5c-1.8-1.7-8.7-6.4-10.6-8l-2.7-2q-4.8-.1-9.6-3.5s1.9 4.7 2.2 6.5q.5 2.6.3 2.7l9.7 5a177 177 0 0 1 11 6c2.7 1.8 5 4.3 9.7 3.3 4.8-1 14.5-3.9 17.6-4.8 3.1-1 11.7-3 14.9-5.3s3.5-2 4-4.2q.8-3.1-1.4-4.3zm-18.4-33 2.4-.3c1.6-.2 1 1.4.7 2q-.3 1-1.4 2.9c-.7 1.1.2 1.3.7 1.5l2.4.6c1.3.3 1-.6 1.1-1.1 0-.5.1-3 .3-4 0-1 1-.7 1.3-.5l1.8 1q1 .3 1-.4l.8-2.2c.3-1-.3-.8-1-.8h-2.6c-1 0-.4-1-.4-1l1.4-2.3c.6-1 .4-1.1-.2-1.3l-3-.7q-.8-.2-.8.3v2.9q.2 1-1 1.1c-1 0-1.5-.5-2.3-1.3s-1-.5-1.1-.2l-.8 3.3q-.2.9.7.5m5.2 23.3s-.1 1.8-.3 2.4c-.2.7-.6 1.6.1 1.9q1.2.6 2 .4c.4 0 .2-1 0-1.7a7 7 0 0 0-1.3-2.4zm11.2 7.3q.5.8 1.1.9.8.3 1-.6a3 3 0 0 0-.5-2.4c-.6-.7-.9-1.3-1.2-.2q-.7 1.7-.4 2.3m-39.1-7.4 1.5.4s0-1.7.3-3c.2-1.2.7-2 .3-2q-.6 0-1 .9c-.4.5-1.3.6-1.6 2.1-.3 1.4.2 1.5.5 1.6"/>
<circle cx="350.8" cy="331.9" r="2.5" fill="#b96b29"/>
<circle cx="357.8" cy="333.6" r="2.5" fill="#b96b29"/>
<circle cx="378.3" cy="338" r="2.5" fill="#b96b29"/>
<circle cx="364.9" cy="335.3" r="2.5" fill="#b96b29"/>
<circle cx="366.7" cy="329.2" r="2.5" fill="#b96b29"/>
<circle cx="368.5" cy="323.1" r="2.5" fill="#b96b29"/>
<circle cx="351" cy="331.7" r="1.2" fill="#d4af3a"/>
<circle cx="358" cy="333.4" r="1.3" fill="#d4af3a"/>
<circle cx="378.5" cy="337.7" r="1.3" fill="#d4af3a"/>
<circle cx="365.1" cy="335.1" r="1.3" fill="#d4af3a"/>
<circle cx="366.9" cy="328.9" r="1.3" fill="#d4af3a"/>
<circle cx="368.7" cy="322.8" r="1.3" fill="#d4af3a"/>
<path fill="#d4af3a" d="M287.5 383c-1.4-.8-2.5-1.7-5.6-3.8s-4.1-4.2-4.1-4.2h1q1-.2 1.2-1.7l-.3-4.4c-.2-2 .3-3.5.3-3.5s1.3 1.2 3.3 2.3 2.8.5 2.8.5l.5-2 2 4c1.3 2 4.5 4.4 6.9 6.2 2.3 1.7 4.9 3.6 6.8 4.2 2 .7.8 1.6.8 1.6s-2 2-6.9 2.8c-5 1-7.4-1-8.7-2"/>
<path fill="#d4af3a" d="M270 381.1c1.8-.2 3-.8 3.3-2.8.2-1.2-.1-6-.1-6l1.2 1.2c.7.9 2.2 1 2.2 1s.2.7 1 1.8a10 10 0 0 0 3.2 3.5l5 3.4a30 30 0 0 0 4.5 3.3 8 8 0 0 1 3.8 5q1 3.6-1.9 3.2c-2-.2-2.1 0-5.3-.8s-7.8-4.4-11-6.5c-3.4-2-5.8-6.3-5.8-6.3z"/>
<path fill="#d4af3a" d="M261.2 384s1.4-1.7 1.9-3.6l1.4-5.8 1.5 3.5c1.1 2.3 3 2.9 3 2.9s0 1 1.6 2.9a23 23 0 0 0 6 5c2.3 1.4 5.3 3.3 6.9 5.6s1.8 4.7 1 5.7-1.8-.2-3.4-1c-1.5-.7-5.9-1.4-11.9-4.6s-8-10.6-8-10.6"/>
<path fill="#d4af3a" d="M258.6 397.5c-1.6-2.5-1.9-8.5-1.9-8.5l1.4-1c.8-.8 2.4-3.1 2.4-3.1l2 4c1.1 1.9 1.6 2.6 3.9 4.6 2.2 2.1 5.3 4.1 6.6 6.3s.6 6.7-.3 8.1c-.8 1.5-2 1-3.3-.2l-3.8-3.4c-2.3-1.9-5.3-4.3-7-6.8"/>
<path fill="#b96b29" d="M278.4 347c-1-1.8-1.9-4-2.7-5l-1.4-2.5s2.2 2 3 3.4a15 15 0 0 1 1.8 4.2q.5 2.4-.7 0zm-8.5 1.6-.6-2s-.3-1.2-.1-1.3q.5 0 1.7 2.7c.6 2 1.5 4.8 1.3 5-.2 0-2-3.2-2.3-4.4M263 354l-.4-1s-.6-2.1-.6-2.6.2-1 .7.2q.8 1.8.8 2.7c.1.5.8 2.2.6 2.4s-.7-.9-1-1.7zm20.9 20.4.9-.5 1.3 1.5q1.3 1 2.2 2a9 9 0 0 0 2.9 2.4q2 1 1.6 1.1c-.2.3-1.2-.3-1.2-.3s-.4 0-2-1-2.1-1.6-3.2-2.5zm-7.3 8.7c-.6-.7-1.3-1.2-1-1.5q.6-.6 1.5.1l3 2.8 2.1 1.7c.8.6 2.4 1.5 3.2 1.9.9.5.1.5.1.5l-1.3-.5-2.6-1.1-1.8-1.1zm-8.4 5.8c-.5-1.1 0-.8 1-.4.9.3 2.9 2.5 4 3.3l3.2 1.7c1.4.7 1 .7 1 1q-.2.3-1 0l-1.7-1-1.1-.2q-.8-.1-2.6-1.7-1.8-1.3-2.8-2.7m-3.9 7.7c1.5.9 4 2.9 4.7 4.2q1.2 2.1 1 2.6c-.3.2-1-.3-1.3-.8l-1-1.4s-2.6-2.5-3.5-3a9 9 0 0 1-2.8-3.3c-.4-1-.1-1.5-.1-1.5s1.6 2.3 3 3.2"/>
<path fill="#c52126" d="M304 181.3a24 24 0 0 1 19-1.6q3.3 1 6 .2a8 8 0 0 0 5.6-6.3s2.6 3.3 0 6.8a8 8 0 0 1-4.6 3.2c-5.4 1-10.8-2.3-15.3-2.6-4.3-.2-7.7 2.5-7.7 2.5s-1 .9-2.5 0q-2.4-1.2-.5-2.3z"/>
<path fill="#d4af3a" d="m319.6 338.7 1.3-.2c.8-.1 2.4-.6 2.8-.9q1-.4 1.7.3c.5.5 4.4 2.9 4.4 2.9h-6.4c-2.3 0-3.8-2.2-3.8-2.2z"/>
<path fill="#d4af3a" d="M301.4 337.6v-.7l.9-1.8s1 .6 2 1.6c1.2 1.3 2.6 3.2 3.2 4 1 1.3 2.6 2.6 1.3.4s-1.7-3.2-2.8-4.6l-1.7-2.2 3.7-.6c2.7-.3 4.4-1.4 5.6-2.1l3.5-2s4.6 2 6.4 2.4c1.7.5 2.7.7 1 1.5-1.6.9-3 1.1-3.9 1.6a11 11 0 0 1-4.9 1.4c-1.5 0-4.2 0-3.1 1q1.6 1.8 3.3 6.4a22 22 0 0 1 1.2 7.4l-.2 2.7s-2-2.1-7-5.3-7-4.3-8-4.7-1.4-.4-1.2-1.6z"/>
<path fill="#d4af3a" d="m282 322.1 1-.4h.2c.6.4 3.7 2.4 5 4.5 1.2 2 2.4 2.2 1 0a19 19 0 0 0-3.4-4.4l-1.1-1q3.8-1.7 7.8-4.5c.4.7 1 2 1.8 2.7 1 1.1 3.4 2.1 3.4 2.1s-1.9-2-2.5-3.5q-.7-1.6-.9-2.6a34 34 0 0 0 5.2-4.9 19 19 0 0 0 4.4 7.9c4 4.5 12 6.7 12 6.7s2.8.3.5 1c-2.3.5-3.9 1.5-8.3.8-4.4-.8-4.7-1-5.3-1.2s-2.2 0 0 .8 7.3 1.6 9.3 1.4c2-.1 4.7-.6 1.8 1-2.8 1.4-4 2-5.7 2s-2.4.6-4.1.6-2.1-.3-3.3-.6q-1.9-.2-2 .7c-.1.9 1.6 1.8 1.5 5a28 28 0 0 1-.6 6.4c-.3 1-1 2.5-1.5 3.4-.4 1-.6 1.8-1 0-.6-2-1.6-4.7-2-6.6-.5-1.8-.7-1.9-.9-2.9s-1.2-1.6-.7.1l1.4 5.5c.3 1.5 1.2 4.4-.3 2.1-1.4-2.2-6.8-8.3-7.7-9.3q-1.3-1.6-2.2-3c-.7-.9-2.5-2-3.4-3.4a30 30 0 0 0-4.2-4.3zm74.2-137.7c-.2 3.3-3.8 9.6-5.9 13-2 3.3-2.6 4.6-2.3 4.8s1.2-1.5 2.4-3 3.3-5 5.2-7.6c1.8-2.6 4.5-9 5-9.5.3-.6.4-1 1.3-.2 1 .7 2.8 7.6.6 12.3s-5.3 8.2-6.7 9.8-3.1 4.2-2.9 4.5.8-.5 2.5-2.2l6.1-6.8c1.4-1.7 3.4-5.2 4.1-6.5q.5-.7.8-.1c.7.6 1.6 2 1.2 5.7-.6 5.2-3.3 8.2-5.7 10.9-2.5 2.6-4.9 4.8-4.5 5.2.3.3 2.8-1.9 4.5-3.5s6-5.7 6.9-6.9c1-1.1 2.3-.6 2.2 4 0 4.4-5 9-7.1 11.5s-4.7 4-4.5 4.5 2.3-.8 3.7-1.7c1.3-1 6.4-4.2 7.4-5 1-.6 1.5.7.2 5s-5 7.7-7.2 9.3-5.8 3.5-5.6 4q.5.6 3.8-1.1c2-1 4.6-2 6.4-2.5s1.8.8 1.8.8.3 3.7-2.3 6.6a14 14 0 0 1-8.5 4.6c-2.9.5-3.3.6-3.2 1q0 .6 1 .5c.6 0 3.3.3 5.7.3s3.7.6 3.7 1.7c0 1 0 2.5-3.6 4.9s-7.7 2.8-9.4 3.1-1.8.5-1.4 1c.3.5 1.1.5 3.8.8 2.7.2 4.4 1.3 4.7 2 .4.8-.2 2.9-2.4 4.4s-5 2-9.3 1.7-5.4-.3-5.5.4c-.2.7 1.3.9 3.4 1.3 2.1.3 3.8.3 5 1.3s.3 3-1.8 4.6c-2 1.5-7.2 1.5-9.3 1.3s-5.5-.9-5.7-.3 1.6 1.4 3.8 2.2 2.8 1.3 3.3 2.2 0 1.8-2.6 2.8-6.5 1-8.5 0q-2.9-1.3-3.4-.8t.4 1.2q1.1 1.4 4.4 2.4c2.2.6 2.4.6 3.2 2.4s-1.2 3.2-5.2 3.3a25 25 0 0 1-11-2.4c-1.8-1-2.4-1.2-2.7-.9q-.5.5.6 1.2c.9.7 3.6 2.6 5 3s1.4 1.3 1.4 2.3.4 2.8-3.2 3.6-7.8.2-11.6-2.5a9 9 0 0 1-2.4-2.2c.3-3.7.2-6.2-.3-8.5q1.3 0 3.7-.9c2.1-.8 3-2.8 3.5-4q.8-1.3 2-.7 1.4.6 3 .6a8 8 0 0 0 5.6-3c1.9-2.2 1.5-4.3 1.6-5.4 0-1 1.8-.3 2.6-.3s3.4-.3 5.7-1.9 2.3-3.7 2.5-4.6 1.1-1.4 1.8-1.7 1.9-.6 3.4-1.8c1.6-1.3 2.2-3.8 2.3-4.7 0-.8 1-1 4.2-2.6s2.9-5 2.8-6.4-1.7-2.5-1.7-3 1-.6 2.2-1.3c1-.6 3.7-1.7 3.8-5.7s-2.6-4.4-2.6-4.4l1.6-.9c1.1-.6 4-2.6 4-5s-1-3.4-1.8-4-3-1.1-3-1.1 1.4-1 2-2.8c.5-1.8.7-4.1-.6-5.8-1.4-1.6-3.6-1.6-4.9-1.6s-2.2.7-2.7.3-.5-.6 0-1.6.5-3.2 0-4c-.3-.8-.3-2.3-3.4-3.3-3-1-6.3 1-7.3 1.6s-.6-.8-.6-1.8 2.1-3.5 4.5-7a79 79 0 0 0 10.7-25.1c.4-1.8 1.1-1 1.1-1s.7.3 2.4 4.5a27 27 0 0 1 1.8 11.2"/>
<path fill="#d4af3a" d="M302.3 257.5a8 8 0 0 0 6-3.8c2-2.7 4.7-3.3 9.5-3.2s7-2.2 8.1-5.5c1.2-3.4-.1-7.1-5-9.5s-7.1-5.1-7.3-9.2c-.2-4.2 4.9-9.3 10-15.1s9-14 9-14 1.4 1.9 1.4 3.8c0 2-.7 4 0 4.5.8.6 1-.8 4-2 3-1.4 5.3-1.5 7.2.6s0 5.8-1.7 7.5-3 3-2.6 3.7c.5.7 3-1 3.8-1.6.8-.5 2.8-2.6 5.6-2.4 2.9.3 3.9 1.1 4.1 3.5.3 2.4-2.5 4.6-4.4 5.5q-2.7 1.4-2 2c.4.5.8 0 1.7-.2s3-.5 4.6 0c1.4.6 3.8 2.6 1.6 5-2.2 2.6-6.8 3.5-7.7 3.7s-1.4-.1-1.5.6c0 .8 2 .8 4.4.9s3.2 2.4 2.9 4.3-4.5 3.8-6.8 4.2-3.1 0-3.3.7q-.4.8 1.5 1.2c1.9.4 3.8.7 4.3 2.7s.5 3-1.4 4.7c-2 1.8-5 1.5-6 1.3s-2-1.2-3-.3c-.9 1 .9 1.7 2.5 2.8 1.5 1 1.4 3.2-1.2 4.7a8 8 0 0 1-6.5.5c-1-.3-2.6-2-3.3-1-.7 1.1 2.3 1.5 3.6 3 1.3 1.6.8 4.2-1.8 5.8-2.5 1.5-6.8.4-7.5-.2q-1.2-1.2-2.2-.4c-.5.6.3 2 .8 3.3s1.6 5-2.3 6.6c-3.8 1.4-7.3-1.9-8-2.5q-1-1-1.8-.5-.7.5.2 1.5a4 4 0 0 1-1.3 5.3 7 7 0 0 1-5 1q-1-3-2.1-7.6h.5c.4 0 .5.5.5.7q-.1.4.2.4t.8.6q.8.6 1.2.5c.3-.2-1-.8-1.3-1.2s-1-1-1-1.5-.3-.7-1-1.3l-.4-.5-1.3-7.7-.5-5.9zM381 200c-4.1 8.3-10 13-10 13l1.2-3.5c.3-1.4 0-3 0-3l4.2-6c2.7-4.2 4.1-10.3 4.1-10.3s-2.5 6.1-4.4 9.3-4.2 6-4.2 6-.8-3-1.4-3q-.8-.2-1.4.9-.9.7-1 .5l1.8-3 1.6-2.3c1.5-2.4 4.8-7 6.5-11.6s5.4-16.1 6-19.5c.7-3.4 1.3-1.6 2 1a31 31 0 0 1 1 11.3 57 57 0 0 1-6 20.2m-5.3 22.4-3.8 2.7.6-1.9.3-3 3-2.7c1.3-1.1 2.9-2.5 6-6.4 3.1-3.8.6-1.5-2 1.3a97 97 0 0 1-7.3 6.8v-1a3 3 0 0 0-.8-2c-.5-.6-.2-.6 2.5-2.7a73 73 0 0 0 18.7-26.1s1.6 9.5-4.2 20-11.5 13.9-13 15m-30.5 60c-3.1-1.2-2.3-4.3-2.3-4.4l3.8 1.3c1.8.7 6.2 2 8 2s1.7-.2 1.7-.4-1.3 0-3.8-.5-7-2.8-7.7-3.3q-1.2-.7.4-.7 1.9-.3 2.5-1c.5-.3 1.6.2 4.5.9a53 53 0 0 0 23 .8c.6-.2 1.4.2-.8 3.9s-10 5.7-15.3 5.6c-5.4 0-10.8-3-14-4.1zm14.3-7.6c-5.5 0-7.6-1.6-7.6-1.6s0-.3.8-1.4.6-3 .6-3l3.6.5q3.5.8 8 .2c4.5-.6 1.7-.9-.7-.6s-6.9-.6-8.3-1c-1.5-.6-.9-1 .1-1.4s3.7-2 3.7-2 6.7.8 13 .1 10.2-2.3 11.5-2.5c1.2-.2 1.2.3 1.2.3s.3.6-1.8 4a18 18 0 0 1-10 6.6 54 54 0 0 1-14 1.8zm1.5-13 .5-1.4c.4-1.2-.6-2-.6-2s1.3-.4 5.4-1c4.2-.4 4.4-.8 7-1.3s4.2-1.3 4.1-1.6q0-.3-2.3.7c-1.5.5-7.9 1.2-10.6 1.6q-3.9.6-4.6.4c-.5-.3-2.1-.7-2.1-.7l2.5-.6a9 9 0 0 0 3.4-1.4c.8-.5 1-.6 4.4-1s6.1-1.5 12.2-3.7 10.6-4.8 11.9-5.8c1.2-1 1.1-.3.8 1-.4 1.2-.3 2.8-3.4 6.4-3 3.6-4.5 5.1-12.3 8s-16.3 2.4-16.3 2.4m22.8-18.8a43 43 0 0 1-16.5 6.8c-.4-.4.4-2 0-3-.4-1.2-1.1-1.2-2.8-1.3-1.6 0-.6-.4-.2-.4s.5 0 3.2-.8c2.7-.7 5.5-1 9.3-2.5 3.7-1.4 4.9-2.5 4.8-2.7s-1.8.9-5.2 2.2-7.6 1.8-9.8 2.3-.3-.5.8-1.1c1-.6 2.5-2.9 2.5-2.9s2.4-.4 6.4-1.6q5.8-1.7 10.7-4.5 1.7-1 3.4-2.3l5.5-4c1.4-1 1-.2.8.4s-1 3.5-3.2 6.6-2.9 4-9.7 8.8m1.1-13.5c-6.7 5.3-12.5 7-13.5 7.4s-.9-.1-.7-.7.3-2.4.3-2.4l5.1-3c2.7-1.5 5.7-3.6 7.6-5 2-1.3.8-1.2.2-.7s-3.3 2-6 3.8l-7 4.1s0-.9-.6-1.4c-.5-.6-2-.4-2-.4l1.2-1.4c.4-.7 2.6-1.4 5.1-2.6a62 62 0 0 0 10.3-7.2c3.3-2.8 9.7-9.8 10-10.3q.8-.9.8.2c0 1.1.3 1.2-.4 4.4s-3.7 9.9-10.4 15.2M361 179.3l-2.2 3.8-1.4 2.5q-.2 0 0-1.5v-3.7l-.3-2.4q.1-1.4.5-2l1.6-7.7c.5-2.7 1-6.7 1-10.2s-.6-1.7-.6 0-.2 4.3-.7 8.3-2.2 10-2.2 10-.1-.9-.7-2.2q-.8-2.1-2-4.5c-1.2-2.4-.4-2-.4-2s2.4-11.3 3-16.6 1.6-19.4 1.6-19.4c3.5 3.5 6 12.6 6.5 21.3.4 6.5-.7 12.5-1 15q-.8 7.2-2.7 11.3m7.7 17.8q0-4-2.4-5s1.5-2.6 2.7-6.3a35 35 0 0 0 2-8.2c1-6 1.6-12.3-.2-2a44 44 0 0 1-4 13.8l-2.6 4.3c.4-2 .5-3.2.4-5.3a34 34 0 0 0-1-7s3.2-6.3 5.2-13.9a88 88 0 0 0 3.4-24.1s3.2 2.4 4.3 14.8a74 74 0 0 1-3.8 28.8c-1.6 4.5-4 10-4 10zm-35.5 94.5 2-1.3c1.6-1 1.1-4 1.1-4l5.5 3.4 3.3 1.3c1 .3.7 0-.1-.5s-1-.8-3.1-1.6c-2-.8-6.2-4.4-6.2-4.4l2.7-.1c1.7-.1 3.8-1 3.8-1s1.7 1.6 8.5 4.5c6.9 3 12.7 3.7 12.7 3.7s-.8 3.5-4 4.7c-2.8 1.1-6.5 1.9-13.8.1a47 47 0 0 1-12.4-4.8m-9 7c-.5-2.9 1.1-4.9 1.1-4.9l1.4 1.1c1 1 2.5 2.7 5.4 4.1 2.8 1.5 3.9 1.4 1.1-.1s-6.8-5.7-6.8-5.7l1-.4c1.1-.4 3 .3 10.7 4.9a29 29 0 0 0 12.2 4.8s-3.5 4.1-12.8 3.8-12.7-4.7-13.3-7.6m-17.6-3.4s1 1.5 2.4 2.4c1.4 1 5 1.5 5 1.5l1 1.3c.7 1 2.3 3.2 3.9 4.3 1.6 1.2 4.2 2 4.4 1.7.1-.3-1.5-1-3.1-2s-3-2.7-4.1-4 .4-1 .4-1h3.8c1.8-.2 1.4 0 2 .8q.6 1.1 2.5 3.2l4.7 5.4c1 1.3.3 1.6.3 1.6s-.5.8-4.7 1.3c-2.5.2-5.3 0-8.2-1.8a25 25 0 0 1-5.6-4.8c-5-5.4-4.7-9.9-4.7-9.9m-2.2 11.3 1.6 1.9s-.7-2.2-1.4-3.3l-.9-1.4a28 28 0 0 0 1.7-5c.4 1.5 1.8 5.8 3.6 8.6 2.2 3.5 4 4.7 2.8 5.6-1.2 1-3.6 1.2-6.3-.2s-3.4-3.5-3.8-5.3l1.3-2.3q.5.3 1.4 1.4"/>
<ellipse cx="-305" cy="169.2" fill="#d4af3a" rx="1.9" ry="1.8" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="M309 165.9c-1.7 0-2.7-.9-4.6-.7a28 28 0 0 0-9 3.6 16 16 0 0 1-10.5.9q-1.5-.6-3-1.5c-4.6-2.5-3.5-7.6-3.5-7.6a18 18 0 0 0 11.3 3.5l-1 .8c-.6.7-1.8 1.5-1.4 1.8s2-1.3 3.4-2.3a21 21 0 0 1 7-3.4c3.5-1 7.4-1.1 10 .3 2.7 1.5 3 3.3 2.6 4q-.3.7-1.3.6m-49.3 37c.8-.2 2.2-.5 4-2.2a78 78 0 0 0 6.8-7.3q.9-1.5-1-2.3c-1.2-.5-2.1-2-2.1-2s2.7-1.1 4.5-2.4c1.9-1.4 6.1-5.2 6.7-6.1.6-1 .3-1.1-.6-1.4s-1-1-1-1 1.9-1.1 3.7-2.6l4.3-4c2.8 1.2 7 .7 9.4.3 4.1-.7 7.1-2.4 7.1-2.4l.7 1.9c.5 1.3 2.4 2 4.3.7 1.9-1.2 1.4-4 1.4-4s1.6-.9 4.7-2q4.6-1.9 8.3-.2c2.5 1.2 3.8 4.7 3.8 7.3 0 2.7-1.4 1.6-1.7 1.2s-.9-2.3-4.1-2.8-6.1 1-9.8 4-4.5 3.3-6.3 5.2-.4 3 2.8 4 7.2-1 7.3-1c.6.3-1.4 3.1-4 4.2-2.8 1-6.3.3-9-.7-1.8-.8-2.1 0-2.6.5-3 3.2-3.8 6-4.3 9.7a4 4 0 0 1-2.8-3.3c0-.6-.3-1.9-1.3-1s-1.8 6.3-1.8 8.7c0 2.5.7 5.7.7 5.7l-.7-.1q-.7-.2-2.5-1.2c-1.2-.7-1.3 0-1.3 1.4s-.4 3.8-2.2 8.3c-1.7 4.4-4.3 5.9-4.3 5.9s-.5-.9-1-2.7c-.5-1.7-.9-5.2-.9-8.2s.8-9.4.8-9.4-3.6 4.4-7.4 5.5-7.6.5-9.2-1.4-.2-2.6.6-2.8"/>
<path fill="#d4af3a" d="M284.8 214.5s1.1 2.5 1.7 3.4c.7 1 1 1 1 1s-1.2 1-2.9 1.6l-3.2.8s2-2.6 2.6-4.4zm-22.9-1.5.9-2.3a16 16 0 0 0 8.7-1.8s-1.8 3.6-3.5 4.8c-1.8 1.3-2 1.3-2 1.3s1.7.3 4.1-2 3.4-5 3.4-5-.5 6.2.4 10l1.2 4.2s-5 1-10.5-.6-6-2-6-2-.7-.2.2-1.4a30 30 0 0 0 3.1-5.2"/>
<path fill="#b96b29" d="m272 199.4 1.8-2.4s.6.6.1 1.4c-.5.9-3 3-4.6 4a13 13 0 0 1-3.3 1.2v-.3s4.2-2 6-3.9m6.7 12.9.4-9c0-1 .7-.8.7-.8l.9.4-1.7 11.3c-.1 1.2-.7 1.1-1 1 .6-.2.7-2.6.7-3zm10.4-33.6c.7-.6 2.1-1 .7.5-1.3 1.5-3.1 3.4-3.7 5-.5 1.6-.8 3.5 0 3 1-.3 1.6-.3 2.6-1.1s2.7-2.1 3.5-2.3c.7-.3 2 0 1 1.1a26 26 0 0 1-4.3 3.7l-4.2 2.9-2 1.4s.9-6.4 2.6-9.1a28 28 0 0 1 3.8-5zm34.2 105.9c1.3 1 3 1.4 3 1.4s.7 0 .7.2-.9 0-1.9-.2c-1-.1-2.3-1.1-2.7-1.5s-1.2-.8-1-1.4.6.5 1.9 1.5m-11 5.9 2.4 1.9 1.3.7 1 .7h-1c-.8-.1-3-1.8-3.6-2.5q-1.1-.8-.8-1 .4-.4.7.2M357 211q-.8 1-1.1 1-.4-.3.5-1.2l.9-.9s.7-1.2 1.1-1.6 1.1-.7.3.3l-1.8 2.4zm3.3 7.4L359 220q-.8 1.1-1.2 1c-.3-.4.8-1 1.2-1.7l1.5-1.5 1.4-.9-1.5 1.7zm-1.2 12c-.9 1-2.1 1.2-2.3.8q-.3-.4.8-1a24 24 0 0 0 4-2.7q.6-.7 1-.6c.1.1-1.2 1.5-1.6 1.8l-2 1.7zm-2.5 9.7q-.2-.6.7-.6l1.6-.2s1.4-.5 1.5-.3-1.1.8-1.7 1q-.9 0-1.5.4-.5.2-.6-.3m-1 10.4c1.5 0 2.8-.4 2.9-.1q-.1.3-1.5.8c-.8.3-2.2.4-3.1.4q-1.4 0-1.1-.7c0-.4 1.3-.4 2.8-.4M343.8 262q.4-.2 1.6.2c.9.2 2.2.4 3 .4l2.5-.1 1 .2q-.2.2-3.1.4c-1.9.2-3.1-.2-4-.3q-1.3-.3-1-.8m-4.9 8.2 2.4.4 2.3.3s.8.1.7.4-.4 0-1 0h-2.6l-2.7-.6c-.7-.1-1.6-.5-1.2-1s1.2.1 2.1.5m-9.4 6.1 3.7 2.7q1 .5.7.8c-.2.2-.7-.3-1.1-.5l-2.7-1.8c-.7-.6-1.7-1.1-1.5-1.6q.1-.4.9.5zm15.7-79.7-1.8 2.4c-1.6 2-.7.2-.3-.4l3.6-4.8c.8-1 1-.5.5.1zm3.5 10.5c-.3-.3.3-.5 1.2-1.7l2.8-3.5s.5-1.1.7-1-.6 1.9-1.4 2.6-1.7 2.2-2.3 2.9q-.7 1-1 .7m-4.6 19.7c0-.6 1.9-.5 2.7-.7l2.4-.5s1.7-.3 1.8 0q0 .3-1.4.5-1.7.5-3.5.8c-1 .2-2 .4-2-.1m4.6-10.7q-1.8 1.4-2.7 1.8c-.7.4-1.2 1-1.5.5q-.4-.5 1.3-1.2l1.6-.8 1.1-.6c.5-.3 1.4-.7.2.3m-10.6-7 1.4-1s1-.8 1.1-.5-1 1.1-1.6 1.6-1.6 2-2.2 1.8c-.5-.3.6-1.2 1.3-1.9m7 27.5 1.6.4q1.2.4-.5.5c-1.7.1-1.7-.3-2.9-.5q-1.7-.3-1.4-.8t1.3-.1zm-2 9.9c.1.2-.8 0-1.4-.2s-1.9-1-2.7-1.1q-.9-.3-.7-.8c0-.5.8-.1 1.8.2s2.2 1.3 2.2 1.3.8.4.9.6zm-12.7 6q-1.3-.8-.8-1.2.4-.5 1.3.4l2 1.5 1.3.6c.5.3 1.2.7 1 1-.1.2-1.2 0-1.8-.6zm-6 8-2-1c-.4-.4-.7-1.4-.3-1.6.3-.1.6.5 1.4 1 .8.7 3.7 2.6 3.7 2.6l.8.3c.8.3.4.6-.2.5q-1 0-2-.7zm-9 7.6c-.7-.3-1.8-1.3-2.4-1.8s-1.2-1.1-.8-1.4q.6-.4 1.5.6l2.2 2.3s.3 0 1.1.8c1 .7 0 .6-.3.3s-.5-.5-1.3-.8"/>
<path fill="#d4af3a" d="m279 154.5-3.6-2.5 4.3-1.1 2.7 2.2z"/>
<ellipse cx="-267.5" cy="151.6" fill="#b96b29" rx="3.4" ry="3.3" transform="scale(-1 1)"/>
<ellipse cx="-267.5" cy="150.8" fill="#d4af3a" rx="2.1" ry="2" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="M274.7 140.2c-1 .3-2 .2-2 .2s-.4-1.6.4-2.3l2.1-1.8.6-.6.1 2.1c0 .8-.3 2-1.2 2.4"/>
<circle cx="-272.8" cy="141.8" r=".6" fill="#d4af3a" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="m277.5 141.2.9 1s-.7 0-1.3.4q-1 .7-2 .7a1 1 0 0 1-.8-1.2q0-.8 1.6-1.3c1-.4 1.4 0 1.6.4"/>
<path fill="#d4af3a" d="M275.1 145.2c2.2.2 5-2.2 5-2.2q.5 3 3.3 3.3h-.2s-1.7 1-3.7 1.2q-1.3.2-2.6-.2a11 11 0 0 1-3.2-1c-1.3-.8-1-2.9-1-2.9s.3 1.6 2.4 1.8m8.4-2.1q1.7-.8 2.8-1c.5 0 1.3-.2 1.2.8s-1.2 1.5-2.2 1.6q-1.4-.1-2.2-.7-1-.4.4-.7m6.8-3.7q1.7-1 1.8-.9c.1.1.6 1.7.3 2.7s-1.1 1.3-2 1.3c-1 0-2.3-.5-2-1.2q.6-1 1.9-2z"/>
<ellipse cx="-289.4" cy="144.1" fill="#d4af3a" rx=".8" ry=".5" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="M287.3 135.8c.6-2.9.5-6-2.2-9.9s-7.3-5.5-7.3-5.5l-1.7-1.9a33 33 0 0 0-4.5-3.6l5.5 1.5v.1a2.4 2.4 0 0 0 2.4 2.5 2 2 0 0 0 1.4-.4c1.1.2 4.7 2.3 5.7 3.1l-.4 1.3a2.4 2.4 0 0 0 2.4 2.5l1.1-.3.3.5 1.8 4a3 3 0 0 0-1 2.1c0 1.4.8 2.5 2 2.5v1.6q.2 1.2-1.8 1.7c-1.4.4-3.3 2.8-3.3 2.8-.1 0-1-1.8-.4-4.6"/>
<path fill="#d4af3a" d="M278.8 117.6q-.8-.7-.2-1.4.7-.5 1.6.1c.9.6.6 1 .2 1.4q-.7.5-1.6-.1m9 6.1q-1-.9-.2-1.8.8-.7 1.8.2 1 1 .2 1.7-.7.7-1.8 0z"/>
<circle cx="-293.2" cy="131.4" r="1.4" fill="#d4af3a" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="M263 112q1.2.2 4.1 1.7c-1.4 0-2.5.9-2.5 2s1.3 2.4 2.7 2.4 2.3-1.2 2.3-2.3q0-.5-.2-.8l2 1.2c4.4 3 5.4 4.5 7.6 8.5s3 8.6 2 11.2c-.9 2.7-4.4 3.2-4.4 3.2s.4-.4.4-.9l-.2-1.7c-.2-1 0-2.2 0-2.2s-.9-.1-1.3.4l-1.6 1.8-1.6 1.6s-.7-1-.7-2.2.8-2.5 1-4.6-1-5.8-3-8.6a24 24 0 0 0-7-5.7 2 2 0 0 1-1.5-2l-.2-2c-.1-1.3 1-1.2 2.2-1z"/>
<path fill="#d4af3a" d="M267.5 116.8q-1.4-.2-1.5-1.4.1-1 1.4-1.1 1.3.1 1.4 1.1 0 1.2-1.3 1.4m-6.6 26a3 3 0 0 1-2.8-.2q-1-.8-.1-1.9a2 2 0 0 1 2.3-.2l2 1.4s-.3.5-1.4.9"/>
<path fill="#d4af3a" d="M257.5 144c.4.6.6 1 1.8 1s2.5 0 3.3-.8l1.3-1.9h1.3s.5 1.9 1 2.4c.7.5 2.7.6 3.6 0 1-.8 1.7-1.6 1.9-1.5q.3.3-1 1.6c-1.3 1.3-1.5 1.3-3 1.3h-8.5s-1.7.1-2.2-1q-.7-1.5-.3-1.9.2-.2.8.8"/>
<path fill="#d4af3a" d="M270.4 142.6c-1.3.7-2 .2-2.4-.3l-.6-.9s1.2-.4 1.8-.8q.8-.5 1.6-.6l.6.2s.3 1.6-1 2.4"/>
<circle cx="-276.6" cy="130" r="2.8" fill="#b96b29" transform="scale(-1 1)"/>
<path fill="#b96b29" d="M271.6 120.2a3 3 0 0 1 3.8.7c.9 1.2.7 2.7-.4 3.5a3 3 0 0 1-3.7-.7c-1-1.2-.8-2.7.3-3.5"/>
<circle cx="-276.7" cy="129.6" r="1.6" fill="#d4af3a" transform="scale(-1 1)"/>
<path fill="#d4af3a" d="M272.8 120.5q1.4-.2 2 .8.6 1.2-1 1.6c-1.8.3-2.6-2-1-2.4"/>
</svg>

After

Width:  |  Height:  |  Size: 56 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-mk" viewBox="0 0 512 512">
<path fill="#d20000" d="M0 0h512v512H0z"/>
<path fill="#ffe600" d="M0 0h86.8L256 246.9 425.2 0H512L0 512h86.8L256 265.1 425.2 512H512zm512 204.8v102.4L0 204.8v102.4zM204.8 0 256 219.4 307.2 0zm0 512L256 292.6 307.2 512z"/>
<circle cx="256" cy="256" r="82.3" fill="#ffe600" stroke="#d20000" stroke-width="18.3"/>
</svg>

After

Width:  |  Height:  |  Size: 407 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-nl" viewBox="0 0 512 512">
<path fill="#ae1c28" d="M0 0h512v170.7H0z"/>
<path fill="#fff" d="M0 170.7h512v170.6H0z"/>
<path fill="#21468b" d="M0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-no" viewBox="0 0 512 512">
<path fill="#ed2939" d="M0 0h512v512H0z"/>
<path fill="#fff" d="M128 0h128v512H128z"/>
<path fill="#fff" d="M0 192h512v128H0z"/>
<path fill="#002664" d="M160 0h64v512h-64z"/>
<path fill="#002664" d="M0 224h512v64H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 318 B

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-pl" viewBox="0 0 512 512">
<g fill-rule="evenodd">
<path fill="#fff" d="M512 512H0V0h512z"/>
<path fill="#dc143c" d="M512 512H0V256h512z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 219 B

+57
View File
@@ -0,0 +1,57 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-pt" viewBox="0 0 512 512">
<path fill="red" d="M204.8 0H512v512H204.7z"/>
<path fill="#060" d="M0 0h204.8v512H-.1z"/>
<g fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".6">
<path stroke-width=".7" d="M293.8 326.6c-34.4-1-192-99.4-193-115.1l8.6-14.5C125 219.7 286.2 315.4 302 312z"/>
<path stroke-width=".7" d="M107.6 195c-3.1 8.3 41.1 35.6 94.3 68 53.2 32.3 99 52.3 102.5 49.5l1.5-2.9q-1 1.6-4.6.6c-14.4-4.2-51.9-21.4-98.3-49.5s-86.8-54.1-93-65.1a7 7 0 0 1-.7-3.3h-.2l-1.3 2.3zm187 132c-.6 1.1-1.7 1.1-3.7 1-12.9-1.5-52-20.5-98-48.1-53.8-32.2-98.2-61.5-93.3-69.1l1.3-2.4.2.1c-4.3 13 87.6 65.5 93 68.9 53.1 33 98 52.2 102 47.2z"/>
<path stroke-width=".7" d="M205 221c34.3-.3 76.8-4.7 101.2-14.4L301 198c-14.5 8-57.1 13.3-96.3 14-46.4-.4-79.1-4.7-95.5-15.7l-5 9.1c30.2 12.7 61 15.5 100.8 15.6"/>
<path stroke-width=".7" d="M307.7 206.8c-.9 1.3-16.8 6.8-40.3 10.8a416 416 0 0 1-62.6 4.6 444 444 0 0 1-60-3.8c-24.5-3.9-37.3-9.3-42-11.2l1.2-2.3a186 186 0 0 0 41.3 11 439 439 0 0 0 59.5 3.7 418 418 0 0 0 62.1-4.6c24-3.9 37.2-8.9 39-11.2zM303 198c-2.6 2-15.6 6.7-38.4 10.3a414 414 0 0 1-59.5 4.2 400 400 0 0 1-57.4-3.7c-23.3-3-35.6-8.5-40-10.1l1.3-2.3c3.4 1.8 15.4 6.6 39 10a423 423 0 0 0 57.1 3.5 409 409 0 0 0 59-4.2c23-3.2 35.4-9 37.3-10.5zM92 262.4c21.2 11.4 68.1 17.2 112.6 17.6 40.5 0 93.3-6.3 113-16.8l-.6-11.4c-6.1 9.7-62.6 19-112.8 18.6s-96.8-8.2-112.3-18.2v10.2"/>
<path stroke-width=".7" d="M318.7 260.8v2.7c-3 3.6-21.6 9-44.9 12.7a463 463 0 0 1-69.7 4.8c-27.4 0-49.2-2-66.2-4.6a165 165 0 0 1-47.3-12.7v-3.2c10.4 6.9 38.3 11.9 47.7 13.4 16.8 2.6 38.5 4.5 65.8 4.5 28.7 0 51.7-2 69.3-4.7 16.8-2.4 40.6-8.7 45.3-12.9m0-9.7v2.8c-3 3.5-21.6 8.9-44.9 12.6a463 463 0 0 1-69.7 4.8c-27.4 0-49.2-2-66.1-4.5A165 165 0 0 1 90.6 254v-3.2c10.4 6.9 38.3 12 47.7 13.4 16.9 2.6 38.6 4.6 65.8 4.6 28.7 0 51.7-2 69.3-4.8 16.8-2.4 40.6-8.7 45.3-12.9m-114.2 73.5c-48.6-.3-90.3-13.2-99.1-15.4l6.4 10c15.5 6.6 56.2 16.4 93.2 15.3s69.3-4 92-15l6.6-10.5c-15.5 7.3-68.3 15.6-99 15.6"/>
<path d="m299.5 317.1-3 4.4a259 259 0 0 1-34.7 8.9 315 315 0 0 1-57.2 5.2c-43.1-.6-78.4-9-95-16.2l-1.4-2.3.3-.4 2.2.9a306 306 0 0 0 94.1 15.5c20 .1 40-2.3 56.1-5.1 24.8-5 34.8-8.7 37.9-10.4zm5.7-9.4h.1l-2.2 3.8c-5.7 2-21.3 6.6-44 9.8-15 2-24.3 4-54 4.6a371 371 0 0 1-100.5-15l-1.2-2.5a424 424 0 0 0 101.7 15c27.2-.6 38.8-2.6 53.6-4.7 26.5-4 39.8-8.4 43.8-9.7l-.1-.2z"/>
<path stroke-width=".7" d="M305.8 253.4c.2 32-16.2 60.8-29.4 73.5a106 106 0 0 1-72.3 30c-32.3.6-62.7-20.5-70.9-29.7-16-18.1-29-41-29.4-72 2-35 15.7-59.3 35.6-76a106 106 0 0 1 68.4-24.2c25.4.7 55.2 13.2 75.7 38 13.4 16.2 19.3 33.8 22.3 60.5zM204.4 143.8c62 0 113 50.5 113 112.3a113 113 0 0 1-113 112.3c-62 0-112.6-50.4-112.6-112.3s50.6-112.3 112.6-112.3"/>
<path stroke-width=".7" d="M204.7 143.4c62 0 112.6 50.7 112.6 112.7s-50.6 112.7-112.6 112.7A113 113 0 0 1 92 256a113 113 0 0 1 112.7-112.7zM94.5 256.1c0 60.6 49.8 110.2 110.2 110.2s110.1-49.6 110.1-110.2S265.1 146 204.7 146A110.6 110.6 0 0 0 94.5 256.1"/>
<path stroke-width=".7" d="M204.8 152.8c56.5 0 103.1 46.5 103.1 103.2s-46.6 103.2-103.1 103.2A103.6 103.6 0 0 1 101.5 256c0-56.7 46.6-103.2 103.2-103.2zM104 256c0 55.4 45.5 100.7 100.8 100.7S305.5 311.4 305.5 256 260 155.3 204.7 155.3 104 200.6 104 256"/>
<path stroke-width=".7" d="M209.3 143h-9.7v226.4h9.7z"/>
<path stroke-width=".7" d="M208.3 141.7h2.5v229h-2.5zm-9.6 0h2.5v229h-2.5z"/>
<path stroke-width=".7" d="M317.4 260.5V252l-6.8-6.3-38.7-10.3-55.8-5.7-67 3.4-47.9 11.4-9.6 7.2v8.3l24.4-11 58-9h55.8l41 4.5 28.4 6.9z"/>
<path stroke-width=".7" d="M204.7 238.7a394 394 0 0 1 72.9 6.5c21.1 4.2 36 9.5 41 15.4v3c-6-7.4-26-12.8-41.5-16-20.3-3.9-46-6.4-72.4-6.4-28 0-54 2.7-74 6.6-16 3.2-37.4 9.5-40.1 15.8v-3c1.5-4.4 17.4-10.9 39.8-15.4a405 405 0 0 1 74.3-6.5m0-9.7a396 396 0 0 1 73 6.5c21 4.2 36 9.5 41 15.5v2.9c-6.1-7.4-26.2-12.8-41.6-15.9-20.3-4-46-6.5-72.4-6.5a402 402 0 0 0-73.8 6.6c-15.5 3-37.8 9.6-40.3 15.8v-3c1.5-4.3 17.8-11 39.8-15.3a405 405 0 0 1 74.3-6.6m-.5-49.3c41.9-.2 78.5 5.9 95.2 14.5l6.1 10.5c-14.5-7.8-54-16-101.3-14.7-38.5.2-79.6 4.2-100.3 15.2l7.3-12.2c17-8.8 57-13.2 93-13.3"/>
<path stroke-width=".7" d="M204.7 188.4c24 0 47 1.3 65.4 4.6 17.1 3.2 33.5 8 35.8 10.6l1.8 3.2c-5.7-3.7-19.8-7.9-38-11.3a367 367 0 0 0-65-4.5c-27 0-48 1.3-66 4.5-19 3.5-32.2 8.6-35.5 11l1.8-3.3c6.3-3.3 16.4-7.2 33.3-10.2 18.6-3.4 39.7-4.4 66.4-4.6m0-9.6a371 371 0 0 1 63.1 4.4c13.9 2.7 27.6 6.9 32.7 10.6l2.6 4.2c-4.5-5-21.4-9.7-36.4-12.3-17.4-3-39.1-4.2-62-4.4-24 0-46.2 1.5-63.4 4.6-16.4 3.2-27 6.9-31.5 9.8l2.3-3.6c6.2-3.2 16.2-6.2 28.8-8.7 17.3-3.1 39.7-4.6 63.8-4.6m56 124.1a293 293 0 0 0-56-4.2c-69.9.8-92.4 14.3-95.2 18.4l-5.2-8.5C122 295.7 160 288.5 205 289.2q35 .5 60.4 5.2l-4.8 8.5"/>
<path d="M204.3 297.5c19.4.3 38.5 1 57 4.5l-1.4 2.4a298 298 0 0 0-55.5-4.3c-25.8-.2-52 2.2-74.6 8.7-7.2 2-19 6.6-20.3 10.4l-1.3-2.2c.4-2.2 7.6-6.9 21-10.6 26-7.5 50.3-8.7 75-9zm.9-9.8a351 351 0 0 1 61.1 5.3l-1.4 2.5a319 319 0 0 0-59.6-5.2c-25.9 0-53.3 1.9-78.3 9.1-8 2.4-22 7.4-22.4 11.4l-1.3-2.3c.3-3.6 12.3-8.4 23.2-11.6a285 285 0 0 1 78.7-9.2"/>
<path stroke-width=".7" d="m304.4 309.9-8.4 13-24.1-21.4-62.6-42-70.5-38.8-36.7-12.5 7.8-14.5 2.7-1.4 22.8 5.7 75 38.7 43.3 27.3 36.3 26 14.8 17.1z"/>
<path stroke-width=".7" d="M100.8 208.5c6.5-4.3 53.7 16.7 103 46.5s96.4 63.6 92.2 70l-1.4 2.1-.7.5c.2 0 .9-1 0-3.3-2.2-6.9-35.5-33.5-91-67-54-32.2-99-51.6-103.5-46zm205.3 101.2c4-8-39.7-41-94-73.1-55.5-31.5-95.6-50-102.9-44.5l-1.6 3 .4-.5c1.3-1.1 3.5-1 4.5-1 12.6.1 48.6 16.6 99 45.6 22.1 12.8 93.4 58.6 93.1 71.4 0 1.1.1 1.3-.3 1.9z"/>
</g>
<g transform="translate(-68.3 28.4)scale(1.13778)">
<path fill="#fff" stroke="#000" stroke-width=".7" d="M180.6 211a59 59 0 0 0 17.5 41.7 59 59 0 0 0 41.8 17.6 59 59 0 0 0 42-17.4 59 59 0 0 0 17.4-41.8v-79.2l-118.7-.2z"/>
<path fill="red" stroke="#000" stroke-width=".5" d="M183 211.1a56 56 0 0 0 16.8 40 57 57 0 0 0 40.2 16.8 57 57 0 0 0 40.2-16.6 56 56 0 0 0 16.7-40v-77H183v76.8m91-53.7v48.9l-.1 5.1a33 33 0 0 1-10 24 34 34 0 0 1-24 10c-9.4 0-17.7-4-23.9-10.2a34 34 0 0 1-10-24v-54z"/>
<g id="pt-e">
<g id="pt-d" fill="#ff0" stroke="#000" stroke-width=".5">
<path stroke="none" d="M190.2 154.4c.1-5.5 4-6.8 4-6.8.1 0 4.3 1.4 4.3 6.9z"/>
<path d="m186.8 147.7-.7 6.3h4.2c0-5.2 4-6 4-6 .1 0 4 1.1 4.1 6h4.2l-.8-6.4zm-1 6.4h17q.5 0 .6.7 0 .8-.6.8h-17q-.5 0-.6-.8 0-.7.7-.7z"/>
<path d="M192 154c0-3.3 2.3-4.2 2.3-4.2s2.3 1 2.3 4.2H192m-5.8-9h16.3q.5.1.6.8 0 .5-.6.6h-16.3q-.5 0-.6-.7 0-.5.6-.6zm.4 1.5H202q.5 0 .6.7t-.6.7h-15.5q-.6 0-.6-.7t.6-.7zm5-10.6h1.2v.8h.9v-.8h1.3v.9h.9v-1h1.2v2q0 .6-.5.6h-4.4q-.5 0-.6-.5zm4.6 2.7.3 6.4h-4.3l.3-6.5h3.7"/>
<path id="pt-a" d="M191 141.6v3.4h-4v-3.4z"/>
<use xlink:href="#pt-a" width="100%" height="100%" x="10.6"/>
<path id="pt-b" d="M186.3 139h1.2v1h.9v-1h1.2v1h.9v-1h1.2v2q0 .6-.5.6h-4.3l-.6-.6z"/>
<use xlink:href="#pt-b" width="100%" height="100%" x="10.6"/>
<path fill="#000001" stroke="none" d="M193.9 140.6c0-.6.9-.6.9 0v1.6h-.9z"/>
<path id="pt-c" fill="#000001" stroke="none" d="M188.6 142.8c0-.6.8-.6.8 0v1.2h-.8z"/>
<use xlink:href="#pt-c" width="100%" height="100%" x="10.6"/>
</g>
<use xlink:href="#pt-d" width="100%" height="100%" y="46.3"/>
<use xlink:href="#pt-d" width="100%" height="100%" transform="rotate(-45.2 312.8 180)"/>
</g>
<use xlink:href="#pt-d" width="100%" height="100%" x="45.7"/>
<use xlink:href="#pt-e" width="100%" height="100%" transform="matrix(-1 0 0 1 479.8 0)"/>
<g id="pt-f" fill="#fff">
<path fill="#039" d="M232.6 202.4a8 8 0 0 0 2.2 5.7 7 7 0 0 0 5.3 2.4q3.2-.2 5.3-2.4a8 8 0 0 0 2.2-5.7v-10.8h-15z"/>
<circle cx="236.1" cy="195.7" r="1.5"/>
<circle cx="244.4" cy="195.7" r="1.5"/>
<circle cx="240.2" cy="199.7" r="1.5"/>
<circle cx="236.1" cy="203.9" r="1.5"/>
<circle cx="244.4" cy="203.9" r="1.5"/>
</g>
<use xlink:href="#pt-f" width="100%" height="100%" y="-26"/>
<use xlink:href="#pt-f" width="100%" height="100%" x="-20.8"/>
<use xlink:href="#pt-f" width="100%" height="100%" x="20.8"/>
<use xlink:href="#pt-f" width="100%" height="100%" y="25.8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ro" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#00319c" d="M0 0h170.7v512H0z"/>
<path fill="#ffde00" d="M170.7 0h170.6v512H170.7z"/>
<path fill="#de2110" d="M341.3 0H512v512H341.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 302 B

+296
View File
@@ -0,0 +1,296 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-rs" viewBox="0 0 512 512">
<defs>
<clipPath id="rs-a">
<path d="M54.3 32h480v480h-480z"/>
</clipPath>
</defs>
<g clip-path="url(#rs-a)" transform="translate(-57.9 -34.1)scale(1.06667)">
<path fill="#fff" d="M-32 32h720v480H-32z"/>
<path fill="#0c4076" d="M-32 32h720v320H-32z"/>
<path fill="#c6363c" d="M-32 32h720v160H-32z"/>
<g transform="matrix(.53333 0 0 .53566 -32 31.8)">
<path fill="#21231e" d="M473.4 259.4v-.2h1v.2l-.5.5-.6-.6zm1 0v.5h-.5l.4-.6zm-93 5.2-1 .2.3-.6 4.2-.2 4-.3 4-.2 3.8-.3 4-.2 3.6-.3 3.7-.2 3.7-.2 3.5-.2h3.4l3.3-.3 3.2-.2 3.2-.2 3-.2h3l3-.2 3-.2 2.7-.2h2.8l2.7-.2 2.5-.2h2.5l2.4-.3h2.2l2.2-.2h2.2l2-.2h2l2-.2h5.3v1h-5.5l-1.8.2h-2l-2 .2h-2.2l-2.2.2-2.4.2h-2.4l-2.5.2-2.5.2H446l-2.8.3h-2.8l-3 .3-2.8.2h-3l-3 .3-3.3.2-3.3.2-3.3.2h-3.5l-3.5.3-3.6.2-3.5.3-3.8.2-3.8.2-4 .3-4 .2-4 .3-4.2.2.5-.6zm-1 .2-.3-.5h.7zm6.4 31-6.5-31 1-.2 6.5 31-.5.7zm.5.5h-.4l-.2-.4.5.3zm95-4-.2 1h.2-5.8l-3 .2h-3l-2.8.2h-3l-3 .2h-6l-3 .2h-3l-3 .2h-3l-3 .2h-3.2l-3 .2h-3l-3 .2h-3l-3 .2h-3l-3 .2h-3l-3 .2h-3l-3 .2h-3l-2.8.2h-5.8l-2.7.2h-2.7v-1h5.8l2.7-.2h5.8l3-.2h3l2.8-.2h3l3-.2h3l3-.2h3l3-.2h3l3-.2h3l3-.2h3.2l3-.2h3l3-.2h3l3-.2h3l3-.2h3l3-.2h3l2.8-.2h3l2.8-.2h.2zm-.2 0h.2zm94.5 3.4 1 .2-.5.3h-2.8l-2.8-.2h-8.8l-2.8-.2h-3l-3-.2H551l-3-.2h-3l-3-.2h-3l-3-.2h-3l-3-.2h-3l-3.2-.2h-3l-3-.2h-3l-3-.2h-3l-3-.2h-3l-3-.2h-3l-3-.2h-3l-3-.2H485l-3-.2.2-1h2.8l3 .2h3l2.8.2h3l3 .2h3l3 .2h3l3 .2h3l3 .2h3l3 .2h3l3 .2h3.2l3 .2h3l3 .2h3l3 .2h3l3 .2h3l3 .2h5.8l2.7.2h3l2.7.2h2.8l-.5.5zm1 .2v.3h-.5l.5-.4zm6.5-31.2-6.5 31h-1l6.5-31.2.6-.4zm-.4-.6h.5v.6zm-93.7-4.8h1l-.6-.5h5.4l2 .2h2l2 .2h2l2.4.2h2.4l2.4.3h5l2.6.3h2.7l3 .3 2.8.2h3l3 .3 3 .3 3.3.2 3.3.2 3.4.2h3.4l3.6.3 3.6.2 3.7.2 3.6.3 4 .2 3.8.3 4 .2 4 .3 4.2.2v1l-4.3-.2-4-.3-4-.2-4-.3-3.7-.2-3.8-.2-3.6-.3-3.6-.2-3.5-.2h-3.4l-3.3-.3-3.3-.2-3.2-.2-3-.2h-3l-3-.2-3-.2h-2.7l-2.6-.3h-2.6l-2.6-.3h-2.5l-2.4-.2-2.3-.2h-2.2l-2.2-.2h-2l-2-.2h-2l-1.8-.2h-3.3l-.5-.6zm.4.5h-.5v-.6l.4.5zm.5-.8v.2h-1v-.2l.3-.5.6.5zm-1 0v-.4h.3l-.4.4zm-8 .3.2-1h8.8l.3.2v.8h-.5v.2h-.3l-.2-1h.2v.7h.2-8.2zm-8.1.2v-1l-.2 1h-.4l-.2-.2-.3-.3v-.5l.2-.2h9.4v1h-8.8l.2-.2v-.8h-.2.4v1zm0-1v1l-.6-.5.5-.5zm-.6.5v-.5h.5l-.6.5z"/>
<path fill="#edb92e" d="M583.5 264.2q4.6-10 11.2-18.7-17.6 13.2-18.7-4-1.2-.2-2.6-.2-13.5 21-26.2-5.8-17.4 27.6-29.2-1-20 25-33.6.2v-2q.6.4-1 0l-.3-.3-1 .2-.8-.2-.2.4q-1.9.2-1 0v2q-13.8 24.6-33.7-.4-11.8 28.6-29.2 1-12.5 27-26 6-1.6 0-2.7.2-1.1 17-18.8 4c4.6 5.5 8 12 11.3 18.6q50.9-2.2 101.3-5 50.8 2.8 101.5 5z"/>
<path fill="#21231e" d="m595 246-.6-1 .7 1-.3.4-.4.5-.4.4-.5.6-.4.5-.3.6-.4.6-.4.6-.5.5-.3.6-.4.6-.4.4-.3.6-.3.6-.3.6-.4.6-.3.5-.3.6-.4.5-.3.6-.3.7-.4.6-.3.6-.3.5-.3.6-.2.8-.3.6-.4.6-.2.6-.3.6-.3.6-.3.6-1-.4.3-.6.3-.7.3-.6.2-.5.4-.6.3-.7.2-.6.3-.6.3-.6.4-.6.3-.6.3-.6.3-.6.5-.6.3-.6.4-.6.3-.6.4-.5.2-.5.4-.6.4-.7.3-.6.4-.5.4-.6.5-.6.4-.5.3-.6.4-.6.4-.6.5-.5.4-.6.4-.5.7.7zm-19.2-4 .3-1 .5.5v1l.2 1v1l.3.7.2.8.3.7.3.7.2.6.4.5.3.5.4.4.5.3.4.3.4.3.5.2h.4l.5.2h1.9l.7-.2.7-.2.8-.3.8-.3.8-.3 1-.5.8-.6 1-.5 1-.7 1-.7 1-.7 1-1 .7 1-1 .7-1 .8-1 .7-1 .6-1 .5-1 .5-1 .5-.8.4-1 .3-.7.3-.8.2h-.8l-.8.2h-2l-.7-.3-.5-.2-.6-.3-.5-.5-.6-.4-.5-.4-.5-.5-.3-.5-.4-.7-.3-.7-.2-.7-.3-.8-.2-1v-.8l-.2-1v-1l.2.3zm.3-1 .4.2v.3zm-2.2.6-.8-.6.4-.2h2v.2h.4l-.2 1h-1.2v-.2h-1.2l.4-.2zm-.8-.6v-.2h.4zm-25.3-5.2-1-.6h1l.8 1.7.8 1.4.8 1.5.8 1.2.8 1.2.8 1.2.8 1 .8 1 .8.8.8.8.8.7.7.5 1 .4.7.3.8.3.7.3h2.3l.8-.2.8-.2.8-.3.8-.4.8-.6.8-.6.8-.7.8-.6 1-1 .7-1 .8-1 .8-1 1-1.4.8.6-.8 1.3-1 1-.7 1.2-1 1-.7 1-1 .7-.8.6-.8.6-1 .6-.7.4-1 .4-.8.2-1 .2h-3l-1-.4-.7-.3-1-.5-.7-.5-1-.6-.7-.7-1-.8-.8-1-.8-1-.8-1-.8-1.2-.8-1.3-.8-1.3-.8-1.5-.8-1.6-.8-1.7h1zm-1-.6.6-.7.4.8h-1zm-28.3-.5-.8-.6 1 .2.6 1.7.7 1.5.7 1.5.8 1.4.7 1.3.8 1.2.8 1 .8 1 .8.7.8.7.8.6 1 .5.7.4.8.2 1 .2h2.5l1-.3.8-.2 1-.4.8-.6 1-.7 1-.6 1-1 1-1 1-1 1-1.2 1-1.3 1-1.6 1-1.5 1.2-1.8 1 .6-1.2 1.7-1 1.5-1.2 1.5-1 1.3-1 1.3-1 1.2-1.2 1-1 1-1 .8-1 .7-1 .6-1 .4-1 .4-1 .4h-3l-1-.2-1-.3-.8-.6-1-.5-.8-.8-1-.8-.7-.8-1-1-.8-1-.8-1.3-.8-1.4-.8-1.5-.8-1.5-.7-1.7-.8-1.7h1zm-.8-.6.5-.6.5.8h-1zm-33.8.7h1v-.2l.8 1.5 1 1.4.8 1.2 1 1.2.8 1 1 1 .8 1 1 .8.8.7 1 .6 1 .4 1 .4.8.3 1 .3 1 .2h2l1-.2 1-.3 1-.3 1.2-.4 1-.6 1.2-.6 1-.6 1.3-.8 1-1 1.3-1 1.2-1 1-1.3 1.4-1.3 1.2-1.4 1.2-1.5.8.7-1.3 1.5-1 1.5-1.3 1.3-1.2 1.3-1.2 1-1.2 1-1 1-1.3 1-1.2.7-1 .6-1.3.6-1 .5-1.3.3-1.2.3h-1l-1.2.2h-1l-1.2-.2-1-.2-1-.3-1-.4-1-.5-1-.7-1-.6-1-1-1-.8-1-1-1-1.2-.8-1.2-1-1.3-.8-1.5-1-1.5v-.3zm0 .3v-.3zm1-2.3v2h-1v-2l.8-.5.3.5zm-1 0v-.8l.8.2-.8.5zm-1 .3 1-.4-.4-.3h1l-.2.2v.4l.4-1h.2v.2l.2.2v.6h-1.8l-.5-.3zm.5.3h-.3v-.3zm.3-1 .2.3-1 .4v-.4l.2-.7.6.2zm-.6-.4.4-.2.2.4zm-1 1 .2-1h.8l.2 1h-1zm-.4-.4-1-.4.7-.3h.8v1h-1zm-1-.4.3-.4h.3zm0 .4v-.4l1 .4-.2.4-.4.3zm.8.4v.3h-.4zm-1-.3h-1l.7.4v.2h.2v-.8h-.2.2l.2-.2h.4l.2 1h-1.9v-.2h-.2v-.5h.2v-.2h.2l.8.4zm-.7-.5.7-.3v.7zm-.3 2.5v-2h1v2.3zm1 0v.2zm-33.6 0-1-.5h1l1 1.4 1.3 1.4 1.3 1.3 1.2 1.2 1.2 1 1.2 1.2 1 1 1.2.7 1 .7 1.2.7 1 .6 1.2.4 1 .3 1 .3h1l1 .2h1l1-.2 1-.2 1-.2 1-.4 1-.5 1-.7.8-.7 1-.8.8-1 1-1 .8-1 1-1.2.8-1.2.8-1.4 1-1.5.8.5-1 1.5-.8 1.4-1 1.2-.8 1.2-1 1.2-1 1-.8 1-1 .8-1 .7-1 .8-1 .5-1 .4-1 .4-1 .3-1.2.2h-2.2l-1-.2-1.3-.3-1-.3-1.2-.5-1-.6-1.2-.6-1.2-.8-1.2-.8-1.2-1-1.2-1-1.2-1-1.2-1.4-1.2-1.3-1.3-1.5-1-1.5h.8zm-1-.5.3-.8.7.7zm-28.3 1.5-1-.4h1l1 1.6 1 1.4 1.2 1.5 1 1.2 1 1.2 1 1 1 1 1 1 1 .7 1 .8 1 .6.8.4 1 .3.8.4h2.8l.8-.2.8-.3.8-.5 1-.5.7-.6 1-.7.7-.8.8-1 .8-1 .8-1.3.8-1.3.8-1.4.8-1.5.7-1.6.8-1.8 1 .4-.8 1.7-.7 1.7-.8 1.5-.8 1.4-.8 1.3-1 1.2-.7 1-1 1-.7 1-1 .7-.8.7-1 .4-.8.5-1 .2-1 .2h-3l-1-.3-1-.3-1-.4-1-.6-1-.7-1-.8-1-1-1-1-1-1-1-1.4-1-1.3-1.2-1.5-1-1.5-1.2-1.7h1zm-1-.4.5-.8.5.7h-1zm-25.6 6.5v-1l.3.2 1 1.3.7 1.2.8 1 .8 1 .8.8 1 .8.7.8 1 .6.7.5.7.3.8.3.8.2.8.2h2.3l.8-.4.8-.3.8-.4.7-.5 1-.6.7-.8.8-.8.8-.8.8-1 1-1 .7-1.2.7-1.2.8-1.3.8-1.6.8-1.5.8-1.7 1 .4-1 1.7-.7 1.5-1 1.4-.7 1.3-.8 1.3-.8 1.2-.8 1-1 1-.8 1-.8.8-.8.7-1 .6-.7.5-1 .5-1 .3-.7.3h-2.6l-1-.2-.8-.2-1-.4-.8-.4-1-.6-.8-.6-.8-.7-1-1-.7-.8-1-1-.8-1-1-1.3-.7-1.4.4.2zm0-1h.2v.2l-.3-.2zm-2.2.8h-1l.4-.5h1.2v-.2h1.5v1H389v.2h-.4zm-1 0v-.4h.4zm-18 3.6-.6.7.6-1 1 1 1.2.6 1 .7 1 .6 1 .4.8.5 1 .4.7.3.8.3.8.3.8.2h.7l.7.2h1.7l.4-.2.5-.2.4-.3.4-.3.4-.3.4-.4.3-.5.2-.5.3-.6.3-.7.3-.7.3-.8v-.8l.3-1v-.8l.2-1h1v1l-.2 1-.2 1-.2 1-.2.7-.2.8-.3.8-.4.7-.2.6-.5.6-.4.5-.6.5-.5.4-.7.2-.6.2-.6.2h-3.3l-.8-.3-.8-.3-1-.3-.8-.4-1-.5-.8-.5-1-.5-1-.6-1-.7-1-.8-1.2-.8.7-.8zm11 18.5v1l-.4-.3-.3-.6-.2-.6-.3-.6-.3-.6-.4-.6-.3-.6-.3-.7-.3-.5-.4-.6-.4-.7-.3-.6-.4-.6-.3-.5-.4-.6-.4-.7-.4-.5-.3-.6-.4-.6-.4-.6-.4-.6-.3-.5-.4-.7-.3-.6-.3-.5-.4-.6-.4-.5-.4-.5-.4-.5-.4-.6-.4-.5-.4-.6-.4-.5.7-.8.4.5.4.6.4.5.4.6.4.5.4.5.4.5.4.6.4.6.4.5.3.6.4.6.4.5.4.6.4.6.3.6.5.6.3.6.3.6.5.6.3.6.3.6.5.6.3.7.3.7.4.6.3.6.4.5.4.6.3.7.3.7.3.6-.5-.3zm0 1h-.3v-.3zm101.4-6v1l-3.2.2h-3.2l-3 .3h-3.3l-3 .3-3.3.2H460l-3 .3-3.3.3h-3.2l-3 .3H444l-3 .3h-3.3l-3.3.2-3 .2h-3.3l-3.2.3h-3l-3.3.3-3.3.2h-3l-3.3.3h-3.2l-3 .2-3.3.2h-3.3l-3.2.2h-3l-3.3.3H384l-3 .2-.2-1h3.2l3.2-.3h3l3.3-.2 3.2-.2h3l3.3-.3h3.2l3-.3h3.3l3.2-.3h3l3.3-.2h3.2l3-.3h3.3l3.2-.3 3-.2h3.3l3.2-.3h3l3.3-.3 3.2-.2h3l3.3-.3 3-.3h3.3l3.2-.3 3-.2h3.3l3.2-.3 3-.3h.2zm101.2 5v1h-3l-3.3-.2-3-.2h-3.3l-3-.3h-3.3l-3.2-.3h-3l-3.3-.2-3.3-.2h-3l-3.3-.3-3.2-.2h-3l-3.3-.3h-3.3l-3-.3h-3.3l-3.2-.3-3-.3h-3.3l-3.3-.3h-3l-3.3-.3h-3l-3.3-.2-3-.2-3.2-.2h-3.2l-3.2-.3h-3l-3.3-.2.2-1h3l3.3.3 3.2.2 3 .2 3.3.2h3l3.3.3h3.2l3 .2 3.3.2 3.2.2h3l3.3.3h3l3.3.2 3.2.2h3l3.3.3h3.3l3 .3h3.3l3.2.3h3l3.3.3h3.3l3 .3h3.3l3.2.2h3.2l3 .2 3.3.2h3.2zm0 1v-1l.5.7zm.5-.3-.2.3h-.4z"/>
<path fill="#edb92e" d="m505 276.2-23 11.4-22.6-11.4 22.7-11.5z"/>
<path fill="#edb92e" d="M474 259.2v.2q-26.4 1-93.2 5.3l6.5 31c29.6-.8 64.3-2 94.8-3 30.7 1 65.3 2.2 95 3l6.5-31q-66.9-4.2-93-5.3v-.2q2.6-.5-8.4-.2c-7-.2-10-.2-8 .2z"/>
<path fill="#edb92e" d="m597.3 236.7-3.7-.6c-7 17.5-13.4 18-14.6 1.4 1.6-.6 1-3.3 2.8-2.2 1 .5 2-1 1.3-2q-2.9-2.2-5.4.4c-.8-2.7-3-4-5.4-2.8-1 .5-.7 2.5.4 2.4 1.8-.2 1 1.6 1.6 3-12.5 16.4-19 14.4-17.4-7.3-3-.7-6.5-1.3-9.6-2.4-11.5 23.3-19.7 24.3-26.4 3.4 1.4-1 1-3.8 3-3 1.3.6 2.3-1.5 1.2-2.4-2.4-2-5-1.3-6.8 1.3-1.4-3-4-4-6.6-2.2-1.2.7-.5 3 .8 2.6 2.3-.7 1.3 2.4 3 3.5-5 19.6-30.8 21-26.6-8.2q-3.5 0-6.6.2l-6.7-.2c4.2 29-21.5 27.8-26.6 8 1.6-1 .6-4 3-3.4 1.2.3 2-2 .7-2.7-2.6-1.7-5.2-.7-6.6 2.2-1.7-2.7-4.3-3.4-6.7-1.4-1 1 0 3 1 2.5 2.2-.8 1.8 2 3.2 3-6.7 21-15 20-26.4-3.4-3 1-6.5 1.7-9.5 2.3 1.6 21.6-5 23.6-17.4 7 .6-1.2 0-3 1.7-2.8 1 0 1.5-2 .4-2.4-2.4-1-4.5 0-5.3 2.8q-2.5-2.7-5.5-.4c-.8 1 .4 2.5 1.3 2 1.8-1 1.2 1.6 2.8 2.2-1.2 16.5-7.6 16-14.6-1.3l-3.7.7c0 4 1.3 8.5 3.8 11.7 10.6 13.5 18.5 10.4 18.8-4.7 14 16.4 24.4 14.3 26.2-3 11.7 21.8 30.5 11.4 31-2 5 19.4 33.5 19.2 35.4-3 1.8 22.2 30.3 22.4 35.4 3 .4 13.4 19.2 23.8 31 2 1.7 17.3 12.3 19.4 26 3 .4 15 8.3 18.2 19 4.7 2.4-3.2 3.8-7.6 3.7-11.7z"/>
<path fill="#21231e" d="m593.7 235.6 3.7.6-.2 1-3.6-.6-.4-.7zm-.5.3v-.4h.5zm-14.3 1 .2 1 .3-.6.2 1.4v1.4l.3 1.3.2 1.2.2 1 .3 1 .3 1 .3.7.3.7.3.6.3.5.3.5.4.2.3.2h1.3l.4-.3.4-.2.5-.4.4-.3.6-.6.5-.6.6-.7.7-1 .6-.8.7-1.2.6-1.2.6-1.3.7-1.3.5-1.5.7-1.6 1 .3-.8 1.6-.6 1.4-.7 1.4-.5 1.3-.7 1.3-.6 1-.6 1-.7 1-.7.8-.6.8-.6.6-.5.5-.6.3-.7.4-.5.2h-1.2l-.6-.2-.5-.2-.5-.4-.5-.4-.4-.6-.3-.6-.3-.8-.4-.8-.3-1-.2-1-.3-1-.2-1.3-.2-1.3-.2-1.5v-1.6l.3-.5zm-.5.4v-.4h.4zm3.5-2.7-.5 1v-.2h-.4v.2h-.2v.2l-.2.2v.3l-.2.3-.2.3v.2h-.2v.3h-.3v.2h-.3l-.2.2-.3-1v-.2h.2v-.2h.2v-.2l.2-.2v-.5h.2v-.4l.2-.2v-.3l.2-.2.2-.2h.2l.2-.2h1l.2.2zm.8-1 .6-1v.2h.2l.2.3v1.6h-.2v.2h-.2v.2h-.2v.2h-.2v.2h-.5v.2h-.8l-.3-.2.5-1v.2h.4v-.2h.2v-.2h.2v-.8zm.6-1v.2zm-6.3 1 1-.3-.8-.2h.2l.2-.3.2-.2.2-.2.3-.2h.2l.2-.2h.2v-.2h.4l.2-.2h2.3l.2.2h.2l.2.2h.2l.2.2h.2l.2.2.2.2-.6.8-.3-.2h-.2l-.2-.2-.2-.2h-.3l-.2-.2h-2.1v.2h-.3v.2h-.2l-.3.2v.2h-.3v.2l-.3.2zm1 .3-.7.5-.2-.8 1 .3zm-5.6-2.7-.4-1h.5l.3-.2h2.1l.4.2h.4l.2.3h.2v.2l.3.2h.2l.2.2v.2l.2.2v.2l.2.2.2.2v.2l.2.3v.2l.2.2v.2l-1 .3v-.5h-.2v-.3l-.2-.2v-.2h-.3v-.3h-.2v-.4h-.3v-.2h-.3v-.2h-.6v-.2h-2l-.2.2zm0 1.4.2 1h-.6v-.2h-.2l-.2-.2-.2-.2-.2-.2V231l.2-.2.2-.2.3-.2.4 1h-.2v1.2h.2zm2.2 3.7-.8-.6v.5-.2h-.2v-1h-.2v-1l-.2-.2h-.7v-1.2h.6l.3.2.2.2h.2v.2l.2.3v.4h.2v1.5h.2v.8zm0-.5h.2l-.2.4zm-18-6.6.3-1 .3.6v2l-.2 1.8v4.9l.3 1.4v1.3l.3 1 .2 1 .3 1 .4.8.3.8.4.5.5.5.4.3.4.3.5.2h1l.6-.2.7-.3.7-.4.8-.5 1-.4.7-.8 1-.8 1-1 1-1 1-1 1-1.4 1-1.4 1.3-1.4.8.6-1.2 1.5-1 1.3-1.2 1.3-1 1-1 1.2-1 1-1 .8-1 .8-.8.6-1 .5-.7.4-.8.3h-.8l-.8.2h-.7l-.7-.2-.6-.3-.6-.5-.5-.5-.4-.7-.5-.7-.4-1-.2-1-.2-1-.2-1.2-.2-1.3V229l.5.6zm.3-1h.3v.6zm-9.3-1.6-1-.4.7-.3.3.2h.3l.3.2h.3l.2.2h.3l.4.2h.6l.3.2h.3l.4.2h.6l.3.2h.5l.3.2h.7l.3.2h.6l.3.2h.5l.3.2h.8l.3.2h.3l-.3 1h-.8l-.2-.2h-.6l-.3-.2h-.8l-.3-.2h-.6l-.4-.2h-.7l-.4-.2h-.3l-.3-.2h-.6l-.3-.2h-.2l-.3-.2h-.6l-.4-.2h-.3l-.4-.2h-.4l-.3-.2h-.2l.7-.4zm-1-.4.3-.4h.4l-.6.4zm-26.2 3.2.6.8.3-.6.6 2 .5 1.7.7 1.6.6 1.6.7 1.4.7 1.2.6 1 .7 1 .7 1 .7.6.6.6.7.4.7.4.7.2h1.4l1-.2.6-.2.8-.4 1-.6.7-.6 1-.8 1-1 .8-1 1-1.2 1-1.3.8-1.4 1-1.6 1-1.7 1-1.8 1-2 1.2-2 1 .3-1.2 2.2-1 2-1 1.8-1 1.8-1 1.6-1 1.5-1 1.3-1 1.2-1 1-1 1-1 1-.8.7-1 .5-.8.4-1 .2-.8.2h-1.8l-.8-.3-.8-.3-.8-.5-.8-.6-.7-.8-.8-1-.7-1-.7-1-.7-1.4-.7-1.4-.7-1.5-.7-1.7-.6-1.7-.7-1.8.2-.6zm-.2.6v-.4l.2-.2zm3.8-3.6-.3 1h-.3l-.2-.2h-.2v.2h-.2v.2h-.2v.5h-.2v.4l-.2.3-.2.2v.2l-.2.3-.3.3-.2.3h-.2l-.5-.7.2-.2.2-.2v-.5h.2v-.4h.2v-.3l.2-.3v-.2l.2-.2.2-.3.2-.2h.2v-.2h.5v-.2h.7l.2.2zm.7-1.6.6-.8.2.2.2.2v.2l.2.3v1.3l-.2.3v.2l-.2.2h-.2v.2h-.2v.2h-.2l-.2.2h-1.1l.4-1h.7v-.2h.2v-.6h.2v-.7h-.2v-.2zm-7 1 1-.3h-1l.2-.3v-.3h.3l.2-.3.2-.3.2-.2.3-.3.3-.3h.2l.2-.3h.3l.2-.2h.5l.2-.2h2.2l.3.2h.2l.3.2h.3l.3.3h.2l.3.3.3.2-.6.8-.2-.2h-.2l-.2-.3-.4-.2h-.2l-.2-.2H521v.2h-.5l-.2.3h-.2v.2h-.2l-.2.3-.2.2-.3.3v.2l-.2.3-.2.2h-1zm1 .2-.6.7-.3-1 1 .2zm-7-2-.4-1h.3l.2-.2h.3l.4-.2h.2l.3-.2h.4l.3-.2h1.6l.2.2h.2l.3.2h.2l.2.3.3.2.2.2v.2l.3.2.2.2.2.2v.2l.3.2.2.3.3.4v.3l-.8.4-.2-.4-.2-.2v-.2l-.3-.2v-.2l-.3-.2-.3-.3h-.2v-.2h-.3l-.3-.2h-.2l-.3-.2h-.4l-.2-.2h-1.2l-.2.2h-.5l-.2.2h-.2l-.2.2-.2.2zm.5 1.7.3 1h-.5l-.2-.2h-.2l-.2-.2-.2-.2-.2-.2v-.2l-.2-.2V224l.2-.2.2-.3.3-.2.5 1h-.2v1.2h.2v.2h.4zm3.7 4-1-.2.2.5h-.2l-.2-.2h-.2v-.3h-.2l-.2-.3v-.4l-.2-.2v-.3l-.2-.2v-.4l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2h-.6l-.4-1h1.1l.3.2h.2v.4h.2v.3l.2.2v.2h.2v.5l.2.3v.5h.2v.4h.2l.2.2.2.2.3.6zm-.3-.5.3.2v.3zm-27-7.2v-1l.6.5-.3 2.7-.2 2.4v2.2l.2 2 .3 2 .4 1.7.5 1.5.7 1.4.6 1 .8 1.2 1 1 1 .7 1 .6 1 .5 1 .4 1.3.3h2.4l1.3-.2 1.2-.3 1.2-.5 1.2-.5 1-.7 1.2-1 1-.8 1-1 1-1.3 1-1.3.8-1.3.8-1.5.6-1.7.5-1.7 1 .2-.6 2-.7 1.6-.8 1.6-1 1.5-1 1.4-1 1.3-1 1-1.2 1-1.2 1-1.2.8-1.3.7-1.2.5-1.3.3-1.4.2h-2.5l-1.2-.3-1.3-.4-1.2-.5-1-.7-1-1-1-1-1-1-.8-1.5-.7-1.5-.6-1.6-.4-1.8-.3-2-.2-2.2V227l.2-2.6.3-2.7.5.6zm0-1h.7v.5l-.6-.5zm-6.6 1.2.2-1h4l.2-.2h2.1v1h-4.1l-.2.2h-2zm-6-.8h-1l.5-.4h5.1l.2.2h1.2v1h-4l-.3-.2h-2l.5-.6zm-1 0v-.5h.5zm-26.4 7.8.5.8.3-.5.6 1.7.5 1.7.7 1.5 1 1.4.8 1.4 1 1.2 1 1 1 1 1.2 1 1.2.6 1.2.5 1.2.5 1.2.3 1.2.2h2.4l1.2-.3 1-.3 1.2-.4 1-.7 1-1 .8-.8 1-1 .6-1.3.6-1.5.6-1.6.4-1.8.3-2v-2l.2-2.3-.2-2.5-.3-2.7h1l.3 2.6.2 2.5v2.2l-.2 2.2-.3 2-.5 1.8-.5 1.6-.7 1.5-.8 1.4-1 1.2-1 1-1 .8-1 .7-1.2.5-1.3.4-1.3.3H463l-1.2-.2-1.3-.3-1.3-.5-1.3-.6-1.3-.7-1.2-1-1.2-1-1-1-1-1.2-1-1.3-1-1.5-.7-1.6-.7-1.7-.6-2 .2-.5zm-.2.5v-.3l.2-.2zm3.7-4-.2 1-.2-.2h-.7v.2l-.2.2v.4l-.2.2v.5l-.2.2v.2l-.2.3v.2l-.2.2v.2h-.2v.3h-.3l-.2.2h-.2l-.4-.7.2-.2.2-.2h.2v-.3l.2-.2v-.5l.2-.2v-.5l.2-.3v-.3l.2-.2.3-.3.2-.2h.2l.2-.2h1.1zm.4-1.8.6-1v.2h.3v.3h.2v.2l.2.2v1.5l-.2.2v.2l-.2.2-.3.2-.2.2h-.9l.3-1h.4v-.4l.2-.2v-.8h-.2v-.2h-.2zm-6.7 2 .8-.6h-.8v-.2l.2-.3.3-.4v-.2l.3-.2.2-.2.2-.2.2-.2.2-.2.2-.2h.2l.3-.3.3-.2h.2l.3-.2h.5l.2-.2h1.6l.4.2h.2l.3.2h.2l.3.2.2.2h.3l-.6 1-.2-.2h-.2l-.2-.2h-.3l-.2-.2h-.5l-.2-.2h-1.3l-.2.2h-.3v.2h-.3l-.2.3-.2.2-.3.3-.3.3-.2.2v.2l-.2.2-.2.2v.3h-.2zm1 0-.5.7-.5-.8h1zm-7-1.2-.8-.8.2-.2.2-.2h.3l.2-.3h.2l.2-.2h.3l.3-.2h2.5l.3.2h.2l.3.2.2.2h.2l.2.3h.2l.2.2.2.2.2.3.2.2h.2v.4l.3.2-.8.6-.2-.2v-.3l-.3-.2-.2-.2-.3-.3-.2-.2h-.2l-.2-.2v-.2h-.2l-.2-.2h-.4l-.2-.2H441l-.2.2h-.2l-.2.2h-.2l-.2.3h-.2l-Line truncated
<path fill="#edb92e" d="M482 263h-22.5l-1.3.2H451l-1.3.2h-4.4l-1.2.2h-2.3l-6.3.4h-.3l-9 .4h-.8l-18.2 1.3h-.2l-26.4 2.4c-5 .6-6-5.3 0-6l26.3-2.2a984 984 0 0 1 37-2.2h2.4l15.4-.5h40.6l15.2.5h3.3a984 984 0 0 1 37 2.3l26.2 2c5.8.7 5 6.6 0 6l-26.4-2.3h-.3l-18.3-1.2h-.6l-9.2-.5h-.3q-3.3 0-6.4-.3h-2.3l-1.2-.2h-4.5l-1.3-.2H506l-1-.2z"/>
<path fill="#0c4076" d="M482 105a17.3 17.3 0 0 1 .1 34.7c-9.3 0-17-7.8-17-17.3s7.7-17.3 17-17.3z"/>
<path fill="#edb92e" d="M485.3 85c1.6-.2 3-1.5 3-3.2s-1.4-3-3-3.2c0-1.7-1.4-3-3.2-3a3 3 0 0 0-3 3c-1.6 0-3 1.5-3 3.2a3 3 0 0 0 3 3v6.5h-5.5a3 3 0 0 0-3-3c-1.8 0-3 1.3-3.2 3a3 3 0 0 0-3 3c0 1.8 1.3 3.2 3 3.2a3 3 0 0 0 3 3c1.8 0 3.2-1.2 3.2-3h5.5V119h6.3V97.6h5.5a3 3 0 0 0 3 3c1.8 0 3-1.4 3.2-3 1.7 0 3-1.5 3-3.2a3 3 0 0 0-3-3c0-1.8-1.4-3.2-3-3.2-2 0-3.2 1.4-3.2 3h-5.5V85z"/>
<path fill="#21231e" d="M487.8 81.8h1v1h-.2v.5h-.2v.3l-.2.3-.2.2v.2h-.2l-.2.2-.2.2h-.2v.2h-.2l-.2.2h-.2v.2h-.8l-.2.2h-.3v-1h.7V84h.2l.3-.2h.2v-.2h.2v-.2l.2-.2V83h.2l.2-.2v-.4l.2-.2zm-3-3.2h1l-.5-.5h.2l.3.2h.4l.3.2h.3l.2.2.2.2h.2v.2h.2l.2.2.2.3.2.2v.2h.2v.4h.2v.5l.2.2v.7h-1V81h-.2v-.4l-.2-.2V80h-.4v-.2h-.2v-.2h-.2l-.3-.2h-.2V79h-.8l-.4-.6zm.4.5h-.4v-.4zm-3-3v-1h.7l.2.2h.2l.3.2h.2l.2.2.2.2h.2v.3l.2.2.2.3.2.2v.2h.2v.4h.2v.8h.2v.3h-1V78h-.2v-.5h-.2v-.2l-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2l-.2-.2h-.4zh-.4zm-3.2 3v-1l-.5.6v-.8l.2-.2V77h.2v-.6h.2v-.2l.2-.2.2-.2.2-.2h.2v-.2h.3v-.2h.3l.3-.2h.4l.2-.2h.4v1h-.5v.2h-.3l-.2.2h-.2v.2h-.2v.2h-.2v.2l-.2.2v.2h-.2v.5h-.2v1l-.6.5zm.5-.4v.5h-.5zm-3 3.2h-1V81l.2-.3v-.5l.2-.2v-.2l.2-.2.2-.3.2-.2h.2l.2-.2h.2v-.2h.3v-.2h.5l.3-.2h.7v1h-.7l-.2.2h-.2l-.2.2-.2.2-.2.2-.2.2v.2h-.2v.4l-.2.2v.8zm3 3h-1l.5.6h-1V85h-.7v-.2h-.3v-.2h-.2l-.2-.2-.2-.2V84h-.2v-.2l-.2-.2-.2-.3v-.5h-.2v-1h1v1h.2v.4h.2v.2h.2v.2l.2.2h.2v.2h.2l.3.2h.7v.2h.2zm-.5-.4h.5v.5l-.5-.6zm-.5 7V85h1v6.3l-.5.5-.5-.5zm1 0v.4h-.5l.5-.5zm-6-.6h5.5v1h-5.5l-.5-.5zm0 1h-.5v-.5zm-3-3v-1h1.2v.2h.3l.3.2.3.2.3.2v.2l.2.2.2.3.2.3.2.3v.4l.2.2v.8h-1v-.7l-.2-.2V90h-.2v-.2h-.2v-.2l-.2-.2-.2-.2h-.2V89h-.4l-.2-.2h-.8zm-3.2 3v-1l-.5.4v-.8l.2-.3V90l.2-.3v-.2h.2v-.2l.2-.2h.2v-.3h.2l.2-.2.2-.2h.2V88h.7v-.2h1.1v1h-1l-.2.2h-.2v.2h-.2v.2h-.2l-.2.2v.2h-.2v.4h-.2v.8l-.2.2v.3l-.5.5zm.5-.5v.5h-.5zm-3 3h-1V93h.2v-.2h.2v-.3l.2-.3.3-.3v-.2h.2v-.2h.2v-.2h.2l.2-.2h.2V91h.7l.2-.2h.8v1h-.7l-.2.2h-.2v.2h-.2v.2h-.2l-.2.2-.2.2v.2h-.2v.5h-.2v.7zm3 3.2h-1l.5.5h-1v-.2h-.5l-.2-.2h-.2v-.2h-.2l-.2-.2-.2-.2v-.2h-.3v-.2l-.2-.2V96h-.2v-.4l-.2-.3v-.8h1v.9h.2v.2l.2.3h.2v.2h.2v.2h.2v.2h.2l.2.2h.5v.2h.4l.6.5zm-.5-.5h.5v.5zm3 3v1h-1l-.2-.2h-.3v-.2h-.3v-.2h-.2l-.2-.2-.2-.3v-.2h-.2v-.3h-.2V99h-.2v-.5l-.2-.2V98h-.2v-.5h1v.7h.2v.4h.2v.2l.2.2v.2h.2v.2h.2v.2h.2l.2.2h.2l.3.2h.6zm3.2-3v1l.5-.4v.8l-.2.2v.3h-.2v.3l-.2.3-.2.2-.2.2-.2.2-.3.2-.2.2h-.2l-.2.2h-.2l-.3.2h-.8v-1h.9v-.2h.2l.2-.2h.2v-.2h.2l.2-.2V99h.2v-.2l.2-.3v-.2l.2-.2v-.4zm-.5.6V97h.5l-.5.6zm6 .5h-5.5v-1h5.5l.5.6zm0-1h.5v.6zm-.5 22V97.6h1V119l-.5.5-.5-.5zm.5.5h-.5v-.5zm6.3 0H479v-1h6.3l.5.5zm.5-.5v.5h-.5zm0-21.4V119h-1V97.6l.5-.5.5.6zm-1 0V97h.5l-.5.6zm6 .5h-5.5v-1h5.5l.5.6zm0-1h.4v.6zm3 3v1h-1l-.3-.2h-.2l-.3-.2-.2-.2h-.2v-.2h-.2l-.2-.2v-.2h-.2v-.2l-.2-.2V99h-.2v-.2h-.2v-1l-.2-.2h1v.4l.2.2v.2l.2.3v.2h.2v.2h.2l.2.2v.2h.2l.3.2h.2v.2h1.1zm3.2-3v1l.5-.5v.7h-.2v.5l-.2.3v.4h-.2v.3h-.2v.2h-.2v.2h-.3v.2l-.3.2h-.3l-.2.2h-.3l-.2.2h-.8v-1h.9v-.2h.2v-.2h.2l.2-.2.2-.2V99h.2v-.2l.2-.2v-.2l.2-.2v-.6l.6-.6zm-.5.5V97h.5l-.5.5zm3-3h1v.9l-.2.3v.2h-.2v.6h-.2l-.2.2v.2h-.2v.2h-.3v.2h-.2v.2h-.3l-.3.2h-.2l-.3.2h-.7v-1h.8V97h.2v-.2h.2l.2-.2.2-.2h.2V96l.2-.3v-.2h.2v-.7l.2-.3zm-3-3.2h1l-.5-.6h.7l.2.2h.4l.3.2.3.2.3.2.2.2.2.3.2.2v.5h.2v.3h.2v.7l.2.3v.4h-1v-.6h-.2v-.4l-.2-.2-.2-.2v-.2h-.2l-.2-.2-.2-.2h-.2V92h-.5l-.2-.2h-.5l-.5-.5zm.5.5h-.5v-.5zm-3-3v-1h1.1l.2.2h.4v.2h.3v.2l.3.2.2.3h.2v.3h.2v.4l.2.2v.4h.2v.7h-1v-.8h-.2V90l-.2-.2-.2-.2-.2-.2-.2-.2h-.2V89h-.5v-.2h-.6zm-3.2 3v-1l-.6.5v-.9l.2-.2V90l.2-.3.2-.3.2-.3h.2v-.2l.2-.2h.2v-.2h.2l.2-.2h.3l.2-.2h.3l.2-.2h1v1h-1l-.3.2h-.2v.2h-.2l-.2.2v.2l-.2.2-.2.2v.2l-.2.2v.7l-.7.6zm.5-.5v.5h-.6zm-6-.5h5.5v1h-5.5l-.5-.5zm0 1h-.5v-.5zm.5-7v6.5h-1V85l.4-.6.6.5zm-1 0v-.4h.4zm.5 0v-.4zm-.5 0v-.4h.4z"/>
<path fill="#21231e" d="M499.8 122.4h-1v-1.7l-.2-1v-.7l-.3-.8-.2-.8-.2-.8-.3-.8-.3-.7-.4-.6-.4-.7-.4-.7-.5-.7-.5-.6-.5-.6-.6-.5-.7-.6-.6-.6-.6-.5-.5-.6-.7-.4-.7-.4-.6-.4-.8-.3-.7-.4-1-.3-.7-.2h-.8l-.8-.2h-.8l-1-.2h-1v-1h2l.8.2h1l.8.3.8.4.8.2.8.4.8.3.8.4.7.4.7.6.7.5.7.6.6.6.6.6.6.6.6.6.5.7.5.7.5.7.4 1 .4.6.4.8.3.8.3 1 .3.7.2.8v1l.2.7v2zM482 140.2v-1h1.8l1-.2.7-.2.8-.2.8-.2 1-.3.6-.2.8-.3.7-.4.8-.3.7-.4.6-.5.7-.5.6-.5.6-.6.5-.5.5-.6.5-.6.5-.6.4-.7.4-.6.4-.8.3-.7.3-1 .3-.6.3-.8.2-.8v-1l.2-.7v-1.6h1v2.8l-.2.8-.2.8-.3 1-.3.7-.3.8-.3.8-.3 1-.4.6-.5.7-.5.7-.5.7-.6.6-.6.7-.6.6-.6.5-.7.6-.7.5-.7.6-.7.4-.8.4-.8.4-.8.3-.8.4-.8.2-1 .2-.8.2h-.8l-1 .2h-1zm-17.5-17.8h1v1.8l.2.7v.8l.3.8.2.8.3.7.3 1 .3.6.5.8.4.7.3.8.5.6.5.6.5.7.6.5.5.6.5.5.7.5.6.5.7.4.7.4.7.5.7.3.7.3.8.4.8.2.8.2.8.2h1.8l.7.2v1h-1.8l-.7-.2-1-.2-.8-.2-.8-.2-1-.3-.7-.2-.8-.4-.8-.4-.7-.4-.7-.5-.7-.4-.7-.5-.7-.4-.7-.6-.5-.7-.5-.6-.5-.7-.5-.7-.5-.7-.4-.7-.4-1-.3-.7-.5-.8-.2-.8-.3-1-.2-.7v-1l-.2-.8v-1.8zm17.6-17.8v1h-1.7l-.7.2h-.8l-.8.3-.8.3-.8.3-.7.3-.8.2-.8.4-.7.4-.7.4-.6.5-.7.4-.6.5-.6.5-.6.6-.5.7-.5.6-.5.7-.4.7-.5.7-.4.7-.2.8-.3.8-.3.8-.2.8-.2.8v.8l-.2 1v1.7h-1v-2.8l.2-1 .2-.8.3-1 .2-.8.4-.8.2-.8.4-.7.4-1 .5-.6.5-.7.5-.7.6-.6.6-.6.7-.6.6-.6.6-.5.7-.4.7-.5.7-.3.8-.4.8-.3.8-.4 1-.2.7-.3h1l.8-.2h.8l1-.2h.8z"/>
<path fill="#edb92e" d="M499 118.8a18 18 0 0 1 0 6q-8.3-.3-17-.3-8.3 0-17 .3a18 18 0 0 1 .3-6l16.8-.3q8.6 0 17 .3z"/>
<path fill="#21231e" d="M499.8 122.4h-1V120h-.2v-1l1-.2v1.7l.2.2v1.6zm-.7 3 .2-1-.6.3v-.8l.2-.2v-1.1h1v2.2h-.2zm.6-.5v.3h-.5l.6-.4zm-17.5 0v-1H494l.4.2h4.8v1h-10.6l-.5-.2h-6zm-17.4 0 1-.3-.5-.4h4.7l.4-.2H482v1h-11l-.5.2H465l-.5-.3zm.6.3h-.5v-.4l.5.3zm-.7-3h1v2.4l-1 .3v-2.6zm.8-4v1l.5-.3v.8l-.2.2v2.5h-1v-3h.2v-.4l.5-.5zm-.5.5v-.4h.5zm17.3-.8v1h-9.1l-.5.2H466l-.7.2v-1h2.5l.5-.2h9l.5-.2h4.2zm17.5.8-1 .2.4.4h-3.6l-.5-.2h-10.3l-.5-.2h-2.1v-1h10l.5.2h6.1l.5.6zm-.5-.5h.4v.5z"/>
<path fill="#edb92e" d="M468.8 133.2c-3.6-2.3-4.3 1.2-4.2 4.7.4 13.2 3 25.8-.6 42-1.4 6.4-3.7 10-1.5 11.3q8.6 5 7.4 7.7c1.5 3.2 5.7 2.6 12 1.6 6.6 1 10.7 1.6 12.4-1.6-1-1.6 1.7-4.5 7.4-7.7 2.2-1.2 0-5-1.5-11.3-3.5-16.2-1-28.8-.7-42 0-3.6-.5-7-4-4.8a29 29 0 0 1-26.8 0z"/>
<path fill="#21231e" d="M465 138h-1v-1.5l.2-.3v-1.6l.2-.3v-.3h.2v-.4l.2-.2v-.3h.2l.2-.3.2-.2.2-.2h.2l.2-.2h1.8l.4.2.4.2h.3l.4.3-.4 1-.4-.3h-.2l-.3-.2h-.3l-.2-.2h-1.2v.2h-.2v.2l-.2.2v.2l-.2.2v.4l-.2.3v.4l-.2.2v2.5zm-.5 42-1-.2.3-1.5.3-1.4.3-1.6.2-1.4.2-1.5.2-1.4v-2.5l.2-1.4V154l-.2-1.2v-2.5l-.2-1.2V148l-.2-1.2v-1.2l-.2-1.3V142l-.2-1.3V138h1v3.5l.2 1.2v2.6l.2 1.2v1.3l.2 1.2v2.5l.2 1.2v1.3l.2 1.3v13.3l-.2 1.4v1.4l-.3 1.5-.2 1.4-.2 1.6-.2 1.4-.2 1.5-.3 1.6zm-1.8 11-.4.8-.3-.2-.2-.2-.2-.2-.2-.2v-.2l-.2-.3V188l.2-.3v-.4l.2-.3v-.4l.2-.4v-.5l.3-.4.2-.4v-.5l.3-.3v-.5l.3-.5v-.6l.3-.4.2-.6v-.6l.3-.6 1 .3-.2.7v.6l-.3.5v.6l-.2.5-.2.4v.6l-.3.4v.5l-.3.4v.4l-.2.4v.4l-.2.4v.4l-.2.3v.4l-.2.4v1.9zl.2.2zm7.6 7.8-1 .4v-.4h.2v-1l-.2-.2v-.2l-.2-.2v-.4l-.3-.2-.2-.2-.2-.3h-.3l-.2-.4-.3-.2-.3-.3h-.3l-.4-.4-.4-.3-.3-.2-.5-.2-.4-.3-.3-.3-.5-.3-.5-.3-.4-.3-.5-.3-.6-.2.5-1 .6.4.5.3.5.3.4.3.5.4.4.3.5.3.5.2.3.3.4.3.4.3.3.4.2.2.3.3.3.2.2.2.2.3.3.2.2.2v.2l.3.3v.2l.3.2v.5l.2.2v1.1l-.2.3v-.5zm-1 .4v-.4zm13 1-.2 1h.2-1.2l-.5.2h-1.2l-.6.2h-1l-.5.2h-4.7l-.4-.2h-.3l-.3-.2-.3-.2-.3-.2-.2-.2-.3-.2-.2-.2-.3-.3h-.2l-.2-.4-.2-.4 1-.4v.2l.2.3.2.2.2.2.2.2h.2l.2.2h.2l.3.2h.3l.3.2h.3l.2.2h4.3l.5-.2h1l.6-.2h1.1l.5-.2h.8zm-.2 0h.2zm12-1 1-.4v.4l-.3.4-.2.3h-.2l-.3.4-.2.2-.3.2-.3.2-.3.2-.4.2h-.3l-.3.2h-.4l-.4.2H486l-.6-.2h-.5l-.6-.2h-1.2l-.5-.2h-.6l.2-1h1.8l.5.2h.6l.6.2h1.1l.5.2h4.2l.3-.2h.3l.2-.2h.3l.2-.2.2-.2.3-.3.2-.2.2-.2v-.3l.3-.2v.5zm1-.4v.4zm6.5-8 .5 1-.5.3-.5.4-.5.3-.4.2-.4.2-.4.3-.4.3-.4.3-.4.3-.3.3-.3.3h-.3l-.2.4-.3.2-.3.3h-.2l-.2.4-.2.2-.2.2v.2l-.3.2v.7h-.2v1.2l-.8.5v-.2l-.2-.2v-1.3l.2-.2v-.3l.3-.3v-.3l.3-.2.2-.3.2-.3.3-.3.2-.2.3-.3.3-.4.4-.2.3-.3.4-.2.4-.3.4-.3.4-.2.4-.3.5-.3.5-.3.5-.2.5-.3.5-.3.5-.3zm-1.7-10.7 1-.2v.6l.3.6.2.6.2.5v.7l.3.5v.5l.2.4v.6l.2.5.2.5v.4l.2.3v.4l.2.4v.5l.2.3v.7l.2.3v1.8l-.2.3v.2l-.3.3h-.2l-.2.3-.3.2-.5-1h.2l.2-.2v-.4h.2v-2.5l-.2-.3v-.3l-.2-.3v-.4l-.2-.4v-.4l-.2-.4v-.4l-.3-.5v-.6l-.2-.4v-.6l-.3-.5v-.6l-.3-.6v-.5l-.2-.6-.2-.6zm-.7-42h1v5.9l-.2 1.4v1.2l-.2 1.2v1.3l-.2 1.3v2.5l-.2 1.2v2.6l-.2 1.2v9.3l.2 1.4v1.4l.2 1.3.2 1.5.2 1.5.2 1.4.2 1.5.3 1.3.3 1.5-1 .3-.3-1.5-.3-1.5-.2-1.4-.2-1.5-.2-1.3-.2-1.5v-1.4l-.2-1.4V167l-.2-1.3v-10.3l.2-1.3v-2.5l.2-1.3V149l.2-1.2v-1.3l.2-1.2V143l.2-1.2zm-3.3-4.4-.5-1h.4l.3-.2.2-.2h.3l.4-.2h1.2l.3.2h.2l.2.3v.2l.2.2h.2v.4l.2.2v.3h.2v.7l.2.3v1.5l.2.3v1.1h-1v-2.4l-.2-.3v-.8l-.2-.3v-.4l-.2-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-1.1l-.2.2h-.3l-.3.2-.3.2zm-27 0 .4-1 .8.5.7.5.7.4.7.2.7.3.8.2.8.3 1 .2.7.2 1 .2h.7l1 .2h.8l1 .2h4.6l.7-.2h1l.8-.3h.8l1-.3.7-.2.8-.3.8-.2.8-.3.7-.4.8-.3.7-.4.7-.3.5 1-.7.3-.7.4-.8.4-.7.3-1 .4-.7.3-.7.3-1 .2-.7.2-1 .2h-.8l-1 .2h-1l-.8.2h-4.6l-1-.2h-.8l-1-.2-.8-.2-1-.2-.7-.2-.8-.3-.8-.3-.8-.3-.8-.2-.8-.4-.7-.4-.7-.4z"/>
<g id="rs-b">
<path fill="#edb92e" d="M363 212c-2-4-.3-9.8.3-11 .3-.8-.6-2.2-1-2.8-13-18.3-13.2-20-4.3-26.8 8-6 26.2-12 39.2-16.2 2.7-2 4.4-5 6.8-7.2-25 1.8-52 12-65.5 23.8-7.2 6.2 1.5 19 12.2 38 6.7 5 9.8 7 12.3 2.3z"/>
<path fill="#21231e" d="m362.8 200.7 1 .4v.2l-.2.2v.3l-.2.3v.5l-.2.2v.6l-.2.4v.7l-.2.3v1.6l-.2.4v2.5l.2.4v.4l.2.4v.4l.2.4v.3l.3.4-1 .3v-.4l-.3-.5v-.4l-.2-.4v-.4l-.2-.5v-.8l-.2-.6v-2.9l.2-.4v-1.2l.2-.3v-.4l.2-.2v-.6l.2-.3v-.6l.2-.2v-.2l.2-.2zm-1-2.2 1-.6v.2h.2v.4h.2v.4h.2v.2l.2.2v.4h.2v1.2l-1-.3v-.6l-.2-.2v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2zm-4-27.5.5 1-.8.5-.7.6-.7.6-.5.6-.6.5-.6.5-.5.6-.5.5-.3.4-.3.5-.3.5v.5l-.3.5v1.7l.2.6.2.8.2.7.4.8.4.8.4 1 .5 1 .6 1 .7 1 .7 1.3 1 1.2.7 1.3 1 1.4 1 1.5 1 1.5 1.3 1.7-.8.5-1.3-1.7-1-1.5-1-1.5-1-1.4-1-1.3-.8-1.2-.8-1.2-.7-1-.6-1-.7-1-.4-1-.6-1-.3-.7-.2-1-.2-.7v-.7l-.2-.7v-1.4l.2-.6.2-.6.2-.6.3-.6.4-.6.5-.5.5-.5.5-.5.6-.6.7-.5.7-.7.7-.6.7-.7zm39-16.2.7.8h-.2l-1.2.5-1.2.5-1.2.4-1.4.3-1.3.4-1.4.5-1.4.4-1.3.5-1.5.5-1.4.5-1.3.5-1.3.5-1.4.5-1.4.5-1.3.5-1.4.5-1.4.5-1.3.6-1.3.6-1.3.5-1.2.7-1.2.5-1.2.6-1 .5-1.2.6-1 .4-1 .6-1 .6-1 .5-1 .7-.7.6-.7.6-.6-1 .8-.5 1-.6.8-.7 1-.6 1-.6 1-.4 1-.6 1.2-.6 1.2-.5 1-.7 1.4-.6 1.3-.5 1.3-.6 1.3-.5 1.2-.4 1.4-.5 1.3-.5 1.3-.5 1.4-.5 1.3-.5 1.4-.5 1.4-.5 1.4-.4 1.4-.5 1.3-.4 1.3-.4 1.3-.4 1.3-.5 1.2-.4 1.3-.5 1.3-.4 1.2-.3zm.7.8h-.2zm6.6-7v-1l.4.8-.2.2-.3.2-.3.3-.2.3-.2.2-.2.2v.3l-.3.2-.2.2-.2.2-.2.3-.3.3-.2.3-.2.2-.2.3v.3l-.3.2-.2.3-.2.2-.3.2-.2.3-.2.2-.2.2h-.2l-.3.4-.3.2-.2.2-.3.2-.2.2-.3.2-.2.2-.6-.8v-.2l.3-.2.3-.2.2-.2.2-.2.2-.2.3-.2.2-.2.2-.2v-.2l.3-.3.2-.2.2-.2.2-.2v-.2l.3-.2.2-.2.2-.3.2-.3v-.3l.3-.2.2-.2.2-.3.2-.2v-.2l.4-.3.2-.2.2-.2.2-.3.2-.2h.3l.2-.3.4.8zm0-1 1.4-.2-1 1-.4-1zM338.8 172l-.7-.8 1.5-1 1.4-1.2 1.4-1 1.5-1.2 1.5-1 1.7-1 1.7-1 1.7-1 2-1 1.8-1 2-1 2-1 2-.8 2-1 2.2-.7 2-.8 2.3-.8 2.3-1 2.2-.6 2.3-.7 2.3-.6 2.4-.5 2.3-.6 2.4-.7 2.4-.5 2.4-.4 2.4-.4 2.4-.4 2.3-.3 2.3-.3 2.4-.3 2.3-.2v1l-2.3.2-2.3.3-2.3.3-2.3.3-2.4.4-2.3.4-2.4.4-2.3.5-2.3.6-2.3.5-2.3.6-2.3.7-2.2.7-2.2.7-2.2.8-2.2.8-2.2.8-2 1-2 .7-2.2 1-2 1-2 1-1.8.8-1.8 1-1.8 1-1.7 1-1.6 1-1.5 1-1.6 1-1.5 1.2-1.4 1-1.2 1.2zm12.2 37.3-.5.8-.2-.2-1-1.6-1-1.8-1-1.7-1-1.7-.8-1.7-1-1.6-.8-1.5-.8-1.5-1-1.5-.7-1.5-.8-1.4-.7-1.3-.7-1.4-.5-1.3-.6-1.3-.6-1.2-.4-1.2-.5-1.2-.3-1-.3-1-.4-1.2v-1l-.2-1v-2.8l.3-.8.3-.8.4-.7.5-.8.5-.7.6-.6.8.8-.6.5-.5.6-.4.7-.3.6-.3.7-.2.8v4.5l.4 1v1l.5 1 .4 1 .4 1.3.5 1.2.4 1.2.6 1.3.7 1.3.7 1.4.7 1.4 1 1.4.7 1.5 1 1.5.8 1.5 1 1.6.8 1.7 1 1.7 1 1.7 1 2 1 1.6h-.3zm-.5.8-.2-.2.2.3zm12.5 1.3v1l.5-.2-.3.4-.2.4-.3.4-.3.3-.3.2-.2.3-.3.2-.3.2h-.3l-.5.2h-2.1l-.4-.2-.5-.2-.4-.2-.3-.2-.5-.2-.4-.3-.5-.3-.5-.3-.4-.4-.6-.3-.5-.3-.5-.4-.6-.4-.5-.4-.6-.4-.5-.5.5-.8.7.4.5.3.6.5.6.3.5.5.4.4.5.2.4.3.4.3.5.3.4.3.4.2.5.2.4.2h.5l.3.2h1.5l.2-.2h.3l.2-.3.2-.2.3-.2.2-.3v-.3l.4-.4.2-.4.4-.4zm-.4.3v-.4h.4zm.4.6v-1l.5.3zm.5-.7.3.6h-.8z"/>
<path fill="#edb92e" d="m359 213.6-1-4.7q-1-4.3.5-8.3c-3-4.2-6.4-8.8-8.8-13.3-1.7-3.3-3-7-2-10.6 1-4 4.5-6.6 7.6-9 3.7-2.8 8.6-5.3 13.6-7.5q16.6-7 26.5-7l-4.2 3.8c-12 4-26.4 9.2-33.3 14.5-9 6.8-8.6 8.4 4.3 26.7.4.6 1.3 2 1 2.7-.6 1.2-1.8 7.2 0 11.4-1 2-2.6 1-4.2 1.2z"/>
<path fill="#21231e" d="m357.4 209 1-.2v.7l.2.2v.7h.2v.8l.2.3v.3l.2.3v.7h.2v.6h.2l-1 .4v-.3l-.2-.3v-.2l-.2-.3v-.5l-.2-.3v-.6h-.2v-.5l-.2-.3v-.7l-.2-.3v-.2zm.6-8 1-.6v.5l-.2.4v.2l-.2.2v1l-.2.2v.8l-.2.2v4.7l.2.3-1 .2v-.6l-.2-.2V203h.2v-1l.2-.2v-.5l.2-.2v-.2l.2-.3v.5zm1-.6v.5zm-9.7-12.8 1-.5v.6l.3.4.3.4v.4l.4.5.2.4.3.4v.5l.4.4.3.4.2.4.2.4.3.4.2.4.3.5.3.4.3.3.3.4.2.4.2.5.3.5.3.4.3.4.3.4.3.4.2.4.3.4.2.4.3.4.3.4.3.4.3.4-1 .6-.2-.4-.3-.4-.3-.4-.3-.4-.4-.4-.3-.4-.2-.4-.2-.4-.3-.4-.3-.4-.3-.5-.4-.4-.3-.3-.3-.4-.2-.4-.3-.5-.3-.5-.3-.4-.4-.4-.3-.4-.2-.5-.2-.4-.3-.5-.2-.4-.3-.4-.3-.4-.2-.4-.3-.5-.2-.4-.2-.5-.3-.5-.2-.4zm-2-11 1 .4-.2.3v4.1l.2.4v.3l.2.3v.7l.3.3v.4l.2.4v.6l.3.3v.3l.3.3v.2l.3.3v.3l.2.3-1 .6v-.3l-.2-.3-.2-.3v-.3l-.3-.4v-.3l-.3-.3v-.3l-.3-.3-.2-.3v-.3l-.2-.2v-.4l-.2-.3v-.3l-.2-.3v-.7l-.2-.4V180l-.2-.4V178l.2-.2v-.7l.2-.3zm7.7-9.2.6.8-.3.2-.3.3-.3.2h-.3l-.3.3-.2.3-.2.2-.3.3-.3.3-.3.2-.3.3-.2.2-.3.3-.4.3-.2.2-.3.3v.3l-.4.3-.2.3-.2.2-.3.3-.3.3-.2.3-.2.4v.3l-.3.3-.2.3v.3l-.3.3v.3l-.2.3v.4l-1-.3v-.4l.2-.4v-.4l.2-.4.2-.4v-.3l.3-.4.2-.2.2-.3.2-.3v-.3l.4-.5.2-.3.2-.2.3-.3.4-.3.2-.3.3-.2.2-.2.3-.3.2-.3.3-.2.3-.3.3-.2.3-.3.2-.2.3-.2.3-.3.3-.2h.4l.3-.4.3-.2zm13.7-7.5.4.8-.4.2-.4.2-.5.2-.5.2-.4.2-.5.2-.4.3-.6.2-.5.2-.5.2h-.5l-.4.3-.5.3-.3.2-.4.2-.5.3-.4.2-.4.2-.4.3-.5.2-.3.2-.4.3-.4.2-.5.2-.4.2-.2.3-.4.2-.4.2-.4.2-.3.3-.4.3-.4.2-.6-.8.3-.3h.4l.4-.4.4-.3.4-.2.4-.2.4-.2.4-.3.4-.2.4-.2.4-.2.4-.3.5-.2.5-.2.4-.2.4-.2.4-.3h.4l.5-.3.4-.3.6-.2.4-.2.5-.2.5-.3.4-.2.5-.2h.4l.4-.3.5-.2.5-.2.4-.2h.5zm27-6.4-.6-.7.5.7H394l-.6.2h-1.8l-.7.2h-.8l-.7.2h-.7l-.8.3-.8.2h-.7l-.8.2-.8.2-1 .2-.8.2-.8.3-1 .3-.8.3-1 .3-.8.2-1 .3-.8.3-1 .3-1 .5-1 .3-.8.4-1 .4-1 .4-1 .4-1 .4-.5-1 1-.4 1-.4 1-.4 1-.4 1-.3 1-.5 1-.3 1-.4 1-.3.8-.3 1-.3.8-.4 1-.2.8-.3h1l.8-.4.8-.2.8-.2.8-.2h.8l.8-.3.8-.2h.7l.7-.2h.8l.7-.2h1.4l.5-.2h1.9l.4.8zm-.3-1h1.4l-1 1zm-4.4 4 4-3.7.8.6-4 3.8-.3.2zm.7.8-.2.2zM358.3 172l-.6-1 .7-.5.7-.5 1-.5.7-.5.8-.5 1-.5.8-.4 1-.5 1-.4.8-.5 1.2-.4 1-.5 1-.3 1-.5 1.2-.5 1-.4 1.3-.5 1-.4 1.2-.5 1-.5 1.3-.5 1-.4 1.3-.4 1.2-.5 1.2-.4 1.2-.4 1.2-.5 1-.4 1.3-.3 1.2-.4 1-.3 1.2-.4.3 1-1.2.3-1 .3-1.3.4-1 .4-1.3.5-1.2.4-1.2.3-1.2.4-1 .4-1.2.6-1.2.4-1 .4-1.3.5-1 .3-1.3.5-1 .4-1.2.5-1 .4-1 .6-1.2.5-1 .5-1 .5-1 .5-1 .5-.8.5-1 .4-.8.4-.8.5-.8.4-.7.5-.7.4zm4.4 26-.8.5-1.3-1.7-1-1.5-1-1.5-1-1.4-1-1.3-.8-1.2-.8-1.2-.7-1-.6-1-.7-1-.4-1-.5-1-.4-.7-.2-1-.2-.7v-.7l-.2-.7v-1.4l.2-.6.2-.6.2-.6.3-.6.4-.6.5-.5.5-.5.5-.5.6-.6.7-.5.7-.7.7-.6.7-.7.6 1-.8.5-.7.6-.7.6-.5.6-.6.5-.6.5-.5.6-.5.5-.3.4-.3.5-.3.5v.5l-.2.6v2.2l.3.8.3.7.4.8.4.8.4 1 .6 1 .5 1 .7 1 .7 1.3 1 1.2.7 1.3 1 1.4 1 1.5 1 1.5 1.3 1.7zm1 3-1-.3v-1h-.2v-.2l-.2-.2v-.2h-.2v-.4l.7-.7.2.2v.4h.2v.2l.2.2v.2l.2.2v.4h.2v1.4zm.2 11.6-1-.5v.6l-.2-.4-.2-.5v-.4l-.2-.4v-.6l-.2-.4v-.5l-.2-.4v-5.4l.2-.4v-.7l.2-.4v-.5l.2-.3v-.4l.2-.2 1 .3v.2l-.2.2v.6l-.2.2v.6l-.2.3v1.1l-.2.4v5.1l.2.4v.8l.3.4v.4l.2.4zv.4zm0-.4v.4zm-5 1v1zh.2l.2-.2h1.4v.2h1.5l.2-.2h.2v-.2h.2v-.2l.2-.2.2-.3 1 .6-.2.2-.2.2v.2l-.2.2h-.2v.2l-.3.2h-.4l-.2.2h-.3v.2h-3v-1zm0 1v-1l.6.2-.5.7zm.6-.8.2.7h-.7l.6-.6z"/>
<path fill="#edb92e" d="M468 132.2q.3-3.6-.3 1.6-.5-1-1.4-1-1.6-.1-1.8 5.2l.2 2.8c-33-.7-52.7 4.3-57.7 14-4.2 7.8 3 21 13.6 36 1.3 1.8 1.2 1.6 0 3-1.8 2.3-3.8 6-2.3 9.2a24 24 0 0 1-21 1.5c-1-3-3.4-4.4-5.4-5.8-.5-.2-2-1-2-1.4l-10.2-31.6c-9.5-30.7 75.3-33.5 88.3-33.5"/>
<path fill="#21231e" d="m467.3 134 1-.4-1 .2V132h.2v-1.2l.2-.2.6-.3.3.3v1.6h-1V131l.4.2h.6v-.2 1l-.2.2v1.8h-1zm1 0-.2 1.6-.7-1.5h1zm-2-.7v-1h.7v.2h.4v.2h.2v.2h.2v.2h.2v.2h.2v.2l-1 .5v-.2h-.2v-.2h-.2v-.2zM465 138h-1v-1.8l.2-.3v-.9l.2-.3v-.7h.2v-.3l.2-.3.2-.3.2-.2h.2v-.2h.2v-.2h.5v1h-.2v.2h-.2v.2l-.2.2v.3l-.2.3v.6l-.2.2v2l-.2.3v.4zm-.3 3.3v-1l-.5.5v-2.3h-.2v-.4l1-.2v1.8l.2.2v1zm.5-.5v.5h-.5zM407.4 155l-1-.5.7-1 .7-.8.7-1 .8-.7 1-.8 1-.8 1-.8 1-.7 1.3-.7 1.2-.7 1.4-.6 1.6-.6 1.5-.5 1.7-.7 1.7-.5 1.8-.4 2-.5 1.8-.5 2-.3 2.2-.4 2.2-.3 2.2-.2 2.4-.3 2.4-.2 2.5-.2h2.5l2.7-.2h2.7l2.8-.2h9v1H453l-2.8.2h-2.6l-2.6.2-2.5.2-2.4.2-2.3.3-2.2.2-2.2.3-2 .4-2 .3-2 .4-1.8.4-1.8.4-1.7.5-1.6.5-1.5.6-1.5.5-1.3.5-1.3.7-1.2.6-1 .7-1 .7-1 .7-1 .7-.6.8-.7.8-.6.8-.6 1zm13.6 35.5-.8.6-1-1.3-1-1.4-1-1.4-1-1.5-.8-1.3-.8-1.4-1-1.3-.7-1.3-.8-1.3-.8-1.4-.7-1.2-.7-1.3-.6-1-.6-1.3-.5-1.2-.5-1.2-.5-1.2-.4-1-.5-1-.3-1.2-.3-1-.3-1-.2-1.2v-1l-.2-1V158l.3-.8.3-1 .2-.7.3-.8 1 .5-.4.7-.2.7-.2.8-.2.8v4.5l.2 1 .3 1v1l.5 1 .3 1.2.4 1 .5 1.2.5 1 .5 1.2.6 1.2.6 1.2.6 1.3.7 1 .7 1.4.8 1.3.8 1.3.8 1.3 1 1.3.8 1.4 1 1.3.8 1.4 1 1.4 1 1.5zm0 3.8-.8-.7.2-.3.2-.2h.2v-.2l.2-.2v-.4l-.2-.2v-.2h-.2v-.3h-.2v-.3h-.2l.8-.7.2.3.2.3v.2h.2v.4h.2v.2l.2.2v1l-.2.2v.2h-.2v.2l-.2.2-.2.2-.2.3zm-2.5 9-.4-.8-.2.7v-.3l-.2-.5v-.3l-.2-.3v-3.5l.2-.3v-.3l.2-.4.2-.3v-.3l.2-.3.2-.3v-.3l.3-.2v-.3l.3-.2.2-.3v-.2l.3-.3.2-.2v-.3l.3-.2.2-.2.8.7-.2.2-.2.2v.2h-.3v.3l-.3.3-.2.2v.3l-.3.3v.3l-.2.2-.2.3v.7l-.2.2v.2l-.2.3v.6l-.2.4v2.7l.2.3.2.3-.3.6zm.3-.5v.4l-.3.2zm-22 1.8 1-.3-.2-.3.6.2.7.3.5.2.6.2h.8l.6.2.6.2h.7l.6.2h5.7l.7-.2h.5l.7-.3h.6l.7-.2.6-.2.7-.2.7-.2.6-.2.6-.3.6-.3.6-.2.7-.4.6-.3.5 1-.6.2-.7.4-.6.4-.7.2-.6.3-.7.2-.6.2-.6.2-.7.2-.6.2h-.6l-.7.3h-.7l-.6.2h-.7l-.7.2h-5l-.7-.2h-.7l-.6-.3h-.7l-.7-.3-.6-.3-.7-.3-.6-.2-.8-.3-.3-.4zm.4.3-.2-.2v-.2zm-5.6-5.8.6-1 .2.2.2.2h.2l.2.2.2.2h.2l.2.2v.2h.3v.3h.3l.2.3h.2l.2.3.3.3.2.2.2.2.2.2v.2l.3.2v.2l.3.2.2.2v.2l.3.3.2.2.2.2v.2l.3.3v.3l.2.3-1 .3-.2-.2v-.3h-.2v-.3l-.2-.3-.2-.2v-.2l-.2-.2-.2-.2v-.2l-.3-.2-.2-.2-.3-.3-.2-.2-.3-.3h-.2l-.2-.3v-.2l-.3-.2h-.2v-.3h-.3l-.2-.2h-.2l-.2-.2h-.2l-.2-.3h-.2zh-.2zm-2-1.7.8-.3h.2v.2h.2l.2.2h.2v.2h.4v.2h.2l.2.2h.2l-.6 1-.2-.2h-.2v-.2h-.4v-.2h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2zm-10.2-31.7 1-.3.2 1 .3 1 .2.8.3 1 .3 1 .3.8.4 1 .3 1 .3 1 .3 1 .3 1 .3.8.2 1 .4 1 .3 1 .2 1 .3 1 .3 1 .4 1 .3 1 .3 1 .3 1 .5 1 .3 1 .3 1 .5 1 .3 1 .3 1 .4 1.2.4 1 .3 1 .4 1.2-1 .3-.3-1-.2-1.2-.3-1-.4-1-.2-1-.3-1-.3-1.2-.4-1-.3-1-.3-1-.4-1-.3-1-.3-1-.3-1-.4-1-.4-1-.3-1-.4-1-.3-1-.3-.8-.3-1-.4-1-.3-1-.3-1-.3-.8-.3-1-.2-1-.3-1-.3-.8-.3-1-.4-.8-.2-1zm88.2-33.6h1l-.5.5h-5.4l-2.5.2h-2.8l-3 .2-3.3.2-3.5.2-3.7.3-3.8.3-4 .4-4 .5-4 .6-4.2.6-4 .7-4.2 1-4 .8-4 1-3.7 1-3.7 1.3-3.4 1.4-3.3 1.4-3 1.5-2.6 1.6-2.3 1.7-2 2-1.6 1.8-1.2 2.2-.8 2.2-.4 2.3v2.5l.7 2.7-1 .3-.6-2.8v-2.8l.3-2.6 1-2.4 1.2-2.2 1.8-2 2-2 2.5-2 2.7-1.6 3-1.5 3.3-1.6 3.5-1.3 3.6-1 4-1.2 3.8-1 4-1 4-.7 4.2-.6 4.2-.6 4-.6 4-.5 4-.4 4-.4 3.7-.4 3.5-.2 3.3-.2 3-.2h3l2.4-.2h5.4l-.6.4zm1 0v.5h-.5l.7-.5zm-.5 0h-.4zm.7 0v.5h-.6z"/>
<path fill="#fff" d="M405 190.2c3.5 0 6.4 2.8 6.4 6.4 0 3.5-3 6.4-6.4 6.4s-6.4-3-6.4-6.4c0-3.6 3-6.4 6.4-6.4"/>
<path fill="#21231e" d="M412 196.6h-1a6 6 0 0 0-3.4-5.3 6 6 0 0 0-2.6-.6v-1c2.3 0 4.6 1.2 5.8 3.2a7 7 0 0 1 1 3.6zm-7 7v-1c2.2 0 4.3-1.4 5.2-3.4a6 6 0 0 0 .7-2.6h1c0 2.3-1.4 4.6-3.4 6a7 7 0 0 1-3.6 1zm-7-7h1c0 2.2 1.4 4.3 3.4 5.3a6 6 0 0 0 2.6.5v1a7 7 0 0 1-5.8-3.3 7 7 0 0 1-1-3.6zm7-7v1a6 6 0 0 0-5.2 3.3 6 6 0 0 0-.7 2.6h-1c0-2.4 1.4-4.7 3.4-6a7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M350.8 203.6c3.2 0 6 2.7 6 6s-2.8 6-6 6-6-2.7-6-6 2.7-6 6-6"/>
<path fill="#21231e" d="M357.2 209.6h-1c0-2-1-4-2.8-4.8a5 5 0 0 0-2.6-.7v-1a7 7 0 0 1 5.4 3 7 7 0 0 1 1 3.6zm-6.4 6.4v-1c2 0 4-1.2 4.8-3a6 6 0 0 0 .6-2.4h1c0 2-1.2 4.3-3 5.4a6 6 0 0 1-3.4 1m-6.5-6.4h1c0 2 1.2 4 3 4.8q1.2.6 2.5.6v1a7 7 0 0 1-5.5-3 7 7 0 0 1-1-3.4m6.5-6.5v1c-2 0-4 1.3-5 3a6 6 0 0 0-.5 2.6h-1c0-2.2 1.2-4.4 3-5.5a6 6 0 0 1 3.5-1z"/>
<path fill="#fff" d="M345 192.7c3.4 0 6 2.7 6 6s-2.6 6-6 6c-3.2 0-5.8-2.7-5.8-6s2.6-6 6-6z"/>
<path fill="#21231e" d="M351.5 198.7h-1c0-2-1.2-4-3-5a5 5 0 0 0-2.4-.5v-1q3.6.1 5.5 3a7 7 0 0 1 1 3.5zM345 205v-1c2 0 4-1 4.8-2.7a6 6 0 0 0 .7-2.6h1c0 2.2-1.2 4.3-3 5.5a6 6 0 0 1-3.4 1zm-6.3-6.3h1c0 2 1.2 4 3 4.8a5 5 0 0 0 2.4.6v1c-2 .2-4.2-1-5.3-3a7 7 0 0 1-1-3.3zm6.4-6.5v1c-1.8 0-3.7 1-4.6 2.8a6 6 0 0 0-.7 2.7h-1c0-2.2 1.2-4.4 3-5.5a6 6 0 0 1 3.4-1z"/>
<path fill="#fff" d="M339.8 181.7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6"/>
<path fill="#21231e" d="M346.2 187.7h-1c0-2-1.2-4-3-5a5 5 0 0 0-2.4-.5v-1c2.2 0 4.3 1.2 5.4 3a7 7 0 0 1 1 3.5m-6.4 6.4v-1q3.2 0 4.7-2.7.7-1.2.7-2.6h1c0 2-1 4.3-3 5.4a6 6 0 0 1-3.4 1zm-6.4-6.3h1c0 2 1 4 3 4.8q1 .6 2.4.6v1a6 6 0 0 1-5.4-3 7 7 0 0 1-1-3.3zm6.4-6.5v1c-2 0-4 1.2-4.8 3a6 6 0 0 0-.6 2.5h-1c0-2.2 1-4.4 3-5.5a6 6 0 0 1 3.4-1"/>
<path fill="#edb92e" d="M444.3 202.2c3.4-.3 2.5 5.8 2 7.2q-1.3 2.6-3 .2c-.8-1.3-2.4-7.2 1-7.5z"/>
<path fill="#21231e" d="m446.6 209.6-1-.4h.2v-.7l.2-.3v-.5l.2-.3V204h-.2v-.3l-.2-.2v-.2l-.2-.2h-.2l-.3-.2h-.2l-.2-.2h-.3v-1h.7l.3.2h.3l.3.2v.2l.3.3.2.2.2.3v.2l.2.3v.7l.2.3v3.7l-.2.3v.5l-.2.3v.5h-.2zm-3.7.3.8-.7.2.3v.2h.2v.2h.2v.2h.7l.2-.2v-.2h.2v-.2h.2v-.3l1 .3v.2l-.2.2v.3h-.2v.2l-.2.2-.2.2h-.4v.2H444v-.2h-.2v-.2h-.2v-.2h-.2v-.3h-.2v-.3h-.3zm1.2-8.4v1h-.4v.2h-.3v.2h-.3v.3l-.2.2v.4h-.2v3.2l.2.2v.6l.2.2v.3l.2.2v.2l.2.2v.2h.2l-1 .7v-.5l-.2-.2v-.2l-.2-.4v-.2l-.2-.3v-.2l-.2-.3v-.7l-.2-.3v-3l.2-.3v-.3l.3-.3.2-.2.2-.3h.2l.2-.3.3-.2h.4l.2-.2z"/>
<path fill="#fff" d="M444.5 205.5c8-.6 6 13.8 4.4 17q-2.8 6.3-6.7.5c-2-3-5.8-17 2.2-17.5z"/>
<path fill="#21231e" d="m449.4 222.7-1-.4.2-.3.2-.4v-.4l.3-.5.2-.5v-.6l.2-.6.2-.7V217l.2-.7v-5.1l-.3-.7v-.6l-.3-.7-.3-.6-.3-.5-.3-.5-.3-.4-.3-.4-.4-.4-.5-.2-.5-.2-.5-.2h-1.4v-1h1.5l.6.2.7.2.5.4.6.4.4.4.4.5.4.7.3.6v.7l.3.6.2.7v.7l.3.8v6.3l-.3.7v.7l-.2.6v.6l-.3.6v.5l-.3.5v.4l-.3.3zm-7.5.5.8-.5.2.3.2.3.3.3.2.3h.2v.4h.3l.2.3.2.2h.4l.2.2h1v-.2h.3l.2-.3v-.2l.2-.2.2-.2h.2l.2-.4v-.3l.3-.3.3-.3.2-.4 1 .4-.3.4-.2.5-.2.3-.2.4-.2.3-.2.3-.2.2-.2.2-.2.3-.3.2h-.2v.3h-.4l-.2.2H445l-.4-.2h-.2l-.3-.3v-.2l-.4-.2-.2-.2-.2-.2-.3-.2-.3-.3-.3-.4-.2-.4zm2.5-17.2v-1zh-.6l-.7.2-.5.2-.5.3-.4.3-.5.4-.3.4-.3.4-.3.6-.2.5v.6l-.2.5v.7l-.2.7v.6l.2.7v1.4l.2.7v.8l.3.7v.6l.3.7.2.6.2.5.2.6.2.7.2.5.2.4.2.4.2.4.2.3-1 .5v-.3l-.3-.5-.2-.4-.2-.4v-.6l-.4-.5-.2-.7-.2-.6-.2-.7-.2-.7v-.7l-.3-.6v-.8l-.2-.7V213l-.2-.7v-.8l.2-.7v-.6l.2-.7v-.7l.4-.6.2-.6.4-.6.4-.5.4-.5.5-.4.6-.3.7-.3.7-.2h.8v1zm0-1v1z"/>
<path fill="#edb92e" d="M379.7 210.8c3.3-1 4 5 3.6 6.6q-.5 2.9-2.7 1c-1-1.2-4-6.5-1-7.6z"/>
<path fill="#21231e" d="m383.8 217.5-1-.2v-2.8l-.2-.3v-.7l-.2-.3v-.3l-.2-.2v-.3l-.3-.2-.2-.2-.3-.3-.3-.3h-.3l-.3-.2h-.6l-.4-1h1.5l.2.2.3.2.2.2.3.2.2.3.2.2v.3l.3.3v.4l.2.3v.3l.2.3v.3l.2.4v.6l.2.3v2.5zm-3.5 1 .7-.6h.2v.2h.2l.2.2h.2v.2h.4l.2-.2v-.2h.2v-.6h.2v-.3l1 .2v.4h-.2v.4l-.2.3v.2h-.2l-.2.2-.2.2h-.2l-.2.2h-1v-.2h-.3l-.2-.2h-.2v-.2h-.2zv-.2zm-.7-8.2.3 1h-.3l-.2.2h-.2v.2l-.2.2v.2l-.2.2v1.8l.2.4v.3l.2.3v.2l.2.3.2.3v.3l.2.3.2.3v.2l.3.2v.2l.2.2.3.3.2.2-.7.6h-.2v-.4l-.3-.2v-.2l-.3-.2v-.3l-.3-.2v-.2l-.2-.3v-.3l-.3-.3v-.4l-.2-.3v-.3l-.3-.3v-.7l-.2-.3V212l.3-.2.2-.3.2-.3.3-.2.2-.2h.2l.4-.3z"/>
<path fill="#fff" d="M380.7 214c7.7-2.5 9.2 12 8.4 15.5q-1.3 6.6-6.2 2c-2.6-2.4-9.7-15-2-17.5z"/>
<path fill="#21231e" d="m389.6 229.6-1-.2v-.8l.2-.4v-2.9l-.2-.7V223l-.3-.7v-.7l-.3-.8-.2-.7-.2-.6-.2-.7-.3-.7-.2-.6-.3-.6-.3-.4-.4-.5-.3-.4-.4-.3-.4-.4-.4-.4-.5-.2h-.5l-.5-.2h-1.1l-.6.3-.4-1 .7-.2h2.2l.7.2.7.2.5.4.5.5.5.4.4.5.4.5.4.6.3.7.3.6.3.6.3.7.2.8.2.8.2.7v.8l.3.7v1.5l.2.7v4.4zm-7 2.3.5-1 .5.4.2.2.3.3h.3l.3.3h.2l.3.3h.3l.2.2h1.4l.2-.2.2-.2.2-.3.3-.3v-.6l.2-.2v-.3l.2-.4v-.2l.2-.4 1 .2v.4l-.2.4v.4l-.2.4-.2.3v.3l-.2.3-.2.4v.3l-.3.2h-.2v.3l-.3.2h-.2l-.2.3h-.8l-.3.2h-.5l-.3-.2h-.2l-.3-.2h-.4l-.3-.3-.3-.2h-.3l-.3-.3-.3-.3-.2-.2-.3-.3zm-2-18.5.3 1-.7.2-.6.3-.4.3-.4.4-.4.4-.3.5-.2.5v.5l-.2.6v2.5l.2.6.2.7.2.6.2.7.3.7v.7l.4.6.3.7.4.6.4.6.3.6.4.6.4.5.3.4.3.4.4.4.2.4.3.3-.5 1-.3-.4-.3-.3-.4-.4-.4-.5-.3-.5-.4-.5-.4-.6-.3-.7-.5-.6-.3-.6-.4-.6-.3-.6-.3-.7-.3-.7-.3-.7-.3-.7-.2-.8-.2-.8v-.7l-.2-.7V218l.2-.7.2-.6.2-.6.3-.5.4-.5.6-.5.6-.4.7-.2.6-.3zm0 .5v-.5z"/>
<path fill="#edb92e" d="M464.2 137.8c-17.2-1.6-35 .2-52.3 7-14.8 6-13.4 23.2 4.6 47q-2.6 4.6-2.7 11h4.8c-1.3-2.8 0-6.4 1.8-9 1-1.5 1.3-1.2 0-3-10.7-15-17.8-28.2-13.6-36 5-9.7 24.6-14.7 57.7-14q0-1.4-.2-2.7l-.3-.2z"/>
<path fill="#21231e" d="m412 145.4-.3-1 1.7-.6 1.6-.6 1.7-.6 1.6-.5 1.6-.4 1.6-.5 1.6-.4 1.7-.4 1.5-.4 1.7-.4 1.6-.4 1.7-.3 1.6-.3 1.8-.3h1.6l1.7-.3 1.7-.2 1.7-.2 1.6-.2h3.3l1.6-.2h11.5l1.6.2h1.6l1.6.3v1l-1.7-.2h-4.9l-1.6-.2h-9.7l-1.6.2H443l-1.5.3-1.7.2-1.6.2-1.6.2H435l-1.7.4-1.6.3-1.7.3-1.6.4-1.6.3-1.7.5-1.5.4-1.6.4-1.8.5-1.6.6-1.6.6-1.7.6-1.6.6-1.6.6zm5 46.7-1-.4.2.5-1.7-2-1.5-2.3-1.5-2-1.3-2.2-1.3-2-1.2-2-1-2-1-2-1-2-.7-1.7-.6-1.8-.5-1.8-.6-1.7-.4-1.6-.3-1.7-.2-1.6v-4.4l.2-1.3.3-1.3.4-1.2.5-1.2.5-1.2.7-1 .8-1 .8-1 1-.7 1-.8 1.3-.8 1.4-.6 1.3-.5.4 1-1.2.5-1.2.6-1 .7-1 .8-1 1-1 .8-.6 1-.7 1-.5 1-.5 1-.3 1.2-.3 1.2-.2 1.2v3.8l.2 1.5.3 1.7.3 1.6.5 1.6.6 1.6.7 1.8.8 1.8 1 2 .8 1.8 1 2 1.2 2 1.2 2 1.4 2 1.4 2.2 1.5 2 1.7 2.3zm0-.5.2.3-.2.3v-.5zm-3 11v1l-.6-.6v-2.5l.2-.4v-1l.2-.4v-.7l.2-.5v-.4l.2-.2v-.4l.2-.3v-.3l.2-.3v-.4l.2-.3v-.3l.2-.3v-.3l.3-.3v-.3l.2-.2.2-.3v-.3l.3-.3v-.4l1 .5v.4l-.3.2v.3l-.2.2-.2.3v.3l-.2.3v.4l-.2.3v.3l-.2.4v.3l-.2.3v.3l-.2.4v.7l-.2.3v.8l-.2.4v.8l-.2.4v1.4l-.2.4v.7l-.5-.6zm0 1h-.6v-.6l.5.5zm4.3-.4 1-.4-.5.8h-4.6v-1h4.6l-.5.7zm1-.4.3.7h-.8l.4-.7zm1-9 .7.6-.2.2v.3l-.2.2-.2.2v.2l-.3.3v.4l-.2.3-.2.2v.3l-.2.3v.2l-.2.3v.7l-.2.2v.5l-.2.3v2.2l.2.3v.4l.2.2-1 .5v-.5l-.2-.3v-.7l-.2-.3v-2.7l.2-.3v-.3l.2-.2v-.3l.2-.3v-.3l.2-.4v-.3l.2-.3.2-.3v-.2l.2-.3.2-.3v-.3l.3-.3v-.3h.3l.2-.3zm0-2.5.7-.5.2.3.2.3v.2h.2v.4h.2v.4l.2.2v.5l-.2.2v.2h-.2v.2l-.2.2v.2h-.2v.3l-.2.2-.8-.6v-.2l.2-.2.2-.2v-.2h.2v-.4h.2v-.2h-.2v-.2l-.2-.2v-.3h-.2v-.3h-.2zm-13.8-36.5 1 .5-.4.7-.2.7-.2.8-.2.8v4.5l.2 1 .3 1v1l.5 1 .3 1.2.4 1 .5 1.2.5 1 .5 1.2.6 1.2.6 1.2.6 1.3.7 1 .7 1.4.8 1.3.8 1.3.8 1.3 1 1.3.8 1.4 1 1.3.8 1.4 1 1.4 1 1.5-.8.6-1-1.3-1-1.4-1-1.4-1-1.5-.8-1.3-.8-1.4-1-1.3-.7-1.3-.8-1.3-.8-1.4-.7-1.2-.7-1.3-.6-1-.6-1.3-.5-1.2-.5-1.2-.5-1.2-.4-1-.5-1-.3-1.2-.3-1-.3-1-.2-1.2v-1l-.2-1V158l.3-.8.3-1 .2-.7.3-.8zm57.7-13.7h1l-.5.5h-14.5l-2.6.2H445l-2.5.3-2.4.2-2.3.3-2.2.2-2.2.3-2 .4-2 .3-2 .4-1.8.4-1.8.4-1.7.5-1.6.5-1.5.6-1.5.5-1.3.5-1.3.7-1.3.6-1 .7-1 .7-1 .7-.8.7-.8.8-.7.8-.6.8-.6 1-1-.5.7-1 .7-.8.7-1 .8-.7 1-.8 1-.8 1-.8 1-.7 1.2-.7 1.3-.7 1.4-.6 1.6-.6 1.5-.5 1.7-.7 1.7-.5 1.8-.4 2-.5 1.8-.5 2-.3 2.2-.4 2.2-.3 2.2-.2 2.4-.3 2.4-.2 2.5-.2h2.5l2.7-.2h2.7l2.8-.2h9l-.6.6zm1 0v.5h-.5zm-1-2.3.7-.7v1.9h.2v1h-1v-2.3l-.2-.2v-.4.3zm.7-.7v.3-.2zm-1.2-.4.8.7v-.6.2h.2v.2l-.7.7v-.4h-.2v-.6zm0 .7-.3-.2.3-.4zm.8 0-.8-.6h.4zm-.4-.7h1l-.6.8-.4-.7z"/>
<path fill="#fff" d="M380.6 142.7c3.4 0 6 2.7 6 6s-2.6 6.3-6 6.3-6.2-2.7-6.2-6.2a6 6 0 0 1 6.2-6z"/>
<path fill="#21231e" d="M387.2 148.8h-1c0-2-1.2-4-3-5a6 6 0 0 0-2.6-.6v-1c2.2 0 4.5 1.2 5.6 3a7 7 0 0 1 1 3.6m-6.6 6.7v-1c2 0 4-1.2 5-3a7 7 0 0 0 .6-2.7h1a7 7 0 0 1-3 5.7 7 7 0 0 1-3.6 1m-6.7-6.7h1c0 2 1.2 4.2 3 5a6 6 0 0 0 2.6.7v1a7 7 0 0 1-5.6-3 7 7 0 0 1-1-3.7m6.6-6.7v1.2c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.6h-1c0-2.2 1-4.4 3-5.6a7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M392.6 138.7c3.4 0 6.2 2.8 6.2 6.2 0 3.3-2.8 6-6.2 6a6 6 0 0 1-6-6c0-3.5 2.6-6.3 6-6.3z"/>
<path fill="#21231e" d="M399.3 145h-1c0-2.2-1.3-4.3-3.2-5.2a6 6 0 0 0-2.4-.6v-1a7 7 0 0 1 5.7 3 7 7 0 0 1 1 3.7zm-6.7 6.6v-1c2 0 4-1.3 5-3a6 6 0 0 0 .7-2.7h1c0 2.2-1.2 4.4-3.2 5.6a7 7 0 0 1-3.4 1zM386 145h1c0 2 1.2 4 3 5a6 6 0 0 0 2.6.6v1a7 7 0 0 1-5.6-3.2 7 7 0 0 1-1-3.5zm6.6-6.8v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.7h-1c0-2.4 1.2-4.7 3.2-5.8a7 7 0 0 1 3.4-1z"/>
<path fill="#edb92e" d="m394.2 144 2 2.4q-14.6 8.1-13 16.7.1 1.4 9.8 31.7 6.7 4.7 10 8.4c-.2.4.8 1.2.5 1.5.7 3-2.8 2.4-5 1.7-1.2-3-4.6-6-6.6-7.5-.5-.2-2-1-2-1.4l-10.2-31.6c-3-9.8 3.6-16.8 14.4-21.7z"/>
<path fill="#21231e" d="m396.3 147-.5-1v.7h-.2v-.2h-.2v-.2l-.2-.2h-.2v-.2l-.2-.2v-.2h-.2v-.2h-.2v-.2l-.2-.2-.2-.2v-.2h-.2l.8-.7.2.2h.2v.4h.2v.2h.2v.2l.2.2.2.2v.2h.2v.2h.2v.2l.2.2v1zm.2-1 .3.6-.5.3zm-12.7 17-1 .2v-3.9l.2-.6.2-.5.2-.6.2-.5.3-.5.2-.5.3-.6.4-.5.3-.6.4-.6.5-.5.4-.6.6-.5.5-.5.6-.5.6-.6.5-.5.7-.4.7-.5.6-.5.8-.5.8-.5 1-.5.7-.5.8-.5.5 1-1 .4-.7.5-.8.4-.8.5-.8.4-.7.5-.7.4-.6.5-.7.4-.6.5-.6.4-.5.5-.6.4-.5.6-.5.5-.4.5-.3.5-.3.5-.3.5-.2.5-.2.5-.2.5-.2.5-.2.5v.5l-.2.5v3.1zm9.5 31.3-.6.8-.2-.2-.6-1.8-.7-1.8-.5-1.8-.6-1.7-.5-1.6-.5-1.5-.5-1.5-.5-1.5-.4-1.4-.4-1.2-.4-1.3-.4-1.3-.4-1-.3-1.3-.4-1-.3-1-.3-1-.3-1-.3-.8-.2-.8-.2-.8-.2-.6-.2-.7v-.5l-.3-.5v-.5l-.2-.4v-.8h-.2v-.3h1v.3l.2.3v.4l.2.3v.5l.2.5.2.6.2.7.2.7.2.7.2 1 .3.7.2 1 .2 1 .3 1 .3 1 .4 1 .4 1.2.3 1.2.3 1.3.5 1.3.4 1.4.5 1.5.4 1.5.7 1.6.5 1.6.5 1.7.6 2 .6 1.7.5 1.8-.2-.2zm-.6.8-.2-.2.2.3zm10.7 8.4-.7-.6v.6l-.2-.2-.2-.2-.3-.3-.2-.2-.2-.2-.3-.3-.2-.2-.2-.2-.2-.3-.3-.2-.3-.2-.2-.2-.3-.3-.3-.3-.3-.2-.4-.3-.3-.2-.4-.3-.4-.3-.3-.2-.4-.3-.4-.3-.4-.3-.3-.3-.5-.3-.4-.2-.3-.3-.4-.2-.4-.3-.5-.3-.4-.3-.3-.3.6-.7.4.3.4.3h.5l.4.4.3.3.4.3.4.3.5.3.3.2.4.3.3.4.4.3.2.2.4.2.3.3.2.2.3.3.3.3.3.2.4.3.3.2.3.3.3.3.2.2.3.3.3.3.3.3.2.2.3.2.2.3.2.2.2.3v.6zm0-.6.3.3-.3.4v-.6zm.6 1.6-1 .2.2-.5v.2h-.2v.2-.2l-.2-.2v-.2h-.2v-1.2l.8.6v-.2h.2v.2l.2.2v.2l.2.2v.7l-.2.2.2-.6zm-1 .2v-.3l.2-.2-.2.6zm-5 1.8 1-.4-.4-.3h.4l.2.2h.7l.4.2h2.3v-.2h.2v-1.4l1-.2v1.6l-.2.3v.2l-.2.2h-.2l-.2.2h-.4l-.2.2h-3l-.3-.2h-.5l-.3-.2h-.5l-.3-.4zm.2.3h-.2v-.3zm-6.6-7.5.6-1 .2.3h.2l.2.3h.2l.3.3.2.3.2.2.2.2.2.2h.2l.2.3.3.2v.3l.4.2.2.3.2.2.3.3.3.2.2.3.3.3.2.2.2.3.2.4.2.3.2.3.2.2.2.3v.3l.3.3v.4l.3.3v.3l-1 .4v-.3l-.2-.4-.2-.3v-.2l-.3-.2v-.3l-.2-.2-.2-.3-.2-.3-.2-.2-.2-.3-.2-.2-.2-.3-.3-.2-.2-.3v-.2l-.3-.3-.3-.2-.2-.2-.2-.2-.3-.2-.2-.2-.2-.2h-.2l-.2-.3h-.2l-.2-.3h-.2l-.2-.3h-.2l-.2-.2zm.6-1 .3.3-.3-.2zm-2.7-.7 1-.3.2.2h.2v.2h.2v.2h.4v.2h.2l.2.2h.2l-.6 1-.2-.2h-.2v-.2h-.4v-.2h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2zm-10-31.7.8-.3.3 1 .3 1 .2.8.3 1 .3 1 .3.8.4 1 .3 1 .3 1 .3 1 .3 1 .3.8.2 1 .4 1 .3 1 .2 1 .3 1 .3 1 .4 1 .3 1 .3 1 .3 1 .5 1 .3 1 .3 1 .5 1 .3 1 .3 1 .4 1.2.4 1 .3 1 .4 1.2-1 .3-.3-1-.2-1.2-.3-1-.4-1-.2-1-.3-1-.3-1.2-.4-1-.3-1-.3-1-.4-1-.3-1-.3-1-.3-1-.4-1-.4-1-.3-1-.4-1-.3-1-.3-.8-.3-1-.4-1-.3-1-.3-1-.3-.8-.3-1-.2-1-.3-1-.3-.8-.3-1-.4-.8-.2-1zm15-22-.7.5h.6l-1 .6-1 .4-1 .5-.8.4-1 .5-.7.4-1 .5-.7.5-.8.5-.7.6-.7.6-.5.6-.7.6-.5.6-.6.7-.5.6-.4.7-.4.6-.4.7-.3.7-.3.7-.3.7v.8l-.2.7v.7l-.2.8v2.6l.3.8v1l.3.7-1 .3-.2-1v-.8l-.3-1V159l.2-1 .2-.7.2-.6.3-.8.4-.7.5-1 .4-.6.4-.7.6-.7.5-.7.6-.6.8-.6.6-.6.8-.6.7-.6.7-.5.8-.6 1-.5.7-.5 1-.5 1-.5.8-.5 1-.5 1-.5.6.2zm-.5-.3.4-.2.2.4z"/>
<path fill="#edb92e" d="m356 217-.4 2q-4.2.8-7.3.3 5.1-4.9 7.6-2.4z"/>
<path fill="#21231e" d="m355 218.8.4-2 1 .2-.3 2-.3.4-.6-.6zm1 0v.5h-.3zm-8 .2.7.7-.3-1 .2.2h4.1l.3-.2h1l.2-.2h.7l.2-.2h.3l.2 1h-.8l-.4.2h-.5l-.4.2h-1.3v.2h-3.7l-.4-1zm.2 1-1-.3.8-.7zm8.2-3-1-.2.2.5v-.2h-.6v-.2h-.6l-.2-.2H353v.2h-.6l-.2.3-.2.2h-.3l-.2.2-.3.2-.2.2-.3.2-.3.2-.2.2-.3.2-.4.3h-.3l-.3.4-.3.3-.7-.7.3-.3.3-.3.3-.3.2-.2.3-.2.3-.2.3-.3h.4l.3-.3h.3l.2-.3h.3l.3-.3h.6l.3-.2h.5l.4-.2h1.3l.2.2h.3l.2.2h.2l.2.2.3.3v.5zm0-.5v.5zm-.5.4h.4-.5zm.3-.5.2.2v.3z"/>
<path fill="#edb92e" d="M355.6 212.3c1.8.4 2 .7 2.7 1 1 0 2.4 0 3.3-1 4-2.2 4 2 2.5 5-1 2.4-.5 5 .6 7 1.2 2.2 1 3.2 0 4l-1.6 1q-1.6 1.8-2-.6c-3.8-1.6-5-5.3-6.2-8.6-1.7-4.7-7.8-5.8.8-7.7z"/>
<path fill="#21231e" d="M358.4 212.7v1h-.7v-.2h-.4l-.2-.2h-.4l-.2-.2h-.8l-.3-.2.2-1h.4l.2.2h.4l.2.2h.4v.2h.8v.2zm3-.7.5 1v-.2l-.2.2h-.3v.2h-.2l-.2.2h-.2l-.2.2h-2.1v-1h2.2l.2-.2h.2v-.2h.2zm3 5.6-.8-.4v-.3l.3-.4v-.5l.2-.2v-.6l.2-.3v-2.2h-.2v-.2h-.2v-.2h-1.2l-.3.2-.3.2-.3.2-.6-1 .4-.2.3-.2h.5l.3-.2h1.8l.2.2.2.2v.2l.2.3v.4l.2.3v2.4l-.2.3v.3l-.2.4v.3l-.2.3v.3zm.6 6.3-1 .4v-.4l-.2-.2v-.4l-.2-.2v-.2l-.2-.2v-.5l-.2-.2v-.8l-.2-.2v-2.3l.2-.2v-.3l.2-.3v-.3l.2-.2 1 .4-.2.2v.4l-.2.2v.8l-.2.2v2l.2.2v.5l.2.2v.4h.2v.2l.2.2v.4h.2zm0 4.6-.6-1h.2v-.2h.2v-1.6l-.2-.3v-.3l-.2-.2v-.2l-.2-.2v-.2l.8-.5v.2l.2.2v.2l.2.2v.2l.2.2v.2l.2.3v1.7l-.2.2v.2h-.2v.2h-.2v.2l-.2.2zm-1.6 1-.8-.7v-.2h.2l.2-.2h.2v-.2h.4v-.2h.2v-.2h.4v-.2h.2l.6.8h-.2v.2h-.4v.2h-.2l-.2.2h-.2v.2h-.2zm-.8-.7v-.2zm-1.8 0 .4-.8.3.4v.5h.2v.2h.4v-.2h.2v-.2h.2l.8.6-.2.2-.2.2-.3.2h-.2v.2h-1.1l-.2-.2v-.2l-.2-.2v-.4h-.2v-.7zm.4-.8h.3v.4zm-7-8 1-.3.2.2v.3l.2.3v.4l.2.3v.3l.3.3.2.4v.3l.2.3.2.3v.3l.3.3v.2l.2.3v.2l.3.3.2.3.2.2v.3l.3.2.2.3.2.2.2.2.2.2h.2l.3.3.3.2.3.2.3.2h.3l.2.2-.4 1-.3-.2-.4-.2-.2-.2-.4-.2-.3-.2-.2-.3-.3-.3-.2-.2-.3-.3-.3-.2-.2-.3-.2-.3-.2-.3-.3-.4-.2-.3-.2-.3-.2-.3v-.4l-.2-.3v-.3l-.3-.3v-.3l-.3-.2v-.3l-.2-.4v-.2l-.2-.3-.2-.3v-.3l-.2-.4v-.3zm1.4-8.4v1l-.6.2-.7.2h-.6l-.5.3h-.4l-.4.3h-.3v.3l-.2.2v.2h-.2v.2l.2.3.3.2.2.3.2.2.2.3.3.2v.3l.4.3.2.3.3.5.2.3.2.4.3.4.2.4v.5l.3.4-1 .3v-.4l-.3-.5-.2-.4-.2-.3-.3-.3-.2-.3v-.2l-.4-.3-.2-.3-.3-.3v-.4l-.3-.2-.3-.3-.3-.4v-.2l-.2-.3-.2-.4v-.6l.3-.3v-.2l.3-.3h.3l.4-.3.4-.3.5-.2h.4l.7-.3.8-.2zm0 1v-1zm0-1"/>
<path fill="#edb92e" d="M357 212.5c3.5 1.2 4-.7 6-.6-.8 5-1.7 7.7-7 5-3-1.6-6.7.7-7.4 2.3-.3.6-2-.6-2.6-2-2.6-5.8 6.7-6.4 11-4.8z"/>
<path fill="#21231e" d="m363.6 212-1-.2.5.6H362v.2h-.3l-.2.2h-.2l-.3.2h-.3v.2h-.7l-.2.2h-2l-.4-.2h-.3l-.3-.2.3-1h.3l.3.2h.4l.2.2h1.3l.2-.2h.2l.2-.2h.5l.2-.2h.2l.3-.2h.5l.2-.2h.4l.6.6zm-.5-.6h.7v.6zm-7.2 6 .4-1 .5.4.5.2h.4l.4.3h.3l.4.2h1.7l.2-.2.3-.3.2-.3v-.2l.2-.2.2-.3v-.3l.2-.3v-.3l.2-.2v-.4l.2-.4v-.4l.2-.4v-.9l.2-.5 1 .2v.5l-.2.5v.8l-.2.5v.4l-.2.4v.4l-.3.4v.3l-.2.4-.2.4v.2l-.3.3-.2.3-.2.2-.3.2-.2.2h-.2l-.3.2h-.3l-.3.2h-1.6l-.4-.2-.4-.2h-.4l-.4-.3-.5-.3-.5-.3zm-6.8 2-1-.3h.2v-.3h.3v-.3l.3-.2v-.2l.3-.2h.3l.2-.3.2-.2h.3l.2-.3h.3l.2-.3h.3l.3-.2h.3l.2-.2h.3l.4-.2h2.3l.3.2h.3l.3.2h.2l-.4 1h-.3l-.2-.2h-.5l-.3-.2h-1.8l-.3.2h-.3l-.3.2h-.3l-.2.3h-.2l-.3.2h-.2l-.2.2h-.2v.2l-.2.2h-.2v.3h-.2v.2h-.2v.2zm-3.5-2 1-.3v.4h.2v.4h.2v.2l.2.2h.2v.2l.2.2h.2v.2h.4v.2l.8.5v.2l-.3.2h-1v-.2h-.2l-.3-.2h-.2v-.2l-.2-.2-.2-.2h-.2v-.3h-.2v-.2h-.2v-.3h-.2v-.4h-.2zm11.8-5.4-.3 1-.4-.2h-.5l-.3-.2h-.5l-.5-.2h-5l-.5.2-.4.2h-.4l-.4.3-.4.3-.3.2v.2l-.4.2-.2.2v.3l-.3.2v1.6l.3.4.2.5-1 .5-.2-.5-.2-.6V215l.2-.6.2-.4.2-.4.3-.3.2-.4.3-.3.4-.3.4-.2.4-.2.5-.2h.5l.5-.3h1.1l.5-.2h3.3l.6.2h.5l.4.2.5.2h.3zm0 .5v-.5z"/>
<path fill="#fff" d="M458.8 124.5c3.4 0 6.2 2.7 6.2 6 0 3.5-2.8 6.3-6.2 6.3s-6-2.8-6-6.2a6 6 0 0 1 6-6z"/>
<path fill="#21231e" d="M465.5 130.6h-1c0-2-1.3-4.2-3.3-5a6 6 0 0 0-2.4-.6v-1a7 7 0 0 1 5.7 3 7 7 0 0 1 1 3.6m-6.7 6.7v-1c2 0 4-1.2 5-3a6 6 0 0 0 .6-2.7h1c0 2.3-1 4.5-3 5.7a7 7 0 0 1-3.6 1m-6.6-6.7h1c0 2 1.3 4.2 3.2 5a6 6 0 0 0 2.4.7v1a7 7 0 0 1-5.6-3.2 7 7 0 0 1-1-3.4zm6.6-6.6v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.6h-1c0-2.2 1.2-4.4 3.2-5.6a7 7 0 0 1 3.4-1"/>
<path fill="#fff" d="M446.3 124.5a6 6 0 0 1 6 6c0 3.5-2.6 6.3-6 6.3s-6-2.8-6-6.2 2.6-6 6-6z"/>
<path fill="#21231e" d="M453 130.6h-1c0-2-1.3-4-3.2-5a6 6 0 0 0-2.5-.6v-1c2.3 0 4.5 1.2 5.6 3a7 7 0 0 1 1 3.6zm-6.7 6.7v-1c2 0 4-1.3 5-3a7 7 0 0 0 .6-2.7h1c0 2.3-1.3 4.5-3.2 5.7a7 7 0 0 1-3.5 1zm-6.6-6.7h1c0 2 1.2 4.2 3 5a6 6 0 0 0 2.6.7v1a7 7 0 0 1-5.6-3.2 7 7 0 0 1-1-3.4zm6.6-6.6v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.6h-1a7 7 0 0 1 3-5.6 7 7 0 0 1 3.6-1"/>
<path fill="#fff" d="M433.8 125.5a6 6 0 0 1 6 6c0 3.5-2.6 6.3-6 6.3s-6-2.8-6-6.2 2.6-6 6-6z"/>
<path fill="#21231e" d="M440.4 131.6h-1c0-2-1.2-4-3-5a6 6 0 0 0-2.6-.6v-1a7 7 0 0 1 5.6 3 7 7 0 0 1 1 3.6m-6.6 6.7v-1c2 0 4-1.3 5-3a6 6 0 0 0 .6-2.7h1a7 7 0 0 1-3 5.7 7 7 0 0 1-3.6 1m-6.6-6.7h1c0 2.2 1.3 4.3 3.2 5.2a6 6 0 0 0 2.4.5v1a6 6 0 0 1-5.6-3.2 7 7 0 0 1-1-3.4zm6.6-6.6v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.6h-1c0-2.3 1.3-4.6 3.3-5.7a7 7 0 0 1 3.3-1z"/>
<path fill="#fff" d="M422.3 127.7c3.3 0 6 2.8 6 6.2 0 3.3-2.7 6-6 6s-6.2-2.7-6.2-6 3-6.3 6.3-6.3z"/>
<path fill="#21231e" d="M429 134h-1a6 6 0 0 0-3.3-5.2 6 6 0 0 0-2.4-.6v-1c2.2 0 4.4 1.2 5.6 3.2a7 7 0 0 1 1 3.5zm-6.7 6.5v-1c2 0 4-1.2 5-3q.6-1.5.6-2.6h1c0 2-1.3 4.3-3.3 5.5a7 7 0 0 1-3.4 1zm-6.7-6.6h1q.2 3.2 3 4.8a5 5 0 0 0 2.7.7v1a7 7 0 0 1-5.7-3 7 7 0 0 1-1-3.6zm6.7-6.8v1c-2 0-4.2 1.3-5 3.2a6 6 0 0 0-.7 2.5h-1q.1-3.9 3.3-6a7 7 0 0 1 3.3-.8z"/>
<path fill="#fff" d="M346.3 159.4a6 6 0 0 1 6 6.2c0 3.4-2.6 6.2-6 6.2s-6-2.8-6-6.2a6 6 0 0 1 6-6.2"/>
<path fill="#21231e" d="M353 165.6h-1c0-2-1.3-4-3.2-5a6 6 0 0 0-2.5-.7v-1c2.2 0 4.5 1 5.6 3a7 7 0 0 1 1 3.6zm-6.7 6.7v-1c2 0 4-1.2 5-3a7 7 0 0 0 .6-2.7h1c0 2.3-1.3 4.5-3.2 5.7a7 7 0 0 1-3.5 1zm-6.7-6.7h1c0 2 1.3 4.2 3.2 5a6 6 0 0 0 2.5.7v1a7 7 0 0 1-5.6-3 7 7 0 0 1-1-3.7zm6.7-6.7v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.6h-1a7 7 0 0 1 3-5.7 7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M356.8 152.8c3.4 0 6.2 2.7 6.2 6 0 3.6-2.8 6.3-6.2 6.3s-6-2.6-6-6a6 6 0 0 1 6-6.2z"/>
<path fill="#21231e" d="M363.5 159h-1c0-2.2-1.3-4.2-3-5a6 6 0 0 0-2.7-.7v-1a7 7 0 0 1 5.7 3 7 7 0 0 1 1 3.6zm-6.7 6.6v-1a5.7 5.7 0 0 0 5.7-5.7h1a7 7 0 0 1-3 5.6 7 7 0 0 1-3.7 1zm-6.6-6.7h1c0 2 1.2 4 3 5a6 6 0 0 0 2.6.6v1a7 7 0 0 1-5.6-3 7 7 0 0 1-1-3.7zm6.6-6.8v1c-2 0-4 1.3-5 3.2a6 6 0 0 0-.6 2.5h-1c0-2.3 1.2-4.6 3-5.8a7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M368.8 147c3.4 0 6 3 6 6.3 0 3.4-2.6 6.2-6 6.2s-6-2.8-6-6.2a6 6 0 0 1 6-6.2z"/>
<path fill="#21231e" d="M375.4 153.3h-1c0-2-1.2-4.2-3-5a6 6 0 0 0-2.6-.7v-1a7 7 0 0 1 5.6 3 7 7 0 0 1 1 3.7m-6.6 6.7v-1c2 0 4-1.3 5-3a6 6 0 0 0 .6-2.7h1a7 7 0 0 1-3 5.7 7 7 0 0 1-3.6 1m-6.7-6.7h1c0 2 1.4 4 3.2 5a6 6 0 0 0 2.6.7v1a7 7 0 0 1-5.7-3.2 7 7 0 0 1-1-3.5zm6.8-6.7v1c-2 0-4 1.2-5 3a6 6 0 0 0-.6 2.7h-1c0-2.3 1-4.5 3-5.7a7 7 0 0 1 3.6-1"/>
<path fill="#fff" d="M338.2 169.5a6 6 0 0 1 6 6.2c0 3.4-2.6 6.2-6 6.2s-6.2-3-6.2-6.3 2.8-6.2 6.2-6.2z"/>
<path fill="#21231e" d="M344.8 175.7h-1c0-2-1.3-4.2-3.2-5a6 6 0 0 0-2.4-.7v-1a7 7 0 0 1 5.6 3 7 7 0 0 1 1 3.7m-6.6 6.7v-1c2 0 4-1.3 5-3a6 6 0 0 0 .6-2.7h1c0 2.3-1.2 4.5-3.2 5.7a7 7 0 0 1-3.4 1m-6.7-6.7h1c0 2 1.2 4 3 5a6 6 0 0 0 2.7.7v1a7 7 0 0 1-5.7-3.2 7 7 0 0 1-1-3.5m6.7-6.7v1c-2 0-4 1.2-5 3a6 6 0 0 0-.7 2.7h-1c0-2.3 1.2-4.5 3.2-5.7a7 7 0 0 1 3.5-1"/>
<path fill="#edb92e" d="m408.8 279 22-12.8 23.4 10-22 13z"/>
<path fill="#21231e" d="m431 266.7-22 12.8-.4-1 22-12.7h.5v1zm-.4-1h.5-.4zm23.5 11-23.3-10 .4-1 23.4 10v1h-.3zm.4-1 1 .5-1 .5zm-22.4 13 22-13 .5 1-22 12.8h-.5zm.5.8-.2.2h-.3zm-23.4-11 23.4 10-.4 1-23.3-10v-1h.4zm-.3 1-1-.4 1-.4v1z"/>
<path fill="#c6363c" d="M431 270.6c5.8-.3 10.5 2.5 10.8 6.3.2 3.7-4.2 7-9.8 7.4-5.7.3-10.4-2.5-10.7-6.3-.2-3.6 4.2-7 9.8-7.4z"/>
<path fill="#21231e" d="M442.3 277h-1v-.7l-.2-.3v-.6l-.3-.3v-.2l-.3-.2-.2-.3-.2-.3-.3-.2-.3-.3-.2-.2-.3-.2h-.5l-.3-.3-.3-.2-.5-.2-.4-.2h-.3l-.4-.2h-.4l-.5-.3h-.9l-.5-.2h-.8l-.5-.2h-1l-.5.2v-1h3.6l.4.2.5.2h.5l.4.2.5.2.5.2.4.2.3.2.4.2.4.2.4.2.4.3.3.3.4.3.3.3.3.3.2.4.2.3.2.3.2.4v.3l.3.4.2.4v.8zm-10.3 8v-1l.4-.2h.9l.5-.2h.4l.4-.2h.5l.4-.3h.5l.4-.2.4-.2.4-.2.3-.2.4-.2.4-.3.4-.2.3-.2.3-.3.3-.2.3-.3v-.3l.3-.3.2-.4.2-.2v-.3l.2-.2v-.6l.2-.3v-1.1l1-.2v1.6l-.2.4v.3l-.3.5v.3l-.3.4-.3.3v.3l-.4.4-.3.3-.3.3-.3.2-.3.3-.4.3-.4.2-.4.3-.4.3-.4.2-.4.2-.5.2h-.4l-.4.2-.5.2h-.4l-.5.2h-.4l-.5.2h-1zm-11.2-6.8h1v.5l.2.3v.3l.2.3.2.3v.2l.3.3.2.2v.3l.4.2.2.3.3.2.2.2.3.2.4.2.2.2h.4l.4.3h.4l.4.2.4.2h.4l.5.2h1l.4.2h2.3v1H428l-.5-.2h-.4l-.5-.3h-.6l-.4-.3h-.4l-.5-.3-.3-.2-.3-.3-.4-.2-.4-.2-.3-.3-.3-.2-.3-.3-.3-.3-.3-.3v-.3l-.3-.3-.2-.3-.2-.4-.2-.3v-1l-.2-.4zm10.3-8 .2 1h-1.5l-.5.2h-.5l-.5.2-.4.2h-.5l-.4.3-.5.2-.4.2-.3.2-.4.2-.3.2-.3.3-.4.2-.3.3-.3.2-.2.3-.3.2-.2.3-.2.3-.2.4-.2.3v.3l-.3.3v.8l-.2.3v.6l-1 .2V277l.2-.4v-.3l.2-.4v-.5l.3-.3.2-.4.3-.3.2-.3.3-.3.2-.4.4-.3.4-.3.3-.3.4-.3.5-.2.3-.3.5-.3.4-.2.4-.2.6-.2h.4l.5-.2h.5l.5-.3h.5l.5-.2zl.5-.2z"/>
<path fill="#edb92e" d="m382.4 270 23.8 9-19.7 13c-5-1.6-9-19-4-22z"/>
<path fill="#21231e" d="m406 279.4-23.8-9 .4-1 23.7 9v1zm.3-1 1 .4-1 .5v-1zm-20 13.3 19.6-13.2.4.8-19.6 13.3h-.4zm.5 1h-.4zm-4.2-23.3-.4 1h.5l-.4.2-.4.3-.4.4-.2.5-.2.4-.2.7-.2.8v1.5l-.2.8v2.8l.3.8v1l.2.8.2 1 .2 1 .3.7v1l.5 1 .3.7.3.7.5.8.4.6.3.6.4.6.4.5.5.4.4.2.3.3.4.2-.3 1-.6-.3-.5-.3-.5-.3-.5-.5-.4-.6-.6-.6-.4-.7-.4-.7-.4-.7-.4-1-.3-.8-.3-1-.3-.8-.4-1-.2-.8-.2-1-.2-1v-.8l-.3-1v-5.2l.3-.8.2-.7.3-.6.3-.6.4-.5.4-.5.5-.4h.6zm-.5 0h.6-.5zm.4.4.2-.4zm-.3-.4h.6-.5z"/>
<path fill="#0c4076" d="M382.8 274.3c5.7-.6 10.5 2 11 5.8.3 3.8-4 7.4-9.6 8-3.6.3-6-3-6.3-6.3-.5-3.4 1-7 4.8-7.4z"/>
<path fill="#21231e" d="M394.2 280h-1v-.5l-.2-.3v-.3l-.2-.4-.2-.3-.2-.2-.2-.2-.2-.3-.2-.2-.3-.3-.2-.2-.3-.2-.3-.2-.4-.2-.3-.2-.4-.2-.3-.2h-.4l-.5-.3h-.4l-.4-.2h-.8l-.5-.2h-3.6l-.2-1h3.8l.4.2h.5l.4.2.4.2h.4l.5.3.4.2h.5l.4.3.2.2.4.3.3.2.2.3.3.3.3.3.2.2.3.3.2.3.2.3v.5l.3.4v.2l.2.4v.4zm-10 8.4v-1h1l.5-.2h.4l.6-.2.5-.2h.5l.4-.3.4-.2h.4l.4-.3.3-.3.4-.2.2-.2.4-.3.3-.2.2-.3.3-.2.2-.3.3-.3.2-.3.2-.2.2-.3v-.4l.3-.3v-.8l.2-.4v-1h1v1.6l-.2.3v.3l-.2.4-.2.3-.2.4-.2.3-.2.4-.3.4-.2.3-.3.3-.3.3-.5.3-.3.3h-.4l-.4.4-.4.2-.4.3h-.4l-.4.3-.5.2h-.5l-.4.3-.5.2h-.6l-.5.2h-.7l-.5.2h-.5zm-6.8-6.7h1v.7l.2.3v.3l.2.4v.3l.2.2v.2l.2.3.2.2v.3l.3.2.2.3v.2l.2.2h.2l.2.3h.2l.2.3.2.2h.3l.2.2h.2l.2.2h.2l.3.2h1.4v1h-2.1l-.3-.2h-.4l-.3-.3-.3-.2-.3-.2-.3-.2-.2-.2-.3-.2-.3-.2-.2-.2-.3-.3-.2-.2v-.2l-.3-.3-.2-.3v-.2l-.3-.4v-.3l-.3-.3-.2-.3v-.3l-.2-.3v-.6l-.2-.3v-.3zm5.4-8v1h-.2l-.4.2h-.8l-.3.3-.3.2-.3.2-.2.2h-.2l-.2.2v.2l-.3.2v.3h-.3v.3l-.2.3-.2.2v.7l-.2.3v.4l-.2.3v2.2h-1V279l.2-.3v-.3l.2-.2v-.3l.2-.3.2-.3v-.4l.3-.3.2-.3.2-.2v-.3l.3-.2.3-.2h.2l.3-.3.4-.2.3-.2h.3l.3-.2h.7l.4-.2h.4z"/>
<path fill="#fff" d="M457.5 280.4c1.7 0 3.2 1.5 3.2 3.2s-1.5 3.3-3.2 3.3-3.2-1.6-3.2-3.4 1.4-3.2 3.2-3.2z"/>
<path fill="#21231e" d="M461.2 283.6h-1v-.6l-.2-.2v-.2l-.2-.3v-.2h-.2v-.2h-.2v-.2h-.2l-.2-.2-.3-.2h-.2l-.2-.2h-.7v-1h1l.3.2.2.2h.2l.3.2v.2h.2v.2h.2v.3h.2v.2l.2.2v.2h.2v.2l.2.3v.5l.2.2v.2zm-3.7 3.8v-1h.4l.3-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2.2-.2v-.2l.2-.2v-1h1.2v.8l-.2.2v.3l-.2.2v.2l-.2.2-.2.3h-.2v.3h-.3l-.2.2-.3.2-.3.2h-.6v.2h-.6v.2h-.3zm-3.7-3.8h1v1h.2v.2l.2.2v.2l.2.2h.2v.2h.2v.2h.2l.2.2h.2v.2h1v1.2h-.7l-.2-.2h-.5v-.2h-.3l-.2-.2-.3-.2-.2-.2h-.2v-.3h-.2l-.2-.3-.2-.3v-.2l-.2-.3v-.3l-.2-.3zm3.7-3.7v1h-.9l-.2.2h-.2v.2h-.2v.2h-.2v.2h-.2v.2h-.2v.2l-.2.3v.5h-.2v.8h-1v-1.2l.2-.2v-.3h.2v-.3l.2-.2.2-.2v-.2h.4v-.2l.2-.2h.2v-.2h.5v-.2h.4v-.2h1z"/>
<path fill="#fff" d="M408.7 283c1.8 0 3.2 1.6 3.2 3.4s-1.5 3.2-3.3 3.2a3 3 0 0 1-3.2-3.2c0-1.8 1.4-3.3 3.2-3.3z"/>
<path fill="#21231e" d="M412.4 286.4h-1v-.9l-.2-.2v-.2l-.2-.2v-.2l-.2-.2-.2-.2h-.2v-.2h-.2v-.2h-.4l-.2-.2h-.8v-1.2h.7v.2h.7v.2h.3v.2h.3v.2h.3l.2.2.2.3h.2l.2.3.2.3v.3h.2v.6l.2.2v.9zm-3.7 3.7v-1h1v-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.3h.2v-.5l.2-.2v-.4h1v.8l-.2.2v.3l-.2.2v.3h-.2v.3h-.2l-.2.3h-.2v.2h-.3l-.2.2h-.2v.2h-.3v.2h-.5l-.2.2h-.8zm-3.7-3.6h1v.8h.2v.5h.2v.2h.2v.2h.2v.2h.2l.2.2h.2v.2h.5v.2h.7v1h-1.2v-.2h-.3v-.2h-.2l-.2-.2-.2-.2h-.2v-.2h-.2v-.3h-.3v-.3h-.2v-.3l-.2-.2v-.2l-.2-.3v-.8zm3.7-3.8v1h-.7v.2h-.5v.2h-.2l-.2.2v.2h-.2l-.2.2v.2l-.2.2v.2h-.2v.7l-.2.3v.4h-1v-1l.2-.2v-.3l.2-.3.2-.3v-.3h.3v-.3h.2l.2-.2.2-.2h.2v-.2h.6l.3-.2h.5l.3-.2h.2z"/>
<path fill="#fff" d="M407 268.3c1.8 0 3.2 1.4 3.2 3.2s-1.4 3.2-3.2 3.2a3 3 0 0 1-3.2-3.2c0-1.8 1.4-3.2 3.2-3.2"/>
<path fill="#21231e" d="M410.7 271.5h-1v-.7l-.2-.2v-.3l-.2-.2h-.2v-.4h-.2v-.2h-.2l-.2-.2h-.2l-.2-.2h-.5v-.2h-.4v-1h.8v.2h.3l.3.2h.2l.3.2h.2v.2l.3.2.2.2.3.3.2.3v.3h.2v.4l.2.2v1zm-3.7 3.7v-1h1v-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2.2-.2v-.4h.2v-1h1v1.2l-.2.2-.2.3v.3h-.2l-.2.3h-.2v.3h-.3v.2h-.2v.2h-.2l-.2.2h-.5l-.3.2h-.7zm-3.7-3.7h1v.8l.2.2v.4h.2v.2l.2.2v.2h.2v.2h.2v.2h.2l.2.2h.5l.2.2h.5v1h-.9l-.3-.2h-.2l-.3-.2-.3-.2h-.2l-.2-.2-.2-.3h-.3v-.3l-.2-.3v-.3h-.2v-.4l-.2-.2v-.8zm3.7-3.7v1h-.8v.2h-.5l-.2.2h-.2v.2h-.2v.2h-.2v.2h-.2v.2l-.2.2v.3l-.2.2v.7h-1v-1.2h.2v-.6h.2v-.3h.2v-.3h.3l.2-.2v-.2h.3v-.2h.3l.2-.2h.4v-.2h.5l.2-.2z"/>
<path fill="#fff" d="M457.3 266.2c1.8 0 3.2 1.4 3.2 3.2s-1.4 3.2-3.2 3.2a3 3 0 0 1-3.2-3.2c0-1.8 1.6-3.2 3.3-3.2z"/>
<path fill="#21231e" d="M461 269.4h-1v-.8h-.2v-.3l-.2-.2v-.2h-.2v-.2h-.2v-.2l-.2-.2h-.2l-.2-.2h-.2l-.3-.2h-.7v-1h1.1l.2.2h.3l.3.2.3.2.2.3h.2v.2h.2v.2h.2v.3h.2v.3l.2.2v.5l.2.2zm-3.7 3.7v-1h1.2v-.2h.2l.2-.2.2-.2v-.2h.2v-.2h.2v-.2l.2-.2v-.5h.2v-.8h1v1.1l-.2.2v.2l-.2.2-.2.3-.2.3-.2.2-.2.2-.2.2h-.2v.2h-.3v.2h-.4l-.2.2h-1zm-3.7-3.6h1v.5l.2.2v.3l.2.2v.2h.2l.2.2v.2h.2v.2h.2l.3.2h.2l.2.2h.7v1h-1l-.3-.2h-.3l-.3-.2-.3-.2v-.2l-.3-.2-.2-.2v-.2h-.2v-.3h-.2v-.6l-.2-.2v-.7zv-.2zm3.7-3.7v1h-.7v.2h-.4l-.2.2h-.2v.2h-.2v.2l-.2.2-.2.2v.4h-.2v.8h-1.2v-.5l.2-.3v-.4l.2-.2v-.2h.2v-.3h.2v-.3h.2v-.2h.3v-.3l.3-.2.3-.2h.3l.2-.2z"/>
<path fill="#edb92e" d="M371 237.4c2.3-.8 6 4.6 7 4.5 1.2-3-1.2-10.5-5.5-16.3-3-1-3.2-3-3.8-7.3-.2-1.3-2.7-.4-2.7 2 0 2.3-.3 3.6.3 5.6q.4 1.2-.8 2.3c4.5 1.8 5.6 4 5.4 9z"/>
<path fill="#21231e" d="m377.6 241.8 1 .3-.5.4h-.4l-.3-.2-.2-.2-.3-.3-.2-.2-.2-.2-.2-.2-.3-.3-.3-.2-.2-.2-.2-.3-.3-.2-.2-.2-.3-.2-.2-.3h-.3l-.3-.3h-.2l-.3-.3-.3-.2h-.2l-.3-.2h-.4l-.2-.2h-.4l-.4-1h1.3l.3.2h.3l.3.2.2.2h.3l.3.3.2.2h.3l.3.3.3.3v.2l.4.2.2.3.3.2v.2l.4.3.2.2.2.2.2.2.2.2.2.2h.2v.2h.3v.2zm1 .3-.2.4h-.3l.5-.3zm-6.3-15.8.4-1 .2.2.3.5.4.5.4.6.4.7.4.5.3.6.5.6.3.6.3.6.4.6.3.6.2.6.2.6.2.5.2.6.2.7.2.5v.6l.3.5v.6l.2.4v.5l.2.4v3.3l-.2.3-1-.2v-.3l.2-.3v-3.6l-.3-.5v-.4l-.2-.5-.2-.5-.2-.5v-.5l-.4-.6-.2-.7-.2-.5-.3-.6-.2-.6-.3-.5-.3-.5-.4-.6-.3-.7-.4-.5-.4-.6-.4-.6-.3-.5-.5-.5-.4-.5.3.2zm.4-1 .2.2-.3-.2zm-4.5-6.7h1v.7l.2.3v1l.2.3v.5l.2.3v.5l.2.2v.4h.2v.3l.2.2v.2h.2v.3h.2l.2.2v.2h.2l.2.2h.2l.2.2h.2l.2.2h.2l.3.2-.4 1h-.2l-.2-.2-.3-.2h-.2l-.2-.2h-.2l-.2-.3-.2-.2h-.2v-.2l-.3-.2-.2-.2-.2-.2v-.2l-.2-.2v-.2l-.2-.3v-.2l-.2-.2v-.3l-.2-.2v-.9l-.2-.3v-.6l-.2-.3V219l-.2-.5zm-1.7 1.8h-1v-1l.2-.2.2-.2v-.4h.2l.2-.3h.2l.2-.2.3-.2h.2l.2-.2h1.2v.2h.2v.3l.2.2v.2h-1.6v.2h-.2v.2h-.2v.2h-.2v.3l-.2.2v.8zm.3 5.6-1 .2v-.6l-.2-.2V224h-.2v-3.6h1v4.2l.2.2v.7l.2.2zm-1 2-.5.8v-1l.2-.2v-.2h.2v-.4h.2v-.4l-.2-.2v-.2l1-.3v1.8h-.2v.4h-.2v.2h-.2v.2l-.2.2h-.2zm-.5.8-.8-.3.7-.6v.8zm5 8.6h1zv-.5l.2-.5v-2l-.2-.3v-.7l-.2-.3v-.6l-.2-.3v-.2l-.2-.3-.2-.3v-.2l-.3-.3v-.2l-.3-.2-.2-.2-.3-.2-.2-.2-.3-.2h-.3l-.3-.3-.2-.2-.3-.2-.4-.2h-.3l-.4-.2.4-1 .4.2.5.2.4.2.3.2.3.2.4.2.3.2.3.2.3.2v.2l.4.3.2.3.3.3v.2l.3.3v.4l.3.3v.3l.3.3v.7l.2.3v.7l.2.4v2.9h-1zm1 0h-1l.5-.5.6.4zm-1 0v-.4h.5l-.4.4z"/>
<path fill="#edb92e" d="M368.8 218.5c-.4 1-.6 1.8-.7 3.4q0 1.5 1.2 3 1.2 1.6 3 2.6 1.2.9 2.3 3 1.1 2.3 1.2 4c0 3.5.8 6.7 2.4 7.3 1.2.3 1.6.2 2.4 0-.2-.6 0-1.5-.7-2.4a8 8 0 0 1-1.4-3.7c0-1-.5-1.6-.2-3.3.4-1.2 1-1.6 1-2.7-.3-2-2-6-4.3-6s-3.4-.3-4.6-3.4c-.3-1-1-1.6-1.2-2z"/>
<path fill="#21231e" d="m368.6 222-1-.2v-1.2l.2-.2v-1h.2v-.7h.2v-.4l1 .3v.4h-.2v1l-.2.2v1.5zm1 2.8-.8.6v-.2h-.2v-.2l-.2-.2v-.2h-.2v-.4h-.2v-.5l-.2-.2v-.7l-.2-.2v-.7h1v1.1h.2v.7h.2v.4h.2v.4h.2v.2zv.2zm3 2.4-.6 1v-.2h-.4v-.2h-.2v-.2h-.2l-.2-.2h-.2v-.2h-.3v-.2h-.2l-.2-.2v-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2l-.2-.2-.2-.2.8-.6.2.2.2.2v.2h.2l.2.2v.2h.2l.2.2.2.2h.2v.2h.2v.2h.3l.2.2.2.2h.2v.2zm2.4 3.2-1 .4v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.2v-.2h-.2l-.2-.2h-.2l.5-1h.2v.2h.2v.2h.2v.2l.2.2.2.2v.2h.2v.2h.2v.2l.2.2v.2l.2.2v.2h.2v.3h.2v.3h.2zm1.2 4.2h-1v-.8l-.2-.2v-.8h-.2v-.5h-.2v-.4h-.2v-.5h-.2v-.2l-.2-.2 1-.4v.4h.2v.4h.2v.5l.2.2v.3h.2v.6h.2v1h.2zm2 6.8-.3 1h-.3l-.2-.2-.3-.3v-.2l-.3-.2h-.2l-.2-.3v-.2h-.2v-.3l-.2-.2v-.3l-.2-.2v-.3l-.2-.2v-.5l-.2-.3v-.8l-.2-.3V236l-.2-.3v-1.5h1v2.1l.2.3v1.4l.2.2v.8l.2.2v.2l.2.2v.4l.2.2v.2l.2.2.2.2v.2h.2v.2h.2l.2.2zm1.7.8 1-.3-.5.5h-.4v.2h-1.4l-.2-.2h-.4l.3-1h.7v.2h1.2v-.2zm1-.3v.4h-.5l.4-.5zm-1.7-2 .8-.7h.2v.4h.2v.4h.2v.8l.2.2v1l-1 .4V241l-.2-.2v-.8h-.2v-.2zm-1.5-4h1v.8h.2v.8l.2.2v.2l.2.2v.2h.2v.5h.2v.2h.2v.2l-.7.7v-.2l-.2-.2v-.4h-.2v-.5h-.2v-.2l-.2-.3v-.2l-.2-.2v-.2l-.2-.3v-.4l-.2-.2v-.5zm-.2-3.4 1 .2v3l.2.2-1 .2v-.8l-.2-.2zm1-2.5h1v.7h-.2v.6l-.2.2v.2l-.2.2v.2h-.2v.6h-.2v.2l-1-.2v-.4h.2v-.4l.2-.2v-.2l.2-.2v-.2l.2-.2v-.9zm-4-5.5v-1h1.1l.3.2h.2l.2.2h.3l.2.2.2.2v.2l.3.2v.2l.3.2.2.3v.2l.3.3.2.2.2.3v.2l.2.3v.2l.2.3v.3l.2.2v.3l.2.2v.5l.2.3v.8h-1v-1l-.2-.2v-.4l-.2-.3-.2-.4v-.2l-.2-.2v-.3l-.3-.3v-.2l-.2-.2v-.3l-.2-.2-.3-.3-.2-.2-.3-.3-.3-.2-.3-.2h-.8zm-5-3.7 1-.4v.3l.2.3v.2l.2.2v.2l.2.2v.2l.2.2.2.2v.2h.2v.2h.2v.2h.4v.2h.7v.2h1.7v1H373l-.3-.2h-.4v-.2h-.3v-.2h-.3v-.3h-.3v-.2l-.2-.2-.2-.3-.2-.2v-.2l-.2-.2-.2-.4-.2-.2v-.3l-.2-.2zm-.4-2-1-.5h1.2v.4h.2v.2h.2v.2h.2v.4h.2v.2l.2.2v.2l.2.2-1 .4v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2v-.2h-.2v-.2h-.2v-.2zm-1-.5.5-1 .5 1zm.6.2.4.2zm-.5-.2.4-1 .5 1z"/>
<path fill="#edb92e" d="m360 227 2-1a6 6 0 0 1 4-.2c5.5 1.7 8 11.6 1.8 14q-1.8.2-3.4-3.3a21 21 0 0 0-4-6.8c-1-1-1-2-.4-2.6z"/>
<path fill="#21231e" d="m362 225.5.3 1h-.4v.2h-.4v.2h-.8v.2h-.2l-.2.2-.7-.7.2-.2v-.2h.4v-.2h.8v-.2h.4v-.2h.4zm4-.2-.2 1h-.5v-.2h-2.1v.2h-.7l-.2.2-.5-1h.4l.3-.2h.8v-.2h1.6l.2.2h.4zm2 15-.2-1h-.2l.5-.2h.6l.4-.4.4-.3.3-.4.3-.5.2-.4.2-.3.2-.5v-.5l.2-.5V232l-.3-.4-.2-.5-.2-.5v-.5l-.4-.4-.2-.5-.4-.3-.3-.5-.4-.3-.3-.5-.4-.3-.5-.3-.3-.4-.5-.2h-.4l.2-1h.6l.5.3.7.3.4.4.6.3.5.4.5.6.3.4.4.5.3.5.3.6.3.5.3.7.2.6v.6l.3.5v3.4l-.2.5-.2.6-.3.6-.2.5-.3.5-.4.5-.4.4-.4.3-.6.3-.6.3zm-4-3.6 1-.4v.4l.2.2.2.3.2.3v.2h.2v.5h.2v.2l.2.2.2.2h.2v.2h.2l.2.2h.8v1.2h-.8v-.2h-.5l-.2-.2-.2-.2-.2-.2-.2-.2v-.2h-.2l-.2-.2v-.2h-.2v-.3l-.2-.2v-.2h-.2v-.2l-.2-.2v-.2l-.2-.3-.3-.3zm-4-6.6.7-.6.2.2v.2h.2l.2.3v.3h.2l.2.3v.2l.2.2h.2v.3l.3.2v.2l.2.2.2.2v.4l.2.2.2.3v.3l.2.2.2.3v.3l.2.3v.3l.3.3.2.4v.3l.3.3v.3l-1 .4v-.6l-.2-.2v-.3l-.3-.2v-.3l-.2-.2v-.5l-.3-.3v-.2l-.2-.2v-.3l-.3-.2-.2-.3v-.2l-.3-.2v-.2h-.2v-.3h-.3v-.3l-.2-.2-.3-.3v-.2l-.2-.2h-.2v-.2l-.2-.2zm0-3.4v1l.3-.2v.2h-.2v.8h.2v.4l.2.2.2.2v.2l-.7.7h-.2v-.2l-.2-.2-.2-.2v-.3h-.2V227h.2v-.2h.2zm-.4 0h.4zm.4 1v-1l.3 1zm.3 0h-.2.3z"/>
<path fill="#edb92e" d="M362.8 227.7c5.4-2 9.4 8.3 4.3 10.2-2.6 1-2.8-2.4-3.4-4.3-.7-2.5-6-4-.8-6z"/>
<path fill="#21231e" d="m367.3 238.4-.4-1h.3l.3-.3.3-.2.2-.3.2-.3.2-.3v-.5l.2-.4v-2.7l-.2-.4-.2-.5-.2-.4-.2-.4-.3-.4-.3-.4-.2-.3-.4-.4-.3-.2-.3-.2-.4-.2-.4-.2h-.4l-.3-.2h-1.3l-.4.2-.4-1h.6l.5-.2h1.6l.5.3.5.2.4.2.4.4.5.4.4.4.3.4.3.5.2.4.3.6.3.5.2.6.2.6v.5l.2.5v1.6l-.2.5v.5l-.3.5-.2.4-.3.5-.4.3-.4.4-.4.2-.5.2zM363 234l1-.4.2.2v.6l.2.2v.8l.2.2v.4l.2.2v.2l.2.3.2.3v.2h.2v.2h.2l.2.2h1.2l.3.8h-.3l-.3.2h-1.2l-.2-.2h-.2v-.2l-.3-.2-.3-.3v-.2l-.2-.2v-.2h-.2v-.3l-.2-.2v-.4h-.2v-.8l-.2-.2v-.8l-.2-.2v-.2zm-.4-6.7.4 1h-.5l-.3.3h-.4l-.2.3h-.2l-.2.2v.2h-.2v.4h.2v.3l.2.2v.2h.3v.3l.3.2h.2l.2.2.2.2.2.2.2.2.2.3.2.3.2.2v.3l.3.2v.6l-1 .3v-.7l-.2-.2h-.2v-.3l-.3-.2h-.2v-.3l-.3-.2-.2-.2-.2-.2-.2-.2v-.2l-.2-.2h-.2v-.3l-.3-.3v-.2l-.2-.2v-.8l.2-.3v-.3l.3-.2.2-.2.3-.3.3-.2h.4l.4-.3.6-.2z"/>
<path fill="#0c4076" d="M363.4 229.3c2.6-1 6.2 5 3 6.5-2.3 1-4-.3-4.8-2-.7-2 0-3.8 1.7-4.5z"/>
<path fill="#21231e" d="m366.5 236.3-.4-1h.3l.2-.2h.2v-.2l.2-.2v-2l-.2-.3v-.3h-.2v-.4l-.3-.3-.2-.2-.3-.4-.2-.2-.2-.2h-.2v-.2l-.3-.2h-1.1l-.3-1h1.5l.4.3.3.2.2.3.2.2.3.2.2.2.2.3v.3l.3.3.2.4.2.3v.4l.2.4v2.6l-.2.3-.2.4-.2.3-.2.2h-.4l-.3.3zm-5.4-2.5 1-.3.2.3.2.3v.2l.2.3.2.2.2.2h.3v.2h.2l.3.2h.4v.2h1.3l.3-.2.5 1h-.7l-.2.2h-1.9l-.2-.2h-.2v-.2h-.3l-.2-.2-.3-.2-.3-.3-.3-.3h-.2v-.3l-.2-.3-.2-.3v-.2zv-.2zm2.2-5 .3 1h-.3l-.2.2-.2.2-.2.2-.2.2-.2.2v.2h-.2v.7l-.2.2v1.1l.2.2v.3l-1 .3v-.7l-.2-.3v-1.5l.2-.2v-.3l.2-.2.2-.3.2-.3v-.2l.3-.2v-.2h.3l.2-.2.3-.2h.2zm.2.5-.2-.5z"/>
<path fill="#fff" d="M400 179.7c3.6 0 6.4 2.8 6.4 6.4s-2.8 6.5-6.3 6.5-6.3-3-6.3-6.4 3-6.3 6.4-6.3z"/>
<path fill="#21231e" d="M407 186h-1c0-2-1.4-4.3-3.3-5.2a6 6 0 0 0-2.6-.6v-1c2.4 0 4.7 1.2 6 3.2a7 7 0 0 1 1 3.7zm-7 7v-1a6 6 0 0 0 5.3-3.3 6 6 0 0 0 .6-2.6h1c0 2.4-1.3 4.7-3.3 6a7 7 0 0 1-3.6 1zm-6.8-7h1a6 6 0 0 0 3.3 5.4 6 6 0 0 0 2.6.6v1a7 7 0 0 1-5.7-3.3 7 7 0 0 1-1-3.6zm7-7v1.2a6 6 0 0 0-5.3 3.2 6 6 0 0 0-.8 2.7h-1a7 7 0 0 1 3.3-5.8 7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M395.4 169c3.5 0 6.4 3 6.4 6.4 0 3.6-3 6.4-6.4 6.4s-6.3-2.8-6.3-6.4c0-3.5 3-6.4 6.4-6.4z"/>
<path fill="#21231e" d="M402.3 175.4h-1c0-2.2-1.3-4.3-3.2-5.3a6 6 0 0 0-2.6-.5v-1q3.8.2 6 3.2a7 7 0 0 1 1 3.7zm-7 7v-1c2.3 0 4.4-1.4 5.4-3.3a6 6 0 0 0 .6-2.6h1c0 2.4-1.2 4.7-3.2 6a7 7 0 0 1-3.6 1zm-6.7-7h1a6 6 0 0 0 3.2 5.3q1.2.6 2.6.6v1a7 7 0 0 1-5.8-3.2 7 7 0 0 1-1-3.6zm6.8-7v1c-2 0-4.2 1.4-5.2 3.3a6 6 0 0 0-.6 2.7h-1c0-2.3 1.2-4.6 3.2-6a7 7 0 0 1 3.6-1"/>
<path fill="#fff" d="M410.7 131.5c3.4 0 6 2.7 6 6s-2.6 6.3-6 6.3-6.2-2.7-6.2-6.2a6 6 0 0 1 6.2-6z"/>
<path fill="#21231e" d="M417.3 137.7h-1c0-2.2-1.2-4.2-3-5a6 6 0 0 0-2.6-.7v-1a7 7 0 0 1 5.6 3 7 7 0 0 1 1 3.7m-6.6 6.6v-1c2 0 4-1.2 5-3q.6-1.5.6-2.6h1c0 2.2-1.2 4.5-3 5.6a7 7 0 0 1-3.6 1m-6.7-6.7h1c0 2.2 1.3 4.2 3.2 5a6 6 0 0 0 2.5.7v1a7 7 0 0 1-5.6-3 7 7 0 0 1-1-3.6zm6.7-6.6v1c-2 0-4 1.2-5 3a6 6 0 0 0-.7 2.6h-1a7 7 0 0 1 3-5.6 7 7 0 0 1 3.7-1"/>
<path fill="#fff" d="M400.4 136.2c3.5 0 6.3 3 6.3 6.4s-2.8 6.4-6.3 6.4-6.4-3-6.4-6.4 2.8-6.4 6.4-6.4"/>
<path fill="#21231e" d="M407.2 142.6h-1a6 6 0 0 0-3.3-5.3 6 6 0 0 0-2.6-.6v-1c2.3 0 4.6 1.2 5.8 3.2a7 7 0 0 1 1 3.6zm-6.8 7v-1c2 0 4.2-1.4 5.2-3.3a6 6 0 0 0 .6-2.7h1c0 2.3-1.2 4.7-3.2 6a7 7 0 0 1-3.6 1m-7-7h1c0 2.2 1.4 4.3 3.3 5.3a6 6 0 0 0 2.7.5v1a7 7 0 0 1-6-3.2 7 7 0 0 1-1-3.7zm7-7v1a6 6 0 0 0-5.3 3.3 6 6 0 0 0-.5 2.6h-1c0-2.4 1.2-4.7 3.2-6a7 7 0 0 1 3.7-1z"/>
<path fill="#fff" d="M392.8 145.3c3.5 0 6.3 2.8 6.3 6.4 0 3.5-2.7 6.4-6.2 6.4s-6.4-2.8-6.4-6.3c0-3.6 3-6.4 6.4-6.4z"/>
<path fill="#21231e" d="M399.6 151.7h-1a6 6 0 0 0-3.2-5.3 6 6 0 0 0-2.6-.6v-1c2.3 0 4.6 1.2 5.8 3.2a7 7 0 0 1 1 3.7m-6.8 7v-1c2 0 4.2-1.4 5.2-3.4a6 6 0 0 0 .6-2.6h1c0 2.3-1.2 4.6-3.2 6a7 7 0 0 1-3.6 1m-7-7h1c0 2 1.4 4.3 3.3 5.2a6 6 0 0 0 2.8.6v1a7 7 0 0 1-6-3.3 7 7 0 0 1-1-3.6zm7-7v1a6 6 0 0 0-5.3 3.3 6 6 0 0 0-.6 2.7h-1c0-2.4 1-4.7 3.2-6a7 7 0 0 1 3.6-1z"/>
<path fill="#fff" d="M391.5 156.7c3.6 0 6.4 2.8 6.4 6.4s-3 6.5-6.5 6.5-6.3-3-6.3-6.4 2.8-6.3 6.3-6.3z"/>
<path fill="#21231e" d="M398.4 163h-1c0-2-1.3-4.2-3.2-5.2a6 6 0 0 0-2.7-.6v-1c2.4 0 4.7 1.2 6 3.2a7 7 0 0 1 1 3.7zm-7 7v-1a6 6 0 0 0 5.4-3.3 6 6 0 0 0 .6-2.6h1c0 2.5-1.3 5-3.4 6a7 7 0 0 1-3.5 1zm-6.7-7h1c0 2.3 1.3 4.4 3.2 5.4q1.1.6 2.5.6v1a7 7 0 0 1-5.8-3.2 7 7 0 0 1-1-3.7zm6.8-6.8v1c-2 0-4.2 1.3-5.2 3.2a6 6 0 0 0-.6 2.7h-1c0-2.3 1.2-4.6 3.2-5.8a7 7 0 0 1 3.5-1z"/>
<path fill="#edb92e" d="M474.4 198q-4-7.5-8.6-9.6 6.4-19.8 2.7-51-.4-4.8-2.2-4.6-1.6-.1-1.8 5.2c.8 11.6 1.7 21.6 1.2 30.2-.5 7.6-2.3 14-4.2 21.2-.3 1 .4 1.7 3.8 3.7q4 2.4 4.7 5h1.5q3.8 1 3 0z"/>
<path fill="#21231e" d="m465.3 188.2 1 .3-.3-.6h.3l.3.2.3.2.2.2h.3l.3.3h.3l.4.4.2.2.3.2.2.3.3.3.3.3.3.3.3.2.3.3.3.3.2.3.2.5.3.3.3.4.2.3.3.4.3.3.2.4.3.4.3.5.2.5.3.4.3.4.3.5.3.5-1 .5-.3-.5-.2-.4-.3-.5-.2-.5-.3-.4-.3-.3-.2-.4-.3-.4-.3-.5-.3-.4-.2-.2-.3-.4-.4-.3-.2-.2-.3-.3v-.3l-.4-.3-.3-.4-.2-.3-.2-.2-.3-.2-.2-.2-.3-.2-.3-.2-.2-.2-.3-.2-.3-.2-.3-.2h-.3l-.3-.3-.4-.2-.3-.6zm.3.6h-.4v-.6zm2.4-51.5h1l.2 2 .2 1.8.2 2 .2 2v1.8l.2 1.8v1.8l.2 1.8v15l-.3 1.5-.2 1.5v1.5l-.3 1.5-.2 1.5-.2 1.4-.2 1.5-.2 1.3-.3 1.4-.3 1.3-.3 1.4-.3 1.3-.4 1.3-.3 1.3-.4 1.2-1-.3.4-1.2.4-1.3.4-1.3.3-1.3.3-1.2.3-1.3.3-1.4.2-1.3.3-1.4.3-1.5.2-1.4V172l.3-1.6V169l.2-1.7v-1.6l.2-1.6v-13.6l-.2-1.8v-2l-.2-1.7-.2-2-.2-1.8-.2-2-.2-2zm-1.7-4v-1h.6v.2h.3v.2h.2l.2.3h.2v.5h.2v.3l.2.2v.3l.2.2v.4l.2.4v.9l.2.3v.4h-1v-.9l-.2-.2v-.6l-.2-.2v-.5h-.2v-.5h-.2v-.2l-.2-.2h-.2v-.2h-.4zM465 138h-1v-1.7l.2-.3v-1l.2-.3v-.7h.2v-.3l.2-.3v-.3h.2l.2-.2.3-.2.2-.2h.5v1h-.2v.2h-.2v.2l-.2.2v.3l-.2.3v.6l-.2.2v1.6l-.2.4v.7zm1.2 30.2h-1V154l-.2-1v-2.8l-.2-1v-2l-.2-1v-1l-.2-1v-2l-.2-1V139l-.2-1 1-.2v1l.2 1.2v2l.2 1v2.2l.2 1v2l.2 1v2l.2 1v2.8l.2 1v12zm-4.3 21.3-1-.3.2-.6v-.7l.3-.8.2-.6.2-.7v-.7l.2-.7.2-.6.2-.7v-.6l.3-.6v-.6l.2-.6.2-.7v-.5l.2-.6.2-.8v-.6l.2-.7.2-.7v-.7l.2-.6v-.7l.2-.6v-.7l.2-.7v-.7l.2-.6V169l.2-.8h1v1.5l-.2.7v1.5l-.2.6v.7l-.2.8v.7l-.2.6v.8l-.2.7-.2.6v.8l-.2.6-.2.7v.7l-.3.7v.6l-.3.7v.6l-.3.6-.2.6v.6l-.3.7-.2.5-.2.7v.6l-.3.7-.2.7-.2.6v.7zm3.6 3-.5 1-.2-.2h-.3l-.3-.3-.3-.2h-.4l-.2-.3h-.2l-.3-.3h-.3v-.2l-.3-.2h-.2l-.2-.2-.2-.3h-.2v-.2h-.2l-.2-.2v-.2l-.2-.2v-1l1 .2v.5h.2v.2l.2.2h.2v.2h.2l.2.2.3.3h.2l.2.2.2.2h.2l.2.3h.3l.3.3.3.2h.3zm4.2 5.2.4 1-.7-.4v-.2l-.2-.2v-.3l-.2-.2v-.3l-.2-.3-.3-.3-.2-.3-.3-.2-.3-.3v-.2h-.3v-.2h-.3v-.2l-.3-.2h-.2v-.3h-.3l-.2-.2h-.3l-.2-.2-.2-.2h-.2l.6-1 .2.2h.2l.3.3.2.2.2.2h.2v.3h.4l.2.3.3.3.3.3.2.2.3.3v.3l.3.2h.2v.3l.3.2v.2h.2v.3l.2.2v.2l.2.3v.3l.2.2v.2l-.6-.3zm.4 1h-.5l-.2-.4zm1.4-1.2-.3 1h-1l-.4-.8h.2l.2-.2zm3.3.5h-1l.7-.4.2.2v.8l-.2.2h-.2v.2h-1.8l-.2-.2h-.3l-.2-.2h-.5l.3-1h.7l.2.2h1v.2h.7v.4zm-1 0v-1l.7.6-.8.4zm0 0h1l-1 .3z"/>
<path fill="#edb92e" d="M458.4 214.6c1.6-1 2-.8 2.4-1.4q.8-1.4.4-3.5c-.8-5.3 2.4-4.6 6-2.2 1.7 1 3.5 4 5 5.3s2.8 1.5 3.2 2v9.2c-.4.6-1.7.7-3.2 2-1.5 1.5-3.3 4.2-5 5.4-3.6 2.3-6.8 3-6-2.2q.4-2.1-.4-3.5c-.5-.7-.8-.4-2.4-1.4a7 7 0 0 1-2.3-2.2c-.4-.8 0-1.7 0-2.6s-.4-1.8 0-2.6q.9-1.2 2.4-2.2z"/>
<path fill="#21231e" d="m460.4 213 .8.5-.2.2-.2.2h-.4v.2h-.4l-.2.2h-.2l-.2.2h-.2v.2h-.5v.2l-.7-1h.6v-.2h.2l.2-.2h.2l.2-.2h.4v-.2h.4v-.2h.2zm.3-3.2 1-.2v2.9l-.2.3v.2h-.2v.2l-1-.5v-.2h.2v-.4h.2v-2.6zm6.7-2.8-.6 1-.3-.3-.3-.2-.3-.2-.4-.2h-.3l-.3-.2h-.3l-.3-.2h-.3v-.2h-.4l-.2-.2h-1.1v.2h-.2v.2l-.2.2v.6l-.2.3v2l-1 .2v-3l.2-.4v-.3l.3-.2.3-.2.2-.2h.2l.2-.2h.2l.2-.2h1.5v.2h.5l.3.2h.3l.4.3h.3l.3.2.4.2.3.2.3.2zm5 5.4-.6.7-.3-.2-.3-.3-.3-.3-.2-.2-.2-.2-.3-.3-.2-.2v-.2h-.2v-.3l-.3-.2-.3-.3-.2-.2v-.2l-.3-.2v-.2h-.3l-.2-.3-.3-.2-.2-.2h-.2l-.2-.2v-.2h-.3v-.2h-.2l.6-1 .2.2.3.2v.2h.3v.3h.3l.2.2.2.2v.2l.3.2h.2v.3l.3.3.2.2v.2l.3.2.3.3h.2v.3l.3.2.2.2.2.2.3.3v.2h.3v.3h.3v.3h.2zm3.5 2.4h-1.2v.3h-.2l-.2-.2h-.2v-.2h-.4l-.2-.2h-.2v-.2h-.3v-.2h-.3v-.2l-.3-.2-.3-.2-.2-.3.7-.6h.2v.2h.2v.2h.2v.2h.2v.2h.3l.2.2.2.2h.2l.2.2h.2l.2.2h.2v.2h.4v.2l.2.2v.2zm-.2-.2v.2zm0 4.8h-1v-4.5l1-.2v4.5zm-1 0h1zm1 5-1-.7v.3-4.4h1zm0-.4v.2zm-3.3 2.5-.7-.8h.2v-.2l.3-.2.3-.2h.3v-.2h.3v-.2h.2l.2-.2h.2v-.2h.4v-.2h.4v-.2l1 .5-.2.2-.2.2h-.2v.2h-.4l-.2.2h-.2v.2h-.4v.2h-.3v.2h-.2l-.2.2-.2.2v.2h-.3zm-5 5.3-.7-.8h.2v-.2l.3-.2.3-.2.2-.2.3-.3.2-.2.2-.2.2-.2v-.2h.3v-.3l.2-.3.2-.2v-.2l.3-.2.3-.2v-.2l.3-.2.2-.2v-.2h.3v-.3l.3-.2.2-.2h.2v-.3h.3v-.3l.8.8-.2.2-.2.2-.2.2v.2h-.3v.3l-.3.2-.3.2-.2.2-.2.2-.2.2v.2l-.3.2v.2l-.2.2h-.2v.3h-.3l-.2.3v.2h-.2l-.2.3-.2.2-.3.3-.3.3-.3.2zm-6.8-2.7 1 .3v2.6l.2.2v.2h.2v.2h1.7v-.2h.4l.3-.2h.3l.3-.3.3-.3h.3l.2-.3.3-.2.3-.2.6.8-.4.2-.3.3-.3.2h-.4l-.3.3-.3.2h-.3l-.2.3h-.4l-.3.2h-.4l-.3.2h-.9l-.3-.2h-.3l-.2-.3-.2-.2-.2-.2v-.3l-.2-.2v-.7l-.2-.3zm-.3-3 .8-.6v.2h.2v.4h.2v.6l.2.2v2.4l-1-.2v-2.5h-.2v-.4h-.2zm-2.3-1.3.7-.8h.2l.2.2h.2l.2.2h.2l.2.2h.4v.2h.2v.2h.2l.2.2.2.2-.8.6-.2-.2h-.2v-.2h-.4l-.2-.2h-.4v-.2h-.2l-.2-.2h-.2v-.2zm.7-.8.5.2zm-3-1.7.8-.5.2.2v.2h.2v.4h.2v.2h.2v.2h.2v.2h.2l.2.2.2.2h.2v.2h.2l-.5 1v-.2h-.2l-.2-.2-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2l-.2-.2-.2-.2v-.2zm-.3-3h1v2.4l-.7.5v-.2h-.2v-.4l-.2-.2v-2.1zm.3-2.8.8.6v.4l-.2.2v1.8h-1V217h.2v-.4zm2.4-2.4.7 1h-.2v.2h-.2l-.2.2-.2.2-.2.2-.2.2-.2.2-.2.2v.2h-.2v.2h-.2v.2l-1-.5h.2v-.2l.2-.2.2-.2v-.2h.2l.2-.2v-.2h.2v-.2h.2l.2-.2h.2v-.2l.2-.2h.2v-.2h.2zm.7 1 .5-.3z"/>
<path fill="#21231e" d="M475.5 220.2v1q-5.8.7-9.7 4.3c1.8-2.7 5.7-4.7 9.7-5.3m0-1.6v-.8q-5.8-.8-9.7-4.4c1.8 2.7 5.7 4.6 9.7 5.2"/>
<path fill="#edb92e" d="M461.2 207.3q1.6.6 3.4 2c1.6 1.5.6 4-1.5 6.6q-1 1.2-1 3.5.1 2.1 1 3.5c2.2 2.5 3.2 5 1.6 6.6q-1.6 1.4-3.4 2v-2.4q.4-2.1-.4-3.5c-.5-.7-.8-.4-2.4-1.4a7 7 0 0 1-2.3-2.2c-.4-.8 0-1.7 0-2.6s-.4-1.8 0-2.6q.9-1.2 2.4-2.2c1.6-1 2-.8 2.4-1.4q.8-1.4.4-3.5v-2.4z"/>
<path fill="#21231e" d="m465 209-.8.6-.2-.2-.2-.2h-.2v-.2h-.2l-.2-.2h-.2v-.2h-.4v-.2h-.5v-.2h-.2l-.2-.2h-.2l-.2-.2h-.2l.4-1h.2l.2.2h.2l.2.2h.3l.2.2h.2v.2h.3l.2.2h.2v.2h.3v.2h.2l.2.2.2.2.2.2zm-1.5 7.2-.8-.6.2-.3v-.2h.2l.2-.3.2-.3v-.2l.3-.2v-.5l.2-.2v-.2h.2v-.3l.2-.2v-.4h.2v-1.6l-.2-.3-.2-.2-.2-.2.7-.7.3.3v.2l.2.2v.2h.2v.5l.2.2v1l-.2.2v.4l-.2.2v.5l-.2.3v.3l-.3.2v.5l-.3.2v.3l-.3.2-.2.3v.2l-.3.2-.3.3-.2.2zm-1 3.3h-1v-.7l.2-.2v-1.2h.2v-1h.2v-.4l.2-.2v-.2h.2v-.2l1 .6h-.2v.4h-.2v.2l-.2.2v.9h-.2v1.4h-.2v.5zm1 3.2-.8.6v-.2h-.2v-.2h-.2v-.4h-.2v-1.1l-.2-.3V220h-.2v-.5h1v1.1l.2.2v.4l.2.2v.4l.2.2v.2h.2v.2zm1.5 7.3-.8-.8h.2v-.2h.2v-.3l.2-.2v-1.9l-.2-.2v-.2l-.2-.2v-.2l-.2-.3v-.2l-.2-.2-.3-.3v-.3l-.3-.2-.2-.2v-.2l-.3-.3-.3-.2.8-.6.2.2h.2v.4l.2.2.2.3h.2v.3l.3.3v.5l.3.3v.3l.2.2v.5l.2.2v.5l.2.2v.7l-.2.4v.4l-.2.2v.2l-.2.2v.2l-.3.2v.2zm-4.3 1.7 1-.2-.7-.4h.7v-.2h.4l.2-.2h.2l.2-.2h.2v-.2h.4v-.2h.2l.2-.2.2-.2h.2v-.2h.2v-.2l.8.8h-.2v.2h-.2v.2h-.2l-.2.2-.2.2h-.2l-.2.2h-.2v.2h-.4v.2h-.4v.2h-.5v.2h-.2l-.2.2h-.2l-.7-.3zm.7.4-.6.3v-.6l.6.4zm-.7-3 1 .3v2l-1 .3v-2.4zm-.3-3 .8-.6v.2h.2v.4h.2v.6l.2.2v2.4l-1-.2v-2.4h-.2v-.4h-.2zm-2.3-1.3.7-.8h.2l.2.2h.2l.2.2h.2l.2.2h.4v.2h.2l.2.2.2.2.2.2-.8.6-.2-.2h-.2v-.2h-.4l-.2-.2h-.4v-.2h-.2l-.2-.2h-.2v-.2zm0 0-.3-.2zm-2.3-2.4.8-.5.2.2v.2h.2v.4h.2v.2h.2v.2h.2v.2h.2l.2.2h.2v.2h.2v.2h.2l-.5 1v-.2h-.2l-.2-.2-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2l-.2-.2-.2-.2v-.2zm0 0 .3.5zm-.3-3h1v2.4l-.7.5v-.2h-.2v-.4l-.2-.2v-2.2zm.3-2.8.8.6v.4l-.2.2v1.8h-1V217h.2v-.4zm2.4-2.4.7 1h-.2v.2h-.2l-.2.2-.2.2-.2.2-.2.2-.2.2-.2.2v.2h-.2v.2h-.2v.2l-1-.5h.2v-.2l.2-.2.2-.2v-.2h.2l.2-.2v-.2h.2v-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2h.2zm2.4-1 .8.5-.2.2-.2.2h-.4v.2h-.4l-.2.2h-.2l-.2.2h-.2v.2h-.5v.2l-.7-1h.6v-.2h.2l.2-.2h.2l.2-.2h.4v-.2h.4v-.2h.2zm.3-3.2 1-.2v2.9l-.2.2v.2h-.2v.2l-1-.5v-.2h.2v-.4h.2zm.5-2v-1l.5.6v2.1l-1 .2V207l.5.6zm0-1h.6v.6zm0 0v1h-.2zM361 226.3l.7-.3q-2.4-3.2-1.3-6.3c-1.3 1.6-1 4.2.6 6.6"/>
<path fill="#edb92e" d="M410.7 200c1.4 1.6 1.2 2 2 2.5q1.4.8 3.3 0c5-2 5 2 3.5 6.4-.7 2-2.8 4.3-3.7 6.2-1 1.7-.7 3-1 3.5l-4 .6-4 .5c-.6-.3-1-1.6-2.6-3s-4.4-3-6-4.6c-3-3.5-4.6-6.8.2-6.7q2 .1 3-1c.4-.7 0-1 .4-3q.5-1.9 1.4-3c.6-.8 1.5-.6 2.3-.7 1-.2 1.6-.7 2.5-.2q1.6.6 2.7 2.3z"/>
<path fill="#21231e" d="m413 202-.6 1-.2-.2-.2-.2-.2-.2v-.2h-.2v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.3-.3-.3.8-.6.3.2.2.3v.2h.2v.4h.2v.4h.2v.2l.2.2v.2h.2v.2l.2.2zm2.9 0 .2 1h-.5l-.2.2h-.8v.2h-1.2l-.3-.2h-.2l-.2-.2h-.2l.5-1v.2h.8v.2h1l.2-.2h.4l.2-.2zm4 7-1-.3v-.4l.2-.4v-.5l.2-.4v-.3l.2-.3v-.7l.2-.4v-1.7l-.2-.2v-.4h-.2l-.2-.3h-.2l-.3-.2H417l-.4.3h-.4l-.3-.8.3-.2h.5l.4-.2h1.8l.3.2h.3l.2.3.2.4v.2l.3.3.2.4v3.3l-.2.4v.4l-.2.4v.4l-.2.4v.4zm-3.7 6.4-1-.5v-.2l.2-.2.2-.2v-.2l.2-.2v-.4l.2-.2.2-.2v-.2l.2-.2.2-.2.2-.3.2-.2.2-.3.2-.2v-.2l.2-.2h.2v-.3l.2-.2v-.2l.2-.2.2-.3v-.2l.2-.2v-.2l.2-.3 1 .3v.2l-.2.3v.2l-.2.2-.2.3v.2l-.2.2v.3l-.3.2-.2.2v.2l-.2.2-.2.2v.2l-.2.2-.2.2v.2l-.3.2v.2l-.2.2-.2.2v.2l-.2.2v.4h-.3v.3l-.2.2-.2.2v.2zm-1.5 3.8-.2-1-.3.2v-.4h.2v-1l.2-.2v-.4l.2-.3v-.4l.2-.3v-.2h.2l1 .4-.2.2-.2.3v.3h-.2v.7h-.2v.9l-.2.2v.4l-.2.2v.2zm.3 0h-.2.3zm-4.3.6v-1h1.1v-.2h1.2v-.2h1.1l.3-.2.2 1h-.4l-.3.2h-.9l-.2.2h-1.3v.2h-.7zm0 0v-1 .5-.5zv-.5zm-4 .5.4-1h-.2 1.3l.3-.2h1.9l.2-.2.2 1h-.8v.2h-1.4v.2h-2zm.2 0h-.3.2zm-3-3 .5-1 .2.3.3.3h.2l.2.3.2.2.2.3.2.2v.2h.2v.2h.2v.2l.2.2v.2h.2v.2h.2v.2l-.4 1v-.2h-.2l-.2-.2-.2-.2v-.2h-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.2-.2-.2-.2-.3v-.2h-.2v-.2h-.3v-.3zm-6-4.8.7-.6.2.2.3.3.3.3h.2v.3l.3.2h.3l.2.3h.2l.2.3.3.3.2.2.2.2h.2v.2l.3.2h.2l.2.3.2.2.3.3h.2l.2.3.3.2.2.2h.2v.2l-.5.8h-.3v-.2l-.3-.2h-.2v-.2l-.3-.2h-.3l-.2-.2-.2-.2h-.2v-.2l-.3-.2h-.2l-.2-.3h-.2v-.2h-.3l-.2-.2-.2-.2h-.2v-.3h-.3l-.2-.3-.2-.2h-.2v-.3h-.3v-.3l-.3-.2h-.2v-.3l-.3-.2zm.4-7.5v1H397l-.2.2h-.4l-.2.2v.2h-.2v1.5l.2.3v.3l.2.2.2.3.2.4.2.3.2.3.2.3.2.3.3.3v.2l.4.3.3.4-.8.5-.4-.3-.3-.3-.2-.4-.2-.4-.2-.3-.3-.4-.2-.3v-.4l-.3-.4v-.3l-.3-.3v-.3l-.3-.3V206l.2-.2.2-.2.2-.2.3-.2h.2l.3-.2h.7l.4-.2h.8zm2.5-.8.8.5-.2.2-.2.2h-.2v.2h-.2l-.2.2h-.2l-.2.2h-.5l-.2.2h-1.1v-1h1.4l.2-.2h.2l.2-.2h.2v-.2zv-.2zm.4-2.8 1 .2v.4h-.2v2h-.2v.4l-.2.2-1-.4h.2v-1.4l.2-.3v-.8zm1.4-3.2.8.6-.2.2v.2h-.2v.4h-.2v.4h-.2v.4l-.2.2v.4l-.2.2v.4l-1-.2v-.7l.2-.2v-.4l.2-.2v-.4h.2v-.4h.2v-.4h.2v-.2h.2v-.4zm.8.6.5-.7zm1.8-1.5.2 1H404v.2h-.4v.2h-.2l-.8-.5.2-.2.2-.2h.2v-.2h.4l.2-.2zm.2 1h-.7.6zm2.7-1.2-.5 1H406l-.2.2h-.4l-.2-1h.8l.2-.2h2.1zm3 3h-1v.2-.2h-.2v-.2l-.2-.2-.2-.2-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2l-.2-.2-.2-.2h-.2l-.2-.2.5-1v.2h.2l.2.2h.2v.2h.2v.2h.2l.2.2.2.2.2.2h.2v.2h.2v.2l.2.2.2.2.2.2v.2l.3.3zm-.2-.4h.2v.3-.4zm-.8.3h1l-1 .2v-.3zm0 .2v-.3.2z"/>
<path fill="#21231e" d="M410 219.4h-.7q-2.1-5.7-6.3-9.5c2.8 1.4 5.5 5.3 7 9.4zm1.4-.2.8-.2q-.8-5.9 1.5-11c-2 2.5-2.8 7-2.3 11.2"/>
<path fill="#edb92e" d="M418.3 202q-.2 2.2-1 4.3c-1 2-3.8 1.3-6.6-.7q-1.4-.9-3.4-.6t-3 1.7c-1.5 2.6-3.4 4-5.2 2.6q-1.6-1.6-2.7-3.5.8-.3 2-.3 2.1.1 3-1c.5-.7 0-1 .6-3q.2-1.9 1.2-3c.6-.8 1.5-.6 2.3-.7 1-.2 1.6-.7 2.5-.2q1.6.6 2.7 2.3c1.4 1.6 1.2 2 2 2.5q1.6.8 3.3 0 1.4-.6 2.3-.5z"/>
<path fill="#21231e" d="m417.7 206.5-1-.4.2-.2v-.5l.2-.2v-.4l.2-.2v-.9l.2-.2v-1.2h.2v-.4h1v1.1l-.2.3v1h-.2v.7h-.2v.7h-.2v.3l-.2.3zm-7.2-.5.5-.8.3.2h.2l.3.3h.2l.3.3h.2l.3.2.2.2h.2l.3.2h.4v.2h.7l.2.2h1v-.2h.2l.2-.2.2-.3.2-.2 1 .5-.2.2-.2.3-.3.3v.2h-.2l-.2.2h-.2l-.3.2h-2l-.2-.2h-.2l-.3-.2h-.2l-.2-.2h-.2l-.3-.2-.2-.2h-.2l-.3-.3-.2-.2h-.3l-.3-.3-.2-.2zm-3-.4-.3-1h1.1v-.2h1.3v.2h.6v.2h.4v.2h.2l.2.2-.5.8h-.2v-.2h-.4v-.2zm-2.7 1.3-.8-.6v-.2h.2v-.2h.2v-.2l.2-.2h.2v-.2h.2l.2-.2h.2l.2-.2h.3v-.2h.8l.2-.2h.5l.2 1h-.7v.2h-.4v.2h-.4l-.2.2h-.2v.2h-.2v.2h-.2v.2h-.2zm-6 2.7.6-.8h.3v.2h.5l.2.2h.5l.3-.2h.3v-.2h.3v-.2h.2v-.3h.3v-.3h.3v-.3l.3-.3.2-.2.2-.2v-.2h.3v-.4l.3-.2.8.5v.2l-.3.2v.3l-.3.2h-.2v.3l-.3.2-.2.2-.2.2-.3.3h-.2l-.2.3h-.2v.3h-.3l-.3.2h-.4l-.3.2h-1.2l-.2-.2h-.2l-.2-.2-.2-.2h-.2zm-2.7-4.4.5 1 .3-.8v.3h.2v.2l.2.2v.2h.2v.2l.2.2v.2h.2v.2l.2.2.2.2.2.2v.2h.2v.2h.2v.2h.2v.2h.2v.2h.2l-.6 1-.2-.2-.2-.2-.2-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.3h-.2v-.2h-.2v-.2h-.2v-.3h-.2v-.2h-.2v-.4h-.2v-.2h-.2V205zm0 .8-.4-.4.5-.3v.8zm2.4-1v1H397v.2h-.4l-.5-1h1.2v-.2zm2.5-.8.8.5-.2.2-.2.2h-.2v.2h-.2l-.2.2h-.2l-.2.2h-.7l-.2.2h-1.2v-1h1.4l.2-.2h.4v-.2h.2v-.2h.2v-.2zm.4-2.8 1 .3v.3l-.2.3v1.7h-.2v.7h-.2l-1-.4h.2v-1.4h.2v-1.3zm1.4-3.2.8.6-.2.2v.2h-.2v.4h-.2v.4h-.2v.4l-.2.2v.7h-.2v.5l-1-.3v-.7l.2-.2v-.4l.2-.2v-.4h.2v-.4h.2v-.4h.2v-.2h.2v-.4zm0 0 .4-.5zm2.6-1 .2 1h-.6v.2h-1v.2h-.2l-.2.2-.8-.6.2-.2.2-.2h.2v-.2h.4l.2-.2h1.4zm0 0h.6zm3 0-.6.8H406v.2h-.5l-.2-1h.8v-.2h2.1zm-.6.8.3.2zm3.3 1.6-.6.6-.2-.2h-.2v-.2l-.2-.2-.2-.2-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2l-.2-.2h-.2v-.2h-.2l-.2-.2.5-1v.2h.2l.2.2h.2v.2h.2v.2h.2l.2.2.2.2.2.2h.2v.2h.2v.2l.2.2.2.2.2.2v.2zm2 2.5-.6 1-.2-.2-.2-.2-.2-.2v-.2h-.2v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.3-.2-.3.7-.6.3.2.2.3v.2h.2v.2l.2.2v.4h.2v.2l.2.2v.2h.2v.2l.2.2zm-.6 1-.2-.2h.2zm3.5-1 .2 1h-.5l-.2.2h-.8v.2h-1.3l-.2-.2h-.2l-.2-.2h-.2l.5-1v.2h.6l.2.2h1.1l.2-.2h.4l.2-.2zm2.8 0h-1l.5.5h-1l-.2.2h-.4l-.2.2h-.2l-.3-1 .2-.2h.8v-.2h1.5l.5.4zm-.5-.5h.6l-.2.5-.5-.5z"/>
<path fill="#edb92e" d="M391.8 221.2c.7-1 1-.8 1.2-1.3q.2-1.4-1-3.2c-2.2-4.6.3-4.2 3.6-2.4q1.5.7 3 2 1.4 1.5 2.7 2.4c1.6 1.2 2.7 1.2 3 1.7q1.4 3 1.8 4.2.6 1.2 1.6 4.2c0 .4-1 .5-1.8 2-.7 1.2-1.2 3.5-2 4.7q-2 2.6-3.4 2.4-1.5 0-1.8-4.2a4 4 0 0 0-1.5-3c-.5-.6-.6-.4-2-1.2a9 9 0 0 1-2.4-2q-.7-1-1-2c-.3-1-.8-1.6-.7-2.4a4 4 0 0 1 .8-2z"/>
<path fill="#21231e" d="m392.5 219.7 1 .3v.2l-.2.2v.2h-.2v.2h-.2v.2h-.2l-.2.2v.2h-.2l-.8-.5v-.2l.2-.2.2-.2.2-.2h.2v-.2h.2zm-.8-2.7 1-.5v.4l.2.2v.4l.2.3v.2l.2.2v2l-1-.3v-1.2l-.2-.2v-.4h-.2v-.4h-.2v-.4zm4.2-3-.6.8h-.3l-.2-.3h-.3l-.3-.3h-.3l-.3-.2h-.3l-.2-.2h-.5l-.2-.2h-.6v1.5l.3.4.2.3.2.4.2.5-1 .5v-.4l-.3-.4-.2-.4v-.7l-.2-.2v-1.5l.2-.2h.2l.2-.2h.2l.2-.2h1.2l.2.2h.6l.3.3.3.2h.3l.3.2.2.2.4.2h.3zm2.8 2-.7 1v-.4h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2l-.2-.2h-.2v-.2h-.2l-.2-.2-.2-.2h-.2l-.2-.2.5-1v.2h.2v.2h.2l.2.2h.2v.2h.2l.2.2h.2v.2h.2v.2h.2l.2.2.2.2.2.2h.2v.2zm2.8 2.4-.6.8h-.2v-.2h-.2v-.2h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2l-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2v-.2h-.2l.6-1v.2h.2l.2.2v.2h.2l.2.2.2.2h.2v.2h.2v.2h.2v.2h.2l.2.2.2.2h.2v.2h.2zm3.3 2-1 .3v.2-.2h-.2v-.2h-.4l-.2-.2h-.2l-.2-.2h-.3l-.2-.2h-.3v-.2h-.3v-.2h-.3v-.2h-.3v-.2l.6-.8.2.2h.2v.2h.3l.2.2h.2l.2.2h.2v.2h.4l.2.2h.4v.2h.2l.2.2h.2v.3zm-.2-.3v.2zm1.7 4.5-1 .4v-.7l-.2-.2v-.4l-.2-.2v-.5h-.2v-.4l-.2-.3v-.3h-.2v-.5h-.2v-.5h-.2v-.3l1-.4v.4l.2.3v.2l.2.3v.2l.2.3v.3l.2.3v.4h.2v.4l.2.2v.4h.2v.4zm0 0-1 .4h.3l.3.2-.5-.5.6.5-.5-.5.6.5-.5-.5.2.4-.2-.3v.2l1-.5v.2-.4.4l-.4-.5.6.5-.5-.5.6.5-.5-.5h.4v.3zm0-.2v.2zm1.6 4.7-1-.2v.3-.5l-.2-.3v-.2l-.2-.3v-.3l-.2-.3v-.2l-.2-.2v-.4l-.2-.2v-.2l-.2-.2v-.4l-.2-.2 1-.5v.4h.2v.5h.2v.5l.2.2v.3l.2.3v.5l.2.2v.3l.2.2v.2l.2.2v.6zm0-.3v.3zm-1.7 2.4-1-.4.2-.2v-.2h.2v-.2l.2-.2v-.2h.2v-.2h.2l.2-.2h.2v-.2h.2v-.2l1 .2v.4h-.2v.2h-.2v.2h-.2l-.2.2-.2.2-.2.2-.2.2-.2.2zm-2.2 5-.7-.7.2-.3v-.2h.2v-.3l.2-.2v-.4l.2-.2v-.5h.2v-.4h.2v-.5h.2v-.2l.2-.3v-.2l.2-.3v-.2l.2-.2 1 .5-.2.2v.3h-.2v.4h-.2v.5l-.2.2v.3l-.2.2v.3l-.2.2v.3l-.2.2v.3l-.2.2v.3h-.2v.3h-.2v.3l-.2.2v.2zm-3.7 2.7.2-1h.4v-.2h.2l.2-.2h.2v-.2h.2l.2-.2.2-.2.2-.2.2-.2.2-.3.2-.2v-.2h.2v-.3l1 .7h-.2v.3h-.3v.3h-.2v.3h-.4v.4h-.2v.2h-.2l-.2.2-.2.2-.2.2h-.3v.2h-.4v.2h-1zm-2.2-5h1v.3l.2.2v1.1h.2v.5l.2.2v.3l.2.2v.2h.2v.2h.2v.2h.2v1h-.8v-.2h-.2l-.2-.2-.2-.2v-.3h-.2v-.2l-.2-.2v-.3l-.2-.3v-.4h-.2v-1l-.2-.2v-1zm-1.2-2.6.7-.6.2.2v.2h.2v.2l.2.2v.2h.2v.3l.2.2v.2h.2v.7h.2v.6l-1 .2v-1h-.2v-.4l-.2-.2v-.2h-.2v-.2h-.2v-.4h-.2v-.2zm-2-1 .4-.8h.3v.2h.4v.2h.4v.2h.4v.2h.4v.2h.2v.2l-.6.7h-.2l-.2-.2h-.4v-.2h-.4l-.2-.2h-.2l-.2-.2h-.3zm-2.5-2 .7-.6.2.2.2.2.2.2.2.2h.2v.2h.2v.2h.2l.2.2h.2v.2h.2l.2.2-.5 1v-.2h-.2v-.2h-.4v-.2l-.2-.2h-.2l-.2-.2-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2v-.2zm-1.2-2.3 1-.4v.2h.2v.4h.2v.5h.2v.4h.2v.2l.2.2-.7.7h-.2v-.4h-.2v-.4h-.2v-.4l-.2-.2v-.8h-.2v-.2zm-.6-2.5h1v.8h.2v.4l.2.2v.2l.2.2v.2l-1 .5v-1h-.2v-.4h-.2v-.6h-.2v-.8zm1 0v.2zm.4-1.5v-1l.3.8v.4h-.2v.2h-.2v.8h-.2v.2h-1v-.9l.2-.2v-.4l.2-.2v-.2h.2v-.4h.2v-.2h.2l.4.7zm0-1h1l-.7.8-.2-.8zm0 0v1l-.4-.8.4-.3zm0 1h-1l.6-.8.4.7z"/>
<path fill="#21231e" d="m406.5 226 .2 1q-2.1.6-3.6 1.8c-1.5 1.2-1.7 2-2.3 3.2q.4-2.1 2-3.7a7 7 0 0 1 3.8-2.2zm-.6-1.6-.5-1q-2.4 0-4.7-1t-4.4-2.8q1.6 2 4.3 3.3 2.5 1.1 5.2 1.5z"/>
<path fill="#edb92e" d="M391.4 213.8c1 .4 1.6.8 2.8 1.8q1.2 1 1.5 3 0 1.7-.3 3.8-.3 1.4.5 3.6.5 2.3 2 3.5c2.3 2.5 4 5 3.4 6.7a4 4 0 0 1-1.5 1.8q-.7-.8-1.4-2.2a8 8 0 0 0-1.5-3.5c-.5-.6-.7-1.3-2-2.2-1-.5-2-.3-2.6-1-1.2-1.6-3-5.5-1.3-7.2s2.3-2.7 1-5.6q-.6-1.5-.6-2.4z"/>
<path fill="#21231e" d="m394.5 215.2-.7.8-.2-.2-.2-.2-.2-.2h-.2v-.2h-.2v-.2h-.4v-.2h-.2l-.2-.2h-.2l-.2-.2h-.4l.3-1h.4v.2h.4v.2h.4v.2h.4v.2h.2v.2h.2l.2.2.2.2h.2v.2h.2v.2zm1.7 3.2-1 .2v-.6h-.2v-.7h-.2v-.4l-.2-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2l.7-.8h.2v.2l.2.2v.2h.2v.2l.2.2v.2h.2v.4l.2.2v.4h.2v.8h.2v.2zm-.3 4h-1V221h.2v-2.4l1-.2v3.3l-.2.2zm-1 0v-.3zm1.4 3.5-1 .2v-.5l-.2-.3v-.4l-.2-.3v-.8h-.2v-1.4h1v1.9h.2v.7h.2v.7h.2zm1.8 3.2-.7.7v-.2l-.2-.2h-.2v-.4h-.2v-.2l-.2-.3v-.2h-.2v-.2l-.2-.3v-.2h-.2v-.5l-.2-.2-.2-.3v-.4h-.2l1-.4v.2l.2.2v.2l.2.3.2.2v.3l.2.2v.2h.2v.2h.2v.4h.2v.2h.2v.2h.2v.2zm3.6 7.2-1-.4.2-.2v-1.3l-.2-.2v-.4l-.2-.2v-.2l-.2-.2-.2-.2v-.2l-.2-.2-.2-.2v-.2l-.3-.2-.2-.3v-.3l-.3-.2-.2-.2-.2-.3-.2-.2-.2-.2-.2-.2-.3-.3h-.2l.7-.8.3.2.2.2.2.3.3.4.3.2.2.2v.3l.3.3.3.2.2.3v.2l.3.2.2.3v.2l.2.2.2.2v.2l.2.2v.2l.2.3v.7l.2.2v1.2l-.2.2zm-2.4 1.8 1-.4-.8-.2.2-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.2l.2-.2v-.2l1 .4v.2l-.2.2v.2l-.2.2v.2h-.2v.2h-.2v.2h-.2v.2h-.2v.2h-.2l-.2.2zm.8.2-.5.3-.3-.5zm-2-2.5 1-.3v.4h.2v.4h.2v.2h.2v.2l.2.2v.2h.2v.4l-.8.4v-.2h-.2v-.2l-.2-.2v-.2h-.2v-.2h-.2v-.4l-.2-.2v-.2zv-.3zm-1.5-3.3.8-.7v.2h.2v.2l.2.2v.2l.2.2v.2h.2v.5h.2v.4l.2.2v.4l.2.3v.7l-.8.2v-.4h-.2v-.5l-.2-.2v-.5h-.2v-.2l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2zm-2-2.2.4-1 .3.2.2.2h.2l.2.2.2.2.2.2v.2h.2v.2h.2v.2l.2.2.2.2v.2h.2v.2l-.7.7-.2-.2v-.2h-.2v-.2h-.2v-.4h-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.4v-.2zm-2.7-1.3.8-.6h.2v.2h.2v.2h.9l.2.2h.4l.2.2h.2l.2.2-.4 1v-.2h-.4l-.2-.2h-.9v-.2h-.4l-.2-.2h-.2l-.2-.2-.2-.2zm-1.2-7.8.7.7v.3h-.2v.3h-.2v2.2l.2.3v.5l.2.2v.2l.2.3v.2l.2.2v.2l.2.2v.2l.3.2v.2h.2v.3l.2.2-.7.7-.2-.3-.2-.3v-.2l-.2-.2v-.2h-.3v-.3l-.2-.3-.2-.4v-.2l-.2-.3v-.2l-.2-.2v-.6l-.2-.3v-2.1l.2-.3v-.2h.2l.2-.3.2-.3zm.7-5 1-.5v.5l.2.2v.3l.2.2v.7h.2v1.7l-.2.2v.2l-.2.2v.2h-.2v.3h-.2v.2l-.2.2-.3.3h-.2l-.2.3-.8-.6.2-.3h.2l.2-.3.2-.3.2-.2v-.3l.2-.2v-.2l.2-.3v-.7l-.2-.2v-.5l-.2-.2-.2-.4v-.2zm0-3v1l.4-.7v1.4h.2v1.1h.2l-1 .6v-.9h-.2v-.7l-.2-.2v-1l.4-.7zm-.6.5-.3-.7h.7l-.5.6zm.4.3v-1zm0-1"/>
<path fill="#edb92e" d="M434 226.2c-1.2 1-1.5 1-2 1.6q-.3 1.3.3 3.3c1.5 5-1.4 4.4-5 2.6-1.6-1-3.6-3.2-5.2-4.4-1.5-1-2.6-1-3-1.6l-1-4.3q-.1-1.2-1-4.4c.4-.7 1.4-1 2.5-2.5 1-1.7 2.2-4.7 3.6-6 3-3.2 6-4.5 6 1.3 0 1.4.3 3 1 3.6s1 .3 2.5 1q1.6 1 2.6 2c.7 1 .5 2 .7 2.8 0 1 .5 1.7.2 2.6q-.6 1.4-2 2.4z"/>
<path fill="#21231e" d="m432.6 228-1-.5h.2v-.4h.4v-.2h.4v-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l.8.8-.2.2h-.2v.2h-.2l-.2.2-.2.2h-.2v.2h-.4v.2zm0 2.9-.8.3v-.2l-.2-.2v-.7h-.2v-2.3h.2v-.2l1 .4v.2h-.2v2.1h.2v.7zm-5.4 3 .5-.8h.3l.3.3h.3l.3.3h.2l.3.2h.2l.3.2h.7l.2.2h.7v-.2h.2v-.4l.2-.3v-1.1l-.2-.3v-.3l-.2-.4 1-.4v.4l.2.4v.8l.2.3v1.1l-.2.4v.2l-.3.2-.2.2h-.2l-.2.2-.4.2h-1.2l-.3-.2h-.6l-.3-.3-.2-.2h-.3l-.3-.2-.4-.2-.3-.2zm-5.4-4.3.6-1 .2.2.3.2.2.3h.2v.3h.3v.3l.3.2.2.2h.2v.3h.3l.2.3.2.3.2.2.3.3h.2v.2l.2.2h.2l.2.2v.2h.3v.2h.2l.2.2.3.2-.5 1-.2-.2h-.2v-.2l-.3-.2h-.2l-.2-.2-.2-.3h-.2l-.2-.2h-.2v-.3h-.3l-.3-.3h-.2v-.3l-.3-.3h-.2l-.2-.3-.3-.3h-.2v-.2h-.3l-.2-.3-.3-.3-.3-.2-.3-.2h-.2zm-3.4-2 1-.2v-.2h.2v.2h.4l.2.2h.4v.2h.7l.2.2h.2l.2.2.3.2h.2l.2.2-.6 1v-.2h-.3v-.2h-.2l-.2-.2-.2-.2h-.3l-.2-.2h-.2v-.2h-.4l-.2-.2h-.2l-.2-.2h-.2v-.2h-.2v-.3zm.2.3v-.3zm-.6-4.2v-1l.5.4v.4l.2.2v1h.2v1.3h.2v1h.2v.5l-1 .2v-.7l-.2-.3v-.7l-.2-.3v-.7h-.2v-.8l-.2-.2v-.6l.4.4zm0-1h.5v.4zm-.5.6 1-.2v-.2l-.5-.2v1h-.3zm-.8-4.7 1 .4v-.2 1.1h.2v1.1l.2.3v.8l.2.2v.9l-1 .2V222l-.2-.2v-.8l-.2-.3v-.7h-.2v-.8l-.2-.2v-.3zm0 .3v-.3zm2.4-2.8 1 .5-.2.2-.2.2-.2.3-.2.2-.2.2h-.2v.2h-.2l-.2.2h-.2v.2h-.2v.2h-.2v.2h-.2l-1-.3h.2v-.4h.2v-.2h.4v-.2h.2l.2-.2.2-.2.2-.2h.2v-.2l.2-.2.2-.2v-.2h.2zm3.7-6.2.8.7h-.2v.3h-.2v.3l-.3.3v.2l-.2.2v.2h-.2v.3l-.2.3v.4l-.3.2v.2l-.2.2v.2l-.2.2v.2l-.2.2v.3l-.2.3v.2l-.2.2v.2h-.2v.2l-.2.2-.2.2-1-.4.2-.2.2-.3v-.2h.2v-.3l.2-.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.2v-.3l.2-.2v-.2h.2l.2-.3v-.2l.2-.2v-.3l.2-.2v-.2l.3-.2v-.2l.2-.2.2-.3v-.2l.2-.2h.2zm6.8 1.8h-1v-1.4l-.2-.4v-.6l-.2-.2v-.4l-.2-.2h-.3v-.2h-1.2l-.3.2-.3.2h-.2l-.3.3-.2.3-.3.2-.2.2-.3.2-.2.2-.3.2-.2.3-.8-.7.3-.3.3-.3.3-.2.2-.3.3-.3.4-.2.3-.2.2-.2h.2l.3-.3h.2l.3-.2h.3l.2-.2h1.2l.3.2.3.3.2.2.2.2.2.3v.6l.2.5v.4l.2.3v1.5zm1 3.3-.8.8-.2-.2v-.2h-.2v-.2l-.2-.2v-.6l-.2-.2v-.7l-.2-.2V212h1v2h.2v.3l.2.2v.2h.2v.2zm2.2 1-.4 1-.3-.2h-.2l-.3-.2h-.4v-.2h-.9v-.2h-.4v-.2h-.2v-.2l.6-.7v.2h.4v.2h.8l.2.2h.4l.2.2h.2l.2.2zm2.8 2.2-.8.6h-.2v-.2l-.2-.2h-.2v-.4h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.4v-.2h-.2l-.2-.2.3-1 .3.2h.2l.2.2.2.2h.2v.2h.2l.2.2h.2v.2h.2v.2h.2v.2h.2v.2h.2v.2h.2zm-.8.6.4.5zm1.5 2.3-1 .2V220l-.2-.2v-.8h-.2v-.4l.7-.6v.2l.2.2v.2l.2.2v2.2h.2v.2zm0 3-.8-.4v-2.1l-.2-.2v-.4h1v1l.2.2v1.8zm-1.8 2.6-.7-.8h.2v-.2l.2-.2v-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.2l.2-.2v-.2h.2v-.2l1 .4-.2.2v.2l-.2.2v.2h-.2v.4h-.2v.2h-.2v.2l-.2.2-.2.2v.2h-.2l-.2.2zm-.7-.8"/>
<path fill="#21231e" d="m418 222.6-.2-1a14 14 0 0 0 7.8-5.3c-1.2 3-4.3 5.3-7.7 6.3zm.2 1.5q0 .6.3 1 5 0 9.2 3c-2-2.3-5.8-3.8-9.5-4"/>
<path fill="#edb92e" d="M432.7 233.3a11 11 0 0 1-3.4-1.5c-1.6-1.3-1.2-3.8.3-6.4q.6-1.4.2-3.5a6 6 0 0 0-1.4-3.5c-2.4-2.3-3.8-4.8-2.6-6.6q1.4-1.8 2.8-2.7.4 1 .4 2.5c0 1.4.3 3 1 3.6s1 .3 2.5 1q1.6 1 2.6 2c.7 1 .6 2 .7 2.8 0 1 .5 1.7.2 2.6q-.6 1.4-2 2.4c-1.2 1-1.5 1-2 1.6q-.3 1.3.3 3.3.4 1.4.4 2.3z"/>
<path fill="#21231e" d="m429 232.2.6-.8h.2v.2h.2l.2.2h.2l.2.2h.2l.2.2h.4v.2h.4l.2.2h.4l.2.2h.2l-.3 1h-.4v-.2h-.4l-.2-.2h-.3v-.2h-.4l-.2-.2h-.2l-.2-.2h-.2v-.2h-.2l-.3-.2h-.2v-.2zm0-7 1 .5v.2h-.2l-.2.4v.2l-.2.2v.2l-.2.3v.4l-.2.2v.5l-.2.2v1.5l.2.3v.2h.2v.3l.2.2.2.2-.6.8-.2-.2-.3-.3v-.2h-.2v-.3l-.2-.2v-.4l-.2-.3V228h.2v-.7l.2-.3.2-.3v-.3l.2-.2v-.3l.3-.3v-.5zm.3-3.3 1-.2v.8h.2v1.9l-.2.2v.7h-.2v.2l-1-.4h.2v-.6h.2V222zm-1.3-3.2.7-.7v.2h.2v.4h.2v.2l.2.2v.2l.2.2v.2l.2.2v.5h.2v.7l.2.2v.8h-1v-1.2l-.2-.2v-.4h-.2v-.4h-.2v-.2l-.2-.2v-.2h-.2v-.2l-.2-.2zm-2.7-7.2 1 .6h-.2v.7l-.2.2v.7l.2.2v.4l.2.2v.2l.2.2v.2l.2.2v.2l.3.2v.2l.2.2.2.2v.2l.3.3.2.3v.2l.3.2.2.2h.2l-.7.8-.2-.2-.2-.2-.3-.2-.2-.2v-.3l-.2-.2-.2-.2-.2-.2-.2-.2v-.2l-.3-.2-.2-.3h-.2v-.3l-.2-.2v-.3l-.2-.2v-.2l-.2-.3v-.4l-.2-.2v-1l.2-.3v-.2l.2-.2v-.2zm3.8-2.5-.8.4.7.3h-.2l-.2.2h-.2l-.2.2h-.2v.2h-.2v.2h-.2l-.2.2-.2.2-.2.2-.2.2-.2.2v.2h-.2v.2l-.2.2-1-.6h.2v-.2l.2-.2.2-.2v-.2h.2v-.2h.2v-.2h.2v-.2h.2l.2-.2.2-.2h.2v-.2h.2l.2-.2.2-.2h.2v-.2zm-.6 0 .5-.5v.6h-.6zm1 2.8h-1v-2.1h-.2v-.4l1-.3v1h.2v2zm1 3.3-.7.8-.2-.2v-.2h-.2v-.2l-.2-.2v-.7l-.2-.2v-.7l-.2-.2V212h1v1.7h.2v.6l.2.2v.2h.2v.2l.2.2zm2.3 1-.4 1-.3-.2h-.2l-.3-.2h-.4v-.2h-.9v-.2h-.4v-.2h-.2v-.2l.6-.7v.2h.4v.2h.8l.2.2h.4l.2.2h.2l.2.2zm2.8 2.2-.8.6h-.2v-.2l-.2-.2h-.2v-.4h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.4v-.2h-.2l-.2-.2.3-1 .3.2h.2l.2.2.2.2h.2v.2h.2l.2.2h.2v.2h.2v.2h.2v.2h.2v.2h.2v.2h.2zm-.8.6.4.5zm1.5 2.3-1 .2V220h-.2v-.8h-.2v-.4l.7-.6v.2l.2.2v.2l.2.2v2h.2v.2zm0 3-.8-.4v-2.2l-.2-.2v-.4h1v1l.2.2v1.8zm-1.8 2.6-.7-.8h.2v-.2l.2-.2v-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.2h.2v-.4h.2v-.2l1 .4-.2.2v.2l-.2.2v.2l-.2.2v.2h-.2v.2h-.2v.2l-.2.2-.2.2v.2h-.2l-.2.2zm-1.7 1.4-1-.5v-.2h.2v-.2h.2v-.2h.4v-.2h.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l.8.8-.2.2h-.2v.2h-.2l-.2.2-.2.2h-.2l-.2.2h-.2zh-.2v.2zm0 2.9-1 .3v-.7h-.2v-3h.2l1 .4h-.2v2.6h.2v.2zm-.2 2.8.3-1-.6.5v-1h-.2v-.9l-.2-.2 1-.5v.9h.2v1h.2v.7zm.7-.5v.7l-.7-.2zm-.5 0-.2.5zm.5 0v.7l-.7-.2z"/>
<path fill="#edb92e" d="M410.6 216.4c4 0 8 3.4 8.4 7.7.6 4.4-2.2 7.7-6 7.8-4 0-7.5-3-8.3-7.3-.7-4 1.8-8 6-8z"/>
<path fill="#21231e" d="m419.5 224-1 .2v-.8l-.2-.4v-.3l-.2-.4v-.3l-.2-.4-.2-.3-.2-.3-.2-.4-.2-.3-.2-.3-.3-.2-.2-.3-.3-.3-.2-.2-.3-.3-.3-.2-.3-.2-.4-.2h-.3l-.3-.3h-.4l-.3-.3h-.3l-.4-.2h-.3l-.3-.2h-1.5v-1h1.9l.4.2h.3l.4.3h.4l.4.3.4.2.3.2.5.2.3.3.3.2.4.2.3.3.3.3.3.3.3.4.2.3.2.4.3.4.2.4.2.4.2.4v.4l.2.4v.4l.2.4v.8zm-6.6 8.3v-1h.9l.2-.2h.6l.3-.2h.4l.2-.2.3-.2h.2l.2-.3h.2l.2-.3.3-.3.3-.2v-.2l.3-.3.2-.2v-.3l.3-.3.2-.4v-.3l.2-.3v-.7l.2-.3V224l1-.2v2.5l-.2.4v.8l-.3.4v.3l-.2.3v.4l-.3.3-.2.3-.3.3h-.2l-.2.4-.3.2-.3.3h-.3l-.3.3-.3.2h-.2l-.3.3h-.4l-.2.2h-.4l-.3.2H413zm-8.7-7.7 1-.2v.7l.2.5v.3l.3.4v.3l.3.3.2.4.2.3.2.3.2.3.2.3.3.3.2.3h.3l.2.3.3.3.2.2.3.2.3.2h.4l.3.3.3.2h.3l.3.2h.7l.3.2h1.2v1h-2l-.3-.2h-.4l-.4-.3h-.4l-.4-.3-.3-.2-.4-.2-.3-.2-.4-.2-.4-.2-.3-.3-.2-.2-.2-.2-.3-.3-.2-.3-.3-.3-.2-.4-.2-.3-.2-.3-.2-.4-.2-.3-.2-.4v-.4l-.2-.4-.2-.4v-.8zm6.2-8.7v1h-1l-.3.2h-.4l-.3.2h-.3l-.3.3h-.2l-.2.3-.3.2-.2.2v.2l-.3.2-.2.2-.2.3-.2.3v.2l-.2.3v.4l-.3.3v.6l-.2.3v.7l-.2.3v.7l.2.4v.8l-1 .2v-3.3l.2-.3v-.3l.2-.4v-.3l.3-.4v-.3l.3-.3.2-.3.2-.3.2-.3h.2l.2-.4.3-.2.3-.2.3-.2.3-.2.3-.2.3-.2h.3l.4-.2h.4l.3-.2h.9z"/>
<path fill="#edb92e" d="M411 218.6c2.8-.2 5.7 2.4 6 5.5.5 3.3-1.5 5.8-4.4 5.8-2.8 0-5.5-2.3-6-5.3s1.3-5.8 4.3-6z"/>
<path fill="#21231e" d="m417.6 224-1 .2v-.6l-.2-.3v-.2l-.2-.3v-.4l-.2-.2v-.2l-.2-.3-.2-.3v-.2l-.3-.2-.2-.2-.2-.3-.2-.2-.2-.2h-.2l-.2-.3h-.2l-.3-.3h-.2l-.2-.2-.4-.2h-.5v-.2h-1.1v-1h1.1l.4.2h.3l.2.2h.2l.3.2h.3l.2.3h.3l.2.2.3.2h.2l.3.3.2.3.2.2.2.2v.2l.3.3h.2v.4l.3.3v.3l.3.2.2.3v.3l.2.3v.8l.2.3zm-5 6.3v-1h1.1l.2-.2h.4l.2-.2h.2v-.2l.3-.2h.3v-.2l.3-.2v-.2h.2l.2-.3v-.2l.2-.2.2-.3v-.7l.2-.2v-1.6h1v2.2l-.2.3v.2l-.2.3v.2l-.3.3-.2.2v.3l-.3.2-.2.2-.2.2-.3.2-.2.2-.2.2h-.2l-.2.2h-.2l-.3.2h-.2l-.2.2zm-6.5-5.8h1l.2.4.2.4v.2l.2.3.2.3v.3l.3.2.2.2.2.2v.2l.3.2.2.2.3.3.2.2.2.2h.2v.3h.3l.2.2h.7l.2.2h1.1v1H411l-.4-.2h-.2l-.3-.2h-.2l-.2-.3h-.3l-.2-.3-.4-.3-.2-.3-.2-.2-.2-.2-.2-.2-.2-.2-.2-.2-.2-.3v-.2l-.3-.2-.2-.2-.2-.3v-.3l-.2-.3v-.3l-.2-.2v-.7zm5-6.4v1h-.6l-.2.2h-.5l-.2.2h-.2l-.2.2-.2.2h-.2l-.2.3v.2l-.3.2-.2.3v.2h-.2v.3l-.2.2v.2l-.2.2v.7l-.2.2v1.7h-1V222h.2v-.4l.2-.3v-.2l.2-.2v-.2l.3-.3v-.2h.2l.2-.3.2-.2.2-.2.2-.2h.2l.2-.2h.3l.2-.3h.2l.2-.2h.6l.3-.2h.6z"/>
<path fill="#0c4076" d="M411.2 220.7c2 0 3.6 1.5 4 3.5.2 2-1 3.6-3 3.6a4 4 0 0 1-3.7-3.4c-.3-2 1-3.6 2.7-3.7"/>
<path fill="#21231e" d="m415.6 224-1 .3v-.4l-.2-.2v-.4h-.2v-.3l-.2-.2-.2-.3-.2-.2-.2-.2h-.2l-.2-.2-.2-.2h-.2l-.2-.2h-.6v-.2h-.6v-1h1l.2.2h.2l.3.2h.3v.2h.3l.2.3v.2h.3l.2.2.2.2.2.3.3.2v.4h.2v.3l.2.3v.4zm-3.3 4.3v-1h.7l.2-.2h.4v-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.5l.2-.2v-1.1l1-.3v2h-.2v.3l-.2.3-.2.3-.2.2h-.2v.2l-.2.2h-.2v.2h-.3l-.2.2h-.4v.2h-1zm-4.3-3.8 1-.2v.5l.2.3v.4h.2v.2l.2.2.2.2.2.3.2.2.2.2h.3v.2h.4l.3.2h.8v1h-1.3l-.2-.2h-.2l-.3-.2h-.2v-.2h-.2v-.2l-.3-.2-.3-.2v-.2h-.2v-.3h-.3v-.3l-.2-.2v-.2h-.2v-.5l-.2-.2v-.5zm2.7-3.8h1l-.5.5h-.7l-.2.2h-.2v.2h-.2l-.2.2-.2.2-.2.2v.2h-.2v.5h-.2v1.3l-1 .3v-1.8h.2v-.5h.2v-.3h.2v-.3h.3v-.3h.3v-.2h.3v-.2h.3v-.2zl.2-.2h.4l-.5.5zm1 0v.5h-.5zm0 0h-1l.5-.5zm-1 0v-.5h.5z"/>
</g>
<path fill="#fff" d="M482 183c4 0 7 3 7 7s-3 6.7-7 6.7c-3.6 0-6.7-3-6.7-6.8 0-4 3-7 6.8-7z"/>
<path fill="#21231e" d="M489.5 189.8h-1c0-2.3-1.4-4.6-3.5-5.6a6 6 0 0 0-3-.7v-1q4.1.1 6.4 3.5a7 7 0 0 1 1 3.8zm-7.4 7.4v-1q4-.2 5.8-3.4a6 6 0 0 0 .7-3h1c0 2.6-1.5 5.2-3.7 6.5a7 7 0 0 1-3.7 1zM475 190h1a6.4 6.4 0 0 0 6.3 6.2v1a7 7 0 0 1-6-3.4 7 7 0 0 1-1.2-4zm7.3-7.5v1a6 6 0 0 0-5.5 3.5 6 6 0 0 0-.7 3h-1q.2-4.2 3.5-6.4a7 7 0 0 1 3.8-1z"/>
<path fill="#fff" d="M482 171c4 0 7 3 7 7a6.8 6.8 0 1 1-13.7-.2c0-3.7 3-6.7 6.8-6.7z"/>
<path fill="#21231e" d="M489.5 178h-1c0-2.4-1.4-4.7-3.6-5.8a6 6 0 0 0-3-.6v-1q4.1.1 6.4 3.4a7 7 0 0 1 1 4zm-7.4 7.3v-1c2.5 0 4.7-1.4 5.8-3.4a6 6 0 0 0 .7-3h1c0 2.5-1.5 5-3.7 6.4a7 7 0 0 1-3.7 1zM475 178h1a6.4 6.4 0 0 0 6.3 6.3v1a7 7 0 0 1-6-3.4 7 7 0 0 1-1.2-4zm7.3-7.4v1a6 6 0 0 0-5.5 3.5 6 6 0 0 0-.7 3h-1c0-2.6 1.4-5 3.5-6.3a7 7 0 0 1 3.8-1z"/>
<path fill="#fff" d="M482 159.6c4 0 7 3 7 7a6.8 6.8 0 1 1-13.7-.2c0-3.7 3-6.8 6.8-6.8z"/>
<path fill="#21231e" d="M489.5 166.5h-1c0-2.4-1.4-4.7-3.6-5.7a6 6 0 0 0-3-.7v-1c2.6 0 5 1.5 6.4 3.6a7 7 0 0 1 1 4zM482 174v-1c2.5 0 4.7-1.5 5.8-3.6q.6-1.5.7-3h1c0 2.6-1.4 5-3.5 6.4a7 7 0 0 1-4 1zm-7.2-7.5h1c0 2.4 1.4 4.7 3.6 5.7a6 6 0 0 0 2.7.7v1a7 7 0 0 1-6-3.6 7 7 0 0 1-1.2-4zm7.3-7.4v1a6 6 0 0 0-5.5 3.6 6 6 0 0 0-.7 3h-1c0-2.6 1.4-5 3.5-6.4a7 7 0 0 1 3.8-1z"/>
<path fill="#fff" d="M482 127c4 0 7 3.2 7 7s-3 6.8-7 6.8a7 7 0 0 1 .1-13.8z"/>
<path fill="#21231e" d="M489.5 134h-1c0-2.4-1.4-4.7-3.6-5.8a6 6 0 0 0-3-.6v-1q4.1.1 6.4 3.4a7 7 0 0 1 1 4zm-7.4 7.3v-1q4-.2 5.8-3.4a6 6 0 0 0 .7-3h1c0 2.5-1.5 5-3.7 6.3a7 7 0 0 1-3.7 1zM475 134h1c0 2.3 1.4 4.6 3.5 5.6a6 6 0 0 0 2.8.7v1a7 7 0 0 1-6-3.4 7 7 0 0 1-1.2-4zm7.3-7.4v1a6 6 0 0 0-5.5 3.5 6 6 0 0 0-.7 3h-1c0-2.6 1.4-5 3.5-6.3a7 7 0 0 1 3.8-1z"/>
<path fill="#fff" d="M482 137.2c4 0 7 3 7 6.8s-3 7-7 7c-3.6 0-6.7-3.2-6.7-7s3-6.8 6.8-6.8z"/>
<path fill="#21231e" d="M489.5 144h-1c0-2.3-1.4-4.6-3.5-5.7a6 6 0 0 0-3-.6v-1c2.7 0 5.2 1.4 6.5 3.6a7 7 0 0 1 1 3.7m-7.4 7.4v-1c2.5 0 4.7-1.4 5.8-3.4a6 6 0 0 0 .7-3h1c0 2.6-1.5 5-3.7 6.4a7 7 0 0 1-3.7 1zM475 144h1c0 2.4 1.4 4.7 3.5 5.7a6 6 0 0 0 2.8.7v1a7 7 0 0 1-6-3.5 7 7 0 0 1-1.2-4zm7.3-7.4v1a6 6 0 0 0-5.5 3.6q-.7 1.6-.7 2.8h-1c0-2.5 1.4-5 3.5-6.2a7 7 0 0 1 3.8-1.2z"/>
<path fill="#fff" d="M482 148.2c4 0 7 3 7 6.8s-3 7-7 7c-3.6 0-6.7-3.2-6.7-7s3-6.8 6.8-6.8z"/>
<path fill="#21231e" d="M489.5 155h-1c0-2.3-1.4-4.6-3.6-5.7a6 6 0 0 0-3-.6v-1q4.1.1 6.4 3.4a7 7 0 0 1 1 4zm-7.4 7.4v-1c2.5 0 4.7-1.4 5.8-3.4a6 6 0 0 0 .7-3h1c0 2.6-1.5 5-3.7 6.4a7 7 0 0 1-3.7 1zM475 155h1c0 2.4 1.4 4.7 3.5 5.7a6 6 0 0 0 2.8.7v1a7 7 0 0 1-6-3.4 7 7 0 0 1-1.2-4zm7.3-7.4v1a7 7 0 0 0-5.5 3.5 6 6 0 0 0-.7 3h-1c0-2.5 1.4-5 3.5-6.3a7 7 0 0 1 3.8-1z"/>
<path fill="#edb92e" d="M487 195.5c1 1.6.7 2 1.4 2.4q1.3.8 3.4.4c5.3-1 4.5 2.3 2.2 6-1 1.7-3.8 3.5-5.2 5s-1.6 2.8-2 3.2h-9.2c-.5-.4-.7-1.7-2-3.2-1.5-1.5-4.2-3.3-5.3-5-2.4-3.7-3-7 2.2-6q2 .4 3.4-.5c.6-.6.3-1 1.3-2.5a7 7 0 0 1 2.2-2.3c1-.5 1.8-.2 2.6-.2 1 0 2-.3 2.8.2a7 7 0 0 1 2.2 2.3z"/>
<path fill="#21231e" d="m488.7 197.5-.7.8-.2-.2h-.2v-.4h-.2v-.4h-.2v-.4l-.2-.2v-.2l-.2-.2-.2-.3v-.2l.8-.6v.2h.2v.3l.2.2v.2h.2v.4h.2v.4h.2v.4h.2zm-.7.8-.3-.3zm3.8-.4v1h-2.4l-.2-.2h-.2l-.2-.2h-.3l-.2-.2.7-1v.2h.2v.2h.8l.2.2h1.9zm2.6 6.6-.8-.6.2-.3.2-.3.2-.4.2-.3v-.3l.3-.3v-.2l.2-.2v-.6l.2-.2v-1.3l-.2-.2h-.2v-.2h-.5l-.2-.2h-2l-.2-1h2.8l.3.2h.2l.2.2.2.2.2.2.2.2v.3l.2.3v1.7l-.2.3v.3l-.3.2v.4l-.3.3v.2l-.3.4v.3l-.3.2-.3.4zm-5.3 5.2-.6-.7.3-.2v-.2h.3l.2-.3v-.2h.3l.2-.2h.2v-.3h.2l.2-.3.2-.2h.2l.2-.3h.2v-.3l.3-.2h.2l.2-.3v-.2l.2-.2.2-.2h.2v-.3h.3v-.4h.3v-.3h.2v-.3l1 .6-.2.2-.2.3v.3l-.2.2h-.2l-.2.3v.2l-.3.2-.3.3v.2h-.3l-.2.3-.2.2h-.2l-.2.3-.2.2-.3.3-.3.3h-.2v.3l-.3.2h-.2v.3l-.3.2h-.2v.3h-.2l-.2.3zm-2.3 3.3v-1l-.3.2v-.2h.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.2v-.2h.2v-.2l.2-.2.2-.3.3-.3.8.6v.2l-.2.2-.2.2-.2.3v.2h-.2v.2l-.2.2v.2l-.2.2v.2h-.2v.4h-.2v.2l-.2.2v.2h-.5zm.3 0h-.2zm-5 .2v-1h4.7v1h-4.5zm0-1h.2zm0 0v1zm-4.7.8.6-.8h-.4 4.2l.2 1h-4.7l-.3-.2zm.3 0h-.2zm-2.4-3.2.7-.7v.2l.2.2.2.3.2.3.2.2v.2h.2v.4h.2v.4h.2v.4h.2v.4h.2v.2l-.5.8-.2-.2v-.2l-.2-.2v-.2h-.2v-.4h-.2v-.4l-.2-.2v-.2h-.2v-.2l-.2-.3-.2-.2-.2-.3-.2-.2zm-5.4-5.2 1-.6v.2l.2.3h.2v.3l.2.2h.2v.3h.3v.3l.2.2h.2l.2.3.2.2v.2l.3.2h.3l.2.3h.2l.2.2v.2h.2l.2.3h.2l.2.3h.2v.3l.3.2h.2v.3h.3v.3l-.6.8-.2-.2-.3-.3-.3-.2-.3-.2v-.2l-.2-.2h-.2l-.2-.3h-.2l-.2-.2h-.2v-.3l-.3-.2h-.2l-.2-.3v-.2h-.3l-.2-.3v-.2h-.3l-.2-.3v-.2l-.3-.2h-.2v-.3l-.3-.2-.2-.2-.2-.2zm2.7-6.7v1l-.6-.2h-2.1l-.2.2h-.2v.2h-.2v.4l-.2.2v1.1l.2.2v.3l.2.3v.2l.2.3.2.4v.3l.3.3.3.4.2.3.2.3-1 .6v-.4l-.3-.3-.2-.4-.2-.4v-.3l-.3-.4v-.4l-.3-.3v-.4l-.2-.3v-.6l-.2-.3v-1l.2-.2v-.2l.3-.2.2-.2.2-.2.4-.2h.3l.3-.2zm3-.5.8.8-.2.2h-.2l-.2.2h-.4l-.2.2h-3v-1h2.5l.2-.2h.4v-.2zm1.4-2.3.7.6v.3l-.2.2v.2h-.2v.4h-.2v.8h-.2v.2h-.2v.2l-.2.2-.8-.7h.2v-.4h.2v-.4h.2v-.2l.2-.2v-.4h.2v-.3h.2v-.3zm0 0 .2-.4zm2.3-2.4.5.8-.2.2h-.2v.2h-.2v.2h-.2l-.2.2-.2.2-.2.2v.2h-.2v.2l-.2.2-.2.2v.2l-1-.6.2-.2.2-.2v-.2h.2v-.2l.2-.2h.2v-.2l.2-.2.2-.2h.2v-.2l.2-.2h.2v-.2h.2zm2.8-.3v1h-2.4l-.5-.7h.2v-.2h.4l.2-.2zm3 .3-.5.8h-.4v-.2H482v-1h2.5l.2.2h.2zm0 0 .3.2zm2 3.2v-1l-.4.8-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.2v-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.4v-.2h-.2v-.2l.4-.8h.2l.2.2.2.2h.2v.2h.2v.2h.2v.2h.2v.2h.2v.2l.2.2.2.2v.2h.2v.2h.2v.2zm.4-.8.5.8h-1zm-.4-.2v1l-.4-.2zm-.4.8-.5-.8h1l-.4.8z"/>
<path fill="#21231e" d="m481.4 212.8-1-.2q-.7-5.6-4.3-9.6c2.8 1.8 4.8 5.7 5.4 9.8z"/>
<path fill="#edb92e" d="M494.2 198.3q-.6 1.7-2 3.5c-1.5 1.6-4 .5-6.5-1.6q-1.4-1-3.6-1a6 6 0 0 0-3.4 1c-2.5 2-5 3.2-6.6 1.6q-1.4-1.7-2-3.5h2.5q2 .6 3.4-.4c.6-.6.3-1 1.3-2.5a7 7 0 0 1 2.2-2.3c1-.5 1.7-.2 2.6-.2s2-.3 2.8.2q1.2.8 2.2 2.3c1 1.6.7 2 1.3 2.4q1.5.8 3.5.4 1.5-.3 2.4 0z"/>
<path fill="#21231e" d="m492.6 202-.7-.6v-.2l.2-.2.2-.2v-.2h.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.2v-.2l.2-.3v-.4l1 .5v.4l-.2.2v.2l-.2.2v.4h-.2v.2l-.2.2v.3h-.2v.2h-.2v.2h-.2v.3l-.2.2-.2.2h-.2zm-7.3-1.4.7-.7h.2l.2.2.3.2.2.2.3.3h.3l.2.2h.2l.2.2h.2l.2.2h.2l.2.2h.4l.2.2h1.6l.2-.2.2-.2h.2v-.2l.8.7-.2.3h-.2v.3h-.3l-.3.2h-.3l-.2.2h-1.9l-.3-.2h-.3l-.2-.2h-.3l-.2-.2h-.3l-.2-.2-.2-.2h-.3l-.2-.3h-.3l-.2-.3-.2-.2-.2-.2-.2-.2-.3-.2zm.7-.7.5.3zm-4-.2v-1h2v.2h.7v.2h.4l.2.2h.2l.2.2h.2v.2h.2l-.7.8h-.2v-.2h-.2v-.2h-1v-.2h-1v-.2zm-3 .8-.7-.7v-.2h.2l.2-.2h.2v-.2h.2l.2-.2h.5v-.2h1.2v-.2h1v1h-1.5v.2h-.5l-.2.2h-.4v.2h-.2zm-7.3 1.5.7-.6.3.2.2.2h.2v.2h1.7v-.2h.5l.2-.2h.2v-.2h.3l.2-.2.2-.2h.3l.2-.3h.2l.2-.3.3-.2v-.2h.3l.7.6-.3.2-.2.2-.3.2-.2.2-.2.2h-.3l-.2.3h-.3l-.2.2h-.2l-.3.2h-.2l-.2.2h-.2l-.2.2h-.3l-.3.2H473l-.4-.2h-.2l-.2-.2-.2-.2zm-1.7-4 .2 1 .3-.7v.2l.2.2v.3l.2.2v.4l.2.2v.2h.2v.4h.2v.4h.2v.2h.2v.2h.2v.2l.2.2-.7.7h-.2v-.2h-.2v-.2h-.2v-.5h-.2v-.2h-.2v-.3l-.2-.2v-.2h-.2v-.4h-.2v-.4l-.2-.2v-.4h-.2l.4-.8zm-.4.7-.2-.6.6-.2zm3-.6-.2 1-.2-.2H470l-.2-1h2.4zm3-.5.7.8-.2.2h-.2l-.2.2h-.4l-.2.2h-2.8v-1h2.3l.2-.2h.4v-.2h.2zm.7.8-.5.4zm.6-3 .7.5v.3l-.2.2v.2h-.2v.4h-.2v.8h-.2v.2h-.2v.2l-.2.2-.8-.7h.2v-.4h.2v-.4h.2v-.2l.2-.2v-.4h.2v-.3h.2zm2.3-2.5.5.8-.2.2h-.2v.2h-.2v.2h-.2l-.2.2-.2.2-.2.2v.2h-.2v.2l-.2.2-.2.2v.2l-1-.6.2-.2.2-.2v-.2h.2v-.2l.2-.2h.2v-.2l.2-.2.2-.2h.2v-.2l.2-.2h.2v-.2h.2zm2.8-.3v1h-2.3l-.5-.7h.2v-.2h.4l.2-.2zm3 .3-.5.8h-.4v-.2H482v-1h2.5l.2.2h.2zm0 0 .3.2zm2.4 2.4-.8.6-.2-.2v-.2h-.2v-.2l-.2-.2-.2-.2-.2-.2v-.2h-.2v-.2h-.2v-.2h-.4v-.2h-.2v-.2l.4-.8h.2l.2.2.2.2h.2v.2h.2v.2h.2v.2h.2v.2h.2v.2l.2.2.2.2v.2h.2v.2h.2zm1.3 2.3-.7.8-.2-.2h-.2v-.4h-.2v-.4h-.2v-.4l-.2-.2v-.2l-.2-.2-.2-.3v-.2l.8-.6v.2h.2v.3l.2.2v.2h.2v.4h.2v.4h.2v.4h.2zm-.7.8-.3-.3zm3.8-.4v1h-2.4l-.2-.2h-.2l-.2-.2h-.4v-.2h-.2l.7-1v.2h.2v.2h.8l.2.2h1.9zm3 .5-1-.4.3.8H492l-.2-1h2.4l.5.7zm-.5-.7h.6l-.3.7zm0 .5.4.2zm0-.5h.6l-.3.7z"/>
<path fill="#edb92e" d="M482 211.7c4.4 0 8 3.4 8 7.7s-3.6 7.8-8 7.8-7.6-3.5-7.6-7.8 3.5-7.8 7.7-7.8z"/>
<path fill="#21231e" d="m483 212.8.8-.2q.8-5.6 4.3-9.6a15 15 0 0 0-5 9.8z"/>
<use xlink:href="#rs-b" width="1350" height="900" transform="matrix(-1 0 0 1 964.3 0)"/>
<path fill="#edb92e" stroke="#21231e" d="M577 293.6c2 .3 3 1.5 3 2.8s-1 2.6-3 3l-1.6.2c-60.8 5.2-125.7 5.2-186.5 0l-1.6-.3c-2-.3-3-1.6-3-3 0-1.2 1-2.4 3-2.7l1.5-.3c60.7-5.2 125.6-5.2 186.4 0q.8 0 1.5.3z"/>
<path fill="#21231e" d="M412 298.4h-.2v-.2h-.2v-1h.4v-.2.2-.2h2.3l.7-.2h3l.7-.2h3l.7-.2h3l.8-.2h3.3l.7-.2h3.8l.7-.2h.8v1.2h-2.9l-.7.2H428l-.7.2h-2.7l-.8.2H421l.7.2h3.7l.7.2h2.9l.7.2h3.7l.7.2h1.5v1.2h-3.7l-.7-.2h-3.8l-.7-.2h-2.9l-.7-.2H420l-.6-.2h-2.9l-.7-.2H413zv-.2zm46.8-3.3v1.3H453l-.7.2h-6.6l-.7.2h-5l-1 .2h-3.5v-1.2h3.5l.8-.2h5.2l.7-.2h5.9l.7-.2h6.8zm23.3 0v1h-13.7l-.7.2h-8.8v-1h1.4l.8-.2H482zm23.5 0v1.3h-11.1l-.7-.2H482v-1zm23.3 1v1h-5.2l-.8-.2h-5.2l-.7-.2h-6.5l-.8-.2h-4.3V295h.7l.7.2h7.5l.8.2h5l.8.2h5.1l.6.2h2.3zm23.4 1v.2h.4v1h-.2l-.2.2h-3l-.7.2h-2.2l-.7.2h-3.8l-.8.2h-2.8l-.7.2H534l-.8.2h-3.6l-.8.2v-1.2h2.2l.7-.2h3.7l.8-.2h3.7l.7-.2h1.8-1.4l-.8-.2h-2.7l-.7-.2H533l-.6-.2h-3.6V296h6.5l.7.2h3l.8.2h2.8l.7.2h3l.7.2h3l.7.2h1.5zm-116.7 1.4h.2l.2.2v.8h-.2v.2h-.2zm93.3 1.2h-.2l-.2-.2v-.2h-.2v-.8h.2v-.2h.4v1.2zm-50.3-36.2v-1h3.8v1h-4zm-1.2-1h1.2v1h-1.2zm-.6 0h.6v1h-.6zm-1 0h1v1h-1zm-.8 0h.6v1h-.7v-1zm-.5 0h.4v1h-.5v-1zm-.8 0h.8v1h-.8zm-1.2 0h1.2v1h-1.2zm-1 0h1v1h-1zm-.2 0h.2v1h-.2zm-1 0h1v1h-1zm-2.5 1v-1h2.6v1zm-.3-1h.2v1h-.2zm-.2 0h.2v1h-.2zm-1 0h1v1h-1zm-.5 0h.4v1h-.4zm-.4 0h.4v1h-.4zm-1.2 0h1.2v1h-1.2zm0 0v1zm0 1h-.2zm-1.2-1h1.2v1l-1.2.2v-1zm-.4 0h.4v1.2h-.4v-1zm-.8.2h.8v1h-.8zm0 0h.3-.2zm-.3 0h.3v1h-.3zm0 1h-.5zm-1-1h1v1h-1zm-1 0h1v1h-1zm-.2 0v1zm-1 0h1v1h-1zm-.6 0h.5v1h-.4v-1zm0 0v1zm-4.2 1.2v-1h3.5v-.2h.4v1h-2.7l-.2.2h-.8zm-.6-1h.5v1h-.6v-1zm-1.7 0h1.6v1h-1.7zm-.2 0h.3v1h-.2v-1zm-1.3 0h1.4v1h-1.3zm-1 0h1v1h-1zm-.6 0h.7v1h-.6zm0 0v1.2-1zm-1.6.2h1.5v1h-1.5zm0 0v1zm-1.2 0h1.2v1h-1.2zm0 0v1zm-1.2 0h1l.2 1H444v-1zm-.8 0h.8v1h-.7zm-1.6.2h1.6v1h-1.5zm0 0v1zm-6.2 1.3v-1h1.7l.2-.2h3.9v1H436l-.2.2zm-.4-1h.3v1h-.2zm-9 1.4v-1h1.3l.3-.2h3.2l.3-.2h3.5v1h-5.1l-.3.2H426zm-.3-1h.2v1h-.3v-1zm-.5 0h.5v1h-.4zm-18.2 2.2v-1h1.8l.5-.2h1.7l.5-.2h2.4l.6-.2h2.6l.6-.2h2.2l.6-.2h2.8l.6-.2h1.2v1h-2.1l-.6.2h-2.7l-.6.2h-2.3l-.6.2h-2.8l-.6.2h-2.2l-.5.2H408l-.6.2h-.6zm-.3-1h.3v1h-.2zm0 0v1zm-26.3 3.4v-1h.7l1-.2h.8l.8-.2h1.6l.8-.2h1l.7-.2h1.6l.8-.2h1.8l.8-.2h.8l1-.2h1.7l.7-.2h1.7l.8-.2h1.8l.7-.2h1.6l1-.2h1.5l1-.2v1H405l-.7.2h-1.7l-.8.2H400l-.7.2h-.8l-1 .2H396l-.8.2h-1.7l-.8.2H391l-.8.2h-.8l-.8.2H387l-1 .2h-.7l-.8.2H383l-1 .2h-.8l-.8.2zm0-7v1h-.5l-.5.2h-.5l-.3.2-.4.2-.3.2h-.2l-.3.3-.2.2-.3.3v.5l-.2.2v1.1h.2v.3l.2.2.2.2.3.3.3.2h.2l.3.2h.3l.4.2h1v1h-1.6l-.4-.2-.4-.2-.3-.2-.3-.2-.3-.2-.4-.2-.2-.3-.2-.2v-.3l-.3-.3-.2-.3v-1.6l.2-.3.2-.3.2-.4.2-.3.2-.3.4-.3.3-.2.4-.2.3-.2h.4l.5-.2h.6l.5-.2zm26.3-2.3v1H405l-.7.2h-1.7l-.8.2H400l-.7.2H396l-.8.2h-1l-.7.2H392l-1 .2h-1.6l-.8.2h-.8l-.8.2h-1.7l-.8.2h-.8l-.8.2H381l-.8.2v-1h1.7l.8-.2h.8l.8-.2h.8l.8-.2h1.7l.8-.2h1.6l1-.2h.8l.8-.2h1.8l.7-.2h1.7l1-.2h.7l.8-.2h1.8l.7-.2h1.6l1-.2h2.5zm0 1v-1zm37-3.2v1.2H439l-1 .2h-2.3l-1.3.2H431l-1.2.2h-2.2l-1.3.2H424l-1.2.2h-2.3l-1 .2H417l-1.2.2h-1.2l-1 .2h-2.3l-1.3.2h-1l-1.2.2h-1.2v-1h2.2l1.2-.2h1l1.2-.2h2.2l1.3-.2h1l1.2-.2h2.3l1-.2h2.3l1.3-.2h2.4l1.2-.2h2.3l1-.2h3.5l1.2-.2h3.5l1-.2h2.5zm1 1-1 .2v-1h1v1zm.2 0h-.3v-1l.2 1zm.2 0h-.3l-.2-1h.4v1zm1.2 0h-1.2v-1h1.2zm0 0v-1zm15.4-1.4v1h-5.5l-.5.2h-6.5l-.4.2h-2.6v-1h5l.5-.2h7.7l.5-.2zm.8 1h-.8v-1h.8zm0 0v-1zm1.4 0h-1.4v-1h1.4zm.2 0h-.2v-1h.2zm0 0h-.2zm0 0v-1zm1.2 0h-1.2v-1h1.2zm.5 0h-.6v-1h.4v1zm.6 0h-.7l-.2-1h.8zm1 0h-1v-1h1zm.2 0h-.3v-1h.2v1zm.4 0h-.4v-1h.4zm3-1 .2 1H468v-1h3.3zm1.2 1h-1v-1l1-.2v1zm0 0v-1.2 1zm1.3-.2h-1.2v-1h1.2zm3.5-1v1h-3.5v-1zm0 1v-1zm1.3 0h-1.2v-1h1.2zm3.6-1v1h-3.5v-1h3.7zm3.8 0v1H482v-1zm1 1h-1v-1h1.2v1zm.2 0v-1zm3.6-1v1H487v-1h4zm1 1h-1v-1h1.2v1zm.2 0v-1zm1 0h-1v-1h1zm3.3-1v1H493v-1h3zm.5 1h-.4v-1h.5zm.3 0h-.3v-1h.3zm1 0h-1v-1h1zm.6 0h-.7v-1h.8zm.5.2h-.6v-1h.5v1zm1 0h-1v-1h1zm0 0v-1zm0 0h.3zm.3 0h-.2v-1h.3zm1.3 0h-1.3v-1h1.4zm0 0v-1zm0 0h.2zm1 0h-1v-1h1zm15.2-.6v1h-6l-.4-.2H504l-.6-.2h-.9v-1h6l.4.2h6.6l.4.2h1.7zm0 1v-1zm1.3 0h-1v-1h1zm.4 0h-.3v-1h.5v1zm.2 0h-.2v-1h.2zm1 .2h-1v-1h1zm37 1v1h-2.3l-1.2-.2h-1l-1.2-.2h-2.2l-1.3-.2H546l-1-.2h-1.3l-1-.2h-2.3l-1.3-.2H537l-1-.2h-4l-1-.2h-2.6l-1-.2H524l-1-.2h-2.4v-1h4.7l1.2.2h3.3l1.3.2h2.3l1.3.2h2.3l1 .2h2.4l1 .2h2.3l1.3.2h2.3l1 .2h2.3l1.2.2h1.2l1 .2h1.3l1 .2h1.3zm0 1v-1zm26.4 1.4v1H582l-.8-.2h-.8l-.8-.2H578l-.7-.2h-.8l-.8-.2H574l-.8-.2h-.8l-.8-.2h-1.8l-.7-.2h-1.5l-.8-.2H565l-.8-.2h-1.7l-.8-.2H560l-.8-.2h-1.6v-1h3.5l.8.2h1.6l1 .2h1.7l.7.2h1.7l.8.2h1.8l.7.2h.8l.8.2h1.8l.8.2h.8l.8.2h1.8l.7.2h.7l.8.2h1.8zm0 7v-1h1.6l.3-.2h.3l.3-.2.2-.2.2-.2h.2v-.3l.2-.2v-.2h.2v-1.4l-.2-.3v-.2h-.3l-.2-.3-.3-.2h-.2l-.3-.3-.4-.2h-.4l-.4-.2h-.4l-.5-.2v-1h.5l.6.2h.5l.5.3.4.3.4.2.3.2.4.3.3.3.2.3v.3l.3.2v.3l.2.3v1.3l-.3.3v.3l-.3.3-.3.4-.3.2-.3.2-.2.2-.3.2-.4.2h-.3l-.4.2H584zm-26.5-2.4v-1h1.8l.8.2h1.8l.8.2h1.8l.7.2h1.7l1 .2h.7l.8.2h1.8l.7.2h1.6l.8.2h1l.8.2h1.6l.8.2h1l.7.2h1.5l.8.2h1l.7.2v1h-1l-.8-.2h-1.6l-.8-.2h-.8l-.8-.2h-1.7l-.8-.2h-.8l-1-.2h-1.6l-.8-.2h-.8l-1-.2h-1.4l-.8-.2H566l-.7-.2h-1.6l-1-.2H561l-1-.2h-1.6zm0-1v1zm-.2 0h.3v1h-.4v-1zM539 265v-1h2.9l.5.2h3l.5.2h2.3l.6.2h2.2l.6.2h2.3l.5.2h2.4l.5.2v1h-1.7l-.5-.2h-2.3l-.6-.2H550l-.7-.2H547l-.6-.2h-2.9l-.6-.2H540l-.6-.2h-.6zm-.4-1h.5v1h-.4v-1zm-.2 0h.2v1h-.2zm-9.2.5v-1h4l.3.2h3.2l.3.2h1.3v1h-1.6l-.3-.2H533l-.4-.2h-3.3zm-.3-1h.2v1h-.3zm-6Line truncated
<path fill="#0c4076" d="M482 269c5.8 0 10.4 3.2 10.4 7s-4.6 7-10.3 7-10-3.2-10-7 4.5-7 10-7z"/>
<path fill="#21231e" d="M493 276h-1v-.3l-.2-.3v-.5l-.2-.3v-.3l-.3-.3-.2-.4v-.3l-.3-.2-.2-.2-.2-.3-.3-.2-.3-.3-.3-.2-.3-.2-.2-.2-.4-.2-.4-.2-.3-.2-.4-.2h-.5l-.4-.3h-.4l-.5-.2h-.8l-.5-.2h-1.9v-1.2h1.7l.6.2h.5l.5.2h.5l.5.2h.4l.5.3.4.2.5.2h.5l.4.3.3.2.4.2.3.3.4.3.3.2.3.3.3.3.2.4.3.3.2.4.2.4.2.4v.3l.2.5v.4l.2.2v.8zm-11 7.4v-1h1.6l.5-.2h1.1l.5-.2h.5l.4-.3h.4l.5-.3h.4l.3-.3h.4l.4-.3.3-.2.4-.3.3-.2.3-.2.3-.3.2-.3.2-.2.3-.3v-.3l.3-.3h.2v-.4l.2-.3v-.7l.2-.3v-.3h1v.4l-.2.4v.4l-.2.4v.3l-.2.3-.2.3-.2.4-.2.3-.3.3-.2.3-.3.2-.3.3-.3.3-.4.3-.3.3-.4.2-.4.3-.5.2-.4.2-.4.2h-.4l-.5.3h-.4l-.5.3h-.5l-.5.2h-1.1l-.5.2zm-10.6-7.4h1v1l.2.3v.3l.2.2.2.3v.4l.3.3.2.3.3.2.2.3.3.3.3.2.3.2.2.3.3.2.4.2h.2l.4.3h.4l.4.3h.5l.3.3h.9l.5.2h1.5l.6.2h.5v1h-1.9l-.5-.2h-.5l-.5-.2-.5-.2h-.4l-.5-.3h-.4l-.4-.3-.4-.2-.4-.2-.4-.3-.4-.2-.3-.2-.4-.2-.4-.3-.3-.3-.3-.3-.2-.4-.3-.3-.2-.3-.2-.4v-.3l-.2-.4-.2-.4v-.8l-.2-.4v-.4zm10.7-7.4v1h-1.3l-.5.2h-.3l-.5.2h-.5l-.4.2h-.4l-.6.3h-.4l-.4.3-.4.2-.3.2-.5.2-.3.2-.3.2-.4.2-.3.3-.3.2-.2.3-.3.3-.2.3-.2.3v.3l-.2.2v.3l-.2.3v.7l-.2.3v.3h-1V275l.2-.5.2-.4v-.2l.3-.4.3-.3.2-.3.3-.3.2-.3.3-.2.3-.3.3-.2.3-.3.3-.3.4-.2.4-.2.4-.2h.4l.4-.3.5-.2.5-.2h.5l.5-.2h1l.5-.2h1.1l.6-.2h.5z"/>
<path fill="#edb92e" d="M482 211.7c4.4 0 8 3.4 8 7.7s-3.6 7.8-8 7.8-7.6-3.5-7.6-7.8 3.5-7.8 7.7-7.8z"/>
<path fill="#21231e" d="M490.4 219.4h-1v-1l-.2-.3v-.4l-.2-.3v-.4l-.2-.4v-.3l-.3-.3-.2-.3-.2-.3v-.3l-.3-.2-.2-.3-.2-.2-.3-.2-.3-.2-.2-.2-.3-.2-.3-.2-.4-.2h-.3l-.3-.3h-.4l-.3-.2h-.4l-.4-.2H482V211l.6.2h1.2l.4.2h.4l.4.2.3.2.4.2h.4l.4.3.3.3.4.2.4.2.3.3.3.3.2.3.3.2.2.3.3.3.2.3.2.5.2.3v.4l.3.4v.8l.2.4v.8l.2.4zm-8.3 8.3v-1h1.5l.3-.2h.3l.3-.2.4-.2h.3l.3-.2h.3l.2-.3.3-.2.2-.2.3-.2.3-.2.2-.3.2-.2.2-.2v-.3l.3-.3.2-.3.2-.4v-.3l.2-.4v-.7l.2-.4v-.8l.2-.4v-.4h1v1.2l-.2.4v.5l-.2.4v.3l-.3.4v.3l-.3.4-.2.3-.2.4-.3.4-.2.3-.3.3-.2.3-.3.3-.3.2-.3.3-.3.3-.3.2-.3.2-.3.2h-.4l-.3.2-.4.2h-.4l-.4.2H482zm-8-8.3h1v1.8l.2.3.2.3v.3l.2.3.2.3.2.2.2.3.2.3.2.3.2.3.2.3.3.2.2.3.3.3.3.2.4.2h.3l.3.3h.3l.4.3h.3l.3.2h.7l.4.2h.7v1h-1.6l-.4-.2h-.3l-.4-.3h-.4l-.4-.3-.4-.3-.3-.2-.5-.2-.3-.2-.3-.2-.4-.2-.3-.3-.3-.3-.2-.3-.3-.3-.2-.3-.2-.3v-.3l-.3-.4v-.3l-.3-.4v-.4l-.2-.5v-.4l-.2-.3zm8-8.3v1.2h-1.3l-.4.2h-.3l-.3.2h-.4l-.3.3h-.3l-.3.2-.3.2-.2.2-.3.2-.2.3-.3.3-.2.2-.2.3-.2.3-.2.4-.2.3-.2.3-.2.3v.3l-.2.3-.2.3v.7l-.2.3v1.1h-1v-1.6l.2-.4v-.4l.2-.4.2-.4v-.4l.3-.3v-.4l.3-.2.2-.3.3-.3.2-.3.3-.4.3-.3.3-.3.2-.2.3-.2.4-.3.2-.2h.4l.3-.2.3-.2h.4l.4-.2h.5l.4-.2h1.1z"/>
<path fill="#edb92e" d="M482 213.7c3.3 0 5.8 2.6 5.8 5.7 0 3.2-2.5 5.8-5.7 5.8-3 0-5.5-2.6-5.5-5.8 0-3 2.5-5.7 5.6-5.7z"/>
<path fill="#21231e" d="M488.3 219.4h-1V218h-.2v-.7l-.2-.2v-.2l-.2-.3-.2-.2v-.2l-.3-.2-.2-.2-.2-.2-.2-.2h-.2l-.2-.3h-.4l-.2-.3h-.2l-.3-.2h-.6l-.2-.2H482v-1h1.8l.3.2h.3l.2.2h.3l.3.3h.4l.2.3h.2l.3.3.3.2.2.2.2.2.2.3v.2l.3.3v.2l.3.3v.2l.2.3.2.3v.6l.2.2v1.3zm-6.2 6.3v-1h1.1l.2-.2h.3l.2-.2h.2l.2-.2h.4l.2-.2.2-.2h.2l.2-.3.2-.2h.2v-.2l.3-.2v-.2l.3-.3.2-.3v-.2l.2-.3v-.2l.2-.3v-1.3h1v2l-.2.3-.2.3-.2.4-.2.3v.2l-.3.2v.2l-.3.2-.2.3h-.2l-.2.3h-.2l-.2.3-.3.2h-.2l-.2.3-.3.2h-.2l-.3.2h-.3l-.3.2h-1.4zm-6-6.3h1v1.3l.2.3v.2l.2.3v.2l.2.2.2.4.2.2v.2h.3l.2.3.2.3v.2h.3l.2.3h.3l.2.2h.2v.2h.4l.2.2h.2l.3.2h.9v1h-1.5l-.3-.2h-.3l-.3-.2h-.2l-.3-.3-.3-.2-.3-.2-.3-.2h-.2l-.2-.3h-.2l-.2-.4-.2-.2-.2-.2-.2-.3-.2-.3v-.3l-.2-.3h-.2v-.4l-.2-.3v-.6l-.2-.3v-.9zm6-6.2v1H481l-.2.2h-.4l-.3.2-.3.3h-.5l-.2.3h-.2v.3l-.3.2h-.2l-.2.3v.3l-.2.2v.2l-.2.3v.2l-.2.2v.2l-.2.3v.4l-.2.2v.7h-1V218l.2-.4v-.3l.2-.3.2-.3v-.2l.2-.3.2-.2.2-.3.2-.2.2-.3.2-.2.2-.2.2-.2.2-.2h.3l.2-.3h.2l.3-.2h.2l.3-.2h.3l.3-.2h.5l.3-.2h.6z"/>
<path fill="#0c4076" d="M482 215.8c2 0 3.7 1.6 3.7 3.6S484 223 482 223c-1.8 0-3.4-1.6-3.4-3.6s1.6-3.6 3.5-3.6z"/>
<path fill="#21231e" d="M486.2 219.4h-1v-.8h-.2v-.3l-.2-.2v-.2l-.2-.2-.2-.2-.2-.3h-.2l-.3-.2h-.2l-.2-.2h-.2v-.2H482v-1h1.4v.2h.5v.2h.3v.2h.3v.3h.3v.2l.2.2.2.3.2.3v.2l.2.2.2.3v.4l.2.2v.6zm-4 4.2v-1h.6v-.2h.5l.3-.2h.2l.2-.2v-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2v-.2l.2-.2v-.4h.2v-1h1v1l-.2.2v.2l-.2.2v.2l-.2.2-.2.3-.2.2-.2.2-.3.2-.3.3h-.3l-.3.2-.3.2h-1l-.3.2zm-4-4.2h1v1.3l.2.2v.2l.2.2.2.2.2.2.2.2.2.2h.2v.2h.4l.2.2h.8v1.2h-.7l-.2-.2h-.3v-.2h-.5l-.2-.2-.3-.3h-.2v-.2l-.2-.2h-.2v-.2l-.2-.3-.2-.3v-.2l-.2-.2-.2-.3v-1.4zm4-4v1h-1l-.2.2h-.2l-.2.2h-.2v.2h-.2l-.2.3-.2.2-.2.2-.2.3v.4h-.2v1h-1V218l.2-.2.2-.3v-.2l.2-.2.2-.2.3-.3.2-.2.2-.2h.2l.2-.2h.2l.2-.2h.3l.3-.2h.9z"/>
</g>
<path fill="#c6363c" d="M135.8 192.6v144.7c0 26 10 49.4 26.2 66.5a87 87 0 0 0 63 27.6c24.8 0 47-10.6 63.3-27.6a96 96 0 0 0 26.2-66.5V192.6z"/>
<path fill="#fff" d="M135 192v145.3l.2 2.5v2.4l.2 2.4.2 2.4.3 2.4.2 2.3.3 2.3.5 2.4.5 2.3.5 2.2.6 2.2.6 2.2.7 2.2.6 2.2.8 2.2.8 2 1 2.2.8 2 1 2.2 1 2 1 2 1.2 2 1 2 1.3 1.8 1.3 2 1.2 1.7 1.4 1.7 1.3 1.8 1.4 1.7 1.6 1.7 1.4 1.6 1.5 1.6 1-1-1.5-1.5-1.5-1.6-1.4-1.6-1.3-1.7-1.3-1.8-1.3-1.8-1.2-2-1.2-1.7-1.2-2-1-1.8-1.2-2-1-2-1-2-1-2-1-2-.8-2-.8-2.2-.8-2-.7-2.3-.6-2-.6-2.4-.6-2.2-.5-2.2-.5-2.3-.4-2.2-.4-2.3-.3-2.3v-2l-.3-2.5-.2-2.4V193.4h177.4v144l-.2 2.4v2.4l-.2 2.5-.2 2.3-.2 2.4-.3 2.3-.5 2.3-.4 2.3-.4 2.4-.5 2.2-.5 2.2-.6 2.3-.8 2.2-.7 2.2-.8 2-.8 2.2-1 2-1 2.2-.8 2-1 2-1 2-1.2 2-1 1.8-1.3 2-1.2 1.7-1.3 2-1.3 1.6-1.4 1.7-1.4 1.7-1.4 1.7-1.5 1.7-1.5 1.6-1.5 1.6-1.6 1.4-1.6 1.5-1.5 1.4-1.6 1.4-1.8 1.3-1.8 1.5-1.7 1.3-1.8 1.2-2 1.2-1.8 1-1.8 1.2-2 1-1.8 1-2 1-2 1-2 .8-2 1-2 .6-2 .7-2.2.7-2 .6-2.3.5-2 .5-2.2.5-2.2.4-2.2.3-2.2.3-2.2.2h-2.3l-2.3.2H225v1.3h4.8l2.3-.3 2.3-.2 2.3-.3 2.2-.3 2.2-.5 2.2-.4 2.2-.4 2.2-.6 2.2-.6 2-.7 2.2-.7 2-.8 2-.8 2-1 2-.8 2-1 2-1 2-1 2-1.2 1.8-1 1.8-1.4 1.8-1.2 1.8-1.3 1.7-1.4 1.7-1.2 1.7-1.5 1.6-1.4 1.6-1.7 1.6-1.5 1.5-1.6 1.5-1.6 1.5-1.7 1.5-1.8 1.4-1.7 1.3-1.8 1.2-1.8 1.3-2 1.2-1.8 1.2-2 1-2 1.2-1.8 1-2 1-2 1-2 1-2.2 1-2 .7-2.2.7-2.2.7-2.2.7-2.2.7-2.2.6-2.3.5-2.3.5-2.4.4-2.3.4-2.3.4-2.3.3-2.4.2-2.4V192z"/>
<g fill="#fff" stroke="#21231e" stroke-width=".5">
<path d="M226 424c.7-.2 2 0 3-1 1.4-1 .8-5 .7-7.7q-.7-20.3-1.8-68.3h-5.6q-1 48-1.8 68.3c0 2.6-.7 6.8.7 7.8 1 1 2.3.8 3 1h1.7z"/>
<path d="M234.4 423.5c-.8 0-2 .2-3-.5-1.5-1-1.6-5.2-1.7-7.8q-1-20.3-4-68 1.5-.4 2.6-.3l.4-.2c5 31.6 7 54 10.4 67.5.6 1.8 1.6 6.5 0 8-1 1-2.2 1-3 1l-1.6.2z"/>
<path d="M244.4 422c-.8 0-2 .5-3.2 0-1.5-1-1.4-5.2-2-7.7-3.5-13.5-5.5-36-10.6-67.5q1.6-.4 2.7-.4c8.2 30.6 10.6 52.2 17 65.6.5 2 2 6.8.6 8.5-1 1-2.3 1-3 1.3l-1.6.3z"/>
<path d="M254.4 419.2c-.8 0-2 1-3.3.4-1.4-.6-1.7-5.2-2.8-7.6-6.3-13.4-8.7-35.2-17-66q1.7-.8 2.7-.8c.7-.3.5-.5 1.6-.7 9.5 30.6 12.6 51.8 21.6 65 1.4 2 2.5 6.4 1.5 7.7-.7 1-2 1.3-2.8 1.6l-1.6.4zm-38.5 4.3c.7 0 2 .2 3-.5 1.4-1 1.5-5.2 1.6-7.8q1-20.3 4-68-1.5-.4-2.6-.3l-.4-.2c-5 31.6-7 54-10.4 67.5-.5 1.8-1.6 6.5 0 8 1 1 2.3 1 3 1l1.7.2z"/>
<path d="M206 422c.7 0 2 .5 3 0 1.6-1 1.5-5.2 2.2-7.7 3.4-13.5 5.4-36 10.4-67.5q-1.6-.4-2.6-.4h-.2c-8 30.6-10.5 52.2-16.7 65.6-.5 2-2 6.8-.6 8.5 1 1 2.2 1 3 1.3l1.5.3z"/>
<path d="M196 419.2c.7 0 2 1 3.2.4 1.5-.6 1.8-5.2 3-7.6 6.2-13.4 8.6-35.2 16.8-66q-1.6-.8-2.7-.8c-.7-.3-.5-.5-1.7-.7-9.5 30.6-12.5 51.8-21.5 65-1.3 2-2.4 6.4-1.4 7.7.7 1 2 1.3 2.7 1.6zm53-23c-.6 0-1.7.5-2.8 0-1.5-.8-2.5-5-3.3-7.5-6.4-12.5-12.2-34.8-21.8-65.8q1.4-.7 2.4-.7.9-.4 2.3-.6c10.2 30.7 14.8 53 24.8 65 1.7 2 3 6.5 2 7.8-.5 1-1.7 1-2.3 1.3l-1.4.4z"/>
<path d="M241 397.8c-.7 0-2 .3-3-.3-1.3-.8-1.8-5-2.3-7.6q-4-20.3-14-67.4l2.4-.4q1.1-.4 2.5-.4c8 31.3 9.8 54.7 17.3 66.7 1.3 2.2 2 6.7 1.2 8-.7.8-2 .8-2.6 1z"/>
<path d="M232.7 398.7c-.7 0-1.8.2-2.8-.5-1.4-1-1.4-5-1.6-7.8q-1.6-20.3-6.2-68 1.4-.4 2.4-.2 1-.3 2.5-.3c4.4 31.7 7 54.7 9.6 67.8.5 2.5 1.3 6.7.3 7.8s-2 .8-2.8 1h-1.5z"/>
<path d="M201.2 396.2c.7 0 1.8.5 3 0 1.4-.8 2.4-5 3.2-7.5 6.3-12.5 12-34.8 21.6-65.8q-1.4-.7-2.3-.7-.8-.4-2.4-.6c-10.2 30.7-14.8 53-24.8 65-1.7 2-3 6.5-2 7.8.5 1 1.7 1 2.3 1.3l1.4.4z"/>
<path d="M209.3 397.8c.7 0 1.8.3 3-.3 1.3-.8 1.8-5 2.3-7.6q4-20.3 14-67.4l-2.4-.4q-1-.4-2.4-.4c-8 31.3-9.8 54.7-17.3 66.7-1.4 2.2-2 6.7-1.2 8 .7.8 2 .8 2.6 1z"/>
<path d="M217.6 398.7c.7 0 1.8.2 2.8-.5 1.3-1 1.3-5 1.5-7.8q1.5-20.3 6-68-1.4-.4-2.3-.2-1-.3-2.5-.3c-4.3 31.7-6.8 54.7-9.5 67.8-.5 2.5-1.3 6.7-.3 7.8.8 1 2 .8 2.7 1h1.6z"/>
<path d="M226 399c.6 0 1.8 0 2.7-.8 1-1 .7-5.2.6-7.8q-.8-20.4-1.7-68.3-1.5 0-2.5.2-.8-.3-2.3 0-.8 47.9-1.7 68.2c0 2.6-.5 6.8.6 7.8 1 .8 2 .7 2.8.8z"/>
<path d="M243.5 377.6c-.6 0-1.4.5-2.2 0-1.2-.8-2-5-2.5-7.5-5-12.4-9.4-34.7-16.6-65.7q1-.5 1.8-.6.6-.4 1.8-.6c7.8 30.8 11.3 53 19 65.2 1.3 2 2.2 6.4 1.6 7.7-.4.8-1.4 1-1.8 1.2l-1 .4z"/>
<path d="M237.3 379.2c-.6 0-1.4.3-2.2-.3-1-1-1.4-5-1.8-7.7q-3-20-10.7-67.3 1-.4 2-.3.6-.5 1.7-.5c6 31.3 7.5 54.7 13.2 66.8 1 2 1.6 6.6 1 7.8-.6 1-1.5 1-2 1l-1 .4z"/>
<path d="M231 380c-.6 0-1.5.3-2.2-.4-1-1-1-5-1.2-7.8q-1.2-20.3-4.7-68 1-.4 1.7-.2a4 4 0 0 1 2-.2c3.2 31.7 5 54.7 7.2 67.8.3 2.6 1 6.7 0 7.8-.5 1-1.4.8-2 1h-1z"/>
<path d="M206.8 377.6c.5 0 1.4.5 2.2 0 1.2-.8 2-5 2.5-7.5 5-12.4 9.3-34.7 16.6-65.7q-.8-.5-1.7-.6-.7-.4-1.8-.6c-7.8 30.8-11.3 53-19 65.2-1.3 2-2.2 6.4-1.6 7.7.3.8 1.2 1 1.7 1.2l1 .4z"/>
<path d="M213 379.2c.6 0 1.4.3 2.2-.3 1-1 1.5-5 2-7.7a2735 2735 0 0 1 10.6-67.3q-1-.4-2-.3-.6-.5-1.7-.5c-6 31.3-7.4 54.7-13 66.8-1.2 2-1.8 6.6-1 7.8.5 1 1.4 1 2 1l1 .4z"/>
<path d="M219.3 380c.6 0 1.5.3 2.2-.4 1-1 1-5 1.2-7.8q1.1-20.3 4.7-68-1-.4-2-.2a3 3 0 0 0-1.7-.2c-3.3 31.7-5.2 54.7-7.3 67.8-.4 2.6-1 6.7-.2 7.8.6 1 1.5.8 2 1h1z"/>
<path d="M225.7 380.4c.5 0 1.4 0 2-.8 1-1 .7-5.2.6-7.8q-.6-20.3-1.3-68.3h-3.7q-.8 48-1.3 68.3c0 2.6-.4 6.8.5 7.8.6.8 1.6.7 2 .8z"/>
<path d="M221.3 330.2v7.4l-5.8 1.2c0 2.6 6.4 29.6 9.6 29.6 3.4 0 9.8-27 9.8-29.6l-5.7-1.2v-7.4c-1.2 1-2.5 2.7-4 4.3a27 27 0 0 0-3.7-4.3z"/>
</g>
<g transform="translate(-32 32)scale(.53333)">
<g id="rs-d">
<path fill="#edb92e" stroke="#21231e" d="M412 584.5c-3 .2-7 2-18.5 12-5 10.2-10 20.5-17 29.4q-3.4 2.1-6 3.2a84 84 0 0 1-17-.5c-4.4-4-11.3-1.2-9 3.2-1.8 1.6 2.2 7.8 5.3 5.2 3-2.4 13-.2 16.4-1.4-1.3 3-10.4 5-16.8 5.5-5.8.2-5.4 6-3 10.5 2.7 1.8.5-.4 1.4.8 2.6 4 8.2 3.4 8.4-.4.2-4 13.8-9 15.8-12-2.5 6-8.2 11.5-11 17.3-3 3-5 3.7-1 10 4.4-.6 4.6-1.7 7.3.8 6-.6 7-2.4 6-7.2-1-5 3.2-14 4.6-16.6 1.2-2.4 1.5-4.8 2.8-6.3 2.2-2.5 12.6-7.7 15.2-6.3 5 2.7 8.7 1 9.3-3.7-1.6-1.2-.4-8-3.8-8.2-2.8-.3-4.4.6-7 2.7-4 3.2-6 5-7.4 4.6-3-.7 5.8-5 13.8-15.6 11-14.5 11.8-22 11.3-27z"/>
<use xlink:href="#rs-c" width="1350" height="900" transform="matrix(-1 0 0 1 801.6 0)"/>
<path fill="#edb92e" stroke="#21231e" d="M345.2 634.5c-4-2.3-6.3-2.2-8-.5-.7 1-1.8.5-1.2-1 2.7-6.3 7-7 10.7-4.2l2.8 2.7c-.6 1-3.8 2.3-4.3 3zm57.4-5.6c4.3-3 8.3-3.6 10.5-2 1.3 1 2.5.3 1.4-1.2-4.4-6.2-9.6-6.5-13.5-3q-1.7 1.5-2.8 3c1.3 1.6 4.3 3.2 4.4 3zM369 664.4c.2-.2-1-5.4-2.6-7q-2.2 1.4-4.3 3.5c-4.2 4.5-4 11 2.5 16.7 1.6 1.5 2.4 0 1.7-1.7-1-2.7.3-6.3 3-11.5zM352.6 651c-4 1.6-5.4 3.7-5.2 6.4 0 1.5-1 2-1.5.3-4.8-11.2.2-13.3 7.5-13 .4 2-.7 6.2-.8 6.2z"/>
<path fill="#21231e" d="m384 621.3 1 .6h-.2v1.8h.2v.4h.2v.2h.2l.2.3.3.2v.2h.4l.2.3h.2l-.6 1h-.2l-.3-.2h-.2l-.2-.2-.2-.2-.3-.3-.3-.3v-.2h-.2v-.3l-.2-.2v-.2h-.2v-.2l-.2-.2V622l.2-.3v-.2zm6.5.8-.8 1-.2-.2-.3-.2-.2-.2-.2-.2h-.3l-.2-.3-.2-.3-.3-.2-.2-.2-.3-.2h-.7l-.2-.2h-.7v.2h-.2l-.2.2v.2h-.2v.3l-.2.2-1-.7v-.3l.2-.2.2-.2.2-.2.3-.3h.3l.2-.2h.4v-.2h.8l.2.2h.4l.2.2h.2l.2.2h.2l.2.3h.2l.2.3h.2l.3.3.2.3.2.2.3.2.2.2h.3z"/>
<path fill="#21231e" d="m387.3 617.6 1 .6v.4h-.2v.8l.2.2v.2l.2.2v.2l.2.2.2.3.3.2h.2l.2.2h.2l.2.2.2.2h.2l-.6 1h-.2l-.3-.2-.3-.3h-.2l-.2-.2-.2-.2-.2-.3v-.2h-.2l-.2-.2v-.2h-.2v-.5h-.2V618h.2v-.3zm6.5.8-.7 1-.2-.3h-.2l-.2-.3-.3-.2-.3-.3h-.2l-.2-.3-.2-.2-.3-.2-.3-.2h-.3v-.2h-.8l-.2.2-.2.2-.2.2v.2l-.3.2-1-.6.2-.3v-.2h.3v-.3h.3l.3-.3h.2l.2-.2h.2v-.2h1.4l.2.2h.2v.2h.3l.3.3h.2l.2.2h.2l.2.3h.2l.3.3v.2l.3.2.3.2z"/>
<path fill="#21231e" d="m391 612.8 1 .6-.2.2v.6h-.2v1l.2.3v.3h.2v.4h.2l.2.3v.2h.2l.2.2.2.2.3.3.3.2h.2l.2.2-.6 1-.3-.2-.2-.2-.2-.2-.3-.2-.2-.2v-.2l-.3-.2v-.2l-.3-.2v-.2l-.3-.2v-.4l-.2-.3v-.2h-.2v-.7h-.2v-1l.2-.3v-.4l.2-.3zm7 1-.8 1-.3-.3-.3-.2-.3-.3-.3-.2v-.2l-.3-.2-.3-.2-.2-.2-.2-.2-.2-.2h-.2l-.2-.2-.3-.2h-.2l-.2-.2h-.7l-.2.2-.2.2-.3.3v.2h-.2v.4l-1-.6v-.3l.2-.2.2-.3v-.2l.3-.2.2-.2v-.2h.3l.2-.2h.3l.2-.2h1.2l.2.2h.2l.2.2.3.2h.3l.2.3.2.2h.3l.2.3.3.2.2.2.3.2.2.2.3.2.2.3.3.2z"/>
<path fill="#21231e" d="m394.8 607.7 1 .5v.5l-.2.3v1.7l.2.2v.3l.2.2v.2l.2.2.3.3v.2l.3.2.2.2h.2l.2.3.2.2.3.2v.2l-.6.8-.3-.2v-.2l-.4-.2-.2-.3-.3-.3v-.2l-.3-.3-.2-.2v-.2l-.3-.3v-.2l-.3-.2-.2-.2v-.2l-.2-.2v-.3l-.2-.3v-2.1l.2-.2v-.2l.2-.3zm7.6 1.3-1 .8-.2-.3-.3-.3-.3-.2-.3-.3-.3-.3v-.2l-.4-.2-.2-.2-.2-.2-.2-.2-.2-.2h-.2l-.2-.2h-.2l-.2-.2h-.3v-.2h-.7v.2l-.3.2-.2.3-.2.2v.2l-.3.2v.2l-1.2-.5.2-.3.2-.3v-.2l.3-.3.2-.2.2-.2.3-.2.3-.2h.5l.3-.2h.8l.3.2h.2l.3.2h.2l.2.3.3.3.2.2.3.2.2.2h.3l.2.3.3.3.2.2.3.3.3.4.3.3.4.3z"/>
<path fill="#21231e" d="m399 602.6 1.2.6v.2l-.2.2-.2.3v2.1l.2.2v.2l.2.2.2.2v.2l.2.2.2.2.2.2v.2l.3.2.2.2.2.2.3.2.2.2.3.2-.8 1-.3-.3-.2-.3-.3-.2-.3-.3-.2-.2-.3-.2-.2-.3v-.2l-.3-.2-.2-.2v-.2l-.3-.2v-.3h-.2v-.4l-.2-.2v-.2l-.2-.3v-2.6l.2-.2v-.2l.2-.2v-.2zm7.7 1.4-1 .8-.2-.3-.3-.3-.2-.3-.3-.3-.3-.3-.2-.2-.2-.3-.3-.3-.2-.2h-.2l-.2-.3-.2-.2h-.2v-.2h-1.3l-.3.2v.2h-.2v.3l-.3.2-.2.3v.2l-1.2-.6.3-.3.2-.3.2-.2.2-.3v-.2l.4-.2h.2l.2-.2h.2l.2-.2h1.7l.2.2.2.2h.2l.2.3h.3l.3.3.2.3h.3l.2.3.3.3.2.2.3.3.3.4.4.3.3.3z"/>
<path fill="#fff" stroke="#21231e" d="m462.6 580.6-9.5-5.4c-4 3-6.8 13.5 0 22.2 8.5 11 12 18.7 14 27.5 6.2-8 8.3-16.3 4.4-24.5-5.3-11-8.7-17-8.8-20z"/>
<path fill="#fff" stroke="#21231e" d="m470.4 573-9.5-5.4c-4 3-12.5 13.2-5.6 22 8.5 10.8 17.3 19 19.4 27.7 6.2-8 8.8-16.2 5-24.4-5.4-11-9.4-17.2-9.4-20z"/>
<path fill="#fff" stroke="#21231e" d="m449.4 569.2-9.5-5.4c-4 3-7 13.5-.2 22.2a65 65 0 0 1 14 27.5c6.2-8 8.4-16.3 4.4-24.4-5.3-11-8.7-17-8.8-19.8z"/>
<path fill="#fff" d="M405.5 469c.2 8.7-1.4 66.5-1.8 74.7s-4 13.7-8 17q-5.3-7.5-6-19.3c-.4-4 .4-58.7.7-70q9.4-25.5 15-2.4z"/>
<path fill="#21231e" d="M404 543.8h-1l.2-1v-2.6l.2-1.7v-4l.2-2.3v-5l.2-2.7v-2.8l.2-3v-3l.2-3v-6.2l.2-3v-6.2l.2-3v-5.8l.2-2.7v-7.4l.2-2V469h1v11.7l-.2 2.5v8.3l-.2 2.8v6l-.2 3.2v3l-.2 3.2v6l-.2 3v3l-.2 2.8v5.3l-.2 2.5v2.3l-.2 2v3.6l-.2 1.5v2zm-8.7 17 .8-.5h-.6l.3-.4.4-.4.4-.3.4-.3.3-.4.5-.4.3-.3.3-.5.4-.4.4-.4.3-.4.4-.5.3-.4.2-.5.2-.5.3-.5.3-.5.2-.6.2-.5.3-.6.2-.5.2-.5v-.7l.3-.6v-.6l.3-.7v-.7l.2-.7v-.7l.2-.8v-1.5h1v2.3l-.2.7v.8l-.3.6v.8l-.3.7-.2.6-.2.8-.2.6-.2.6-.2.6-.3.6-.4.5-.2.6-.3.5-.2.5-.3.5-.3.5-.3.5-.4.4-.4.5-.3.4-.5.5-.3.4-.4.3-.3.4-.4.3-.2.4-.4.3-.4.3h-.7zm.7.2-.4.3-.3-.4h.7zm-7-19.5h1v.6l.2.8v1.4l.2.7v.6l.2.7v.6l.3.6v1.5l.3.5v.7l.3.6.2.6.2.6.2.6.2.5.2.7.2.6.2.5.3.6.2.5.3.4.2.6.3.5.3.5.3.5.2.4.3.4.3.5.3.3-.7.6-.3-.6-.4-.4-.3-.5-.3-.5-.3-.5-.3-.6-.2-.6-.3-.5-.4-.7-.2-.5-.3-.6v-.6l-.3-.5-.3-.5-.2-.6-.2-.7-.2-.6v-.7l-.3-.6-.2-.6v-.7l-.3-.5v-.7l-.2-.7-.2-.6v-.6l-.2-.7v-1l-.2-.8v-1.5zm1-70.2.8.3v8l-.2 2.3V492l-.2 2.7v11.6l-.2 3v19l-.2 2.2v11h-1v-20.9l.2-2.7v-14.6l.2-2.8V489l.2-2.6v-8.9l.2-2v-4.2zm16-2.3h-1l-.4-1.3-.3-1.3-.4-1-.5-1.2-.4-1-.2-1-.4-.7-.4-.8-.4-.6-.4-.4-.4-.4-.4-.3-.3-.3h-.3l-.4-.2h-.7l-.4.3-.4.3-.5.5-.5.4-.5.5-.5.7-.5.7-.5.8-.5 1-.6 1-.5 1.2-.6 1.3-.6 1.3-.6 1.5-.6 1.6-1-.3.7-1.6.6-1.5.7-1.3.6-1.4.5-1.2.6-1 .5-1 .5-1 .5-.8.6-.7.5-.7.6-.6.6-.4.5-.4.6-.3.5-.2h1.1l.5.3.5.3.5.5.5.5.4.6.4.6.5.8.4.8.4 1 .5 1 .3 1 .4 1.4.4 1.3.4 1.4zm0 0"/>
<path fill="#fff" d="m368 454.8-2.6 79.6c-.8 7 8.4 49 13.5 57.4 12.3-5.3 15-33.8 12.6-61.6s.4-51.8 2.3-56.8c0-6.3-1-12.8-1-19.3-8 .5-17.2.4-25 .8z"/>
<path fill="#21231e" d="m365 534.4 2.4-79.6h1l-2.5 79.6h-1zm13.7 57 .3.8-.6-.2-.5-1-.6-1-.5-1.2-.7-1.4-.5-1.6-.6-1.6-.6-1.8-.6-2-.5-2-.6-2-.7-2.2-.6-2.2-.6-2.3-.4-2.3-.6-2.3-.5-2.3-.6-2.3-.6-2.3-.4-2.2-.5-2.2-.4-2.2-.3-2-.4-2.2-.3-2-.3-1.8-.3-1.7-.2-1.6v-1.4l-.3-1.3v-2.8h1v4.1l.2 1.5.2 1.6.3 1.7.3 1.8.3 2 .4 2 .4 2.2.5 2.2.4 2.2.4 2.3.5 2.2.4 2.3.6 2.3.5 2.4.6 2.3.7 2.2.5 2.2.6 2 .6 2.2.6 2 .6 2 .5 1.7.7 1.5.6 1.5.5 1.4.5 1.2.5 1 .5 1-.6-.3zm.3.8-.4.2-.2-.4zm12-62h1l.3 2.5v2.7l.3 2.6v2.6l.2 2.6v12.5l-.3 2.4-.2 2.5-.2 2.3v2.3l-.4 2.3-.4 2.2-.3 2-.6 2-.4 2-.6 2-.5 1.7-.8 1.7-.6 1.5-.7 1.5-1 1.5-.7 1.3-1 1.2-1 1-1 1-1 1-1 .6-1.3.6-.3-1 1-.4 1-.7 1-.7 1-1 1-1 .7-1 .8-1.3.8-1.4.7-1.5.6-1.5.6-1.6.5-2 .5-1.7.5-2 .4-2 .3-2 .3-2 .3-2.3v-2.3l.4-2.3V558l.3-2.3V538l-.2-2.6-.2-2.6-.2-2.6zm2.4-56.8h1v.2l-.2.5-.2.7-.2.7-.2 1-.2.8-.2 1-.2 1.2v1.2l-.3 1.3-.2 1.3-.2 1.4v1.6l-.3 1.6v1.7l-.3 1.7v2l-.2 1.8v2l-.2 2V520l.2 2.5.2 2.5.2 2.6v2.5l-1 .2v-2.6l-.3-2.5-.2-2.5V520l-.2-2.5v-20.1l.2-2v-2l.3-2v-1.7l.2-1.7.2-1.8.2-1.6v-1.4l.3-1.5.2-1.3v-1.2l.3-1V477l.3-1 .2-.8.2-.7.2-.7.2-.5zm1 0v.2zm-1.5-18.8v-1l.4.4v3.7l.2.6v2.5l.2.6v1.8l.2.6v2.4l.2.6v3.6l.2.6v2.4h-1V468l-.2-.5V465l-.2-.5v-1.9l-.2-.6v-1.7l-.2-.6v-3l-.2-.6V454l.5.6zm0-1h.4v.4l-.5-.4zm-24.6 1.2h-1l.4-.5h3.9l.8-.2h12.8l.8-.2h4.7l.8-.2h1.7v1h-4.1l-.7.2h-8l-.8.2h-7.1l-.8.2h-3.9l.4-.4zm-1 0v-.4h.4zm.5 0h.4-.5zm-.6 0v-.4h.4z"/>
<path fill="#fff" d="m353 468 1.8 79.5c-.4 7 11 48.5 16.4 56.5 12.6-9.8 10-49.8 8.2-62-4-24-3.2-52-2-56.5 0-6.3-.2-13.8-.6-20.4q-12 1.6-23.7 2.8z"/>
<path fill="#21231e" d="m354.3 547.5-1.7-79.6 1-.2 1.7 79.6h-1zm16.6 56 .5 1-.7-.2-.5-1-.6-1-.6-1.2-.5-1.2-.7-1.6-.6-1.6-.7-1.8-.7-1.8-.7-2-.5-2-.7-2.2-.7-2.2-.6-2.2-.7-2.3-.7-2.2-.7-2.3-.6-2.3-.6-2.3-.5-2.3-.6-2.2-.6-2.2-.5-2-.6-2-.4-2-.4-2-.4-1.6-.3-1.6-.3-1.3-.2-1.3v-1.2l-.2-1v-.8h1v1.8l.2 1 .2 1.3.2 1.5.2 1.5.3 1.7.4 2 .4 1.8.4 2 .5 2 .6 2.2.4 2.2.6 2.3.7 2.3.6 2.3.7 2.2.6 2.3.7 2.3.7 2.3.7 2.2.7 2 .7 2.2.8 2 .7 1.8.6 1.7.8 1.7.6 1.4.6 1.3.6 1.2.5 1 .6.7h-.7zm.5 1-.4.2-.2-.4h.7zM379 542l1-.2v1.2l.2 1.4.2 1.5.2 1.8.2 1.7v2l.3 2 .2 2v2l.2 2.3v12l-.2 2.4v2.3l-.3 2.4-.4 2.4-.3 2.4-.3 2.2-.4 2.2-.5 2.2-.4 2-.6 2-.7 2-.8 1.6-.8 1.7-1 1.5-1 1.3-1 1.4-1.2 1-.6-.8 1-1 1-1 1-1.4.8-1.4.8-1.6.8-1.7.6-1.8.6-2 .5-2 .6-2 .4-2.2.4-2.3.3-2.3.2-2.4v-2.3l.3-2.4V569l.2-2.2V560l-.2-2.3v-2.2l-.2-2v-2l-.2-2-.2-1.6-.2-1.8-.2-1.5v-1.3l-.3-1.2zm-2-56.7h1v.6l-.2.7-.2.7v1l-.2.8v2.4l-.2 1.3v2.8l-.2 1.6v15.2l.2 2v2.3l.3 2v2.4l.3 2.3v2.2l.3 2.2.4 2.3.2 2.4.3 2.2.2 2.3.3 2.2.4 2.3-1 .2-.5-2.3-.3-2.4-.3-2.3-.4-2.3-.2-2.4-.2-2.3-.2-2.3-.2-2.3-.2-2.2v-2.3l-.3-2.2V512l-.2-2v-16l.2-1.4v-1.3l.2-1.2v-2l.3-1v-.8l.2-.6v-.7.2zm-.2-20v-1l.5.6v3.4l.2.7v3.8l.2.6v6l.2.6v5.3h-1V480l-.2-.7v-5.7l-.2-.6v-4.5l-.2-.7v-2.4l.4.4zm0-1h.4v.6l-.5-.4zm-23.7 4v-1l.8-.2h.8l.7-.2h2.2l.7-.2h.8l.7-.2h.8l.7-.2h1.4l.7-.2h.8l.7-.2h.6l.8-.2h1.7l.6-.2h.7l.8-.2h.8l.7-.2h1.5l.8-.2h.7l.7-.2h.8l.7-.2v1h-.6l-.6.2h-.8l-.7.2h-1.6l-.7.2h-.8l-.8.2h-.8l-.7.2h-1.5l-.7.2h-.8l-.7.2h-.7l-.8.2H362l-.7.2h-.7l-.8.2h-.7l-.6.2H357l-1 .2h-.6l-.7.2h-.8zm.2-1v1l-.6-.6.5-.6zm-.6.4v-.6h.5l-.4.5z"/>
<path fill="#fff" d="m342.2 486.7 21-.5c-2 8.6-.5 35.6 3.2 56.2 3.3 19.3 8 69.3-12 81.3-12.5-26.4-3-52-9.2-75-5-24.3-6-44.6-3-62"/>
<path fill="#21231e" d="m363 486.7-20.8.5v-1l21-.5.4.6zm0-1h.7v.6zm3.8 56.6-1 .2-.3-2-.3-2-.3-2-.4-2-.3-2-.2-2-.2-2.2-.2-2-.2-2.2-.2-2-.2-2-.2-2-.2-2.2v-2l-.3-2v-2l-.2-2V499l-.2-1.7v-4.5l.2-1.4v-1.2l.2-1.2v-1l.3-1v-1l1 .3v.8l-.2 1v1l-.2 1.3v5.4l-.2 1.6v4.9l.2 1.8v4l.2 1.8v2l.3 2v2l.3 2 .2 2v2l.3 2.2.3 2 .2 2.2.3 2 .3 2 .3 2 .3 2.2.2 2 .3 2 .3 2zM354 624l.8-.6-.7-.2 2-1.2 1.5-1.4 1.5-1.6 1.4-1.8 1.3-2 1-2 1-2.3 1-2.6 1-2.5.6-2.8.6-2.7.4-2.8.4-3 .4-3 .3-3 .2-3v-3l.2-3v-6.2l-.2-3-.2-3v-3l-.4-3-.2-2.7-.3-2.7-.2-2.5-.4-2.5-.2-2.4-.3-2.2-.3-2-.3-2h.8l.4 1.8.3 2 .3 2.3.3 2.3.4 2.5.3 2.7.3 2.7.2 2.8.2 3 .2 2.8.2 3v15.5l-.3 3-.3 3.2-.4 3-.4 2.8-.5 3-.8 2.7-.7 2.7-.8 2.6-1 2.4-1 2.4-1.2 2.2-1.3 2-1.4 2-1.6 1.5-1.7 1.5-1.8 1.3zm.7 0-.5.4-.3-.5h.7zm-10-75.2 1-.2.5 2.2.4 2.2.4 2.2.3 2.2.3 2.2.2 2.3v2l.2 2.4v25.4l.2 2.4v2.4l.3 2.4.3 2.4.3 2.4.5 2.4.4 2.4.5 2.4.7 2.5.7 2.4.8 2.5 1 2.5 1 2.4-.8.5-1.2-2.6-1-2.5-.8-2.6-.8-2.5-.7-2.5-.5-2.5-.5-2.4-.4-2.5-.2-2.3-.3-2.5-.2-2.4V594l-.2-2.4V564l-.2-2.3-.2-2.3-.2-2.2-.4-2.2-.3-2-.5-2.3-.5-2v-.2zm-2.5-62.6v1l.5-.4-.3 1.6-.2 1.7-.2 1.7-.2 1.7-.2 1.7v1.8l-.3 1.8v3.5l-.2 2v7.3l.2 2v1.8l.2 2v2l.2 2 .2 2 .2 2 .2 2 .3 2 .2 2 .3 2.3.3 2 .3 2.2.4 2.2.4 2.2.4 2 .4 2.4.4 2.2-1 .2-.4-2.2-.4-2.3-.4-2.2-.4-2-.3-2.3-.4-2.2-.3-2-.3-2.2-.2-2-.3-2.2-.2-2-.2-2-.2-2v-2l-.2-2v-2l-.2-1.8v-13l.2-1.8v-1.8l.2-1.7.2-1.7.2-1.7.2-1.7.2-1.7.3-1.7.5-.4zm-.5.4v-.4h.5zm.5 0v-.4zm-.5 0v-.4h.5z"/>
<path fill="#fff" d="M422 454.4c0 8.7-3.2 66.5-4 74.6s-4.5 13.6-8.4 16.6q-5.1-7.5-5.5-19.3c0-4 2.3-58.7 3-70 7-17 11.8-17.4 15-2z"/>
<path fill="#21231e" d="M418.5 529h-1v-1l.2-1v-1.5l.2-1.7v-2l.2-2v-2.3l.3-2.4v-2.5l.3-2.7.2-2.8v-3l.3-3 .2-3 .2-3v-3l.3-3.2.2-3 .2-3.2v-3l.3-3V474l.3-3v-2.5l.2-2.4V464l.2-2v-2l.2-1.8v-3.7h1v5.6l-.2 2v2l-.2 2.3v2.6l-.3 2.6-.2 2.7v2.8l-.3 3-.2 3v3l-.3 3-.2 3v3.2l-.3 3-.2 3.2-.2 3v3l-.2 2.7-.2 2.6v2.6l-.3 2.4v2.3l-.3 2v2l-.2 1.5v1.5l-.2 1.2v1zm-9.3 17 .8-.6-.7-.2.4-.2.3-.3.4-.4.3-.3.4-.3.4-.4.4-.3.3-.4.4-.4.4-.4.3-.5.4-.4.3-.6.3-.4.3-.5.3-.5.3-.5v-.5l.4-.6.2-.5.3-.7v-.6l.3-.6.2-.5.2-.7.2-.6V532l.3-.8v-.8l.2-.7v-.7h1v1.6l-.2.7-.2.7v.7l-.2.7-.2.7-.2.8-.2.6-.2.6-.2.6-.3.6-.2.6-.3.6-.3.5-.2.5-.3.5-.3.5-.5.5-.3.5-.3.4-.4.4-.4.4-.3.4-.5.4-.4.4-.2.4-.4.3-.4.4-.3.4-.4.3-.4.3h-.8zm.7 0-.5.4-.3-.5h.7zm-6.4-19.7h1v2.8l.2.8v.7l.2.7v1.2l.3.6v.8l.2.6.2.7v.7l.3.6v.6l.3.5.2.6.2.6.2.6v.6l.3.5.3.6.2.4.2.5.3.5.3.5.2.5.3.4.4.4.3.5.3.4-.8.5-.3-.6-.4-.4-.3-.5-.3-.6-.3-.6-.3-.5-.3-.6v-.6l-.4-.5-.2-.6-.2-.6-.2-.4-.2-.6-.2-.6-.2-.6-.2-.6-.2-.7-.2-.6v-.7l-.3-.7v-.6l-.2-.7v-.6l-.2-.7v-1.4l-.2-.7v-1.5l-.2-.7v-1.5zm3-70 .8.3v-.2 2.6l-.2 1.6v2l-.2 2v2l-.2 2.4v2.5l-.2 2.6v2.8l-.3 2.8v2.8l-.2 3v2.8l-.2 3v3l-.2 2.8v3l-.3 2.7v2.7l-.2 2.7v7.3l-.2 2v3.8l-.2 1.6v3.3h-1v-6.8l.2-2V513l.2-2.4V508l.2-2.6.2-2.8v-2.8l.2-3V494l.2-3 .2-2.8v-3l.2-2.8v-3l.3-2.7V474l.2-2.5V469l.2-2.4v-4.2l.2-1.8V459l.2-1.5v-1.2zm16-2h-1v.2l-.3-1.4-.3-1.2-.3-1.2-.4-1-.3-1-.4-1-.3-1-.4-.6-.5-.6-.4-.5-.2-.5-.4-.4h-.4l-.3-.2h-1.1l-.4.2-.5.2-.4.4-.5.5-.5.6-.6.7-.5.7-.5 1-.6.8-.5 1-.6 1.2-.7 1.2-.6 1.4-.6 1.4-.6 1.6-1-.4.7-1.6.8-1.4.6-1.4.6-1.2.6-1.2.6-1 .6-1 .6-1 .6-.7.5-.7.6-.6.6-.6.6-.4.5-.3h.6l.5-.3h1.2l.5.3.5.4.5.4.4.5.4.6.4.5.4.8.3 1 .4.8.3 1.2.3 1 .4 1.3.3 1.4.3 1.3zm-.5 0h.6-.5zm.6 0"/>
<path fill="#fff" d="M385 407.2c-.6 18.7-1 59-.8 62.6a51 51 0 0 0 6.7 22.6c4-3 7.7-8.5 8.4-16.6.6-6.7.8-39.5 1-56-4.3-2.6-8.7-6-12.2-9.7l-3.2-2.8z"/>
<path fill="#21231e" d="M384.7 469.8h-1v-23.4l.2-2.4v-19.6l.2-2.4v-9l.2-2v-3.8h1v7.8l-.2 2.4v9.4l-.2 2.5v12.5l-.2 2.4v25.4zm6 22.2.5.8h-.7l-.3-.6-.3-.5-.4-.6-.4-.4-.3-.6-.4-.6-.3-.7-.3-.6-.2-.6-.3-.7-.3-.7-.3-.7-.3-.7-.3-.8-.2-.7-.3-.7-.3-.8-.2-.8-.2-.7-.2-.8v-.8l-.3-.8v-.8l-.3-.8v-.8l-.2-.8v-.8l-.2-.8v-.7l-.2-.7v-1.5h1v2.5l.2.8v.8l.3.7v.8l.2.8.2.8.2.8v.7l.2.7.3.8.2.7.2.8v.8l.4.7.2.7.3.7v.7l.4.6.3.7.3.7.2.6.3.6.3.5.3.6.4.6.3.6.3.5.3.4h-.7zm.5.8-.4.3-.3-.3zm7.7-17h1l-.2.8v.8l-.2.7v.8l-.3.7-.2.7v.7l-.2.5-.2.7-.2.6-.3.6v.6l-.4.6-.2.6-.3.5-.2.5-.3.6-.3.5-.3.5-.4.4-.4.5-.3.4-.5.6-.3.4-.4.4-.3.4-.4.4-.3.3-.3.4-.4.2-.4.3-.4.3-.6-.8.4-.3.3-.3.4-.3.4-.2.4-.4.4-.3.3-.3.4-.4.4-.4.3-.5.4-.4.3-.3.3-.5.3-.5.3-.4.3-.5.2-.6.3-.5.2-.5.3-.6.2-.6.2-.5.2-.6.2-.8.2-.6.2-.6v-.7l.3-.7v-.7l.2-.8v-.7l.2-.8zm1-55.5.6-.8.2.4v18.8l-.2 2.2v14.6l-.2 2v8.9l-.2 1.5v4.3l-.2 1v2.6h-1V471l.2-1.5v-6.3l.2-1.8v-12l.2-2v-20.8l.2-1.7v-5.1l.2.3zm.6-.8h.2v.4l-.2-.5zm-12.4-9.8v1l.4-1 .3.4.2.5.4.3.3.3.3.5.3.3.4.3.3.4.4.4.3.3.4.4.4.3.4.3.3.3.4.3.3.4.4.3.4.3.5.3.3.2.4.2.4.3.4.3.4.3.4.3.4.3h.4l.4.4.4.3.4.2.4.2.4.3-.6.8-.4-.3-.4-.2-.4-.3-.4-.3-.4-.2-.4-.3-.4-.3-.4-.3-.4-.2-.4-.3-.4-.3-.4-.3-.4-.4-.3-.3-.4-.3-.3-.3-.4-.3-.4-.3-.5-.2-.4-.3-.2-.3-.4-.4-.4-.3-.3-.3-.4-.3-.4-.5-.4-.3-.4-.3-.3-.5-.5-.3-.3-.4-.3-.3.2-.8zm-.3.8-1-.8h1.2l-.3.8zm-2.5-3.3h-1l1-.3.2.2.2.2v.2h.3v.3h.4v.2l.3.2.3.2v.2h.3v.2h.2v.2h.3l.2.2v.2h.2v.2l-.2.8v-1l-.3 1h-.2v-.2h-.2v-.2h-.2v-.2h-.2l-.2-.2v-.2h-.2l-.2-.2-.3-.3h-.3v-.3h-.3v-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2h-.2l1-.4zm-1 0v-1.4l1 1z"/>
<path fill="#fff" d="M400 419.3c-.4 20-.2 51.7 0 54.3a34 34 0 0 0 5.4 19.3c4-3.2 8-8.8 8.8-17 .5-5 2-26.4 2-42.8-5.3-1.7-12-10.4-16.3-14z"/>
<path fill="#21231e" d="M400.4 473.5h-1v-12.9l-.2-1.7v-33.7l.2-2v-4h1v14.5l-.2 2v37.1l.2.5v.3zm4.7 19 .7.8h-.7l-.3-.6-.3-.5-.3-.5-.2-.5-.3-.5-.3-.5-.2-.6-.3-.5-.2-.5-.3-.5-.2-.6-.2-.6-.2-.6-.2-.6-.2-.6-.2-.6-.2-.6v-.6l-.3-.7v-.6l-.3-.8v-.6l-.2-.7v-.8l-.2-.6v-.7l-.2-.8v-1.5l-.2-.7v-1.4h1v2.8l.2.7v.7l.2.7v.7l.2.7v.7l.2.6v.7l.3.6v.6l.3.5v.6l.3.7.3.6.2.6.2.5.2.5v.6l.4.5.2.7.3.5v.5l.4.5.2.5.3.5.2.5.3.5.3.4zm.7.8-.4.3-.3-.4zm8-17.3h1v1.7l-.3.7v.7l-.3.8v.7l-.2.7-.2.7-.2.5-.3.7v.6l-.4.6-.2.6-.3.5-.2.6-.3.6-.4.6-.2.5-.3.5-.4.5-.2.4-.4.6-.3.4-.3.5-.3.4-.4.3-.4.3-.4.4-.3.3-.5.4-.4.3-.3.3-.6-.8.5-.3.3-.3.4-.4.4-.3.3-.4.3-.4.3-.4.4-.4.3-.4.4-.4.3-.5.3-.4.4-.4.3-.5.3-.3.3-.6.3-.4.2-.5.3-.5.2-.5.3-.6.2-.6.2-.6.2-.6.2-.7.2-.7.2-.7v-.7l.3-.8v-.7l.2-.7v-.8zm2.4-42.3.4-1 .3.5v7.9l-.2 1.6v5.1l-.2 1.6v1.7l-.2 1.7v4.6l-.2 1.6v2.9l-.2 1.3v1.4l-.2 1.3v2.5l-.2 1v2.2l-.2 1v1.6l-.2.7v1.1h-1v-2l.2-.8v-1.8l.2-1v-2.2l.2-1.2v-2.7l.2-1.3v-1.5l.2-1.4V457l.2-1.6v-1.6l.2-1.6V449l.2-1.6V443l.2-1.6V433l.5.5zm.4-1h.3v.5zm-16-13.4h-1l.8-.4.5.2.4.4.5.4.5.4.5.5.4.4.4.4.5.5.4.6.5.4.4.5.5.4.5.6.5.5.5.5.5.6.6.5.5.6.7.5.5.6.5.5.6.5.5.5.5.4.6.4.4.4.5.3.4.3.5.3.4.3.5.2.4.2-.3 1-.5-.2-.5-.3-.5-.2-.6-.3-.6-.4-.6-.3-.5-.4-.6-.4-.5-.4-.6-.5-.5-.5-.5-.5-.5-.5-.6-.5-.5-.5-.7-.5-.5-.6-.5-.5-.6-.5-.5-.6-.5-.4-.5-.5-.5-.5-.5-.5-.5-.5-.5-.4-.4-.4-.5-.4-.4-.5-.6-.4-.4-.3-.4-.4.8-.3zm-1 0v-1l.8.6-.8.2z"/>
<path fill="#fff" d="M387 405.6 373 385c-2.8 10.2-6.3 82.8-2.2 88.8a36 36 0 0 0 6.4 19.3c4-3 9.8-8.5 8-16.7-1.6-8.2.4-62.3 2-70.7z"/>
<path fill="#21231e" d="m373.3 384.7 14.2 20.6-.8.5-14.3-20.5v-.4zm-1 .2.4-1 .5.7-.8.2zm-1.2 88.7h-1l.2.3-.5-.8-.3-1.2-.3-1.5v-1.8l-.3-2-.2-2.5v-2.6l-.2-2.8v-20.3l.2-3.7V431l.2-4 .2-3.6v-3.8l.3-3.7.2-3.7.2-3.4.3-3.5.2-3.2.3-3 .2-3 .3-2.5.3-2.4.2-2 .3-2 .3-1.3.4-1 1 .2-.3 1-.2 1.5-.3 1.8-.3 2-.2 2.4-.3 2.5-.3 2.8-.2 3-.3 3.3-.3 3.3-.2 3.4-.2 3.6-.2 3.7v3.7l-.3 3.8v3.8l-.2 3.7v3.8l-.2 3.6v19.6l.2 2.6.2 2.3.2 2v1.8l.4 1.5.3 1 .3.7zm0-.2v.2zm5.7 19.2.6.8-.7-.2-.3-.4-.3-.6-.3-.5-.3-.6-.3-.5-.2-.6-.3-.6-.3-.5-.3-.6-.4-.5-.2-.6-.3-.6v-.6l-.4-.6-.2-.6-.2-.6-.3-.6-.3-.6-.2-.7v-.6l-.3-.5-.2-.7v-.7l-.3-.6v-.6l-.2-.7v-.6l-.2-.7v-1.5l-.2-.7v-.7h1v1.4l.2.7v.6l.2.6v.7l.2.6v.7l.2.6.2.6v.6l.3.6.2.7.2.6.2.7v.6l.3.6.2.5.3.6.2.6.3.6.2.5.3.6.3.5.3.5.3.6.3.4.2.5.3.4.3.4.3.5.4.5h-.7zm.6.8-.4.3-.4-.5zm7.4-17 1-.3v.8l.2.7v3l-.2.7-.2.7v.7l-.3.7-.2.6-.2.6-.2.6-.3.6-.3.5-.4.6-.3.4-.4.5-.5.4-.3.5-.4.4-.4.4-.4.4-.5.5-.4.4-.3.5-.4.3-.4.4-.5.3-.4.3-.3.4-.4.3-.6-.8.4-.3.5-.3.4-.2.3-.4.3-.3.4-.3.4-.4.4-.3.4-.4.4-.5.4-.3.4-.4.4-.5.3-.5.5-.5.3-.5.3-.5.4-.5.3-.5.2-.6v-.6l.3-.5.2-.5.2-.7v-.6l.2-.6v-3.1l-.2-.7zM387 406v-1l.6.7-.2 1-.2 1v1.4l-.3 1.7-.2 2-.2 2v2l-.3 2.4v2.5l-.3 2.6v2.8l-.2 2.7-.2 2.8v3l-.2 2.8v3l-.2 3v8.5l-.2 2.8v13.5l.2 2v1.5l.2 1.4v1l.2 1-1 .2v-1l-.2-1v-1.5l-.2-1.7v-4l-.2-2v-15.5l.2-3V441l.2-3v-2.8l.2-3 .2-2.7v-2.8l.2-2.7.2-2.5V419l.3-2.3v-2.2l.3-2v-2l.3-1.5.3-1.4v-1.2l.3-1 .5.7zm0-1h.7v.7zm0 0v1l-.3-.2.4-.7zm0 1h-.2v-.2l.3.3z"/>
<path fill="#fff" d="M358 451.2q-1.2 17-1 23 .3 11.6 5.5 19.2c4-3 9-8.5 8.4-16.6-2-31.5 1.4-61 5.4-89.6-3.4-2.5-9-5-12.5-7.6q-10.4 57.3-6 71.6z"/>
<path fill="#21231e" d="M357.4 474h-1v-7.4l.2-.7V463l.2-.7V459l.2-1v-1.8l.2-1v-3l.2-1h1v2l-.2 1v2l-.2 1v2.8l-.2.7v3.2l-.2.7v4.2l-.2.6v4.3zm4.8 19 .6.8h-.7l-.3-.6-.3-.5-.3-.5-.2-.5-.2-.5-.3-.5-.3-.6-.2-.4-.3-.6-.2-.5-.3-.6-.2-.7-.2-.6-.2-.6-.2-.5-.2-.6v-.7l-.3-.6v-.7l-.3-.6v-.7l-.2-.6-.2-.6v-.7l-.2-.7v-1.4l-.2-.7v-1.5l-.2-.7v-.7h1v2.2l.2.7v1.2l.2.7v.6l.2.6v.7l.2.5.2.7v.6l.3.6v.6l.3.6.2.6.2.6.2.6.2.6.2.5.2.6.2.6.3.6.2.5.2.5.2.5.3.5.3.5.3.5.3.5.3.4zm.6.8-.4.3-.3-.3h.8zm7.6-17h1v3.7l-.3.7v.6l-.2.7v.7l-.4.6-.2.6-.2.6-.3.6-.3.6-.3.5-.2.6-.3.5-.4.5-.2.5-.4.4-.4.5-.4.4-.3.6-.4.4-.5.4-.4.4-.3.4-.4.3-.4.3-.5.4-.4.3-.2.3-.6-.8.3-.3.4-.3.3-.3.3-.2.4-.4.4-.3.4-.3.4-.4.3-.4.4-.5.3-.4.3-.4.4-.4.3-.5.3-.5.3-.5.2-.5.3-.5.3-.5.2-.6.3-.6.2-.5.2-.6.2-.8v-.6l.2-.6v-.7l.2-.6zm5.7-89.2.7-.8.2.5-.5 2.6-.4 2.6-.3 2.7-.3 2.7-.4 2.7-.3 2.7-.3 2.7-.4 2.8-.3 2.8-.3 2.7-.3 2.8-.2 2.8-.2 2.8-.3 2.7-.2 2.8v3l-.3 2.7-.2 2.8-.2 2.8v2.8l-.2 3v8.5l-.2 3v8.6l.2 3v3l.2 2.8.2 3h-1l-.2-3v-3l-.2-2.8v-5.8l-.2-3V451l.2-2.8v-3l.2-2.7v-2.7l.3-3 .2-2.7.2-2.8V428l.3-2.7.3-2.8.3-2.8V417l.4-2.8.3-2.7.3-2.8.4-2.7.3-2.7.4-2.7.4-2.7.4-2.8.3-2.7.5-2.7.4-2.7v.6zm.7-.8.2.2v.3l-.3-.5zm-12.3-7-1-.3.8-.3.3.2.4.2.2.3h.3l.4.3.3.3.4.2.4.2.5.3.4.2.3.2.4.3.4.2.5.3.5.2.4.2.4.2.4.2.5.3.4.2.5.2.4.2.3.2.4.3h.4l.5.4.4.2.3.2.3.3.4.3.3.3.4.2-.6.8-.2-.2-.4-.3h-.3l-.3-.3-.3-.3-.4-.2-.4-.2-.4-.3-.4-.2-.4-.2-.4-.3-.4-.2-.4-.3-.5-.2-.4-.2-.4-.2-.5-.2-.5-.3-.4-.2-.4-.2-.4-.2-.4-.2-.4-.3h-.4l-.4-.4-.4-.2-.4-.2-.4-.3-.4-.3-.3-.3-.4-.2-.4-.2.8-.3zm-1-.3.2-.8.6.5zm-6 71.4.8.6-.8-.2-.3-1v-1l-.3-1-.2-1.3v-1l-.2-1.4V443l-.2-1.6v-5l.2-1.8v-2l.2-2 .2-2 .2-2.3.2-2.3.2-2.5.3-2.4.3-2.5.3-2.6.4-2.8.4-2.8.4-3 .4-2.8.5-3 .6-3.2.5-3.2.6-3.3.6-3.5.6-3.5.6-3.5 1 .2-.6 3.5-.6 3.5-.6 3.4-.5 3.3-.4 3.3-.5 3-.4 3-.4 3-.4 3-.4 2.7-.3 2.7-.3 2.7-.3 2.5-.2 2.4-.3 2.4-.3 2.3v2.2l-.3 2v2l-.2 2v9.8l.2 1.4v1.2l.3 1 .2 1.2v1l.4 1-1-.3zm.8.6-.5.6-.3-.8zm0 0-.7-.7 1 .3zm-.7-.7 1-1v1.3z"/>
<path fill="#fff" d="m343.6 471.8 4 14.7c.2 7.7 5 14 8.3 19 4-3 7.5-8 6.5-16-5.8-46.5-3.8-77.3 6-115-22 15-32.3 46.6-25 97.3z"/>
<path fill="#21231e" d="m347 486.6-3.8-14.7 1-.3 3.8 14.6zm8.6 18.7.6.7h-.7l-.3-.6-.3-.5-.4-.5-.4-.5-.3-.5-.5-.6-.3-.6-.4-.5-.3-.7-.4-.5-.2-.6-.4-.5-.3-.6-.2-.5-.4-.6-.3-.7-.2-.6-.3-.6-.3-.5-.3-.7-.3-.6-.3-.7-.2-.7-.2-.6-.2-.7-.2-.7-.2-.7v-.6l-.2-.7v-.8l-.2-.8v-.7h1v1.4l.2.7v.7l.2.7.2.6v.6l.3.7v.5l.4.7.2.6.3.6v.6l.4.7.3.6.3.6.3.4.3.6.4.6.3.6.3.5.4.6.3.5.4.5.3.6.3.4.4.5.3.4.4.5.3.4.3.5.4.3h-.8zm.6.7-.4.3-.3-.4h.8zm5.8-16.5h1v5.5l-.2.6v.7l-.3.6v.4l-.3.6-.2.6-.3.5-.2.6-.2.5-.3.6-.3.5-.3.6-.3.4-.2.4-.3.4-.4.5-.2.3-.3.4-.4.4-.3.3-.3.4-.4.4-.4.3-.3.3-.7-.8.4-.3.4-.4.3-.3.4-.3.4-.3.3-.4.4-.3.4-.4.3-.4.3-.4.3-.4.3-.4.3-.4v-.5l.4-.3.2-.5.2-.5.2-.5.2-.5.2-.5.2-.6v-.6l.2-.5v-.6l.2-.7v-5.1zm6.8-114.7-.6-1 .8.7-1 3.5-.8 3.5-.8 3.4-.7 3.4-.7 3.4-.7 3.3-.6 3.5-.6 3.4-.4 3.2-.5 3.3-.3 3.4-.4 3.2-.4 3.4-.4 3.3-.2 3.3-.2 3.4-.2 3.4v3.6l-.2 3.5v14.7l.3 3.7.3 4 .2 3.8.3 4 .4 3.8.3 4 .5 4.2.4 4.3.6 4.4h-1l-.6-4.2-.4-4.3-.5-4.2-.3-4-.4-4-.3-4-.2-3.8-.2-3.8v-3.7l-.2-3.7v-14.3l.2-3.4.2-3.4.2-3.4.2-3.4.3-3.3.3-3.4.4-3.3.4-3.3.6-3.4.5-3.3.7-3.4.6-3.3.7-3.4.7-3.4.8-3.5.8-3.4.8-3.5 1-3.6.8.6zm-.6-1 1-.6-.2 1.3zm-24 98h-1l-.7-4.6-.5-4.6-.5-4.5-.3-4.3-.3-4.2-.2-4.2v-8l.2-3.8.2-3.7.3-3.7.4-3.4.5-3.4.6-3.3.8-3.3.7-3 1-3 1-3 1-2.8 1-2.8 1.2-2.5 1.3-2.5 1.4-2.4 1.5-2.3 1.4-2 1.6-2.2 1.7-2 1.7-2 2-1.6 1.8-1.7 2-1.6 2-1.5.6.8-2 1.4-2 1.6-1.8 1.6-1.8 1.7-1.8 2-1.6 2-1.6 2-1.5 2-1.4 2.3-1.4 2.4-1.4 2.4-1.2 2.6-1 2.7-1 2.7-1 3-1 3-.7 3-.7 3-.6 3.4-.5 3.3-.5 3.5-.3 3.6-.3 3.7-.2 3.8v12l.4 4.4.3 4.3.5 4.5.5 4.6.6 4.7zm-.6 0h.5-.4zm-.4 0"/>
<path fill="#fff" d="m344.8 486.2-20.3-6.7c-2.5 20.2 1.4 45.6 7 79 4.6 29 2 51.6-1.5 66.2 21.6-3 30.7-30.7 20-83-3-13.7-5.8-31.3-5.2-55.5"/>
<path fill="#21231e" d="m324.7 479 20.2 6.7-.4 1-20.2-6.7-.3-.6zm-.6.4v-.6l.7.2zm8 79-1 .3-.6-3-.5-3.2-.6-3-.5-3-.5-3-.5-2.8-.4-3-.4-2.7-.4-2.7-.4-2.7-.4-2.7-.3-2.7-.4-2.6-.3-2.6-.3-2.4-.2-2.5-.3-2.3-.2-2.4-.2-2.4v-2.4l-.3-2.3V500l-.2-2v-10.7l.2-2 .2-2v-2l.4-2 1 .3-.2 2-.2 1.8v2l-.2 2v4l-.2 2v4.3l.2 2.3v2.3l.2 2.3.2 2.3.2 2.2.2 2.4.3 2.4.2 2.5.3 2.5.3 2.5.3 2.6.4 2.8.4 2.6.3 2.7.4 3 .6 2.7.4 2.8.4 3 .5 3 .4 2.8.5 3 .4 3 .5 3.2zm-2 65.8v1l-.5-.6.4-1.4.2-1.4.3-1.5.3-1.5.3-1.6.4-1.6.3-1.7.2-1.8.2-1.7.2-1.8.2-2 .2-1.8v-2l.3-2 .2-2v-2l.2-2v-13.4l-.2-2.3-.2-2.4-.2-2.4-.2-2.5-.3-2.5-.3-2.6-.3-2.6-.4-2.6-.4-2.7 1-.2.3 2.7.4 2.7.3 2.5.3 2.6.3 2.6.2 2.5.2 2.6v2.4l.3 2.4v6.8l.2 2.3v4.3l-.2 2v2l-.2 2v2l-.2 2-.2 2-.2 1.8-.3 1.8v1.8l-.4 1.7-.2 1.8-.3 1.6-.2 1.6-.3 1.5-.3 1.5-.4 1.5-.3 1.4-.5-.8zm0 1h-.6v-.6zm19.4-83.5 1-.2 1 5 .8 4.6.7 4.6.5 4.5.5 4.3.4 4 .3 4 .2 4v7.2l-.2 3.4-.2 3.2-.4 3-.4 3-.6 3-.6 2.6-.7 2.6-1 2.4-1 2.3-1 2.2-1 2-1.2 1.8-1.3 1.7-1.4 1.5-1.4 1.4-1.5 1.3-1.5 1-1.8 1-1.8.7-2 .7-2 .5-2 .2v-1l2-.3 1.8-.6 1.8-.6 1.7-.8 1.6-1 1.5-1 1.5-1 1.4-1.5 1.3-1.5 1.3-1.6 1-1.8 1.2-2 1-2 1-2.2.7-2.3.6-2.4.7-2.7.5-3 .4-2.8.4-3 .2-3.3v-3.5l.2-3.5v-3.7l-.3-3.8-.3-4-.3-4-.4-4.3-.7-4.4-.8-4.5-.8-4.7-1-5zm-4.8-55 .3-1 .3.5V503l.2 2 .2 1.8v2l.2 1.8v1.8l.2 1.8.2 1.7.2 1.8.2 1.7.2 1.7.2 1.6.2 1.6.2 1.6.2 1.5.3 1.5.3 1.5.3 1.5.2 1.4.3 1.4.3 1.4.3 1.4.3 1.3v1.3l.4 1.2-1 .2-.2-1.3-.3-1.3-.4-1.2-.2-1.4-.3-1.4-.2-1.4-.2-1.4-.3-1.5-.2-1.5v-1.5l-.4-1.6-.2-1.5-.2-1.6-.2-1.7-.2-1.7-.2-1.7-.2-1.8-.2-1.8v-1.7l-.3-2-.2-1.7v-2l-.3-1.8v-4l-.2-2v-13l.3.5zm.3-1 .3.2v.2l-.4-.5z"/>
<path fill="#21231e" d="M343.6 584h1v.7h-.8v-.2h-.2v-.4zm-4.3-40 1-.4.4 1.2.2 1.2v1l.4 1.3.2 1.3.2 1.2.2 1.2.2 1.2.2 1.3.2 1.3.2 1.2.2 1.3.2 1.3.2 1.2.2 1.3v1.4l.3 1.3v2.6l.3 1.3v1.3l.2 1.2v2.6l.2 1.3v9.1h-1v-7.6l-.2-1.2v-2.7l-.2-1.3V570l-.2-1.3v-1.3l-.2-1.3v-1l-.3-1.4v-1.3l-.3-1.3-.2-1.3v-1.2l-.3-1.3-.2-1.3-.2-1.3-.2-1.3v-1.2l-.4-1.2-.2-1.2-.2-1.2-.2-1.2-.3-1.2-.2-1.2-.2-1.2zm-5.5-50.4h1.2v6.4l.2 1.5v3.3l.2 1.5v1.6l.2 1.5v1.6l.3 1.5v3.1l.3 1.5.2 1.7v1.5l.3 1.6.2 1.5.2 1.6.2 1.4.2 1.6.3 1.5v1.6l.4 1.6.2 1.6.3 1.6.2 1.5.3 1.6.3 1.5.3 1.5.4 1.6-1 .3-.5-1.7-.4-1.6-.3-1.6-.3-1.5-.3-1.5-.2-1.6-.3-1.6-.2-1.5-.3-1.6-.2-1.6-.2-1.4-.3-1.6-.2-1.5-.2-1.6v-1.7l-.3-1.5-.2-1.6v-1.5l-.2-1.6v-1.4l-.2-1.6-.2-1.6V508l-.2-1.7V503l-.2-1.4v-6.4l-.2-1.6zm1.2 0h-1.2v-.2h.2v-.2h.2v-.2h.8v.2h.2v.4z"/>
<path fill="#fff" d="m344 367.3 13.2 2.2-4.6 23.3c-5 24.3-8.2 53-4.5 84.2q2.2 17.5 7.6 28.5c-14.6-1.6-24-15.8-22.7-40.6q-3.9-43.4 3.2-69.4c1.4-5 5.8-24.2 7.8-28.3z"/>
<path fill="#21231e" d="m357 370-13-2.3v-1l13.3 2.3.4.6zm.3-1h.5v.6zm-4.2 24-1-.3.2-.3v-.4l.2-.5v-.6l.2-.7v-.6l.3-.7v-.8l.2-.8v-1l.3-.7.2-1 .2-1v-.8l.2-1 .2-1 .2-1 .2-1 .2-1 .2-.8v-1l.3-.7.2-1v-.7l.3-.7v-.7l.3-.6v-.6l.2-.5v-.4l.2-.3v-.3l1 .2v.6l-.2.5v.5l-.3.6v.5l-.2.7-.2.8-.2.8v1l-.3.7-.2 1v.8l-.2 1-.2 1-.2 1-.2.8-.2 1-.2 1v1l-.3 1-.2.7-.2 1v.7l-.2.7v1l-.2.5-.2.6v.7l-.2.5v.7zm-4.4 84h-1l-.3-2.8-.3-3-.3-3-.2-2.8v-2.8l-.3-3V457l-.2-3v-11l.2-2.7v-2.7l.2-2.7.2-2.8.2-2.6.2-2.6.3-2.6.2-2.6.3-2.6.3-2.5.4-2.5.4-2.5.4-2.5.3-2.4.4-2.4.3-2.4.5-2.4.4-2.3.5-2.3.4-2.3 1 .2-.4 2.2-.4 2.3-.5 2.3-.4 2.4-.4 2.4-.5 2.4-.4 2.4-.2 2.4-.4 2.5-.3 2.5-.2 2.5-.3 2.6-.3 2.5-.2 2.5-.2 2.7-.2 2.6-.2 2.6v2.6l-.2 2.7v2.7l-.2 2.7v11.5l.2 3v2.7l.3 3 .2 2.7.3 3 .3 2.8.3 3zm7 29v-1l-.4.8-.4-.7-.3-.6-.3-.8-.4-.7-.3-1-.3-.6-.3-.8-.4-.8-.3-.8-.2-.8-.2-.8-.3-1-.2-.7-.3-1-.2-.8-.3-1-.2-1-.2-.8v-1l-.3-1-.2-.8-.2-1-.2-1v-1l-.3-1-.2-1v-1l-.3-1-.2-1v-1l-.2-1.2-.2-1 1-.2v1l.3 1v1.2l.2 1v1l.3 1 .2 1 .2 1v1l.2 1 .2 1 .2 1 .2 1 .3.8.3 1 .2.8.2 1 .3.7.2 1 .3.7.2.8.3 1 .3.7.3.8.3.7v.8l.4.8.4.7.3.7.4.7.3.6.3.7-.5.7zm.4-.7.4.8h-1l.6-.7zM332.4 465l1-.2v8.8l.2 2 .2 2 .3 2 .4 1.8.4 1.7.4 1.8.5 1.7.5 1.5.6 1.5.6 1.5.7 1.3.8 1.3.7 1.3.8 1 1 1 .8 1.2 1 1 1 .8 1 .8 1 .8 1 .7 1.2.6 1.2.4 1.2.5 1.2.3 1.2.3 1.3.3h1.4l-.2 1h-1.3l-1.4-.4-1.3-.4-1.3-.4-1.2-.5-1.2-.5-1.2-.7-1-.6-1.2-.8-1-.8-1-1-1-1-1-1-1-1.2-.7-1.2-.8-1.3-.8-1.2-.7-1.4-.7-1.5-.5-1.6-.6-1.6-.5-1.7-.4-1.6-.4-1.8-.3-2-.2-1.8-.2-2-.2-2v-8.9zm3.3-69.6 1 .3-.5 1.6-.4 1.7-.4 1.7-.3 1.7-.3 1.8-.3 1.8-.3 1.8-.2 2-.2 1.8-.3 2-.2 2v2l-.3 2v2l-.2 2-.2 2.3v4.3l-.2 2.2v9.6l.2 2.5v2.4l.2 2.5v2.5l.2 2.6.2 2.6.2 2.5.2 2.6.2 2.8h-1l-.2-2.6-.2-2.6-.2-2.7-.2-2.7v-2.5l-.2-2.6v-2.5l-.2-2.4v-18.5l.3-2v-2.2l.3-2v-2.2l.3-2 .3-2 .3-2 .2-1.8.3-2 .4-1.8.4-1.8.3-1.8.5-1.7.4-1.7.3-1.7.4-1.6zm8.3-28.6v1l.4-.3-.2.4-.2.4-.2.6-.2.6-.3.7-.2.8-.2 1-.2.8-.3 1-.3 1-.2 1-.3 1-.3 1-.3 1.2-.4 1-.3 1.2-.3 1-.2 1.2-.3 1-.3 1.2-.3 1-.3 1-.3 1-.2 1-.3 1-.3 1-.2.8-.2.8-.2.7v.6l-.2.6v.5l-1-.3v-.5l.2-.7.2-.6.2-.8v-1l.4-.7.2-1 .2-.8.3-1 .2-1 .3-1 .2-1.2.3-1 .3-1.2.3-1 .4-1 .2-1.2.3-1 .2-1.2.3-1 .3-1 .3-1 .4-1 .2-1 .3-1v-.8l.4-.8.2-.7.3-.7v-.6l.3-.5.2-.5.5-.2zm-.5.2.2-.3h.3zm.5.3v-.5zm-.5-.3.2-.3h.3z"/>
<path fill="#fff" d="m336 370.4 6.4 7.4c-1.8 1.4 1.3 7-.4 18-1 6.4-4.8 13.8-5.6 22-2.5 26.5-2.5 59.8 5.4 81-14.5-1.6-19.5-14.8-20-39.6-.4-20.6-.4-44.7 4.7-72.7q1.5-9.7 9.4-16z"/>
<path fill="#21231e" d="m342 378.2-6.5-7.5.8-.6 6.5 7.5v.7h-.7zm.8-.7.4.4-.5.2v-.7zm-.3 18.5h-1l.2-1.2v-1l.2-1V385l-.2-.6v-1.2l-.2-.6v-1l-.2-.4v-2.8l.2-.3.3-.4v-.2l.7.8v.2h-.2v.5h-.2v2.2l.2.4v1.1l.2.6v1.1l.2.7v8.1l-.2 1v1l-.3 1zm-5.6 21.7h-1v-1.8l.2-.7.2-.7v-.7l.3-1v-.6l.3-.8.2-.7.2-.8.2-.7.2-.7.2-.7.2-.6.2-.7.2-.7.3-.7v-.7l.3-.6.2-.7.2-.7.2-.6v-.7l.3-.6.2-.6.2-.5.2-.7v-.6l.2-.6v-.6l.2-.6v-.5h1v1.2l-.3.6v.7l-.2.6v.7l-.3.7-.2.6-.2.6v.7l-.3.7-.2.6-.3.7-.2.6-.2.6-.2.7-.2.7-.2.7-.3.7-.3.7-.2.8-.2.7-.2.7v.7l-.2.6-.2.7v.7l-.3.8v.7l-.2.7v1.5zm4.8 81.6v-1l-.4.7-.8-2-.6-2-.7-2.3-.6-2.2-.5-2.3-.5-2.4-.5-2.4-.4-2.4-.4-2.5-.4-2.5-.3-2.5-.3-2.6-.2-2.7-.2-2.7-.2-2.6v-2.7l-.2-2.7V455l-.2-2.8v-10.9l.2-2.7V436l.2-2.8v-2.7l.3-2.6.3-2.7.2-2.6.2-2.6.2-2.4h1l-.3 2.6-.2 2.6-.2 2.5-.2 2.6v2.6l-.2 2.6v2.7l-.2 2.6v2.7l-.2 2.7V455l.2 2.7v2.7l.2 2.7.2 2.5.2 2.7.2 2.6.3 2.5.3 2.6.4 2.4.4 2.4.4 2.5.4 2.3.5 2.4.7 2.2.6 2.2.6 2.2.6 2 .7 2-.5.7zm.5-.6.3.7h-.8zm-21-39.5h1v2.2l.2 2.3v2l.2 2.2.2 2v2l.3 1.7.3 1.8.3 1.8.2 1.6.4 1.5.4 1.6.4 1.5.5 1.4.4 1.2.6 1.3.6 1.2.6 1 .7 1 .6 1 .8 1 .8 1 .8.6 1 .7 1 .7 1 .6 1 .5 1 .4 1 .4 1.3.3 1.3.2 1.3.2-.2 1-1.4-.2-1.3-.3-1-.4-1.3-.4-1.2-.5-1-.6-1.2-.5-1-.7-1-.7-.8-.7-1-1-.7-1-.7-1-.6-1-.7-1.2-.6-1-.5-1.4-.5-1.3-.5-1.5-.5-1.5-.4-1.5-.3-1.7-.4-1.5-.4-1.8-.3-1.8-.2-2v-2l-.3-2v-2l-.2-2.3v-2.2l-.2-2.3zm4.7-72.8 1 .2-.5 2.6-.4 2.6-.3 2.5-.4 2.6-.3 2.4-.4 2.5-.3 2.4-.3 2.4-.2 2.4-.2 2.5-.3 2.3v2.4l-.3 2.4-.2 2.3v2.4l-.2 2.2v2.3l-.2 2v4.5l-.2 2.2V459h-1v-26.9l.2-2.2v-2l.2-2.4v-2.3l.2-2.3.2-2.3.2-2.4.2-2.3.2-2.4.2-2.5.3-2.3.3-2.4.3-2.5.4-2.5.3-2.6.4-2.5.4-2.6.6-2.6.4-2.6zm10.3-16.3-.8.7h.7l-.4.5-.5.4-.5.4-.4.4-.4.5-.4.3-.4.4-.4.5-.4.4-.4.5-.3.5-.4.5-.3.5-.3.4-.3.4-.3.5-.2.4-.3.5-.2.4-.3.5-.2.4-.2.6-.2.5-.2.5-.2.6-.2.5-.2.7v.5l-.3.6v.6l-.2.6v.6l-1-.2v-.6l.2-.6.2-.6v-.6l.3-.6v-.6l.3-.5.2-.6.2-.6.2-.4.3-.6v-.5l.4-.5.2-.6.3-.5.2-.6.3-.5.3-.6.4-.5.3-.6.4-.5.2-.6.4-.4.4-.5.4-.4.4-.4.5-.4.4-.5.5-.4.4-.4.5-.6.6-.4h.7zm-.7 0 .4-.3.3.4z"/>
<path fill="#fff" d="m372.3 390-13.4-1.6c-2.6 12.7-4.5 21.7-5 26.2-1 9 1.8 17 5 23 5-3 7.2-9.4 9.2-18.6s2.7-19.2 4-29z"/>
<path fill="#21231e" d="m359 388 13.3 1.5v1l-13.5-1.6-.4-.7.5-.4zm-.6.3v-.4h.5l-.6.3zm-4 26.4h-1l.2-.6v-.8l.2-.6v-1l.2-.6v-.7l.3-.6v-.7l.2-.7v-.7l.3-.7v-1l.2-.7.2-.8.2-1v-.7l.3-1 .2-.8v-1l.2-1 .2-1 .2-1 .2-1 .2-1 .3-1 .2-1 .2-1.2.2-1.2v-1l.4-1.3 1 .2-.3 1.2v1l-.3 1.3-.3 1-.2 1.2-.2 1-.2 1-.2 1-.2 1-.2 1-.2 1v1l-.3 1-.2.8-.2.8v1l-.3.7v.8l-.3.7v.8l-.2.7-.2.7v.6l-.2.7v1.3l-.2.5v1.1l-.2.5v.9zm4.3 22.6.5.8h-.7l-.3-.7-.3-.6-.4-.6-.3-.5-.3-.6-.2-.7-.3-.7-.2-.6-.3-.7-.2-.7-.3-.7-.2-.8-.2-.7v-.7l-.3-.7-.2-.7-.2-.7v-.8l-.3-.7v-.7l-.2-.8v-.8l-.2-.7v-.8l-.2-.8v-6.1h1v6.6l.2 1v1.4l.3.7v1.6l.3.8.2.7v.8l.3.7.2.7.3.7.2.6.2.7.2.6.2.6.2.6.3.7.3.5.2.6.3.7.3.6.3.6.3.5-.7-.2zm.5.8-.5.4-.2-.5h.7zm8.6-19h1l-.3 1-.2.8-.2 1v.7l-.3.8-.2.7-.2.8-.2.7v.8l-.4.7-.2.7-.3.6v.7l-.4.6-.2.7-.3.6-.2.5-.3.5-.3.6-.3.5-.4.7-.3.5-.4.5-.5.4-.3.6-.4.4-.4.4-.4.4-.5.3-.4.5-.4.3-.4.3-.5-.7.4-.3.5-.3.4-.3.3-.4.4-.3.4-.4.4-.4.4-.5.3-.4.4-.4.4-.5.3-.4.4-.5.2-.4.3-.6.2-.5.3-.6.2-.7.3-.7.2-.6.3-.7.2-.6.2-.7v-.8l.3-.7.3-.7.2-.8.2-.8.2-.8.2-.8.2-.8.2-1zm4.5-28.5v-1l.5.5v1l-.3 1v.8l-.2 1v1l-.2.8v1l-.2.8v1l-.2 1v.8l-.2 1v1l-.2.8v1l-.2.8v1l-.2.8v1l-.2 1-.2.8v1l-.2.8-.2 1v.8l-.2 1-.2.8-.2.8v1l-.3.8v1l-.3.7h-1l.3-1v-.8l.3-1 .2-.7v-1l.3-.8v-1l.3-.8.2-1v-1l.3-.8v-1l.2-.8v-1l.2-.8v-1.8l.2-1v-1l.2-.8v-1l.2-.8v-1l.3-1v-1.8l.2-1v-.8l.2-1v-.8l.3-1v-1l.6.7zm0-1h.6l-.2.5zm0 0v1zm0 1"/>
<path fill="#fff" d="M407 418.6h-11.8q-.7 16.6-.5 22.3c.4 7.5 2.3 14 5.5 18.7 3.6-3 7-8.3 7.6-16.2.6-8-.6-16.5-.7-25z"/>
<path fill="#21231e" d="M395.2 418.2h12v1h-12l-.5-.5zm-.5.5v-.5h.5zm.5 22.2h-1v-11.4l.2-.8v-4.4l.2-.8v-5h1v7.8l-.2.7v5.6l-.2.7v7.7zm4.7 18.4.6.7h-.8l-.3-.5-.3-.4-.3-.4-.4-.5-.2-.4-.3-.5v-.4l-.4-.6-.2-.5-.3-.5v-.6l-.3-.6-.2-.5-.2-.6-.2-.6v-.5l-.2-.6-.2-.7-.2-.6v-.6l-.2-.6-.2-.6v-.7l-.2-.5v-.7l-.2-.7v-.5l-.2-.7v-2.6l-.2-.7h1v1.1l.2.7v1.4l.2.7v.7l.2.6v.7l.2.6v.6l.3.6v.7l.3.6v.7l.3.5v.6l.2.6.2.5.2.5.2.5.3.6.3.5.2.6.3.5.2.6.3.5.2.6.3.5.3.6.4.4.2.5h-.7zm.6.7-.5.5-.2-.5h.8zm6.7-16.6h1v1.6l-.2.7v1.4l-.3.8v.6l-.3.7-.2.7-.2.6-.2.6-.2.5-.2.5-.3.6v.5l-.4.7-.2.5-.3.5-.2.5-.3.4-.3.5-.3.5-.4.4-.3.3-.4.4-.4.4-.3.5-.4.3-.4.4-.4.3-.3.3-.4.3-.7-.6.2-.3.4-.2.3-.3.2-.4.3-.2.4-.3.2-.4.3-.3.3-.4.3-.4.4-.5.3-.4.2-.4.2-.4.3-.5.2-.5.3-.5.2-.5.3-.5.2-.6.2-.4v-.6l.3-.6v-.6l.3-.6v-.7l.3-.6v-1.4l.2-.7V444zm0-24.3v-1l.4.6v5.6l.2 1v4.6l.2.8v2.8l.2.8v9.2h-1v-11.8l-.2-.8V429l-.2-1v-3l-.2-1v-5.3l.4.5zm0-1h.4v.6l-.5-.5z"/>
<path fill="#fff" d="m420 424.2-12 .6q0 16.5.3 22.2a37 37 0 0 0 6.2 18.6c3.5-3 6.8-8.6 7-16.5s-1-16.3-1.5-24.8z"/>
<path fill="#21231e" d="m408 424.3 12-.6v1l-12 .6-.4-.5.5-.5zm-.4.5v-.5h.5l-.4.5zm1.2 22.2h-1v-4l-.2-.6v-17.8h1V445l.2.4zm5.4 18.2.6.8h-.7l-.2-.5-.3-.5-.3-.5-.3-.5-.4-.5-.3-.5-.3-.5-.2-.5-.3-.5-.3-.5-.2-.6-.2-.5-.3-.7-.2-.6-.2-.6v-.4l-.3-.6-.2-.6v-.7l-.3-.6-.2-.5v-.6l-.3-.8v-.6l-.2-.7v-.8l-.2-.6v-.7l-.2-.8v-2h.8v.7l.2.7v1.4l.2.7.2.7v.7l.2.7v.6l.3.6v.7l.2.6v.5l.3.6.2.7.2.5v.6l.3.6.2.5.2.5.2.5.2.5.2.6.2.6.3.5.2.6.2.4.3.5.3.5.3.4.3.5.3.3h-.8zm.6.8-.4.4-.3-.5h.8zm6.2-17h1v2.3l-.2.7v.7l-.2.7-.2.6v.7l-.3.6v.6l-.2.5-.2.6-.2.7-.2.6-.3.5-.3.6-.3.5-.2.4-.3.5-.4.4-.3.4-.3.5-.3.4-.3.4-.3.5-.2.4-.3.3-.3.4-.4.3-.3.4-.3.3-.4.3-.6-.8.3-.3.3-.4.3-.3.4-.3.3-.4.4-.3.4-.4.2-.5.3-.4.2-.3.3-.4.3-.5.2-.4.3-.5.2-.6.3-.5.2-.6.2-.5.2-.6.2-.6.2-.6.2-.5.2-.6v-.5l.3-.7v-.6l.2-.7v-.6l.2-.8zm-1-25.3v1l.5-.5v2.4l.2.8v1.5l.2.7v3.4l.2.7v1.5l.2.8v.8l.2.8v2.5l.2.7v2.3l.2.8v5.3h-1v-7.4l-.2-.7v-1.6l-.2-.8V437l-.2-1v-1.5l-.2-.7v-1.6l-.2-.8v-1.6l-.2-.8v-1.6l-.2-.8v-2.2l.4-.6zm-.5.6v-.6h.5zm.5.4v-1z"/>
<path fill="#fff" d="m395 412.4-12-.8q-1.8 16.4-2.2 22-.4 11.6 4 19.3c3.8-2.8 7.7-8 8.8-15.7 1.2-8 .8-16.4 1.4-25z"/>
<path fill="#21231e" d="m383 411 12 1v1l-12-1-.4-.5.5-.4zm-.4.5v-.5h.5l-.4.5zm-1.3 22.2h-1V431l.2-.4V429l.2-.6v-1.2l.2-.6v-2.2l.2-.7v-1.4l.2-.8v-.8l.2-.8v-1l.2-.8v-1l.2-.8v-1l.3-1v-1l.2-1v-1l1 .2v2l-.3 1v1l-.2 1v.8l-.2 1v.7l-.2 1v.8l-.2.8v.8l-.2.8v1.3l-.2.7v1.4l-.2.6v1.1l-.2.6v1.7l-.2.5v2.7zm3.2 18.8.5.8-.7-.2-.2-.4-.2-.5-.2-.4-.3-.5-.2-.4v-.5l-.3-.5-.3-.5v-.6l-.3-.5v-.7l-.3-.6v-.6l-.3-.4v-.6l-.3-.7V443l-.2-.5v-.7l-.2-.6V440l-.2-.8v-5.5h1v6.8l.2.7v.7l.2.6v.6l.2.5v.6l.2.7.2.6v.6l.3.5v.5l.3.6.2.5.2.7.2.5.2.5.2.5.2.5.3.5.2.5.2.5.2.4h-.7zm.5.8-.4.3-.3-.5.8.3zm8.2-16h1l-.2.8v.8l-.3.7v.7l-.3.7-.2.6-.2.6-.2.6-.3.6-.2.6-.3.6-.2.6-.3.5-.3.6-.3.5-.4.5-.3.5-.3.4-.3.5-.3.4-.4.6-.3.4-.4.4-.3.3-.4.4-.3.5-.4.3-.5.3-.3.4-.4.3-.4.3-.3.3-.5-.8.3-.3.4-.2.3-.3.4-.3.2-.3.4-.2.3-.4.3-.3.3-.3.3-.4.2-.4.4-.5.3-.4.2-.3.3-.5.3-.5.3-.4.4-.5.2-.5.3-.6v-.5l.4-.6.2-.5.2-.5.2-.6.2-.7.2-.7.2-.6.2-.7v-.7l.2-.7.2-.8zm1.8-24.4v-1l.5.5v1.5l-.2.8v5.6l-.2.7v9l-.2.8v2.2l-.2.8v.7l-.2.8v1l-.2.6v.8l-1-.2v-.7l.2-.8v-1.5l.2-.7V432l.2-.8V428l.2-.8V421l.2-.7v-4.8l.2-.8v-2.3l.6.5zm0-1h.5v.5z"/>
<path fill="#fff" d="m384 399.6-13.2-5.4c-2.2 8.8-5.2 37.8 0 50 4.5-3 9-8.4 10.3-17 1.6-9 2-18.2 3-27.6z"/>
<path fill="#21231e" d="m371 393.7 13 5.4-.3 1-13-5.4-.4-.6zm-.7.3.2-.5.5.2zm.3 50 .6.7-.7-.2-.5-1.2-.5-1.3-.3-1.4-.4-1.5-.3-1.5-.3-1.6-.2-1.7-.2-1.7v-1.8l-.2-1.8v-3.6l-.2-2V418l.2-2v-1.8l.2-1.8.2-1.8V409l.3-1.8.2-1.7V404l.3-1.6.2-1.5.3-1.4.2-1.3.2-1.2.2-1v-1l.3-1 1 .3-.2 1v.8l-.3 1.2-.3 1.2-.2 1.3-.2 1.4-.2 1.5-.2 1.6-.2 1.6v1.7l-.3 1.7v1.7l-.2 1.8v1.8l-.2 1.8v3.8l-.2 2v5.4l.2 1.8v1.7l.2 1.8.2 1.7.2 1.6.3 1.6.3 1.5.3 1.3.5 1.4.4 1.2.4 1h-.8zm.6.7-.5.3-.2-.5zm9.4-17.7h1v1l-.3.8-.2.8v.7l-.3.8-.2.8-.3.7-.2.7-.3.7-.3.5-.3.7-.2.6-.3.6-.3.6-.3.6-.5.5-.3.7-.4.5-.4.5-.3.5-.3.4-.4.5-.4.5-.5.4-.4.3-.3.4-.4.4-.4.4-.5.4-.5.3-.4.4-.4.3-.6-.8.4-.4.4-.3.4-.3.4-.3.4-.4.4-.3.4-.4.4-.4.4-.4.3-.4.4-.4.3-.5.3-.4.4-.4.3-.5.4-.4.3-.6.3-.5.3-.6.3-.6.2-.6.3-.7.3-.5.2-.6.3-.8.2-.7.2-.7.3-.7v-.7l.3-.7.2-.8v-.8zm3-27 .4-1 .4.6v1l-.2.8v1.7l-.2 1v1l-.2.8v1.8l-.2.8v1.7l-.2 1v1.7l-.2 1v1.6l-.2 1v.7l-.2 1v.8l-.2.8v1l-.2.7v.8l-.2 1v.8l-.2 1v.7l-.3.8v.8h-1v-.8l.2-1v-1.5l.2-.8v-1l.2-.8v-1l.2-.7v-1l.2-.7v-1l.2-.7v-1.6l.2-1v-.7l.2-1V410l.2-.8v-1.8l.2-.8v-1.8l.2-1v-1.5l.2-1v-.8l.2-1zm.4-1 .4.3v.3l-.4-.5zm0 .6-.3.4.2-.4zm0-.5.4.3v.3l-.4-.5z"/>
<path id="rs-c" fill="#edb92e" stroke="#21231e" d="M387.8 701.2c8 6.3 15.2-8.3 8-27.6-5-14.2-30.6-8.7-16.4 10.5.7 1 1.6.6 1.5-.4-1-4 1.6-6.3 5.5-5.5 10.5 2 11 22-.6 17.6-2.2-.2-.4 4 1.7 5.6z"/>
<path fill="#edb92e" stroke="#21231e" d="M400 639.2c-1 3.4-5 7.6-8 10.8-4.5 4.8-3.4 12.2-.2 16 3.5 4.4 5 9.2 5.8 13 .5 2.3 1.5 7.4.8 15.5-.4 4-2.7 6-5 7.7q-1.5 1.5 4.3 6c.4.2 1.5 1 2 3 .2.4 2 .4 2.2 0 .4-2 1.6-2.8 2-3 3.7-3 5.4-5 4.2-6-2.2-1.7-4.6-3.8-5-7.7-.6-8 .3-13.2.8-15.5.8-3.8 2.3-8.6 6-13 3-3.8 4.2-11.2-.5-16-3-3.2-7-7.4-8-10.7q0-1.1-.7-1-.6-.1-.7 1z"/>
<path fill="#edb92e" d="M395 685.5h11.6c2 0 3.6 1.6 3.6 3.5 0 2-1.6 3.5-3.6 3.5H395c-2 0-3.5-1.5-3.5-3.5s1.6-3.5 3.6-3.5z"/>
<path fill="#21231e" d="M406.6 686H395v-1h11.6zm4 3h-1v-1l-.2-.2v-.2l-.2-.2v-.2h-.2v-.2h-.2v-.2h-.3v-.2h-.2v-.2h-.3l-.2-.2h-.5v-.2h-.6v-1h1l.2.2h.4v.2h.3l.2.2h.2v.2h.2v.2l.3.2.2.2v.2l.3.2.2.3v.3l.2.3v.4l.2.2zm-1 0h1zm-3 4v-1h.8l.3-.2h.2l.2-.2.2-.2h.3v-.2l.2-.2v-.2l.2-.2.2-.3v-.3l.2-.2v-.8h1v1.2l-.2.2v.4h-.2v.3l-.2.3h-.2v.3h-.2v.3h-.3l-.3.2v.2h-.3l-.2.2h-.2l-.3.2h-.4l-.2.2h-.8zm-11.5-1h11.6v1H395v-1zm-4-3h1v1l.2.2v.3h.2v.2h.2v.2h.2v.2h.2v.2h.2l.3.2h.2l.2.2.3.2h.6v1h-1.2v-.2h-.3l-.2-.2h-.2l-.2-.2-.2-.2-.3-.2-.3-.3-.2-.3-.2-.3-.2-.2v-.2l-.2-.2v-.4l-.2-.2zm1 0h-1zm3-4v1H394v.2h-.3l-.2.2h-.2v.2h-.2v.2l-.2.2-.2.2-.2.2v.2l-.2.2v.5h-.2v.6h-1v-1.2l.2-.2v-.2h.2v-.3h.2v-.2l.2-.2v-.2h.2l.2-.2v-.2h.3v-.2h.3v-.2h.3l.3-.2h.2l.2-.2h.5l.2-.2h.7z"/>
<path fill="#fff" d="m417 579.2.5-11c-4.4-2.2-15.2 0-19.7 10.2a65 65 0 0 1-18 25.2c10 1.8 18.4-.2 23.7-7.4 7.3-9.8 11.2-15.7 13.6-17z"/>
<path fill="#21231e" d="m418 568.3-.4 11h-1l.4-11 .7-.5zm-.3-.5.3.2v.3zm-19.5 10.8-1-.4.6-1 .5-.8.5-1 .6-.7.5-.8.5-.8.7-.7.6-.6.7-.7.7-.5.7-.6.6-.4.7-.5.8-.4.8-.4.7-.3.8-.3.7-.2.7-.3h.7l.8-.3h1.2l.7-.2h2l.7.2h.5l.6.2.5.2.4.2-.4 1-.4-.3h-.5l-.5-.2h-.5l-.5-.2h-3l-.7.2h-.6l-.7.3-.7.2-.7.2-.7.2-.8.3-.7.3-.7.4-.7.4-.5.5-.7.5-.7.6-.5.6-.6.6-.7.7-.6.6-.5.7-.6.8-.5 1-.3.7zM380 603l-.2 1-.2-.8.6-.6.6-.5.7-.5.6-.6.7-.6.6-.6.5-.6.6-.6.6-.7.6-.7.5-.6.7-.7.5-.7.6-.7.5-.7.6-.8.4-.8.5-.7.5-.8.5-1 .5-.8.6-.8.6-1 .5-.8.6-1 .6-1 .5-1 .5-1 .5-1 .6-1 .5-1.2.5-1.2 1 .4-.6 1.2-.5 1-.6 1.2-.5 1-.4 1-.6 1-.5 1-.5 1-.6 1-.5 1-.6.8-.6.8-.5.8-.5.8-.6.8-.5.8-.6.7-.4.7-.6.8-.6.6-.5.7-.7.7-.6.6-.6.7-.4.6-.6.5-.6.6-.7.7-.6.5-.5.6-.6.6-.8.5-.2-1zm-.2 1h-1l.8-.8zm23.4-8 .8.5-.6.7-.5.6-.7.6-.6.6-.6.6-.5.5-.6.6-.7.5-.7.5-.7.4-.8.3-.7.4-.7.3-.7.3-.7.2-.7.3-.8.2-.8.2h-.8l-.8.2h-.8l-1 .2h-6.1l-1-.2-.8-.2-1-.2.2-1 1 .2.8.2h1l.8.2h6.6l.8-.3h.8l.7-.3.8-.2.7-.2.7-.3.7-.2.7-.2.8-.4.6-.3.7-.3.7-.4.6-.5.6-.5.5-.5.5-.5.6-.5.5-.6.7-.6.5-.7zm13.4-16.8h1l-.2.4-.2.2h-.2l-.3.3-.2.3-.3.2-.2.3-.3.3-.3.4-.3.3-.2.4-.4.5-.3.4-.3.3-.4.5-.3.5-.4.5-.5.6-.3.5-.4.5-.5.7-.5.6-.5.7-.5.7-.5.7-.5.6-.6.8-.6.8-.5.8-.6 1-.7.7-.7 1-.6.8-.8-.6.6-1 .7-1 .6-.7.7-1 .6-.7.6-.8.5-.7.5-.7.5-.7.5-.7.5-.8.5-.6.5-.6.4-.6.4-.6.4-.6.4-.5.4-.5.3-.5.3-.4.4-.5.3-.4.3-.4.3-.3.4-.3.3-.3.2-.2.3-.3.3-.2.4-.2h.2l.3-.2zm1 0v.3h-.2z"/>
<path fill="#fff" d="m425.2 580.6-11.8-5q-7.5 14.9-9.5 20.2c-2.8 7-3.5 13.8-2.3 19.5a22 22 0 0 0 14-11.6c3.8-7 6-15.3 9.5-23z"/>
<path fill="#21231e" d="m413.6 575.2 11.7 5-.3 1-11.8-5-.3-.8.6-.2zm-.7.2v-.4l.6.2zm-8.7 20.6-1-.4.2-.3.2-.4.2-.5v-.4l.2-.3.2-.5.2-.4.2-.5.2-.5.3-.5.2-.5.3-.6.2-.5.3-.6.3-.5.3-.7.2-.6.3-.6.3-.7.4-.8.3-.7.4-.8.2-.7.4-.8.4-.8.4-.8.4-.8.4-.8.4-1 .4-.8.5-.8.4-1 .8.5-.4.8-.5 1-.5.8-.4.8-.3 1-.4.7-.4 1-.5.7-.4.7-.3.8-.3.7-.4.6-.3.7-.3.7-.3.6-.2.7-.3.6-.3.5-.3.6-.3.6-.3.6-.2.5-.3.5-.3.5-.2.5-.2.4-.2.5v.5l-.2.4-.2.3v.3l-.3.4zm-2.7 18.8.2 1-.6-.4v-.5l-.2-.7v-.6l-.2-.5v-1.7l-.2-.5v-3.7l.2-.6v-1.2l.2-.7v-.6l.2-.5v-.7l.2-.6.2-.7v-.6l.3-.8v-.6l.3-.7.2-.7.2-.7.2-.7.2-.7.2-.7 1 .4-.3.6v.7l-.4.6-.2.6-.2.6-.2.7v.5l-.3.6v.8l-.3.6v.6l-.2.7v.6l-.2.6v.5l-.2.6v1.9l-.2.5v3.5l.2.5v1.2l.2.5v.7l.2.5-.6-.4zm.2 1h-.5v-.4zm13.5-12.4 1 .5-.5.6-.3.6-.4.6-.3.6-.4.6-.4.5-.5.6-.5.6-.4.5-.4.6-.5.4-.4.5-.5.4-.5.3-.4.4-.6.4-.5.4-.5.4-.5.3-.5.4-.5.3-.4.2-.5.2-.4.2-.5.2-.4.3h-.4l-.5.2-.5.2h-.5l-.4.3h-.5l-.2-1h.9l.4-.3h.4l.4-.3h.5l.4-.3.5-.2.4-.2.5-.2.5-.2.5-.3.5-.3.4-.3.4-.4.4-.3.5-.4.4-.4.4-.4.6-.4.4-.5.4-.4.5-.6.3-.4.4-.5.4-.6.5-.6.4-.5.2-.7.4-.6.4-.6.3-.6zM425 581l.3-.8.3.6-.3.7-.3.8-.3.7-.3.7-.3.8-.2.7-.3.7-.2.7-.3.7-.3.8-.3.7-.2.6-.3.8-.4.7-.3 1-.2.6-.2.7-.3.8-.3.8-.2.7-.3.7-.3.8-.3.7-.2.7-.3.7-.3.8-.3.7-.5.7-.3.7-.3.6-.5.6-.3.7-1-.6.5-.6.3-.7.3-.6.3-.6.4-.7.3-.6.3-.7.3-.7.2-.7.3-.7.2-.7.3-1 .3-.6.3-.7.3-.7v-.8l.4-.7.3-.7.3-.8.2-.7.3-.7.3-.8.3-.7.3-.7.3-.7.3-.7.2-1 .3-.6.3-.8.3-.7.4-.8.3-.8zm.3-.8.5.2-.2.4z"/>
<path fill="#fff" d="m428.6 580.6-12.7-2q-3.7 16.1-4.4 22-1.4 11.3 2.7 19.4c4.3-2.3 8.8-7 10.7-14.7 2-7.8 2.2-16.3 3.6-24.7z"/>
<path fill="#21231e" d="m416 578.2 12.6 2v1l-12.8-2-.4-.6zm-.6.4v-.5l.6.2-.6.4zm-3.3 22h-1l.2-.5V599l.2-.6v-.5l.2-.6v-.5l.2-.6v-.6l.2-.5v-.6l.2-.6v-.6l.2-.6.2-.7v-.6l.3-.7v-.7l.2-.8v-.8l.3-.8.2-.8v-.8l.3-1 .2-.7.2-1 .2-.8.2-1 .2-1 .2-1 .2-1 1 .3-.2 1-.2 1-.2 1-.2 1-.2.8-.2 1-.2.7-.2 1-.2.7v.8l-.3.8-.2.8v.8l-.2.7v.6l-.2.7-.2.7v.6l-.2.6-.2.7v.4l-.2.6v.6l-.2.5v1.1l-.2.6v.4l-.2.4v1.2zm2 19 .5.8-.7-.2-.2-.5-.3-.5-.2-.5v-.6l-.3-.4-.2-.5-.2-.5-.2-.6v-.6l-.2-.6v-.6l-.3-.6v-.6l-.2-.6v-.6l-.2-.6v-2.8l-.2-.6v-5.4l.2-.7v-1.5h1v3.1l-.2.7v5.1l.2.6v1.2l.2.6v.6l.2.6v.6l.3.6v.5l.2.6v.7l.3.5v.5l.3.6.2.5.2.5.2.5.3.5.2.5-.7-.2zm.5.8-.5.3-.2-.5zm10-15.2 1 .2-.2.7-.2.8v.7l-.3.7-.3.7-.2.5-.3.6-.4.7-.3.6-.3.6-.3.4-.3.6-.4.5-.3.5-.4.5-.4.5-.4.4-.4.5-.4.3-.4.4-.4.4-.4.5-.4.3-.4.4-.4.3-.4.3-.4.3-.4.4-.5.3-.4.3-.4.2-.5.2-.5-.8.4-.2.4-.3h.4l.4-.4.4-.3.4-.3.4-.3.4-.3.4-.3.4-.3.3-.3.4-.4.4-.4.5-.4.3-.4.4-.5.3-.4.4-.6.2-.5.3-.6.3-.5.3-.6.4-.6.3-.5.3-.6.2-.6.3-.5.2-.6.3-.8.2-.7.2-.6zm4-25v1l.5-.5v.8l-.2.8v.7l-.2.8v.8l-.2.8v.8l-.2.8v.7l-.2.8v.8l-.2.8v1.7l-.2.7v.8l-.2.8v.8l-.2.8v.7l-.2.8v1l-.2.6v.8l-.2.8-.2.7v.7l-.2.7-.2.8v.7l-.3.7-.2.7-1-.2.2-.8.2-.7v-.7l.2-.8v-.7l.3-.7v-.8l.2-.8v-.7l.2-.8v-1.5l.2-.8v-.7l.2-.8v-1l.2-.7v-.7l.2-.8v-1.6l.2-.8v-.8l.2-.7v-1l.2-.7v-.8l.2-.8v-.8l.2-1v-.7l.3-.8.5-.4zm-.4.3v-.4h.5l-.4.5zm.5.6v-1z"/>
<path fill="#fff" d="M430.4 563.4h7.6c4.4 2 9 12.2 3.7 22a66 66 0 0 0-9.5 29.2c-7.4-6.8-10.8-14.7-8.2-23.3 3.4-11.7 6.8-25.2 6.4-28z"/>
<path fill="#21231e" d="M438 564h-7.6v-1h7.8zm0-1h.2zm4 22.5-.8-.5.5-1 .4-.8.4-.8.3-1 .3-.8.2-1v-.7l.3-1V574l-.2-1v-.6l-.3-.8-.2-.7-.3-.8-.2-.7-.3-.6-.3-.7-.3-.6-.3-.6-.3-.4-.3-.5-.4-.3-.4-.4-.3-.4-.4-.4-.5-.3-.4-.3-.3-.2.5-1 .4.4.5.2.5.4.4.4.4.4.4.4.4.5.4.6.3.6.3.6.3.6.3.7.2.7.3.7.2.6.2.8v.7l.3 1v.8l.2.8v3.5l-.2 1v1l-.4.8-.3 1-.3 1-.4.8-.4 1-.5 1zM432 615l.5-.7-.8.3v-.8l.2-1v-2.3l.3-1v-.7l.2-.8.2-1 .2-.8v-1l.2-.7.3-1 .2-.8.2-1 .2-.7.3-1 .3-.8.3-1 .4-1 .4-.8.4-1 .4-1 .4-1 .4-1 .5-1 .4-1 .5-1 .5-1 .5-1.2.6-1 .6-1.2 1 .5-.7 1-.6 1.2-.7 1-.5 1-.5 1.2-.5 1-.4 1-.5 1-.5.8-.4 1-.3 1-.3 1-.4.8-.3 1-.3.8-.2.8-.3 1-.4.8-.2.8-.2 1-.2.7-.2 1v.7l-.3.8v1l-.3.7v.8l-.2.8v1l-.2.7v1.8l-1 .2zm.7-.3v1l-.8-.7.7-.3zm-9.2-23.6 1 .4-.3.8-.2.8v.8l-.2.7v.8l-.2.8v3l.2.8.2.8v.7l.3.7.3.7.2.8.3.7.4.7.4.6.3.7.5.7.4.7.4.7.5.6.4.7.5.7.4.6.6.7.6.6.6.7.7.6.5.7-.6.7-.8-.6-.7-.7-.6-.7-.7-.6-.6-.7-.6-.7-.4-.7-.6-.7-.4-.7-.5-.8-.3-.7-.5-.7-.3-.8-.5-.7-.3-.7-.3-.8-.4-.7-.2-.7-.2-.8-.2-.7v-.8l-.2-.8v-.8l-.2-.8v-2.5l.2-.8v-.8l.3-.8.3-1 .2-.7zm7-28v1l.4-.7v1.8l-.2.5v1.1l-.2.7-.2.7v.8l-.3.8v.8l-.2 1-.2.8-.2 1-.3 1v1l-.4 1-.2 1-.3 1-.2 1-.2 1-.3 1.2-.3 1-.3 1.2-.3 1-.3 1.2-.3 1.2-.3 1-.3 1.2-.2 1-.4 1.2-1-.3.4-1 .3-1 .5-1.2.3-1 .3-1.2.4-1.2.3-1 .3-1.2.3-1 .3-1.2.3-1 .2-1 .2-1 .3-1.2.2-1 .2-1 .2-1 .2-.8.2-1 .2-.8.2-.8v-.8l.3-.7v-.8l.2-.7v-.6l.2-.6v-2l.4-.6zm-.6.5v-.6h.4l-.5.5zm.4 0v-.6.4zm-.5 0v-.6h.4l-.5.5z"/>
<path fill="#fff" d="m433.8 558.2-12-4.3q-6.5 15-8.4 20.5-3.4 11-1 19.6a21 21 0 0 0 14-11.6c3.3-7.3 4.5-16.3 7.4-24.2"/>
<path fill="#21231e" d="m422 553.4 12 4.3-.4 1-12-4.4-.3-.6zm-.7.3.2-.4h.4l-.7.4zm-7.4 21-1-.3v-.4l.2-.4v-.4l.3-.4v-.4l.3-.5.2-.5v-.5l.3-.5.2-.5.2-.6.3-.6.2-.5.2-.6.3-.6.2-.6.2-.6.3-.7.3-.6.3-.7.4-.7.3-.8.3-.8.3-.7.4-.8.3-1 .4-.8.3-1 .4-.8.5-1 .4-.8.3-1 1 .5-.5 1-.4 1-.4.8-.3 1-.4.8-.4.8-.4.8-.3.8-.4.8-.4.8-.3.7-.3.8-.3.8-.3.7-.3.6v.8l-.4.6-.2.6-.3.6v.6l-.3.6-.2.5-.2.5-.2.5v.5l-.3.5-.2.4v.5l-.3.4v.3l-.2.3v.4zm-1.8 19 .3 1-.6-.4-.3-.6v-.5l-.2-.6v-.5l-.2-.5v-.6l-.2-.7V583l.2-.7V581l.3-.6v-.7l.2-.6v-.6l.3-.6.2-.7v-.6l.3-.7.2-.6.2-.6 1 .3-.3.6-.2.7-.2.7-.2.6v.7l-.2.6v.7l-.2.6-.2.6v.6l-.2.6v1l-.2.7v6.5l.2.6v1.1l.2.4.2.6v.5l.2.5zm.3 1h-.5v-.4l.5.3zM426 582l.8.6-.3.6-.4.7-.2.5-.4.6-.4.7-.4.6-.4.5-.4.5-.4.5-.5.5-.5.5-.5.4-.4.4-.5.4-.4.4-.4.4-.5.3-.5.5-.5.3-.5.3-.5.4-.4.2-.5.3h-.5l-.5.4-.5.2-.5.2-.5.2h-.4l-.6.3-.4.2h-.5l-.3-.8.4-.2h.5l.5-.3.5-.2h.4l.5-.3.4-.2.4-.3.5-.2.5-.2.5-.3.4-.3.5-.3.5-.4.4-.3.6-.3.4-.4.5-.3.4-.4.4-.5.4-.4.4-.4.4-.4.4-.5.4-.5.4-.5.4-.5.4-.6.3-.6.3-.6.3-.6.3-.7zm7.6-23.4.4-1 .3.7-.3.8-.3.8-.2.8-.3.7-.2.8-.2 1-.2.6-.2.8-.3.8v.8l-.3.7-.2.8-.2.8-.2.8v1l-.3.6-.2.8-.2.8-.2.8v.7l-.3.8-.2.8-.3.7-.2.8-.2.7-.3.8-.3.7-.2.7-.3.8-.3.7-.3.7-.2.6-1-.5.4-.5.3-.7.3-.7.3-.6.3-.7.3-.8.2-.8.2-.7.3-.8.2-.7.2-.8.2-.7v-.8l.4-.8.2-.8.2-.8.2-.8.2-.8.2-.7.2-.7.2-.8.2-.8.2-.8.2-1 .2-.6.2-.8.3-.8.3-.8.2-.7.3-.8.3-.8.3-.7.3.6zm.4-1 .4.3v.3zm-.2.6-.2.4zm.2-.5.4.2v.3l-.4-.6z"/>
<path fill="#fff" d="m425.6 549.7-2.6-16.4c-2.8-.7-6.5 5.8-10 9.5q-1.6 1.3-2.6 2.8a23 23 0 0 0-3.4 8 62 62 0 0 1-13 27.6c10.2-.2 18-3.8 21.8-12 5-11 7.7-17.7 9.8-19.5"/>
<path fill="#21231e" d="m423.4 533.3 2.7 16.4h-1l-2.5-16.3.6-.6.4.5zm-.3-.4h.4v.2l-.3-.4zm-9.7 10.2-.8-.7.4-.4.2-.2.3-.4.4-.4.2-.4.3-.4.4-.4.2-.5.3-.4.4-.5.2-.4.3-.3.4-.5.2-.4.3-.4.4-.3.2-.3.3-.4.3-.3.4-.3.3-.4.3-.2.3-.2.4-.2.4-.3h.3l.4-.3h.3l.4-.2h1.1l-.2 1h-1l-.3.2-.2.2-.3.2-.3.2-.3.2-.3.3v.4l-.5.3-.3.3-.3.3-.4.4-.3.4-.3.5-.4.4-.3.3-.3.4-.3.4-.4.5-.3.5-.3.4-.4.4-.3.4-.4.4-.3.4-.4.4-.4.4-.3.4zm-2.5 2.7-.8-.7v-.2h.2l.2-.2v-.2h.2v-.2h.2v-.2h.2v-.2h.2v-.2l.2-.2h.2v-.2l.2-.2h.2v-.2h.2v-.2h.2v-.2l1 .6h-.2l-.2.2v.2h-.2v.2h-.2l-.2.2v.2h-.2l-.2.2v.2h-.2v.2h-.2v.2h-.2v.2h-.2v.2l-.2.2v.2zm-3.3 7.7-1-.2v-.6l.2-.4v-.3l.2-.3v-.8l.2-.3v-.3l.2-.3v-.3h.2v-.4l.2-.3v-.2l.2-.2v-.2l.3-.3v-.2l.2-.2v-.2l.3-.2v-.3h.2v-.3l.3-.3v-.2l.3-.2v-.2l.2-.3.2-.2.8.6-.3.3v.2l-.2.2-.2.2v.4l-.2.2-.2.2v.3l-.2.3v.2l-.2.3-.2.2v.2l-.2.3v.2l-.2.3v.2l-.2.3v.2l-.2.3v.5l-.2.3v.2l-.2.3v.5l-.2.3v.7zm-13.4 27v1l-.3-.8.5-.8.5-.7.5-.6.5-.8.5-.7.5-.7.4-.6.6-.7.4-.7.5-.8.4-.7.4-.7.4-.8.4-.7.4-.7.4-.8.4-.8.4-.8.4-1 .4-1 .3-.8.3-1 .3-.8.4-1 .3-1 .4-1 .3-1 .3-1.2.3-1 .3-1.3.2-1.3.3-1.2 1 .2-.3 1.2-.3 1-.4 1.3-.3 1.2-.4 1-.4 1-.3 1.2-.4 1-.4 1-.4 1-.3 1-.5.8-.4 1-.3.8-.4.8-.4 1-.5.8-.4.8-.3.7-.5.8-.4.8-.5.8-.4.7-.6.7-.5.7-.5.7-.5.7-.5.8-.5.7-.5.6-.5.8-.5.7-.4-.8zm0 1h-1l.7-.8zm21.3-12.8 1 .4-.5.7-.4 1-.4.6-.4.7-.5.7-.4.6-.5.6-.5.6-.5.6-.6.5-.6.4-.6.5-.7.4-.6.4-.8.4-.7.4-.7.4-.7.3-.7.3-.8.3-.8.2-.8.2-1 .2-.7.2-1 .2-.7.2h-1l-.8.2h-1l-.8.2h-2v-1h2.8l1-.2h.8l.8-.2 1-.2h.7l1-.3.7-.2.7-.3.8-.2.7-.3.7-.3.7-.3.6-.3.7-.3.6-.4.7-.5.6-.4.6-.4.5-.4.6-.6.5-.5.5-.4.5-.6.5-.6.4-.7.5-.6.4-.6.3-.7.3-.8zm9.8-19.2h1v.3l-.3.3v.2l-.3.2-.2.2-.2.2-.2.3-.3.4-.2.2-.2.4v.4l-.4.5-.2.5-.3.5-.2.5-.3.6-.2.6-.3.6-.3.6-.4.7-.3.8-.3.8-.4.7-.3.8-.4 1-.4.7-.4 1-.4.8-.4 1-.4 1-.5.8-.3 1-.5 1-1-.4.6-1 .4-1 .5-1 .4-1 .5-1 .4-.8.3-1 .4-.7.4-.8.4-.8.4-1 .3-.6.4-.7.4-.7.3-.6.4-.7.3-.6.2-.4.2-.6.3-.5.2-.5.3-.4.2-.5.3-.3.2-.4.2-.3.2-.4.2-.3.2-.3.2-.2.3-.2.3-.2-.2.4zm1 0v.3z"/>
</g>
<use xlink:href="#rs-d" width="1350" height="900" transform="matrix(-1 0 0 1 964.3 0)"/>
<path fill="#fff" stroke="#21231e" d="m536.4 415 4.8-1.5a66 66 0 0 1-21.4-22c-7.8-12 3.3-33 8-44.3a53 53 0 0 0 21.2-31c1-7.3-19-4.7-33 2-17 8-19.3 5.7-25.4 7-2.2 10.5 2.4 11.4 6.7 11-9.4 15-13.3 29.8-15 51.4-2-21.6-6-36.5-15.3-51.5 4.3.6 9-.3 6.7-10.7-6-1.4-8.4.8-25.4-7.2-14-6.5-34-9-33-1.8 3 13.7 11.8 24 21.3 31 4.6 11.2 15.7 32.4 8 44.2a67 67 0 0 1-21.5 22q2.5 1 5 1.5c-1.2 1.5-11 4.5-12.3 3.7-7.6-5.3-15-18-21.4-25.2-18.2-20.8-27.3-36.7-24-49.6 4.5-14 20.8-29.6-.8-27-21.7 2.8-39.6 32.5-45.4 68.3l-4.7 33.5c11.8-4.3 17-8.5 20.2-13.7q-2.1 7.5-3.2 14.2c9.7-1.7 16.7-6.4 19.2-15q1.6-5.3 3.6-9c3 1 7 1 11.6 0 1.7 4 15 19 22.2 30 8 12.2 20.2 21.6 24.2 22 2 .4 2.7-4 2.2-10.2 21 .2 42.3.3 63-.4q31.1.9 63 .4c-.6 6 0 10.6 2 10.3 4-.5 16.2-10 24.3-22.2 7-10.8 20.5-25.7 22.2-30a20 20 0 0 0 11.6.2q2 3.7 3.5 9c2.5 8.6 9.5 13.3 19.2 15q-1-6.6-3.2-14c3.2 5 8.4 9.3 20.2 13.6-1.4-9-2.7-20.3-4.8-33.5-5.8-35.8-23.7-65.5-45.4-68.2-21.6-2.6-5.3 13-.7 27 3.3 12.8-5.8 28.7-24 49.5-6.5 7.3-13.8 20-21.4 25.2-1.2.8-11-2.2-12.2-3.8z"/>
<g id="rs-e">
<path fill="#edb92e" d="M440.6 356.8c-2 1.8-8.8 1.8-18 2.4q-22.3.7-25.3-13.5c5.6 6.6 12.2 10 19.7 8 10.2-2.5 16-4.3 18.3-3.8 1.6.4 4 3.3 5.3 6.8z"/>
<path fill="#21231e" d="M422.6 359.7v-1h1.8l.7-.2h2.5l.8-.2h2.8l.8-.2h3l.5-.2h1l.4-.2h.5l.4-.2h.4l.5-.2h.3l.3-.2h.4l.3-.2h.2v-.3h.3l.6.7h-.3l-.3.3-.3.2h-.2l-.3.3h-.4l-.3.2h-.4l-.4.2h-.5l-.5.2h-1l-.5.2h-1.5l-.6.2H432l-.6.2h-2.8l-.8.2h-2.3l-1 .2H423zm-25-14.3-.7.6.8-.4.2 1 .2.7.3.8.3 1 .3.6.5.7.4.7.4.6.5.6.4.6.6.6.6.5.6.4.7.5.6.3.8.5.8.4.8.3 1 .4.8.2 1 .3 1 .2 1 .2h1l1 .3h1.2l1.2.3h6.6v1H416l-1-.2h-1.3l-1-.3h-1.2l-1-.3-1-.3-1-.3-1-.2-1-.4-.8-.3-.8-.4-1-.4-.7-.5-.7-.5-.7-.5-.7-.6-.6-.5-.6-.6-.4-.8-.5-.7-.3-.7-.4-.8-.4-.7-.4-1-.3-.7-.3-1-.2-.8 1-.4zm19.3 8 .2 1h-.8l-.7.2h-1.4l-.7.2h-2.8l-.6-.2-.7-.2h-.7l-.6-.3h-.7l-.6-.3-.8-.3-.6-.3-.6-.2-.6-.3-.6-.4-.6-.3-.6-.4-.6-.4-.6-.6-.6-.4-.6-.5-.5-.6-.6-.6-.4-.6-.6-.5-.5-.6-.6-.7.7-.6.5.6.5.6.6.6.5.5.5.5.6.5.4.5.6.5.6.4.5.5.7.4.6.3.6.3.4.3.6.2.6.3.6.2.7.2h.5l.6.3.7.2h.6l.7.2h4.7l.6-.3h.7zm18.5-4-.3 1h-2.6l-.4.2h-.9l-.5.2-.5.2h-.5l-.6.2h-.5l-.6.3h-.5l-.7.3-.7.2h-.6l-.7.3-.7.2-.8.2-.8.2h-.8l-.8.3-1 .3-.8.2-1 .2-.8.3-.3-1 1-.2.8-.2 1-.2.7-.2 1-.2.7-.2.8-.2.7-.2.8-.2.7-.2.7-.2.6-.2h.6l.6-.3h.6l.6-.3h.6l.5-.2h.6l.5-.2h.6l.4-.2h.9l.3-.2zm5.4 7.8-.7-.8-.2.6v-.7l-.3-.3v-.3l-.3-.3v-.3l-.3-.2v-.3l-.2-.2-.2-.3v-.3l-.3-.2-.2-.3v-.2l-.2-.2-.2-.3v-.2l-.3-.2h-.2v-.3h-.2l-.2-.3v-.2h-.3l-.2-.2v-.2h-.2v-.2h-.4v-.2h-.2l.3-1h.2l.2.2.2.2h.2l.2.2.2.2h.2v.3l.3.2.2.2h.2l.2.3v.2l.2.2.2.2.2.2.2.2.2.3v.2l.3.2.2.3.2.3v.3l.3.4.2.3v.3l.3.3v.3l.3.3v.3l.2.3v.6zm0-.6.2.3-.3.2v-.6zm-21.5-35.4v1.5h-.4v-.4h-.2v-.7l.2-.2h.2v-.2zm15.3 4-.5 1.3-.6-.2-.5-.2h-.6l-.5-.2-.4-.2-.5-.2-.4-.2-.5-.2-.4-.2h-.5l-.4-.3-.5-.2h-.4l-.4-.3h-.5l-.4-.3h-.5l-.4-.2h-.4l-.5-.2h-.4l-.3-.2h-1.3l-.4-.2H420v-2h2.9l.5.2h1l.4.2h.5l.3.2.5.2h.5l.4.2.5.2h.5l.4.3h.6l.5.3.6.2h.5l.6.3.5.2.6.2h.5l.6.3.6.2.5.2.6.2h.5zm9 2.5v1.5H442l-.3-.2h-.4l-.3-.2h-.3l-.4-.2h-.3l-.4-.2h-.4l-.3-.2h-.5l-.3-.2-.4-.2h-.3l-.4-.2h-.2l-.3-.2h-.3l-.3-.3h-.7l-.2-.2h-.4l-.2-.2.4-1.5h.2l.2.2h.2l.2.2h.3v.2h.4l.3.2h.3l.3.2h.4l.3.2.3.2h.4l.3.2h.4l.3.2h.4l.4.2h.3l.4.2h.4l.3.2h.6l.3.2zm0 1.5v-1.5h.4v.2h.2v.2h.2v.9h-.2v.2h-.2v.2h-.2zm23.4 7.5.2-1.2h.2v.2h.2v.8h-.6zm-10-2 .4-1.2h.5l.3.2h.4l.3.2h1.1l.2.2h.6l.2.2h.6l.3.2h.7l.4.2h.8l.3.2h.9l.2.2h.9l.5.2h.8l-.2 1.3h-.9l-.4-.2h-.6l-.4-.2h-.7l-.4-.2h-1.4l-.3-.2h-.7l-.3-.2h-.6l-.3-.2h-.5l-.4-.2h-.5l-.3-.2h-.5l-.2-.2h-.5l-.2-.2zm.4-1.2-.3 1h-.2v-.8h.8z"/>
<path fill="#21231e" d="m431.5 332-.7 1.3-.4-.3-.4-.2-.4-.3-.3-.2-.4-.3-.4-.3-.3-.3-.4-.3-.4-.2-.3-.4-.3-.3-.2-.3-.3-.3-.3-.4-.2-.3-.3-.4-.2-.3-.3-.4-.2-.4-.2-.3-.3-.5-.2-.4-.2-.2v-.4l-.3-.4-.2-.3-.2-.4-.2-.3v-.4l-.2-.3-.2-.3-.2-.4 1.4-.7.2.2.2.4v.3l.3.2.2.4.2.3v.3l.3.3.2.4.2.3.2.4v.3l.3.4.3.3.2.4.2.4.2.4.3.3.2.4.3.4.2.3.3.4.3.3.3.3.3.2.3.3.3.3.2.2.4.3.3.2.2.2.3.2zm7.6-5.3 1.5.8-.2.4-.2.3-.2.4-.2.4-.3.5-.3.4-.2.2-.3.4-.3.3-.3.2-.3.3-.3.3-.3.3-.2.4-.3.2-.3.2-.4.2-.3.2-.3.2h-.3l-.5.2h-.3l-.3.2h-2.2l-.4-.2-.3-.2h-.2l.7-1.5h.5l.2.2h1.7l.3-.2h.2l.3-.2h.2l.3-.3.2-.2.3-.2h.2l.3-.3.2-.3.3-.2.2-.3.3-.4.2-.3.3-.3.2-.4.2-.3.3-.4.2-.5.2-.4v-.3z"/>
<path fill="#21231e" d="m429.6 331-2.2-2.3q-1.6-1.9 2-5.4l4.2 2q-1.4 4.5-4 5.6zm-6.3 82-.4 1h-.4v-1zm23.8-3.6 1 1-.7.5-.6.4-.7.5-.6.4-.7.5-.7.3-.7.4-.7.4-.7.4-1 .3-.6.3-.8.2-.7.2-1 .2-.7.2h-.8l-.8.2h-5l-1-.2h-.8l-.8-.3h-1l-.8-.4h-1l-.7-.3-1-.3-.8-.3.3-1 .8.2 1 .3 1 .3.7.2 1 .2.7.2h.8l1 .2h.7l.7.2h4.8l.7-.2h1l.6-.3.7-.2h.7l.7-.4.7-.3.7-.3.8-.3.6-.4.7-.4.7-.5.6-.5.6-.5.5-.5zm3-2.8 1 .8h-.2v.2h-.2l-.2.2v.2h-.2v.2h-.2v.2h-.2l-.2.2-.2.2v.2h-.2v.2h-.2l-.2.2-.2.2-.2.2-.2.2v.2h-.2l-1-.8h.2v-.2h.2v-.2h.2l.2-.2.2-.2.2-.2v-.2h.2v-.2h.2l.2-.2.2-.2.2-.2v-.2h.2l.2-.2.2-.2v-.2zm1 .8-1-.8.2-.2h.7v.2h.2z"/>
<path fill="#21231e" d="m452.2 401.3-1.2-.2v-.2h.2l.2-.2h.6v.4h.2zm-5.5 16 .3 1-.7-.8.2-.4.2-.4.2-.5v-.4l.3-.4.2-.5.2-.5v-.5l.3-.4.2-.5.2-.5v-.5l.3-.5v-.5l.3-.5v-.5l.3-.5v-.5l.3-.5v-.5l.2-.5.2-.6v-.5l.2-.6v-.5l.2-.4v-.6l.2-.6v-.5l.2-.7v-.6l.2-.6 1.2.3v.6l-.2.5v.6l-.2.6v.6l-.2.6v.5l-.2.6-.2.4v.6l-.2.5v.6l-.3.6v.5l-.3.6v.6l-.3.5v.5l-.2.5-.2.5v.5l-.3.5-.2.5v.5l-.2.4-.2.5-.2.6-.2.5-.2.6-.2.5-.2.5-.2.5-.7-.8zm-.4.2 1 .5v.2h-.2v.2h-.4l-.2-.2v-.2h-.2v-.4zm15.2-9.5 1 .7-.3.4-.4.6-.3.4-.4.4-.2.4-.4.4-.4.4-.4.4-.4.3-.4.4-.5.3-.4.4-.6.3-.4.3-.5.3-.5.3-.5.3-.5.2-.5.3-.5.3-.6.2-.5.3-.7.2-.5.3-.6.2-.6.2-.6.2-.6.2-.6.2-.6.2-.6.2-.6.2-.3-1.2.6-.2h.6l.5-.3.6-.2.6-.2.6-.2h.6l.5-.3.6-.3.4-.2.5-.2.4-.3.5-.2.4-.3.5-.3.4-.4.5-.3.3-.3.5-.3.4-.3.4-.3.4-.2.4-.4.4-.3.4-.3.4-.3.3-.4.3-.4.3-.4.3-.4.2-.4.3-.4zm1 .7-1-.7h.2v-.2h.7v.2h.2z"/>
<path fill="#21231e" d="M461.3 400.2H460v-.2h.2v-.2h1v.4zm4.4 15.7.7 1-.8-.3-.3-.4-.3-.4-.2-.5-.3-.5-.2-.4-.3-.4-.2-.5-.3-.5-.2-.4-.2-.5v-.5l-.3-.5-.2-.5-.2-.5-.2-.5v-.5l-.2-.5-.2-.5v-.6l-.3-.5v-.5l-.3-.6v-.4l-.2-.6v-.6l-.2-.5v-.7l-.2-.6v-.6l-.2-.5v-1.2h1v.5l.2.6v.6l.2.5v1.4l.2.5.2.6v.5l.2.5v.6l.3.5v.4l.2.5.2.4v.5l.2.4.2.5.2.4.2.5.2.4.2.4.2.5.2.4.2.5.3.4.3.4.2.4.3.5.2.3.3.4.3.3h-1zm0 .7 1-.7v.8l-.2.2h-.2v.2h-.4v-.2h-.2zm7.6-12.6 1 .4v.6l-.2.5v.5l-.2.5-.2.5-.2.5-.2.5-.2.5-.2.4-.2.4-.2.4-.3.5v.4l-.4.4-.2.5-.3.4v.3l-.4.3-.3.4-.3.4-.2.3-.3.3-.3.4-.3.3-.4.3-.3.3-.3.3-.3.2-.3.3-.3.4-.3.2-.3.3-.7-1 .3-.3.3-.2.3-.3.3-.2.2-.3.3-.3.2-.2.3-.3.3-.4.3-.3.2-.4.3-.4.4-.3.2-.4.3-.4v-.4l.4-.3.2-.5.3-.4v-.3l.3-.4.2-.4.2-.6.2-.4v-.5l.3-.4.2-.4.2-.5v-.4l.3-.5v-.4zm1 .4-1-.3v-.2h.8v.2h.2z"/>
<path fill="#edb92e" d="M406 317.8c-16.7 1.8-16 12.6-.4 28 3 3 4 3 4.6-.2 1.2-5.3 7-8.5 10.2-4.4 2 2.7 1.6 4.3 8.6 3.3 9 7.5 16.2 12.8 4.5 12.3-5-.2-9 2.8-15.7 2.5 2 4 2.3 4.3 3.4 4.6 6.5 1.2 13.3-2.8 19.8-1.6 14.3 2.6-23-38.6-22.4-40.3 1-4-9-11.5-12.7-4.2z"/>
<path fill="#21231e" d="m406 345.5-.7.7-1.4-1.5-1.5-1.4-1.2-1.4-1.2-1.4-1-1.3-1-1.3-.8-1.3-.8-1.2-.8-1.2-.6-1.2-.5-1-.5-1.2-.3-1-.3-1v-1l-.2-1v-1l.2-1 .2-.8.4-.8.4-.8.6-.7.6-.7.8-.6.8-.6 1-.5 1-.5 1-.4 1.4-.4 1.3-.3 1.3-.2 1.6-.2v1l-1.5.2-1.4.2-1.2.3-1.2.3-1 .4-1 .5-1 .4-.6.5-.7.7-.6.6-.4.6-.4.6-.4.7-.2.8-.2 1v1.7l.2.8.2 1 .4 1 .4 1 .5 1 .5 1.3.7 1 .8 1.3 1 1.4.8 1.2 1 1.3 1.2 1.3 1.2 1.3 1.4 1.4 1.4 1.5zm3.8 0 1 .2v.3l-.2.3v.5l-.2.2v.2l-.2.2v.2l-.2.2-.3.3v.2h-.3l-.2.2H408l-.2-.2h-.2l-.2-.2h-.2l-.2-.3-.2-.2h-.2l-.3-.4h-.2l-.2-.3-.3-.3-.2-.2.7-.7.2.2.3.3.2.2.3.2.2.2.2.2h.2l.2.2.2.2h.3v.2h.6l.2-.2v-.2l.2-.2v-.2l.2-.4v-.4l.2-.3zm11-4.6-.8.5-.3-.4h-.3l-.3-.4-.2-.2-.3-.2h-.3l-.3-.2h-.5l-.3-.2h-1.5l-.3.2-.4.2h-.2l-.4.3-.3.2-.2.2-.4.2-.3.2-.2.3-.3.3-.2.4-.3.3-.2.4-.3.5-.2.4-.2.3v.5l-.2.4v.5l-1-.2v-.5l.2-.5.2-.5.2-.5.3-.5.2-.4.2-.4.3-.4.3-.4.3-.4.4-.3.4-.3.4-.3.4-.2.3-.2.3-.2.5-.2h.4l.4-.2h.4l.4-.2h1l.4.2h.6l.4.3.4.3.3.2.4.3.4.5.4.4zm8.6 3.2-.7.7h.4-.5l-.6.2h-4.5l-.2-.2h-.3l-.2-.2h-.2l-.2-.2-.2-.2-.2-.2h-.2v-.3l-.3-.3v-.2l-.3-.2v-.2h-.3l-.2-.3-.2-.3v-.2l-.3-.2v-.3l-.3-.2.8-.6h.2v.4l.3.2.2.3.3.3v.2l.2.2.2.2.2.2.2.2.2.2h.2v.2h.3l.2.2h.2l.2.2h5.4zm-.4 0 .2-.2.2.2zm4.5 13v-1h4.1l.5-.2h.4l.3-.3h.2v-1l-.2-.3-.2-.4-.3-.5-.4-.4-.5-.4-.4-.4-.4-.5-.6-.5-.6-.6-.6-.6-.7-.5-.6-.6-.7-.7-.7-.7-.8-.6-.8-.7-.8-.7-1-.7.8-.8.8.7.8.5.8.7.8.7.7.5.8.6.8.7.7.6.6.6.7.4.6.6.5.5.5.5.4.5.4.4.4.4.3.4.2.4v2.3l-.4.3-.3.2-.4.3-.6.2h-.6l-.7.2h-3.6zm-15.2 2-1 .4.5-.8h3.4l.6-.2h.5l.5-.2h.5l.5-.2h.5l.4-.2h.5l.5-.2.4-.2h.6l.4-.2.5-.2h.4l.4-.2h.5l.3-.2h.5l.4-.2h.5l.5-.2h2.6v1H431l-.4.2h-.9l-.4.3h-.8l-.4.2h-.5l-.4.3h-.6l-.5.2h-.5l-.5.3h-.5l-.5.2h-.5l-.5.2h-.5l-.6.2h-.6l-.6.2h-3l.5-.7zm-1 .4-.3-.8h.8l-.4.8zm4 4-.2.8h-.4v-.2h-.2l-.2-.2h-.2v-.2h-.2v-.2h-.2v-.2l-.2-.3-.2-.2-.3-.3-.2-.2v-.2h-.2v-.4l-.3-.2v-.3h-.3v-.4l-.3-.3v-.4l-.3-.4 1-.5v.5l.2.3.2.3v.4l.3.3.2.2v.2l.3.2v.2l.2.2v.2h.2v.3l.2.2v.2l.2.2h.2v.2h.4v.2h.4zm19.8-1.7v1l-.7-.2h-4.7l-.6.2h-.7l-.6.2h-.7l-.6.2-.6.2h-.6l-.6.2h-.7l-.7.3-.6.2h-.6l-.6.2h-.6l-.6.2h-4.6l-.6-.2.3-1 .6.2h4.8l.6-.2h.6l.5-.2h.6l.7-.2.7-.2h.6l.6-.2.6-.2h.7l.5-.2h.6l.7-.2h.7l.6-.2h4.9zm-23-40 1 .3.3.3.5.7.6.8.8 1.2 1 1.2 1 1.3 1 1.5 1.3 1.5 1.3 1.7 1.4 1.8 1.4 1.8 1.5 1.8 1.5 2 1.5 1.8 1.4 2 1.4 2 1.3 1.7 1.3 2 1.2 1.7 1 1.7 1 1.6.8 1.5.6 1.4.4 1.3.3 1.2v2l-.6.7-.8.5H441v-1h2l.4-.3.3-.4V360l-.3-1.2-.4-1.2-.6-1.3-.8-1.5-1-1.6-1-1.7-1-1.8-1.3-1.8-1.4-2-1.4-2-1.4-1.8-1.5-2-1.4-1.8-1.5-1.8-1.4-1.8-1.3-1.8-1.3-1.6-1.2-1.6-1-1.5-1.2-1.3-1-1.2-.7-1-.6-1-.5-.7-.3-.4v-.6zm-12-3.7v-1l-.5.2.4-.6.3-.7.5-.6.5-.4.5-.3.6-.3.5-.2.5-.2h2.6l.6.2.6.2.5.3.6.2.6.3.4.4.5.2.4.4.4.4.5.5.4.4.3.4.3.4.3.5.2.5.2.5v.5l.2.5v1l-1-.3v-1l-.2-.4v-.4l-.3-.4-.2-.4-.3-.4-.3-.4-.5-.4-.3-.4-.5-.4-.4-.4-.5-.3-.4-.3-.5-.3h-.7l-.5-.3-.5-.2h-.5l-.6-.2h-2l-.4.3-.5.2-.4.3-.5.5-.4.4-.3.4-.3.6-.4.3zm.4-.3-.2.2h-.2zm-.5-.2v.5zm.4.2-.2.2h-.2z"/>
<path fill="#21231e" d="m407 317.2-1.3.4v-.6h.2v-.2h.8v.2h.2v.2zm7.6 10.6-.8 1-.4-.4-.3-.4-.3-.3-.3-.3-.4-.3-.3-.2-.3-.4-.3-.3-.2-.2-.3-.4-.3-.3-.3-.2-.4-.4-.3-.3-.2-.2-.2-.4-.2-.3-.3-.3-.2-.3v-.4l-.4-.3-.2-.4-.2-.4-.2-.4-.2-.4v-.4l-.3-.3-.2-.4v-.3l-.3-.3v-.4l-.3-.4 1.2-.4v.7l.3.3v.3l.3.4v.3l.3.4v.3l.3.4.2.3.2.3.2.4v.3l.3.3.2.4.3.3.2.3.3.4.2.3.3.3.3.3.4.4.2.3.3.3.2.3.3.3.4.2.2.3.3.3.4.4.2.3.4.3zm-.8 1 .8-1v.2h.2v.7h-.4v.2zm-9.5-4.6q1.3.1 1.2 1.5c-.1 1.4-.8 1.3-1.5 1.2q-1.3-.4-1.2-1.6c.1-1.2.8-1.3 1.5-1.2zm-45.9 38.4h.2v-.2h.4v.2h.2v.2l.2.2v.4h-.2zm-22.7 56 .2-1V416l.4-1.5.3-1.5.4-1.7.6-1.8.5-2 .7-2 .6-2 .6-2 .6-2.2.8-2.2.7-2.2 1-2 .7-2.3 1-2.3.7-2.2 1-2.2.8-2 1-2.2.8-2 1-2 .8-2 1-1.7.8-1.5 1-1.6.7-1.5 1-1.3.7-1.2.8-1 .8-.8.8-.6.7 1-.6.5-.7.8-.7 1-.8 1-1 1.3-.7 1.5-1 1.5-.7 1.7-1 1.7-.8 2-1 1.8-.8 2-1 2-.8 2.3-1 2-.7 2.3-1 2.3-.7 2.3-.8 2.2-.7 2.2-.8 2.2-.6 2-.6 2-.6 2.2-.6 2-.5 1.8-.4 1.8-.4 1.6-.5 1.6-.2 1.4-.2 1.3v1H336zm5.6 80.5-.7-2-.7-2-.7-2.2-.6-2.2-.6-2.2-.4-2.4-.4-2.3-.5-2.4-.4-2.5-.3-2.4-.3-2.5-.3-2.7-.2-2.6-.3-2.6-.2-2.5-.2-2.6v-3l-.2-2.7v-18.5l.3-2.7v-2.5l.2-2.6v-2.7l.3-2.6.2-2.5.2-2.5h1.2l-.3 2.6-.2 2.5-.2 2.6-.2 2.6v2.6l-.2 2.6v2.6l-.2 2.7v18.8l.2 2.6v2.6l.3 2.6.3 2.6.3 2.6.3 2.5.2 2.5.3 2.5.4 2.4.3 2.5.5 2.3.5 2.3.5 2.3.6 2.2.6 2 .7 2.2.7 2-1 .4zm1-.4v.8h-.8v-.2l1-.6z"/>
</g>
<use xlink:href="#rs-e" width="1350" height="900" transform="matrix(-1 0 0 1 964.3 0)"/>
<path fill="#21231e" d="M473 398.4h-.2v.5h-.2v.8l.2.7v1.6l.2.8.2.8v.7l.3.8v.8l.3.7.2.8.3.7.2.7.2.7.3.7.3.8.3.7.3.7.2.7.3.6.4.6.2.7.4.5.4.7.4.7.4.5.5.6.4.8.5.6.4.6.5.6.6.6.2.2h.8v-.2l.6-.6.5-.6.5-.6.4-.6.5-.7.5-.5.4-.6.3-.8.4-.6.3-.7.4-.8.4-.6.3-.7.4-.8.3-.7.3-.7.3-.7.3-.6.2-.7.3-.7.3-.7.2-.8v-.7l.3-.7.2-.7v-.7l.3-.8v-1.4l.2-.8v-2.1h-1v.4l-.2 1v1.4l-.2.7v.7l-.2.7-.2.7v.8l-.3.7-.2.7-.2.7v.6l-.3.7-.3.7-.2.7-.3.7-.4.6-.2.7-.4.7-.3.6-.3.7-.4.6-.4.7-.3.6-.4.6-.5.6-.4.6-.4.7-.4.7-.4.6-.5.6h-.2l-.5-.7-.4-.6-.4-.6-.4-.6-.4-.6-.4-.6-.4-.6-.4-.6-.3-.7-.4-.6-.4-.7-.3-.6-.3-.7-.4-.7-.2-.6-.3-.7-.2-.7-.2-.7-.2-.7-.2-.7-.2-.8-.2-.7-.2-.7v-.7l-.3-.6v-.8l-.2-.7v-2.3l-.2-.8v-.4h-.2v-.2h-.5z"/>
</g>
<g transform="translate(-32 32)scale(.53333)">
<path fill="#c6363c" d="M549.2 422v108.6a72 72 0 0 1-19.7 49.8 65 65 0 0 1-94.7.1 73 73 0 0 1-19.7-49.8V422z"/>
<use xlink:href="#rs-f" width="1350" height="900" transform="matrix(-1 0 0 1 964.3 -90)"/>
<path fill="#fff" stroke="#21231e" d="M549.2 488.6v28h-53v83a64 64 0 0 1-14 1.5 59 59 0 0 1-14-1.5v-83h-53v-28h53V422h28v66.6z"/>
<use xlink:href="#rs-f" width="1350" height="900" transform="translate(0 -90)"/>
<path id="rs-f" fill="#fff" stroke="#21231e" d="M459 567.3V528c-8.4-2.3-29.3-10.6-30.3 7.6-.5 8.4 10.4 14 13.3 6.5 1.4-3.4 0-6-2-6.7-2.4-.8-4.6 1-4 3.6-8.8-4.7 8-14.6 15.5-1.7 2.2 3.7-3.7 7.6-3.7 10.4 0 2.7 5.8 7 3.7 10.3-7.7 12.8-24.3 3-15.5-1.6-.6 2.7 1.6 4.5 4 3.6 2-.8 3.4-3.3 2-6.8-3-7.5-13.8-2-13.3 6.5 1 18.2 22 10 30.4 7.6z"/>
<use xlink:href="#rs-f" width="1350" height="900" transform="matrix(-1 0 0 1 964.3 0)"/>
<path fill="#21231e" d="M414 421v113.3l.2 1.7v2l.4 1.7.2 1.8.3 1.7.4 1.8.3 1.8.4 1.7.6 1.7.5 1.7.6 1.5.6 1.7.5 1.6.7 1.6.7 1.6.7 1.6.8 1.6.8 1.5.8 1.5 1 1.5.8 1.5 1 1.4.8 1.4 1 1.4 1 1.4 1 1.3 1 1.5 1.2 1.2 1 1.3 1.2 1.2 1.2 1.2 1.2 1.2 1.2 1 1.3 1.3 1 1 1.4 1 1.3 1 1.4 1 1.5 1 1.4 1 1.3.8 1.4.8 1.5.8 1.4.8 1.6.7 1.5.6 1.5.7 1.5.6 1.6.5 1.6.5 1.5.5 1.6.5 1.8.4 1.6.4 1.7.4 1.8.2 1.7.3 1.7.2h1.7l1.6.2h8.9l1.7-.3 1.8-.3 1.7-.3 1.7-.2 1.7-.4 1.5-.4 1.7-.4 1.6-.5 1.6-.5 1.6-.5 1.5-.6 1.6-.7 1.6-.7 1.6-.7 1.5-.8 1.4-.8 1.6-.8 1.4-1 1.5-.7 1.4-1 1.2-1 1.4-1 1.3-1 1.2-1 1.3-1 1.2-1.3 1.2-1 1-1.3 1.3-1.2 1.2-1.2 1-1.3 1.2-1.2 1-1.4 1-1.2 1-1.4 1-1.4 1-1.4 1-1.4.7-1.5 1-1.5.7-1.5.8-1.5.8-1.6.7-1.6.6-1.6.6-1.6.6-1.6.5-1.7.5-1.6.5-1.8.4-1.7.6-1.7.3-1.8.2-1.8.3-1.7.3-1.8v-.9l.3-2 .2-1.7V421zm2.3 2.2H548V536l-.3 1.6-.2 1.8-.2 1.7-.3 1.8-.3 1.7-.4 1.7-.3 1.7-.5 1.5-.4 1.7-.4 1.6-.5 1.6-.5 1.6-.6 1.5-.8 1.6-.7 1.6-.7 1.5-.7 1.6-.7 1.4-.8 1.5-.8 1.4-1 1.3-.8 1.4-1 1.3-1 1.3-1 1.3-1 1.3-1 1.2-1.2 1.2-1 1.2-1.2 1-1.2 1.3-1.2 1-1.2 1-1.2 1-1.3 1-1.2 1-1.4 1-1.4 1-1.3.8-1.4 1-1.5.7-1.4.8-1.3.7-1.5.8-1.5.6-1.4.7-1.5.7-1.6.5-1.5.6-1.5.5-1.6.4-1.6.4-1.6.5-1.6.3-1.6.3-1.7.4H489l-1.8.3h-1.7l-1.6.2h-3.6l-1.6-.2H477l-1.6-.3h-1.6l-1.7-.4-1.5-.2-1.6-.3-1.7-.4-1.6-.3-1.6-.4-1.4-.5-1.6-.6-1.5-.5-1.5-.6-1.5-.6-1.4-.6-1.4-.8-1.4-.7-1.4-.8-1.4-.8-1.4-1-1.3-.7-1.4-1-1.3-1-1.2-1-1.3-1-1.2-1-1.2-1-1-1-1.3-1.3-1-1-1.2-1.3-1-1.2-1-1.3-1.2-1.4-1-1.3-1-1.3-1-1.3-.8-1.4-.8-1.4-1-1.5-.7-1.5-.8-1.4-.7-1.5-.8-1.4-.6-1.5-.6-1.5-.6-1.5-.5-1.6-.6-1.6-.5-1.6-.5-1.7-.4-1.6-.4-1.8-.4-1.7-.3-1.7-.3-1.7-.2-1.6-.2-1.8-.2-1.7v-5.4z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 177 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ru" viewBox="0 0 512 512">
<path fill="#fff" d="M0 0h512v170.7H0z"/>
<path fill="#0039a6" d="M0 170.7h512v170.6H0z"/>
<path fill="#d52b1e" d="M0 341.3h512V512H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-se" viewBox="0 0 512 512">
<path fill="#005293" d="M0 0h512v512H0z"/>
<path fill="#fecb00" d="M134 0v204.8H0v102.4h134V512h102.4V307.2H512V204.8H236.4V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 223 B

+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-si" viewBox="0 0 512 512">
<defs>
<clipPath id="si-a">
<path fill-opacity=".7" d="M60.2 0h497.3v497.3H60.2z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="1pt" clip-path="url(#si-a)" transform="translate(-62)scale(1.0295)">
<path fill="#fff" d="M0 0h994.7v497.3H0z"/>
<path fill="#d50000" d="M0 331.6h994.7v165.7H0z"/>
<path fill="#0000bf" d="M0 165.8h994.7v165.8H0z"/>
<path fill="#d50000" d="M282 90.3c-3.9 59.9-6.1 92.7-15.2 107.9-9.9 16.3-19.5 28.2-58 42.6-38.4-14.4-48-26.3-57.9-42.6-9-15.2-11.3-48-15.2-107.9l5.7-1.9c11.4-3.5 20-6.3 26.3-7.5 9-2 16.7-4.1 41-4.6 24.3.4 32 2.7 41 4.6q9.6 2.1 26.6 7.5z"/>
<path fill="#0000bf" d="M276.4 88.3c-3.7 59.8-6.7 87.2-11.6 100.3-9.3 22.6-24.1 35-56 46.7-31.8-11.8-46.6-24.1-56-46.7-4.8-13.1-7.9-40.4-11.4-100.3q16.8-5.4 26.3-7.5c9-1.9 16.7-4.2 41-4.6 24.3.4 32.1 2.7 41.1 4.6q9.6 2.1 26.6 7.5"/>
<path fill="#ffdf00" d="m208.8 106.6 1.5 3.7 6.7.9-4.3 2.6 4.2 2.8-6.1 1-1.9 3.3-2-3.4-6-.8 4-2.9-4-2.6 6.4-1z"/>
<path fill="#fff" d="m262.5 174.5-3.7-3-2.7-4.4-5.2-4.6-2.8-4.6-5.2-4.7-2.6-4.6-2.8-2.3-1.9-1.7-4.7 4.1-2.6 4.6-3.3 2.9-3.5-2.8-2.7-4.7-10-17.7-10 17.7-2.6 4.7-3.6 2.8-3.2-3-2.6-4.5-4.7-4.1-1.9 1.7-2.8 2.3-2.6 4.6-5.2 4.7-2.8 4.6-5.3 4.6-2.6 4.4-3.7 3a64 64 0 0 0 18 35.2c6.4 6.1 19.5 14.4 35.5 19.9a101 101 0 0 0 35.7-20 64 64 0 0 0 18.1-35.1"/>
<path fill="#ffdf00" d="m224.8 81.5 1.5 3.6 6.7 1-4.3 2.5 4.2 2.9-6.1 1-1.9 3.3-2-3.4-5.9-.8 4-3-4.1-2.5 6.4-1zm-32 0 1.5 3.5 6.7 1-4.3 2.6 4.2 2.8-6.1 1-1.9 3.3-2.1-3.4-5.8-.8 4-2.9-4.1-2.6 6.4-1 1.5-3.6z"/>
<path fill="#0000bf" d="M254.2 197.2H247l-6.7-.5-8.1-4-9.2.1-7.9 3.9-6.2.5-6.3-.5-7.9-3.9h-9.1l-8.1 4-6.7.4h-7.4l-3.5-6 .1-.2 10.9 1.8 6.6-.5 8.1-4h9.2l8 4 6.2.5 6.2-.6 8-3.8h9l8.2 3.9 6.7.5 10.5-1.9.2.3zm-84 9.3 7.2-.5 8.1-4h9.1l8 3.9 6.2.6 6.2-.6 8-3.9h9.1l8.1 4 7.3.5 4.7-5.8-.2-.2-5 1.5-6.7-.5-8.1-4h-9.1l-8 4-6.2.5-6.2-.5-8-4h-9.1l-8.1 4-6.7.5-5-1.2v.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-sk" viewBox="0 0 512 512">
<path fill="#ee1c25" d="M0 0h512v512H0z"/>
<path fill="#0b4ea2" d="M0 0h512v341.3H0z"/>
<path fill="#fff" d="M0 0h512v170.7H0z"/>
<path fill="#fff" d="M203.2 395.5c-45.9-22-111.5-66-111.5-152.8s4.1-126.2 4.1-126.2h214.8s4.2 39.4 4.2 126.2S249 373.4 203.2 395.5"/>
<path fill="#ee1c25" d="M203.2 384c-42.1-20.3-102.3-60.5-102.3-140.2s3.8-115.8 3.8-115.8h197s3.8 36.2 3.8 115.8-60.2 120-102.3 140.2"/>
<path fill="#fff" d="M212.2 223c11.4.2 33.7.6 53.5-6 0 0-.6 7-.6 15.3s.6 15.3.6 15.3a172 172 0 0 0-53.5-6v44h-18v-44a172 172 0 0 0-53.5 6s.6-7 .6-15.3-.6-15.3-.6-15.3c19.9 6.6 42.1 6.2 53.5 6v-27.7a128 128 0 0 0-42.3 6.1s.5-7 .5-15.3-.5-15.4-.5-15.4c17 5.7 31.9 6.2 42.2 6-.5-17.4-5.6-39.4-5.6-39.4s10.5.8 14.7.8 14.7-.8 14.7-.8-5.1 22-5.7 39.5a126 126 0 0 0 42.3-6s-.5 7-.5 15.3.5 15.3.5 15.3c-17-5.7-31.9-6.1-42.3-6z"/>
<path fill="#0b4ea2" d="M203.2 280.8c-21.2 0-32.6 29.4-32.6 29.4s-6.3-14-23.6-14c-11.7 0-20.3 10.5-25.8 20.2 21.3 33.8 55.3 54.7 82 67.6 26.7-12.9 60.7-33.8 82-67.7-5.5-9.6-14.1-20-25.8-20-17.3 0-23.6 14-23.6 14s-11.4-29.5-32.6-29.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ua" viewBox="0 0 512 512">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="gold" d="M0 0h512v512H0z"/>
<path fill="#0057b8" d="M0 0h512v256H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 232 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-xk" viewBox="0 0 512 512">
<path fill="#244AA5" d="M0 0h512v512H0z"/>
<path fill="#D0A650" d="m202.3 245.7 1.3-.7 1.6-.5s.8-.4 1.2-.4 1.3.5 1.5.9c.3.4 1.7 1 1.7 1s1 .4 1.1 0q.3-.4 1-.5c.7-.1.6-.4.6-.7q0-.5.5-1.1c.3-.4 1-.6 1-.6s1-.4 1-1 1.1-1 1.7-1c.5-.1 0-.4 1.3-.2 1.4.1 1.6-.3 1.7-.7 0-.5 0-.9.9-.8q1.3.3 1.3-.4c0-.4.8-.7-.6-1-1.4-.5-1.3-.2-1.4-.8s0-.6-1-.7-.4-.5-.5-1q-.1-.6-.5-1c-.2-.4-.7-.4-.7-.8q.2-.8-.6-.8c-.8 0-.7 0-.7-.4q0-.8.6-.9.9-.2.7-.9v-1.5l.6-.8 2.3-2c.2-.3.9-.6 1.2 0q.4.9.8 1.2c.2.3 1 0 1.2-.2.2-.4.2-1.5.7-2.2q.8-.9.9-1.4 0-.9.9-1c.9-.1.9-.7.9-1.1s.2-.9 1-1c.6 0 1-.5 1.2.1s.4.7.9.9q.7.5 1.4.2c.4-.3.5-.8 1-.5q.8.4 1-.6l.2-1.5s.1-.4 1-.4.2-.5.6-.8c.5-.4.5-.7 1-.5s1.5-.2 1-.7c-.4-.6-1.4-1.4-.9-2 .6-.6.8-1.7.3-1.8s-1.4.1-1.5-.5-.3-1 .3-1.2c.7 0 1.9 0 2.2-.4s1.5-.5 1.5-1.4c0-1 .3-1 .4-1.6.1-.5.1-1.6-.3-2.1l-1.3-1.6a2 2 0 0 0-1.2-1q-1.2-.2-1.3-.8c0-.6.5 0 .5-1.3s-1-1.6-1-1.6l-1.3-.8s-2.4-.6-3-.6-1.4-.7-.7-1.1q1.2-.8.7-1.8c-.5-1-.5-1-1.5-1.3q-1.5-.3-.9-1.1c.5-.6.6-1 1.2-.6q.9.7 2.2.2 1-.6 1.4-.2c.3.3.8.7 1.4.2.5-.6 1.5-.6 2-.4.5.3 1.6.2 2 0s1-.8 1-1.1.5-1 1-1.2c.4-.3 1-1.1 1.5-1.1s.6-.5.6-.9.4-1 .5-1.2c0-.3 1-.7 1.6-.1s1.5.7 1.5.7h1.4q.8-.4 1.8 0c.6.5 1.6.5 1.6.5l.1-1q.2-.7-.1-1.2c-.4-.3-.9-.6-.1-1.2.7-.5.7-.4 1.4-.4l1.8.1c.6 0 1.5.5 1.5 1s.4.9.8 1.1q.9.4.9 1-.4.6.3.7.9 0 .8.6-.2.7.7.9t.8 1l.1 1c.1.4.8 1.1.8 1.1s1 1.2-.3.6-1.6-.8-2-.7l-.8.3-1 .2c-.6 0-1 1-.3 1.1q1.1.1 1 1c-.1.9-.2.4-.6.8s-.7.2-1.3.2q-1 .1-.8 1c.2.9.2.4.5.8q.5.7 1.2.1c.5-.4.9-.7 1.1.1s.7.8 1.2.8q.8 0 1.2-.3.8-.3 1.2.7.1.9 1 1 .6.4.8.9.2.7.8.3.9-.4 1.5 0l.8 1v.8l1.8.6.2.8q0 .6.7.4h1.3s.7-.3.7-.8.3-.6.7-.6c.5 0 1.5.3 1.8-.2q.6-.8 1.7-.4l2.4.9s-.2 0 .3 1c.6.8 1.6.8 1.6.8l.3 1.1c.2.3 1 .5 1.5.2q.6-.7 1.6-.8 1 0 1-.3c0-.3-.6-.6-.6-1.2s.6-.5.7-1 .6 0 1 .3q.6.4.8 1c.2.6.8 1.5 1.2 1.7.5.2 1.7.3 2-.2s1.3-.6 1.3.5c-.1 1-.3 1 .3 1.4s.8 1.6.2 1.9q-.7.3-.7 1-.2 1.1.2 1.6.8.5.8 1c0 .5.5 1.9.5 1.9s.3 1.5.2 1.8-.2 1 .6.9q1.1-.3 1 .6c-.1.9.3 1-.2 1.2s-.7 1-.3 1.3q.8.2 1 .7c0 .4.8 1 1.3.6q.4-.5.9-.6.7 0 .3-.7-.6-.7-.1-1.2c.4-.2.5-1 .5-1s.6-.8 1.2 0c.7.7.8.4 1.2.4q.7.1.7.9c0 .8 1 .3 1 1l.1 2.3s0 1 .6 1.3.4.4.1.9q-.2.7-.1 1.4c.1.7.1.5-.3.9q-.8.5-.4 1.2c.3.6.2.9 1 1.5.7.6.6.9.6 1.3s.7 1 .7 1.4c0 .5.6 1 1 1s1 .9 1 1.3l.2 1.6s-.1.2.4.2 1 .6 1.1 0q.5-.9 1.1-1c.4-.2 1.2-.5 1.5-.3q.7.4 1.2.3h2.2s.4 0 .7.7 1.1 1.4.7 1.8c-.5.3-.5 1 0 1q1-.1 1.2.4-.1.8.5.5.8-.4 1 0t1 .6q.5.4.9-.2c.3-.4 1.4-.7 1.4-.7s1.5-.3 2-.2l1.3.5s.4.9.2 1.4-.4.4-.2 1.2q.1 1-.5.8-.7-.3-.8.6c-.1.9-.4.8-.4 1.4s0 .5-.3.8q-.7.5 0 .8c.6 0 .4.6.4 1v1s0 .5-.6.5q-1.1-.1-1.3.4c-.2.5-.8 1-.2 1.5q.9.6.1.8c-.4.2-.6 1 0 1.3q1 .4.3.9c-.5.3-1.4.6-1.4 1q0 .7-.8.7-1.2.1-.1 1.6c.6.9.8.7 1.3.7s.4-.7.4-.7h2q.9 0 1.5-.2c.4-.2 1.4-.7 1.4.1s.2.7.7 1 .5.1 1 .6q.4.6.6-.5c.3-.6.9-1.2 1.3-.5s.5.5 1 .5q.9 0 .8.7c-.1.7.6.4 1.2.3s.7.4 1 .8q.3.5.8.4.5.1 1-.4c.3-.3.6-.8 1-.2s1.1.4 1.3-.2 1-1.1 1.5-.4q.6.7 1.3.7t1.4.5c.3.5 1.2 1.2.5 1.7s-1.4 1.2-.8 1.3q.9.2 1.3.8c.4.4 1.6.6 2.1.7s1 .2 1.7-.4c.6-.5 1.3-.5 1.2.2s.3 1.5.3 1.5.4 1 .4 1.5q-.2.4.8 1c1 .6.7.9 1.4.4q.9-.8.8-1.2v-1.4s1-.2 1.3-.4 1.9-1 2.2-.7.9.2 1.2 0h1.5q.6.2 1.8-.6c.7-.6.7-.4 1.2 0s.4 1 .2 1.3 0 1 .6 1q1 0 .8.9l.2 1.3c.1.4.1.5 1.1.8 1 .2.9.8 1 1.3q.4.6-.3.7-.9 0-.8.7c-.1.4-1 .6-1 .6l-.8-.2s-.6 0-.8.4c-.3.3-.9 1-.5 1.6q.6.9 0 1.2c-.4.3-.6 1.5-1 1.3s-.8 0-1.1.2-.3 1.4.4 1.8q1.1.9 1 1l.8 1.3-.5.7c-.2.4-.7 1.3-.2 1.5s.3 1-.2 1-1.2-.2-1.3.5-.4.5-1 .4-2-.4-2.3.3q-.3 1-1 .8c-.7 0-.5 1-.3 1.5s.9 1 .9 1 .5.4.5 1.3-.4.8-.7.2-1.1-.5-1.2.1-.2 1.7-.9 1.7q-.8 0-1.2.6c-.2.3-.7.9-.7 1.2s-.1.7-.6.8c-.4 0-.5 1-.5 1s-1.1.2-1.2.6 0 1.1-.9 1.2q-1.3.3-1.7 1.1-.5.7 0 1.2c.4.4.6 1.8 1 2q1 .3.8 1c0 .8-.6 1.3.1 1.3q1.2 0 1 .8c-.3.6-.3.6-.9.9-.5.3-.8.3-.8 1s.2.6-.3.9q-.6.4-.5 1c0 .6-.1.4-.7 1s-1.3.6-1.8.4q-.5 0-.6-.6c-.1-.6-1-.6-1.4-.5-.5.1-1.1.6-1.1.6s-.8.5-.5 1q.5.8-.4 1c-.7 0-2.2.7-2.6 0-.5-.9-1.8-1.4-2-.3s.6.9.5 2c-.1 1-.3 1-.7 1.3q-.4.4-.4 1c-.1.4-.2 1.4.4 1.7s1 1.3 1 1.8 0 1 .6 1q1.1.2 1.3-.7c.1-.6 1.4-.9 1.5.3s0 1.8.6 2.3l1.2 1.5c.6.6 1 2.4 1 2.4-1 .2-1.2 1.4-1.8 1.6l-1 .2-.5.7c-.7.3-.5-.8-1.4-.4-.7.3-.8 1.2-1.8.8-.6-.2-1-2.3-1.1-2.8 0-.3-.7-1.2-.9-1.5-.6-1-1.8 0-2.2-.3-.7-.4-.4-1.2-1.3-1.2-2-.2-.6 1.1-1.4 1.4-1.5.6-1.5-1.4-2.7-.5-.4.3.7.6.4 1.2-.2.4-1.4 1.2-1.3 1.8.1.8 1 1 1.2 1.7.7 1.7-.4 3-2.2 1.6-1-.8-1.1-2.2-2.6-2.1l-.9.4q-.4-.2-1-.2c-.5.2 0 .6-.1.9l-.9.8c-.4.5-.3 1-.9 1.5-.5.3-.9.1-1.4.7-.6.6-.7 1.5-1.4 2.1-.4.3-.6-.9-1 0q-.2 0-.3.2v.6c1 2-1 2.6-1.7 4-.3.5.5 1 .2 1.4l-1.2 1.4q0 .4-.3.8c-.2.2-.7 0-.8.3 0 .3.6.6.8.8s-.1.5 0 .6c1 1 .4.7.9 1.6 0 .2.4 0 .5.3l-.1.6.8 1.1c0 .3-1.5 1-1.8 1-.9 0 .8-1.8-.7-1.8-.6 0-.6 1.4-.8 1.6h-.8c-.9 0-1.5 1.1-1.9 1.2h-1.1c-.4 0-.8.4-1.2.4q-.2 0-.5-.3l-1.6-.5c-.3 0-.8.4-1 .2-.2-.1.3-.7-.4-1l-.6-1.4q.4-1.2.4-2.5c-.2-1-1.5-1.7-2-2.4-.2-.5-.4-2-1-2.3a5 5 0 0 1-1.4-1.7q.2-.3 0-.8-.5-.3-.8-.8c-.1-.4 0-1.5-.4-1.7-.4-.3-3.3-.6-4-.7q-.5 0-.9-.3l-.4-.5-.9.1h-.7c-1 .6-1.1 1.4-2 2-.9.8-1.8.7-2.7 1.5-.6.5-1 1.7-1.5 2.2-.2.3-.6 0-.7.1l-1.6 1.5s0 .3-.2.4l-1 .2h-1c-.4.2-.4.7-2 1l-.3.2-.6.5h-.6q-.6.1-1 .4c-.9 0-2.2-.4-2.8-.1-.4.1-1.5 1.3-2 1.4h-.6c-.2 0-.5 1.4-.6 1.7l-2 1.7-.5.8q-.3.4-.8.4-.7-.1-1.3-.4c-.7 0-2.7-.2-3.3 0-.6 0-.4 1.9-2Line truncated
<path fill="#FFF" d="m281.4 124.5 12.9 39.7-33.8-24.5h41.7l-33.7 24.5zm50.2 8.9 12.9 39.7-33.8-24.5h41.8L318.7 173zm47.9 17.4 13 39.7-33.8-24.5h41.7l-33.8 24.5zm-149.1-26.3-13 39.7 33.8-24.5h-41.7l33.8 24.5-13-39.7zm-50.2 8.9-13 39.7 33.8-24.5h-41.7L193 173l-13-39.7zm-47.7 17.4 12.9 39.7-33.8-24.5h41.7l-33.7 24.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+1
View File
@@ -0,0 +1 @@
{"version":8,"sources":{"ne2_shaded":{"maxzoom":6,"tileSize":256,"tiles":["https://tiles.openfreemap.org/natural_earth/ne2sr/{z}/{x}/{y}.png"],"type":"raster"},"openmaptiles":{"type":"vector","url":"https://tiles.openfreemap.org/planet"}},"sprite":"https://tiles.openfreemap.org/sprites/ofm_f384/ofm","glyphs":"https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf","layers":[{"id":"background","type":"background","paint":{"background-color":"#f8f4f0"}},{"id":"natural_earth","type":"raster","source":"ne2_shaded","maxzoom":7,"paint":{"raster-opacity":["interpolate",["exponential",1.5],["zoom"],0,0.6,6,0.1]}},{"id":"park","type":"fill","source":"openmaptiles","source-layer":"park","paint":{"fill-color":"#d8e8c8","fill-opacity":0.7,"fill-outline-color":"rgba(95, 208, 100, 1)"}},{"id":"park_outline","type":"line","source":"openmaptiles","source-layer":"park","paint":{"line-color":"rgba(228, 241, 215, 1)","line-dasharray":[1,1.5]}},{"id":"landuse_residential","type":"fill","source":"openmaptiles","source-layer":"landuse","maxzoom":12,"filter":["==",["get","class"],"residential"],"paint":{"fill-color":["interpolate",["linear"],["zoom"],9,"hsla(0,3%,85%,0.84)",12,"hsla(35,57%,88%,0.49)"]}},{"id":"landcover_wood","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"wood"],"paint":{"fill-antialias":false,"fill-color":"hsla(98,61%,72%,0.7)","fill-opacity":0.4}},{"id":"landcover_grass","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"grass"],"paint":{"fill-antialias":false,"fill-color":"rgba(176, 213, 154, 1)","fill-opacity":0.3}},{"id":"landcover_ice","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"ice"],"paint":{"fill-antialias":false,"fill-color":"rgba(224, 236, 236, 1)","fill-opacity":0.8}},{"id":"landcover_wetland","type":"fill","source":"openmaptiles","source-layer":"landcover","minzoom":12,"filter":["==",["get","class"],"wetland"],"paint":{"fill-antialias":true,"fill-opacity":0.8,"fill-pattern":"wetland_bg_11","fill-translate-anchor":"map"}},{"id":"landuse_pitch","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"pitch"],"paint":{"fill-color":"#DEE3CD"}},{"id":"landuse_track","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"track"],"paint":{"fill-color":"#DEE3CD"}},{"id":"landuse_cemetery","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"cemetery"],"paint":{"fill-color":"hsl(75,37%,81%)"}},{"id":"landuse_hospital","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"hospital"],"paint":{"fill-color":"#fde"}},{"id":"landuse_school","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"school"],"paint":{"fill-color":"rgb(236,238,204)"}},{"id":"waterway_tunnel","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["==",["get","brunnel"],"tunnel"],"paint":{"line-color":"#a0c8f0","line-dasharray":[3,3],"line-gap-width":["interpolate",["linear"],["zoom"],12,0,20,6],"line-opacity":1,"line-width":["interpolate",["exponential",1.4],["zoom"],8,1,20,2]}},{"id":"waterway_river","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["all",["==",["get","class"],"river"],["!=",["get","brunnel"],"tunnel"]],"layout":{"line-cap":"round"},"paint":{"line-color":"#a0c8f0","line-width":["interpolate",["exponential",1.2],["zoom"],11,0.5,20,6]}},{"id":"waterway_other","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["all",["!=",["get","class"],"river"],["!=",["get","brunnel"],"tunnel"]],"layout":{"line-cap":"round"},"paint":{"line-color":"#a0c8f0","line-width":["interpolate",["exponential",1.3],["zoom"],13,0.5,20,6]}},{"id":"water","type":"fill","source":"openmaptiles","source-layer":"water","filter":["!=",["get","brunnel"],"tunnel"],"paint":{"fill-color":"rgb(158,189,255)"}},{"id":"landcover_sand","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"sand"],"paint":{"fill-color":"rgba(247, 239, 195, 1)"}},{"id":"aeroway_fill","type":"fill","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["match",["geometry-type"],["MultiPolygon","Polygon"],true,false],"paint":{"fill-color":"rgba(229, 228, 224, 1)","fill-opacity":0.7}},{"id":"aeroway_runway","type":"line","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["all",["match",["geometry-type"],["LineString","MultiLineString"],true,false],["==",["get","class"],"runway"]],"paint":{"line-color":"#f0ede9","line-width":["interpolate",["exponential",1.2],["zoom"],11,3,20,16]}},{"id":"aeroway_taxiway","type":"line","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["all",["match",["geometry-type"],["LineString","MultiLineString"],true,false],["==",["get","class"],"taxiway"Line truncated
+15
View File
@@ -0,0 +1,15 @@
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).mlcontour=e()}(this,(function(){"use strict";var t,e,i;function r(r,s){if(t)if(e){var n="var sharedChunk = {}; ("+t+")(sharedChunk); ("+e+")(sharedChunk);",o={};t(o),i=s(o),"undefined"!=typeof window&&(i.workerUrl=window.URL.createObjectURL(new Blob([n],{type:"text/javascript"})))}else e=s;else t=s}return r(0,(function(t){class e{constructor(t,e){this.start=t,this.end=e,this.points=[],this.append=this.append.bind(this),this.prepend=this.prepend.bind(this)}append(t,e){this.points.push(Math.round(t),Math.round(e))}prepend(t,e){this.points.splice(0,0,Math.round(t),Math.round(e))}lineString(){return this.toArray()}isEmpty(){return this.points.length<2}appendFragment(t){this.points.push(...t.points),this.end=t.end}toArray(){return this.points}}const i=[[],[[[1,2],[0,1]]],[[[2,1],[1,2]]],[[[2,1],[0,1]]],[[[1,0],[2,1]]],[[[1,2],[0,1]],[[1,0],[2,1]]],[[[1,0],[1,2]]],[[[1,0],[0,1]]],[[[0,1],[1,0]]],[[[1,2],[1,0]]],[[[0,1],[1,0]],[[2,1],[1,2]]],[[[2,1],[1,0]]],[[[0,1],[2,1]]],[[[1,2],[2,1]]],[[[0,1],[1,2]]],[]];function r(t,e,i,r){return(e=2*e+r[0])+(i=2*i+r[1])*(t+1)*2}function s(t,e,i){return(e-t)/(i-t)}function n(t,n,o=4096,a=1){if(!t)return{};const h=o/(n.width-1);let l,c,d,u,f,p;const w={},g=new Map,m=new Map;function b(t,e,i){0===t[0]?i(h*(p-1),h*(f-s(d,e,l))):2===t[0]?i(h*p,h*(f-s(u,e,c))):0===t[1]?i(h*(p-s(c,e,l)),h*(f-1)):i(h*(p-s(u,e,d)),h*f)}for(f=1-a;f<n.height+a;f++){c=n.get(0,f-1),u=n.get(0,f);let s=Math.min(c,u),o=Math.max(c,u);for(p=1-a;p<n.width+a;p++){l=c,d=u,c=n.get(p,f-1),u=n.get(p,f);const a=s,h=o;if(s=Math.min(c,u),o=Math.max(c,u),isNaN(l)||isNaN(c)||isNaN(u)||isNaN(d))continue;const y=Math.min(a,s),v=Math.max(h,o),P=Math.ceil(y/t)*t,x=Math.floor(v/t)*t;for(let s=P;s<=x;s+=t){const t=l>s,o=c>s,a=d>s,h=u>s;for(const l of i[(t?8:0)|(o?4:0)|(h?2:0)|(a?1:0)]){let t=g.get(s);t||g.set(s,t=new Map);let i=m.get(s);i||m.set(s,i=new Map);const o=l[0],a=l[1],h=r(n.width,p,f,o),c=r(n.width,p,f,a);let d,u;if(d=i.get(h))if(i.delete(h),u=t.get(c))if(t.delete(c),d===u){if(b(a,s,d.append),!d.isEmpty()){let t=w[s];t||(w[s]=t=[]),t.push(d.lineString())}}else d.appendFragment(u),i.set(d.end=u.end,d);else b(a,s,d.append),i.set(d.end=c,d);else if(d=t.get(c))t.delete(c),b(o,s,d.prepend),t.set(d.start=h,d);else{const r=new e(h,c);b(o,s,r.append),b(a,s,r.append),t.set(h,r),i.set(c,r)}}}}}for(const[t,e]of g.entries()){let i=null;for(const r of e.values())r.isEmpty()||(null==i&&(i=w[t]||(w[t]=[])),i.push(r.lineString()))}return w}
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */function o(t,e){var i={};for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&e.indexOf(r)<0&&(i[r]=t[r]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(r=Object.getOwnPropertySymbols(t);s<r.length;s++)e.indexOf(r[s])<0&&Object.prototype.propertyIsEnumerable.call(t,r[s])&&(i[r[s]]=t[r[s]])}return i}function a(t,e,i,r){return new(i||(i=Promise))((function(s,n){function o(t){try{h(r.next(t))}catch(t){n(t)}}function a(t){try{h(r.throw(t))}catch(t){n(t)}}function h(t){var e;t.done?s(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}h((r=r.apply(t,e||[])).next())}))}function h(t){const e=Object.entries(t);return e.sort((([t],[e])=>t<e?-1:t>e?1:0)),e}function l(t){return h(t).map((([t,e])=>[t,..."number"==typeof e?[e]:e].join("*"))).join("~")}function c(t){return h(t).map((([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(e)}`)).join(",")}let d=null;function u(){return null==d&&(d="undefined"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext("2d")&&"function"==typeof createImageBitmap),d||!1}let f=null;function p(t,e,i){let r=()=>{};const s=setTimeout((()=>{r(new Error("timed out")),null==i||i.abort()}),t);w(i,(()=>{r(new Error("aborted")),clearTimeout(s)}));const n=new Promise(((t,e)=>{r=e}));return Promise.race([n,e.finally((()=>clearTimeout(s)))])}function w(t,e){e&&(null==t||t.signal.addEventListener("abort",e))}function g(t){var e;return Boolean(null===(e=null==t?void 0:t.signal)||void 0===e?void 0:e.aborted)}let m,b,y,v,P=0;class x{constructor(t=100){this.size=()=>this.items.size,this.get=(t,e,i)=>{let r=this.items.get(t);if(r)r.lastUsed=++P,r.waiting++;else{const i=new AbortController,s=e(t,i);r={abortController:i,item:s,lastUsed:++P,waiting:1},this.items.set(t,r),this.prune()}const s=this.items,n=r.item.then((t=>t),(e=>(s.delete(t),Promise.reject(e))));let o=!1;return w(i,(()=>{var e;r&&r.abortController&&!o&&(o=!0,--r.waiting<=0&&(null===(e=r.abortController)||void 0===e||e.abort(),s.delete(t)))})),n},this.clear=()=>this.items.clear(),this.maxSize=t,this.items=new Map}prune(){if(this.items.size>this.maxSize){let t,e=1/0;this.items.forEach(((i,r)=>{i.lastUsed<e&&(e=i.lastUsed,t=r)})),void 0!==t&&this.items.delete(t)}}}function F(t,e){return m||(m=new OffscreenCanvas(t.width,t.height),b=m.getContext("2d",{willReadFrequently:!0})),V(t,e,m,b)}const k=function(){if(null==f&&(f=!1,u()&&"undefined"!=typeof VideoFrame)){const t=5,e=new OffscreenCanvas(5,5).getContext("2d",{willReadFrequently:!0});if(e){for(let i=0;i<t*t;i++){const r=4*i;e.fillStyle=`rgb(${r},${r+1},${r+2})`,e.fillRect(i%t,Math.floor(i/t),1,1)}const i=e.getImageData(0,0,t,t).data;for(let e=0;e<t*t*4;e++)if(e%4!=3&&i[e]!==e){f=!0;break}}}return f||!1}()?function(t,e,i){return a(this,void 0,void 0,(function*(){var r,s,n;const o=yield createImageBitmap(t);if(g(i))return null;const a=new VideoFrame(o,{timestamp:0});try{if(!((null===(r=null==a?void 0:a.format)||void 0===r?void 0:r.startsWith("BGR"))||(null===(s=null==a?void 0:a.format)||void 0===s?void 0:s.startsWith("RGB"))))throw new Error(`Unrecognized format: ${null==a?void 0:a.format}`);const t=null===(n=null==a?void 0:a.format)||void 0===n?void 0:n.startsWith("BGR"),i=a.allocationSize(),h=new Uint8ClampedArray(i);if(yield a.copyTo(h),t)for(let t=0;t<h.length;t+=4){const e=h[t];h[t]=h[t+2],h[t+2]=e}return M(o.width,o.height,e,h)}catch(t){return g(i)?null:F(o,e)}finally{a.close()}}))}:u()?function(t,e,i){return a(this,void 0,void 0,(function*(){const r=yield createImageBitmap(t);return g(i)?null:F(r,e)}))}:"undefined"!=typeof WorkerGlobalScope&&"undefined"!=typeof self&&self instanceof WorkerGlobalScope?function(t,e,i){return self.actor.send("decodeImage",[],i,void 0,t,e)}:function(t,e,i){return a(this,void 0,void 0,(function*(){y||(y=document.createElement("canvas"),v=y.getContext("2d",{willReadFrequently:!0}));const r=new Image;w(i,(()=>r.src=""));return V(yield new Promise(((e,s)=>{r.onload=()=>{g(i)||e(r),URL.revokeObjectURL(r.src),r.onload=null},r.onerror=()=>s(new Error("Could not load image.")),r.src=t.size?URL.createObjectURL(t):""})),e,y,v)}))};function V(t,e,i,r){if(i.width=t.width,i.height=t.height,!r)throw new Error("failed to get context");r.drawImage(t,0,0,t.width,t.height);const s=r.getImageData(0,0,t.width,t.height).data;return M(t.width,t.height,e,s)}function M(t,e,i,r){const s="mapbox"===i?(t,e,i)=>.1*(256*t*256+256*e+i)-1e4:(t,e,i)=>256*t+e+i/256-32768,n=new Float32Array(t*e);for(let t=0;t<r.length;t+=4)n[t/4]=s(r[t],r[t+1],r[t+2]);return{width:t,height:e,data:n}}class S{constructor(t,e,i){this.split=(t,e,i)=>{if(0===t)return this;const r=1<<t,s=e*this.width/r,n=i*this.height/r;return new S(this.width/r,this.height/r,((t,e)=>this.get(t+s,e+n)))},this.subsamplePixelCenters=t=>{const e=(t,e,i)=>isNaN(t)?e:isNaN(e)?t:t+(e-t)*i;if(t<=1)return this;const i=.5-1/(2*t);return new S(this.width*t,this.height*t,((r,s)=>{const n=r/t-iLine truncated
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Steve Vigneau
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
File diff suppressed because it is too large. Load diff
Binary file not shown.
@@ -1,16 +0,0 @@
"use strict";var protomaps_themes_base=(()=>{var f=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames,x=Object.getOwnPropertySymbols;var j=Object.prototype.hasOwnProperty,$=Object.prototype.propertyIsEnumerable;var z=(a,e,i)=>e in a?f(a,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):a[e]=i,b=(a,e)=>{for(var i in e||(e={}))j.call(e,i)&&z(a,i,e[i]);if(x)for(var i of x(e))$.call(e,i)&&z(a,i,e[i]);return a};var l=(a,e)=>f(a,"name",{value:e,configurable:!0});var C=(a,e)=>{for(var i in e)f(a,i,{get:e[i],enumerable:!0})},P=(a,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!j.call(a,r)&&r!==i&&f(a,r,{get:()=>e[r],enumerable:!(n=D(e,r))||n.enumerable});return a};var A=a=>P(f({},"__esModule",{value:!0}),a);var W={};C(W,{default:()=>S,labels:()=>I,labelsWithCustomTheme:()=>K,language_script_pairs:()=>m,layers:()=>B,layersWithCustomTheme:()=>V,layersWithPartialCustomTheme:()=>F,namedTheme:()=>H,noLabels:()=>E,noLabelsWithCustomTheme:()=>G});function t(a,e){let i="script";return a==="name"?i="script":a==="name2"?i="script2":a==="name3"&&(i="script3"),[["coalesce",["get",`pgf:${a}`],["get",a]],{"text-font":["case",["==",["get",i],"Devanagari"],["literal",["Noto Sans Devanagari Regular v1"]],["literal",[e||"Noto Sans Regular"]]]}]}l(t,"get_name_block");function c(a,e,i){let n="name";return i==="name"?n="":i==="name2"?n="2":i==="name3"&&(n="3"),e==="Latin"?["has",`script${n}`]:a==="ja"?["all",["!=",["get",`script${n}`],"Han"],["!=",["get",`script${n}`],"Hiragana"],["!=",["get",`script${n}`],"Katakana"],["!=",["get",`script${n}`],"Mixed-Japanese"]]:["!=",["get",`script${n}`],e]}l(c,"is_not_in_target_script");function o(a){return a==="Devanagari"?{"text-font":["literal",["Noto Sans Devanagari Regular v1"]]}:{}}l(o,"get_font_formatting");function v(a){let e=m.find(i=>i.lang===a);return e===void 0?"Latin":e.script}l(v,"get_default_script");function L(a,e){let i=e||v(a),n;return i==="Devanagari"?n="pgf:":n="",["format",["coalesce",["get",`${n}name:${a}`],["get","name:en"]],o(i)]}l(L,"get_country_name");function s(a,e,i){let n=e||v(a),r;return n==="Devanagari"?r="pgf:":r="",["case",["all",["any",["has","name"],["has","pgf:name"]],["!",["any",["has","name2"],["has","pgf:name2"]]],["!",["any",["has","name3"],["has","pgf:name3"]]]],["case",c(a,n,"name"),["case",["any",["is-supported-script",["get","name"]],["has","pgf:name"]],["format",["coalesce",["get",`${r}name:${a}`],["get","name:en"]],o(n),`
`,{},["case",["all",["!",["has",`${r}name:${a}`]],["has","name:en"],["!",["has","script"]]],"",["coalesce",["get","pgf:name"],["get","name"]]],{"text-font":["case",["==",["get","script"],"Devanagari"],["literal",["Noto Sans Devanagari Regular v1"]],["literal",[i||"Noto Sans Regular"]]]}],["get","name:en"]],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n)]],["all",["any",["has","name"],["has","pgf:name"]],["any",["has","name2"],["has","pgf:name2"]],["!",["any",["has","name3"],["has","pgf:name3"]]]],["case",["all",c(a,n,"name"),c(a,n,"name2")],["format",["get",`${r}name:${a}`],o(n),`
`,{},...t("name"),`
`,{},...t("name2")],["case",c(a,n,"name2"),["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n),`
`,{},...t("name2")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name2"],["get","name2"]],o(n),`
`,{},...t("name")]]],["case",["all",c(a,n,"name"),c(a,n,"name2"),c(a,n,"name3")],["format",["get",`${r}name:${a}`],o(n),`
`,{},...t("name"),`
`,{},...t("name2"),`
`,{},...t("name3")],["case",["!",c(a,n,"name")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n),`
`,{},...t("name2"),`
`,{},...t("name3")],["!",c(a,n,"name2")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name2"],["get","name2"]],o(n),`
`,{},...t("name"),`
`,{},...t("name3")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name3"],["get","name3"]],o(n),`
`,{},...t("name"),`
`,{},...t("name2")]]]]}l(s,"get_multiline_name");var m=[{lang:"ar",full_name:"Arabic",script:"Arabic"},{lang:"cs",full_name:"Czech",script:"Latin"},{lang:"bg",full_name:"Bulgarian",script:"Cyrillic"},{lang:"da",full_name:"Danish",script:"Latin"},{lang:"de",full_name:"German",script:"Latin"},{lang:"el",full_name:"Greek",script:"Greek"},{lang:"en",full_name:"English",script:"Latin"},{lang:"es",full_name:"Spanish",script:"Latin"},{lang:"et",full_name:"Estonian",script:"Latin"},{lang:"fa",full_name:"Persian",script:"Arabic"},{lang:"fi",full_name:"Finnish",script:"Latin"},{lang:"fr",full_name:"French",script:"Latin"},{lang:"ga",full_name:"Irish",script:"Latin"},{lang:"he",full_name:"Hebrew",script:"Hebrew"},{lang:"hi",full_name:"Hindi",script:"Devanagari"},{lang:"hr",full_name:"Croatian",script:"Latin"},{lang:"hu",full_name:"Hungarian",script:"Latin"},{lang:"id",full_name:"Indonesian",script:"Latin"},{lang:"it",full_name:"Italian",script:"Latin"},{lang:"ja",full_name:"Japanese",script:""},{lang:"ko",full_name:"Korean",script:"Hangul"},{lang:"lt",full_name:"Lithuanian",script:"Latin"},{lang:"lv",full_name:"Latvian",script:"Latin"},{lang:"ne",full_name:"Nepali",script:"Devanagari"},{lang:"nl",full_name:"Dutch",script:"Latin"},{lang:"no",full_name:"Norwegian",script:"Latin"},{lang:"mr",full_name:"Marathi",script:"Devanagari"},{lang:"mt",full_name:"Maltese",script:"Latin"},{lang:"pl",full_name:"Polish",script:"Latin"},{lang:"pt",full_name:"Portuguese",script:"Latin"},{lang:"ro",full_name:"Romanian",script:"Latin"},{lang:"ru",full_name:"Russian",script:"Cyrillic"},{lang:"sk",full_name:"Slovak",script:"Latin"},{lang:"sl",full_name:"Slovenian",script:"Latin"},{lang:"sv",full_name:"Swedish",script:"Latin"},{lang:"tr",full_name:"Turkish",script:"Latin"},{lang:"uk",full_name:"Ukrainian",script:"Cyrillic"},{lang:"ur",full_name:"Urdu",script:"Arabic"},{lang:"vi",full_name:"Vietnamese",script:"Latin"},{lang:"zh-Hans",full_name:"Chinese (Simplified)",script:"Han"},{lang:"zh-Hant",full_name:"Chinese (Traditional)",script:"Han"}];function _(a,e){return[{id:"background",type:"background",paint:{"background-color":e.background}},{id:"earth",type:"fill",filter:["==","$type","Polygon"],source:a,"source-layer":"earth",paint:{"fill-color":e.earth}},...e.landcover?[{id:"landcover",type:"fill",source:a,"source-layer":"landcover",paint:{"fill-color":["match",["get","kind"],"grassland",e.landcover.grassland,"barren",e.landcover.barren,"urban_area",e.landcover.urban_area,"farmland",e.landcover.farmland,"glacier",e.landcover.glacier,"scrub",e.landcover.scrub,e.landcover.forest],"fill-opacity":["interpolate",["linear"],["zoom"],5,1,7,0]}}]:[],{id:"landuse_park",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","national_park","park","cemetery","protected_area","nature_reserve","forest","golf_course","wood","nature_reserve","forest","scrub","grassland","grass","military","naval_base","airfield"],paint:{"fill-opacity":["interpolate",["linear"],["zoom"],6,0,11,1],"fill-color":["case",["in",["get","kind"],["literal",["national_park","park","cemetery","protected_area","nature_reserve","forest","golf_course"]]],e.park_b,["in",["get","kind"],["literal",["wood","nature_reserve","forest"]]],e.wood_b,["in",["get","kind"],["literal",["scrub","grassland","grass"]]],e.scrub_b,["in",["get","kind"],["literal",["glacier"]]],e.glacier,["in",["get","kind"],["literal",["sand"]]],e.sand,["in",["get","kind"],["literal",["military","naval_base","airfield"]]],e.zoo,e.earth]}},{id:"landuse_urban_green",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","allotments","village_green","playground"],paint:{"fill-color":e.park_b,"fill-opacity":.7}},{id:"landuse_hospital",type:"fill",source:a,"source-layer":"landuse",filter:["==","kind","hospital"],paint:{"fill-color":e.hospital}},{id:"landuse_industrial",type:"fill",source:a,"source-layer":"landuse",filter:["==","kind","industrial"],paint:{"fill-color":e.industrial}},{id:"landuse_school",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","school","university","college"],paint:{"fill-color":e.school}},{id:"landuse_beach",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","beach"],paint:{"fill-color":e.beach}},{id:"landuse_zoo",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","zoo"],paint:{"fill-color":e.zoo}},{id:"landuse_aerodrome",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","aerodrome"],paint:{"fill-color":e.aerodrome}},{id:"roads_runway",type:"line",source:a,"source-layer":"roads",filter:["==","kind_detail","runway"],paint:{"line-color":e.runway,"line-width":["interpolate",["exponential",1.6],["zoom"],10,0,12,4,18,30]}},{id:"roads_taxiway",type:"line",source:a,"source-layer":"roads",minzoom:13,filter:["==","kind_detail","taxiway"],paint:{"line-color":e.runway,"line-width":["interpolate",["exponential",1.6],["zoom"],13,0,13.5,1,15,6]}},{id:"landuse_runway",type:"fill",source:a,"source-layer":"landuse",filter:["any",["in Line truncated
//# sourceMappingURL=protomaps-themes-base.js.map
+22
View File
@@ -0,0 +1,22 @@
Copyright (c) 2026, Volodymyr Agafonkin
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+533
View File
@@ -0,0 +1,533 @@
// Vendored from github.com/mourner/suncalc (BSD-2-Clause, see LICENSE in
// this directory) — used for public/js/sun.js's sunrise/sunset readout.
// Only getTimes (and its own helpers below) is actually used; the
// moon-position/-phase/-times exports below it are unused dead weight here,
// left in rather than hand-trimmed so this stays a straight, verifiable
// copy of the upstream file.
// shortcuts for easier to read formulas
const {PI, sin, cos, tan, asin, atan2: atan, acos, sqrt, abs, round} = Math;
const rad = PI / 180;
// date/time constants and conversions
const dayMs = 1000 * 60 * 60 * 24;
const J1970 = 2440588;
const J2000 = 2451545;
const earthRadius = 6378.14; // equatorial radius in km, for the Moon's topocentric parallax
function fromJulian(j) {
return new Date((j + 0.5 - J1970) * dayMs);
}
function toDays(date) {
return date.valueOf() / dayMs - 0.5 + J1970 - J2000;
}
// ΔT = TT − UT in seconds (Espenak & Meeus polynomial fits, good ~1900–2150). The Meeus position
// series are defined in Terrestrial Time, but SunCalc's input Dates are UT — so the position math
// runs on days-since-J2000 shifted by deltaT, while sidereal time stays on UT. ~69 s today;
// negligible for the Sun (<0.001°), real for the Moon. d only needs ~month accuracy here (ΔT
// changes <1 s/yr), so the decimal year is derived arithmetically from d rather than from the Date.
function deltaT(d) {
const y = 2000 + d / 365.2425;
let t;
if (y < 1920) { t = y - 1900; return -2.79 + t * (1.494119 + t * (-0.0598939 + t * (0.0061966 - t * 0.000197))); }
if (y < 1941) { t = y - 1920; return 21.20 + t * (0.84493 + t * (-0.076100 + t * 0.0020936)); }
if (y < 1961) { t = y - 1950; return 29.07 + t * (0.407 + t * (-1 / 233 + t / 2547)); }
if (y < 1986) { t = y - 1975; return 45.45 + t * (1.067 + t * (-1 / 260 - t / 718)); }
if (y < 2005) { t = y - 2000; return 63.86 + t * (0.3345 + t * (-0.060374 + t * (0.0017275 + t * (0.000651814 + t * 0.00002373599)))); }
if (y < 2050) { t = y - 2000; return 62.92 + t * (0.32217 + t * 0.005589); }
t = (y - 1820) / 100; return -20 + 32 * t * t - 0.5628 * (2150 - y);
}
function toDaysTT(d) {
return d + deltaT(d) / 86400;
}
// general calculations for position
// north-based clockwise azimuth in degrees (0 = N, 90 = E, 180 = S, 270 = W)
function azimuth(H, phi, dec) {
return (atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)) / rad + 540) % 360;
}
function altitude(H, phi, dec) {
return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H));
}
// Greenwich mean sidereal time, formula 12.4 of Meeus (linear term; sub-arcsec T^2/T^3 dropped)
function siderealTime(d, lw) {
return rad * (280.46061837 + 360.98564736629 * d) - lw;
}
function astroRefraction(h) {
if (h < 0) h = 0; // formula valid for positive altitudes only
// Meeus 16.4: 1.02 / tan(h + 10.26 / (h + 5.10)), h in degrees, arcmin result — folded into rad
return 0.0002967 / tan(h + 0.00312536 / (h + 0.08901179));
}
// general sun calculations
// Sun's apparent equatorial coordinates, Meeus ch. 25. d = days since J2000 (TT); t = Julian centuries.
function sunCoords(d) {
const t = d / 36525;
const L0 = rad * (280.46646 + t * (36000.76983 + t * 0.0003032)); // 25.2 geometric mean longitude
const M = rad * (357.52911 + t * (35999.05029 - t * 0.0001537)); // 25.3 mean anomaly
const sinM = sin(M);
const cosM = cos(M);
const C = rad * ((1.914602 - t * (0.004817 + t * 0.000014)) * sinM + // equation of center
(0.019993 - 0.000101 * t) * 2 * sinM * cosM + 0.000289 * sinM * (3 - 4 * sinM * sinM));
const Om = rad * (125.04 - 1934.136 * t); // longitude of the ascending node
const L = L0 + C - rad * (0.00569 + 0.00478 * sin(Om)); // apparent longitude (nutation + aberration)
// 22.2 mean obliquity + 25.8 correction for apparent position
const e = rad * (23.439291 - t * (0.0130042 + t * (0.00000016 - t * 0.000000504))) + rad * 0.00256 * cos(Om);
return {
ra: atan(cos(e) * sin(L), cos(L)), // 25.6
dec: asin(sin(e) * sin(L)) // 25.7
};
}
// calculates sun position for a given date and latitude/longitude
export function getPosition(date, lat, lng) {
const lw = rad * -lng;
const phi = rad * lat;
const d = toDays(date);
const c = sunCoords(toDaysTT(d)); // position series run on Terrestrial Time
const H = siderealTime(d, lw) - c.ra; // sidereal time stays on UT
const h = altitude(H, phi, c.dec);
return {
azimuth: azimuth(H, phi, c.dec),
// apparent (refraction-corrected) altitude in degrees
altitude: (h + astroRefraction(h)) / rad
};
}
// sun times configuration (angle, morning name, evening name)
export const times = [
[-0.833, 'sunrise', 'sunset'],
[-0.3, 'sunriseEnd', 'sunsetStart'],
[-6, 'dawn', 'dusk'],
[-12, 'nauticalDawn', 'nauticalDusk'],
[-18, 'nightEnd', 'night'],
[6, 'goldenHourEnd', 'goldenHour']
];
// adds a custom time to the times config
export function addTime(angle, riseName, setName) {
times.push([angle, riseName, setName]);
}
// calculations for sun times — Meeus ch.15 (rising, transit, setting), solving the Sun's
// local hour angle directly off the same apparent coordinates (sunCoords) and sidereal time
// used by getPosition. Day offsets d are in UT days since J2000; the position series run on TT.
const J0 = 0.0009;
function observerAngle(height) {
return -2.076 * sqrt(height) / 60;
}
// wrap an angle to (-PI, PI]
function wrapPi(a) {
return a - 2 * PI * round(a / (2 * PI));
}
// refines a transit time so the Sun's local hour angle is zero (Meeus 15.2; dH/dd ~= 2*PI/day,
// the sidereal excess and the Sun's own motion cancelling to one solar day).
function solarTransit(dt, lw) {
for (let i = 0; i < 3; i++) {
const H = wrapPi(siderealTime(dt, lw) - sunCoords(toDaysTT(dt)).ra);
dt -= H / (2 * PI);
}
return dt;
}
// time the Sun reaches altitude h0 on the given side of transit (sign -1 = rise, +1 = set);
// starts from the hour angle at transit and converges with Meeus' altitude correction (15.2).
function getSetJ(h0, dt, sign, lw, phi, decT) {
const cosH0 = (sin(h0) - sin(phi) * sin(decT)) / (cos(phi) * cos(decT));
if (cosH0 < -1 || cosH0 > 1) return NaN; // sun stays above / below this altitude all day
let d = dt + sign * acos(cosH0) / (2 * PI);
for (let i = 0; i < 2; i++) {
const c = sunCoords(toDaysTT(d));
const H = wrapPi(siderealTime(d, lw) - c.ra);
const h = altitude(H, phi, c.dec);
const sinH = cos(phi) * cos(c.dec) * sin(H);
if (abs(sinH) < 1e-6) break; // grazing the horizon — correction is ill-conditioned
d += (h - h0) / (2 * PI * sinH);
}
return d;
}
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
export function getTimes(date, lat, lng, height = 0) {
const lw = rad * -lng;
const phi = rad * lat;
const dh = observerAngle(height);
// Anchor to the input date's UTC solar day regardless of its time-of-day, killing the historical
// "always pass noon" footgun where an early-morning Date returned the previous day's events:
// round to that day's noon, offset to the nearest local solar noon, then let solarTransit refine.
const d = round(toDays(date) - J0 - lw / (2 * PI));
const dt = solarTransit(d + J0 + lw / (2 * PI), lw);
const dec = sunCoords(toDaysTT(dt)).dec; // declination at transit, shared by every rise/set solve
const result = {
solarNoon: fromJulian(dt + J2000),
nadir: fromJulian(dt + J2000 - 0.5)
};
for (const [angle, riseName, setName] of times) {
const h0 = (angle + dh) * rad;
const jrise = getSetJ(h0, dt, -1, lw, phi, dec);
const jset = getSetJ(h0, dt, 1, lw, phi, dec);
// a NaN means the Sun never reaches this altitude on this day — report null, not Invalid Date
result[riseName] = Number.isNaN(jrise) ? null : fromJulian(jrise + J2000);
result[setName] = Number.isNaN(jset) ? null : fromJulian(jset + J2000);
}
// polar day/night: when the Sun never crosses the standard rise/set altitude, flag which side it
// stays on by comparing its altitude at solar noon (its daily maximum) against that threshold.
if (result.sunrise === null) {
const noonAlt = altitude(0, phi, dec);
const riseSetAlt = (times[0][0] + dh) * rad;
result.alwaysUp = noonAlt > riseSetAlt;
result.alwaysDown = noonAlt <= riseSetAlt;
}
return result;
}
// moon calculations, based on Meeus ch. 47 (truncated ELP-2000/82 series)
// Nutation in longitude (Δψ) and true obliquity of the ecliptic, both in degrees,
// from the abridged series of Meeus ch. 22 (sub-arcsecond, ample for our needs).
function nutationObliquity(t) {
const om = rad * (125.04452 - 1934.136261 * t); // longitude of the Moon's ascending node
const ls = rad * (280.4665 + 36000.7698 * t); // mean longitude of the Sun
const lm = rad * (218.3165 + 481267.8813 * t); // mean longitude of the Moon
const dpsi = (-17.20 * sin(om) - 1.32 * sin(2 * ls) - 0.23 * sin(2 * lm) + 0.21 * sin(2 * om)) / 3600;
const deps = (9.20 * cos(om) + 0.57 * cos(2 * ls) + 0.10 * cos(2 * lm) - 0.09 * cos(2 * om)) / 3600;
const eps0 = 23.439291 - t * (0.0130042 + t * (0.00000016 - t * 0.000000504)); // 22.2 mean obliquity
return {dpsi, eps: rad * (eps0 + deps)};
}
// Meeus table 47.A — periodic terms for the Moon's longitude (Σl, ×1e-6 deg) and
// distance (Σr, ×1e-3 km). Flat rows of 6: D, M, M', F, Σl, Σr.
const moonLon = new Int32Array([
0, 0, 1, 0, 6288774, -20905355,
2, 0, -1, 0, 1274027, -3699111,
2, 0, 0, 0, 658314, -2955968,
0, 0, 2, 0, 213618, -569925,
0, 1, 0, 0, -185116, 48888,
0, 0, 0, 2, -114332, -3149,
2, 0, -2, 0, 58793, 246158,
2, -1, -1, 0, 57066, -152138,
2, 0, 1, 0, 53322, -170733,
2, -1, 0, 0, 45758, -204586,
0, 1, -1, 0, -40923, -129620,
1, 0, 0, 0, -34720, 108743,
0, 1, 1, 0, -30383, 104755,
2, 0, 0, -2, 15327, 10321,
0, 0, 1, 2, -12528, 0,
0, 0, 1, -2, 10980, 79661,
4, 0, -1, 0, 10675, -34782,
0, 0, 3, 0, 10034, -23210,
4, 0, -2, 0, 8548, -21636,
2, 1, -1, 0, -7888, 24208,
2, 1, 0, 0, -6766, 30824,
1, 0, -1, 0, -5163, -8379,
1, 1, 0, 0, 4987, -16675,
2, -1, 1, 0, 4036, -12831,
2, 0, 2, 0, 3994, -10445,
4, 0, 0, 0, 3861, -11650,
2, 0, -3, 0, 3665, 14403,
0, 1, -2, 0, -2689, -7003,
2, 0, -1, 2, -2602, 0,
2, -1, -2, 0, 2390, 10056,
1, 0, 1, 0, -2348, 6322,
2, -2, 0, 0, 2236, -9884,
0, 1, 2, 0, -2120, 5751,
0, 2, 0, 0, -2069, 0,
2, -2, -1, 0, 2048, -4950,
2, 0, 1, -2, -1773, 4130,
2, 0, 0, 2, -1595, 0,
4, -1, -1, 0, 1215, -3958,
0, 0, 2, 2, -1110, 0,
3, 0, -1, 0, -892, 3258,
2, 1, 1, 0, -810, 2616,
4, -1, -2, 0, 759, -1897,
0, 2, -1, 0, -713, -2117,
2, 2, -1, 0, -700, 2354,
2, 1, -2, 0, 691, 0,
2, -1, 0, -2, 596, 0,
4, 0, 1, 0, 549, -1423,
0, 0, 4, 0, 537, -1117,
4, -1, 0, 0, 520, -1571,
1, 0, -2, 0, -487, -1739,
2, 1, 0, -2, -399, 0,
0, 0, 2, -2, -381, -4421,
1, 1, 1, 0, 351, 0,
3, 0, -2, 0, -340, 0,
4, 0, -3, 0, 330, 0,
2, -1, 2, 0, 327, 0,
0, 2, 1, 0, -323, 1165,
1, 1, -1, 0, 299, 0,
2, 0, 3, 0, 294, 0,
2, 0, -1, -2, 0, 8752
]);
// Meeus table 47.B — periodic terms for the Moon's latitude (Σb, ×1e-6 deg).
// Flat rows of 5: D, M, M', F, Σb.
const moonLat = new Int32Array([
0, 0, 0, 1, 5128122,
0, 0, 1, 1, 280602,
0, 0, 1, -1, 277693,
2, 0, 0, -1, 173237,
2, 0, -1, 1, 55413,
2, 0, -1, -1, 46271,
2, 0, 0, 1, 32573,
0, 0, 2, 1, 17198,
2, 0, 1, -1, 9266,
0, 0, 2, -1, 8822,
2, -1, 0, -1, 8216,
2, 0, -2, -1, 4324,
2, 0, 1, 1, 4200,
2, 1, 0, -1, -3359,
2, -1, -1, 1, 2463,
2, -1, 0, 1, 2211,
2, -1, -1, -1, 2065,
0, 1, -1, -1, -1870,
4, 0, -1, -1, 1828,
0, 1, 0, 1, -1794,
0, 0, 0, 3, -1749,
0, 1, -1, 1, -1565,
1, 0, 0, 1, -1491,
0, 1, 1, 1, -1475,
0, 1, 1, -1, -1410,
0, 1, 0, -1, -1344,
1, 0, 0, -1, -1335,
0, 0, 3, 1, 1107,
4, 0, 0, -1, 1021,
4, 0, -1, 1, 833,
0, 0, 1, -3, 777,
4, 0, -2, 1, 671,
2, 0, 0, -3, 607,
2, 0, 2, -1, 596,
2, -1, 1, -1, 491,
2, 0, -2, 1, -451,
0, 0, 3, -1, 439,
2, 0, 2, 1, 422,
2, 0, -3, -1, 421,
2, 1, -1, 1, -366,
2, 1, 0, 1, -351,
4, 0, 0, 1, 331,
2, -1, 1, 1, 315,
2, -2, 0, -1, 302,
0, 0, 1, 3, -283,
2, 1, 1, -1, -229,
1, 1, 0, -1, 223,
1, 1, 0, 1, 223,
0, 1, -2, -1, -220,
2, 1, -1, -1, -220,
1, 0, 1, 1, -185,
2, -1, -2, -1, 181,
0, 1, 2, 1, -177,
4, 0, -2, -1, 176,
4, -1, -1, -1, 166,
1, 0, 1, -1, -164,
4, 0, 1, -1, 132,
1, 0, -1, -1, -119,
4, -1, 0, -1, 115,
2, -2, 0, 1, 107
]);
// geocentric apparent equatorial coordinates of the Moon, Meeus ch. 47. d = days since J2000 (TT).
function moonCoords(d) {
const t = d / 36525;
// fundamental arguments (degrees), 47.1–47.6
const Lp = 218.3164477 + t * (481267.88123421 + t * (-0.0015786 + t * (1 / 538841 - t / 65194000)));
const D = 297.8501921 + t * (445267.1114034 + t * (-0.0018819 + t * (1 / 545868 - t / 113065000)));
const M = 357.5291092 + t * (35999.0502909 + t * (-0.0001536 + t / 24490000));
const Mp = 134.9633964 + t * (477198.8675055 + t * (0.0087414 + t * (1 / 69699 - t / 14712000)));
const F = 93.2720950 + t * (483202.0175233 + t * (-0.0036539 + t * (-1 / 3526000 + t / 863310000)));
const A1 = 119.75 + 131.849 * t;
const A2 = 53.09 + 479264.290 * t;
const A3 = 313.45 + 481266.484 * t;
const E = 1 - t * (0.002516 + t * 0.0000074); // eccentricity factor for solar-anomaly terms
const Dr = rad * D, Mr = rad * M, Mpr = rad * Mp, Fr = rad * F;
let sl = 0, sr = 0, sb = 0;
for (let i = 0; i < moonLon.length; i += 6) {
const m = moonLon[i + 1];
const arg = moonLon[i] * Dr + m * Mr + moonLon[i + 2] * Mpr + moonLon[i + 3] * Fr;
const f = m === 1 || m === -1 ? E : m === 2 || m === -2 ? E * E : 1;
sl += moonLon[i + 4] * f * sin(arg);
sr += moonLon[i + 5] * f * cos(arg);
}
for (let i = 0; i < moonLat.length; i += 5) {
const m = moonLat[i + 1];
const arg = moonLat[i] * Dr + m * Mr + moonLat[i + 2] * Mpr + moonLat[i + 3] * Fr;
const f = m === 1 || m === -1 ? E : m === 2 || m === -2 ? E * E : 1;
sb += moonLat[i + 4] * f * sin(arg);
}
// additive terms (Venus, Jupiter and flattening of the Earth), 47 p.342
const A1r = rad * A1, Lpr = rad * Lp;
sl += 3958 * sin(A1r) + 1962 * sin(Lpr - Fr) + 318 * sin(rad * A2);
sb += -2235 * sin(Lpr) + 382 * sin(rad * A3) + 175 * sin(A1r - Fr) + 175 * sin(A1r + Fr) +
127 * sin(Lpr - Mpr) - 115 * sin(Lpr + Mpr);
const {dpsi, eps} = nutationObliquity(t);
const l = rad * (Lp + sl / 1e6 + dpsi); // apparent ecliptic longitude
const b = rad * (sb / 1e6); // ecliptic latitude
return {
ra: atan(sin(l) * cos(eps) - tan(b) * sin(eps), cos(l)), // 13.3
dec: asin(sin(b) * cos(eps) + cos(b) * sin(eps) * sin(l)), // 13.4
dist: 385000.56 + sr / 1000 // distance to the Moon in km
};
}
export function getMoonPosition(date, lat, lng) {
const lw = rad * -lng;
const phi = rad * lat;
const d = toDays(date);
const c = moonCoords(toDaysTT(d)); // position series run on Terrestrial Time
const H = siderealTime(d, lw) - c.ra; // sidereal time stays on UT
// geocentric parallax (Meeus ch.40) lowers the moon along its vertical circle, leaving
// azimuth unchanged; sin(parallax) = (Earth radius / distance) * cos(geocentric altitude).
const hGeo = altitude(H, phi, c.dec);
const h = hGeo - asin(earthRadius / c.dist * cos(hGeo));
// parallactic angle, Meeus 14.1
const pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
return {
azimuth: azimuth(H, phi, c.dec),
// apparent (refraction-corrected) altitude in degrees
altitude: (h + astroRefraction(h)) / rad,
distance: c.dist,
parallacticAngle: pa / rad
};
}
// moon illumination parameters, Meeus ch. 48 (and idlastro's mphase.pro)
export function getMoonIllumination(date = new Date()) {
const d = toDaysTT(toDays(date));
const s = sunCoords(d);
const m = moonCoords(d);
const sdist = 149598000; // distance from Earth to Sun in km
const phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra));
const inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi));
const angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
const waxing = angle < 0; // bright limb leads → illuminated fraction is growing (new → full)
return {
// illuminated fraction, 0 (new) → 1 (full); reaches the exact extrema only at perfect
// syzygy (eclipses), so a "full" moon typically peaks a hair under 1 — this is correct
fraction: (1 + cos(inc)) / 2,
// phase, 0 → 1: 0 new, 0.25 first quarter, 0.5 full, 0.75 last quarter (waxing for phase < 0.5)
phase: 0.5 + 0.5 * inc * (waxing ? -1 : 1) / PI,
// position angle of the bright limb, degrees (v2: all angles emitted in degrees) — subtract
// getMoonPosition().parallacticAngle, also degrees, to get the zenith-relative tilt
angle: angle / rad,
waxing // true while the Moon is waxing (new → full), false while waning (full → new)
};
}
function hoursLater(date, h) {
return new Date(date.valueOf() + h * dayMs / 24);
}
// height of the moon's upper limb above the rise/set horizon (deg): topocentric centre altitude plus
// the moon's semidiameter (0.2725 * equatorial horizontal parallax, so it tracks distance: ~0.25° at
// apogee, ~0.28° at perigee) plus the residual horizon refraction our model under-bends (~0.09°,
// tuned vs USNO). Crossing zero == upper-limb rise/set, the USNO convention.
function moonHeight(date, lat, lng) {
const p = getMoonPosition(date, lat, lng);
return p.altitude + 0.2725 * asin(earthRadius / p.distance) / rad + 0.09;
}
// polish a crossing time (ms): the quadratic sampler's parabola root sits up to ~0.2° off the true
// altitude curve, so Newton-refine against the real moonHeight. Two central-difference steps drop the
// mean error from ~0.65 to ~0.28 min; more don't help (near grazing dh/dt is small either way).
function refineMoonCross(tMs, lat, lng) {
for (let i = 0; i < 2; i++) {
const h = moonHeight(new Date(tMs), lat, lng);
const dh = (moonHeight(new Date(tMs + 30000), lat, lng) - moonHeight(new Date(tMs - 30000), lat, lng)) / 60000;
tMs -= h / dh; // dh is degrees per ms, so h/dh is ms
}
return tMs;
}
export function getMoonTimes(date, lat, lng) {
// scan the UTC calendar day of the given date; the date is treated as a UTC instant like
// everywhere else in the API. For a local-civil-day window, pass a date at local midnight.
const t = new Date(date);
t.setUTCHours(0, 0, 0, 0);
let h0 = moonHeight(t, lat, lng);
let rise, set, hMax = h0;
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
for (let i = 1; i <= 24; i += 2) {
const h1 = moonHeight(hoursLater(t, i), lat, lng);
const h2 = moonHeight(hoursLater(t, i + 1), lat, lng);
hMax = Math.max(hMax, h1, h2);
const a = (h0 + h2) / 2 - h1;
const b = (h2 - h0) / 2;
const xe = -b / (2 * a);
const d = b * b - 4 * a * h1;
let roots = 0, x1 = 0, x2 = 0;
const ye = (a * xe + b) * xe + h1;
if (d >= 0) {
const dx = sqrt(d) / (abs(a) * 2);
x1 = xe - dx;
x2 = xe + dx;
if (abs(x1) <= 1) roots++;
if (abs(x2) <= 1) roots++;
if (x1 < -1) x1 = x2;
}
if (roots === 1) {
if (h0 < 0) rise = i + x1;
else set = i + x1;
} else if (roots === 2) {
rise = i + (ye < 0 ? x2 : x1);
set = i + (ye < 0 ? x1 : x2);
}
if (rise !== undefined && set !== undefined) break;
h0 = h2;
}
const result = {};
// compare against undefined, not truthiness: a crossing at exactly hour 0 (midnight) is a real
// event whose numeric value is 0, which a truthy check would wrongly treat as "no event".
if (rise !== undefined) result.rise = new Date(refineMoonCross(hoursLater(t, rise).valueOf(), lat, lng));
if (set !== undefined) result.set = new Date(refineMoonCross(hoursLater(t, set).valueOf(), lat, lng));
// no crossing all day: flag which side the moon stays on by testing its highest sampled height
// against the rise/set threshold (already baked into moonHeight), setting both like getTimes does
if (rise === undefined && set === undefined) {
result.alwaysUp = hMax > 0;
result.alwaysDown = hMax <= 0;
}
return result;
}
+150
View File
@@ -18,11 +18,40 @@ class AppTest < Minitest::Test
assert_includes response.body, "livetrack-panel" assert_includes response.body, "livetrack-panel"
end end
# Every public/js/*.js module map.js (or something it imports) can reach
# needs its own versioned importmap entry (views/layout.erb) or it skips
# ASSET_VERSION's whole cache-busting story — easy to forget when adding a
# new module, since the app still works fine locally either way (a plain
# unmapped import just resolves straight to the unversioned file).
# map.js itself is excluded: it's the one entry point loaded directly by
# its own <script> tag, not imported by anything, so it has no importmap
# entry of its own by design.
def test_every_own_js_module_has_a_versioned_importmap_entry
response = request.get("/")
modules = Dir.glob(File.join(__dir__, "..", "public", "js", "*.js")).map { |path| File.basename(path) } - ["map.js"]
modules.each do |name|
assert_match(%r{"/js/#{Regexp.escape(name)}":\s*"/js/#{Regexp.escape(name)}\?v=\d+"}, response.body, "#{name} is missing from the importmap")
end
end
def test_offline_tiles_reports_unavailable_when_no_pmtiles_present def test_offline_tiles_reports_unavailable_when_no_pmtiles_present
response = request.get("/api/offline-tiles") response = request.get("/api/offline-tiles")
assert_equal({ "available" => false }, json_body(response)) assert_equal({ "available" => false }, json_body(response))
end end
def test_hiking_trails_reports_no_countries_when_none_extracted_yet
response = request.get("/api/hiking-trails")
assert_equal({ "countries" => [] }, json_body(response))
end
def test_hiking_trails_file_404s_for_a_country_with_no_extract
assert_equal 404, request.get("/tiles/hiking_trails/SK.geojson").status
end
def test_hiking_trails_file_404s_for_a_malformed_country_code
assert_equal 404, request.get("/tiles/hiking_trails/../../etc/passwd.geojson").status
end
def test_kiwix_reports_unavailable_when_no_url_configured def test_kiwix_reports_unavailable_when_no_url_configured
response = request.get("/api/kiwix") response = request.get("/api/kiwix")
assert_equal({ "available" => false, "url" => "" }, json_body(response)) assert_equal({ "available" => false, "url" => "" }, json_body(response))
@@ -32,6 +61,10 @@ class AppTest < Minitest::Test
assert_equal 404, request.get("/kiwix/search").status assert_equal 404, request.get("/kiwix/search").status
end end
def test_kiwix_nearby_404s_when_no_url_configured
assert_equal 404, request.get("/api/kiwix/nearby", params: { south: "48", west: "17", north: "48.1", east: "17.1" }).status
end
def test_gnss_position_unavailable_with_no_fix def test_gnss_position_unavailable_with_no_fix
response = request.get("/api/gnss/position", **logged_in_env) response = request.get("/api/gnss/position", **logged_in_env)
assert_equal({ "available" => false }, json_body(response)) assert_equal({ "available" => false }, json_body(response))
@@ -50,6 +83,11 @@ class AppTest < Minitest::Test
assert_equal 401, request.delete("/api/gpx/folder/x").status assert_equal 401, request.delete("/api/gpx/folder/x").status
assert_equal 401, request.post("/api/gpx/move").status assert_equal 401, request.post("/api/gpx/move").status
assert_equal 401, request.post("/api/plugins/test-plugin/run").status assert_equal 401, request.post("/api/plugins/test-plugin/run").status
assert_equal 401, request.post("/api/offline-tiles/build").status
assert_equal 401, request.post("/api/terrain-tiles/build").status
assert_equal 401, request.delete("/api/offline-tiles").status
assert_equal 401, request.delete("/api/offline-tiles/cache/SK").status
assert_equal 401, request.delete("/api/terrain-tiles").status
end end
# --- GPX/FIT upload and the tree --- # --- GPX/FIT upload and the tree ---
@@ -327,6 +365,118 @@ class AppTest < Minitest::Test
assert_equal 502, response.status assert_equal 502, response.status
end end
# --- offline regions ---
def test_offline_regions_lists_available_and_selected
body = json_body(request.get("/api/offline-regions"))
assert_operator body["available"].size, :>, 0
assert_includes body["available"], { "code" => "SK", "name" => "Slovakia" }
assert_equal [], body["selected"]
end
def test_offline_regions_post_requires_login
assert_equal 401, request.post("/api/offline-regions").status
end
def test_offline_regions_post_saves_the_selection
response = request.post("/api/offline-regions", params: { "codes" => %w[SK AT] }, **logged_in_env)
assert_equal 200, response.status
assert_equal %w[SK AT], json_body(request.get("/api/offline-regions"))["selected"]
end
def test_offline_regions_post_rejects_an_unknown_code
response = request.post("/api/offline-regions", params: { "codes" => ["XX"] }, **logged_in_env)
assert_equal 400, response.status
end
# --- pmtiles regeneration ---
#
# The full build (network + go-pmtiles) is covered in isolation, with
# fakes injected, by test_pmtiles_builder.rb — same "keep this offline"
# reasoning as the rest of this file. What's route-specific and worth
# covering here is the empty-selection validation and the
# 409-while-running guard, neither of which needs a real build to
# actually run.
def test_offline_tiles_build_status_defaults_to_idle
assert_equal({ "state" => "idle" }, json_body(request.get("/api/offline-tiles/build-status")))
assert_equal({ "state" => "idle" }, json_body(request.get("/api/terrain-tiles/build-status")))
end
def test_check_update_is_null_before_any_build
assert_nil json_body(request.get("/api/offline-tiles/check-update"))["update_available"]
assert_nil json_body(request.get("/api/terrain-tiles/check-update"))["update_available"]
end
def test_offline_tiles_build_is_409_with_no_selection
response = request.post("/api/offline-tiles/build", **logged_in_env)
assert_equal 409, response.status
end
def test_terrain_tiles_build_is_409_with_no_selection
response = request.post("/api/terrain-tiles/build", **logged_in_env)
assert_equal 409, response.status
end
# download_extract blocks on a Queue, called directly (not through the
# route, which has no way to inject a fake) so the build is
# deterministically still "running" when the route call below happens.
def test_offline_tiles_build_is_409_when_one_is_already_running
Maptool::PmtilesBuilder.set_selected_regions(Maptool::App::DATA_DIR, ["SK"])
gate = Queue.new
thread = Maptool::PmtilesBuilder.build_offline(data_dir: Maptool::App::DATA_DIR, download_extract: ->(_url, _dest) { gate.pop })
response = request.post("/api/offline-tiles/build", **logged_in_env)
assert_equal 409, response.status
ensure
gate.push("unused")
thread&.join
end
def test_offline_tiles_delete_is_ok_when_nothing_was_built
assert_equal 200, request.delete("/api/offline-tiles", **logged_in_env).status
end
def test_offline_tiles_delete_removes_the_file_and_cache
path = File.join(Maptool::App::DATA_DIR, "offline.pmtiles")
cache_dir = File.join(Maptool::App::DATA_DIR, "geofabrik_cache")
File.write(path, "fake")
FileUtils.mkdir_p(cache_dir)
File.write(File.join(cache_dir, "SK.osm.pbf"), "fake")
response = request.delete("/api/offline-tiles", **logged_in_env)
assert_equal 200, response.status
refute_path_exists path
refute_path_exists cache_dir
end
def test_offline_tiles_cache_lists_and_deletes_one_country
cache_dir = File.join(Maptool::App::DATA_DIR, "geofabrik_cache")
FileUtils.mkdir_p(cache_dir)
File.write(File.join(cache_dir, "SK.osm.pbf"), "fake")
extracts = json_body(request.get("/api/offline-tiles/cache"))["extracts"]
assert_equal ["SK"], extracts.map { |e| e["code"] }
response = request.delete("/api/offline-tiles/cache/SK", **logged_in_env)
assert_equal 200, response.status
refute_path_exists File.join(cache_dir, "SK.osm.pbf")
ensure
FileUtils.remove_entry(cache_dir) if cache_dir && Dir.exist?(cache_dir)
end
def test_offline_tiles_delete_is_409_when_a_build_is_running
Maptool::PmtilesBuilder.set_selected_regions(Maptool::App::DATA_DIR, ["SK"])
gate = Queue.new
thread = Maptool::PmtilesBuilder.build_offline(data_dir: Maptool::App::DATA_DIR, download_extract: ->(_url, _dest) { gate.pop })
assert_equal 409, request.delete("/api/offline-tiles", **logged_in_env).status
ensure
gate.push("unused")
thread&.join
end
# --- LiveTrack webhook --- # --- LiveTrack webhook ---
def test_livetrack_webhook_requires_the_configured_secret def test_livetrack_webhook_requires_the_configured_secret
+2
View File
@@ -66,6 +66,8 @@ module AppTestHelper
def teardown def teardown
FileUtils.rm_rf(Dir.glob(File.join(Maptool::App::DATA_DIR, "gpx", "*"))) FileUtils.rm_rf(Dir.glob(File.join(Maptool::App::DATA_DIR, "gpx", "*")))
File.write(File.join(Maptool::App::DATA_DIR, "livetrack.json"), "[]") File.write(File.join(Maptool::App::DATA_DIR, "livetrack.json"), "[]")
FileUtils.rm_f(File.join(Maptool::App::DATA_DIR, "offline_regions.json"))
Maptool::PmtilesBuilder.reset!
super super
end end
+125
View File
@@ -0,0 +1,125 @@
require_relative "test_helper"
require "maptool/hiking_trails"
# No real osmium calls — extract() is a thin subprocess-plus-parse wrapper
# (same "not independently tested" reasoning OverpassPois.fetch gets), but
# parse_opl is pure text in, GeoJSON out, and that's the part actually worth
# protecting: the OPL escaping rules and the way-id -> relation-member
# stitching are easy to get subtly wrong and osmium isn't installed in every
# environment this test suite runs in.
class HikingTrailsTest < Minitest::Test
def test_parse_opl_builds_a_multilinestring_from_member_way_geometry
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
w2 Thighway=path Nn11x20.088y49.18,n12x20.089y49.181
r1 Troute=hiking,type=route,name=Rysy Mw1@,w2@
OPL
features = Maptool::HikingTrails.parse_opl(opl)["features"]
assert_equal 1, features.size
feature = features.first
assert_equal "MultiLineString", feature["geometry"]["type"]
assert_equal [[[20.087, 49.179], [20.088, 49.18]], [[20.088, 49.18], [20.089, 49.181]]], feature["geometry"]["coordinates"]
assert_equal "Rysy", feature["properties"]["name"]
end
def test_parse_opl_skips_relations_that_are_not_route_hiking
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1 Ttype=multipolygon Mw1@outer
OPL
assert_empty Maptool::HikingTrails.parse_opl(opl)["features"]
end
def test_parse_opl_falls_back_to_description_when_there_is_no_name
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1 Troute=hiking,type=route,description=Rázc.%20%pri%20%Žabom%20%potoku%20%-%20%Rysy Mw1@
OPL
feature = Maptool::HikingTrails.parse_opl(opl)["features"].first
assert_equal "Rázc. pri Žabom potoku - Rysy", feature["properties"]["name"]
end
def test_parse_opl_unescapes_commas_and_equals_in_tag_values
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1 Troute=hiking,type=route,name=Salt%2c%%20%Pepper%3d%Co Mw1@
OPL
feature = Maptool::HikingTrails.parse_opl(opl)["features"].first
assert_equal "Salt, Pepper=Co", feature["properties"]["name"]
end
def test_parse_opl_carries_over_colour_network_operator_ref
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1 Troute=hiking,type=route,name=Trail,colour=red,network=rwn,operator=KST,ref=0933 Mw1@
OPL
properties = Maptool::HikingTrails.parse_opl(opl)["features"].first["properties"]
assert_equal "red", properties["colour"]
assert_equal "rwn", properties["network"]
assert_equal "KST", properties["operator"]
assert_equal "0933", properties["ref"]
end
def test_parse_opl_computes_distance_from_member_way_geometry
# Roughly 111.2m per 0.001 degree of latitude at the equator.
opl = <<~OPL
w1 Thighway=path Nn10x0.0y0.0,n11x0.0y0.001
r1 Troute=hiking,type=route,name=Trail Mw1@
OPL
distance = Maptool::HikingTrails.parse_opl(opl)["features"].first["properties"]["distance_meters"]
assert_in_delta 111.2, distance, 1.0
end
def test_parse_opl_drops_a_relation_whose_member_ways_are_all_missing
opl = <<~OPL
r1 Troute=hiking,type=route,name=Ghost Mw999@
OPL
assert_empty Maptool::HikingTrails.parse_opl(opl)["features"]
end
def test_parse_opl_carries_the_osm_relation_id_for_cross_country_dedup
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1246205 Troute=hiking,type=route,name=Trail Mw1@
OPL
feature = Maptool::HikingTrails.parse_opl(opl)["features"].first
assert_equal 1246205, feature["properties"]["id"]
end
def test_parse_opl_rounds_coordinates_to_5_decimal_places
opl = <<~OPL
w1 Thighway=path Nn10x20.0876543210y49.1791234567,n11x20.0880000001y49.1800000009
r1 Troute=hiking,type=route,name=Trail Mw1@
OPL
coords = Maptool::HikingTrails.parse_opl(opl)["features"].first["geometry"]["coordinates"]
assert_equal [[[20.08765, 49.17912], [20.088, 49.18]]], coords
end
def test_parse_opl_ignores_a_dangling_member_reference_but_keeps_the_rest
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
r1 Troute=hiking,type=route,name=Trail Mw1@,w999@
OPL
features = Maptool::HikingTrails.parse_opl(opl)["features"]
assert_equal 1, features.size
assert_equal 1, features.first["geometry"]["coordinates"].size
end
end
+54
View File
@@ -70,4 +70,58 @@ class KiwixTest < Minitest::Test
assert_equal "GET /kiwix/content/wikipedia/Dlh%C3%A9_diely HTTP/1.1\r\n", request_line assert_equal "GET /kiwix/content/wikipedia/Dlh%C3%A9_diely HTTP/1.1\r\n", request_line
assert_equal "200", response.code assert_equal "200", response.code
end end
# kiwix-serve's own geo search never echoes back a result's coordinates
# (verified live against a real ZIM) — .nearby fetches each matching
# article's page too and reads its <meta name="geo.position"> tag, so
# this stub server has to answer both the search request and each
# article-page request that follows, in turn.
def test_nearby_builds_a_feature_per_geotagged_search_result
server = TCPServer.new("127.0.0.1", 0)
port = server.addr[1]
search_xml = <<~XML
<?xml version="1.0" encoding="UTF-8"?>
<rss><channel>
<item><title>Foo</title><link>/kiwix/content/wikipedia/Foo</link></item>
<item><title>Bar</title><link>/kiwix/content/wikipedia/Bar</link></item>
</channel></rss>
XML
thread = Thread.new do
# 3, not 2: .nearby fetches every search result's own page to look
# for coordinates, including Bar's — which turns out to have none
# and gets filtered out, but the request still happens. Leaving this
# at 2 the first time round left that third connection's request
# unaccepted, and it hung for the full default read_timeout instead
# of failing fast.
3.times do
client = server.accept
request_line = client.gets
body =
if request_line.include?("/kiwix/search")
search_xml
elsif request_line.include?("/Foo")
'<meta name="geo.position" content="48.1;17.2">'
else
"no coordinates here"
end
client.print("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: #{body.bytesize}\r\n\r\n#{body}")
client.close
end
end
geojson = Maptool::Kiwix.nearby("http://127.0.0.1:#{port}", [48.0, 17.0, 48.2, 17.3])
thread.join
server.close
assert_equal 1, geojson["features"].size, "Bar has no geo.position tag and must be skipped, not crash"
feature = geojson["features"].first
assert_equal "Foo", feature["properties"]["name"]
assert_equal [17.2, 48.1], feature["geometry"]["coordinates"]
end
def test_nearby_returns_an_empty_collection_when_the_search_itself_fails
geojson = Maptool::Kiwix.nearby("http://127.0.0.1:1", [48.0, 17.0, 48.2, 17.3], open_timeout: 1)
assert_equal({ "type" => "FeatureCollection", "features" => [] }, geojson)
end
end end
+38 -3
View File
@@ -16,6 +16,7 @@ class LiveTrackClientTest < Minitest::Test
<script>self.__next_f.push([1,"6:[\\"$\\",\\"div\\",null,{}]"])</script> <script>self.__next_f.push([1,"6:[\\"$\\",\\"div\\",null,{}]"])</script>
<script>self.__next_f.push([1,"9:{\\"session\\":{\\"sessionName\\":\\"Test Ride\\",\\"start\\":\\"2026-01-01T00:00:00Z\\",\\"end\\":\\"2026-01-01T01:00:00Z\\"}}"])</script> <script>self.__next_f.push([1,"9:{\\"session\\":{\\"sessionName\\":\\"Test Ride\\",\\"start\\":\\"2026-01-01T00:00:00Z\\",\\"end\\":\\"2026-01-01T01:00:00Z\\"}}"])</script>
<script>self.__next_f.push([1,"10:{\\"unrelated\\":true,\\"nested\\":{\\"data\\":{\\"pages\\":[{\\"trackPoints\\":[{\\"position\\":{\\"lat\\":48.1,\\"lon\\":17.2},\\"dateTime\\":\\"2026-01-01T00:00:00Z\\",\\"totalDistanceMeters\\":\\"$undefined\\"},{\\"position\\":{\\"lat\\":48.2,\\"lon\\":17.3},\\"dateTime\\":\\"2026-01-01T00:01:00Z\\",\\"activityType\\":\\"CYCLING\\",\\"totalDistanceMeters\\":5000,\\"totalDurationSecs\\":600}]}]}}}"])</script> <script>self.__next_f.push([1,"10:{\\"unrelated\\":true,\\"nested\\":{\\"data\\":{\\"pages\\":[{\\"trackPoints\\":[{\\"position\\":{\\"lat\\":48.1,\\"lon\\":17.2},\\"dateTime\\":\\"2026-01-01T00:00:00Z\\",\\"totalDistanceMeters\\":\\"$undefined\\"},{\\"position\\":{\\"lat\\":48.2,\\"lon\\":17.3},\\"dateTime\\":\\"2026-01-01T00:01:00Z\\",\\"activityType\\":\\"CYCLING\\",\\"totalDistanceMeters\\":5000,\\"totalDurationSecs\\":600}]}]}}}"])</script>
<script>self.__next_f.push([1,"11:{\\"courses\\":[{\\"name\\":\\"40 km okruh\\",\\"geoPoints\\":[{\\"position\\":{\\"lat\\":48.05,\\"lon\\":17.35},\\"distanceMeters\\":0},{\\"position\\":{\\"lat\\":48.06,\\"lon\\":17.36},\\"distanceMeters\\":1000}]}]}"])</script>
</body></html> </body></html>
HTML HTML
@@ -49,18 +50,52 @@ class LiveTrackClientTest < Minitest::Test
assert_equal "Test Ride", summary["session_name"] assert_equal "Test Ride", summary["session_name"]
assert_equal "CYCLING", summary["activity_type"] assert_equal "CYCLING", summary["activity_type"]
assert_equal 5000, summary["distance_meters"] assert_equal 5000, summary["distance_meters"]
refute summary["active"], "session has an end timestamp" refute summary["active"], "last track point is long in the past"
end end
def test_summarize_marks_active_when_session_has_no_end # Not session["end"].nil? (the original signal, before 2026-09-20 — see
# LiveTrackClient's own comment): verified live against a real,
# still-recording session that day, Garmin had already set `end` to
# exactly start + 24h (the share link's own expiry, not an end time),
# so that check called an active session "ended".
def test_summarize_marks_active_when_last_point_is_recent
payloads = @client.send(:parse_payloads, FAKE_PAGE) payloads = @client.send(:parse_payloads, FAKE_PAGE)
points = @client.send(:clean, @client.send(:search, payloads) { |n| n["trackPoints"] if n["trackPoints"].is_a?(Array) }) points = @client.send(:clean, @client.send(:search, payloads) { |n| n["trackPoints"] if n["trackPoints"].is_a?(Array) })
session = @client.send(:clean, @client.send(:search, payloads) { |n| n if n["sessionName"] }).merge("end" => nil) points[-1] = points[-1].merge("dateTime" => Time.now.utc.iso8601)
# end present and in the future, same shape Garmin actually sends for a
# session that's still live — active must come from the point, not this.
session = @client.send(:clean, @client.send(:search, payloads) { |n| n if n["sessionName"] })
.merge("end" => (Time.now.utc + 24 * 60 * 60).iso8601)
result = Maptool::LiveTrackClient::Result.new(points: points, session: session) result = Maptool::LiveTrackClient::Result.new(points: points, session: session)
assert Maptool::LiveTrackClient.summarize(result)["active"] assert Maptool::LiveTrackClient.summarize(result)["active"]
end end
# Verified live the same day: Garmin updates a real session's `end` from
# the future placeholder to within a second of the last point's own
# time immediately once the activity is saved on the watch — so a fresh
# last point alone isn't enough, `end` already being in the past has to
# win even then.
def test_summarize_marks_inactive_once_end_is_in_the_past_even_with_a_fresh_point
payloads = @client.send(:parse_payloads, FAKE_PAGE)
points = @client.send(:clean, @client.send(:search, payloads) { |n| n["trackPoints"] if n["trackPoints"].is_a?(Array) })
points[-1] = points[-1].merge("dateTime" => Time.now.utc.iso8601)
session = @client.send(:clean, @client.send(:search, payloads) { |n| n if n["sessionName"] })
.merge("end" => (Time.now.utc - 1).iso8601)
result = Maptool::LiveTrackClient::Result.new(points: points, session: session)
refute Maptool::LiveTrackClient.summarize(result)["active"]
end
def test_finds_the_planned_course_alongside_the_actual_track
payloads = @client.send(:parse_payloads, FAKE_PAGE)
course = @client.send(:clean, @client.send(:search, payloads) { |n| n["courses"].first if n["courses"].is_a?(Array) && !n["courses"].empty? })
assert_equal "40 km okruh", course["name"]
assert_equal 2, course["geoPoints"].size
assert_in_delta 48.06, course["geoPoints"].last["position"]["lat"]
end
def test_no_track_points_found_in_a_page_without_them def test_no_track_points_found_in_a_page_without_them
points = @client.send(:search, @client.send(:parse_payloads, "<html></html>")) { |n| n["trackPoints"] if n["trackPoints"].is_a?(Array) } points = @client.send(:search, @client.send(:parse_payloads, "<html></html>")) { |n| n["trackPoints"] if n["trackPoints"].is_a?(Array) }
assert_nil points assert_nil points
+17
View File
@@ -56,6 +56,23 @@ class OverpassPoisTest < Minitest::Test
assert_equal "Opening hours: 24/7\nOperator: Town Council\nhttps://example.com", description assert_equal "Opening hours: 24/7\nOperator: Town Council\nhttps://example.com", description
end end
def test_to_geojson_strips_embedded_newlines_from_tag_values
response = {
"elements" => [
{
"type" => "node",
"lat" => 48.1,
"lon" => 17.2,
"tags" => { "amenity" => "toilets", "operator" => "Acme Co\nhttps://evil.example" },
},
],
}
description = Maptool::OverpassPois.to_geojson(response)["features"].first["properties"]["description"]
assert_equal "Operator: Acme Co https://evil.example", description, "an embedded newline must not inject a second, independently-rendered line"
end
def test_to_geojson_omits_description_when_no_recognized_tags_are_present def test_to_geojson_omits_description_when_no_recognized_tags_are_present
response = { "elements" => [{ "type" => "node", "lat" => 48.1, "lon" => 17.2, "tags" => { "amenity" => "toilets" } }] } response = { "elements" => [{ "type" => "node", "lat" => 48.1, "lon" => 17.2, "tags" => { "amenity" => "toilets" } }] }
+11
View File
@@ -53,6 +53,17 @@ class PluginsTest < Minitest::Test
assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) } assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) }
end end
# Distinct from the "did not print valid JSON" message above — that one's
# about the script's stdout, this is about plugin.json itself, before the
# script is ever run. Same exception class either way, but a clear
# message matters since this is what a "Failed" plugin run shows the user
# (see public/js/plugins.js).
def test_run_raises_a_distinct_message_for_an_invalid_manifest
File.write(File.join(@plugin_dir, "plugin.json"), "{not valid json")
error = assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) }
assert_match(/invalid plugin\.json/, error.message)
end
# Multiple dirs (app.rb's PLUGINS_DIRS: the built-in plugins/ plus an # Multiple dirs (app.rb's PLUGINS_DIRS: the built-in plugins/ plus an
# optional private one under data/plugins/) — list/run both accept an # optional private one under data/plugins/) — list/run both accept an
# array, not just the single-dir form the tests above exercise. # array, not just the single-dir form the tests above exercise.
+673
View File
@@ -0,0 +1,673 @@
require_relative "test_helper"
require "maptool/pmtiles_builder"
require "tmpdir"
require "fileutils"
# Network calls (Geofabrik downloads/HEADs, the mapterhorn manifest) are
# never exercised here — same "keep this offline/deterministic" reasoning
# test_helper.rb already gives for /api/plugins staying off the real
# Overpass API — build_offline/build_terrain take download_extract/
# fetch_manifest precisely so tests can swap those out. Fake `pmtiles`,
# `osmium`, and `java` binaries (PMTILES_BIN/OSMIUM_BIN/JAVA_BIN) stand in
# for the real tools, logging each invocation's subcommand/first arg to
# PMTILES_LOG so tests can assert on what actually got called (e.g. "was
# merge invoked, and with what").
#
# Country codes below are real ones from the vendored
# lib/maptool/data/europe_countries.geojson (Slovakia, Iceland, Spain) —
# chosen so their real bboxes fall cleanly inside/outside the fake
# manifest's chunk extents below, without needing a second fake geometry
# source.
class PmtilesBuilderTest < Minitest::Test
def setup
@data_dir = Dir.mktmpdir("maptool-pmtiles-test-data")
@bin_dir = Dir.mktmpdir("maptool-pmtiles-test-bin")
@log = File.join(@bin_dir, "pmtiles.log")
# Output path: $3 for `extract SOURCE OUTPUT --region=... --maxzoom=...`
# and `convert INPUT OUTPUT` alike; the *last* arg for `merge INPUT_1
# [INPUT_2 ...] OUTPUT`, since its input count varies.
@pmtiles_ok = File.join(@bin_dir, "pmtiles-ok")
File.write(@pmtiles_ok, <<~SH)
#!/bin/sh
echo "$1 $2" >> "$PMTILES_LOG"
case "$1" in
merge)
for last; do :; done
echo fake > "$last"
;;
*)
echo fake > "$3"
;;
esac
SH
File.chmod(0o755, @pmtiles_ok)
@pmtiles_fail = File.join(@bin_dir, "pmtiles-fail")
File.write(@pmtiles_fail, "#!/bin/sh\necho 'boom' >&2\nexit 1\n")
File.chmod(0o755, @pmtiles_fail)
# Mimics go-pmtiles' real extract failure mode: hundreds of progress
# lines before the actual error — see command_output's tail-truncation.
@pmtiles_fail_noisy = File.join(@bin_dir, "pmtiles-fail-noisy")
File.write(@pmtiles_fail_noisy, <<~SH)
#!/bin/sh
i=0
while [ "$i" -lt 200 ]; do
echo "fetching $i dirs, $i chunks, 1 requests" >&2
i=$((i + 1))
done
echo "stream error: stream ID 9; INTERNAL_ERROR" >&2
exit 1
SH
File.chmod(0o755, @pmtiles_fail_noisy)
# osmium merge PBF1 PBF2 ... -o OUTPUT — output is always "-o"'s next
# arg, unlike pmtiles' varying-position output above.
@osmium_ok = File.join(@bin_dir, "osmium-ok")
File.write(@osmium_ok, <<~SH)
#!/bin/sh
echo "osmium $1" >> "$PMTILES_LOG"
prev=""
for arg; do
if [ "$prev" = "-o" ]; then echo fake > "$arg"; fi
prev="$arg"
done
SH
File.chmod(0o755, @osmium_ok)
# Same shape as osmium-ok, but fails on tags-filter specifically (the
# first subcommand HikingTrails.extract runs) while still succeeding
# on merge — lets a single-country build (which never calls merge)
# exercise "hiking-trails extraction failed" without also breaking
# offline.pmtiles' own osmium use in a multi-country test.
@osmium_fail_tags_filter = File.join(@bin_dir, "osmium-fail-tags-filter")
File.write(@osmium_fail_tags_filter, <<~SH)
#!/bin/sh
echo "osmium $1" >> "$PMTILES_LOG"
if [ "$1" = "tags-filter" ]; then
echo "boom" >&2
exit 1
fi
prev=""
for arg; do
if [ "$prev" = "-o" ]; then echo fake > "$arg"; fi
prev="$arg"
done
SH
File.chmod(0o755, @osmium_fail_tags_filter)
# Stands in for `java -jar planetiler.jar --osm-path=... --output=OUT`
# — PLANETILER_JAR itself is irrelevant here (this fake ignores -jar's
# argument entirely), only JAVA_BIN needs pointing at it.
@java_ok = File.join(@bin_dir, "java-ok")
File.write(@java_ok, <<~SH)
#!/bin/sh
echo "planetiler ${3%%=*}" >> "$PMTILES_LOG"
for arg; do
case "$arg" in
--output=*) echo fake > "${arg#--output=}" ;;
esac
done
SH
File.chmod(0o755, @java_ok)
@java_fail = File.join(@bin_dir, "java-fail")
File.write(@java_fail, "#!/bin/sh\necho 'boom' >&2\nexit 1\n")
File.chmod(0o755, @java_fail)
@original_pmtiles_bin = ENV["PMTILES_BIN"]
@original_osmium_bin = ENV["OSMIUM_BIN"]
@original_java_bin = ENV["JAVA_BIN"]
ENV["PMTILES_LOG"] = @log
ENV["OSMIUM_BIN"] = @osmium_ok
ENV["JAVA_BIN"] = @java_ok
Maptool::PmtilesBuilder.reset!
end
def teardown
FileUtils.remove_entry(@data_dir)
FileUtils.remove_entry(@bin_dir)
ENV["PMTILES_BIN"] = @original_pmtiles_bin
ENV["OSMIUM_BIN"] = @original_osmium_bin
ENV["JAVA_BIN"] = @original_java_bin
ENV.delete("PMTILES_LOG")
Maptool::PmtilesBuilder.reset!
end
# download_extract stub: writes a marker file (same "fake" content the
# pmtiles/osmium/java fixtures use) instead of hitting Geofabrik, and
# returns a fixed Last-Modified so sidecar-meta assertions are stable.
# `calls` (an Array, shared across builds if the caller wants it) records
# every URL actually downloaded, for asserting the per-country cache
# skipped (or didn't skip) a re-download.
def fake_download(last_modified: "Wed, 10 Sep 2026 08:00:01 GMT", calls: nil)
->(url, dest) {
calls << url if calls
File.write(dest, "fake")
last_modified
}
end
def log_lines
File.exist?(@log) ? File.readlines(@log, chomp: true) : []
end
def select_regions(*codes)
Maptool::PmtilesBuilder.set_selected_regions(@data_dir, codes)
end
# chunk-a covers Slovakia's real extent, chunk-b covers Iceland's —
# Spain falls in neither, for the "no chunk intersects" case.
def fake_manifest(chunk_a_md5: "md5-a")
[
{ "name" => "planet.pmtiles", "url" => "https://example.com/planet.pmtiles", "md5sum" => "md5-planet", "min_lon" => -180.0, "min_lat" => -85.0, "max_lon" => 180.0, "max_lat" => 85.0, "min_zoom" => 0, "max_zoom" => 12 },
{ "name" => "chunk-a.pmtiles", "url" => "https://example.com/chunk-a.pmtiles", "md5sum" => chunk_a_md5, "min_lon" => 16.0, "min_lat" => 47.0, "max_lon" => 23.0, "max_lat" => 50.0, "min_zoom" => 13, "max_zoom" => 15 },
{ "name" => "chunk-b.pmtiles", "url" => "https://example.com/chunk-b.pmtiles", "md5sum" => "md5-b", "min_lon" => -25.0, "min_lat" => 63.0, "max_lon" => -13.0, "max_lat" => 67.0, "min_zoom" => 13, "max_zoom" => 15 },
]
end
def test_offline_and_terrain_status_start_idle
assert_equal({ state: "idle" }, Maptool::PmtilesBuilder.offline_status)
assert_equal({ state: "idle" }, Maptool::PmtilesBuilder.terrain_status)
end
# --- region selection ---
def test_available_regions_includes_known_countries
codes = Maptool::PmtilesBuilder.available_regions.map { |r| r[:code] }
assert_includes codes, "SK"
assert_includes codes, "IS"
end
def test_selected_regions_defaults_to_empty
assert_empty Maptool::PmtilesBuilder.selected_regions(@data_dir)
end
def test_set_selected_regions_round_trips
select_regions("SK", "AT")
assert_equal ["SK", "AT"], Maptool::PmtilesBuilder.selected_regions(@data_dir)
end
def test_set_selected_regions_rejects_an_unknown_code
assert_raises(Maptool::PmtilesBuilder::BuildError) { select_regions("SK", "XX") }
end
# --- offline.pmtiles ---
def test_build_offline_raises_with_no_selection
assert_raises(Maptool::PmtilesBuilder::BuildError) do
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
end
end
def test_build_offline_writes_the_file_and_reports_done
select_regions("SK")
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
assert_path_exists File.join(@data_dir, "offline.pmtiles")
refute_path_exists File.join(@data_dir, "offline.tmp.pmtiles")
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
assert_equal ["planetiler --osm-path", "osmium tags-filter", "osmium add-locations-to-ways", "osmium cat"], log_lines
assert_equal(
{ "sources" => [{ "code" => "SK", "slug" => "europe/slovakia", "last_modified" => "Wed, 10 Sep 2026 08:00:01 GMT" }] },
Maptool::PmtilesBuilder.offline_meta(@data_dir),
)
end
def test_build_offline_also_writes_a_per_country_hiking_trails_geojson
select_regions("SK")
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
# The fake osmium binary doesn't produce real OPL output, just
# confirms the file lands (one per country) and is valid (empty)
# GeoJSON — the real parsing logic is covered by test_hiking_trails.rb
# against real OPL fixtures.
assert_equal ["SK"], Maptool::PmtilesBuilder.hiking_trails_countries(@data_dir)
path = Maptool::PmtilesBuilder.hiking_trails_country_path(@data_dir, "SK")
assert_equal({ "type" => "FeatureCollection", "features" => [] }, JSON.parse(File.read(path)))
end
def test_build_offline_only_re_extracts_hiking_trails_for_a_newly_added_country
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
File.write(@log, "")
select_regions("SK", "AT")
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download,
fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" },
).join
# SK's cached extract is already newer than SK.osm.pbf (untouched
# since the first build), so only AT's hiking trails get (re-)extracted
# — SK.osm.pbf itself is also skipped by cached_extract's own
# Last-Modified check, "osmium merge" runs regardless since two
# countries are now selected.
assert_equal(
["osmium merge", "planetiler --osm-path", "osmium tags-filter", "osmium add-locations-to-ways", "osmium cat"],
log_lines,
)
assert_equal %w[AT SK], Maptool::PmtilesBuilder.hiking_trails_countries(@data_dir)
end
# The actual reasoning: hiking trails are a bonus overlay bolted onto the
# existing offline.pmtiles build, not the core map — an osmium hiccup on
# this one step shouldn't take down an otherwise-successful build.
def test_build_offline_still_succeeds_when_hiking_trails_extraction_fails
select_regions("SK")
ENV["OSMIUM_BIN"] = @osmium_fail_tags_filter
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
assert_path_exists File.join(@data_dir, "offline.pmtiles")
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
refute_path_exists File.join(@data_dir, "hiking_trails.geojson")
refute_path_exists "#{File.join(@data_dir, "hiking_trails.geojson")}.tmp"
end
# Two countries selected — osmium merge should run before planetiler,
# each downloaded source recorded in the sidecar meta.
def test_build_offline_merges_multiple_selected_countries
select_regions("SK", "AT")
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
assert_equal(
[
"osmium merge", "planetiler --osm-path",
"osmium tags-filter", "osmium add-locations-to-ways", "osmium cat", # SK's own hiking-trails extraction
"osmium tags-filter", "osmium add-locations-to-ways", "osmium cat", # AT's own
],
log_lines,
)
codes = Maptool::PmtilesBuilder.offline_meta(@data_dir)["sources"].map { |s| s["code"] }
assert_equal %w[SK AT], codes
end
# The actual bug report this covers: hitting "Apply" after tweaking the
# region selection used to re-download every selected country's .osm.pbf
# from scratch, even ones already fetched by an earlier build.
def test_build_offline_skips_redownloading_an_unchanged_cached_country
select_regions("SK")
calls = []
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download(calls: calls)).join
assert_equal 1, calls.size
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download(calls: calls),
fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" },
).join
assert_equal 1, calls.size
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
end
def test_build_offline_redownloads_a_country_whose_extract_changed
select_regions("SK")
calls = []
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download(calls: calls)).join
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download(calls: calls, last_modified: "Thu, 11 Sep 2026 08:00:01 GMT"),
fetch_last_modified: ->(_url) { "Thu, 11 Sep 2026 08:00:01 GMT" },
).join
assert_equal 2, calls.size
end
# A cache-freshness check that itself fails (Geofabrik unreachable) should
# fall back to re-downloading rather than aborting the build.
def test_build_offline_redownloads_when_the_cache_check_fails
select_regions("SK")
calls = []
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download(calls: calls)).join
fetch_last_modified = ->(_url) { raise Maptool::PmtilesBuilder::BuildError, "could not reach geofabrik" }
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download(calls: calls),
fetch_last_modified: fetch_last_modified,
).join
assert_equal 2, calls.size
assert_equal "done", Maptool::PmtilesBuilder.offline_status[:state]
end
def test_build_offline_only_downloads_a_newly_added_country
select_regions("SK")
calls = []
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download(calls: calls)).join
select_regions("SK", "AT")
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download(calls: calls),
fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" },
).join
assert_equal 1, calls.count { |url| url.include?("slovakia") }
assert_equal 1, calls.count { |url| url.include?("austria") }
end
# offline_status[:step] should say which country is in flight right now
# and which of the current build's selection are already done/still
# pending — not just "running since X".
def test_build_offline_reports_progress_step_while_running
select_regions("SK", "AT")
started = Queue.new
gate = Queue.new
download_extract = ->(url, dest) {
started.push(url)
gate.pop
File.write(dest, "fake")
"Wed, 10 Sep 2026 08:00:01 GMT"
}
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: download_extract)
started.pop
assert_equal "fetching SK (1/2) — pending: AT", Maptool::PmtilesBuilder.offline_status[:step]
gate.push(:go)
started.pop
assert_equal "fetching AT (2/2) — done: SK", Maptool::PmtilesBuilder.offline_status[:step]
gate.push(:go)
thread.join
assert_nil Maptool::PmtilesBuilder.offline_status[:step]
end
def test_delete_offline_data_removes_the_file_meta_and_cache
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
assert_path_exists File.join(@data_dir, "offline.pmtiles")
assert_path_exists File.join(@data_dir, "geofabrik_cache", "SK.osm.pbf")
Maptool::PmtilesBuilder.delete_offline_data(@data_dir)
refute_path_exists File.join(@data_dir, "offline.pmtiles")
refute_path_exists File.join(@data_dir, "geofabrik_cache")
assert_nil Maptool::PmtilesBuilder.offline_meta(@data_dir)
end
def test_delete_offline_data_is_a_noop_when_nothing_was_built
Maptool::PmtilesBuilder.delete_offline_data(@data_dir) # should not raise
end
def test_delete_offline_data_raises_while_a_build_is_running
select_regions("SK")
gate = Queue.new
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: ->(_url, _dest) { gate.pop })
assert_raises(Maptool::PmtilesBuilder::BuildError) { Maptool::PmtilesBuilder.delete_offline_data(@data_dir) }
ensure
gate.push("unused")
thread&.join
end
def test_delete_cached_extract_removes_only_that_country
select_regions("SK", "AT")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
Maptool::PmtilesBuilder.delete_cached_extract(@data_dir, "SK")
refute_path_exists File.join(@data_dir, "geofabrik_cache", "SK.osm.pbf")
assert_path_exists File.join(@data_dir, "geofabrik_cache", "AT.osm.pbf")
end
def test_delete_terrain_data_removes_the_file_and_meta
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_ok
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
assert_path_exists File.join(@data_dir, "terrain.pmtiles")
Maptool::PmtilesBuilder.delete_terrain_data(@data_dir)
refute_path_exists File.join(@data_dir, "terrain.pmtiles")
assert_nil Maptool::PmtilesBuilder.terrain_meta(@data_dir)
end
def test_cached_extracts_is_empty_before_any_download
assert_empty Maptool::PmtilesBuilder.cached_extracts(@data_dir)
end
# Deliberately still selected — cached_extracts reflects the cache on
# disk, not the current selection or the last build's own meta (see
# cached_extracts' own comment for why those three can all disagree).
def test_cached_extracts_lists_what_was_downloaded
select_regions("SK", "AT")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
extracts = Maptool::PmtilesBuilder.cached_extracts(@data_dir)
assert_equal %w[AT SK], extracts.map { |e| e["code"] }
assert_equal [4, 4], extracts.map { |e| e["size"] } # "fake".bytesize
assert_equal ["Wed, 10 Sep 2026 08:00:01 GMT"] * 2, extracts.map { |e| e["last_modified"] }
end
def test_offline_meta_is_nil_before_any_build
assert_nil Maptool::PmtilesBuilder.offline_meta(@data_dir)
end
def test_offline_update_available_is_nil_before_any_build
assert_nil Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_last_modified: ->(_url) { "unused" })
end
def test_offline_update_available_is_false_when_unchanged
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
refute Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" })
end
def test_offline_update_available_is_true_when_a_newer_extract_exists
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
assert Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_last_modified: ->(_url) { "Thu, 11 Sep 2026 08:00:01 GMT" })
end
# Same recorded source, but the selection has since grown — recorded
# sources no longer match what the current selection would need.
def test_offline_update_available_is_true_when_the_selection_grew
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
select_regions("SK", "AT")
assert Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" })
end
def test_offline_update_available_is_nil_when_geofabrik_is_unreachable
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
fetch_last_modified = ->(_url) { raise Maptool::PmtilesBuilder::BuildError, "could not reach geofabrik" }
assert_nil Maptool::PmtilesBuilder.offline_update_available?(@data_dir, fetch_last_modified: fetch_last_modified)
end
def test_build_offline_reports_an_error_status_on_non_zero_exit
select_regions("SK")
ENV["JAVA_BIN"] = @java_fail
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
refute_path_exists File.join(@data_dir, "offline.pmtiles")
status = Maptool::PmtilesBuilder.offline_status
assert_equal "error", status[:state]
assert_match(/exited with status/, status[:message])
end
def test_build_offline_reports_a_clear_error_when_planetiler_is_missing
select_regions("SK")
ENV["JAVA_BIN"] = File.join(@bin_dir, "does-not-exist")
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
status = Maptool::PmtilesBuilder.offline_status
assert_equal "error", status[:state]
assert_match(/Java and planetiler\.jar installed/, status[:message])
end
# download_extract blocks on a Queue so the build is deterministically
# still "running" when the second call happens, rather than racing a real
# one to see which state wins.
def test_build_offline_raises_when_one_is_already_running
select_regions("SK")
gate = Queue.new
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: ->(_url, _dest) { gate.pop })
assert_raises(Maptool::PmtilesBuilder::BuildError) do
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
end
ensure
gate.push("Wed, 10 Sep 2026 08:00:01 GMT")
thread&.join
end
# --- terrain.pmtiles ---
def test_build_terrain_raises_with_no_selection
assert_raises(Maptool::PmtilesBuilder::BuildError) do
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
end
end
# Spain intersects neither fake chunk — only planet.pmtiles matches, so
# this should be a single extract straight to the final path, no merge.
def test_build_terrain_extracts_only_planet_when_no_chunk_intersects
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_ok
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
assert_path_exists File.join(@data_dir, "terrain.pmtiles")
assert_equal "done", Maptool::PmtilesBuilder.terrain_status[:state]
assert_equal ["extract https://example.com/planet.pmtiles"], log_lines
end
# Slovakia intersects chunk-a only — planet.pmtiles (always) plus that
# one chunk get extracted, then merged.
def test_build_terrain_extracts_and_merges_a_single_intersecting_chunk
select_regions("SK")
ENV["PMTILES_BIN"] = @pmtiles_ok
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
assert_path_exists File.join(@data_dir, "terrain.pmtiles")
assert_equal "done", Maptool::PmtilesBuilder.terrain_status[:state]
extracted = log_lines.select { |line| line.start_with?("extract ") }
merged = log_lines.select { |line| line.start_with?("merge ") }
assert_equal ["extract https://example.com/planet.pmtiles", "extract https://example.com/chunk-a.pmtiles"], extracted
assert_equal 1, merged.size
assert_equal(
{ "sources" => [{ "name" => "planet.pmtiles", "md5sum" => "md5-planet" }, { "name" => "chunk-a.pmtiles", "md5sum" => "md5-a" }] },
Maptool::PmtilesBuilder.terrain_meta(@data_dir),
)
end
def test_terrain_meta_is_nil_before_any_build
assert_nil Maptool::PmtilesBuilder.terrain_meta(@data_dir)
end
def test_terrain_update_available_is_false_when_unchanged
select_regions("SK")
ENV["PMTILES_BIN"] = @pmtiles_ok
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
refute Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest })
end
# Same selection, but chunk-a's md5sum has since changed upstream.
def test_terrain_update_available_is_true_when_a_source_changed
select_regions("SK")
ENV["PMTILES_BIN"] = @pmtiles_ok
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
assert Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest(chunk_a_md5: "md5-a-v2") })
end
# Same recorded sources, but the selection has since grown to also cover
# chunk-b — recorded sources no longer match what the current selection
# would need.
def test_terrain_update_available_is_true_when_the_selection_grew
select_regions("SK")
ENV["PMTILES_BIN"] = @pmtiles_ok
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest }).join
select_regions("SK", "IS")
assert Maptool::PmtilesBuilder.terrain_update_available?(@data_dir, fetch_manifest: -> { fake_manifest })
end
# Slovakia + Iceland together intersect both fake chunks — planet + both
# chunks, one 3-way merge.
def test_build_terrain_merges_multiple_intersecting_chunks
select_regions("SK", "IS")
ENV["PMTILES_BIN"] = @pmtiles_ok
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
assert_equal "done", Maptool::PmtilesBuilder.terrain_status[:state]
extracted = log_lines.count { |line| line.start_with?("extract ") }
assert_equal 3, extracted # planet + chunk-a + chunk-b
assert_equal 1, log_lines.count { |line| line.start_with?("merge ") }
end
def test_build_terrain_reports_a_clear_error_when_the_manifest_is_missing_planet
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_ok
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest.reject { |i| i["name"] == "planet.pmtiles" } })
thread.join
status = Maptool::PmtilesBuilder.terrain_status
assert_equal "error", status[:state]
assert_match(/missing planet\.pmtiles/, status[:message])
end
def test_build_terrain_reports_an_error_status_on_non_zero_exit
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_fail
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
refute_path_exists File.join(@data_dir, "terrain.pmtiles")
status = Maptool::PmtilesBuilder.terrain_status
assert_equal "error", status[:state]
assert_match(/exited with status/, status[:message])
end
# The real trigger for this: a Mapterhorn extract that failed after ~7000
# progress lines, producing a 500k+ character status[:message] that was
# unreadable in the web UI.
def test_build_terrain_truncates_a_noisy_command_failure
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_fail_noisy
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { fake_manifest })
thread.join
status = Maptool::PmtilesBuilder.terrain_status
assert_equal "error", status[:state]
assert_match(/\(\d+ earlier lines omitted\)/, status[:message])
assert_match(/stream error: stream ID 9/, status[:message])
assert_operator status[:message].length, :<, 5000
end
def test_build_terrain_raises_when_one_is_already_running
select_regions("ES")
ENV["PMTILES_BIN"] = @pmtiles_ok
gate = Queue.new
thread = Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { gate.pop })
assert_raises(Maptool::PmtilesBuilder::BuildError) do
Maptool::PmtilesBuilder.build_terrain(data_dir: @data_dir, fetch_manifest: -> { "unused" })
end
ensure
gate.push(fake_manifest)
thread&.join
end
end
+38 -2
View File
@@ -1,7 +1,10 @@
<div id="app"> <div id="app">
<header class="topbar"> <header class="topbar">
<span class="topbar__brand" title="rev <%= revision %>">MapTool</span> <span class="topbar__brand" title="rev <%= revision %>">MapTool</span>
<span class="topbar__coords"></span> <span class="topbar__center">
<span class="topbar__coords"></span>
<span class="topbar__sun" hidden></span>
</span>
<span class="topbar__offline-status" hidden></span> <span class="topbar__offline-status" hidden></span>
<div class="topbar__auth"> <div class="topbar__auth">
<% if logged_in %> <% if logged_in %>
@@ -28,6 +31,11 @@
<div class="profile-panel__charts"></div> <div class="profile-panel__charts"></div>
</div> </div>
<div id="trail-panel" class="trail-panel" hidden>
<button type="button" class="trail-panel__close" aria-label="Close">&times;</button>
<div class="trail-panel__body"></div>
</div>
<div id="basemap-panel" class="basemap-panel" hidden> <div id="basemap-panel" class="basemap-panel" hidden>
<div class="basemap-panel__header"> <div class="basemap-panel__header">
<strong>Base map</strong> <strong>Base map</strong>
@@ -47,7 +55,7 @@
<div id="gpx-panel" class="gpx-panel" hidden> <div id="gpx-panel" class="gpx-panel" hidden>
<div class="gpx-panel__header"> <div class="gpx-panel__header">
<strong>Tracks</strong> <strong>Tracks &amp; Points</strong>
<div class="gpx-panel__actions"> <div class="gpx-panel__actions">
<button type="button" class="gpx-panel__action" data-action="new-folder" title="New folder" aria-label="New folder"></button> <button type="button" class="gpx-panel__action" data-action="new-folder" title="New folder" aria-label="New folder"></button>
<button type="button" class="gpx-panel__action" data-action="upload" title="Upload GPX/FIT/GeoJSON file" aria-label="Upload GPX/FIT/GeoJSON file"></button> <button type="button" class="gpx-panel__action" data-action="upload" title="Upload GPX/FIT/GeoJSON file" aria-label="Upload GPX/FIT/GeoJSON file"></button>
@@ -62,6 +70,22 @@
<div class="gpx-legend" hidden></div> <div class="gpx-legend" hidden></div>
</div> </div>
<div id="overlays-panel" class="basemap-panel" hidden>
<div class="basemap-panel__header">
<strong>Overlays</strong>
<button type="button" class="basemap-panel__close" aria-label="Close">&times;</button>
</div>
<!-- Each item is a live layer refetched/toggled from its own source as
the map moves, not a file to add/remove/reorder — a plain on/off
list, no upload/new-folder actions the way Tracks & Points has.
Populated by OverlaysControl#mount (public/js/overlays.js), not
here — Radar's item needs its own scrub/forecast markup built by
RadarControl itself (radar.js), so all three items go through the
same "control owns its own <li>" path rather than half static
markup, half dynamic. -->
<ul class="overlays-panel__list"></ul>
</div>
<div id="livetrack-panel" class="basemap-panel" hidden> <div id="livetrack-panel" class="basemap-panel" hidden>
<div class="basemap-panel__header"> <div class="basemap-panel__header">
<strong>LiveTrack sessions</strong> <strong>LiveTrack sessions</strong>
@@ -69,3 +93,15 @@
</div> </div>
<ul class="livetrack-panel__list"></ul> <ul class="livetrack-panel__list"></ul>
</div> </div>
<div id="offline-data-panel" class="basemap-panel" hidden>
<div class="basemap-panel__header">
<strong>Offline data</strong>
<button type="button" class="basemap-panel__close" aria-label="Close">&times;</button>
</div>
<!-- Two rows (map data / terrain data), each its own status text +
"Regenerate for current view" button — built by
OfflineDataControl#mount (public/js/offline_data.js), same
"control owns its content" story as the Overlays panel. -->
<div class="offline-data-panel__body"></div>
</div>
+9 -1
View File
@@ -20,7 +20,7 @@
} }
</script> </script>
<script src="/vendor/pmtiles/pmtiles.js"></script> <script src="/vendor/pmtiles/pmtiles.js"></script>
<script src="/vendor/protomaps-themes-base/protomaps-themes-base.js"></script> <script src="/vendor/maplibre-contour/maplibre-contour.min.js"></script>
<%# Vendored libs above don't change between deploys, so they're left <%# Vendored libs above don't change between deploys, so they're left
unversioned; app.js's own modules do, and map.js statically imports unversioned; app.js's own modules do, and map.js statically imports
them by bare path — an import map remaps those paths to a versioned them by bare path — an import map remaps those paths to a versioned
@@ -31,16 +31,24 @@
{ {
"imports": { "imports": {
"/js/auth.js": "/js/auth.js?v=<%= ASSET_VERSION %>", "/js/auth.js": "/js/auth.js?v=<%= ASSET_VERSION %>",
"/js/contour.js": "/js/contour.js?v=<%= ASSET_VERSION %>",
"/js/dem.js": "/js/dem.js?v=<%= ASSET_VERSION %>",
"/js/gnss.js": "/js/gnss.js?v=<%= ASSET_VERSION %>", "/js/gnss.js": "/js/gnss.js?v=<%= ASSET_VERSION %>",
"/js/gpx.js": "/js/gpx.js?v=<%= ASSET_VERSION %>", "/js/gpx.js": "/js/gpx.js?v=<%= ASSET_VERSION %>",
"/js/hiking_trails.js": "/js/hiking_trails.js?v=<%= ASSET_VERSION %>",
"/js/icons.js": "/js/icons.js?v=<%= ASSET_VERSION %>", "/js/icons.js": "/js/icons.js?v=<%= ASSET_VERSION %>",
"/js/livetrack.js": "/js/livetrack.js?v=<%= ASSET_VERSION %>", "/js/livetrack.js": "/js/livetrack.js?v=<%= ASSET_VERSION %>",
"/js/locate.js": "/js/locate.js?v=<%= ASSET_VERSION %>", "/js/locate.js": "/js/locate.js?v=<%= ASSET_VERSION %>",
"/js/measure.js": "/js/measure.js?v=<%= ASSET_VERSION %>", "/js/measure.js": "/js/measure.js?v=<%= ASSET_VERSION %>",
"/js/notes.js": "/js/notes.js?v=<%= ASSET_VERSION %>", "/js/notes.js": "/js/notes.js?v=<%= ASSET_VERSION %>",
"/js/offline_data.js": "/js/offline_data.js?v=<%= ASSET_VERSION %>",
"/js/overlays.js": "/js/overlays.js?v=<%= ASSET_VERSION %>",
"/js/panels.js": "/js/panels.js?v=<%= ASSET_VERSION %>", "/js/panels.js": "/js/panels.js?v=<%= ASSET_VERSION %>",
"/js/plugins.js": "/js/plugins.js?v=<%= ASSET_VERSION %>", "/js/plugins.js": "/js/plugins.js?v=<%= ASSET_VERSION %>",
"/js/radar.js": "/js/radar.js?v=<%= ASSET_VERSION %>", "/js/radar.js": "/js/radar.js?v=<%= ASSET_VERSION %>",
"/js/search.js": "/js/search.js?v=<%= ASSET_VERSION %>",
"/js/shadow.js": "/js/shadow.js?v=<%= ASSET_VERSION %>",
"/js/sun.js": "/js/sun.js?v=<%= ASSET_VERSION %>",
"/js/track_profile.js": "/js/track_profile.js?v=<%= ASSET_VERSION %>" "/js/track_profile.js": "/js/track_profile.js?v=<%= ASSET_VERSION %>"
} }
} }