Compare commits

...

4 Commits

Author SHA1 Message Date
rayd1o
a2025dbfe0 release: bump version to 0.75.2
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
release / images (push) Has been cancelled
ci / delivery (push) Has been cancelled
2026-09-25 02:03:53 +08:00
rayd1o
003b316d42 release: bump version to 0.75.1
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
2026-09-25 00:16:30 +08:00
rayd1o
0c40764c58 feat: configure compute comparisons and restore adaptive globe dragging
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
2026-09-24 22:59:55 +08:00
rayd1o
5bfc790dec release: bump version to 0.75.0
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
release / images (push) Has been cancelled
ci / delivery (push) Has been cancelled
2026-09-23 21:20:12 +08:00
232 changed files with 15325 additions and 1623 deletions

View File

@@ -5,6 +5,15 @@
!VERSION
!backend/
!backend/**
!config/
!config/earth-boundary-pov-policy.china-v1.json
!scripts/
!scripts/build_earth_boundary_china_pov_geojson.py
!scripts/build_earth_boundary_pmtiles.py
!scripts/earth_boundary_policy.py
!resources/
!resources/earth/
!resources/earth/boundary-pov-geometry.china-v1.json
!aiprovider/
!aiprovider/**

5
.gitignore vendored
View File

@@ -160,3 +160,8 @@ data/earth-boundary-sources/
# Git; publish PMTiles/MVT artifacts through release/deploy storage instead of
# committing thousands of generated tile files.
frontend/public/earth/data/boundaries/
# Offline comparison originals; runtime 8K maps and provenance remain versioned.
resources/earth/candidates/nasa-blue-marble/*.jpg
resources/earth/candidates/natural-earth-3/*.jpg
*:Zone.Identifier

View File

@@ -113,6 +113,9 @@ behavior and document the compatibility note in `docs/harness-audit.md` or
## Operating Mode
- Default to acting directly when the user gives a clear task.
- For authorized multi-agent work, follow the Coding Agent Model Strategy in
`rules.md` workflow: Sol for implementation, Luna for bounded supporting work,
and Astra for complex reasoning; the main agent owns final verification.
- Ask before acting only when the missing decision is risky, cannot be
discovered from repository context, and no conservative assumption is safe.
- Read relevant files before editing.

View File

@@ -17,6 +17,7 @@ are the source of detail for specific subsystems.
| `deploy/helm/planet/` | Helm chart for staging/deployment smoke paths | CI runs helm lint/template when delivery checks are available. |
| `.gitea/workflows/` | CI, release image build, staging deploy workflows | This repository uses Gitea workflow files, not `.github/workflows/`. |
| `planet.sh` | Main local lifecycle script | Owns init/start/restart/stop/health/log/createuser/destroy. |
| `planet.cmd` and `scripts/windows/` | Portable Windows entry, bound desktop launcher generation, and WSL restart bridge | Delegates service lifecycle to `planet.sh`; Windows and isolated WSL regression tests live under `scripts/harness/`. |
## Runtime Entry Points

View File

@@ -1 +1 @@
0.74.7
0.75.2

View File

@@ -28,6 +28,11 @@ RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
COPY backend /app/backend
COPY config/earth-boundary-pov-policy.china-v1.json /app/config/earth-boundary-pov-policy.china-v1.json
COPY scripts/build_earth_boundary_china_pov_geojson.py /app/scripts/build_earth_boundary_china_pov_geojson.py
COPY scripts/build_earth_boundary_pmtiles.py /app/scripts/build_earth_boundary_pmtiles.py
COPY scripts/earth_boundary_policy.py /app/scripts/earth_boundary_policy.py
COPY resources/earth/boundary-pov-geometry.china-v1.json /app/resources/earth/boundary-pov-geometry.china-v1.json
COPY VERSION /app/VERSION
EXPOSE 8000

View File

@@ -12,6 +12,7 @@ from app.api.v1 import (
alerts,
settings,
collected_data,
compute_intelligence,
data_products,
layers,
visualization,
@@ -43,6 +44,7 @@ api_router.include_router(alerts.router, prefix="/alerts", tags=["alerts"])
api_router.include_router(settings.router, prefix="/settings", tags=["settings"])
api_router.include_router(system_control.router, prefix="/system", tags=["system"])
api_router.include_router(data_products.router, prefix="/data-products", tags=["data-products"])
api_router.include_router(compute_intelligence.router, prefix="/data-products/compute", tags=["compute"])
api_router.include_router(layers.router, prefix="/layers", tags=["layers"])
api_router.include_router(visualization.router, prefix="/visualization", tags=["visualization"])
api_router.include_router(

View File

@@ -0,0 +1,231 @@
"""Compute observations, administrator configuration and independent facility samples."""
from datetime import UTC, datetime
from typing import Literal
from app.api.v1.visualization import convert_compute_centers_to_geojson
from app.core.earth_geography_policy import display_country
from app.core.enums import UserRole
from app.core.security import get_current_user
from app.db.session import get_db
from app.models.collected_data import CollectedData
from app.models.data_snapshot import DataSnapshot
from app.models.user import User
from app.schemas.compute_configuration import (
ComputeConfiguration,
ComputeConfigurationReset,
ComputeConfigurationWrite,
)
from app.services.compute_catalog import (
PARTICIPANTS,
participant_members,
source_country_names,
source_country_participants,
)
from app.services.compute_center_locations import refresh_compute_center_location_cache
from app.services.compute_configuration import (
DEFAULT_CONFIGURATION,
configuration_response,
publish_configuration,
read_configuration,
)
from app.services.compute_history import compute_history
from fastapi import APIRouter, Depends, HTTPException, Query, Response
from sqlalchemy import and_, case, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
router = APIRouter()
SOURCES = ("top500", "epoch_ai_gpu")
def selected_countries(countries: str) -> list[str]:
codes = list(dict.fromkeys(code.strip().upper() for code in countries.split(",")))
if not codes or len(codes) > 12 or any(code not in PARTICIPANTS for code in codes):
raise HTTPException(422, "Select 1–12 participants from the compute catalog")
members = participant_members(codes)
flattened = [country for participant in members.values() for country in participant]
if len(flattened) != len(set(flattened)):
raise HTTPException(422, "Comparison participants cannot have overlapping country members")
return codes
@router.get("/series")
async def get_series(
response: Response,
countries: str = "CN,US",
mode: Literal["global_share", "bilateral_share", "absolute"] = "global_share",
db: AsyncSession = Depends(get_db),
):
codes = selected_countries(countries)
envelope = await read_configuration(db)
response.headers["Cache-Control"] = "no-store"
result = compute_history(codes, mode, ComputeConfiguration.model_validate(envelope["config"]))
result.update(
version=envelope["revision"],
provenance="bundled_reports" if envelope["is_default"] else "admin_curated_reports",
)
return result
async def configuration_admin(user: User = Depends(get_current_user)) -> User:
if user.role not in {UserRole.ADMIN.value, UserRole.SUPER_ADMIN.value}:
raise HTTPException(403, "Administrator access required")
return user
@router.get("/configuration")
async def get_configuration(
response: Response,
_actor: User = Depends(configuration_admin),
db: AsyncSession = Depends(get_db),
):
response.headers["Cache-Control"] = "no-store"
return configuration_response(await read_configuration(db))
@router.put("/configuration")
async def update_configuration(
payload: ComputeConfigurationWrite,
actor: User = Depends(configuration_admin),
db: AsyncSession = Depends(get_db),
):
envelope = await publish_configuration(
db, revision=payload.revision, config=payload.config, actor=actor,
)
return configuration_response(envelope)
@router.post("/configuration/reset")
async def reset_configuration(
payload: ComputeConfigurationReset,
actor: User = Depends(configuration_admin),
db: AsyncSession = Depends(get_db),
):
envelope = await publish_configuration(
db, revision=payload.revision, config=DEFAULT_CONFIGURATION, actor=actor, reset=True,
)
return configuration_response(envelope)
async def sample_query(db: AsyncSession, countries: list[str], year: int | None):
now = datetime.now(UTC)
cutoff = min(datetime(year + 1, 1, 1, tzinfo=UTC), now) if year else now
ranked = (
select(
DataSnapshot.id,
func.row_number()
.over(
partition_by=DataSnapshot.source,
order_by=(DataSnapshot.completed_at.desc(), DataSnapshot.id.desc()),
)
.label("rank"),
)
.where(
DataSnapshot.source.in_(SOURCES),
DataSnapshot.status == "success",
DataSnapshot.completed_at < cutoff,
)
.subquery()
)
snapshots = select(ranked.c.id).where(ranked.c.rank == 1)
# Old collector rows have ambiguous units/identity and must not become
# national evidence merely because a new UI can display them.
query = select(CollectedData).where(
CollectedData.snapshot_id.in_(snapshots),
CollectedData.is_valid == 1,
CollectedData.extra_data["compute_contract"].as_string() == "v1",
or_(
CollectedData.source == "top500",
and_(
CollectedData.extra_data["status"].as_string() == "Existing",
func.coalesce(CollectedData.extra_data["superseded_by"].as_string(), "") == "",
CollectedData.extra_data["possible_duplicate"].as_boolean().is_not(True),
),
),
CollectedData.extra_data["country"].as_string().in_(source_country_names(countries)),
)
return query
@router.get("/facilities")
async def get_facilities(
countries: str = "CN,US",
year: int | None = Query(None, ge=2000, le=2100),
limit: int = Query(1000, ge=1, le=2000),
db: AsyncSession = Depends(get_db),
):
codes = selected_countries(countries)
query = await sample_query(db, codes, year)
country = CollectedData.extra_data["country"].as_string()
source_participants = source_country_participants(codes)
# Canonical source subdivisions retain their raw label on each record but
# share one owner in the comparison, using the same policy as the globe.
owner_cases = [
(country == name, display_country(name))
for name in source_country_names(codes)
if display_country(name) != name
]
display = case(*owner_cases, else_=country) if owner_cases else country
participant_cases = [(country == name, code) for name, code in source_participants.items()]
participant = case(*participant_cases, else_=None)
counts = (
(
await db.execute(
query.with_only_columns(
display.label("country"),
participant.label("participant_code"),
CollectedData.source,
func.count().label("count"),
).group_by(display, participant, CollectedData.source)
)
)
.mappings()
.all()
)
participant_counts = (
(
await db.execute(
query.with_only_columns(
participant.label("participant_code"),
CollectedData.source,
func.count().label("count"),
).group_by(participant, CollectedData.source)
)
)
.mappings()
.all()
)
records = (
(await db.execute(query.order_by(CollectedData.name, CollectedData.id).limit(limit)))
.scalars()
.all()
)
if year:
await refresh_compute_center_location_cache(db, as_of=datetime(year + 1, 1, 1, tzinfo=UTC))
else:
await refresh_compute_center_location_cache(db)
payload = convert_compute_centers_to_geojson(records)
for feature in payload["features"]:
feature["properties"]["historical_snapshot"] = year is not None
properties = feature["properties"]
properties["source_country"] = properties.get("country")
properties["country"] = display_country(properties.get("country"))
total = sum(row["count"] for row in counts)
payload.update(
{
"counts": [dict(row) for row in counts],
"participant_counts": [dict(row) for row in participant_counts],
"participants": [PARTICIPANTS[code] for code in codes],
"total_count": total,
"returned_count": len(records),
"truncated": total > limit,
"reference_year": year,
"scope": "public_source_records",
"history_basis": (
"snapshots_collected_by_year_end" if year else "latest_successful_snapshot"
),
"coverage": "available" if total else "no_verified_snapshot",
"cross_source_deduplicated": False,
}
)
return payload

View File

@@ -644,7 +644,9 @@ def _normalize_capacity_band(capacity_value: Optional[float], capacity_unit: str
return "unknown"
unit = str(capacity_unit or "").strip().lower()
if unit in {"pflop/s", "pflops", "pflop"}:
if unit in {"flop/s", "flops"}:
normalized_tflops = capacity_value / 1e12
elif unit in {"pflop/s", "pflops", "pflop"}:
normalized_tflops = capacity_value * 1000
elif unit in {"gflop/s", "gflops", "gflop"}:
normalized_tflops = capacity_value
@@ -672,9 +674,13 @@ def convert_compute_centers_to_geojson(records: List[CollectedData]) -> Dict[str
"""
features: List[Dict[str, Any]] = []
unresolved: List[Dict[str, Any]] = []
withheld_count = 0
for record in records:
metadata = record.extra_data or {}
if metadata.get("compute_contract") == "v1" and metadata.get("anonymous"):
withheld_count += 1
continue
result = resolve_compute_center_location_full(record, metadata)
site_type = (
"supercomputer"
@@ -706,7 +712,10 @@ def convert_compute_centers_to_geojson(records: List[CollectedData]) -> Dict[str
latitude = location.latitude
longitude = location.longitude
if site_type == "supercomputer":
if metadata.get("compute_contract") == "v1":
capacity_value = _parse_float(get_record_field(record, "value"))
capacity_unit = str(get_record_field(record, "unit") or "FLOP/s")
elif site_type == "supercomputer":
capacity_value = _parse_float(get_record_field(record, "rmax"))
capacity_unit = "GFlops"
else:
@@ -767,7 +776,7 @@ def convert_compute_centers_to_geojson(records: List[CollectedData]) -> Dict[str
}
)
return {"type": "FeatureCollection", "features": features, "unresolved": unresolved}
return {"type": "FeatureCollection", "features": features, "unresolved": unresolved, "withheld_count": withheld_count}
VESSEL_TYPE_FILTERS = {

View File

@@ -0,0 +1,57 @@
"""Country display ownership for Earth comparisons and cards.
Keep source-country evidence separate; use this projection only for display
grouping so regional source records cannot become competing national keys.
"""
from __future__ import annotations
import json
from functools import lru_cache
from pathlib import Path
from app.core.countries import COUNTRY_ENTRIES, normalize_country
_DISPLAY_ISO_A3 = {
canonical: next(
(alias for alias in aliases if len(alias) == 3 and alias.isascii() and alias.isupper()),
None,
)
for canonical, aliases in COUNTRY_ENTRIES
}
_COUNTRY_BY_ISO_A3 = {code: country for country, code in _DISPLAY_ISO_A3.items() if code}
_POLICY_PATH = Path(__file__).resolve().parents[3] / "config/earth-boundary-pov-policy.china-v1.json"
@lru_cache(maxsize=1)
def _policy_country_by_code() -> dict[str, str]:
try:
content = _POLICY_PATH.read_text(encoding="utf-8")
except FileNotFoundError as error:
raise FileNotFoundError(f"EARTH_DISPLAY_POLICY_MISSING: {_POLICY_PATH}") from error
policy = json.loads(content)
if policy.get("profile") != "china-pov-v1":
raise ValueError("unsupported Earth display-policy profile")
mappings = {}
for rule in policy["rules"]:
target = rule.get("targetIsoA3") or rule.get("parentIsoA3")
if target not in _COUNTRY_BY_ISO_A3:
continue
for code in rule.get("sourceIsoA3", []):
mappings[code] = _COUNTRY_BY_ISO_A3[target]
return mappings
def display_country(value: str | None) -> str | None:
country = normalize_country(value)
code = _DISPLAY_ISO_A3.get(country) if country else str(value or "").strip().upper()
owner = _policy_country_by_code().get(code)
if owner:
return owner
return country
def display_iso_a3(value: str | None) -> str | None:
country = display_country(value)
return _DISPLAY_ISO_A3.get(country) if country else None

View File

@@ -6,7 +6,7 @@ import redis
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from sqlalchemy import text
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
@@ -81,6 +81,21 @@ def decode_token(token: str) -> Optional[dict]:
return None
async def _load_active_user(user_id: str, db: AsyncSession) -> User:
try:
parsed_id = int(user_id)
except (TypeError, ValueError):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
result = await db.execute(select(User).where(User.id == parsed_id))
user = result.scalar_one_or_none()
if user is None or not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or inactive",
)
return user
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db),
@@ -103,27 +118,7 @@ async def get_current_user(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
)
result = await db.execute(
text(
"SELECT id, username, email, password_hash, role, is_active, gatekeeper_groups FROM users WHERE id = :id"
),
{"id": int(user_id)},
)
row = result.fetchone()
if row is None or not row[5]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or inactive",
)
user = User()
user.id = row[0]
user.username = row[1]
user.email = row[2]
user.password_hash = row[3]
user.role = row[4]
user.is_active = row[5]
user.gatekeeper_groups = row[6] or []
return user
return await _load_active_user(user_id, db)
async def get_current_user_refresh(
@@ -143,27 +138,7 @@ async def get_current_user_refresh(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
)
result = await db.execute(
text(
"SELECT id, username, email, password_hash, role, is_active, gatekeeper_groups FROM users WHERE id = :id"
),
{"id": int(user_id)},
)
row = result.fetchone()
if row is None or not row[5]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or inactive",
)
user = User()
user.id = row[0]
user.username = row[1]
user.email = row[2]
user.password_hash = row[3]
user.role = row[4]
user.is_active = row[5]
user.gatekeeper_groups = row[6] or []
return user
return await _load_active_user(user_id, db)
def blacklist_token(token: str) -> None:

View File

@@ -0,0 +1,94 @@
"""Validated, source-backed observations for the compute comparison cards."""
from datetime import date
from typing import Annotated, Literal, Self
from app.services.compute_catalog import COUNTRY_PARTICIPANTS
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator, model_validator
Year = Annotated[int, Field(strict=True, ge=2000, le=2100)]
Value = Annotated[float, Field(strict=True, allow_inf_nan=False, ge=0, le=1e12)]
Identifier = Annotated[str, Field(min_length=1, max_length=80, pattern=r"^[a-zA-Z0-9_-]+$")]
Text = Annotated[str, Field(min_length=1, max_length=500)]
class ConfigurationModel(BaseModel):
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
class ComputeSource(ConfigurationModel):
id: Identifier
title: Text
url: HttpUrl
publisher: Text
report_year: Year
published_at: date | None = None
locator: Text
@field_validator("url")
@classmethod
def public_reference(cls, value: HttpUrl) -> HttpUrl:
if value.username or value.password or len(str(value)) > 2000:
raise ValueError("Use a report URL without credentials (maximum 2000 characters)")
return value
class ComputeObservation(ConfigurationModel):
year: Year
metric: Literal["global_share", "absolute"]
source_id: Identifier
values: dict[str, Value] = Field(min_length=1, max_length=250)
@model_validator(mode="after")
def comparable_values(self) -> Self:
if any(code not in COUNTRY_PARTICIPANTS for code in self.values):
raise ValueError(
"Use countries from the canonical compute catalog, not regional totals"
)
if self.metric == "global_share" and sum(self.values.values()) > 100.000001:
raise ValueError("Global shares for one year must total at most 100 percent")
return self
class InnovationObservation(ConfigurationModel):
year: Year
value: Annotated[float, Field(strict=True, allow_inf_nan=False, ge=-100, le=100)]
source_id: Identifier
class ComputeConfiguration(ConfigurationModel):
reviewed_at: date
sources: list[ComputeSource] = Field(max_length=200)
observations: list[ComputeObservation] = Field(max_length=202)
innovation: list[InnovationObservation] = Field(max_length=101)
@model_validator(mode="after")
def consistent_evidence(self) -> Self:
ids = [source.id for source in self.sources]
if len(ids) != len(set(ids)):
raise ValueError("Report source IDs must be unique")
keys = [(row.metric, row.year) for row in self.observations]
if len(keys) != len(set(keys)):
raise ValueError("Each metric/year may have only one observation")
years = [row.year for row in self.innovation]
if len(years) != len(set(years)):
raise ValueError("Innovation observation years must be unique")
sources = {source.id: source for source in self.sources}
for row in [*self.observations, *self.innovation]:
source = sources.get(row.source_id)
if source is None:
raise ValueError(f"Missing report source: {row.source_id}")
if row.year > source.report_year:
raise ValueError("Observation year must not be after its report year")
if self.reviewed_at > date.today():
raise ValueError("Review date must not be in the future")
return self
class ComputeConfigurationWrite(ConfigurationModel):
revision: str = Field(min_length=1, max_length=80)
config: ComputeConfiguration
class ComputeConfigurationReset(ConfigurationModel):
revision: str = Field(min_length=1, max_length=80)

View File

@@ -36,6 +36,9 @@ class UserInDB(UserBase):
class UserResponse(UserBase):
# Bootstrap accounts have reserved .local email addresses. Validate email
# on writes, while allowing existing local identities to be serialized.
email: str
id: int
role: str
gatekeeper_groups: list[str] = Field(default_factory=list)

View File

@@ -31,6 +31,7 @@ class BaseCollector(ABC):
frequency_hours: int = 4
data_type: str = "generic"
fail_on_empty: bool = False
preserve_snapshot_on_failure: bool = False
def __init__(self):
self._current_task = None
@@ -361,6 +362,8 @@ class BaseCollector(ABC):
context={"task_id": task_id, "raw_count": len(raw_data)},
)
data = self.transform(raw_data)
if self.fail_on_empty and not data:
raise RuntimeError(f"Collector {self.name} produced no valid records")
await self._log_collection_event(
"collector.phase.transforming.success",
"Collector transform phase completed",
@@ -456,6 +459,10 @@ class BaseCollector(ABC):
raise
except Exception as e:
await db.rollback()
if snapshot_id is not None and self.preserve_snapshot_on_failure:
await self._rollback_incomplete_run(
db, task_id=task_id, snapshot_id=snapshot_id, reason=str(e)
)
task.status = JobStatus.FAILED.value
task.phase = "failed"
task.phase_message = str(e)
@@ -465,6 +472,8 @@ class BaseCollector(ABC):
snapshot = await db.get(DataSnapshot, snapshot_id)
if snapshot:
snapshot.status = SnapshotStatus.FAILED.value
if self.preserve_snapshot_on_failure:
snapshot.is_current = False
snapshot.completed_at = datetime.now(UTC)
snapshot.summary = {"error": str(e)}
await db.commit()

View File

@@ -0,0 +1,82 @@
"""Quality gates shared by the two compute sources."""
from datetime import datetime
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.countries import normalize_country
from app.models.compute_center_location import ComputeCenterLocationRecord
from app.models.data_snapshot import DataSnapshot
from app.services.collectors.base import BaseCollector
class ComputeCollector(BaseCollector):
fail_on_empty = True
preserve_snapshot_on_failure = True
async def _create_snapshot(
self, db: AsyncSession, task_id: int, data: list[dict], started_at: datetime
) -> int:
previous_count = await db.scalar(
select(DataSnapshot.record_count)
.where(
DataSnapshot.source == self.name,
DataSnapshot.is_current.is_(True),
DataSnapshot.status == "success",
)
.order_by(DataSnapshot.id.desc())
.limit(1)
)
if previous_count and len(data) < previous_count * 0.6:
raise ValueError("Compute source lost over 40% of rows; previous snapshot retained")
return await super()._create_snapshot(db, task_id, data, started_at)
async def _save_data(self, db: AsyncSession, data: list[dict], **kwargs) -> int:
if self.name == "top500":
await self._copy_verified_locations(db, data)
return await super()._save_data(db, data, **kwargs)
async def _copy_verified_locations(self, db: AsyncSession, data: list[dict]) -> None:
"""Keep historical location keys; copy only unambiguous exact identities.
Never migrate by rank: the machine occupying rank 1 changes over time.
"""
names = [item["name"] for item in data]
locations = (
(
await db.execute(
select(ComputeCenterLocationRecord).where(
ComputeCenterLocationRecord.source == self.name,
ComputeCenterLocationRecord.name.in_(names),
)
)
)
.scalars()
.all()
)
existing = {location.source_id for location in locations}
for item in data:
if item["source_id"] in existing:
continue
country = normalize_country(item["country"])
if not country:
continue
matches = [
location
for location in locations
if location.name == item["name"]
and normalize_country(location.country) == country
and (not location.site or location.site == item.get("metadata", {}).get("site"))
and not location.source_id.startswith("top500_system_")
]
if len(matches) != 1:
continue
old = matches[0]
values = {
column.name: getattr(old, column.name)
for column in old.__table__.columns
if column.name not in {"id", "source_id", "created_at", "updated_at"}
}
db.add(ComputeCenterLocationRecord(**values, source_id=item["source_id"]))
existing.add(item["source_id"])

View File

@@ -1,119 +1,135 @@
"""Epoch AI GPU Clusters Collector
Collects data from Epoch AI GPU clusters tracking.
https://epoch.ai/data/gpu-clusters
"""
"""Epoch's official CSV; preserve precision, source dates and anonymous records."""
import csv
import hashlib
import io
import json
import math
import re
from typing import Dict, Any, List
from datetime import UTC, datetime
from bs4 import BeautifulSoup
from urllib.parse import urlparse
import httpx
from app.services.collectors.base import BaseCollector
from app.core.countries import normalize_country
from app.services.collectors.compute_base import ComputeCollector
from app.services.epoch_location import assess_epoch_location
EPOCH_CSV_URL = "https://epoch.ai/data/gpu_clusters.csv"
class EpochAIGPUCollector(BaseCollector):
class EpochAIGPUCollector(ComputeCollector):
name = "epoch_ai_gpu"
priority = "P0"
module = "L1"
frequency_hours = 6
data_type = "gpu_cluster"
async def fetch(self) -> List[Dict[str, Any]]:
"""Fetch Epoch AI GPU clusters data from webpage"""
url = self._resolved_url or ""
async with httpx.AsyncClient(timeout=60.0) as client:
async def fetch(self) -> list[dict]:
configured = self._resolved_url or EPOCH_CSV_URL
parsed = urlparse(configured)
url = (
EPOCH_CSV_URL
if parsed.hostname == "epoch.ai" and not parsed.path.endswith(".csv")
else configured
)
async with httpx.AsyncClient(timeout=60, follow_redirects=True) as client:
response = await client.get(url)
response.raise_for_status()
return self.parse_response(response.text)
return self.parse_response(response.text)
def parse_response(self, html: str) -> List[Dict[str, Any]]:
"""Parse Epoch AI webpage to extract GPU cluster data"""
data = []
soup = BeautifulSoup(html, "html.parser")
def parse_response(self, text: str) -> list[dict]:
reader = csv.DictReader(io.StringIO(text.lstrip("\ufeff")))
required = {"Name", "Country", "Status", "First Operational Date", "16-bit OP/s (log)"}
if not required.issubset(reader.fieldnames or []):
raise ValueError(
"Epoch CSV schema changed or HTML returned; previous snapshot retained"
)
entries = []
seen = set()
anonymous_counts = {}
for row in reader:
name = (row.get("Name") or "").strip()
country = normalize_country(row.get("Country"))
if not name:
raise ValueError("Epoch row has missing identity")
source_id = f"epoch_{re.sub(r'[^a-zA-Z0-9]', '_', name.lower())}"
anonymous = "anonym" in name.lower()
if anonymous:
# Public anonymous rows have no facility identity. Preserve
# multiplicity within snapshots, without guessing names.
digest = hashlib.sha256(json.dumps(row, sort_keys=True).encode()).hexdigest()[:24]
anonymous_counts[digest] = anonymous_counts.get(digest, 0) + 1
source_id = f"epoch_anonymous_{digest}_{anonymous_counts[digest]}"
if source_id in seen:
raise ValueError("Epoch identity collision; previous snapshot retained")
seen.add(source_id)
raw_date = row.get("First Operational Date", "")
reference_date = raw_date if re.fullmatch(r"\d{4}-\d{2}-\d{2}", raw_date) else None
value = None
raw_value = row.get("16-bit OP/s (log)", "")
if raw_value:
exponent = float(raw_value)
if not math.isfinite(exponent) or not 0 <= exponent <= 30:
raise ValueError("Epoch performance exponent out of bounds")
value = 10**exponent
anonymous = anonymous or (country == "中国" and not row.get("Location"))
location = row.get("Location")
location_assessment = assess_epoch_location(
location,
country=country,
raw_country=row.get("Country"),
)
entries.append(
{
"source_id": source_id,
"name": name,
"country": country,
"value": value,
"unit": "FLOP/s",
"reference_date": reference_date,
"latitude": None if anonymous else self.coordinate(row.get("latitude"), 90),
"longitude": None if anonymous else self.coordinate(row.get("longitude"), 180),
"metadata": {
"compute_contract": "v1",
"precision": "FP16",
"measurement": "theoretical",
"source_url": EPOCH_CSV_URL,
"raw_reference_date": raw_date,
"status": row.get("Status"),
"certainty": row.get("Certainty"),
"operator": row.get("Owner"),
"gpu_type": row.get("Chip type (primary)"),
"gpu_count": row.get("Chip quantity (primary)"),
"location": location,
"location_precision": location_assessment.precision,
"location_precision_reason": location_assessment.reason,
"raw_country": row.get("Country"),
"raw_performance": raw_value,
"performance_encoding": "log10",
"builds_upon": row.get("Builds Upon"),
"superseded_by": row.get("Superseded by"),
"possible_duplicate": row.get("Possible Duplicate", "").lower() == "true",
"include_in_standard_analysis": row.get(
"Include in Standard Analysis", ""
).lower()
== "true",
"rounded": country == "中国",
"anonymous": anonymous,
"identity_scope": "snapshot_record" if anonymous else "named_cluster",
"country_unresolved": country is None,
"sources": [
row[f"Source {i}"] for i in range(1, 6) if row.get(f"Source {i}")
],
},
}
)
return entries
# Try to find data table on the page
tables = soup.find_all("table")
for table in tables:
rows = table.find_all("tr")
for row in rows[1:]: # Skip header
cells = row.find_all(["td", "th"])
if len(cells) >= 5:
try:
cluster_name = cells[0].get_text(strip=True)
if not cluster_name or cluster_name in ["Cluster", "System", "Name"]:
continue
location_cell = cells[1].get_text(strip=True) if len(cells) > 1 else ""
country, city = self._parse_location(location_cell)
perf_cell = cells[2].get_text(strip=True) if len(cells) > 2 else ""
entry = {
"source_id": f"epoch_{re.sub(r'[^a-zA-Z0-9]', '_', cluster_name.lower())}",
"name": cluster_name,
"country": country,
"city": city,
"latitude": "",
"longitude": "",
"value": self._parse_performance(perf_cell),
"unit": "TFlop/s",
"metadata": {
"raw_data": perf_cell,
},
"reference_date": datetime.now(UTC).strftime("%Y-%m-%d"),
}
data.append(entry)
except (ValueError, IndexError, AttributeError):
continue
# If no table found, return sample data
if not data:
data = self._get_sample_data()
return data
def _parse_location(self, location: str) -> tuple:
"""Parse location string into country and city"""
if not location:
return "", ""
if "," in location:
parts = location.rsplit(",", 1)
city = parts[0].strip()
country = parts[1].strip() if len(parts) > 1 else ""
return country, city
return location, ""
def _parse_performance(self, perf: str) -> str:
"""Parse performance string to extract value"""
if not perf:
return "0"
match = re.search(r"([\d,.]+)\s*(TFlop/s|PFlop/s|GFlop/s)?", perf, re.I)
if match:
return match.group(1).replace(",", "")
match = re.search(r"([\d,.]+)", perf)
if match:
return match.group(1).replace(",", "")
return "0"
def _get_sample_data(self) -> List[Dict[str, Any]]:
"""Return sample data for testing when scraping fails"""
return [
{
"source_id": "epoch_sample_1",
"name": "Sample GPU Cluster",
"country": "United States",
"city": "San Francisco, CA",
"latitude": "",
"longitude": "",
"value": "1000",
"unit": "TFlop/s",
"metadata": {
"note": "Sample data - Epoch AI page structure may vary",
},
"reference_date": datetime.now(UTC).strftime("%Y-%m-%d"),
},
]
@staticmethod
def coordinate(value: str | None, bound: int) -> float | None:
if not value:
return None
number = float(value)
if not math.isfinite(number) or abs(number) > bound:
raise ValueError("Epoch coordinate out of bounds")
return number

View File

@@ -5,16 +5,18 @@ https://top500.org/lists/top500/
"""
import asyncio
import math
import re
from urllib.parse import urljoin
from typing import Dict, Any, List
from bs4 import BeautifulSoup
import httpx
from app.core.data_sources import get_data_sources_config
from app.services.collectors.base import BaseCollector
from app.services.collectors.compute_base import ComputeCollector
class TOP500Collector(BaseCollector):
class TOP500Collector(ComputeCollector):
name = "top500"
priority = "P0"
module = "L1"
@@ -28,7 +30,7 @@ class TOP500Collector(BaseCollector):
async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client:
response = await client.get(url)
response.raise_for_status()
entries = self.parse_response(response.text)
entries = self.parse_response(response.text, str(response.url))
semaphore = asyncio.Semaphore(8)
@@ -55,7 +57,7 @@ class TOP500Collector(BaseCollector):
system_name = link.get_text(" ", strip=True) if link else system_cell.get_text(" ", strip=True)
detail_url = ""
if link and link.get("href"):
detail_url = f"{str(top500_base_url).rstrip('/')}{link.get('href')}"
detail_url = urljoin(str(top500_base_url), link.get("href"))
manufacturer = ""
if link and link.next_sibling:
@@ -127,10 +129,14 @@ class TOP500Collector(BaseCollector):
return detail_map
def parse_response(self, html: str) -> List[Dict[str, Any]]:
def parse_response(self, html: str, source_url: str = "") -> List[Dict[str, Any]]:
"""Parse TOP500 HTML response"""
data = []
soup = BeautifulSoup(html, "html.parser")
canonical = soup.find("link", rel="canonical")
date_url = source_url or (canonical.get("href", "") if canonical else "") or self._resolved_url or ""
date_match = re.search(r"/lists/top500/(\d{4})/(06|11)/", date_url)
reference_date = f"{date_match[1]}-{date_match[2]}-01" if date_match else None
# Find the table with TOP500 data
table = None
@@ -152,6 +158,9 @@ class TOP500Collector(BaseCollector):
table = soup.find("table", {"class": "top500-table"}) or soup.find("table", {"id": "top500"})
if table:
headers = [cell.get_text(" ", strip=True) for cell in table.select("tr")[0].find_all(["th", "td"])]
if not reference_date:
raise ValueError("TOP500 list date missing; previous snapshot retained")
rows = table.select("tr")
for row in rows[1:]: # Skip header row
cells = row.find_all(["td", "th"])
@@ -176,25 +185,32 @@ class TOP500Collector(BaseCollector):
cores = cells[2].get_text(strip=True).replace(",", "")
rmax_text = cells[3].get_text(strip=True)
rmax = self._parse_performance(rmax_text)
rmax = self._parse_performance(rmax_text, headers[3])
rpeak_text = cells[4].get_text(strip=True)
rpeak = self._parse_performance(rpeak_text)
rpeak = self._parse_performance(rpeak_text, headers[4])
power = ""
if len(cells) >= 6:
power = cells[5].get_text(strip=True).replace(",", "")
identity = re.search(r"/system/(\d+)", detail_url)
if not identity:
raise ValueError("TOP500 stable system ID missing")
entry = {
"source_id": f"top500_{rank}",
"source_id": f"top500_system_{identity[1]}",
"name": system_name,
"country": country,
"city": city,
"latitude": 0.0,
"longitude": 0.0,
"latitude": None,
"longitude": None,
"value": str(rmax),
"unit": "PFlop/s",
"unit": "FLOP/s",
"metadata": {
"compute_contract": "v1",
"precision": "FP64", "measurement": "LINPACK Rmax",
"source_url": detail_url, "list_url": date_url,
"status": "listed", "rmax_flops": rmax, "rpeak_flops": rpeak,
"rank": rank,
"cores": cores,
"rmax": rmax_text,
@@ -204,15 +220,11 @@ class TOP500Collector(BaseCollector):
"site": site,
},
"_detail_url": detail_url,
"reference_date": "2025-11-01",
"reference_date": reference_date,
}
data.append(entry)
except (ValueError, IndexError, AttributeError) as e:
continue
# If scraping failed, return sample data for testing
if not data:
data = self._get_sample_data()
raise ValueError("Invalid TOP500 row; previous snapshot retained") from e
return data
@@ -227,9 +239,14 @@ class TOP500Collector(BaseCollector):
return 0.0
return 0.0
def _parse_performance(self, text: str) -> float:
def _parse_performance(self, text: str, header: str = "") -> float:
"""Parse performance value from text (handles E, P, T suffixes)"""
text = text.strip().upper()
text = text.strip().upper().replace(",", "")
if not re.search(r"[EPTGMK]F", text):
unit = re.search(r"\b([EPTGMK])FLOP/?S", header.upper())
if not unit:
raise ValueError("TOP500 performance unit missing")
text = f"{text} {unit[1]}FLOP/S"
multipliers = {
"E": 1e18,
"P": 1e15,
@@ -249,73 +266,9 @@ class TOP500Collector(BaseCollector):
# Try simple float parsing
try:
return float(text.replace(",", ""))
value = float(text.replace(",", ""))
if not math.isfinite(value) or value < 0:
raise ValueError("Invalid TOP500 performance")
return value
except ValueError:
return 0.0
def _get_sample_data(self) -> List[Dict[str, Any]]:
"""Return sample data for testing when scraping fails"""
return [
{
"source_id": "top500_1",
"name": "El Capitan - HPE Cray EX255a, AMD 4th Gen EPYC 24C 1.8GHz, AMD Instinct MI300A",
"country": "United States",
"city": "Livermore, CA",
"latitude": 37.6819,
"longitude": -121.7681,
"value": "1742.00",
"unit": "PFlop/s",
"metadata": {
"rank": 1,
"cores": "11039616",
"rmax": "1742.00",
"rpeak": "2746.38",
"power": "29581",
"manufacturer": "HPE",
"site": "DOE/NNSA/LLNL",
"processor": "AMD 4th Gen EPYC 24C 1.8GHz",
"interconnect": "Slingshot-11",
"installation_year": "2025",
},
"reference_date": "2025-11-01",
},
{
"source_id": "top500_2",
"name": "Frontier - HPE Cray EX235a, AMD Optimized 3rd Generation EPYC 64C 2GHz, AMD Instinct MI250X",
"country": "United States",
"city": "Oak Ridge, TN",
"latitude": 36.0107,
"longitude": -84.2663,
"value": "1353.00",
"unit": "PFlop/s",
"metadata": {
"rank": 2,
"cores": "9066176",
"rmax": "1353.00",
"rpeak": "2055.72",
"power": "24607",
"manufacturer": "HPE",
"site": "DOE/SC/Oak Ridge National Laboratory",
},
"reference_date": "2025-11-01",
},
{
"source_id": "top500_3",
"name": "Aurora - HPE Cray EX - Intel Exascale Compute Blade, Xeon CPU Max 9470 52C 2.4GHz, Intel Data Center GPU Max",
"country": "United States",
"city": "Argonne, IL",
"latitude": 41.3784,
"longitude": -87.8600,
"value": "1012.00",
"unit": "PFlop/s",
"metadata": {
"rank": 3,
"cores": "9264128",
"rmax": "1012.00",
"rpeak": "1980.01",
"power": "38698",
"manufacturer": "Intel",
},
"reference_date": "2025-11-01",
},
]
raise ValueError("Invalid TOP500 performance") from None

View File

@@ -0,0 +1,200 @@
"""Canonical country and mutually exclusive regional compute participants."""
import re
from collections.abc import Iterable
from app.core.countries import COUNTRY_CENTROIDS, COUNTRY_ENTRIES
from app.core.earth_geography_policy import display_country
PALETTE = ("#91baff", "#f0c88a", "#89d2c2", "#c5abdf", "#eda9b3", "#b6c99a")
DEFAULT_PARTICIPANTS = ("CN", "US", "EUROPE", "APAC", "OTHER")
# Explicit membership prevents a regional total from double-counting a country.
# APAC deliberately excludes China.
COMPARISON_GROUPS = (
("CN", "中国", "China", ("CN",), {"latitude": 35.8617, "longitude": 104.1954}),
("US", "美国", "United States", ("US",), {"latitude": 39.8283, "longitude": -98.5795}),
(
"EUROPE",
"欧洲",
"Europe",
(
"GB",
"DE",
"FR",
"CH",
"ES",
"FI",
"HU",
"IE",
"IT",
"LU",
"MD",
"NL",
"PL",
"RO",
"RU",
"UA",
),
{"latitude": 54.0, "longitude": 20.0},
),
(
"APAC",
"亚太(不含中国)",
"Asia Pacific (excluding China)",
("JP", "KR", "IN", "AU", "AF", "ID", "NP", "PH", "PK", "SG", "TH"),
{"latitude": 36.0, "longitude": 130.5},
),
(
"OTHER",
"其他地区",
"Other",
(
"CA",
"AO",
"EG",
"KE",
"MZ",
"NG",
"UG",
"ZA",
"BR",
"CO",
"CR",
"DO",
"MX",
"AE",
"SY",
"TR",
),
{"latitude": 12.0, "longitude": 10.0},
),
)
def _country_code(canonical: str, aliases: list[str]) -> str | None:
code = next((alias for alias in aliases if re.fullmatch(r"[A-Z]{2}", alias)), None)
return "GB" if canonical == "英国" else code
def build_country_catalog() -> list[dict]:
"""Build the policy-projected canonical country drilldown catalog."""
countries = []
for canonical, aliases in COUNTRY_ENTRIES:
if display_country(canonical) != canonical or canonical not in COUNTRY_CENTROIDS:
continue
code = _country_code(canonical, aliases)
if code:
countries.append(
{
"code": code,
"name": canonical,
"name_en": aliases[0],
"kind": "country",
"members": [code],
"anchor": dict(COUNTRY_CENTROIDS[canonical]),
"anchor_kind": "regional_label",
}
)
return countries
COUNTRY_CATALOG = build_country_catalog()
COUNTRIES = {entry["code"]: entry["name"] for entry in COUNTRY_CATALOG}
COUNTRY_PARTICIPANTS = {entry["code"]: entry for entry in COUNTRY_CATALOG}
def _validate_group_members() -> None:
members = [
member for _code, _name, _name_en, codes, _anchor in COMPARISON_GROUPS for member in codes
]
if len(members) != len(set(members)) or set(members) != set(COUNTRIES):
raise ValueError("compute comparison groups must cover country catalog exactly once")
_validate_group_members()
GROUP_BY_MEMBER = {
member: code
for code, _name, _name_en, members, _anchor in COMPARISON_GROUPS
for member in members
}
def build_catalog() -> list[dict]:
"""Return the five default participants before country drilldown entries."""
catalog: list[dict] = []
for index, (code, name, name_en, members, anchor) in enumerate(COMPARISON_GROUPS):
entry = (
dict(COUNTRY_PARTICIPANTS[code])
if code in COUNTRY_PARTICIPANTS
else {
"code": code,
"name": name,
"name_en": name_en,
"kind": "region",
"members": list(members),
"anchor": dict(anchor),
"anchor_kind": "regional_label",
}
)
entry.update(
{
"members": list(members),
"parent_group": name,
"parent_group_code": code,
"order": index,
"default_selected": True,
"color": PALETTE[index % len(PALETTE)],
}
)
catalog.append(entry)
for entry in COUNTRY_CATALOG:
if entry["code"] in DEFAULT_PARTICIPANTS:
continue
country = dict(entry)
group_code = GROUP_BY_MEMBER[country["code"]]
group_name = next(group[1] for group in COMPARISON_GROUPS if group[0] == group_code)
country.update(
{
"parent_group": group_name,
"parent_group_code": group_code,
"order": len(catalog),
"default_selected": False,
"color": PALETTE[len(catalog) % len(PALETTE)],
}
)
catalog.append(country)
return catalog
CATALOG = build_catalog()
PARTICIPANTS = {entry["code"]: entry for entry in CATALOG}
def participant_members(codes: Iterable[str]) -> dict[str, tuple[str, ...]]:
"""Resolve country or region participant codes to canonical country codes."""
return {code: tuple(PARTICIPANTS[code]["members"]) for code in codes}
def source_country_names(codes: Iterable[str]) -> list[str]:
"""Return unique canonical source labels, respecting China and Serbia ownership."""
selected_members = {
member for members in participant_members(codes).values() for member in members
}
selected_names = {COUNTRIES[code] for code in selected_members}
return [name for name, _aliases in COUNTRY_ENTRIES if display_country(name) in selected_names]
def source_country_participants(codes: Iterable[str]) -> dict[str, str]:
"""Map stored source-country labels to their requested participant code."""
owner_by_country = {
member: participant
for participant, members in participant_members(codes).items()
for member in members
}
code_by_name = {name: code for code, name in COUNTRIES.items()}
return {
name: owner_by_country[code_by_name[display_country(name)]]
for name in source_country_names(codes)
}

View File

@@ -3,7 +3,12 @@
This module is a thin domain wrapper that wires up
:mod:`app.services.location` for compute centers:
SourceCoordinates
Confirmed stored location -> trustworthy source coordinates -> exact
official facility registration -> source city/site coordinates.
Epoch AI coordinates are admitted only at the grain asserted by its official
``Location`` column. Country/region/empty locations remain unresolved even
when the CSV happens to contain a latitude/longitude pair.
The online Nominatim step is intentionally reserved for the user-triggered
``collect-location`` flow. The regular GeoJSON endpoint runs during Earth
@@ -31,6 +36,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.core.collected_data_fields import get_record_field
from app.models.collected_data import CollectedData
from app.models.compute_center_location import ComputeCenterLocationRecord
from app.services.compute_center_verified_sites import get_verified_compute_site
from app.services.epoch_location import assess_epoch_location
from app.services.location import (
LocationCandidate,
@@ -166,8 +173,13 @@ def set_compute_center_location_cache(
async def refresh_compute_center_location_cache(
session: AsyncSession,
*,
as_of: datetime | None = None,
) -> dict[str, dict[str, Any]]:
result = await session.execute(select(ComputeCenterLocationRecord))
query = select(ComputeCenterLocationRecord)
if as_of is not None:
query = query.where(ComputeCenterLocationRecord.verified_at < as_of)
result = await session.execute(query)
records = result.scalars().all()
cache = {}
for record in records:
@@ -350,6 +362,58 @@ class ROROrganizationResolver:
)
class ComputeCenterSourceCoordinatesResolver:
"""Use source coordinates only at the grain asserted by the source text."""
name = "source_coordinates"
def __init__(self) -> None:
self._default = SourceCoordinatesResolver()
def resolve(self, query: LocationQuery) -> ResolverOutput:
extra = query.extra or {}
if extra.get("source") != "epoch_ai_gpu":
return self._default.resolve(query)
if query.source_latitude in (None, 0.0) or query.source_longitude in (None, 0.0):
return ResolverOutput()
assessment = assess_epoch_location(
extra.get("source_location"),
country=query.country,
raw_country=extra.get("source_raw_country"),
)
if not assessment.is_renderable:
return ResolverOutput(
attempted_queries=(f"source_coordinates:{assessment.precision}",)
)
output = self._default.resolve(query)
if not output.candidates:
return output
candidate = output.candidates[0]
source_location = extra.get("source_location")
source_location_text = (
source_location.strip() if isinstance(source_location, str) else ""
)
source_note = assessment.reason
if source_location_text:
source_note = f"{source_note}; Epoch Location: {source_location_text}"
return ResolverOutput(
candidates=(
LocationCandidate(
**{
**candidate.__dict__,
"precision": assessment.precision,
"confidence": 1.0 if assessment.precision == "precise" else 0.9,
"source_note": source_note,
"needs_confirmation": assessment.precision != "precise",
"matched_location_name": source_location_text
or candidate.matched_location_name,
}
),
),
attempted_queries=output.attempted_queries,
)
class StoredComputeCenterLocationResolver:
"""Resolve a compute center through the DB-backed current-location cache."""
@@ -367,13 +431,26 @@ class StoredComputeCenterLocationResolver:
longitude = parse_float(stored.get("longitude"))
if latitude in (None, 0.0) or longitude in (None, 0.0):
return ResolverOutput()
precision = stored.get("precision") or "city"
if (
extra.get("source") == "epoch_ai_gpu"
and stored.get("location_source") == "source_coordinates"
):
assessment = assess_epoch_location(
extra.get("source_location"),
country=query.country,
raw_country=extra.get("source_raw_country"),
)
if not assessment.is_renderable:
return ResolverOutput()
precision = assessment.precision
return ResolverOutput(
candidates=(
LocationCandidate(
latitude=latitude,
longitude=longitude,
display_name=stored.get("name") or query.name or "Compute center",
precision=stored.get("precision") or "city",
precision=precision,
confidence=float(stored.get("confidence") or 0.85),
query=f"stored_compute_center_location::{stored.get('source')}:{stored.get('source_id')}",
source=self.name,
@@ -386,6 +463,60 @@ class StoredComputeCenterLocationResolver:
matched_location_name=stored.get("site") or stored.get("name") or query.name,
location_verified_at=stored.get("verified_at"),
suggested_registry_entry=None,
raw_payload={
"stored_location_source": stored.get("location_source"),
"verification_status": stored.get("verification_status"),
},
),
)
)
class VerifiedComputeSiteResolver:
"""Resolve an exact source identity to an evidence-backed facility site."""
name = "official_facility_registry"
def resolve(self, query: LocationQuery) -> ResolverOutput:
extra = query.extra or {}
source = coerce_str(extra.get("source"))
source_id = coerce_str(extra.get("source_id"))
if not source or not source_id:
return ResolverOutput()
site = get_verified_compute_site(source, source_id)
if not site:
return ResolverOutput()
source_urls = [coerce_str(url) for url in site.get("sources", []) if coerce_str(url)]
source_note = coerce_str(site.get("source_note"))
if source_urls:
source_note = f"{source_note} Sources: {'; '.join(source_urls)}".strip()
return ResolverOutput(
candidates=(
LocationCandidate(
latitude=float(site["latitude"]),
longitude=float(site["longitude"]),
display_name=coerce_str(site.get("matched_location_name"))
or query.name
or "Compute center",
precision=coerce_str(site.get("precision")) or "site",
confidence=1.0,
query=f"official_facility_registry::{source}:{source_id}",
source=self.name,
source_note=source_note or None,
matched_fields=("source", "source_id"),
needs_confirmation=False,
city=coerce_str(site.get("city")) or None,
region=None,
country=coerce_str(site.get("country")) or query.country,
matched_location_name=coerce_str(site.get("matched_location_name"))
or None,
location_verified_at=coerce_str(site.get("verified_at")) or None,
suggested_registry_entry=None,
raw_payload={
"address": site.get("address"),
"coordinate_basis": site.get("coordinate_basis"),
"sources": source_urls,
},
),
)
)
@@ -400,7 +531,7 @@ def _short_system_name(name: Any) -> str:
return head or text
def _record_context(record: Any, metadata: dict[str, Any]) -> dict[str, str]:
def _record_context(record: Any, metadata: dict[str, Any]) -> dict[str, Any]:
name = coerce_str(getattr(record, "name", None))
return {
"source": coerce_str(getattr(record, "source", None)),
@@ -417,11 +548,13 @@ def _record_context(record: Any, metadata: dict[str, Any]) -> dict[str, str]:
or metadata.get("manufacturer")
),
"organization": coerce_str(metadata.get("organization")),
"source_location": metadata.get("location"),
"source_raw_country": metadata.get("raw_country"),
}
def _context_to_query(
context: dict[str, str],
context: dict[str, Any],
*,
source_lat: float | None = None,
source_lon: float | None = None,
@@ -444,6 +577,8 @@ def _context_to_query(
"site": context.get("site") or "",
"operator": context.get("operator") or "",
"organization": context.get("organization") or "",
"source_location": context.get("source_location"),
"source_raw_country": context.get("source_raw_country"),
},
)
@@ -497,11 +632,36 @@ def _compute_center_query_plan(
return plan
def _compute_center_candidate_sort_key(candidate: LocationCandidate) -> tuple[int, int, float]:
stored_origin = (candidate.raw_payload or {}).get("stored_location_source")
stored_confirmed = (
candidate.source == "stored_compute_center_location"
and not candidate.needs_confirmation
and stored_origin != "source_coordinates"
)
if stored_confirmed:
source_rank = 0
elif candidate.source == "source_coordinates" and candidate.precision == "precise":
source_rank = 1
elif candidate.source == "official_facility_registry":
source_rank = 2
elif candidate.source == "source_coordinates":
source_rank = 3
elif candidate.source == "stored_compute_center_location":
source_rank = 4
else:
source_rank = 5
precision_rank = {"precise": 0, "site": 1, "city": 2}.get(candidate.precision, 9)
return (source_rank, precision_rank, -float(candidate.confidence or 0))
COMPUTE_CENTER_PIPELINE = LocationPipeline(
[
SourceCoordinatesResolver(),
ComputeCenterSourceCoordinatesResolver(),
StoredComputeCenterLocationResolver(),
VerifiedComputeSiteResolver(),
],
sort_key=_compute_center_candidate_sort_key,
failure_reason=(
"Could not resolve to city-level coordinates from source coords"
" or stored compute-center location."
@@ -510,7 +670,8 @@ COMPUTE_CENTER_PIPELINE = LocationPipeline(
COMPUTE_CENTER_COLLECTION_PIPELINE = LocationPipeline(
[
SourceCoordinatesResolver(),
ComputeCenterSourceCoordinatesResolver(),
VerifiedComputeSiteResolver(),
ROROrganizationResolver(),
NominatimResolver(
query_plan_builder=_compute_center_query_plan,
@@ -518,6 +679,7 @@ COMPUTE_CENTER_COLLECTION_PIPELINE = LocationPipeline(
geocoder=lambda q: _geocode_online(q),
),
],
sort_key=_compute_center_candidate_sort_key,
failure_reason=(
"Could not resolve to city-level coordinates from source coords"
", ROR organization lookup, or online geocoding."
@@ -531,6 +693,7 @@ COMPUTE_CENTER_COLLECTION_PIPELINE = LocationPipeline(
_GEOGRAPHY_MODE_BY_SOURCE = {
"source_coordinates": "source_coordinates",
"stored_compute_center_location": "stored_compute_center_location",
"official_facility_registry": "official_facility_registry",
"ror_organization_registry": "ror_organization",
"nominatim_online_geocode": "online_geocode",
}
@@ -539,7 +702,7 @@ _GEOGRAPHY_MODE_BY_SOURCE = {
def _candidate_to_location(
candidate: LocationCandidate,
*,
context: dict[str, str],
context: dict[str, Any],
) -> ComputeCenterLocation:
geography_mode = _GEOGRAPHY_MODE_BY_SOURCE.get(candidate.source, "online_geocode")
is_estimated = candidate.needs_confirmation or candidate.source.startswith(
@@ -594,7 +757,7 @@ def _candidate_to_location(
def _diagnostic_for(
record: Any,
context: dict[str, str],
context: dict[str, Any],
*,
failure_reason: str,
attempted_queries: tuple[str, ...] = (),
@@ -738,7 +901,7 @@ def build_compute_center_location_query(
organization: str | None = None,
) -> LocationQuery:
name_value = coerce_str(name)
context: dict[str, str] = {
context: dict[str, Any] = {
"source": coerce_str(source),
"source_id": coerce_str(source_id),
"name": name_value,
@@ -783,6 +946,23 @@ async def seed_compute_center_locations_from_source_coords(
longitude = parse_float(get_record_field(record, "longitude"))
if latitude in (None, 0.0) or longitude in (None, 0.0):
continue
metadata = record.extra_data or {}
precision = "precise"
confidence = 1.0
needs_confirmation = False
source_note = "Seeded from source-provided compute-center coordinates"
if source_value == "epoch_ai_gpu":
assessment = assess_epoch_location(
metadata.get("location"),
country=get_record_field(record, "country"),
raw_country=metadata.get("raw_country"),
)
if not assessment.is_renderable:
continue
precision = assessment.precision
confidence = 1.0 if precision == "precise" else 0.9
needs_confirmation = precision != "precise"
source_note = assessment.reason
existing = await session.scalar(
select(ComputeCenterLocationRecord)
.where(ComputeCenterLocationRecord.source == source_value)
@@ -790,7 +970,6 @@ async def seed_compute_center_locations_from_source_coords(
)
if existing:
continue
metadata = record.extra_data or {}
session.add(
ComputeCenterLocationRecord(
source=source_value,
@@ -802,16 +981,16 @@ async def seed_compute_center_locations_from_source_coords(
country=coerce_str(get_record_field(record, "country")) or None,
latitude=latitude,
longitude=longitude,
precision="precise",
confidence=1.0,
precision=precision,
confidence=confidence,
location_source="source_coordinates",
source_note="Seeded from source-provided compute-center coordinates",
source_note=source_note,
raw_payload={
"record_id": getattr(record, "id", None),
"source": source_value,
"source_id": source_id,
},
needs_confirmation=False,
needs_confirmation=needs_confirmation,
verification_status="source_provided",
verified_at=None,
)

View File

@@ -0,0 +1,44 @@
{
"version": 1,
"sites": [
{
"source": "epoch_ai_gpu",
"source_ids": ["epoch_jamstec_zettascaler_2_0_gyoukou", "epoch_jamstec_zettascaler_2_2_gyoukou"],
"latitude": 35.380924,
"longitude": 139.625008,
"precision": "site",
"location_source": "official_facility_registry",
"country": "日本",
"city": "Yokohama",
"matched_location_name": "JAMSTEC Yokohama Institute for Earth Sciences",
"address": "3173-25 Showa-machi, Kanazawa-ku, Yokohama, Kanagawa, Japan",
"source_note": "Gyoukou deployment confirmed by JAMSTEC's 2017 release. Campus-level anchor from its official access map center, not a surveyed machine-room or building coordinate.",
"verified_at": "2026-09-21T17:06:31Z",
"coordinate_basis": "official_access_map_center",
"sources": [
"https://www.jamstec.go.jp/j/about/press_release/archive/2017/20171114.pdf",
"https://www.jamstec.go.jp/e/about/access/yokohama.html"
]
},
{
"source": "epoch_ai_gpu",
"source_ids": ["epoch_argonne_nl_sophia__formerly_theta_"],
"latitude": 41.718109383287235,
"longitude": -87.984498648496,
"precision": "site",
"location_source": "official_facility_registry",
"country": "美国",
"city": "Argonne",
"matched_location_name": "Argonne Leadership Computing Facility",
"address": "9700 South Cass Avenue, Building 240, Argonne, IL 60439, USA",
"source_note": "Sophia is an ALCF system. Campus-level anchor from ALCF's official visitor map center; the published address identifies Building 240 but the coordinate is not a surveyed building or rack location.",
"verified_at": "2026-09-21T17:06:31Z",
"coordinate_basis": "official_access_map_center",
"sources": [
"https://www.alcf.anl.gov/systems/sophia",
"https://docs.alcf.anl.gov/sophia/",
"https://www.alcf.anl.gov/about/visiting-alcf"
]
}
]
}

View File

@@ -0,0 +1,26 @@
"""Evidence-backed facility anchors; never match an operator's headquarters by name."""
import json
from copy import deepcopy
from functools import lru_cache
from pathlib import Path
@lru_cache(maxsize=1)
def _site_index() -> dict[tuple[str, str], dict]:
payload = json.loads(Path(__file__).with_suffix(".json").read_text(encoding="utf-8"))
index = {}
for site in payload["sites"]:
if not (-90 <= site["latitude"] <= 90 and -180 <= site["longitude"] <= 180):
raise ValueError("Invalid verified compute facility coordinate")
for source_id in site["source_ids"]:
key = (site["source"], source_id)
if key in index:
raise ValueError("Duplicate verified compute facility identity")
index[key] = site
return index
def get_verified_compute_site(source: str, source_id: str) -> dict | None:
site = _site_index().get((source, source_id))
return deepcopy(site) if site else None

View File

@@ -0,0 +1,99 @@
"""Database overrides with bundled defaults and atomic, revision-checked publishing."""
import hashlib
from pathlib import Path
from uuid import uuid4
from app.core.request_context import get_request_id
from app.models.system_log import AuditLog
from app.models.system_setting import SystemSetting
from app.models.user import User
from app.schemas.compute_configuration import ComputeConfiguration
from app.services.compute_catalog import COUNTRY_CATALOG
from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.ext.asyncio import AsyncSession
CATEGORY = "compute_comparison"
DEFAULT_PATH = Path(__file__).with_name("compute_history_defaults.json")
DEFAULT_CONFIGURATION = ComputeConfiguration.model_validate_json(DEFAULT_PATH.read_text())
DEFAULT_REVISION = hashlib.sha256(DEFAULT_PATH.read_bytes()).hexdigest()
def default_envelope() -> dict:
return {
"config": DEFAULT_CONFIGURATION.model_dump(mode="json"),
"revision": DEFAULT_REVISION,
"is_default": True,
}
async def read_configuration(db: AsyncSession) -> dict:
record = (
await db.execute(select(SystemSetting).where(SystemSetting.category == CATEGORY))
).scalar_one_or_none()
return dict(record.payload) if record else default_envelope()
def configuration_response(envelope: dict) -> dict:
return {
**envelope,
"countries": [
{key: country[key] for key in ("code", "name", "name_en")}
for country in COUNTRY_CATALOG
],
}
async def publish_configuration(
db: AsyncSession,
*,
revision: str,
config: ComputeConfiguration,
actor: User,
reset: bool = False,
) -> dict:
# The unique category plus row lock also serializes concurrent first writes.
await db.execute(
insert(SystemSetting)
.values(category=CATEGORY, payload=default_envelope())
.on_conflict_do_nothing(index_elements=[SystemSetting.category])
)
record = (
await db.execute(
select(SystemSetting).where(SystemSetting.category == CATEGORY).with_for_update()
)
).scalar_one()
if record.payload["revision"] != revision:
await db.rollback()
raise HTTPException(409, "Configuration changed. Reload before saving your edits.")
envelope = (
default_envelope()
if reset
else {
"config": config.model_dump(mode="json"),
"revision": uuid4().hex,
"is_default": reset,
}
)
before = record.payload
if reset:
await db.delete(record)
else:
record.payload = envelope
# Audit and configuration commit together: a published edit must be attributable.
db.add(
AuditLog(
actor_id=actor.id,
actor_name=actor.username,
action="compute_comparison.reset" if reset else "compute_comparison.update",
target_type="system_setting",
target_id=CATEGORY,
result="success",
request_id=get_request_id(),
details={"before": before, "after": envelope},
)
)
await db.commit()
return envelope

View File

@@ -0,0 +1,122 @@
"""Versioned, source-backed annual observations for compute comparisons."""
from copy import deepcopy
from datetime import date
from app.schemas.compute_configuration import ComputeConfiguration
from app.services.compute_catalog import (
CATALOG,
DEFAULT_PARTICIPANTS,
PARTICIPANTS,
participant_members,
)
from app.services.compute_configuration import DEFAULT_CONFIGURATION
def _participant_value(
members: tuple[str, ...], observations: dict[str, float | int], mode: str
) -> tuple[float | int | None, dict]:
covered = [member for member in members if member in observations]
missing = [member for member in members if member not in observations]
coverage = {
"status": "complete" if not missing else "incomplete",
"required_members": list(members),
"covered_members": covered,
"missing_members": missing,
}
if missing:
return None, coverage
if mode == "bilateral_share":
if (
not set(members).issubset({"CN", "US"})
or not {"CN", "US"}.issubset(observations)
or observations["US"] + observations["CN"] <= 0
):
coverage.update(
{
"status": "incompatible",
"reason": "bilateral_share_requires_CN_and_US_observations",
}
)
return None, coverage
return (
round(
sum(observations[member] for member in members)
/ (observations["US"] + observations["CN"])
* 100,
2,
),
coverage,
)
return sum(observations[member] for member in members), coverage
def compute_history(
countries: list[str],
mode: str = "global_share",
config: ComputeConfiguration | None = None,
) -> dict:
"""Aggregate only fully covered, same-year compatible observations."""
config = config if config is not None else DEFAULT_CONFIGURATION
metric = "global_share" if mode == "bilateral_share" else mode
annual = {row.year: row for row in config.observations if row.metric == metric}
gaps = {row.year: row for row in config.innovation}
observed_years = [row.year for row in config.observations] + list(gaps)
years = tuple(
range(
min(observed_years, default=date.today().year),
max([date.today().year, *observed_years]) + 1,
)
)
members_by_participant = participant_members(countries)
rows = []
for year in years:
record = annual.get(year)
observations = record.values if record else {}
values, coverage = {}, {}
for code, members in members_by_participant.items():
values[code], coverage[code] = _participant_value(members, observations, mode)
evidence_id = record.source_id if record else None
rows.append(
{"year": year, "values": values, "coverage": coverage, "evidence_id": evidence_id}
)
selected_members = {member for members in members_by_participant.values() for member in members}
has_innovation_scope = {"CN", "US"}.issubset(selected_members)
return {
"version": f"compute-history-{config.reviewed_at}",
"provenance": "curated_reports",
"reviewed_at": config.reviewed_at.isoformat(),
"time_basis": "retrospective",
"years": years,
"mode": mode,
"unit": "EFlop/s (FP32)" if mode == "absolute" else "%",
"denominator": {"global_share": "global", "bilateral_share": "US+CN", "absolute": None}[
mode
],
"catalog": deepcopy(CATALOG),
"default_participants": list(DEFAULT_PARTICIPANTS),
"countries": [deepcopy(PARTICIPANTS[code]) for code in countries],
"compute": rows,
"innovation": [
{
"year": year,
"value": gaps[year].value if year in gaps and has_innovation_scope else None,
"evidence_id": gaps[year].source_id
if year in gaps and has_innovation_scope
else None,
}
for year in years
],
"evidence": {
source.id: source.model_dump(mode="json", exclude={"id"}) for source in config.sources
},
"innovation_scope": ["US", "CN"],
"limitations": [
"公开设施样本不代表全国总算力;FP64、FP16 与 FP32 不可直接相加。",
"区域值只在全部成员有同年、同指标、可比较观测时汇总;缺失成员时返回 null。",
"全球份额不从中美或其他局部成员反推剩余地区。",
"年度资料按已配置报告引用展示,观测年与报告年份分列;跨年方法可比性仍需核验。",
"创新分差为美国减中国;只有分差,不反推单国得分,不证明算力与创新的因果关系。",
],
}

View File

@@ -0,0 +1,125 @@
{
"reviewed_at": "2026-09-22",
"sources": [
{
"id": "caict-2021",
"title": "中国算力发展指数白皮书(2021)",
"url": "https://www.caict.ac.cn/kxyj/qwfb/bps/202109/P020210918521091309950.pdf",
"publisher": "中国信息通信研究院",
"report_year": 2021,
"published_at": null,
"locator": "正文 11 页图 4(2020 年全球份额)"
},
{
"id": "caict-2022",
"title": "中国算力发展指数白皮书(2022)",
"url": "https://www.caict.ac.cn/kxyj/qwfb/bps/202211/P020221105727522653499.pdf",
"publisher": "中国信息通信研究院",
"report_year": 2022,
"published_at": null,
"locator": "正文 13 页图 5(2021 年全球份额)"
},
{
"id": "caict-2023",
"title": "中国算力发展指数白皮书(2023)",
"url": "https://www.caict.ac.cn/kxyj/qwfb/bps/202309/P020240326630458153765.pdf",
"publisher": "中国信息通信研究院",
"report_year": 2023,
"published_at": null,
"locator": "正文 13 页图 5(2022 年全球份额);16 页(中国总规模)"
},
{
"id": "caict-2024",
"title": "先进计算暨算力发展指数蓝皮书(2024年)",
"url": "https://www.caict.ac.cn/kxyj/qwfb/bps/202501/P020250117503621662777.pdf",
"publisher": "中国信息通信研究院",
"report_year": 2024,
"published_at": null,
"locator": "正文 10 页图 3(2023 年全球份额);12 页(中国总规模)"
},
{
"id": "innovation-2026",
"title": "全球人工智能创新指数报告 2026:科技日报报道",
"url": "https://finance.sina.com.cn/tech/roll/2026-07-17/doc-iniickaz8224252.shtml",
"publisher": "科技日报(新浪转载)",
"report_year": 2026,
"published_at": "2026-07-17",
"locator": "2023–2025 年美国减中国的指数分差"
}
],
"observations": [
{
"year": 2020,
"metric": "global_share",
"source_id": "caict-2021",
"values": {
"US": 36,
"CN": 31,
"JP": 6
}
},
{
"year": 2021,
"metric": "global_share",
"source_id": "caict-2022",
"values": {
"US": 34,
"CN": 33,
"JP": 5
}
},
{
"year": 2022,
"metric": "global_share",
"source_id": "caict-2023",
"values": {
"US": 34,
"CN": 33,
"JP": 4
}
},
{
"year": 2023,
"metric": "global_share",
"source_id": "caict-2024",
"values": {
"US": 41,
"CN": 31,
"JP": 4
}
},
{
"year": 2022,
"metric": "absolute",
"source_id": "caict-2023",
"values": {
"CN": 302
}
},
{
"year": 2023,
"metric": "absolute",
"source_id": "caict-2024",
"values": {
"CN": 435
}
}
],
"innovation": [
{
"year": 2023,
"value": 22.02,
"source_id": "innovation-2026"
},
{
"year": 2024,
"value": 19.96,
"source_id": "innovation-2026"
},
{
"year": 2025,
"value": 17.95,
"source_id": "innovation-2026"
}
]
}

View File

@@ -6,8 +6,10 @@ import asyncio
import hashlib
import json
import shutil
import subprocess
import sys
from datetime import UTC, datetime
from functools import lru_cache
from pathlib import Path
from typing import Any
from uuid import uuid4
@@ -25,8 +27,11 @@ BOUNDARY_MANIFEST_PATH = BOUNDARY_OUTPUT_DIR / "manifest.json"
PMTILES_ARTIFACT_PATH = (
REPO_ROOT / "frontend/public/earth/data/boundaries/earth-boundaries-china-pov-v1.pmtiles"
)
LEGACY_GEOJSON_PATH = REPO_ROOT / "frontend/public/earth/data/countries-admin0.min.geojson"
POLICY_FALLBACK_PATH = (
REPO_ROOT / "frontend/public/earth/data/countries-admin0.china-pov-v1.geojson"
)
POV_POLICY_PATH = REPO_ROOT / "config/earth-boundary-pov-policy.china-v1.json"
POV_GEOMETRY_PATH = REPO_ROOT / "resources/earth/boundary-pov-geometry.china-v1.json"
LOCAL_CONFIG_PATH = REPO_ROOT / "config/earth-boundary-sources.local.json"
EXAMPLE_CONFIG_PATH = REPO_ROOT / "config/earth-boundary-sources.example.json"
@@ -150,6 +155,21 @@ def _stable_json_hash(payload: Any) -> str:
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()
@lru_cache(maxsize=16)
def _read_versioned_asset_cached(
path: Path, mtime_ns: int, size_bytes: int
) -> tuple[dict[str, Any], str]:
payload = _read_json(path)
return payload, _stable_json_hash(payload)
def _versioned_asset(path: Path) -> tuple[dict[str, Any], str]:
if not path.exists():
return {}, ""
stat = path.stat()
return _read_versioned_asset_cached(path, stat.st_mtime_ns, stat.st_size)
def _artifact_extension(endpoint: str, content_type: str, payload: bytes) -> str:
suffix = Path(endpoint.split("?", 1)[0]).suffix.lower()
if suffix in {".json", ".geojson", ".zip", ".pbf"}:
@@ -176,62 +196,37 @@ def _directory_stats(path: Path) -> dict[str, int]:
return {"file_count": len(files), "size_bytes": sum(item.stat().st_size for item in files)}
def _load_source_feature_collection(source: dict[str, Any]) -> dict[str, Any]:
path = REPO_ROOT / source["path"]
payload = _read_json(path)
features = payload.get("features") if isinstance(payload, dict) else None
return {
"type": "FeatureCollection",
"features": features if isinstance(features, list) else [],
}
def _write_high_precision_geojson_manifest(
sources: list[dict[str, Any]],
build_input_hash: str,
missing_tools: list[str],
) -> dict[str, Any]:
BOUNDARY_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
admin0 = next(source for source in sources if source["kind"] == "admin0-boundaries")
coastline = next(source for source in sources if source["kind"] == "coastline")
claim_lines = next(source for source in sources if source["kind"] == "claim-lines")
admin0_payload = _load_source_feature_collection(admin0)
coastline_payload = _load_source_feature_collection(coastline)
claim_payload = _load_source_feature_collection(claim_lines)
for feature in coastline_payload["features"]:
props = feature.setdefault("properties", {})
if isinstance(props, dict):
props["PLANET_LAYER"] = "coastline"
base_payload = {
"type": "FeatureCollection",
"features": [*admin0_payload["features"], *coastline_payload["features"]],
}
base_path = BOUNDARY_OUTPUT_DIR / "earth-boundaries-high-precision-base.geojson"
hover_path = BOUNDARY_OUTPUT_DIR / "earth-boundaries-high-precision-hover.geojson"
claim_path = BOUNDARY_OUTPUT_DIR / "earth-boundaries-high-precision-claims.geojson"
_write_json(base_path, base_payload)
_write_json(hover_path, admin0_payload)
_write_json(claim_path, claim_payload)
manifest = {
"version": "natural-earth-v1",
"builtAt": _utc_now_iso(),
"tileProvider": "geojson-high-precision",
"format": "geojson-directory",
"buildInputHash": build_input_hash,
"base": base_path.name,
"hoverIndex": hover_path.name,
"claimLine": claim_path.name,
"sourceFeatureCount": {
"admin0": len(admin0_payload["features"]),
"coastline": len(coastline_payload["features"]),
"claimLines": len(claim_payload["features"]),
},
"pmtiles": None,
"missingTools": missing_tools,
}
result = subprocess.run(
[
sys.executable,
str(Path(__file__).resolve().parents[3] / "scripts/build_earth_boundary_china_pov_geojson.py"),
"--admin0-source", admin0["path"],
"--coastline-source", coastline["path"],
"--claims-source", claim_lines["path"],
"--output-dir", _relative(BOUNDARY_OUTPUT_DIR),
"--build-input-hash", build_input_hash,
"--pov-policy", _relative(POV_POLICY_PATH),
"--pov-geometry", str(POV_GEOMETRY_PATH),
],
cwd=REPO_ROOT,
capture_output=True,
text=True,
)
if result.returncode:
raise EarthBoundaryBuildError(
result.stderr.strip() or "Earth boundary policy build failed",
code="policy_build_failed",
)
manifest = _read_json(BOUNDARY_MANIFEST_PATH)
manifest["missingTools"] = missing_tools
_write_json(BOUNDARY_MANIFEST_PATH, manifest)
return manifest
@@ -291,6 +286,7 @@ def _build_input_hash(source_manifest: dict[str, Any]) -> str:
for source in source_manifest.get("sources", [])
],
"pov_policy": source_manifest.get("povPolicy"),
"pov_geometry_sha256": _versioned_asset(POV_GEOMETRY_PATH)[1],
"build_config": BUILD_CONFIG,
}
)
@@ -301,7 +297,47 @@ def _has_current_artifacts(boundary_manifest: dict[str, Any], build_input_hash:
bool(boundary_manifest)
and boundary_manifest.get("buildInputHash") == build_input_hash
and boundary_manifest.get("tileProvider") == "pmtiles-mvt"
and _policy_verified(boundary_manifest)
and PMTILES_ARTIFACT_PATH.exists()
and _pmtiles_hover_exists(boundary_manifest)
)
def _policy_verified(manifest: dict[str, Any]) -> bool:
policy, policy_hash = _versioned_asset(POV_POLICY_PATH)
geometry, geometry_hash = _versioned_asset(POV_GEOMETRY_PATH)
coverage = manifest.get("povPolicy") or {}
return (
policy.get("profile") == "china-pov-v1"
and coverage.get("profile") == policy["profile"]
and coverage.get("sha256") == policy_hash
and coverage.get("reviewedGeometrySourceCommit")
== geometry.get("sourceCommit")
and coverage.get("geometrySha256") == geometry_hash
and coverage.get("applied") is True
and {"china-taiwan-penghu", "china-hong-kong", "china-macao", "kosovo"}
<= set(coverage.get("appliedRuleIds") or [])
)
def _geojson_artifacts_exist(manifest: dict[str, Any]) -> bool:
paths = [manifest.get("base"), manifest.get("hoverIndex")]
if manifest.get("chinaClaims", {}).get("available"):
paths.append(manifest.get("claimLine"))
for asset in paths:
if not isinstance(asset, str) or Path(asset).name != asset:
return False
if not (BOUNDARY_OUTPUT_DIR / asset).is_file():
return False
return True
def _pmtiles_hover_exists(manifest: dict[str, Any]) -> bool:
hover = manifest.get("hoverIndex")
return (
isinstance(hover, str)
and Path(hover).name == hover
and (BOUNDARY_OUTPUT_DIR / hover).is_file()
)
@@ -313,24 +349,34 @@ def get_boundary_status() -> dict[str, Any]:
manifest_exists = BOUNDARY_MANIFEST_PATH.exists()
high_precision_ready = (
manifest_exists
and _policy_verified(boundary_manifest)
and (
(
boundary_manifest.get("tileProvider") == "pmtiles-mvt"
and pmtiles_exists
and _pmtiles_hover_exists(boundary_manifest)
)
or (
boundary_manifest.get("tileProvider") == "geojson-high-precision"
and _geojson_artifacts_exist(boundary_manifest)
)
or boundary_manifest.get("tileProvider") == "geojson-high-precision"
)
)
legacy_exists = LEGACY_GEOJSON_PATH.exists()
legacy_exists = POLICY_FALLBACK_PATH.exists() and _policy_verified(
_versioned_asset(POLICY_FALLBACK_PATH)[0]
)
provider = (
boundary_manifest.get("tileProvider")
if high_precision_ready
else "legacy-geojson" if legacy_exists else "missing"
else "policy-geojson-fallback" if legacy_exists else "missing"
)
return {
"provider": provider,
"high_precision_ready": high_precision_ready,
"fallback_available": legacy_exists,
"policy_publishable": bool(
high_precision_ready and boundary_manifest.get("povPolicy", {}).get("publicationReady")
),
"config_source": config_source,
"config_path": _relative(LOCAL_CONFIG_PATH),
"config_exists": LOCAL_CONFIG_PATH.exists(),
@@ -347,6 +393,8 @@ def get_boundary_status() -> dict[str, Any]:
"tileProvider": boundary_manifest.get("tileProvider"),
"buildInputHash": boundary_manifest.get("buildInputHash"),
"builtAt": boundary_manifest.get("builtAt"),
"povPolicy": boundary_manifest.get("povPolicy"),
"publicationReady": boundary_manifest.get("povPolicy", {}).get("publicationReady", False),
},
"pmtiles": {
"path": _relative(PMTILES_ARTIFACT_PATH),
@@ -354,9 +402,9 @@ def get_boundary_status() -> dict[str, Any]:
"size_bytes": PMTILES_ARTIFACT_PATH.stat().st_size if pmtiles_exists else 0,
},
"legacy": {
"path": _relative(LEGACY_GEOJSON_PATH),
"path": _relative(POLICY_FALLBACK_PATH),
"exists": legacy_exists,
"size_bytes": LEGACY_GEOJSON_PATH.stat().st_size if legacy_exists else 0,
"size_bytes": POLICY_FALLBACK_PATH.stat().st_size if legacy_exists else 0,
},
"source_manifest": {
"path": _relative(SOURCE_MANIFEST_PATH),
@@ -577,6 +625,8 @@ async def build_boundary_assets(progress_callback: Any = None) -> dict[str, Any]
build_input_hash,
"--pov-policy",
_relative(POV_POLICY_PATH),
"--pov-geometry",
str(POV_GEOMETRY_PATH),
]
)
if progress_callback:

View File

@@ -0,0 +1,198 @@
"""Interpret the geographic grain of Epoch AI's official ``Location`` field."""
from __future__ import annotations
from dataclasses import dataclass
import math
import re
from typing import Any
from app.core.countries import normalize_country
_COUNTRY_LABEL_OVERRIDES = {
"korea republic of": "韩国",
}
_REGION_ONLY_LABELS = {
"europe",
"iowa",
"maryland",
"new mexico",
"texas",
"the nordics",
"virginia",
"washington",
}
_UNLOCATABLE_LABELS = {'"on prem" for tesla', "on prem for tesla"}
_HEMISPHERE_COORDINATE_PATTERN = re.compile(
r"(?P<degrees>\d{1,3})(?:[°º]|\s)\s*(?P<minutes>\d{1,2})(?:['′]|\s)"
r"(?P<seconds>\d{1,2}(?:\.\d+)?)?[\"″]?\s*(?P<hemisphere>[NSEW])",
re.IGNORECASE,
)
_LABELED_COORDINATE_PAIR_PATTERN = re.compile(
r"\b(?:lat|latitude)\b\s*[:=]?\s*(?P<latitude>-?\d{1,2}(?:\.\d+)?)"
r".{0,40}\b(?:lon|lng|longitude)\b\s*[:=]?\s*"
r"(?P<longitude>-?\d{1,3}(?:\.\d+)?)",
re.IGNORECASE | re.DOTALL,
)
_DECIMAL_COORDINATE_PAIR_PATTERN = re.compile(
r"^\s*[\[(]?\s*(-?\d{1,2}\.\d+)\s*[,;]\s*"
r"(-?\d{1,3}\.\d+)\s*[\])]?\s*$"
)
_NUMBERED_STREET_PATTERN = re.compile(
r"(?:\b(?:no\.?\s*)?\d+[a-z]?\s+(?:[\w.'’\-]+\s+){0,6}"
r"(?:road|rd|street|st|avenue|ave|boulevard|blvd|drive|dr|lane|ln|"
r"highway|hwy|way|parkway|pkwy|chome)\b|"
r"\b(?:via|rue|calle|plaça|strasse|straße)\b[^,\n]{0,80},?\s*\d)",
re.IGNORECASE,
)
_POSTAL_CODE_PATTERN = re.compile(
r"(?:\b\d{5}(?:-\d{4})?\b|\b\d{3}-\d{4}\b|"
r"\b[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}\b)",
re.IGNORECASE,
)
_SITE_CONTEXT_PATTERN = re.compile(
r"\b(?:campus|building|cluster|data\s+cent(?:er|re)|facility|institute|"
r"laborator(?:y|ies)|science\s+park|supercomputing\s+cent(?:er|re))\b",
re.IGNORECASE,
)
_COORDINATE_CUE_PATTERN = re.compile(
r"(?:\b(?:lat|latitude|lon|lng|longitude)\b|[°º].*[NSEW])",
re.IGNORECASE | re.DOTALL,
)
@dataclass(frozen=True)
class EpochLocationAssessment:
precision: str
reason: str
@property
def is_renderable(self) -> bool:
return self.precision in {"precise", "site", "city"}
def _normalized_label(value: Any) -> str:
return re.sub(r"[^a-z0-9]+", " ", str(value or "").casefold()).strip()
def _country_from_label(value: Any) -> str | None:
return normalize_country(value) or _COUNTRY_LABEL_OVERRIDES.get(
_normalized_label(value)
)
def _coordinate(mapping: dict[str, Any], keys: tuple[str, ...], bound: float) -> float | None:
raw = next((mapping.get(key) for key in keys if mapping.get(key) not in (None, "")), None)
if isinstance(raw, bool):
return None
try:
number = float(raw)
except (TypeError, ValueError):
return None
return number if math.isfinite(number) and abs(number) <= bound else None
def _has_explicit_coordinate_pair(text: str) -> bool:
hemispheres = {}
for match in _HEMISPHERE_COORDINATE_PATTERN.finditer(text):
hemisphere = match.group("hemisphere").upper()
degrees = int(match.group("degrees"))
minutes = int(match.group("minutes"))
seconds = float(match.group("seconds") or 0)
bound = 90 if hemisphere in {"N", "S"} else 180
if degrees <= bound and minutes < 60 and seconds < 60:
hemispheres[hemisphere] = degrees
if ({"N", "S"} & hemispheres.keys()) and ({"E", "W"} & hemispheres.keys()):
return True
labeled_pair = _LABELED_COORDINATE_PAIR_PATTERN.search(text)
if labeled_pair:
latitude = float(labeled_pair.group("latitude"))
longitude = float(labeled_pair.group("longitude"))
return abs(latitude) <= 90 and abs(longitude) <= 180
decimal_pair = _DECIMAL_COORDINATE_PAIR_PATTERN.match(text)
if not decimal_pair:
return False
latitude, longitude = map(float, decimal_pair.groups())
return abs(latitude) <= 90 and abs(longitude) <= 180
def _has_address_evidence(text: str) -> bool:
if _NUMBERED_STREET_PATTERN.search(text):
return True
return bool(
_POSTAL_CODE_PATTERN.search(text)
and ("\n" in text or text.count(",") >= 2)
and _SITE_CONTEXT_PATTERN.search(text)
)
def assess_epoch_location(
location: Any,
*,
country: Any = None,
raw_country: Any = None,
) -> EpochLocationAssessment:
"""Classify source coordinates without upgrading their source text.
Epoch's CSV supplies coordinates even when ``Location`` contains only a
country. Those coordinates are useful source evidence, but are not a
facility point and must not enter the renderable location pipeline.
"""
if isinstance(location, dict):
latitude = _coordinate(location, ("lat", "latitude"), 90)
longitude = _coordinate(location, ("lon", "lng", "longitude"), 180)
has_coordinates = latitude is not None and longitude is not None
return EpochLocationAssessment(
"precise" if has_coordinates else "unknown",
"complete explicit coordinate object"
if has_coordinates
else "coordinate object lacks a complete valid latitude/longitude pair",
)
text = str(location or "").strip()
if not text:
return EpochLocationAssessment("unknown", "Epoch Location is empty")
normalized = _normalized_label(text)
normalized_country = normalize_country(country)
source_country = normalize_country(raw_country)
location_country = _country_from_label(text)
if location_country or (
raw_country and normalized == _normalized_label(raw_country)
):
matched = location_country or source_country or normalized_country
return EpochLocationAssessment(
"country",
f"Epoch Location identifies country only: {matched or text}",
)
if normalized in _REGION_ONLY_LABELS:
return EpochLocationAssessment(
"region", f"Epoch Location identifies region/state only: {text}"
)
comma_parts = [part.strip() for part in text.split(",") if part.strip()]
if (
len(comma_parts) == 2
and _normalized_label(comma_parts[0]) in _REGION_ONLY_LABELS
and _country_from_label(comma_parts[1])
):
return EpochLocationAssessment(
"region", f"Epoch Location identifies region/state only: {text}"
)
if normalized in _UNLOCATABLE_LABELS:
return EpochLocationAssessment("unknown", "Epoch Location has no geographic place")
if normalized.isdigit():
return EpochLocationAssessment("unknown", "Epoch Location is only a numeric label")
if _has_explicit_coordinate_pair(text):
return EpochLocationAssessment(
"precise", "Epoch Location contains a complete valid coordinate pair"
)
if _COORDINATE_CUE_PATTERN.search(text):
return EpochLocationAssessment(
"unknown", "Epoch Location contains an incomplete or invalid coordinate pair"
)
if _has_address_evidence(text):
return EpochLocationAssessment("precise", "Epoch Location contains an address")
if "\n" in text or _SITE_CONTEXT_PATTERN.search(text):
return EpochLocationAssessment("site", "Epoch Location names a facility or campus")
return EpochLocationAssessment("city", "Epoch Location identifies a locality")

View File

@@ -15,6 +15,12 @@ DEFAULT_TV_SOURCE_ID = "aljazeera-mubasher"
TV_SETTINGS_CATEGORY = "tv"
TV_LIVE_SOURCE_COLLECTOR = "news_live_streams"
TV_LIVE_SOURCE_DATA_TYPE = "news_live_stream"
CCTV4_HOMEPAGE = "https://tv.cctv.com/live/cctv4/"
CCTV4_STREAM = "https://h5cul1yar48um3t.wcetv.com/hls/cctv4.m3u8"
UNSUPPORTED_CCTV4_STREAM = (
"https://ldocctvwbcdtxy.liveplay.myqcloud.com/ldocctvwbcd/cdrmldcctv4_1_td.m3u8"
)
CCTV4_PLAYBACK_NOTE = "CCTV-4 美洲版,使用 WCETV HLS 直播源;节目编排可能与亚洲版不同。"
DEFAULT_TV_SETTINGS = {
"default_source_id": DEFAULT_TV_SOURCE_ID,
@@ -22,20 +28,21 @@ DEFAULT_TV_SETTINGS = {
"sources": [
{
"id": "cctv4",
"name": "CCTV-4 中文国际",
"name": "CCTV-4 中文国际(美洲版)",
"provider": "CCTV",
"region": "China",
"language": "zh-CN",
"source_type": "hls",
"embed_url": "https://tv.cctv.com/live/cctv4/",
"stream_url": "https://ldocctvwbcdtxy.liveplay.myqcloud.com/ldocctvwbcd/cdrmldcctv4_1_td.m3u8",
"homepage_url": "https://tv.cctv.com/live/cctv4/",
"embed_url": "",
"stream_url": CCTV4_STREAM,
"use_proxy": False,
"homepage_url": CCTV4_HOMEPAGE,
"poster_url": "",
"is_enabled": True,
"is_fallback": True,
"sort_order": 10,
"collector_source": None,
"notes": "默认兜底新闻直播源。优先尝试 CCTV-4 官方 HLS 播放流,若直播放失败则回退到央视官网直播页。",
"notes": CCTV4_PLAYBACK_NOTE,
},
{
"id": "reuters-tv",
@@ -237,6 +244,17 @@ def _clean_int(value: Any, *, default: int) -> int:
def normalize_tv_source(source: dict[str, Any] | None, *, index: int = 0) -> dict[str, Any]:
payload = dict(source or {})
# Retire only the formerly bundled CDRM URL, including saved copies of that default.
# HTTP 200 and valid TS framing do not mean this stream is decodable by hls.js.
if _clean_text(payload.get("id")) == "cctv4" and _clean_url(
payload.get("stream_url")
) == UNSUPPORTED_CCTV4_STREAM:
payload.update(
name="CCTV-4 中文国际(美洲版)",
source_type="hls", stream_url=CCTV4_STREAM, embed_url="",
use_proxy=False,
homepage_url=CCTV4_HOMEPAGE, notes=CCTV4_PLAYBACK_NOTE,
)
source_id = _clean_text(payload.get("id")) or f"tv-source-{index + 1}"
source_type = _clean_text(payload.get("source_type")).lower()
youtube_video_id = _clean_text(payload.get("youtube_video_id"))
@@ -259,6 +277,7 @@ def normalize_tv_source(source: dict[str, Any] | None, *, index: int = 0) -> dic
"source_type": source_type,
"embed_url": _clean_url(payload.get("embed_url")),
"stream_url": _clean_url(payload.get("stream_url")),
"use_proxy": _clean_bool(payload.get("use_proxy"), default=True),
"homepage_url": _clean_url(payload.get("homepage_url")),
"poster_url": _clean_url(payload.get("poster_url")),
"youtube_video_id": youtube_video_id,

View File

@@ -0,0 +1,72 @@
from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock
import httpx
import pytest
from fastapi import FastAPI
from pydantic import ValidationError
from app.api.v1.auth import router
from app.core import security
from app.db.session import DEFAULT_LOGIN_USERS, get_db
from app.models.user import User
from app.schemas.user import UserRegister
async def identity_request(monkeypatch, user, token=None):
result = MagicMock()
result.scalar_one_or_none.return_value = user
db = AsyncMock()
db.execute.return_value = result
monkeypatch.setattr(security.redis_client, "sismember", lambda *_: False)
app = FastAPI()
app.include_router(router, prefix="/auth")
async def override_db():
yield db
app.dependency_overrides[get_db] = override_db
async with httpx.AsyncClient(
transport=httpx.ASGITransport(app=app), base_url="http://test"
) as client:
return await client.get(
"/auth/me",
headers={"Authorization": "Bearer " + (token or security.create_access_token({"sub": 1}))},
)
@pytest.mark.asyncio
@pytest.mark.parametrize("default", DEFAULT_LOGIN_USERS)
async def test_default_identity_preserves_profile_fields(monkeypatch, default):
created = datetime(2026, 9, 21, tzinfo=UTC)
user = User(
id=1, username=default["username"], email=default["email"],
role=default["role"], password_hash="not-exposed", is_active=True,
email_verified=True, gatekeeper_groups=["ops"], created_at=created,
)
response = await identity_request(monkeypatch, user)
assert response.status_code == 200
payload = response.json()
assert payload["email"] == default["email"]
assert payload["email_verified"] is True
assert datetime.fromisoformat(payload["created_at"].replace("Z", "+00:00")) == created
assert payload["gatekeeper_groups"] == ["ops"]
assert "password_hash" not in payload
@pytest.mark.asyncio
@pytest.mark.parametrize("user", [None, User(id=1, is_active=False)])
async def test_missing_or_inactive_identity_is_rejected(monkeypatch, user):
response = await identity_request(monkeypatch, user)
assert response.status_code == 401
@pytest.mark.asyncio
async def test_refresh_token_is_not_an_access_identity(monkeypatch):
response = await identity_request(monkeypatch, None, security.create_refresh_token({"sub": 1}))
assert response.status_code == 401
def test_registration_still_validates_email():
with pytest.raises(ValidationError):
UserRegister(username="newuser", email="newuser@planet.local", password="example-password")

View File

@@ -79,7 +79,8 @@ class TestTOP500Collector:
"""Test parsing empty response"""
collector = TOP500Collector()
result = collector.parse_response("<html><body><table></table></body></html>")
assert len(result) > 0
assert result == []
assert collector.fail_on_empty
def test_parse_response_single_item(self):
"""Test parsing single item response"""
@@ -97,9 +98,9 @@ class TestTOP500Collector:
</tr>
</table>
"""
result = collector.parse_response(response)
result = collector.parse_response(response, "https://top500.org/lists/top500/2025/11/")
assert len(result) == 1
assert result[0]["source_id"] == "top500_1"
assert result[0]["source_id"] == "top500_system_1"
assert result[0]["name"] == "Test Supercomputer"
assert result[0]["country"] == "USA"
assert result[0]["metadata"]["rank"] == 1
@@ -113,7 +114,7 @@ class TestTOP500Collector:
<tr><th>Rank</th><th>System</th><th>Cores</th><th>Rmax</th><th>Rpeak</th><th>Power</th></tr>
<tr>
<td>1</td>
<td>Valid\nVendor\nSite\nUSA</td>
<td><a href="/system/2/">Valid</a>\nVendor\nSite\nUSA</td>
<td>1000</td>
<td>10 PFLOP/s</td>
<td>12 PFLOP/s</td>
@@ -129,7 +130,7 @@ class TestTOP500Collector:
</tr>
</table>
"""
result = collector.parse_response(response)
result = collector.parse_response(response, "https://top500.org/lists/top500/2025/11/")
assert len(result) == 1
assert result[0]["name"] == "Valid"

View File

@@ -0,0 +1,222 @@
from copy import deepcopy
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
import pytest
from app.api.v1.compute_intelligence import configuration_admin, get_series, router
from app.core.security import get_current_user
from app.db.session import get_db
from app.models.system_log import AuditLog
from app.schemas.compute_configuration import ComputeConfiguration
from app.services.compute_configuration import (
DEFAULT_CONFIGURATION,
default_envelope,
publish_configuration,
read_configuration,
)
from app.services.compute_history import compute_history
from fastapi import FastAPI, HTTPException, Response
from httpx import ASGITransport, AsyncClient
from pydantic import ValidationError
def default_config():
return DEFAULT_CONFIGURATION.model_dump(mode="json")
@pytest.mark.parametrize(
"mutation",
[
lambda value: value["observations"][0]["values"].update(CN=101),
lambda value: value["observations"][0]["values"].update(CN=-1),
lambda value: value["observations"][0]["values"].update(CN=float("nan")),
lambda value: value["observations"][0]["values"].update(CN=True),
lambda value: value["observations"][0]["values"].update(EUROPE=10),
lambda value: value["observations"][0].update(source_id="unknown"),
lambda value: value["observations"][0].update(year=2026),
lambda value: value["observations"].append(value["observations"][0]),
lambda value: value["sources"].append(value["sources"][0]),
lambda value: value["innovation"].append(value["innovation"][0]),
lambda value: value["sources"][0].update(url="javascript:alert(1)"),
lambda value: value["sources"][0].update(url="https://user:pass@example.com/report"),
],
)
def test_rejects_invalid_or_ambiguous_evidence(mutation):
payload = default_config()
mutation(payload)
with pytest.raises(ValidationError):
ComputeConfiguration.model_validate(payload)
def test_arbitrary_report_updates_values_sources_and_years_without_code_changes():
payload = default_config()
payload["sources"].append(
{
"id": "new-report",
"title": "New report",
"url": "https://example.com/report",
"publisher": "Research group",
"report_year": 2026,
"locator": "Figure 2",
}
)
payload["observations"].append(
{
"year": 2025,
"metric": "global_share",
"source_id": "new-report",
"values": {"CN": 25, "US": 50, "JP": 0},
}
)
config = ComputeConfiguration.model_validate(payload)
result = compute_history(["CN", "US", "JP", "EUROPE"], config=config)
row = next(row for row in result["compute"] if row["year"] == 2025)
assert row["values"] == {"CN": 25, "US": 50, "JP": 0, "EUROPE": None}
assert result["evidence"][row["evidence_id"]]["title"] == "New report"
assert DEFAULT_CONFIGURATION.model_dump(mode="json") == default_config()
def test_zero_bilateral_denominator_is_missing_and_empty_override_stays_empty():
payload = default_config()
payload["observations"][0]["values"] = {"CN": 0, "US": 0}
result = compute_history(
["CN", "US"], "bilateral_share", ComputeConfiguration.model_validate(payload)
)
assert result["compute"][0]["values"] == {"CN": None, "US": None}
payload.update(sources=[], observations=[], innovation=[])
result = compute_history(["CN"], config=ComputeConfiguration.model_validate(payload))
assert all(row["values"]["CN"] is None for row in result["compute"])
assert result["evidence"] == {}
def fake_db(record=None):
db = AsyncMock()
db.add = MagicMock()
result = MagicMock()
result.scalar_one_or_none.return_value = record
result.scalar_one.return_value = record
db.execute.return_value = result
return db
@pytest.mark.asyncio
async def test_fresh_install_uses_defaults_without_writing_database():
db = fake_db()
assert await read_configuration(db) == default_envelope()
db.add.assert_not_called()
db.commit.assert_not_awaited()
@pytest.mark.asyncio
async def test_series_reads_persisted_override_and_disables_http_cache():
envelope = default_envelope()
envelope["is_default"] = False
envelope["revision"] = "custom"
envelope["config"]["observations"][3]["values"]["CN"] = 30
response = Response()
data = await get_series(
response, "CN,US", "global_share", fake_db(SimpleNamespace(payload=envelope))
)
assert data["compute"][3]["values"]["CN"] == 30
assert data["version"] == "custom"
assert data["provenance"] == "admin_curated_reports"
assert response.headers["Cache-Control"] == "no-store"
@pytest.mark.asyncio
async def test_publish_and_audit_are_one_transaction_and_conflicts_do_not_overwrite():
record = SimpleNamespace(payload=default_envelope())
db = fake_db(record)
actor = SimpleNamespace(id=1, username="admin")
old = deepcopy(record.payload)
result = await publish_configuration(
db,
revision=old["revision"],
config=DEFAULT_CONFIGURATION,
actor=actor,
)
assert result["revision"] != old["revision"]
audit = db.add.call_args.args[0]
assert isinstance(audit, AuditLog)
assert audit.details == {"before": old, "after": result}
assert audit.actor_id == actor.id
db.commit.assert_awaited_once()
with pytest.raises(HTTPException) as failure:
await publish_configuration(
db, revision=old["revision"], config=DEFAULT_CONFIGURATION, actor=actor
)
assert failure.value.status_code == 409
assert record.payload == result
db.rollback.assert_awaited_once()
db.commit.assert_awaited_once()
@pytest.mark.asyncio
async def test_reset_changes_revision_and_restores_bundled_data():
record = SimpleNamespace(
payload={**default_envelope(), "revision": "custom", "is_default": False}
)
result = await publish_configuration(
fake_db(record),
revision="custom",
config=DEFAULT_CONFIGURATION,
actor=SimpleNamespace(id=1, username="admin"),
reset=True,
)
assert result["is_default"] is True
assert result["config"] == default_config()
assert result["revision"] != "custom"
@pytest.mark.asyncio
@pytest.mark.parametrize("role", ["user", "viewer", "operator"])
async def test_non_admin_cannot_configure(role):
with pytest.raises(HTTPException) as failure:
await configuration_admin(SimpleNamespace(role=role))
assert failure.value.status_code == 403
@pytest.mark.asyncio
async def test_http_configuration_auth_validation_and_public_series():
app = FastAPI()
app.include_router(router)
record = SimpleNamespace(payload=default_envelope())
db = fake_db(record)
app.dependency_overrides[get_db] = lambda: db
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
assert (await client.get("/configuration")).status_code == 401
assert (await client.get("/series")).status_code == 200
app.dependency_overrides[get_current_user] = lambda: SimpleNamespace(
id=1,
username="admin",
role="admin",
)
loaded = (await client.get("/configuration")).json()
invalid = deepcopy(loaded["config"])
invalid["observations"][0]["values"]["CN"] = 200
response = await client.put(
"/configuration",
json={
"revision": loaded["revision"],
"config": invalid,
},
)
assert response.status_code == 422
db.commit.assert_not_awaited()
response = await client.put(
"/configuration",
json={
"revision": loaded["revision"],
"config": loaded["config"],
},
)
assert response.status_code == 200
assert response.json()["revision"] != loaded["revision"]
assert (
await client.post(
"/configuration/reset",
json={
"revision": loaded["revision"],
},
)
).status_code == 409

View File

@@ -0,0 +1,268 @@
import csv
import io
from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock
import pytest
from fastapi import HTTPException
from sqlalchemy.dialects import postgresql
from app.api.v1.compute_intelligence import sample_query, selected_countries
from app.services.collectors.epoch_ai import EpochAIGPUCollector
from app.services.collectors.top500 import TOP500Collector
from app.services.compute_history import compute_history
def epoch_csv(**overrides):
row = {
"Name": "Cluster Alpha",
"Country": "United States of America",
"Status": "Existing",
"First Operational Date": "2023-05-02",
"16-bit OP/s (log)": "18",
"Location": "3231 Riverport Rd, Memphis, TN 38109",
"latitude": "35",
"longitude": "-90",
**overrides,
}
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=list(row))
writer.writeheader()
writer.writerow(row)
return output.getvalue()
def test_epoch_csv_preserves_precision_date_and_missing_values():
collector = EpochAIGPUCollector()
row = collector.parse_response(epoch_csv())[0]
assert row["value"] == 1e18
assert row["unit"] == "FLOP/s"
assert row["metadata"]["precision"] == "FP16"
assert row["metadata"]["location_precision"] == "precise"
assert row["metadata"]["raw_country"] == "United States of America"
assert row["reference_date"] == "2023-05-02"
missing = collector.parse_response(
epoch_csv(**{"16-bit OP/s (log)": "", "First Operational Date": ""})
)[0]
assert missing["value"] is None
assert missing["reference_date"] is None
@pytest.mark.parametrize(
("location", "country", "expected"),
[
("United States of America", "United States of America", "country"),
("Japan", "Japan", "country"),
("Korea (Republic of)", "South Korea", "country"),
("Texas", "United States of America", "region"),
("Iowa, USA", "United States of America", "region"),
("Seoul", "South Korea", "city"),
("Paris, France", "France", "city"),
("Skolkovo Innovation Center\nMoscow Oblast\nRussia", "Russia", "site"),
("Cluster 2, Texas", "United States of America", "site"),
("Campus 7, Tokyo", "Japan", "site"),
("Building 240, Argonne", "United States of America", "site"),
("7", "United States of America", "unknown"),
("Jülich Supercomputing Centre\n52428 Jülich, Germany", "Germany", "precise"),
("35.380924,139.625008", "Japan", "precise"),
('33°24\'28.7"N 112°21\'54.3"W', "United States of America", "precise"),
("latitude: 35.0 longitude: 181.0", "United States of America", "unknown"),
('99°99\'00"N 200°00\'00"W', "United States of America", "unknown"),
("3231 Riverport Rd, Memphis, TN 38109", "United States of America", "precise"),
],
)
def test_epoch_location_precision_follows_location_field(location, country, expected):
row = EpochAIGPUCollector().parse_response(
epoch_csv(Location=location, Country=country)
)[0]
assert row["metadata"]["location_precision"] == expected
@pytest.mark.parametrize(
("location", "expected"),
[
({"lat": 35.0, "lng": 139.0}, "precise"),
({"latitude": 35.0, "longitude": 181.0}, "unknown"),
({"lat": 35.0}, "unknown"),
({"lng": 139.0}, "unknown"),
({"lat": "invalid", "lng": 139.0}, "unknown"),
],
)
def test_epoch_coordinate_object_requires_complete_valid_pair(location, expected):
from app.services.epoch_location import assess_epoch_location
assert assess_epoch_location(location).precision == expected
def test_epoch_anonymous_rows_are_not_geocoded():
row = EpochAIGPUCollector().parse_response(epoch_csv(Name="Anonymized 1", Country="China"))[0]
assert row["latitude"] is None and row["longitude"] is None
assert row["metadata"]["anonymous"] and row["metadata"]["rounded"]
@pytest.mark.parametrize(
"payload",
["<html>blocked</html>", "Name,Country\nA,US", epoch_csv(**{"16-bit OP/s (log)": "nan"})],
)
def test_epoch_invalid_payload_fails_closed(payload):
with pytest.raises(ValueError):
EpochAIGPUCollector().parse_response(payload)
def test_epoch_duplicate_identity_rejected():
payload = epoch_csv()
with pytest.raises(ValueError, match="identity collision"):
EpochAIGPUCollector().parse_response(payload + payload.splitlines()[1] + "\n")
def test_anonymous_source_rows_preserve_multiplicity_without_claiming_identity():
payload = epoch_csv(Name="Anonymized Chinese System", Country="China")
rows = EpochAIGPUCollector().parse_response(payload + payload.splitlines()[1] + "\n")
assert len(rows) == 2
assert rows[0]["source_id"] != rows[1]["source_id"]
assert all(row["metadata"]["identity_scope"] == "snapshot_record" for row in rows)
def test_top500_units_and_stable_identity():
collector = TOP500Collector()
html = '<table id="top500"><tr><th>Rank</th><th>System</th><th>Cores</th><th>Rmax [TFlop/s]</th><th>Rpeak [PFlop/s]</th><th>Power</th></tr><tr><td>{rank}</td><td><a href="/system/123/">Test</a><br>Lab<br>USA</td><td>10</td><td>1,500</td><td>2</td><td>1</td></tr></table>'
first = collector.parse_response(
html.format(rank=1), "https://top500.org/lists/top500/2025/11/"
)[0]
second = collector.parse_response(
html.format(rank=2), "https://top500.org/lists/top500/2026/06/"
)[0]
assert first["source_id"] == second["source_id"] == "top500_system_123"
assert float(first["value"]) == 1.5e15
assert first["metadata"]["rpeak_flops"] == 2e15
assert first["reference_date"] == "2025-11-01"
assert second["reference_date"] == "2026-06-01"
with pytest.raises(ValueError, match="date missing"):
collector.parse_response(html.format(rank=1))
@pytest.mark.asyncio
async def test_large_source_loss_does_not_create_snapshot():
db = AsyncMock()
db.scalar.return_value = 500
db.add = MagicMock()
with pytest.raises(ValueError, match="40%"):
await TOP500Collector()._create_snapshot(db, 1, [{}] * 2, datetime.now(UTC))
db.add.assert_not_called()
db.commit.assert_not_awaited()
def test_history_missing_years_and_denominators():
global_data = compute_history(["CN", "US"])
assert global_data["compute"][3]["values"] == {"CN": 31, "US": 41}
assert global_data["compute"][-1]["values"] == {"CN": None, "US": None}
bilateral = compute_history(["CN", "US"], "bilateral_share")
assert sum(bilateral["compute"][3]["values"].values()) == 100
assert bilateral["denominator"] == "US+CN"
absolute = compute_history(["US", "CN"], "absolute")
assert absolute["compute"][3]["values"] == {"US": None, "CN": 435}
assert global_data["innovation"][-2]["value"] == 17.95
assert global_data["innovation"][-1]["value"] is None
assert global_data["evidence"]["innovation-2026"]["published_at"] == "2026-07-17"
@pytest.mark.asyncio
async def test_snapshot_query_uses_observation_cutoff_not_location():
query = await sample_query(AsyncMock(), ["CN"], 2023)
sql = str(query.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))
assert "2024-01-01" in sql
assert "row_number() OVER (PARTITION BY" in sql
assert "compute_contract" in sql and "中国" in sql
assert "latitude" not in sql and "longitude" not in sql
def test_country_input_is_bounded_and_deduplicated():
assert selected_countries("CN,CN,US") == ["CN", "US"]
assert selected_countries("CN,US,EUROPE,APAC,OTHER")[-1] == "OTHER"
with pytest.raises(HTTPException):
selected_countries("invalid")
with pytest.raises(HTTPException, match="overlapping"):
selected_countries("EUROPE,DE")
def test_catalog_supports_global_participants_without_inventing_history():
from app.services.compute_catalog import (
CATALOG,
COMPARISON_GROUPS,
COUNTRIES,
source_country_names,
source_country_participants,
)
assert {"CN", "US", "JP", "KR", "IN", "DE", "FR", "GB", "AU", "BR", "ZA"}.issubset(
{entry["code"] for entry in CATALOG}
)
assert not {"TW", "HK", "MO", "XK"}.intersection(entry["code"] for entry in CATALOG)
owners = source_country_names(["CN"])
assert {"中国", "中国(台湾)", "中国(香港)", "中国(澳门)"}.issubset(owners)
assert source_country_participants(["CN"])["中国(台湾)"] == "CN"
assert [entry["code"] for entry in CATALOG[:5]] == ["CN", "US", "EUROPE", "APAC", "OTHER"]
assert [entry["kind"] for entry in CATALOG[:5]] == [
"country",
"country",
"region",
"region",
"region",
]
group_members = [
member
for _code, _name, _name_en, members, _anchor in COMPARISON_GROUPS
for member in members
]
assert len(group_members) == len(set(group_members))
assert set(group_members) == set(COUNTRIES)
data = compute_history(["JP", "KR"])
assert [row["values"]["JP"] for row in data["compute"][:4]] == [6, 5, 4, 4]
assert all(row["values"]["KR"] is None for row in data["compute"])
assert all(row["value"] is None for row in data["innovation"])
assert len(data["catalog"]) > 30
def test_region_history_requires_complete_same_year_member_coverage():
data = compute_history(["CN", "US", "EUROPE", "APAC", "OTHER"])
row_2023 = next(row for row in data["compute"] if row["year"] == 2023)
assert row_2023["values"]["CN"] == 31
assert row_2023["values"]["US"] == 41
assert row_2023["values"]["EUROPE"] is None
assert row_2023["values"]["APAC"] is None
assert row_2023["values"]["OTHER"] is None
assert row_2023["coverage"]["EUROPE"]["status"] == "incomplete"
assert "DE" in row_2023["coverage"]["EUROPE"]["missing_members"]
assert data["default_participants"] == ["CN", "US", "EUROPE", "APAC", "OTHER"]
def test_anonymous_records_never_enter_the_location_repair_queue():
from app.api.v1.visualization import convert_compute_centers_to_geojson
from app.models.collected_data import CollectedData
record = CollectedData(
source="epoch_ai_gpu",
source_id="anonymous",
name="Anonymous",
extra_data={"compute_contract": "v1", "anonymous": True},
)
result = convert_compute_centers_to_geojson([record])
assert result["features"] == []
assert result["unresolved"] == []
assert result["withheld_count"] == 1
def test_absolute_report_year_mapping_and_bilateral_denominator_ignore_other_countries():
absolute = compute_history(["CN"], "absolute")
observed = {row["year"]: row for row in absolute["compute"]}
assert observed[2022]["values"]["CN"] == 302
assert observed[2022]["evidence_id"] == "caict-2023"
assert observed[2023]["evidence_id"] == "caict-2024"
bilateral = compute_history(["CN", "US", "JP"], "bilateral_share")
values = next(row["values"] for row in bilateral["compute"] if row["year"] == 2023)
assert values == {"CN": 43.06, "US": 56.94, "JP": None}
for row in absolute["compute"]:
if row["evidence_id"]:
report = absolute["evidence"][row["evidence_id"]]
assert report["url"].startswith("https://www.caict.ac.cn/")

View File

@@ -0,0 +1,18 @@
from app.services.compute_center_verified_sites import get_verified_compute_site
def test_registry_requires_exact_facility_identity_not_operator_name():
assert get_verified_compute_site("epoch_ai_gpu", "NVIDIA") is None
assert get_verified_compute_site("top500", "epoch_argonne_nl_sophia__formerly_theta_") is None
def test_official_campus_anchors_preserve_evidence_and_site_level_precision():
site = get_verified_compute_site("epoch_ai_gpu", "epoch_jamstec_zettascaler_2_2_gyoukou")
assert site["city"] == "Yokohama"
assert site["precision"] == "site"
assert site["coordinate_basis"] == "official_access_map_center"
assert site["sources"]
site["latitude"] = 0
assert get_verified_compute_site(
"epoch_ai_gpu", "epoch_jamstec_zettascaler_2_2_gyoukou"
)["latitude"] != 0

View File

@@ -1,4 +1,5 @@
import json
from pathlib import Path
import pytest
@@ -8,6 +9,10 @@ from app.services import earth_boundaries
def write_geojson(path, name="Test"):
names = [name]
if name == "earth_admin0_boundaries":
names = ["China", "India", "Japan", "Palestine", "Taiwan", "Hong Kong", "Macao", "Serbia", "Kosovo"]
codes = {"China": "CHN", "India": "IND", "Japan": "JPN", "Palestine": "PSE", "Taiwan": "TWN", "Hong Kong": "HKG", "Macao": "MAC", "Serbia": "SRB", "Kosovo": "KOS"}
path.write_text(
json.dumps(
{
@@ -15,12 +20,17 @@ def write_geojson(path, name="Test"):
"features": [
{
"type": "Feature",
"properties": {"name": name},
"properties": {
"NAME": f"source renamed {item}" if item in {"Taiwan", "Kosovo"} else item,
"ISO_A3": "-99" if item == "Kosovo" else codes.get(item, "TEST"),
"ADM0_A3": "XKS" if item == "Kosovo" else codes.get(item, "TEST"),
},
"geometry": {
"type": "Polygon",
"coordinates": [[[-1, -1], [1, -1], [1, 1], [-1, 1], [-1, -1]]],
},
}
for item in names
],
}
),
@@ -37,9 +47,14 @@ def patch_paths(monkeypatch, tmp_path):
config = repo / "config/earth-boundary-sources.local.json"
example = repo / "config/earth-boundary-sources.example.json"
policy = repo / "config/earth-boundary-pov-policy.china-v1.json"
geometry = repo / "resources/earth/boundary-pov-geometry.china-v1.json"
for path in (source_dir, boundary_dir, pmtiles.parent, legacy.parent, config.parent):
path.mkdir(parents=True, exist_ok=True)
policy.write_text('{"productionTileFormat":"pmtiles+mvt"}\n', encoding="utf-8")
source_policy = Path(__file__).resolve().parents[2] / "config/earth-boundary-pov-policy.china-v1.json"
policy.write_text(source_policy.read_text(encoding="utf-8"), encoding="utf-8")
geometry.parent.mkdir(parents=True, exist_ok=True)
source_geometry = Path(__file__).resolve().parents[2] / "resources/earth/boundary-pov-geometry.china-v1.json"
geometry.write_bytes(source_geometry.read_bytes())
example.write_text('{"collectorConfigs":{}}\n', encoding="utf-8")
monkeypatch.setattr(earth_boundaries, "REPO_ROOT", repo)
monkeypatch.setattr(earth_boundaries, "SOURCE_OUTPUT_DIR", source_dir)
@@ -49,10 +64,11 @@ def patch_paths(monkeypatch, tmp_path):
monkeypatch.setattr(earth_boundaries, "BOUNDARY_OUTPUT_DIR", boundary_dir)
monkeypatch.setattr(earth_boundaries, "BOUNDARY_MANIFEST_PATH", boundary_dir / "manifest.json")
monkeypatch.setattr(earth_boundaries, "PMTILES_ARTIFACT_PATH", pmtiles)
monkeypatch.setattr(earth_boundaries, "LEGACY_GEOJSON_PATH", legacy)
monkeypatch.setattr(earth_boundaries, "POLICY_FALLBACK_PATH", legacy)
monkeypatch.setattr(earth_boundaries, "LOCAL_CONFIG_PATH", config)
monkeypatch.setattr(earth_boundaries, "EXAMPLE_CONFIG_PATH", example)
monkeypatch.setattr(earth_boundaries, "POV_POLICY_PATH", policy)
monkeypatch.setattr(earth_boundaries, "POV_GEOMETRY_PATH", geometry)
return {
"repo": repo,
"config": config,
@@ -62,27 +78,73 @@ def patch_paths(monkeypatch, tmp_path):
}
def write_fallback(path, policy_path, geometry_path):
policy = earth_boundaries._read_json(policy_path)
geometry = earth_boundaries._read_json(geometry_path)
coverage = {
"profile": "china-pov-v1",
"sha256": earth_boundaries._stable_json_hash(policy),
"geometrySha256": earth_boundaries._stable_json_hash(geometry),
"reviewedGeometrySourceCommit": geometry["sourceCommit"],
"applied": True,
"appliedRuleIds": ["china-taiwan-penghu", "china-hong-kong", "china-macao", "kosovo"],
}
path.write_text(json.dumps({"type": "FeatureCollection", "features": [], "povPolicy": coverage}), encoding="utf-8")
def test_boundary_status_uses_legacy_provider_when_pmtiles_missing(monkeypatch, tmp_path):
paths = patch_paths(monkeypatch, tmp_path)
write_fallback(paths["legacy"], earth_boundaries.POV_POLICY_PATH, earth_boundaries.POV_GEOMETRY_PATH)
status = earth_boundaries.get_boundary_status()
assert status["provider"] == "policy-geojson-fallback"
assert status["fallback_available"] is True
assert status["high_precision_ready"] is False
def test_boundary_status_rejects_unverified_fallback(monkeypatch, tmp_path):
paths = patch_paths(monkeypatch, tmp_path)
write_geojson(paths["legacy"])
status = earth_boundaries.get_boundary_status()
assert status["provider"] == "legacy-geojson"
assert status["fallback_available"] is True
assert status["high_precision_ready"] is False
assert status["provider"] == "missing"
assert status["fallback_available"] is False
def test_boundary_status_prefers_high_precision_when_manifest_and_pmtiles_exist(monkeypatch, tmp_path):
paths = patch_paths(monkeypatch, tmp_path)
write_geojson(paths["legacy"])
write_fallback(paths["legacy"], earth_boundaries.POV_POLICY_PATH, earth_boundaries.POV_GEOMETRY_PATH)
paths["pmtiles"].write_bytes(b"pmtiles")
paths["manifest"].write_text('{"tileProvider":"pmtiles-mvt"}\n', encoding="utf-8")
(paths["manifest"].parent / "hover-index.geojson").write_text("{}", encoding="utf-8")
coverage = {
"profile": "china-pov-v1",
"sha256": earth_boundaries._stable_json_hash(earth_boundaries._read_json(earth_boundaries.POV_POLICY_PATH)),
"applied": True,
"appliedRuleIds": ["china-taiwan-penghu", "china-hong-kong", "china-macao", "kosovo"],
"reviewedGeometrySourceCommit": earth_boundaries._read_json(earth_boundaries.POV_GEOMETRY_PATH)["sourceCommit"],
"geometrySha256": earth_boundaries._stable_json_hash(earth_boundaries._read_json(earth_boundaries.POV_GEOMETRY_PATH)),
}
paths["manifest"].write_text(json.dumps({"tileProvider": "pmtiles-mvt", "hoverIndex": "hover-index.geojson", "povPolicy": coverage}), encoding="utf-8")
status = earth_boundaries.get_boundary_status()
assert status["provider"] == "pmtiles-mvt"
assert status["high_precision_ready"] is True
assert status["policy_publishable"] is False
def test_boundary_status_rejects_unverified_high_precision(monkeypatch, tmp_path):
paths = patch_paths(monkeypatch, tmp_path)
write_fallback(paths["legacy"], earth_boundaries.POV_POLICY_PATH, earth_boundaries.POV_GEOMETRY_PATH)
paths["pmtiles"].write_bytes(b"pmtiles")
paths["manifest"].write_text('{"tileProvider":"pmtiles-mvt","povPolicy":{"applied":false}}', encoding="utf-8")
status = earth_boundaries.get_boundary_status()
assert status["provider"] == "policy-geojson-fallback"
assert status["high_precision_ready"] is False
def test_save_boundary_config_writes_local_config(monkeypatch, tmp_path):
@@ -130,6 +192,22 @@ async def test_build_reports_missing_tools_after_source_artifacts(monkeypatch, t
assert status["high_precision_ready"] is True
assert (paths["repo"] / "data/earth-boundary-sources/manifest.json").exists()
assert paths["manifest"].exists()
manifest = json.loads(paths["manifest"].read_text(encoding="utf-8"))
assert manifest["povPolicy"]["applied"] is True
assert manifest["povPolicy"]["coverage"] == "partial"
assert manifest["povPolicy"]["publicationReady"] is False
assert "china-south-china-sea-islands" in manifest["povPolicy"]["unresolvedRuleIds"]
hover = json.loads((paths["manifest"].parent / manifest["hoverIndex"]).read_text(encoding="utf-8"))
by_rule = {
feature["properties"].get("PLANET_POLICY_RULE"): feature
for feature in hover["features"]
}
assert by_rule["china-hong-kong"]["properties"]["ISO_A3"] == "CHN"
assert by_rule["china-macao"]["properties"]["ISO_A3"] == "CHN"
assert all(
feature["properties"].get("ADM0_A3") not in {"TWN", "KOS", "XKS"}
for feature in hover["features"]
)
assert any(phase == "download" for _progress, phase, _message in progress_events)
@@ -175,3 +253,23 @@ def test_earth_boundary_collectors_are_not_registered_as_datasources():
assert removed.isdisjoint(DEFAULT_DATASOURCES)
for source in removed:
assert collector_registry.get(source) is None
def test_static_policy_fallback_contains_reviewed_jeju_polygon():
repo = Path(__file__).resolve().parents[2]
path = repo / "frontend/public/earth/data/countries-admin0.china-pov-v1.geojson"
payload = json.loads(path.read_text(encoding="utf-8"))
matches = [
feature for feature in payload["features"]
if feature["properties"].get("PLANET_GEOMETRY_SUPPLEMENT") == "jeju-island"
]
assert len(matches) == 1
jeju = matches[0]
assert jeju["properties"]["ISO_A3"] == "KOR"
assert jeju["properties"]["PLANET_SOURCE_NE_ID"] == 1159320985
assert payload["povPolicy"]["geometrySupplement"]["jejuComponent"] == 3
ring = jeju["geometry"]["coordinates"][0]
assert len(ring) == 61
assert min(point[0] for point in ring) < 126.53 < max(point[0] for point in ring)
assert min(point[1] for point in ring) < 33.36 < max(point[1] for point in ring)

View File

@@ -0,0 +1,41 @@
import json
from pathlib import Path
import pytest
from app.core.earth_geography_policy import display_country, display_iso_a3
def test_missing_deployment_policy_has_an_actionable_error(monkeypatch, tmp_path):
from app.core import earth_geography_policy as policy
policy._policy_country_by_code.cache_clear()
monkeypatch.setattr(policy, "_POLICY_PATH", tmp_path / "missing-policy.json")
try:
with pytest.raises(FileNotFoundError, match="EARTH_DISPLAY_POLICY_MISSING"):
policy.display_country("China")
finally:
policy._policy_country_by_code.cache_clear()
def test_display_country_groups_china_regions_and_kosovo() -> None:
for source in ("Taiwan", "TWN", "Hong Kong", "HKG", "Macao", "MAC", "中国(台湾)"):
assert display_country(source) == "中国"
assert display_iso_a3(source) == "CHN"
for source in ("Kosovo", "XK", "SRB"):
assert display_country(source) == "塞尔维亚"
assert display_iso_a3(source) == "SRB"
def test_display_country_keeps_other_country_and_rejects_unknown() -> None:
assert display_country("Japan") == "日本"
assert display_iso_a3("Japan") == "JPN"
assert display_country("unexpected non-country") is None
def test_country_projection_agrees_with_versioned_boundary_rules() -> None:
path = Path(__file__).resolve().parents[2] / "config/earth-boundary-pov-policy.china-v1.json"
policy = json.loads(path.read_text(encoding="utf-8"))
for rule in policy["rules"]:
for code in rule.get("sourceIsoA3", []):
assert display_iso_a3(code) == rule.get("targetIsoA3", rule.get("parentIsoA3"))

View File

@@ -0,0 +1,37 @@
from app.services.tv_streams import (
CCTV4_HOMEPAGE,
CCTV4_STREAM,
UNSUPPORTED_CCTV4_STREAM,
normalize_tv_settings,
normalize_tv_source,
)
def test_default_keeps_aljazeera_and_does_not_offer_cdrm_as_plain_hls():
settings = normalize_tv_settings(None)
assert settings["default_source_id"] == "aljazeera-mubasher"
cctv = next(source for source in settings["sources"] if source["id"] == "cctv4")
assert cctv["source_type"] == "hls"
assert cctv["stream_url"] == CCTV4_STREAM
assert "美洲版" in cctv["name"]
assert cctv["use_proxy"] is False
assert cctv["homepage_url"] == CCTV4_HOMEPAGE
def test_saved_legacy_default_is_corrected_without_mutating_input():
saved = {"id": "cctv4", "source_type": "hls", "stream_url": UNSUPPORTED_CCTV4_STREAM}
source = normalize_tv_source(saved)
assert source["source_type"] == "hls"
assert source["stream_url"] == CCTV4_STREAM
assert source["use_proxy"] is False
assert saved["stream_url"] == UNSUPPORTED_CCTV4_STREAM
def test_custom_cctv_stream_and_fallback_preference_are_preserved():
custom = {"id": "cctv4", "source_type": "hls", "stream_url": "https://example.com/licensed.m3u8"}
settings = normalize_tv_settings({"auto_fallback": False, "sources": [custom]})
source = next(source for source in settings["sources"] if source["id"] == "cctv4")
assert source["stream_url"] == custom["stream_url"]
assert source["source_type"] == "hls"
assert source["use_proxy"] is True
assert settings["auto_fallback"] is False

View File

@@ -76,6 +76,7 @@ def test_convert_compute_centers_to_geojson_unifies_sources():
longitude=-90.05,
metadata={
"organization": "xAI",
"location": "3231 Riverport Rd, Memphis, TN 38109",
"gpu_type": "H100",
"gpu_count": 100000,
"value": "20000",
@@ -168,6 +169,241 @@ def test_compute_center_source_coordinates_win_over_stored_location():
compute_center_locations.set_compute_center_location_cache({})
def test_epoch_country_level_coordinates_are_unresolved():
record = _build_record(
record_id=33,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Meta 100k",
country="United States",
city="",
latitude=39.381266,
longitude=-97.922211,
metadata={
"location": "United States of America",
"raw_country": "United States of America",
},
)
payload = convert_compute_centers_to_geojson([record])
assert payload["features"] == []
assert len(payload["unresolved"]) == 1
assert payload["unresolved"][0]["source_id"] == "epoch_ai_gpu-33"
assert "source_coordinates:country" in payload["unresolved"][0]["attempted_queries"]
def test_epoch_city_coordinates_are_not_labeled_precise():
record = _build_record(
record_id=34,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Seoul Cluster",
country="South Korea",
city="",
latitude=37.5665,
longitude=126.978,
metadata={"location": "Seoul", "raw_country": "South Korea"},
)
result = compute_center_locations.resolve_compute_center_location_full(
record, record.extra_data
)
assert result.is_resolved
assert result.location.location_precision == "city"
assert result.location.needs_confirmation is True
assert result.location.is_estimated is True
assert result.location.city is None
assert result.location.matched_location_name == "Seoul"
assert "Epoch Location: Seoul" in result.location.location_source_note
def test_epoch_city_geojson_preserves_source_location_without_guessing_city():
record = _build_record(
record_id=37,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Petrobras Fênix",
country="Brazil",
city="",
latitude=-22.909534,
longitude=-43.209934,
metadata={
"location": "Rio de Janeiro, Brazil",
"raw_country": "Brazil",
},
)
payload = convert_compute_centers_to_geojson([record])
feature = payload["features"][0]
assert feature["properties"]["location_precision"] == "city"
assert feature["properties"]["matched_location_name"] == "Rio de Janeiro, Brazil"
assert "Epoch Location: Rio de Janeiro, Brazil" in feature["properties"][
"location_source_note"
]
def test_confirmed_stored_location_beats_epoch_precise_coordinates():
compute_center_locations.set_compute_center_location_cache({
"epoch_ai_gpu:epoch_ai_gpu-35": {
"source": "epoch_ai_gpu",
"source_id": "epoch_ai_gpu-35",
"name": "Confirmed Site",
"latitude": 35.0,
"longitude": 139.0,
"precision": "site",
"confidence": 1.0,
"location_source": "official_facility_registry",
"needs_confirmation": False,
}
})
try:
record = _build_record(
record_id=35,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Japan Cluster",
country="Japan",
city="Tokyo",
latitude=35.68882,
longitude=139.692526,
metadata={
"location": "1-1 Verified Street, Tokyo 100-0001, Japan",
"raw_country": "Japan",
},
)
result = compute_center_locations.resolve_compute_center_location_full(
record, record.extra_data
)
assert result.location.location_source == "stored_compute_center_location"
assert result.location.location_precision == "site"
assert (result.location.latitude, result.location.longitude) == (35.0, 139.0)
finally:
compute_center_locations.set_compute_center_location_cache({})
def test_legacy_seeded_country_coordinate_is_ignored_on_read():
compute_center_locations.set_compute_center_location_cache({
"epoch_ai_gpu:epoch_ai_gpu-36": {
"source": "epoch_ai_gpu",
"source_id": "epoch_ai_gpu-36",
"name": "Legacy Country Point",
"latitude": 36.386493,
"longitude": 138.59223,
"precision": "precise",
"confidence": 1.0,
"location_source": "source_coordinates",
"needs_confirmation": False,
}
})
try:
record = _build_record(
record_id=36,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Legacy Japan Cluster",
country="Japan",
city="",
latitude=36.386493,
longitude=138.59223,
metadata={"location": "Japan", "raw_country": "Japan"},
)
result = compute_center_locations.resolve_compute_center_location_full(
record, record.extra_data
)
assert result.location is None
finally:
compute_center_locations.set_compute_center_location_cache({})
def test_verified_sites_replace_epoch_country_center_coordinates():
specifications = [
(
"epoch_jamstec_zettascaler_2_0_gyoukou",
"Gyoukou 2.0",
"Japan",
36.386493,
138.59223,
[139.625008, 35.380924],
),
(
"epoch_jamstec_zettascaler_2_2_gyoukou",
"Gyoukou 2.2",
"Japan",
36.386493,
138.59223,
[139.625008, 35.380924],
),
(
"epoch_argonne_nl_sophia__formerly_theta_",
"Sophia",
"United States of America",
39.381266,
-97.922211,
[-87.984498648496, 41.718109383287235],
),
]
records = []
for index, (source_id, name, country, latitude, longitude, _expected) in enumerate(
specifications, start=40
):
record = _build_record(
record_id=index,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name=name,
country=country,
city="",
latitude=latitude,
longitude=longitude,
metadata={"location": country, "raw_country": country},
)
record.source_id = source_id
records.append(record)
payload = convert_compute_centers_to_geojson(records)
assert payload["unresolved"] == []
assert len(payload["features"]) == 3
for feature, specification in zip(payload["features"], specifications):
assert feature["geometry"]["coordinates"] == specification[-1]
assert feature["properties"]["location_precision"] == "site"
assert feature["properties"]["location_source"] == "official_facility_registry"
assert "https://" in feature["properties"]["location_source_note"]
def test_verified_site_beats_epoch_city_level_source_coordinates():
record = _build_record(
record_id=44,
source="epoch_ai_gpu",
data_type="gpu_cluster",
name="Gyoukou 2.2",
country="Japan",
city="Tokyo",
latitude=35.68882,
longitude=139.692526,
metadata={"location": "Tokyo, Japan", "raw_country": "Japan"},
)
record.source_id = "epoch_jamstec_zettascaler_2_2_gyoukou"
result = compute_center_locations.resolve_compute_center_location_full(
record, record.extra_data
)
assert result.location.location_source == "official_facility_registry"
assert result.location.location_precision == "site"
assert (result.location.latitude, result.location.longitude) == (
35.380924,
139.625008,
)
def test_compute_center_geojson_uses_stored_location_when_source_coords_missing():
compute_center_locations.set_compute_center_location_cache({
"epoch_ai_gpu:epoch_ai_gpu-32": {
@@ -637,7 +873,11 @@ async def test_compute_centers_geojson_endpoint_returns_stats():
city="Memphis",
latitude=35.15,
longitude=-90.05,
metadata={"value": "20000", "unit": "TFlop/s"},
metadata={
"location": "3231 Riverport Rd, Memphis, TN 38109",
"value": "20000",
"unit": "TFlop/s",
},
),
]
@@ -741,7 +981,11 @@ async def test_visualization_geo_summary_returns_counts(monkeypatch):
city="Memphis",
latitude=35.15,
longitude=-90.05,
metadata={"value": "20000", "unit": "TFlop/s"},
metadata={
"location": "3231 Riverport Rd, Memphis, TN 38109",
"value": "20000",
"unit": "TFlop/s",
},
),
]

View File

@@ -25,6 +25,25 @@
{
"id": "china-taiwan-penghu",
"name": "Taiwan and Penghu",
"sourceIsoA3": ["TWN"],
"action": "union_to_country",
"targetIsoA3": "CHN",
"hoverIsoA3": "CHN",
"labelPolicy": "show_country_only"
},
{
"id": "china-hong-kong",
"name": "Hong Kong",
"sourceIsoA3": ["HKG"],
"action": "union_to_country",
"targetIsoA3": "CHN",
"hoverIsoA3": "CHN",
"labelPolicy": "show_country_only"
},
{
"id": "china-macao",
"name": "Macao",
"sourceIsoA3": ["MAC"],
"action": "union_to_country",
"targetIsoA3": "CHN",
"hoverIsoA3": "CHN",
@@ -57,6 +76,7 @@
{
"id": "kosovo",
"name": "Kosovo",
"sourceIsoA3": ["KOS", "XKS"],
"action": "render_as_disputed_with_parent",
"parentIsoA3": "SRB",
"hoverIsoA3": "SRB",

View File

@@ -8,6 +8,53 @@ This project follows the repository versioning rule:
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
- `bugfix` -> `+0.0.1`
## [0.75.2] — 2026-09-25
Released: 2026-09-25
### Highlights
- 修复 Windows 启动器固定使用旧版 PowerShell 的问题,优先在 Windows Terminal 新窗口中运行 PowerShell 7。
### Fixed
- 仓库与桌面 CMD 引导优先使用 `pwsh.exe`,缺少新版 PowerShell 或 Terminal 时保留可用环境回退。
- 已以管理员运行的入口同样进入首选终端,子窗口校验权限后直接衔接 WSL,避免重复提权和递归开窗。
- 补齐终端选择、含空格路径、提权分支回归和中英文说明,保持 WSL 重启参数及默认端口不变。
---
## [0.75.1] — 2026-09-25
Released: 2026-09-25
### Highlights
- Windows 启动器从当前项目位置识别 WSL 发行版与目录,首次生成本机桌面 CMD,后续沿用标准重启流程。
- 算力对比数据可由后台维护;小球恢复惯性拖动,大幅放大后保留球面跟手,修复移出球面后的反向旋转。
### Added / Fixed / Improved
- 桌面入口兼容两种 WSL UNC 路径和 Windows 本地磁盘映射,保留原有文件,支持只安装检查;默认端口仍为 3000 / 8000 / 8010。
- 算力报告来源、年度观测与创新分差使用数据库配置,提供数值校验、版本冲突保护、事务审计、恢复默认和地球自动刷新。
- 根据相机视锥在按下时固定拖动模式;小球使用旧版位移平滑与惯性,大球不再用球外最近点生成反向旋转。
- 更新开发 agent 的 Sol / Luna / Astra 分工规则,补齐中英文运维说明、启动故障编号与隔离回归测试。
---
## [0.75.0] — 2026-09-23
Released: 2026-09-23
### Highlights
- 新增算力与资源区域比较、可拖动的地理卡片和移动端原位展开;以算力占比为主,保留趋势、覆盖与来源信息。
- 四张 8K 底图可切换,原始底图设为默认并列于首位;优化高清首帧加载,修复最大缩放穿透和高倍拖动不跟手。
### Added / Fixed / Improved
- 统一物件卡片的先绘线后显示时序与靠边布局;地球明显转向后成组调整区域卡片,减少交叉并保留阅读时间。
- 合并算力区域目录及历史口径,校验设施坐标粒度与公开站点,避免以样本数冒充算力占比或将近似坐标规则排列。
- 地表贴图异步解码与按需替换,启动模块打包、数据并行加载;复用共享下拉菜单、HUD 样式与移动端交互。
- 国界贴合地表并贯穿固定地理显示规则;修复直播恢复、已选频道回退、本地账户身份响应与相关交互回归。
- 补齐后端镜像所需规则、审核几何和构建脚本,记录高精国界重建要求,更新中英文使用及运维文档。
---
## [0.74.7] — 2026-09-17
Released: 2026-09-17

View File

@@ -79,6 +79,16 @@ git diff --unified=0 HEAD -- <path>
## Harness Commands
Windows desktop launcher tests run separately on Windows with
`powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\harness\test_windows_launcher.ps1`.
They use temporary desktop folders and mocked restart commands; no UAC prompt
or real service restart is triggered. The WSL bridge regression suite,
`scripts/harness/test_windows_restart.py`, is included in quick-check and uses
disposable command substitutes. For a real entry-point check without restarting,
run the repository `planet.cmd --install-only`, then the generated desktop
launcher with the same flag. Installation and mocked checks do not prove a live
elevated restart; report that separately when it has not been exercised.
| Tier | Command | What It Does |
| --- | --- | --- |
| Doctor | `scripts/harness/doctor.sh` | Checks required files, required tools, optional delivery tools, and forbidden frontend lockfiles. |

View File

@@ -22,6 +22,8 @@
当前重点入口:
- [开发 Agent 模型分配与 Token 控制计划](development-agent-model-allocation-plan.md)
- [Earth 地理资源、空间浮动卡片与性能实施计划](earth-spatial-presentation-and-geographic-assets-plan.md)
- [算力与资源态势:展示、采集与 AI 迭代实施计划](compute-resource-intelligence-plan.md)
- [控制台 i18n 接入计划](/home/ray/dev/linkong/planet/docs/plans/admin-console-i18n-plan.md)
- [Earth Mobile Drawer UI Plan](/home/ray/dev/linkong/planet/docs/plans/earth-mobile-drawer-ui-plan.md)

View File

@@ -1,9 +1,11 @@
# 算力与资源态势:展示、采集与 AI 迭代实施计划
状态:待实施。2026-09-14 已完成当前代码边界、主要公开数据入口和历史指标口径核查;本计划不代表采集器、评分服务或 AI 迭代已经上线。面向产品、开发、数据研究与运维人员,作为此专题后续实现的设计依据。
状态:首版空间卡片原型实现中(2026-09-21)。已接入算力专题入口、45 个参与方目录、年份/口径选择、区域数据卡与来源、成功快照设施查询,以及 Epoch/TOP500 的首批质量修复。旧固定双图布局已移除。历史年度数据使用受控版本导入;各国完整指标覆盖、卡片图表、全榜获取、跨源设施合并、持久化指标管理、变化归因与 A4 AI 解读尚未完成,不视为全部 A1—A3 已验收。后续章节保留完整设计目标,当前实现以技术文档为准。2026-09-14 完成的口径核查仍是内容依据。面向产品、开发、数据研究与运维人员,作为此专题后续实现的设计依据。
## 0. 当前执行方向与范围
2026-09-21 展示方向修订:UI、地球材质、政治展示规则、地理资源对齐及性能门槛以[Earth 地理资源与空间卡片计划](earth-spatial-presentation-and-geographic-assets-plan.md)为准。下文固定右侧国家面板、底部横贯双图等布局不再作为实施目标。采用从区域锚点引出连接线、卡片围绕地球自适应浮动的方向,先验证空间交互再完善卡片内容。数据来源、口径与证据要求继续有效。
平台长期定位是“信息展示 + 决策辅助”。当前实施重点是把全球算力对比作为第一个成熟专题做清楚:地图显示资源在哪里,图表说明规模和相对位置怎样变化,证据说明数字从哪里来,AI 帮助阅读变化。
采用“专题先交付,公共能力按复用需要沉淀”的顺序。第 7—9 节的完整 AI 迭代、模型实验与通用持久化是后续路线,不是首版展示的前置条件;第 6 节总分保持实验性,不能为了首屏有大数字而生成不完整排名。
@@ -99,6 +101,14 @@
原始主张、报告估计、已验证记录和未来情景用文字标签区分。年度点之间的视觉连线只辅助阅读,不进入计算;不能把连线插值写成月度实测。正式指标没有新观测时保留参考期并提示新鲜度,不让刷新网页造成“国家实力实时跳动”。
### 3.3a 当前数据合同(2026-09-22)
默认主指标是全球计算设备总规模,拆分基础计算、智能计算和超级计算,观察期为 2020—2023。CAICT 报告核查目前使用官方 PDF 索引文本;本地 PDF 下载未成功,因此不宣称已肉眼核验 PDF 页面。绝对量合同记录 2022 年 302(证据版本 `caict2023`)和 2023 年 435(证据版本 `caict2024`);2024 年报告正式书名为《先进计算暨算力发展指数蓝皮书(2024年)》。官方 URL 已替换为四个镜像来源。
日本全球份额观测为 2020/2021/2022/2023 = 6%/5%/4%/4%。产品分组 `APAC` 与 `OTHER` 不能直接套用报告中的 Japan 或“其他”;`EUROPE` 成员集合尚未与报告口径对齐,仍返回缺失。双边份额分母固定为 US + China,新加入日本不改变分母。主要指标缺失时可显示最近有报告的年份(当前可到 2023),而不是留空为 2026;用户明确选择 2026 时仍显示缺失,不回填最近值。不可比性尚未确认,不展示未经确认的同比变化;覆盖数据保留在辅助区并带 count。
数据 API 审计字段为 `reviewed_at=2026-09-22`,版本与该日期一致。独立 Epoch 份额模式尚未实现,用户对该问法的选择待后续确认;当前合同不提前新增该模式或声称完成 dataset 计算。
默认“历史回顾”允许查看最新证据重述后的过去;“当时可知”只使用在截止时点已经发表且可证明可获得的资料。每张图同时展示参考期与知识截止时间。已选 2025 年而指标最新只到 2023 年时,必须显示“最新观测:2023”,或在严格同年模式显示缺失。
### 3.4 地球编码与交互

View File

@@ -0,0 +1,154 @@
# Planet 开发 Agent 模型分配与 Token 控制计划
日期:2026-09-21。状态:已用于本轮地理与空间卡片开发;未改变全局或项目默认模型配置。适用于已授权的开发任务,不代替用户对任务范围的决定。
目标:在固定政治展示规则、现有 UI 逻辑和性能门槛下,降低完成一个合格任务的总成本与返工次数。不是让所有工作都使用低价模型,也不是让最强模型从头包办。
当前重点任务见 [Earth 地理资源与空间卡片计划](earth-spatial-presentation-and-geographic-assets-plan.md)。该计划的功能与验收要求优先;本文件只决定如何组织开发。
## 1. 模型角色
模型定位参考 OpenAI Docs;下表的具体分工是针对 Planet 的工程建议,需要结合实际一次通过率和返工记录调整,不是模型能力保证。
| 模型 | 精确模型 ID | 默认推理强度 | 分配的工作 | 不作为默认用途 |
| --- | --- | --- | --- | --- |
| Astra | `gpt-6-astra` | `high` | 跨系统难题、空间展示方案、复杂坐标/深度/性能诊断、关键设计与独立审查 | 日常文件搜索、机械编辑、反复运行已确定命令 |
| Sol | `gpt-5.6-sol` | `medium`;复杂实现用 `high` | 执行主控、核心代码、共享组件整合、Three.js 状态与生命周期、最终集成 | 逐条整理大批重复记录、重复复述整个项目背景 |
| Terra | `gpt-5.6-terra` | `medium` | 接口已明确的常规实现、局部 UI 修复、数据处理脚本、测试编写、浏览器回归 | 独自决定空间交互方向、坐标模型或跨模块架构 |
| Luna | `gpt-5.6-luna` | `low`;明确的小修改可用 `medium` | 定向检索、清单比对、日志归类、说明文档初稿、固定格式整理 | 核心渲染、数据迁移、几何归属修改、最终审美或性能放行 |
日常推荐组合是 Sol 主控加一个 Terra worker;Luna 和 Astra 按需进入,不要求每个阶段凑齐四种模型。已经完成的资源准备不因采用这份方案重新做一遍。
当前会话的主模型不会因为写入计划自动变成 Sol。真正执行时,主会话模型由实际任务设置决定,子 Agent 在派发时明确指定模型和推理强度,并核对返回的运行配置;不能把角色名称当作已经切换模型的证据。
## 2. 本项目任务分配表
| 工作包 | 主执行 | 独立验证/审查 | 交付和升级条件 |
| --- | --- | --- | --- |
| 现有窗口、连线、巡航、滚动条入口盘点 | Terra | Sol 检查引用与遗漏 | 文件/符号清单、现有行为证据;Luna 可辅助定向搜索,无需读取全仓库 |
| 贴图归档、尺寸、哈希、配准元数据检查 | 直接运行脚本;脚本需新增时 Terra | Sol 抽查实际输出 | S0 已完成,后续只做有变化的核对,不委托模型重复计算哈希 |
| 固定政治规则的技术合同与几何覆盖边界 | Sol | Astra 审查规则是否贯穿各数据路径 | 权属口径已由项目确定,任何模型均不得重新决定;标识不明确先核对,不能猜测几何 |
| 贴图、海岸线、拾取错位定位 | Sol `high` | Astra 在根因不清或涉及多个坐标层时介入 | 用明确经纬点复现,区分错误数据、回退、投影、UV 与半像素偏移 |
| 规则已明确的映射、来源清单、局部转换脚本 | Terra | Sol;关键几何结果由 Astra 抽查 | 只落实批准合同;发现新的几何语义不得自行扩大规则 |
| 多区域空间卡片的第一版运动设计 | Astra `high` | Sol 检查能否复用当前实现 | 明确锚点、投影、避让、换侧、背面及阅读稳定性;产出小合同,不产出整套 UI 大改 |
| 2 张卡片的连接、弹性移动、生命周期原型 | Sol `high` | Terra 实测;Astra 看关键动效证据 | 优先实现最小完整循环;使用参考图和实际运动证据,不用文字自评代替 |
| 扩至 4/6 卡、增删区域和事件整合 | Sol | Terra 跑交互/竞态测试 | 复用原型合同;`main.js` 等共享入口只由指定 owner 写入 |
| 共享窗口行为、标准字体、移动抽屉及深色滚动条 | Terra | Sol 看渲染与旧面板回归 | 修改公共滚动所有者,验证图层等所有相关页;涉及共享状态重构时升级给 Sol |
| 地球柔光、材质层次、节点与背景关系 | Sol | Astra 对照 UE/PPT/海报审查 | 第一轮只改少量确定参数并给实际截图;性能退化交 Sol 诊断,难题交 Astra |
| 卡片内指标、普通图表、加载/缺失/错误状态 | Terra | Sol | 数据合同先冻结;不复活固定全屏大面板,不编造缺失年份 |
| 国家/区域接口和前后端联动 | Terra;跨模块变更由 Sol | Sol | 不硬编码中美为唯一对象;范围筛选、比较对象和组合去重必须一致 |
| 性能基准工具和回归测试 | Terra | Sol 检查方法;Astra 审查疑难退化 | 固定设备、数据与相机操作;日志含帧时间和内存证据,不能只报平均 FPS |
| 单位、日期、缓存、旧请求覆盖等复杂数据故障 | Sol | Astra 仅处理跨系统未解问题 | 先给最小复现和失败测试,避免多个模型同时试不同修复 |
| 中英文手册、资源清单、计划状态同步 | Luna 初稿;技术性较强时 Terra | Sol 对照实际 diff | 已实现与待实施分开;Luna 不能自行宣告功能完成 |
| 最终集成与验收 | Sol | Astra 对关键风险做一次集中审查 | 相关测试、渲染、政治规则及性能证据齐全;普通修订不逐项再次走 Astra |
## 3. 顺序与并行安排
### 第一轮:证据与边界
Sol 读取现有计划、当前工作区差异和必要规则,整理短任务清单。Terra 盘点实际运行的数据提供者、组件和性能基线;独立且批量的文档/引用整理才交 Luna。先读已有检查结果,避免重新下载贴图、重新研究 World Monitor 或重复跑已经通过的检查。
Sol 将确定的组件接口、规则约束、实际截图与未解问题整理成一个短包,Astra 只审查高返工风险的空间方案和坐标问题。Astra 返回决定、理由和验证点,不再从零扫描整个仓库。
### 第二轮:最小空间原型与独立 UI 修复
Sol 实现 2 张卡片的锚点、连线和有阻尼移动;Terra 可以同时处理公共滚动条或写回归测试,前提是文件所有权不冲突。先完成最小可演示运动,再扩展内容和卡片数量。若两者都要改 `controls.js`、`hud.css` 等共享文件,则按顺序交接,不并行写入。
Astra 对原型的实际截图/录屏做一次聚焦审查,检查是否符合“从地球四周延伸”的方向。空间原型尚未确认时,不展开完整指标卡片和全量国家 UI,以免重演方向错误后大规模返工。
### 第三轮:内容、全球扩展与性能
合同稳定后,Terra 接入指标与状态,Sol 负责与渲染/生命周期的整合。性能测试由脚本执行,Terra 整理差异;可复现的退化先交 Sol。只有不能局部解释的 GPU/CPU、坐标、异步问题才交 Astra。
### 第四轮:一次集成验收
Sol 整合并运行所需检查;Luna 根据最终 diff 更新文档初稿。Astra 集中审查关键几何、政治规则完整性、卡片运动与性能结论。已通过范围不反复重测,除非新修改影响该范围。
## 4. 并发上限与文件所有权
当前环境总并发槽位为 4,含主 Agent;执行时仍以实际可用额度为准。默认使用主 Agent 加 1 个 worker。只有存在两项以上真正独立的工作,且主 Agent 同时有有用工作可做时,才扩大到主 Agent 加 2 个 worker;第 4 个槽位只给临时审查或独立批处理。
每项任务指定可写文件范围。`main.js`、`controls.js`、共享 HUD/InfoCard 与跨层状态接口同一时间只有一个写入 owner。其他 Agent 只读检查或提交补丁建议,由 owner 集成。不得并行启动多个浏览器重测相同场景,也不得共享有冲突的登录/本地存储测试状态。
浏览器验证优先使用当前可用的 Browser 控制能力,包括统一 CUA 入口。不能仅因技能目录中缺少单独的 Browser 条目就判定工具不存在;应继续检查已暴露工具并多次重试连接。只有多次确认 Browser 不可用后,才考虑独立 Playwright,并说明具体阻塞。
Agent 完成有边界的任务后停止。只有同一任务需要补充验证时才续用;不能让 worker 在空闲时自动寻找新功能或自行扩大范围。创建用户侧新任务不作为内部拆工方式。
## 5. 节省 Token 的具体做法
1. 派发窄任务,不默认继承整个历史对话。使用简短任务卡加源文件路径;必要历史按最小范围提供。短上下文不能省略政治规则、性能要求和相关视觉证据。
2. 主控保存一份简洁任务账本:当前合同、文件 owner、已完成证据、失败原因、下一步。各 Agent 引用同一事实,不各写一份长计划。
3. 先 `rg` 定位,再读函数和局部 diff;不重复读取巨大 JSON、整仓日志或全部历史文档。大数据留在文件里,由脚本给出数量、哈希、差异和异常样例。
4. 图片、截图和录屏用实际文件查看。不能为了省 token 把视觉任务变成只读文件名或文字概括。
5. 相同来源只研究一次,保存来源、日期和关键结论;只有数据变化、结论不足或任务扩大时重查。
6. 子任务首次任务卡建议控制在约 500–1500 token,结果摘要约 300–800 token;这是上下文组织目标,不是截断必要证据的硬上限。
7. 返回“改了什么、哪些文件、验证证据、剩余阻塞”即可。完整日志和截图放到文件,用路径引用;失败时返回相关片段,不整段倒入主控上下文。
8. 哈希、解压、下载、构建、测试和统计交给确定性工具。不要为了运行一条已知命令另开一个 Agent。
9. 不重复安排多个模型完整实现同一功能。关键风险做一次独立审查,普通修改由测试与 owner 检查。
10. 默认不开 `max` / `ultra` 推理。提高模型或推理强度只针对具体难题,并记录升级理由。
## 6. 升级与停止条件
| 情况 | 处理 |
| --- | --- |
| Luna 遇到不明确需求、需要理解跨文件状态或开始猜测行为 | 交 Terra;若涉及核心状态/几何,直接交 Sol |
| Terra 的任务超出明确合同,或同一实质失败在一次有针对性的修复后仍未解决 | 给 Sol 最小复现、尝试记录和失败证据,不继续盲试 |
| Sol 遇到跨坐标层、性能来源不明或与现有交互语义冲突的问题 | 给 Astra 聚焦诊断任务;可以由 Astra 完成必要关键补丁,不限于口头审查 |
| 已知一开始就是复杂的空间布局或高风险几何设计 | 直接用 Sol/Astra,不为了经过低模型梯级而增加一次无效尝试 |
| 网络、凭证、工具缺失、原始资料不存在 | 不通过升模型解决环境问题;检查环境或记录缺口,继续独立工作 |
| 失败来自缺失用户决定或无法识别的地名/视觉目标 | 提出具体问题,不让多个模型猜不同版本 |
默认允许 worker 一次有证据的修复;是否继续由主控根据新信息决定。超时或 token 消耗增加但没有新增证据时停止扩展,整理已知结果后重新分配,不能无限自递归。
## 7. 审查门槛
Astra 重点进入三个位置:最初高风险空间/几何合同、首次运动原型、最终关键风险验收;中间疑难问题按需增加。它不需要审阅每一处 CSS、每一段翻译或每个测试日志。
Sol 对各 worker 的交付负责。不能以“另一模型说通过”代替运行结果。任何模型都无权改变项目政治口径、降低性能门槛、把未知数据填成确定值,或自行部署和发版。
涉及政治几何和性能结论,使用与实现过程分离的检查证据;普通文档格式或固定映射使用针对性检查即可。视觉方向仍由已确认的用户要求和参考图决定,Astra 的审美判断不替代用户目标。
## 8. 单任务派发模板
```text
任务:一个可独立验收的目标
模型与推理:明确 model ID + reasoning effort
必要约束:相关 rules 模块、政治口径、性能、共享组件、视觉参考
输入:最少文件/符号、当前合同、已有验证结果和图片路径
可写范围:明确文件;共享文件由指定 owner 修改
验收:必须出现的行为、必要测试/渲染/性能证据
停止/升级:哪些条件不能继续猜测或扩展
返回:结果摘要、改动文件、检查结果、证据路径、遗留问题
```
派发有意使用较小上下文时,必须把不可省略的约束写进任务卡。执行任务的 Agent 自行按规则加载相应模块,不因主控已读过就假定子 Agent 也知道。
## 9. 如何验证分配确实更高效
每个工作包记录模型、推理强度、输入/输出 token(环境可提供时)、耗时、工具耗时、修复轮数、一次验收是否通过和最终证据。若读不到准确 token,就标记未知,不用消息长度伪造 token 统计。
比较每个已验收任务的总消耗,包含主控、worker、审查和返工,而不是仅比较某次调用。先完成 3–5 个有代表性的工作包,再调整默认路由;这个数量是试点取样建议,不代表统计显著性或保证节省比例。
API 单价、订阅额度和任务级成本不是同一个指标。本计划不预先承诺固定节省百分比;模型可用性和实际费用以执行时配置为准。低成本模型导致反复修复时应提前升级;高能力模型能明显减少整体返工时可以直接采用。
## 10. 官方依据与执行边界
OpenAI Docs 将 Astra 定位为最具能力、适合困难端到端任务的模型,Sol 面向复杂专业工作,Terra 平衡能力与成本,Luna 面向成本敏感任务;它们并未规定 Planet 各文件应由哪个模型处理。本计划按任务风险给出项目级分工。
- [OpenAI 模型说明](https://developers.openai.com/api/docs/models)
- [Codex 模型选择](https://learn.chatgpt.com/docs/models)
- [子 Agent 配置与模型选择](https://learn.chatgpt.com/docs/agent-configuration/subagents)
只在用户已授权相应开发与委派、且运行环境支持时执行这套拆工。仅制定本计划不会切换当前会话模型、修改默认配置或自动启动 Agent。执行时若某个精确模型不可用,先说明并选择可用替代,不把替代模型冒充原分配。
## 11. 本轮实际执行账本
| 工作包 | 实际分配 | 结果与升级 |
| --- | --- | --- |
| 地理政策、源证据、几何构建与回退 | Sol `high`,`geography_policy` | 版本化规则与校验已实现;如实保留未核验几何,未用模型猜测填补 |
| 空间连接线、卡片生命周期与主入口 | Sol `high`,`spatial_callouts` | 完成原型;六卡避让多次渲染仍有冲突后,交主控接手约束求解,worker 停止同文件写入 |
| 同机性能基线、对照工具、公共滚动条 | Terra `medium`,`performance_baseline` | 保存桌面/模拟手机各 5 次基线;共享滚动容器修复和对照工具已完成 |
| 复杂避让、全球目录/API、视觉参数、集成验证 | 主会话 | 主控整合并审查;性能脚本的场景同步多次失败后也由主控接手修复。独占布局文件,浏览器/构建按顺序执行,避免测试相互干扰 |
| Luna 辅助 | 未启用 | 没有值得额外传递上下文的独立机械任务,不为凑齐模型而派工 |
本轮精确 token 与费用不可得,记为未知;不报告未经测量的节省比例。空间避让存在返工,不能把功能实现或子任务数等同于效率提升。性能问题应在首次稳定复现后采集 CPU/布局证据,再修改热点;本轮最终定位到共享 SVG 长度读取造成的同步布局,单纯调整避让参数不足以解释该成本。性能、运动观感及地理缺项以功能计划的验收状态为准。

View File

@@ -0,0 +1,237 @@
# Earth 地理资源与空间卡片展示实施计划
日期:2026-09-21。状态:本地可运行原型。贴图已替换并归档旧图;固定规则、部分核验几何、全球参与方目录、空间卡片、共享移动滚动条与轻量材质调整已实现。完整政治几何、逐段海岸对齐、视觉认可及性能门槛尚未全部验收,不代表可发布。
本计划以已确认的展示方向为准,替代算力专题旧方案中的固定右侧栏、横贯底部双图和隐藏其他面板的布局。空间卡片与算力目录已有本地可运行原型,但仍需完成最终 QA;算力数据采集、指标证据和长期模型设计继续参见[算力与资源态势计划](compute-resource-intelligence-plan.md)。
## 1. 目标与工作边界
地球是空间信息的主体。打开区域对比后,多个区域分别引出连接线,牵引周围的浮动信息卡。地球转动时,地理锚点跟随球体,卡片保持正向可读,通过平滑、有阻尼的运动调整位置并互相避让,形成信息从地球四周延伸出来的感觉。
地球质感参考 UE 3D 截图在易拉宝与 PPT 中的表现:球面有层次、边缘与明暗交界柔和,背景退后,数据与球体构成同一空间。保持当前操作流畅度是硬验收条件。
已授权实施范围包括资源、空间卡片、共享 UI 与性能验证。继续使用现有 Three.js 引擎和资源加载机制。资源版本与实际转换参数以[地球资源说明](../../resources/earth/README.md)和清单为准。
### 当前实现与未完成项
本轮已实现卡片运动、配置控制、内容承载及中性地球材质调整。配置窗口与卡片显示开关已分离,关闭配置不再停用卡片。卡片默认常显基础信息:36px 主数值、年份/指标/口径说明、设施样本与 Epoch 来源、趋势首末年份及单位、证据入口和区域成员数;长列表仅在需要时展开完整图表。桌面卡片约宽 268px,高度随内容自适应,国家约 214px、区域约 223px;展开卡片支持用户调整至最高 480px,位置/尺寸可保存并恢复自动布局。手机保留同等摘要信息。高清 overlay 使用白色 MeshBasicMaterial、opacity 1、toneMapped:false;r128/r160 按 active API 使用对应 sRGB 读写路径。没有新增 GPU draw pass 或高分辨率纹理,但新增并预加载 45 个本地 SVG 旗帜资源。
旧版底边换侧的连续跳动已定位并由路径规划与稳定换侧策略处理。新实现由 `spatial-callout-controller.js` 管理,布局采用事件驱动并保留有效旧矩形;投影连线持续跟随地球,另一侧候选需持续 1800ms 才换侧,12 秒阅读间隔用于抑制不必要的替换。悬停、焦点和详情阅读会锁定卡片,用户拖动复用 `hud-drag.js`,现有 controls 也消费该共享模块。多卡布局通过实际空闲矩形安排最终位置,优先避开已有功能窗;局部 UI QA 已完成,全球性能与最终发布回归仍按后续门槛验证。下方性能数字和旧尺寸的回归记录属于此前原型,不能直接作为当前大卡片的性能验收结论。
- 底图默认“原始底图”(菜单首项),显示菜单可切换并保存;Natural Earth I 保留,NASA 21K 仅作源归档。运行时统一 8K,按需只保留一张 GPU 纹理;成功后释放旧图,失败保留旧图,过期响应不能覆盖当前图源,不新增渲染层。星空逐字节恢复 commit `83a10a6c`,不再校准密度和颜色。高清 overlay 使用白色 MeshBasicMaterial;r128/r160 按 active API 选择对应 sRGB 读写。没有新增长驻高分辨率纹理、后处理或 GPU draw pass;算力卡另新增并预加载 45 个本地 SVG 旗帜资源。Natural Earth 原图偏浅,不把不存在的细节写成恢复结果。
- 地理政策由版本化清单贯穿规范编码、后台聚合、高精几何及低精回退。中国、台湾、港澳、科索沃等已有回归;已核验几何及济州岛补全见资源说明。高精技术就绪与政策可发布分开:`high_precision_ready=true`,`policy_publishable=false`。东沙、中沙、曾母暗沙点注记及经核验海上主张线仍有缺项。
- 规范目录覆盖 45 个国家编码,界面分组为中国、美国、欧洲、不含中国的亚太和其他;分组不当作统计合计。当前空间原型最多同时显示 6 张,后端最多接受 12 个参与方。没有历史观测的地区返回 `null` 历史与覆盖信息;设施样本数永远不作为计算出的国家总量。
- 桌面卡片复用共享 HUD 和连接线,保留稳定 DOM 身份;选择器和来源窗使用共享拖动。背面卡片保留内容并隐去连接线。旋转通过原动画循环更新,投影连线持续更新;布局按事件分帧处理并保留有效旧矩形。`main.js` 的 `isDragging`、`activeTouchPoints`、`hasActiveGlobeInertia` 接入运动模式:手动/惯性快速模式以 72ms/12px 为响应预算,在当前帧完成受限布局,停止后一次 settle;slow auto 保留 1.8 秒换侧和 12 秒阅读保持,用户拖动放置优先。
- 手机复用公共抽屉与深色滚动所有者,使用适合手机阅读的字号;目前是抽屉内紧凑卡片列表,尚未实现手机环绕空间卡片。桌面原型没有强制隐藏新闻、媒体或统计窗口。
- 2/4/6 卡片、背面投影、共享窗口避让、饱和纵列与共享连线动画已有 11 项单元测试。浏览器已检查 1366×768、1920×1080、390×844 及 125%/150% 对应的有效 CSS 视口;单元测试不替代运动观感验收。
### 本轮空间卡片 UI 定向验收(2026-09-21)
本计划保留此前的历史验证记录;它们不代表当前地图源切换或全球 Earth 验收已经完成。当前实现说明只记录所需资源生命周期与 UI 约束,Browser/全球性能结论仍按后续门槛单独复核。
以上是本轮卡片 UI 与局部资产的定向证据。全球地理政策、逐段海岸对齐、完整 Earth 渲染性能、真实设备性能及发布级回归仍按本计划后续门槛单独验收。
## 2. 优先级与不可退让的要求
| 优先级 | 要求 | 执行原则 |
| --- | --- | --- |
| P0 | 固定政治展示规则 | 外部数据、更新、回退及 AI 均不能覆盖项目规则 |
| P0 | 性能不退化 | 在同设备、同数据、同场景下验证;可复现的退化不能用视觉提升抵消 |
| P1 | 地理对应一致 | 贴图定位、海岸线、区域归属、拾取和卡片锚点一致 |
| P1 | 地球关联的空间卡片 | 使用现有连接线与展示机制扩展,不恢复固定大屏布局 |
| P1 | 共享 UI 与移动端一致 | 共用字体、标题栏、按钮、滚动容器及窗口行为 |
| P2 | 卡片内图表和内容完善 | 先验收空间关系与运动,再充实图表和解释 |
## 3. 固定政治展示规则
项目展示采用固定中国视角:中国相关领土及争议区域按项目既定中国视角处理,台湾按中国处理;科索沃按塞尔维亚处理。该口径先于数据源选择,不由数据源默认分类、模型输出或换源结果决定。
实施时建立单一、版本化的规则清单与几何修正包。名称映射只是其中一部分,必须覆盖国家编码、Polygon/MultiPolygon 归属、边界绘制、区域填色、鼠标拾取、高亮、统计及卡片归属。处理后的结果才可以成为运行资源;未经规则校验的新来源不得替换正式资源,旧资源回退也必须通过同一规则。
规则清单应包含:稳定规则 ID、目标区域的明确标识、目标归属、显示名称、几何来源、合并/裁切操作、版本及回归样例。不能只依赖模糊地名字符串替换。语音中未能确认的具体岛礁名称必须在修正清单中明确定位,不能猜测名字后直接移动多边形。
海岸线、陆地行政区域和海上主张线是不同几何层。项目口径在各层一致执行;海上主张线不能作为陆地面直接填充,也不能代替具体岛礁的几何和归属规则。
必须具备的失败测试:外部源恢复原始分类、别名变化、同区域不同编码、几何修改后拾取仍用旧数据、低精度回退覆盖规则。任何一项失败,都不发布该资源版本。
## 4. 已查明的现状
### 4.1 贴图与国界
- 原运行贴图为 `frontend/public/earth/assets/8k_earth_daymap.jpg`,8192×4096;代码直接加载 JPG,未读取该文件配套的地理定位元数据。
- `country-boundaries.js` 使用独立 GeoJSON/PMTiles 几何绘线与拾取;高精度是一个选择状态,存在退回低精度的路径,按钮名称不等于实际正在使用的文件。
- 本机此前检查到 `natural-earth-v1`、`geojson-high-precision` 构建清单,以及独立 base、hover、claim 文件;该构建不能直接等同于完整应用了项目政治几何规则。实施前需要重新读取实际运行版本。
- 已提供的两张问题截图中,一张出现海岸折线与影像陆地错位;另一张有可见岛屿但没有对应线框。截图只能证明现象,尚不能断言根因一定是 UV、投影、低精度回退或缺岛。
- 精度检查必须同时检查“看得到轮廓”和“能选中对应区域”,二者不能各用一套不一致的几何。
### 4.2 World Monitor 对照
本机 World Monitor `463aec0f` 使用独立地表图片与 `countries.geojson`;经纬度归属由点落入多边形的计算决定,二维底图和自带的交互区域也分开加载。
本次文件统计:World Monitor 的中国、日本、韩国分别为 200、60、14 个坐标点;Planet 的低精度分别为 240、65、19;本机高精度文件分别为 14,118、6,952、3,060。点数不等于测绘准确度,但表明不能靠直接换用 World Monitor 的简化国家文件解决近景海岸问题。它的原始归属处理也不能代替本项目规则。
### 4.3 已有共享机制
| 入口 | 当前作用 | 计划复用方式 |
| --- | --- | --- |
| `frontend/public/earth/js/callout-connector.js` | 连接线、元素锚点与路径 | 扩展为多个稳定区域锚点与卡片配对 |
| `frontend/public/earth/js/presentation-controller.js` | 展示生命周期与连接器更新 | 在单项展示的基础上设计受控的多项展示,不破坏现有模式 |
| `frontend/public/earth/js/cruise-sequencer.js` | 巡航时序 | 保留当前巡航行为,区域对比明确自己的生命周期 |
| `frontend/public/earth/js/info-card.js` | 公用详情与移动详情 | 抽出/复用内容渲染能力,不复制成另一套卡片组件 |
| `frontend/public/earth/js/hud-panels.js`、`controls.js` | 面板收起、显示、拖动与约束 | 工具窗继续遵循这些规则;明确 createHUDPanel 本身不包办所有拖动/缩放 |
| `frontend/public/earth/css/hud.css`、`info-panel.css` | 公用外观、字号、滚动与移动抽屉 | 去掉专题私设的大字号、独立控件和白色滚动条 |
## 5. 地理资源:先采用配套贴图的简单方案
### 5.1 选择与准备
默认运行底图为“原始底图”(菜单首项),显示菜单可切换并保存底图源;Natural Earth I 保留可用。NASA 21K 仅保留为源归档,运行包统一使用 8192×4096 的单张 GPU 贴图。官方源版本与许可信息仍以资源清单为准,不能把归档源误写成运行资源。
采用不带政治标注的物理地表图。按需只保留一张运行 GPU 贴图;新源成功加载后释放旧图,失败保留旧图,过期响应不得覆盖当前图源。源包保留 TIFF、世界文件和说明,运行资源统一为 8192×4096 的 Web 图。转换只做尺寸与格式适配,不用生成式图像重绘海岸、岛屿或国界,不随意裁切、旋转或拉伸经纬范围;底图切换不新增渲染层。
下载的原始大文件保存在本地 `downloads/earth/`,不纳入前端发布包。可追溯清单、配准信息与旧贴图归档放在 `resources/earth/`;运行图放在现有 `frontend/public/earth/assets/`。旧图必须先按字节完整归档并核对 SHA256,之后才能替换运行路径。
### 5.2 对齐验证
1. 读取 TIFF/TFW 的像元大小、原点、行列方向、地理范围和 CRS;同时记录 JPEG 派生后的像元中心定位,避免半像素偏移。
2. 明确运行中加载的贴图、边界文件、资源版本与是否发生回退。
3. 使用同一投影和球面经纬转换叠加成套矢量,检查赤道、零经线、日期变更线和高纬区域。
4. 在截图对应海岸、近海岛屿、台湾周边和其他不同经度的典型海岸逐点验证,区分整体偏移、局部概化和缺失几何。
5. 政治修正后的同一份区域几何用于标注、高亮和拾取。所有需要烘焙的线也从这份数据导出,不能在图片和矢量上分别手工描线。
6. 小岛、岛礁或海岸不满足要求时,先补充对应区域数据并重新配准;仅当这条简化路线确实不够用时,再考虑局部细节或分级资源。
成套资源声明配准不等于本项目已经逐像素验收。不能承诺卫星影像每一处潮线都与概化海岸线完全一致;验收需保存具体偏差和缺失清单,不能靠强行拉伸边界迎合图片。
### 5.3 常态渲染与交互
优先预合成常态显示所需的边界,以减少额外线段绘制。需要独立国界开关时,评估复用现有覆盖层或同定位的透明边界层;不默认再常驻一张 8K 纹理。矢量数据继续承担区域判定和选中高亮,不在每帧重新解释整份 GeoJSON。
本阶段不以替换 Three.js、引入新 GIS 平台或建设全球在线瓦片服务作为前置条件。
## 6. 整体视觉方向
参照资产:`Planet-Handoff-20260916` 中的完整易拉宝效果稿和卡片展示 PPT,尤其 PPT 内来自 UE 3D 的原始星球画面。海报中的线点含概念叠加,不能拿来充当精确地理数据。
- 地球保持正圆与正确地理位置,视觉上成为主体;不把纹理精细度等同于画面品质。
- 球面采用柔和、连续的明暗层次,边缘有薄而自然的大气过渡;地理细节仍然可读。
- 降低背景星点对比与密度造成的干扰,让背景退后;变化以参考图与实际渲染对照决定。
- 数据节点与连接线有轻重层级,避免强霓虹、油亮塑料感、浓重太空雾和粗硬轮廓。
- 优先在现有材质、光照与透明度范围内调整;新增后处理、辉光或高分辨率透明层须先证明成本。
- 用同一位置、距离、光照条件对比前后效果,近景与远景都检查边缘、深度冲突和细节。
## 7. 区域卡片:从地球四周延伸出来
### 7.1 行为模型
一个区域对应稳定的地理锚点、连接线和卡片 ID。锚点随球体旋转;卡片处在屏幕空间,保持正向、稳定大小和内容身份。转动时变化的是屏幕位置与线的路径,不重建卡片、不交换身份、不清空正在阅读的内容。
卡片根据区域投影分布到球体周围,保留疏密和空间关系,不固定平均分成八个方向,不横排占满屏幕。使用有限的避让和有阻尼的缓动,最终能够安定;不持续抖动或为了追踪锚点逐帧跳位。
### 7.2 布局与生命周期
- 沿用现有连线的起点/终点接口和动画语言,先验证 2 张、4 张、6 张卡片;6 张是原型测试档位,不是已确定的产品上限。
- 保持地域锚点与区域绑定;算法可以移动卡片,但不能移动真实地理坐标来换取好看的布局。
- 优先避让其他卡片和已有工具窗,不自动隐藏新闻、直播、统计、图层面板。
- 对卡片换侧设置稳定区间,避免锚点越过某条中心线就立即左右互换。
- 背面区域不画成穿过地球的前景锚点。建议保留卡片身份,用边缘提示表达背面方位;完整隐藏、收束或边缘提示的最终表现用短动画原型比较后决定。
- 用户正在操作的卡片短暂锁定其屏幕位置;是否允许长期固定/拖动偏移,先以现有交互能力做原型验证,不增加未经确认的复杂模式。
- 增删区域、切换年份、退出对比、隐藏相关图层和请求失败都要有明确的清理与恢复逻辑;旧响应不能复活已关闭卡片。
- 区域名称锚点用于对比展示;不能把区域中心点画成设施位置。
### 7.3 卡片内容与参与方
先验证空间展示,再完善图表。首轮卡片可以只包含名称、一个有来源的关键指标、参考期及来源入口;固定侧栏和横贯底部双图的旧设计不再继续扩展,普通浮窗加分页的方案也不是最终展示方向。
参与方必须支持全球扩展,不能在接口和 UI 中永久硬编码成中美两项。日本、韩国等可以作为独立对象;亚太、欧洲可用于选择菜单的分组或区域筛选。具体默认组合与区域汇总口径还需在完整 UI 原型中确定,不把早期讨论的菜单建议当成已确认设计。组合区域与成员国不能重复计入同一分母。
缺少同口径历史数据的参与方仍可展示公开设施覆盖及明确的缺失状态,不伪造总量、得分或排名。全国总算力、公开 AI 集群样本、TOP500 FP64 和创新指数继续分开。2026 年未核验值不得加入正式历史。A4 AI 解读不阻塞空间展示验收。
## 8. 共享窗口与移动端
设置、来源、图层等操作窗口继续复用已有标题栏、按钮、拖动和按能力启用的缩放逻辑。不得用单独字号和固定网格重新构建一套视觉体系。数据更新和语言切换应保留窗口位置、尺寸、选中对象和滚动进度。
移动端复用原底部抽屉与详情展示。区域卡片数量受可用空间控制,保证地球与主要交互可见;可收束为可切换的关联卡片,但具体排列需要实机原型确认。不能把桌面全部卡片原尺寸堆到手机上。
白色滚动条问题从 `.earth-mobile-drawer-content` 及实际滚动所有者核对,统一复用 Earth 的深色滚动条样式与滚动容器。覆盖图层、搜索、态势、算力、新闻和设置页;底部导航保持固定,避免嵌套重复滚动,不仅对算力页补一条局部 CSS。
## 9. 性能基线与硬验收
### 9.1 基线
资源替换前的画面可通过归档旧贴图复现。保存设备、浏览器、GPU、视口、DPR、图层开关、数据量、相机路径与资源版本。在相同条件下重复测试,至少记录 5 次稳定运行,并区分冷启动、热启动和稳定旋转。
观察:首个可操作画面时间、首个有效地球画面时间、帧时间 p50/p95/p99、长帧/长任务、交互延迟、JS 堆与纹理内存估算、draw calls、纹理/对象数、网络字节及请求次数。只比较平均 FPS 不足以证明没有卡顿。
### 9.2 实现预算
- 运行贴图先维持 8192×4096。相同格式下纹理像素量不增加;RGBA8 粗估基础约 128 MiB,完整 mipmap 约 171 MiB,不能把 JPEG 文件变小当成显存同比下降。
- 保持已有 mesh、图标实例与纹理缓存;旋转不重新解码图片或构造国家几何。
- 地理投影和轻量位置更新与渲染帧同步;碰撞布局只在锚点明显变化、卡片变化或窗口尺寸变化时重算,不让每张卡片各起一套动画循环。
- 卡片 DOM 保持稳定,图表仅在数据/尺寸变化时更新;避免每帧测量布局后又写布局造成同步回流。
- 网络、缓存、监听器、纹理及卡片数量均有边界,关闭/重开多轮不得持续增长。
- 默认不增加新的高分辨率常驻覆盖层、昂贵后处理或全部区域的最高精度常驻数据。
### 9.3 判定
同条件下不能出现可复现的流畅度、加载、交互或资源占用退化。测量噪声必须记录并重复验证,不能事先允许固定比例退化来降低要求。超出预算先调整效果、卡片密度或实现,再复测。桌面与移动分别验收;没有真实手机数据时只能说明模拟检查通过,不能宣布手机性能达标。
### 9.4 内容卡扩容前的原型验证记录(2026-09-21)
同机 Headless Edge、桌面 1440×900 / 手机尺寸模拟 390×844、DPR 1、自动旋转,每种尺寸 5 次;预热 1 秒、帧时间采样 2 秒。该短采样用于定位,不能代替真实设备长时交互验收。原始基线标识 `2026-09-20T18-30-56-184Z`,最新对照标识 `2026-09-20T21-07-40-495Z`(UTC);完整 JSON 留在本机临时验证目录 `planet-earth-performance-baseline`。
| 项目 | 原基线 | 本轮结果 | 判断 |
| --- | --- | --- | --- |
| 桌面无卡 p95 帧时间,5 次中位数 | 7.09 ms | 7.10 ms | 接近;不能据此宣称完整性能通过 |
| 手机尺寸模拟无卡 p95 | 7.00 ms | 7.20 ms | 未做真实手机测量 |
| 桌面 2 / 4 / 6 卡 p95,5 次中位数 | 无同场景旧实现对照 | 7.10 / 7.10 / 14.00 ms | 六卡比本轮无卡场景有明确成本,仍需优化 |
| 六卡采样窗口长任务 | — | 5 次均为 0 | 没有 50 ms 长任务不等于没有掉帧 |
| 桌面 DOMContentLoaded 中位数 | 1.262 s | 1.627 s | 已移除 Google Fonts 的约 10 秒阻塞;仍不宣称加载性能与基线完全持平 |
| 桌面基础 JS 堆中位数 | 约 50.2 MiB | 约 53.2 MiB | 不是显存;未替代纹理/绘制统计 |
每轮另做 5 次专题开关并强制 GC 检查,关闭后的保留堆约 25 MB,首末差约 0.28–0.33 MB;这不构成长期无泄漏证明。浏览器单独核对关闭后的卡片悬停/焦点监听器为 0,来源窗与拖出容器的选择器均能关闭。
最终 Bun 构建、前端规则检查、文档一致性检查与 `git diff --check` 通过。既有前端 smoke(使用其 API mocks)通过 321 项路由/交互检查,包括公共页面、鉴权、管理导航、移动布局和管理页缩放;Earth 空间卡片另用真实本地接口检查,不能混同两种证据。
布局求解已分帧执行、减少重复候选评分、优先前景区域,在合格布局找到后停止重复试排;工具栏按可见按钮而非透明容器避让。CPU 采样发现共享 SVG 的 `getTotalLength()` 同步布局热点,已改为按已知折线点计算长度,保留原绘线动画。11 项布局/投影/连线测试通过。1366×768 浏览器中 4/6 卡、旋转后、稳定后及工具栏展开时未发现与卡片、选择器或现有 HUD 的矩形交叠;1920×1080、1093×614、911×512 同样完成六卡边界检查,后两项是浏览器缩放的等效 CSS 视口,不冒充真实设备缩放测试。46 项相关后端测试通过,高精与回退的台湾、藏南、阿克赛钦、科索沃、济州岛浏览器拾取均符合规则。官方几何缺项、完整海岸对齐、六卡性能与真实手机仍未通过最终验收。
## 10. 分阶段实施与交付门槛
开发模型分工、文件所有权、并行限制和上下文控制按[开发 Agent 模型分配计划](development-agent-model-allocation-plan.md)执行:Sol 主控与核心实现,Terra 承担明确子任务,Luna 处理有边界的辅助工作,Astra 负责高风险设计及疑难审查。模型分工不改变本计划的政治规则、视觉方向和性能验收门槛。
| 阶段 | 工作 | 必须交付的证据 |
| --- | --- | --- |
| S0 资源准备 | 新贴图、配准信息、旧图归档、来源清单 | 新旧尺寸/哈希、解码与球面渲染检查;本轮范围 |
| S1 规则与配准 | 固定政治清单、实际提供者诊断、配套边界对齐 | 规则测试、截图问题点对照、回退测试;不能只报高精度开关已打开 |
| S2 空间运动原型 | 复用连接器,多区域锚点、卡片稳定身份与避让 | 转动/缩放/换侧/背面/退出短录屏,2/4/6 卡档位及性能差值 |
| S3 视觉与组件 | 柔光质感、背景层次、共享字体与移动滚动 | UE/PPT/海报参照对照、多个视角、桌面/手机及缩放截图 |
| S4 内容联动 | 多参与方、真实指标与来源、时间/设施联动 | 可操作路径、缺失/错误状态、来源与口径验证 |
| S5 回归与发布准备 | 性能复测、旧图层/巡航/工具窗回归、同步文档 | 完整验收记录;需要发布时另走仓库发布流程 |
S1 未通过,不能把新图片称为边界问题已解决。S2 的空间交互未确认,不能直接投入完整图表和全部国家卡片。性能或政治规则任何一项未通过,后续阶段不能据此宣布完成。
## 11. 验收清单
- 政治:标签、几何、填色、拾取、统计、卡片与回退源口径一致;科索沃等明确规则有专门用例。
- 地理:截图所指海岸和小岛、台湾周边、日期变更线、高纬度;记录偏差而非只肉眼说“差不多”。
- 视觉:球体不变形,边缘柔和但地理清楚,卡片从区域延伸且不压满地球;远距无闪烁和黑块。
- 交互:转动、缩放、选中、背面、卡片增删、触摸、键盘、语言切换、退出、窗口大小变化及旧响应竞态。
- 共存:新闻、媒体、统计、图层及原详情卡保持可用;不通过强制隐藏制造布局空间。
- 移动:公共抽屉无白色原生滚动条,底部导航固定,只有预期内容区滚动,文字和操作不被截断。
- 响应式:1366×768、1920×1080、约 390×844,以及 125%/150% 缩放;模拟与实机结果分别记录。
- 回归:公共页面、鉴权、管理路由、导航/搜索/标签页、既有 BGP/新闻/动捕巡航与旧图层。
- 性能:与归档基线同条件比较,保存帧时间、内存、网络和交互证据;循环打开关闭验证资源释放。
- 文档:更新实际变更的中英文手册、Earth context、图层规则和资源清单;计划与已实现行为不能混写。
## 12. 来源、恢复与完成定义
- [Natural Earth 栅格与矢量配准说明](https://www.naturalearthdata.com/downloads/10m-raster-data/)
- [Natural Earth I 资源页面](https://www.naturalearthdata.com/downloads/10m-raster-data/10m-natural-earth-1/)
- [Natural Earth 使用条款](https://www.naturalearthdata.com/about/terms-of-use/)
- [现有 Presentation 解耦计划](earth-presentation-decoupled-architecture-plan.md)
- [原高精度边界计划及历史说明](earth-high-precision-boundary-tiles-plan.md)
- [地球前端上下文](../technical/zh/earth-frontend-context.md)
- [渲染层次规范](../technical/zh/earth-render-layer-order.md)
旧贴图恢复路径和 SHA256 见资源清单。先核对归档哈希,再将归档副本复制回原运行资源路径并重建预览;不以回退图片为由撤销其他用户改动。新资源和旧归档均不得无记录覆盖。
最终完成意味着:展示规则固定且经验证,地图与区域选择对应,多个卡片确实由地球锚点牵引并稳定运动,操作组件和手机滚动保持一致,实际性能不低于基线。单张效果图、构建通过或“高精度”名称都不能单独证明完成。

View File

@@ -99,7 +99,7 @@ AIS vessel collectors use a different persistence path from regular `CollectedDa
Earth boundaries are no longer data collectors. They are Earth static rendering assets: the Earth Assets settings panel owns source configuration, and `/api/v1/earth/boundaries/*` builds `frontend/public/earth/data/boundaries/earth-boundaries-china-pov-v1.pmtiles`. When no high-precision PMTiles artifact is available locally, the frontend uses the bundled low-precision GeoJSON fallback and does not write boundary records to `CollectedData`.
TOP500 and Epoch AI compute sources do not always provide usable coordinates. The unified Earth compute-center endpoint uses only valid source-provided coordinates or `compute_center_locations` dimension-table coordinates during the main map startup path; records without coordinates are returned as `unresolved` instead of being rendered from a local registry, country centroid, or guessed city. When users manually collect candidates, the backend queries ROR and Nominatim/OpenStreetMap from source fields; accepted candidates are saved into `compute_center_locations` and rendered from that table on the next layer refresh.
TOP500 and Epoch AI compute sources do not always provide usable coordinates. Epoch's official CSV `Location` may contain only a country or be empty even when a row has latitude/longitude; it is never treated as a precise site without the required source semantics. The unified Earth compute-center endpoint uses valid source-provided coordinates or `compute_center_locations` dimension-table coordinates during the main map startup path; records without renderable coordinates remain in the complete `unresolved` list instead of being rendered from a local registry, country centroid, or guessed headquarters. `precise`/`site` keep source coordinates; city-level and confirmation-required points may receive only a deterministic display offset keyed by `source` + `source_id`, with the original coordinate untouched. When users manually collect candidates, the backend queries ROR and Nominatim/OpenStreetMap from source fields; accepted candidates are saved into `compute_center_locations` and rendered from that table on the next layer refresh.
Admin collection management follows the business hierarchy instead of flattening every endpoint into one table:
@@ -460,3 +460,15 @@ POST /api/v1/datasources/trigger-batch
The request body may pass `source_ids` for selected rows. Without `source_ids`, the backend filters by `product`, `module`, `is_active`, `run_status`, `collected`, `credential_status`, and `q`. The endpoint skips disabled sources, sources already running without `force`, and sources still inside their frequency window, then returns `triggered`, `skipped`, and `failed` groups.
**Core file**: `backend/app/api/v1/datasources.py`
## Compute source contracts and topic APIs
Epoch uses its official public CSV, retaining theoretical FP16 performance, original dates, status, anonymization/rounding flags, and upgrade relationships. Anonymous rows use content fingerprints plus equal-row ordinals to preserve snapshot multiplicity, without claiming persistent facility identity or assigning coordinates. Unknown countries remain flagged and are excluded from selected-country counts. TOP500 uses system-detail IDs, reads the edition from the list URL, and converts header/cell units into FLOP/s with an explicit FP64 LINPACK basis. Public pages may expose only part of an edition; this does not establish full TOP500 coverage.
Neither collector returns sample data on failure. Empty fetches or transformed results, schema errors, and losses exceeding 40% of the previous snapshot fail while retaining prior data. Incomplete writes are rolled back. TOP500 location migration copies only a unique exact name and canonical-country match to the new system ID, retaining the historical key; rank is never a migration key.
`compute_history_defaults.json` ships the original report data. `compute_configuration.py` reads a complete override from the `compute_comparison` category in `system_settings`; absent records use bundled defaults without migrations or manual initialization. `compute_history.py` performs same-year aggregation. Reference periods and publication dates remain distinct, and unknown publication days remain unspecified. `/api/v1/data-products/compute/series` reads configuration on each request with `Cache-Control: no-store`, offering global share, fixed US + China bilateral share and FP32 absolute observations. Innovation gaps remain independent. An empty override stays empty instead of silently restoring observations.
Under the same prefix, `GET/PUT /configuration` and `POST /configuration/reset` require admin/super_admin. Writes accept `{revision, config}` and reset accepts `revision`. Pydantic validates canonical countries, finite values, share totals, duplicate metric/year pairs and report references; each metric/year has one report basis. PostgreSQL category uniqueness and row locking serialize first writes and updates; stale revisions return 409. Configuration and before/after audit records commit in one transaction. Reset removes the override so subsequent reads use the current bundled defaults. `version` is a configuration revision, while `reviewed_at` is the supplied review date. Admin-curated data is not automatically labeled official. Source URLs are references, not scrape instructions.
`/api/v1/data-products/compute/facilities` reads successful snapshots with `compute_contract=v1`, applying country filters and per-source counts in SQL. Epoch excludes planned, decommissioned, superseded, and suspected duplicate records; TOP500 means list observations. Cross-source deduplication remains pending. Historical cutoffs use collection time, never pretending a recently fetched old source was available earlier. Geographic results are bounded and report truncation; total record counts are independent of this limit. Official site helpers are keyed by exact `source` + `source_id`; their access-map centers are site-level references only and retain their official URL in `source_note`.

View File

@@ -1,5 +1,19 @@
# Earth Frontend Context
## Configurable compute observations
Compute cards read annual observations from `/api/v1/data-products/compute/series`; the admin Compute comparison section is the maintenance entry point. `compute-topic.js` fetches with no-store, listens for same-origin `planet-compute-configuration` notifications, and checks every 60 seconds while visible and enabled, plus when the page becomes visible. Observation refresh does not reload facility layers. `destroy()` releases the timer, channel and visibility listener. Country membership, full regional coverage and missing-value semantics remain backend-owned.
## Surface texture provenance
On 2026-09-21, `assets/8k_earth_daymap.jpg` was replaced with an 8192×4096 Web derivative of the paired Natural Earth I raster. Its loading path and rendering code remain unchanged. The source is a 16200×8100 EPSG:4326 image; the previous JPEG is preserved byte-for-byte under `resources/earth/archive/2026-09-21/`. Source/license records, the catalogue versus embedded-version discrepancy, SHA256 hashes, JPEG parameters, and recalculated world-file metadata live in `resources/earth/natural-earth-1/`. Large originals remain in Git-ignored `downloads/earth/`, outside the frontend release bundle.
The base-map selector defaults to Original basemap, listed first and saves the source chosen in the display menu. Natural Earth I remains available alongside “Original basemap”, a byte-identical copy of the September 21, 2026 archive; NASA 21K is source archive material only, while runtime delivery stays at one 8K texture. A new source replaces the old GPU texture only after successful loading and releases the old one then; failures retain the old texture and stale responses cannot overwrite the current source. This is a source switch, not an additional rendering layer. The physical texture is JPEG sRGB, does not bake political borders, and detailed boundary alignment is not yet validated. The starfield is restored byte-for-byte to commit `83a10a6c`: `celestial.js` matches that commit, `skyTint` and `skyBrightness` are removed, and `brightStarOpacity` is back to `0.88`; star density and color are no longer calibrated. The HD overlay uses a white `MeshBasicMaterial` with opacity `1` and `toneMapped:false`, with `texture-colors.js` centralizing sRGB read/write. The 0.128 path uses `encoding` plus `LinearTosRGB`; the 0.160.1 path uses `colorSpace` plus the normal colorspace fragment. There is no Phong white-light or colored twilight overlay, blue night tint, `cameraBoost`, or shoulder curve; daylight neutral scaling keeps brightness at or below the source maximum, and dark mode only applies neutral scaling. The original texture asset is unchanged. Natural Earth is intrinsically pale; this does not claim missing detail was restored. Earth still supports the Three 0.128 import-map runtime and frontend package Three 0.160.1 runtime, and WebGL1 derivatives remain enabled for `fwidth`.
Day/night shading uses a narrow smooth transition centered on the solar horizon (normal dot product ±0.12). The day side is capped at the source brightness; night-side linear illumination is 0.24, rising smoothly to at most 0.60 in source shadows to retain dark ocean and forest detail. Shading uses a neutral scalar without added exposure, colored veils, rendering passes, or textures.
Basemaps and TV channels share `hud-select-menu.js` for the HUD popover, list, selection mark, keyboard navigation, and positioning; `tv-source-menu.js` only adapts channel copy. Basemaps disable search, pagination, and counts. The trigger owner identifies popover clicks as internal to the mobile drawer.
This document describes the current real structure of the Earth display frontend. The focus is on helping future changes to the HUD, layers, media panel, real terrain, and BGP visualization avoid repeating past structural and state-sync pitfalls.
Related references:
@@ -161,6 +175,12 @@ Each module is responsible for its own:
`tv-source-menu.js` reuses HUD and legend-list styling in a popover with search, a scrolling list, and a fixed count footer. `tv.js` requests 50 entries from `/api/v1/tv/streams` and caches channel details; search runs against the complete backend catalog, and pagination uses `next_offset`. Cancellation and a request generation counter prevent stale results from replacing a newer search. Catalog refresh uses `selected_id` to restore a channel outside the first page.
`tv-playback-policy.js` owns live recovery decisions: all non-fatal HLS events remain with the player, while fatal network/media failures receive at most three recovery attempts. Switching requires automatic fallback to be enabled, no explicit user channel selection, and an embeddable backup. Async callbacks in `tv.js` verify player identity and channel; native video errors no longer bypass HLS recovery. `tv_streams.py` replaces the old bundled CCTV-4 CDRM URL, including saved copies, with WCETV's America-edition HLS stream, names the edition explicitly, and preserves custom streams. An HTTP success does not establish video decodability.
Sources with verified CORS support may explicitly set `use_proxy:false` for direct browser playback without segment forwarding. Other sources continue to use the TV proxy by default. The bundled WCETV source uses direct playback.
Source information reuses `createHUDPanel` and its existing 0.22-second CSS transition. The shared controller measures content into `--hud-body-content-height`, avoiding the non-interpolable change from `max-height: none` to zero. ResizeObserver updates content measurements and compensates the top position of upward-expanding dragged panels during animation, keeping their bottom edge fixed without disabling the body transition. The shell's top transition is paused during compensation to prevent drift from double interpolation.
`brand.js` manages Earth HUD brand resources. Static assets provide the default brand; runtime overrides come from `/api/v1/earth/brand`, and uploaded images are served from `/earth-brand-assets/...`. The frontend must treat logo/title images and text fallback separately: if an image fails, show the text title; if text fields are empty, rely on backend defaults so the HUD brand area never renders blank. The console Earth Content page owns saving and resetting brand configuration; the Earth frontend only consumes it.
`about.js` manages the About card inside Earth settings. Frontend defaults remain as a fallback, while runtime content is loaded from `/api/v1/earth/about`. If the request fails or fields are missing, the renderer must fall back per field so the settings page never renders an empty card. Admin exposes an Earth Content `About` tab; saving uses `PUT /api/v1/earth/about`, and restoring defaults uses `DELETE /api/v1/earth/about`.
@@ -349,6 +369,9 @@ If future cable, satellite, or news cruise is added, do not copy a new set of `m
[controls.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/controls.js) owns the Earth zoom state, and every zoom entry point must ultimately call `setZoomLevel()` to write the camera distance. Do not write `camera.position.z` from other modules, or the zoom percentage, drag sensitivity, and Interactable clustering thresholds will diverge again.
`globe-navigation.js` maps zoom to `sqrt(R² + (D₀² - R²) / zoom²)` with minimum surface clearance and the matching inverse. The 100% camera pose remains unchanged; 500% stays outside the globe. Mouse and single-touch drags choose a mode on pointer-down from the actual camera frustum. A fully visible globe retains legacy screen-delta sensitivity, 0.18 smoothing and release inertia, including movement outside its silhouette. A viewport-clipped globe uses pixel-aligned ray/sphere grabbing only when the initial pointer hits its surface. Subsequent missed intersections pause surface updates and clear fling speed instead of using a closest-point substitute that can reverse rotation. Grabs starting in space use screen deltas. The mode stays fixed throughout the drag; auto-rotation is suspended during dragging and pinching. Returning from a pinch to one finger reselects the mode and rebases the anchor. Click suppression uses displacement from pointer-down, and cancellation clears inertia.
Interactable clustering is selected per layer through `cluster.strategy`. `stable-spherical` uses discrete zoom bands and local 3D bucket clustering, so BGP, compute centers, and Earth interactables do not regroup while the globe rotates inside the same band. `dynamic-screen` keeps projection-based clustering, and `none` disables it. Vessels explicitly disable clustering and avoidance so incremental updates do not rebuild cluster topology. Stable cluster dots stay rigidly aligned to their 3D centroid projection and do not participate in 2D avoidance. See [Earth Interactable Clustering](/home/ray/dev/linkong/planet/docs/technical/en/earth-interactable-clustering.md) for strategy configuration and tuning.
Wheel input has two paths. Traditional mouse wheels keep the 10% step and short animation, using `wheelZoomTarget` as the logical base for continuous wheel input. Trackpads and high-precision wheels use the pixel delta for continuous zoom and call `setZoomLevel()` directly instead of passing through the 10% stepped animation. The trackpad path also filters a short-window, old-direction residual delta after a real direction change so inertia tails do not pull a just-reversed zoom back in the previous direction.
@@ -385,3 +408,46 @@ Therefore:
For console structure, see:
- [Admin Frontend Context](/home/ray/dev/linkong/planet/docs/technical/en/frontend-admin-frontend-context.md)
## Spatial compute cards
`compute-topic.js` owns periods, measures, participants, and evidence. `compute_catalog.py` supplies canonical names, picker groups, and regional label anchors for 45 currently registered participants. Unknown historical values remain missing. APIs are no longer limited to CN/US. Group labels never become overlapping statistical denominators, and regional coordinates must not become facility locations.
Earth's existing Roboto Condensed face is now served locally from `assets/fonts/`, with its source and license in that directory and the shared declaration in `css/fonts.css`. The remote Google Fonts stylesheet no longer blocks the page; font family and weights are preserved.
`spatial-callouts.js` delegates layout to `spatial-callout-controller.js` and reuses `CalloutConnector` with stable region IDs. Layout is event-driven and retains valid old rectangles while work is pending; the projected source line follows the globe continuously. The layout reference is the direction of the actual source exit point, so a card stays near the object side instead of being pulled from a top-left geographic point to an opposite corner. Neighboring same-sector placements, shorter connectors, and fewer crossings are preferred; the solver may exchange free positions for at most three rounds rather than relayout everything each frame. In fullscreen Earth, nearby foreground positions are allowed so cards of normal size do not cross the whole globe merely to avoid it. In slow mode, sustained direction drift or connector crossings must persist for 1.8 seconds before proposing a joint reflow. Automatic proposals are at least 8 seconds apart and need an improvement of at least 18% plus a minimum absolute gain. Manual/inertial input retains the 72ms/12px response budget. Expansion and manual placement grant 12 seconds of reading time; hover, keyboard focus and dragging continue to lock a card, while residual pointer focus does not lock it forever. Manual placement is a preference: after its hold, automatic repair requires at least 45 degrees of source direction change. Restored positions are not permanent pins. Rear connectors retain the globe-outline cue without changing card identity. Local position/size persistence and reset-to-auto are part of the controller. A `ResizeObserver` handles resizing and collapse; configuration and source windows retain shared drag handles.
Object presentation is shared through `PresentationController` and the new object-callout anchor/layout path. A click on a BGP, compute, vessel, satellite, or cable object draws the connector before placing the adjacent card. The source anchor begins at the actual click or original region label, then follows a visible highlight geometry sample as rotation approaches occlusion. The stable `layoutReferencePoint` is derived from the actual connector exit direction, so `packDirectionalEdgeCallouts` keeps cards on the corresponding screen edge and leaves the globe's center open; a card may move inward only when that edge lane is unavailable, and it may never cover its own connector exit point. The bounded joint assignment considers up to 24 beam states and 32 candidates, avoids existing HUD rectangles, then allows at most three safe exchanges to reduce directional crossings; the previous solver remains a fallback and the work is not a full relayout every frame. A slow source-direction gate also checks whether the actual card target reverses more than 90 degrees from the source before committing a change. Manual drag and hover locks take priority. The source point itself remains a per-frame visual connection target; fixed objects track live positions while cards remain draggable. Rear-side objects retain a globe-outline direction cue, a subdued connector, and a hollow source point; these are orientation guides rather than geographic points. Country candidates are capped at 96 with a hard limit of 128 and cached, so GeoJSON is not scanned every frame. Compute cards keep their original point independent from dynamic source-point movement, and the shared card endpoint slides along the obstacle perimeter. Cable presentation prefers the true ray-hit local point, then samples highlighted geometry by `cableId`. Mobile keeps the existing mini popup and opens drawer details. A stale asynchronous presentation must never dismiss a newer selection.
`configVisible` and `cardsEnabled` are separate. X/Escape closes only configuration; disabling the switch releases connectors and restores the previous map query/layer state. Configuration and source windows bind HUD typography directly so reparenting cannot enlarge their text. The main card presents the selected compute metric as a large share percentage or an absolute value with unit, observation year, historical context, and source entry. Missing comparable data is explicit; no empty trend chart is rendered without observations. Facility records and coverage remain complete in the expanded auxiliary area, where sample counts are evidence context rather than a compute value or global coverage rate. Long lists open only when needed. Mobile preserves the same single metric meaning in its accordion. Local position/size persistence and reset-to-auto remain available. Stable DOM, dimensions, and reading state survive updates, and stale responses cannot re-enable cards.
Motion presentation uses the shared `main.js` input state. Sustained drift or crossings trigger a joint assignment, rather than each card competing independently for leftover slots. `callout-region-path.js` provides the short connection and terminal stub used both for rendering and intersection scoring. Candidate paths are precomputed and layout states are cloned only after pruning. Existing cards remain visible during planning; queued reflows survive frame throttling.
The shared `CalloutConnector` calculates polyline length from its points instead of reading `getTotalLength()` after each SVG write, avoiding repeated synchronous layout during motion. Initial draw animation is retained, so existing cruise presentations receive the same optimization.
On mobile, compute uses a compact regional overview in the existing drawer. Rows are an in-place accordion: tapping the same header collapses it, tapping another collapses the previous row, and at most one expanded card is shown. The expanded card keeps trends, region members, facilities, media, and sources; there is no region subpage, while Settings remains a separate on-demand page. The shared HUD collapsible panel animation is reused and respects reduced motion. Summary loading remains active when map cards are off; enabling cards shares the map loading event and cancels stale requests. Drawer handlers use `composedPath()` so inner HTML replacement cannot collapse the active row. Facility names remain in a scroll region capped at 240px, with left-aligned wrapping rows and at least 42px touch height.
`/series` retains source-backed annual observations; the default primary metric is global compute-device scale split into basic, intelligent, and supercomputer capacity for 2020–2023. Reviewed absolute observations are 302 for 2022 (`caict2023`) and 435 for 2023 (`caict2024`); Japan's global share is 6%/5%/4%/4% for 2020–2023. Product groups APAC excluding China and Other do not map directly to report Japan or “other”; Europe remains `null` until membership aligns with the report. Bilateral share keeps a fixed US + China denominator. Missing primary values may show the latest reported year (currently 2023), while an explicit 2026 selection stays missing. Unconfirmed comparability does not produce year-over-year change. `/facilities` filters and counts unique canonical country codes in successful collection snapshots; coverage counts remain auxiliary, and are not capacity or a global coverage rate. `compute_catalog.py` groups China, US, Europe, APAC excluding China, and Other, covering 45 canonical country codes; these display groups never become statistical denominators. `earth_geography_policy.py` shares display ownership with map policy, grouping Taiwan/Hong Kong/Macao source subdivisions under China while retaining `source_country`. API review metadata is `reviewed_at=2026-09-22` with the matching version date. An independent Epoch share mode is not implemented. Innovation differences are supplied only when both CN and US are selected.
### Boundary surface attachment
Normal borders, coastlines, claim lines, highlights and landing points use `earth-surface.js` to share the HD sphere UV-triangle surface. Borders draw after the texture but before landing points. Highlight lines are always transparent-queue objects with depth testing, preventing through-globe display or overpainting markers. Geometry is computed and cached at construction, not reprojected per frame. See the [render-layer specification](earth-render-layer-order.md).
### News ticker edge fading
A separate pseudo-element paints the ticker background and horizontal rules, fading to full transparency outside the text bounds. The shell has no rectangular backdrop filter, box shadow, or text mask. The region label sizes to its content and the scrolling text follows directly without fading. Alert states change only the gradient rule color so they cannot reintroduce rectangular shadow edges.
Compute cards directly reuse the shared `.hud-panel` background, border, shadow, and glass treatment. Summary, essentials, and detail areas add no separate black surface. Their headers share inset spacing with the layer panel through `.hud-panel__header--inset` / `.layer-panel-header`; primary numbers remain prominent and essential information stays visible.
Saved placement restoration migrates legacy central positions: only old central coordinate locks without the current position marker return to automatic layout, retaining dimensions. Existing edge positions are preserved and versioned. New drags save `positionVersion=2` and continue to honor deliberate placement. The existing storage key is retained instead of clearing every personalized position.
### First frame and spatial card entrance
`earth-runtime-plugin.mjs` bundles local Earth modules into content-hashed entries and shared chunks for both development and production. The existing import map still pins third-party versions; relative assets and workers retain the `/earth/js/` base. `earth-preload.js` requests the selected full-size map early. `earth-texture-image.js` decodes an ImageBitmap asynchronously and transfers that same image to Three.js without resampling or changing texture color/sampling settings. Replaced and destroyed textures release their bitmaps. The full-resolution surface precedes independent concurrent data layers; data refresh retains it. Versioned boundary geometry, picking indexes and claim lines use HTTP caching and parallel requests. The live player engine is imported on demand; media and realtime connections start after the surface frame.
The root attributes `data-earth-first-frame-ms` and `data-earth-surface-ready-ms` record navigation-to-render timings. Measure cold and warm caches separately; local warm results are not a guarantee for all first-visit networks.
Positioned cards stay hidden while the shared connector draws for 420 ms, then fade in using the HUD 180 ms opacity timing. Targets remain fixed during drawing; subsequent motion does not replay entrance. Reduced-motion preferences skip this animation.
Object clicks, search focus and gesture confirmation share `PresentationController`. Cable selection updates selection/legend state without independently opening the card. InfoCard reveal callbacks are versioned so staging or hiding invalidates older callbacks. Connector completion gates card reveal; cancelled cruises no longer hide a newer selection. Object details reuse the regional-card edge candidates and collision scoring, with dimensions measured while staged. Selecting a mobile search result collapses the search drawer so the focused object, connector and compact card remain visible.

View File

@@ -1,5 +1,7 @@
# Earth Render Layer Order
The compute topic reuses the compute-center Interactable icons, depth behavior, and layer order. Country/year changes replace that layer's data and clear prior selection. Its HUD is screen UI excluded from Earth picking in `main.js`. Regional cards are tethered in screen space; rear anchors do not draw connectors through the globe. Facility details reuse the existing card and mobile details drawer. The existing outer atmosphere shell remains, while the starfield is restored to commit `83a10a6c` (`brightStarOpacity:0.88`; no tint/brightness calibration). The HD overlay is a white `MeshBasicMaterial` with opacity 1 and `toneMapped:false`; `texture-colors.js` selects the r128 `encoding`/`LinearTosRGB` path or r160 `colorSpace`/colorspace fragment. No Phong white light, colored twilight, blue night tint, camera boost, or shoulder curve is added. No new post-processing, GPU draw pass, or high-resolution texture was added; the compute topic adds 45 local SVG flag assets.
This document records the current Earth renderer's layer order and the intent of each layer. When adjusting `renderOrder`, radius offsets, depth strategy, or pointer interaction, update this document accordingly.
Note: the layer control panel order and the registration / startup load order are two separate semantics.
@@ -20,12 +22,12 @@ Note: the layer control panel order and the registration / startup load order ar
| 0.86 | Land/ocean base fill | `country-boundaries.js` | `landAltitudeOffset = 0.32`; ocean `#010609`, land `#080f1b` | Raycast disabled | Base map remains usable even when country borders are off; radius is separated from the base sphere to avoid far-zoom z-fighting. |
| 0.96 | HD Earth texture | `earth.js` | `textureOverlayAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | Surface picking target when visible | HD texture always overlays the land/ocean base fill; radius must stay above the land/ocean base and far enough from the base sphere. |
| 1 | Atmospheric glow and clouds | `earth.js` | Atmosphere / cloud spheres | Not in normal object selection path | Cloud layer controlled by the "Cloud Layer" toggle. |
| 1 | Submarine cables / landing points | `cables.js`, `cable-batches.js` | All cable segments share one `LineSegments`; all landing points use instanced billboards; render order `1` and altitude offset `0.2` are unchanged | Original `Line` / `Sprite` objects retain per-item picking and selection state but no longer draw individually; landing-point sphere occlusion remains, with `depthTest: false` on the batch | Style textures and instance attributes preserve color, pulse, size, visibility, and click behavior. |
| 1 | Submarine cables / landing points | `cables.js`, `cable-batches.js` | All cable segments share one `LineSegments`; all landing points use instanced billboards; render order remains `1`; cable altitude offset stays `0.2`, while landing-point anchors use the HD surface offset `0.48` | Original `Line` / `Sprite` objects retain per-item picking and selection state but no longer draw individually; landing-point sphere occlusion remains, with `depthTest: false` on the batch | Style textures and instance attributes preserve color, pulse, size, visibility, and click behavior. |
| 1.2 | Real terrain | `earth.js`, `terrain.js` | `TERRAIN_CONFIG.baseRadiusOffset` plus terrain displacement | Raycast disabled | Terrain overlays HD texture; temporarily hidden when HD texture is off, restores to prior state when re-enabled. |
| 2.05 | Grid lines | `earth.js` | `CONFIG.earthRadius + 0.14` | Raycast disabled | Low-opacity lines over HD texture. |
| 2.2 | Country borders | `country-boundaries.js` | `lineAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48`; claim lines have no extra lift | Raycast disabled | Line geometry still has its own `renderOrder`, but it shares the exact same radius as the HD texture shell to avoid parallax while the globe rotates. |
| 2.29 | Country border hover glow | `country-boundaries.js` | Hover radius + glow offset | `depthTest: false`, raycast disabled | Additive glow to reinforce border edge and terrain hover visibility. |
| 2.3 | Country border hover line | `country-boundaries.js` | `hoverAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | `depthTest: false`, raycast disabled | Neon red-orange hover line; aligned with the normal borders and HD texture shell to avoid ghosting or floating; China and Taiwan share the same highlight group. |
| 0.97–0.99 | Country borders | `country-boundaries.js` | `lineAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48`; claim lines have no extra lift | Raycast disabled | Line geometry still has its own `renderOrder`, but it shares the exact same radius as the HD texture shell to avoid parallax while the globe rotates. |
| 0.98 | Country border hover glow | `country-boundaries.js` | Same surface mapping, no extra lift | `depthTest: true`, transparent render queue, raycast disabled | Additive glow to reinforce border edge and terrain hover visibility. |
| 0.99 | Country border hover line | `country-boundaries.js` | `hoverAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | `depthTest: true`, transparent render queue, raycast disabled | Neon red-orange hover line; aligned with the normal borders and HD texture shell to avoid ghosting or floating; China and Taiwan share the same highlight group. |
| 3 | Satellite footprint fill / Iridium coverage ring | `satellites.js`, `iridium-footprint-adapter.js` | `GROUND_FOOTPRINT_RENDER_ORDER` | depth-tested; Iridium adapter fill / ring use the same renderOrder | Footprint above land / texture / terrain and country borders, below compute centers and satellites. |
| 3-4.5 | BGP collectors, event rings, and event markers | `bgp.js`, `interactable.js` | Collector markers use `BGP_COLLECTOR_RENDER_ORDER = 4.4`; event markers use `BGP_EVENT_RENDER_ORDER = 4.5`; overlays remain in BGP-owned groups | Screen-space Interactable picking; BGP collectors/events may use collision avoidance where appropriate | BGP collectors share the surface-facility band with vessels; BGP events share the compute-center band. |
| 4.3 | AIS vessel track lines | `vessels.js` | `VESSEL_RENDER_ORDER - 0.1`; `CONFIG.earthRadius + VESSEL_CONFIG.track.altitudeOffset` | Follows vessel selection, not independently picked | Recent track for the selected vessel, below vessel markers. |
@@ -73,7 +75,16 @@ Maintenance rules:
| --- | --- |
| Earth coordinate hover | When HD texture is visible, uses the HD texture overlay as the surface picking target; otherwise uses the Earth base sphere. |
| Country border hover | Converts surface pick coordinates to lat/lon, then uses GeoJSON point-in-polygon; the border hover line itself does not receive raycasts. |
| Country border hover visual | On hover, dims normal border lines and draws no-depth-test glow and solid lines. |
| Country border hover visual | On hover, dims normal border lines and draws depth-tested glow and solid lines below landing points. |
| China / Taiwan hover | `CHN` and `TWN` are grouped in the same hover highlight group; the tooltip still shows the actually-hit feature. |
| Terrain | Acts as a visual layer only; `terrain.raycast` is disabled. |
| Satellites | Uses screen-space satellite picking to prevent footprints or surface layers from blocking satellite clicks. |
### Surface attachment and landing point order
`earth-surface.js` projects geographic vertices onto the high-resolution SphereGeometry UV triangles at geometry construction time. Boundaries, coastlines, claim lines, hover outlines and landing points share this surface mapping. Long edges are subdivided using the surface grid spacing. Landing-point anchors now use the texture shell offset of 0.48; cable line offsets remain 0.2. The draw order is texture 0.96, boundaries/highlights 0.97–0.99, then landing points 1. Hover outlines always use the transparent queue with depth testing, so they do not draw through the globe or over landing markers. Geography is unchanged, and cached geometry is not rebuilt during rotation.
### Startup loading versus drawing order
The selected full-resolution surface loads before independent data requests; boundary construction no longer blocks its first frame. This does not change renderOrder, depth behavior, geometry precision or image resolution. Data refresh retains the surface, with texture disposal limited to successful replacement or scene teardown.

View File

@@ -1,5 +1,9 @@
# Admin Frontend Context
## Compute comparison configuration
The Earth content `compute_comparison` section mounts `ComputeComparisonSettings.tsx`. Structured forms maintain report sources, annual country observations and innovation gaps using the shared Panel, Input, Select, Button and confirmation dialog; operators do not edit JSON. Drafts survive section switches, full-page unload warns about unsaved work, and discarding a draft or restoring defaults requires confirmation. Management data loads on activation. Publishing sends the backend revision to prevent overwriting concurrent edits, and failures preserve the draft. The content area scrolls internally while the heading and publish controls stay in the first viewport. `compute-comparison-model.ts` owns types and same-origin update notifications; `compute-comparison.css` only owns this section's layout.
This document describes the current real structure of the console frontend. The goal is to help future page development, table refactoring, layout governance, and state consolidation quickly find the right entry points.
Related references:

View File

@@ -16,6 +16,10 @@ The refactored rules:
- Earth renders only city-level or better locations.
- Local JSON registries are not runtime candidate sources for compute centers or BGP collectors; persisted location facts live in database dimension tables.
For compute-center display, `precise` and `site` candidates keep their source coordinates. `city`, `estimated`, and `needs_confirmation` records that share a coordinate are separated only in `displayLatLon`: a stable hash of `source` and `source_id` selects an irregular cloud direction and a radius in the 0.4–6 km range. The display offset is deterministic, is neither a circle, grid, nor spiral, and never uses a database row ID or list order. Source coordinates are never written back. Tooltips and detail cards label these points “City-level approximate location (dispersed display)”.
The resolver priority is confirmation cache, source precise coordinate, official registered site, source city/site, then unconfirmed cache. A city-level approximation must never be presented as `precise` or as a guessed headquarters. Official registration helpers are keyed by exact `source` + `source_id`; the current entries cover Gyoukou 2.0/2.2 at JAMSTEC Yokohama and Sophia ALCF (three records across two campuses). Their official access-map centers are site points only, not rack/building survey points, and each candidate keeps the official URL in `source_note`.
## Core Interfaces
```python
@@ -51,7 +55,7 @@ class LocationResolver(Protocol):
def resolve(self, query: LocationQuery) -> ResolverOutput: ...
```
`LocationPipeline.collect_candidates()` returns sorted candidates plus `attempted_queries`; `resolve_best()` returns the best candidate with diagnostics. The default sort key ranks source, precision, and confidence, then deduplicates candidates with the same source and rounded coordinates.
`LocationPipeline.collect_candidates()` returns sorted candidates plus `attempted_queries`; `resolve_best()` returns the best candidate with diagnostics. The default sort key ranks source, precision, and confidence, then deduplicates candidates with the same source and rounded coordinates. Legacy records are reclassified through the same path as `country`, `region`, or `unknown`; non-renderable records remain in the complete `unresolved` list.
## Built-In Resolvers

View File

@@ -269,6 +269,8 @@ Earth at `http://localhost:3000/earth` is the public 3D situational page; no log
A single globe view of: BGP events and observations, satellites and tracks, cables and landing points, compute centers, country boundaries / graticules / high-res tiles / clouds / terrain, news live streams and situational news, search and focus details.
At 500% zoom the viewpoint remains outside the surface. Mouse or single-finger dragging keeps the grabbed surface under the pointer, with auto-rotation suspended while held. Release retains inertia; pausing before release stops it. After a pinch, the remaining finger can continue dragging immediately.
### Layer Control
The right-side layer panel toggles layers. Common layers: graticule, country boundaries, high-res tiles, atmospheric clouds, cables, compute centers, BGP, satellites, AIS vessels, terrain. Satellite tracks are no longer listed as a standalone layer; they are controlled from Settings.
@@ -279,6 +281,8 @@ Dependencies:
- Tracks depend on satellites
- With high-res tiles disabled, the globe shows the base map with edge highlighting
The base-map selector defaults to Original basemap, listed first. The display menu can switch among the available map sources and saves the selection locally. The existing Natural Earth I source and an “Original basemap” option remain available; the archived option is a byte-identical copy of the September 21, 2026 archive; NASA 21K is source archive material only, while runtime delivery stays at one 8K texture. Loading replaces the previous GPU texture only after success, releases the old texture then, keeps it on failure, and ignores stale responses. The switch does not add a rendering layer.
### Legend
The bottom-left legend follows the focused or enabled layer. Covered: cables, satellites, country boundaries, compute centers, BGP, AIS vessels.
@@ -289,15 +293,21 @@ AIS vessel legend colors by type: cargo, tanker, passenger, fishing, military, m
Search finds cables, landing points, satellites, compute centers, BGP events, BGP observers. Results jump to and focus the object.
Click a BGP, compute, vessel, satellite, or cable object on the globe to draw a connector from the object before revealing a nearby detail card. The card follows the source direction toward the nearest usable screen edge, leaving breathing room around the globe; it never covers its own connector exit point. As the globe rotates, the source moves smoothly toward a visible part of the highlight. Fully hidden objects retain an outline direction cue with a lighter line and hollow source point. Cards stay readable and remain draggable; their connection points adjust along the edge automatically. Mobile shows a compact popup first, with full details available in the drawer. Dropdown menus and tool lists retain their existing opening behavior.
### Choosing a News Live Stream
Open the live tab in the media panel and click the current channel to open the search menu. Search covers the complete catalog. The list loads 50 channels at a time and appends another page when you scroll to the bottom. A fixed footer shows the loaded and matching channel counts; a failed request can be retried below the list. The default source is Al Jazeera Mubasher, using its HLS playback URL. Other channels remain subject to availability of their playback service.
Buffering and recoverable playback errors do not switch channels. A manually selected channel remains selected for the current page session; refresh to retry after failure. An automatically started channel may switch only after fatal recovery is exhausted, automatic fallback is enabled, and a playable backup exists. The bundled CCTV-4 source uses WCETV's directly playable America-edition HLS stream. The edition is identified in the channel name because its schedule may differ from the Asia edition. The old bundled CDRM URL is no longer passed to the generic decoder; other custom CCTV-4 URLs remain unchanged.
Source information briefly expands when a channel loads, then collapses with the existing HUD animation. Manual toggles use the same component. Expanded height is measured from actual content; panels dragged to the bottom retain their bottom anchor while animating upward.
### Coordinate Candidate Collection
Compute center and BGP observer detail cards support automatic coordinate-candidate collection. Click the object then use "Collect Coordinate Candidates" or "Re-collect Coordinates". The backend assembles candidates from source coordinates, public-org registry APIs, and online geocoders. When regular sources have no candidate, the current default AI Provider runs one LLM factcheck fallback. BGP observers' stored coordinates only fill query context; they are not returned as candidates.
Candidates preview on Earth directly. Saving a compute-center candidate writes to the `compute_center_locations` dimension table and refreshes the layer immediately. The notification badge at the top-left of the compute-center layer shows the unresolved count; clicking it opens the queue, supports single collection, or "Adopt All" to save the top-confidence candidates from top to bottom. Records without candidates stay in the queue rather than being faked to country centroids.
Candidates preview on Earth directly. Saving a compute-center candidate writes to the `compute_center_locations` dimension table and refreshes the layer immediately. Precise and site candidates keep their source coordinates. City-level, estimated, and confirmation-required records that overlap are dispersed only for display using a stable source + source ID placement; the source coordinate is never changed. Their tooltip and detail card say “City-level approximate location (dispersed display)”. The notification badge at the top-left of the compute-center layer shows the unresolved count; clicking it opens the queue, supports single collection, or "Adopt All" to save the top-confidence candidates from top to bottom. Records without candidates stay in the complete queue rather than being faked to country centroids or headquarters.
Recommended single-object flow:
@@ -399,3 +409,19 @@ Docs supports: category navigation, Markdown rendering, tables and code blocks,
- [Quickstart](/home/ray/dev/linkong/planet/docs/technical/en/quickstart.md)
- [FAQ](/home/ray/dev/linkong/planet/docs/technical/en/faq.md)
- [Intelligent Planet Ops Runbook](/home/ray/dev/linkong/planet/docs/technical/en/ops-runbook.md)
## Compute intelligence
Administrators maintain card data in **Earth content → Compute comparison**. Add a report title, URL, publisher, report year and page/figure locator under Sources, then add country values by observation year, metric and source under Observations. Save and publish applies the complete configuration. Global shares use the world denominator; absolute values use FP32 EFlop/s. Bilateral shares are derived from same-year US and China observations, and regions require every member. Do not mix FP16/FP64 capacity, regional estimates or facility counts. Omit missing countries; enter zero only when the source explicitly reports zero.
Innovation gap observations remain independent US-minus-China values. Referenced reports cannot be removed until their observations are removed or reassigned. Duplicate metric/year rows, missing sources and global shares totaling over 100% are rejected. Concurrent edits return a conflict while preserving the local draft. Restore defaults requires confirmation and restores the bundled report data. Publishing and restoring are audited. Same-origin Earth pages receive a refresh notification; other visible pages check once per minute and hidden pages check when visible again. Reports must still be reviewed manually; configuring a URL does not scrape a report.
Open the analysis icon beside Compute Centers, then enable Show region cards. Connectors draw to their target positions before the cards fade in; subsequent globe motion does not replay entrance. Closing the configuration window or pressing Escape leaves the cards enabled; the analysis icon reopens configuration, while the switch disables the cards. Anchors rotate with Earth while cards remain readable. Dragging or expanding grants reading time; after a substantial change in geographic direction, cards can move together when that materially reduces crossing or detours. Active interaction prevents movement, while manual positions remain preferences rather than permanent pins. Rear regions retain their state without a connector drawn through the globe. Existing layer, news, and media windows remain available.
The picker groups the canonical comparison catalog as China, US, Europe, APAC excluding China, and Other; the groups are display buckets, not statistical denominators. It offers period and metric selection. Bilateral share still uses US + China as its fixed denominator; unavailable measures stay missing for other participants. Regional anchors are label positions, never facility locations. Source buttons open report evidence and facility buttons reuse the existing detail card. Mobile uses a compact regional overview in the drawer. Each row is an accordion: tapping the same header collapses it, tapping another collapses the old row, and at most one long card is open. The expanded card keeps trends, region members, facilities, media, and sources; there is no region subpage. Settings remain a separate on-demand page.
The default national metric is global compute-device scale, covering basic, intelligent, and supercomputer capacity with observations from 2020–2023. The reviewed absolute observations are 302 for 2022 (`caict2023` evidence) and 435 for 2023 (`caict2024` evidence). Japan's global-share observations are 6%/5%/4%/4% for 2020/2021/2022/2023. Product groups APAC and OTHER do not mean the report's Japan or “other” categories, and EUROPE remains missing until its membership matches the report. Bilateral share always uses US + China as its denominator. A missing primary value may show the latest reported year (currently 2023); explicitly selecting 2026 still shows missing and does not backfill. Unconfirmed comparability does not produce year-over-year change. National report metrics remain separate from public facility samples; coverage counts remain auxiliary evidence, never computed capacity or a global coverage rate. TOP500 and Epoch may overlap, so source-record counts are not a global census. The bundled reports were reviewed on 2026-09-22; published overrides use their maintained review date and observations. An independent Epoch share mode is not implemented.
This prototype shows up to six content cards. The main card centers on the selected compute metric: a large share percentage or absolute value with its unit, observation year, historical context, and source entry. Missing comparable data is shown as “No comparable data yet”; a trend is omitted when there are no observations rather than rendered as an empty chart. Facility records and coverage metadata remain complete but move to the expanded auxiliary area; sample counts are evidence context, never a compute value or global coverage rate. Region previews may show member counts, and long lists open only when needed. Desktop compact cards are about 268px wide with content-driven height; expanded cards support user resizing up to 480px. Mobile keeps the same single metric meaning in the one-card accordion rather than promoting facility counts. Local card position and size are saved, with a reset-to-auto action. The content layout leaves room for later image and video integration, but those media data are not connected yet. When outside space is insufficient, cards can travel through the globe's foreground instead of teleporting or shrinking to labels.
When the map cards switch is off, the overview summary still loads. Turning it on shares the map loading event; a newer request cancels the previous one. Drawer event handling follows `composedPath()` so replacing inner HTML cannot accidentally collapse the active row. The shared HUD collapsible panel animation is reused and respects reduced-motion preferences. Facility names remain in a scroll region capped at 240px; rows are left-aligned, wrap long names, and retain at least 42px of touch height.

View File

@@ -60,6 +60,7 @@ This document standardizes terms used across Intelligent Planet, the console, ba
| `traffic_stats` | 流量统计 | Cloudflare Radar traffic statistics |
| `as_stats` | 自治系统统计 | Cloudflare Radar AS statistics |
| `compute_center` | 算力中心 | Aggregated Earth compute-center object |
| `compute_intelligence` | 算力与资源态势 | Compute intelligence: national observations, public samples, history, and sources |
| `generic` | 通用数据 | Generic collector output |
| `generic_records` | 通用记录 | Generic mapping output |

View File

@@ -1,6 +1,20 @@
# `planet.sh` Startup Performance Optimization
## Background
## Windows desktop launcher
The launcher prefers a new Windows Terminal window and explicitly runs PowerShell 7 (`pwsh.exe`), independently of the default Terminal profile. Without PowerShell 7 it falls back to Windows PowerShell; without Terminal it starts the available PowerShell directly. The CMD bootstrap also prefers PowerShell 7. Ordinary entries request elevation; entries already running as administrator still dispatch to the preferred terminal without another elevation request. The child validates its privileges and enters WSL directly, preventing recursive windows. The existing post-restart WSL shell is retained.
On first use, open the actual project folder in Windows Explorer and double-click the repository's `planet.cmd`. It derives the WSL distribution and Linux project directory from its own location, creates `Planet.cmd` on the current user's actual Windows desktop, then follows the normal restart flow. Both `\\wsl$\distribution\...` and `\\wsl.localhost\distribution\...` work. Local Windows-drive checkouts use the default WSL distribution and `wslpath`. Windows resolves the desktop location, including redirected desktops such as OneDrive; WSL, zsh and project dependencies must already be installed.
Subsequent desktop launches use the bound target, request the existing administrator elevation and call `planet.sh restart --allow-lan`. No test ports are embedded: defaults remain frontend 3000, backend 8000 and AI Provider 8010, subject to explicit `DEFAULT_*_PORT` environment overrides. Successful restarts open Earth using the actual `ports.env` values and retain the WSL shell; failures display their error without opening a success page. `scripts/windows/planet-launcher.ps1` owns path resolution, desktop installation and elevation; `scripts/windows/restart.zsh` only bridges to the existing lifecycle script.
Use `planet.cmd --install-only` on Windows to generate or validate a launcher without restarting services. Re-running the repository entry updates the same target's launcher. Other projects and unmanaged files are preserved by choosing a target-specific filename. After moving a checkout, regenerate from its new location; old launchers never guess another directory. Desktop launchers require the scripts in the checkout and are not standalone installation packages.
Do not copy the raw repository `planet.cmd` to the desktop by itself: first installation needs the adjacent `scripts/windows/` directory. On another computer, generate a new launcher from that computer's checkout instead of reusing an old desktop copy. Server paths such as `/home/linkong/planet` and personal workstation paths both work without machine-specific Git edits. Run under the Windows account that owns the WSL distribution; elevating under a different account may expose a different set of registered distributions. Installation itself does not require administrator privileges; elevation is requested for the actual restart.
Run Windows regression checks with `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\harness\test_windows_launcher.ps1`. They cover target resolution, special characters, generated launchers and name collisions. `scripts/harness/test_windows_restart.py` uses disposable command substitutes to check restart arguments, browser opening after success and shell retention after failure. The latter runs in quick-check without starting real services.
## Startup background
Use `zsh ./planet.sh start --non-motion-agent` for daily startup; use `init` when preparing a new environment. When investigating latency, distinguish initial dependency downloads, container readiness, and application initialization using the stage timestamps.

View File

@@ -67,6 +67,8 @@ First startup seeds two default accounts (see `DEFAULT_LOGIN_USERS` in `backend/
Both seed accounts are created with `email_verified = TRUE` and can log into the console immediately. Any other account must either go through the public registration flow described in the Manual, or be created via `./planet.sh createuser`.
An isolated preview database does not receive default accounts when application lifespan is skipped. For a default-account 401, first verify the actual database and account existence. Run the existing `ensure_default_admin_user` against the confirmed database only when needed; it does not reset existing passwords. Verify both `/api/v1/auth/login` and authenticated `/api/v1/auth/me`: identity loading preserves full fields such as `email_verified` and `created_at`, and responses accept existing bootstrap `.local` addresses while registration and email updates stay strict. The AUTH catalog markers below require manually confirmed evidence; generic 401/500 responses alone do not establish these causes.
Specify custom ports:
```bash
@@ -309,7 +311,11 @@ For each newly confirmed cause, update both language tables with a stable code,
<!-- planet-error-catalog:start -->
| Error code | Matching fragments (semicolon-separated) | Cause / established failure | Remedy |
| --- | --- | --- | --- |
| P_WINDOWS_LAUNCHER_TARGET_MISSING | P_WINDOWS_LAUNCHER_TARGET_MISSING | The bound WSL project is unavailable or lacks launcher scripts, commonly after moving a checkout, losing access to a distribution or copying another machine's desktop launcher. | Run the repository planet.cmd from the current checkout to regenerate this machine's desktop launcher and verify the WSL distribution; do not hardcode another machine's path. |
| P_PROXY_EXTERNAL | PLANET_PROXY_EXTERNAL | Docker proxy settings belong to another configuration source or have been edited manually. | Inspect daemon.json, systemd proxy settings and the Planet ownership record before changing ownership; existing settings are preserved. |
| P_AUTH_BOOTSTRAP_MISSING | DEFAULT_LOGIN_USER_MISSING | The active backend database is confirmed to lack default accounts, often in an isolated preview that skipped initialization. | Verify database ownership and run the existing account initializer without resetting existing passwords or clearing data. |
| P_AUTH_PROFILE_INVALID | AUTH_IDENTITY_RESPONSE_INVALID | Login succeeds but the identity response lacks required fields or incorrectly rejects a stored local email address. | Load the complete persisted identity, separate response serialization from registration validation, and run default-account identity regressions. |
| P_EARTH_POLICY_MISSING | EARTH_DISPLAY_POLICY_MISSING | The backend image or deployment directory omitted the shared geography policy required when compute routes import. | Include config/earth-boundary-pov-policy.china-v1.json, the boundary builders and reviewed geometry through both Dockerfile and .dockerignore; rebuild the image without resetting the database. |
| P_PROXY_CHANGED | PLANET_PROXY_CONFIG_CHANGED | Docker proxy settings changed after detection. | Wait for concurrent configuration work to finish and retry without overwriting it. |
| P_PROXY_ROLLBACK | PLANET_PROXY_ROLLBACK_FAILED | Docker or previously running containers could not be restored after a failed proxy update. | Inspect daemon and container logs; the original configuration was restored. Use daemon.json.planet-proxy.bak for manual recovery if necessary. |
| P_PROXY_CONFIG | PLANET_PROXY_CONFIG_FAILED;Docker 构建代理检测失败;Docker 构建代理更新失败 | Automatic proxy detection, validation or service update failed. | Check Python 3, curl, Docker and sudo access; updates attempt rollback and proxy credentials are excluded from error output. |
@@ -472,3 +478,13 @@ uv run pytest backend/tests/test_otp_service.py
- [System Service Control](/home/ray/dev/linkong/planet/docs/technical/en/backend-system-service-control.md)
- [Docker + Compose + Buildx Upgrade](/home/ray/dev/linkong/planet/docs/technical/en/ops-docker-compose-buildx-upgrade.md)
- [Data Collectors](/home/ray/dev/linkong/planet/docs/technical/en/backend-collectors.md)
## Shipping the selectable Earth basemaps
These changes from 0.74.7 add no database migration or dependency-lock update. Restart the existing Bun development services; static/container deployments must rebuild the frontend and relevant images rather than keep an old dist after pulling. New modules, four runtime maps, flags, policy fallback geometry and the shared policy must be delivered together. Committing only already-tracked files omits required new assets.
The old `frontend/public/earth/assets/8k_earth_daymap.jpg` path remains but now contains Natural Earth I. The exact 0.74.7 image is retained at `frontend/public/earth/assets/maps/archived-original-20260921-8k.jpg`, exposed as Original basemap. Pull/start does not delete the archive. Settings without a map-source field use the Original basemap default. `resources/earth/candidates` contains provenance/reference originals and is not a frontend runtime dependency.
High-precision boundary outputs live in a Git-ignored generated directory. Pulling does not deliver them automatically. Outputs lacking the required new policy metadata are rejected in favor of the shipped policy fallback; regenerate and publish the static artifacts through the existing boundary workflow to retain high precision. This fallback does not reduce basemap resolution.
Run `PLANET_HARNESS_DOCKER_SMOKE=1 .venv/bin/python scripts/harness/test_backend_runtime_assets.py` to exercise the actual Docker context and production COPY rules, verify the shared policy/geometry and builder imports, and exclude environment files. This probe does not start the full application stack.

View File

@@ -55,6 +55,7 @@ Once in, verify:
- The live-stream menu searches the complete channel catalog and loads more on scroll; Al Jazeera Mubasher is the default
- Mouse drag, wheel zoom, and the zoom percentage indicator work
- The settings panel can switch rotate / cruise / motion modes; motion settings can select the input source and allowed gestures; view settings can switch hover tooltip content, and satellite settings can toggle real-altitude layering and track display
- The base map defaults to Original basemap, listed first; the display menu can switch and save the map source. Natural Earth I remains available, NASA 21K is source archive material only, and runtime uses one 8K texture at a time.
## 5. Recover a Lost Password
@@ -70,3 +71,9 @@ Open `/forgot-password`, enter your email, receive a code, then enter the code p
- Troubleshooting and configuration questions: [FAQ](/home/ray/dev/linkong/planet/docs/technical/en/faq.md)
- Detailed Earth coordinate candidate flow: see the Earth section in [Intelligent Planet Manual](/home/ray/dev/linkong/planet/docs/technical/en/manual.md)
- Deployment / operations commands: [Intelligent Planet Ops Runbook](/home/ray/dev/linkong/planet/docs/technical/en/ops-runbook.md)
## Try compute intelligence
To maintain card values, an administrator opens **Earth content → Compute comparison**, adds report sources, enters comparable annual country observations, then chooses **Save and publish**. Omit missing countries; facility counts never replace compute shares. Bundled defaults can be restored and Earth cards automatically check published configuration.
Open the analysis icon beside Compute Centers and enable Show region cards. Closing configuration keeps the cards active; the switch controls their visibility. Resize or collapse desktop cards and inspect observed history, sources, and facility details. On mobile, use the Compute drawer tab. National indicators and facility samples are separate. “—” means missing evidence, not zero capacity.

View File

@@ -100,7 +100,7 @@ AIS 船只类采集器和其它 `CollectedData` 采集器的落库路径不同
智能星球国界不再属于采集器体系。它是智能星球静态渲染资产,由控制台 `运维与配置 -> 智能星球内容 -> 国界精度` 维护源配置,并由 `/api/v1/earth/boundaries/*` 构建 `frontend/public/earth/data/boundaries/earth-boundaries-china-pov-v1.pmtiles`。本地没有高精 PMTiles 时,前端会使用仓库内置的低精度 GeoJSON 作为 fallback,不会向 `CollectedData` 写入国界记录。
TOP500 和 Epoch AI 算力数据的公开源不总是提供可用经纬度。Earth 统一算力中心接口在主地图启动链路中只使用源数据自带坐标或 `compute_center_locations` 维表坐标;缺少坐标的记录会进入 `unresolved`,不会通过本地注册表、国家质心或猜测城市自动渲染。用户手动采集候选时,后端会用源字段调用 ROR 组织注册 API 和 Nominatim/OpenStreetMap 在线搜索;候选经前端保存后写入 `compute_center_locations`,后续地图刷新再从维表渲染。
TOP500 和 Epoch AI 算力数据的公开源不总是提供可用经纬度。Epoch 官方 CSV 的 `Location` 可能只有国家或为空,即使行中存在经纬度,也不能仅凭它当作精确 site。Earth 统一算力中心接口在主地图启动链路中只使用有效源坐标或 `compute_center_locations` 维表坐标;不可渲染记录保留在完整 `unresolved` 列表,不通过本地注册表、国家中心或猜测总部自动渲染。`precise`/`site` 保持源坐标;城市级和待确认点只可按 `source` + `source_id` 生成确定性的展示偏移,原坐标不改写。用户手动采集候选时,后端会用源字段调用 ROR 组织注册 API 和 Nominatim/OpenStreetMap 在线搜索;候选经前端保存后写入 `compute_center_locations`,后续地图刷新再从维表渲染。
### 新闻直播频道目录
@@ -491,3 +491,15 @@ POST /api/v1/datasources/trigger-batch
请求体可以传 `source_ids` 精确触发选中项;如果不传 `source_ids`,后端按 `product`、`module`、`is_active`、`run_status`、`collected`、`credential_status` 和 `q` 过滤后触发。接口会跳过禁用源、正在运行且未 `force` 的源,以及未到频率窗口的源,并返回 `triggered`、`skipped`、`failed` 三组结果。
**核心文件**: `backend/app/api/v1/datasources.py`
## 算力来源合同与专题接口
Epoch 改用官方公开 CSV,保留 FP16 理论性能、原始日期、状态、匿名/取整标记与扩建关系。匿名记录以内容指纹和同值序号保留快照内的多条记录,不声称跨快照识别设施,不参与定位。未知国家保留缺失标记;它们不进入已选国家的统计。TOP500 以系统详情 ID 标识对象,榜期从榜单 URL 读取,性能按表头或单元格单位转换为 FLOP/s 并保留 FP64 LINPACK 口径。公开页面可能只提供部分榜单,不能宣称已取得完整 TOP500。
两个采集器不再生成示例回退。空原文、转换后空结果、结构错误或相对上次快照减少超过 40% 会失败并保留旧快照;写入失败回滚不完整记录。TOP500 身份切换仅在旧位置名称、规范国家完全一致且唯一时复制位置到新系统 ID,旧位置保留用于历史查询,绝不按名次迁移。
`compute_history_defaults.json` 提供原有报告数据,`compute_configuration.py` 从 `system_settings` 的 `compute_comparison` 分类读取完整数据库覆盖;没有记录时使用默认值,不需要迁移或人工初始化。`compute_history.py` 只处理同年聚合,参考期与发表时间分开;未知具体发表日保持未提供。`/api/v1/data-products/compute/series` 支持全球份额、固定中美分母的双边份额及 FP32 绝对量,逐次读取配置并返回 `Cache-Control: no-store`。创新分差独立返回,不反推单国指数。空配置保留缺失,不回填默认观测。
同前缀的 `GET/PUT /configuration` 和 `POST /configuration/reset` 仅允许 admin/super_admin。写入体为 `{revision, config}`,重置只需要 `revision`。Pydantic 校验国家目录、有限数值、比例总和、重复指标年份和报告引用;每个指标年份只允许一个报告口径。PostgreSQL 唯一分类与行锁串行化首次写入和更新,旧版本返回 409。审计记录和配置在同一事务提交;重置移除覆盖,下次读取随版本默认值。`version` 为配置修订标识,`reviewed_at` 为录入的核验日期,不能将管理员配置一概称为官方报告。来源链接只作引用,不触发抓取。
`/api/v1/data-products/compute/facilities` 仅读取 `compute_contract=v1` 的成功快照,数据库执行国家过滤和按来源计数。Epoch 默认排除规划、退役、已被替代和疑似重复记录;TOP500 表示榜单观测,跨源仍未合并。历史选择按采集时间截止,不将最近抓到的旧资料冒充过去已知的快照。地理结果有返回上限与截断标记,记录统计不受该上限影响。官方 site helper 按精确 `source` + `source_id` 匹配;access map 中心只表示 site 级参考点,并在 `source_note` 保留官方 URL。

View File

@@ -1,5 +1,15 @@
# 智能星球前端结构
## 地表贴图资源记录
2026-09-21,`assets/8k_earth_daymap.jpg` 替换为 Natural Earth I 配套栅格的 8192×4096 Web 派生图,加载路径和渲染代码未变。源图为 EPSG:4326、16200×8100;旧 JPG 在仓库 `resources/earth/archive/2026-09-21/` 逐字节保留。来源、许可、源包/目录版本差异、SHA256、JPEG 参数及按导出尺寸重算的定位文件见 `resources/earth/natural-earth-1/`。原始大文件位于 Git 忽略的 `downloads/earth/`,不进入前端发布包。
底图选择器默认使用“原始底图”,并将该选项放在首位,显示菜单可切换并在本地保存选择。已有 Natural Earth I 保留可用,并增加“原始底图”选项,使用 2026-09-21 归档原图的逐字节副本;NASA 21K 只作为源归档,运行时统一使用一张 8K 贴图。新贴图成功加载后才替换并释放旧 GPU 纹理,失败时保留旧纹理,过期响应不会覆盖当前结果;这是源切换,不新增渲染层。物理贴图为 JPEG sRGB,未烘焙政治边界,也未完成逐段边界对齐验收。星空逐字节恢复到上次提交前的 commit `83a10a6c`:`celestial.js` 与该提交一致,删除 `skyTint`、`skyBrightness`,`brightStarOpacity` 恢复为 `.88`,不再校准星空密度和颜色。高清 overlay 使用白色 `MeshBasicMaterial`、opacity 1、`toneMapped:false`,由 `texture-colors.js` 统一 sRGB 读写;r128 使用 `encoding`/`LinearTosRGB`,r160 使用 `colorSpace`/colorspace fragment。不叠加 Phong 白光、彩色暮光、蓝色夜染、`cameraBoost` 或 shoulder 曲线;daylight 中性亮度不超过原图最大值,dark 仅做 neutral 缩放,原图资源不变。Natural Earth 原图本身偏浅,不声称恢复不存在的细节。为 WebGL1 的 `fwidth` 开启 derivatives。
昼夜明暗使用以太阳地平线为中心的窄平滑过渡(法线点积 ±0.12)。日面保持原图亮度上限;夜面线性光照系数为 0.24,原图暗部最高保留 0.60,按源亮度连续过渡,以保持暗色海洋和森林细节。只使用中性标量,不增加曝光、彩色罩层、额外渲染通道或纹理。
地表底图与新闻频道复用 `hud-select-menu.js` 的 HUD 浮层、列表、选中标记、键盘导航和定位;`tv-source-menu.js` 仅保留频道文案适配。底图关闭搜索、分页和计数,菜单通过触发按钮归属参与移动抽屉的内部点击判断。
本文件描述当前智能星球前端的真实结构,重点是帮助后续继续改 HUD、图层、媒体面板、真实地形、BGP 可视化时,不再重复踩结构和状态同步上的坑。
相关规则建议一起参考:
@@ -9,6 +19,8 @@
## 当前目标
算力卡片的年度观测由 `/api/v1/data-products/compute/series` 返回,后台“算力对比”配置为唯一维护入口。`compute-topic.js` 以 no-store 请求读取数据,接收同源 `planet-compute-configuration` 发布通知,并在可见且专题启用时每 60 秒及页面恢复可见时检查更新;刷新观测不重拉设施图层。`destroy()` 释放计时器、通知通道和可见性监听。国家目录、区域完整覆盖条件与缺失语义仍由后端统一处理。
Earth 前端不是普通管理页,它是独立的大屏展示前端。当前产品目标是:
- 维持地球视图的空间感和可读性
@@ -170,7 +182,13 @@ Browser Camera provider 的手势识别管线在 [motion-browser-provider.js](/h
`tv.js` 管理 `media-panel` 里的直播 / 态势新闻 tab。toolbar 打开或切换 TV/新闻时,会通过 `earth:tv-visibility-change` 和 `earth:tv-tab-change` 回写 Earth 设置:面板可见性仍按 desktop/mobile viewport 存在 `views.<scope>.panelVisibility.media-panel`,当前 tab 存在 `shared.mediaPanelActiveTab`,因此刷新页面后能恢复用户上次打开的直播或新闻状态。`closeTransientMobileOverlays()` 这类临时收起会带 `persist:false`,不会覆盖用户偏好。
`tv-source-menu.js` 复用 HUD 面板与图例列表样式,使用 popover 显示搜索、滚动列表和固定计数栏。`tv.js` 按 50 条请求 `/api/v1/tv/streams`,维护当前频道缓存;搜索交给后端完整目录,翻页用 `next_offset`。菜单通过取消请求和请求代数屏蔽过期响应,不能让旧搜索覆盖新输入。刷新目录时通过 `selected_id` 恢复不在第一页的频道。
`tv-source-menu.js` 通过共享 `hud-select-menu.js` 复用 HUD 面板与图例列表样式,使用 popover 显示搜索、滚动列表和固定计数栏。`tv.js` 按 50 条请求 `/api/v1/tv/streams`,维护当前频道缓存;搜索交给后端完整目录,翻页用 `next_offset`。菜单通过取消请求和请求代数屏蔽过期响应,不能让旧搜索覆盖新输入。刷新目录时通过 `selected_id` 恢复不在第一页的频道。
直播恢复策略由 `tv-playback-policy.js` 统一判断:所有非致命 HLS 事件交给播放器恢复;致命网络或媒体错误最多恢复三次。只有后台允许自动回退、用户未手动指定频道且备用源可内嵌播放时才允许换台。`tv.js` 的异步回调核对播放器实例与频道,原生 video 错误不再抢先绕过 HLS 恢复。`tv_streams.py` 将旧内置 CCTV-4 CDRM 地址(含已保存配置中的同一地址)更新为 WCETV 的美洲版 HLS,名称明确版本并保留其他自定义源;HTTP 成功不代表视频可以解码。
已验证支持 CORS 的直播源可显式设置 `use_proxy:false`,由浏览器直连以减少分片转发开销;未指定时仍默认使用 TV 代理。WCETV 内置源使用直连。
直播信息折叠继续复用 `createHUDPanel` 和原有 0.22 秒 CSS 过渡。共享控制器测量正文高度写入 `--hud-body-content-height`,避免 `max-height: none` 到 `0` 无法插值;内容尺寸变化由 ResizeObserver 更新。向上展开的已拖动面板通过尺寸观察补偿顶部位置,动画期间保持底边,不再关闭正文 transition;补偿期间暂停外框的 top 过渡,避免二次插值导致位置漂移。
`brand.js` 管理智能星球 HUD 品牌资源。默认品牌来自静态资源,运行时覆盖值来自 `/api/v1/earth/brand`,上传的图片通过 `/earth-brand-assets/...` 读取。前端必须把 logo/title 图片和文本 fallback 分开处理:图片加载失败时显示文本标题,文本字段为空时使用后端默认值,避免 HUD 品牌区空白。控制台的智能星球内容页负责保存和重置品牌配置,智能星球前端只消费结果。
@@ -427,17 +445,9 @@ asset 图标大小由 `Interactable` 的 `icon.fitSize` 控制。SVG / 图片文
[main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) 只负责在双指捏合缩放和动捕缩放时调用 `setZoomLevel()` 和 `showZoomStatusCapsule()`。鼠标滚轮与缩放按钮的胶囊提示应继续放在 `controls.js`,避免同一种缩放反馈散落在多个模块。
拖拽地球的旋转灵敏度会根据当前缩放连续衰减,而不是按某个缩放阈值分段:
`globe-navigation.js` 统一投影缩放与表面拖动。相机距离采用 `sqrt(R² + (D₀² - R²) / zoom²)`,并保留最小地表净空;100% 视角不变,500% 仍在球外,反向换算也使用同一公式。滚轮、按钮、捏合、搜索与巡航继续共用 `setZoomLevel()`。
```javascript
const scale = THREE.MathUtils.clamp(
Math.pow(zoom, -CONFIG.dragRotationZoomExponent),
CONFIG.dragRotationScaleMin,
CONFIG.dragRotationScaleMax,
);
```
调参入口在 [constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js):`dragRotationFactorBase` 控制基础速度,`dragRotationZoomExponent` 控制放大后的衰减曲线,`dragRotationScaleMin` / `dragRotationScaleMax` 控制上下限。
鼠标和单指在按下时按实际相机视锥选择并固定拖动模式:球体完整放入视口时,使用旧版屏幕位移、缩放灵敏度、0.18 追赶平滑和松手衰减惯性,指针移出球面仍保持原方向;球体放大到被视口裁切且按下命中球面时,才用射线抓点与四元数逐像素跟手。大球拖动移出球面后暂停更新并清除甩动速度,不再用可能反向的最近点代替真实交点;从球外开始则使用屏幕位移拖动。一趟拖动中不切换模式,自转不叠加。双指进入缩放时清除惯性,回到单指时重新判断模式并建立抓点。拖动阈值仍按起点累计位移判断,取消手势不产生惯性。
### `data-status-target`
@@ -528,3 +538,46 @@ SGP4 传播输出是惯性系位置,不能直接当成 Earth 的经纬度固
- [cruise-sequencer.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cruise-sequencer.js)
- [callout-connector.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/callout-connector.js)
- [bgp-cruise-adapter.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp-cruise-adapter.js)
## 算力空间卡片
Earth 原有 Roboto Condensed 字体已随 `assets/fonts/` 本地分发,许可证与来源同目录保存,`css/fonts.css` 统一声明;页面不再等待 Google Fonts 远程样式表。字体家族与字重保持原样。
`compute-topic.js` 管理参考期、口径、参与方和来源,读取后端 `compute_catalog.py` 提供的规范名称、分组与地区标签锚点。当前目录覆盖 45 个已有坐标的参与方,未知历史指标返回缺失。参与方接口不再只接受中美;区域分组不参与分母计算,坐标不能复用为设施定位。
`spatial-callouts.js` 将布局委托给新的 `spatial-callout-controller.js`,按稳定地区 ID 复用 `CalloutConnector` 管理卡片。布局采用事件驱动;新计算尚未完成时保留有效旧矩形,投影 source 连线持续跟随地球。布局参考点改为实际出线方向,因此卡片保持在对象同侧,不会被左上地理点牵到右下角。solver 优先同扇区的邻近位置、较短连线和较少交叉;最多交换三轮空位改善 assignment,不在每帧全量重排。全屏地球允许附近前景位置,正常大小尽量留在球外,不为了完全避开球体而跨越整盘面。慢速模式下,方向明显偏移或连线持续交叉 1.8 秒后才能提议整体重排,两次自动提议至少间隔 8 秒;只有整体代价降低至少 18% 且超过最低收益门槛才采用。手动/惯性仍在 72ms/12px 预算内响应。展开与手动摆放获得 12 秒阅读缓冲;鼠标悬停、键盘焦点及拖动继续锁定,普通点击留下的焦点不永久锁卡。手动位置保持为偏好,缓冲结束且地球出线方向变化至少 45° 后才允许自动调整,旧保存位置不作为永久锁定。背面保留球体轮廓方向提示且不改变卡片身份。本地位置/尺寸保存与“恢复自动布局”由控制器处理。`ResizeObserver` 响应卡片调整尺寸和收起;选择器与来源窗复用共享拖动句柄。
对象展示统一通过 `PresentationController` 及新的 object-callout anchor/layout 路径。点击真实 BGP、算力中心、船只、卫星或海缆对象时,先绘制连接线,再把卡片放到对象旁边。源锚点从实际点击位置或原始区域标签开始;接近遮挡时,随着旋转自动平滑移向仍可见的高亮几何采样。`layoutReferencePoint` 取实际出线点的方向参考,`packDirectionalEdgeCallouts` 优先把卡片放到对应方向的屏幕边缘,给地球中间留空;边缘 lane 不可用时才内收,卡片不能盖住自己的出线点。有界联合分配最多使用 24 个 beam 状态和 32 个候选,避开已有 HUD,再最多三轮安全交换减少方向交叉;旧 solver 作为 fallback,不逐帧全量重排。slow 模式还检查实际卡片目标相对 source 反向超过 90° 后才提交变化;manual/hover 锁优先。source point 仍每帧更新为连线目标;固定对象跟踪动态位置,卡片仍可拖动。完全背面的对象保留球体轮廓方向提示、较淡连线和空心源点,这些是方向引导而非地理点。国家候选最多 96 条、硬上限 128,并使用缓存,不在每帧扫描 GeoJSON。算力卡继续让原始 point 独立于动态 source point,保持阅读稳定;共享卡片端接点沿障碍物周界滑移。海缆展示优先真实 ray-hit 的 local point,再按 `cableId` 采样高亮几何。移动端沿用 mini popup,点击后打开抽屉详情。旧异步 presentation 不能 dismiss 新的 selection。
配置窗的 `configVisible` 与卡片的 `cardsEnabled` 分离;X/Escape 只关闭配置,开关停用时才释放连线并恢复专题查询前的图层状态。配置和来源窗直接绑定 HUD 字号,避免拖到 `container` 后继承大号正文。主卡围绕当前选择的算力指标,显示大号百分比或带单位的绝对量、观测年份、历史上下文和来源入口。缺少可比数据时明确提示,不在没有观测值时绘制空趋势图。设施记录和覆盖信息仍完整保留在展开后的辅助区,样本数只是证据上下文,不能当作算力值或全球覆盖率。长列表仅在需要时展开;手机手风琴保持同一单一占比/绝对量语义。本地位置/尺寸保存与“恢复自动布局”仍可用;无变化的 DOM、尺寸、阅读状态应保留,过时响应不能重新启用卡片。
动捕/地球运动模式由 `main.js` 的 `isDragging`、`activeTouchPoints`、`hasActiveGlobeInertia` 接入。方向错位或交叉触发的是一组卡片联合优化,而非逐卡抢占剩余空位。`callout-region-path.js` 提供短直连加末端短折线;实际绘制、交叉评分及候选搜索共用路径几何。候选路径只计算一次,裁剪候选后才复制布局状态,避免重排期间不必要的分配。新布局计算期间保留现有卡片;队列不会因帧间隔而丢失重排请求。
共享 `CalloutConnector` 直接从折线点计算长度,不在每次写入 SVG 点后调用 `getTotalLength()`,避免多条移动连线触发反复同步布局。原有首次绘线动画保留;这一优化同时适用于原巡航展示。
移动端算力使用现有抽屉里的紧凑地区概览。同一列表采用原地手风琴:重复点击卡头收起,点击另一张先收起旧卡,最多同时展开一张长卡。展开详情保留趋势、区域成员、设施、媒体和来源,不设地区二级页面;设置仍是单独按需页。复用共享 HUD 折叠面板动画,并尊重 reduced-motion。地图卡片开关关闭时仍加载摘要,打开后共享地图加载事件并取消旧请求;抽屉事件使用 `composedPath()`,通过 innerHTML 换节点也不会误收起当前卡片。设施名单使用最高 240px 的内部滚动区域,名称左对齐并正常换行,每行至少保留 42px 触控高度。
`/series` 保留可追溯年度数据;默认主指标为全球计算设备总规模,拆分基础计算、智能计算和超级计算,观察期为 2020—2023。已核对绝对量为 2022 年 302(`caict2023`)和 2023 年 435(`caict2024`);日本全球份额为 2020—2023 的 6%/5%/4%/4%。产品分组 APAC(不含中国)和 OTHER 不能直接套报告里的日本或“其他”;EUROPE 在成员口径对齐前保持 `null`。双边份额固定使用美国加中国分母。主指标缺失时可显示最近报告年份(当前为 2023),明确选择 2026 仍保持缺失;不可比性未确认时不展示同比变化。`/facilities` 按成功采集快照过滤,并按规范目录中的唯一国家编码计数,覆盖 count 仅作为辅助信息,不能当作算力值或全球覆盖率。`compute_catalog.py` 将中国、美国、欧洲、不含中国的亚太和其他作为展示分组,覆盖 45 个规范国家编码;分组不作为统计分母。`earth_geography_policy.py` 与地图配置共用显示归属,台湾、港澳来源分区归入中国,同时保存 `source_country`。API 审计字段为 `reviewed_at=2026-09-22`,版本与该日期一致。独立 Epoch 份额模式尚未实现。宏观报告的统计范围仍按其原始方法说明,创新分差只在中美同时选择时提供。
### 国界贴地与登陆点
普通国界、海岸、主张线、高亮及登陆点使用 `earth-surface.js` 对齐高清球面的 UV 三角形。国界与高亮在贴图之后、登陆点之前绘制,高亮开启深度测试且始终位于透明渲染队列;不再透过球体显示或盖住登陆点。几何构建时计算并缓存,不增加每帧重投影。具体顺序见[渲染图层规范](earth-render-layer-order.md)。
### 新闻栏端部渐变
新闻栏的底色与上下细线由独立伪元素绘制,两端在正文范围之外渐变到完全透明;外框不使用矩形 backdrop-filter、box-shadow 或文字遮罩。地区标签按内容占宽,正文紧接标签,滚动文字不做渐隐。警报状态只改变渐变细线的颜色,避免重新引入矩形阴影边界。
算力卡片外观直接复用共享 `.hud-panel` 的背景、边框、阴影和玻璃效果,摘要、基础信息及详情区不再叠加单独黑底。标题栏与图层面板共用 `.hud-panel__header--inset` / `.layer-panel-header` 的内边距;主数字继续突出,基础信息保持常显。
保存位置恢复时会迁移旧版居中拖放坐标:只解除位于屏幕核心区域、且没有新版位置标记的旧坐标锁,保留宽高;旧版边缘位置保持不动并更新标记。新拖放保存 `positionVersion=2`,以后仍尊重用户的新位置。迁移沿用原存储键,避免因全量清空布局而丢失所有个性化位置。
### 首帧与空间卡片入场
Earth 由 `earth-runtime-plugin.mjs` 将本地模块合并为带内容哈希的入口与共享块,开发服务和正式构建使用同一路径;第三方依赖仍由原 import map 固定版本,`/earth/js/` 的相对资源及 Worker 路径不变。`earth-preload.js` 在应用初始化前请求选中的原尺寸底图,`earth-texture-image.js` 使用 ImageBitmap 异步解码,并把同一个解码结果交给 Three.js;不重采样、不改变色彩空间和采样设置,替换/销毁纹理时释放位图。高清地表先于数据图层就绪,刷新数据保留已有贴图。独立数据图层并行请求,国界版本化资源使用 HTTP 缓存,基础几何、拾取索引与声明线并行读取。直播引擎按需导入,媒体及实时连接在地表首帧后启动。
`html` 的 `data-earth-first-frame-ms` 和 `data-earth-surface-ready-ms` 记录本次导航至首次绘制、完整地表首次绘制的毫秒数;冷缓存与热缓存应分别实测,不能把本机热缓存结果承诺为任意网络下的首访时间。
空间卡片定位后保持隐藏,先复用 `CalloutConnector` 的 420 ms 绘线动画,再按共享 HUD 的 180 ms 淡入时长显示卡片。绘线期间固定目标位置,后续跟随运动不重播入场;系统减少动态效果时直接呈现。
物件点击、搜索定位及手势确认统一通过 `PresentationController`。海缆选择处理器只更新选择与图例,不再绕过控制器提前显示卡片;共享 InfoCard 的显示回调带版本标记,新的隐藏/预布局请求会使旧回调失效。所有连线卡片以绘线完成事件作为显示门槛,取消的旧巡航不能关闭新的选择卡片。物件详情复用算力卡的边缘候选与避让评分,按隐藏状态下测得的真实尺寸定位置;手机搜索结果被选中时收起搜索抽屉,让地图定位、引导线与精简卡片可见。

View File

@@ -12,6 +12,8 @@
## 地表图层栈
算力专题沿用原算力中心 Interactable 图标、深度与层次顺序;国家/年份切换会替换该层数据并清除旧选中状态。专题 HUD 属于屏幕 UI,在 `main.js` 的指针排除列表中,不参与地球 picking;区域数据卡在屏幕空间由地理锚点牵引,背面不画穿球连线,设施详情复用原详情卡和移动端详情抽屉。外层大气壳保持原有实现;星空逐字节恢复到 commit `83a10a6c`,`brightStarOpacity` 为 `.88`,删除 `skyTint`/`skyBrightness`,不再校准密度和颜色。高清 overlay 使用白色 `MeshBasicMaterial`、opacity 1、`toneMapped:false`;`texture-colors.js` 统一选择 r128 的 `encoding`/`LinearTosRGB` 或 r160 的 `colorSpace`/colorspace fragment。不叠加 Phong 白光、彩色暮光、蓝色夜染、camera boost 或 shoulder 曲线。没有新增后处理、GPU draw pass 或高分辨率纹理;算力专题新增 45 个本地 SVG 旗帜资源。
| 顺序 | 图层 | 来源 | 渲染 / 半径策略 | 深度 / 交互策略 | 备注 |
| --- | --- | --- | --- | --- | --- |
| -1000 | 天体背景 mesh | `celestial.js` | 背景球 | 不参与地表拾取 | 位于所有智能星球内容之后。 |
@@ -21,12 +23,12 @@
| 0.86 | 海陆基座填充 | `country-boundaries.js` | `landAltitudeOffset = 0.32`; 海洋 `#010609`,陆地 `#080f1b` | 禁用 raycast | 即使国界线关闭,基座地图仍保持可用;半径与基座球拉开以避免远距 z-fighting。 |
| 0.96 | 高清 Earth 材质 | `earth.js` | `textureOverlayAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | 可见时作为地表拾取目标 | 高清材质始终压过海陆基座填充;半径必须高于海陆基座并与基座球保持足够间距。 |
| 1 | 大气辉光和云图 | `earth.js` | 大气 / 云层球 | 不走普通对象选择路径 | 云图由“大气云图”图层开关控制。 |
| 1 | 海缆 / 登陆点 | `cables.js`, `cable-batches.js` | 海缆全量线段合并为一个 `LineSegments`;登陆点全量使用实例化 billboard;`renderOrder = 1`、半径偏移 `0.2` 保持不变 | 原始 `Line` / `Sprite` 仅作为逐项拾取和选择状态对象,材质不再单独绘制;登陆点仍按球体遮挡判断背面可见性,批量材质 `depthTest: false` | 样式通过线缆样式纹理和登陆点实例属性同步;保留颜色、脉冲、尺寸、显隐与点击语义。 |
| 1 | 海缆 / 登陆点 | `cables.js`, `cable-batches.js` | 海缆全量线段合并为一个 `LineSegments`;登陆点全量使用实例化 billboard;`renderOrder = 1`、海缆半径偏移仍为 `0.2`;登陆点锚点改为高清地表的 `0.48` | 原始 `Line` / `Sprite` 仅作为逐项拾取和选择状态对象,材质不再单独绘制;登陆点仍按球体遮挡判断背面可见性,批量材质 `depthTest: false` | 样式通过线缆样式纹理和登陆点实例属性同步;保留颜色、脉冲、尺寸、显隐与点击语义。 |
| 1.2 | 真实地形 | `earth.js`, `terrain.js` | `TERRAIN_CONFIG.baseRadiusOffset` 加地形位移 | 禁用 raycast | 地形压过高清材质;高清材质关闭时临时隐藏,重新开启后恢复原状态。 |
| 2.05 | 经纬线 | `earth.js` | `CONFIG.earthRadius + 0.14` | 禁用 raycast | 低透明度显示在高清材质上。 |
| 2.2 | 国界线 | `country-boundaries.js` | `lineAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48`;claim 线不再额外抬高 | `depthTest: true`,禁用 raycast | 线层使用独立 line geometry 与 `renderOrder` 控制,但半径与高清材质壳完全一致,避免转动地球时与高清贴图出现视差。 |
| 2.29 | 国界 hover 光晕 | `country-boundaries.js` | hover 半径加 glow 偏移 | `depthTest: false`,禁用 raycast | 用 additive 光晕增强交界边和地形开启时的 hover 可见性。 |
| 2.3 | 国界 hover 实线 | `country-boundaries.js` | `hoverAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | `depthTest: false`,禁用 raycast | 霓虹红橘 hover 线;与普通国界线和高清材质同源半径对齐,避免重影和漂浮感;中国和中国(台湾)共享高亮组。 |
| 0.97–0.99 | 国界线 | `country-boundaries.js` | `lineAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48`;claim 线不再额外抬高 | `depthTest: true`,禁用 raycast | 线层使用独立 line geometry 与 `renderOrder` 控制,但半径与高清材质壳完全一致,避免转动地球时与高清贴图出现视差。 |
| 0.98 | 国界 hover 光晕 | `country-boundaries.js` | 与地表锚点相同,无额外抬高 | `depthTest: true`,透明绘制队列,禁用 raycast | additive 光晕处于登陆点下方,背面由球体遮挡。 |
| 0.99 | 国界 hover 实线 | `country-boundaries.js` | `hoverAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | `depthTest: true`,透明绘制队列,禁用 raycast | 霓虹红橘 hover 线;与普通国界线和高清材质同源半径对齐,避免重影和漂浮感;中国和中国(台湾)共享高亮组。 |
| 3 | 卫星 footprint 填充 / Iridium coverage ring | `satellites.js`, `iridium-footprint-adapter.js` | `GROUND_FOOTPRINT_RENDER_ORDER` | depth-tested;Iridium adapter 的 fill / ring 也使用同一 renderOrder | Footprint 在 land / texture / terrain 和国界线之上,但在算力中心和卫星之下。 |
| 3-4.5 | BGP 观测站、事件扩散圈和事件 marker | `bgp.js`, `interactable.js` | BGP 观测站和事件 marker 均使用 `Interactable` 批量 `THREE.Points`;事件 marker 使用 `BGP_EVENT_RENDER_ORDER = 4.5`;观测站主图标使用 `BGP_COLLECTOR_RENDER_ORDER = 4.4` 和 `BGP_CONFIG.collectorAltitudeOffset = 0.2`;事件 overlay 进入 `bgp-event-overlay-layer`;观测站 halo 和覆盖扇形进入 `bgp-collector-radar-layer` | BGP 事件和观测站都通过 `Interactable` 屏幕空间 picking,并参与同坐标避让 | BGP 观测站主图标与船只同层;BGP 事件与算力中心同层;向外扩散圈、观测站雷达/覆盖动画继续由 BGP 业务逻辑驱动。 |
| 4.3 | AIS 船只轨迹线 | `vessels.js` | `VESSEL_RENDER_ORDER - 0.1`;`CONFIG.earthRadius + VESSEL_CONFIG.track.altitudeOffset` | 跟随船只显隐,不单独参与拾取 | 选中船只后显示最近轨迹,低于船只 marker。 |
@@ -74,8 +76,17 @@ Earth 的地表不是单一 mesh,而是多层近似同心球:基座球、海
| --- | --- |
| 智能星球坐标 hover | 高清材质可见时使用高清材质 overlay 作为地表拾取目标,否则使用智能星球基座球。 |
| 国界 hover | 先把地表拾取坐标转成经纬度,再用 GeoJSON 点面判断;国界 hover 线本身不接收 raycast。 |
| 国界 hover 视觉 | hover 时压暗普通国界线,并绘制无深度测试的光晕和实线。 |
| 国界 hover 视觉 | hover 时压暗普通国界线,并绘制有深度测试的光晕和实线;不覆盖登陆点。 |
| 中国 / 台湾 hover | `CHN` 和 `TWN` 被归到同一个 hover 高亮组;tooltip 仍显示鼠标实际命中的 feature。 |
| 地形 | 只作为视觉层参与,`terrain.raycast` 已禁用。 |
| 卫星 | 使用屏幕空间卫星拾取,避免 footprint 或地表层挡住卫星点击。 |
| 船只 | 不使用对象级 sprite raycast。`main.js` 会在拖动 / 惯性期间跳过 hover picking,平时将正面船只投影到屏幕坐标,用像素半径命中最近船只;点击后可加载轨迹线。 |
### 国界贴地与登陆点顺序
`earth-surface.js` 在几何构建阶段按高清 SphereGeometry 的 UV 三角形计算贴地点,供普通国界、海岸、主张线、高亮和登陆点共用。长线段按地表网格间距细分,避免把理想球面顶点之间的长弦当作地表。高亮始终进入透明队列并启用深度测试:地表贴图 0.96 → 国界/高亮 0.97–0.99 → 登陆点 1。只调整地表附着与遮挡,不改变地理数据或经纬度归属;构建后缓存,不在转动时重新投影几何。
### 启动加载与绘制顺序
运行时优先完成选中的完整高清地表,再启动相互独立的数据图层请求;国界构建不再阻塞地表首帧。该启动优化不改变各图层的 renderOrder、深度策略、几何精度或贴图分辨率。数据刷新不清除当前地表,纹理仅在替换成功或场景销毁时释放。

View File

@@ -1,5 +1,9 @@
# 控制台前端结构
## 算力对比配置
“智能星球内容”的 `compute_comparison` 分区挂载独立 `ComputeComparisonSettings.tsx`,通过结构化表单维护来源、年度国家观测及创新分差,复用现有 Panel、Input、Select、Button 和确认弹窗,不让运营人员编辑 JSON。草稿在同页切换分区时保留,整页离开提示未保存内容;手动重新加载和恢复默认需确认。组件仅激活时读取管理接口,保存使用后端 revision 防止覆盖其他编辑者,失败保留草稿。表单内容区负责内部滚动,标题和发布操作留在首屏。`compute-comparison-model.ts` 集中类型与同源更新通知,`compute-comparison.css` 只负责该分区布局。
本文件描述当前控制台前端的真实结构,目标是帮助后续页面开发、表格改造、布局治理和状态收口时快速找到正确入口。
相关规则建议一起参考:

View File

@@ -16,6 +16,10 @@
- 只有达到城市级或更高精度的位置能渲染到 Earth。
- 本地 JSON registry 不作为算力中心或 BGP 观测站的运行时候选来源;持久事实写入数据库维表。
算力中心展示中,`precise` 和 `site` 候选保持源坐标。多个 `city`、`estimated`、`needs_confirmation` 记录坐标相同时,只在 `displayLatLon` 上分散:使用 `source` 与 `source_id` 的稳定 hash 选择不规则云状方向,并在 0.4–6 km 范围选择半径。偏移确定性与记录顺序无关,不使用数据库行 ID,也不是圆形、网格或螺旋;源坐标永不写回。tooltip 和详情卡标注“城市级近似位置(分散显示)”。
位置优先级为:确认缓存、源精确坐标、官方登记 site、源 city/site、未确认缓存。城市级近似不能标成 `precise`,也不能猜测总部。官方登记 helper 按精确 `source` + `source_id` 匹配;当前覆盖 Gyoukou 2.0/2.2 的 JAMSTEC Yokohama 与 Sophia ALCF,共 3 条、2 个园区。官方 access map 中心只作为 site 点,不表示 rack/building 测绘点,并在 `source_note` 保留官方 URL。
## 核心接口
```python
@@ -51,7 +55,7 @@ class LocationResolver(Protocol):
def resolve(self, query: LocationQuery) -> ResolverOutput: ...
```
`LocationPipeline.collect_candidates()` 返回排序后的候选和 `attempted_queries`;`resolve_best()` 返回最佳候选及诊断信息。默认排序按 source rank、precision rank、confidence,且对同 source 和同坐标候选去重。
`LocationPipeline.collect_candidates()` 返回排序后的候选和 `attempted_queries`;`resolve_best()` 返回最佳候选及诊断信息。默认排序按 source rank、precision rank、confidence,且对同 source 和同坐标候选去重。旧记录也经过同一条路径重新归类为 `country`、`region` 或 `unknown`;不可渲染记录保留在完整 `unresolved` 列表中。
## 内置 resolver

View File

@@ -268,6 +268,8 @@ Base URL 输入框尾端的插头图标会触发轻量连接测试:检查代
在一个地球视图中观察:BGP 事件与观测态势、卫星和轨迹、海缆与登陆点、算力中心、国界线/经纬线/高清材质/云图/地形、新闻直播和态势新闻、搜索和聚焦对象详情。
放大至 500% 时视点仍在地表外。鼠标或单指拖动会让抓住的地表位置跟随指针;按住时不会叠加自转,松手保留惯性,停住再松手则停止。双指缩放后可直接用剩下的一根手指继续拖动。
### 图层控制
右侧图层面板用于打开或关闭图层。常见图层:经纬线、国界线、高清材质、大气云图、海缆、算力中心、BGP 观测、卫星、AIS 船只、地形。卫星轨迹不再作为单独图层出现在图层列表中,改在设置面板里控制。
@@ -278,6 +280,8 @@ Base URL 输入框尾端的插头图标会触发轻量连接测试:检查代
- 轨迹依赖卫星
- 高清材质关闭时,地球显示基座地图和边缘识别效果
底图选择器默认使用“原始底图”,并将该选项放在首位,显示菜单可切换可用底图并在本地保存选择。已有 Natural Earth I 保留可用,并增加“原始底图”选项,使用 2026-09-21 归档原图的逐字节副本;NASA 21K 只作为源归档,运行时统一使用一张 8K 贴图。新贴图成功加载后才替换并释放旧 GPU 纹理,失败时保留旧纹理,过期响应不会覆盖当前结果;切换不新增渲染层。
### 图例
左下角图例会跟随当前聚焦或启用的图层切换。已覆盖:海缆、卫星、国界线、算力中心、BGP、AIS 船只。
@@ -288,15 +292,21 @@ AIS 船只图例按船型显示颜色:货轮、油轮、客船、渔船、军
支持查找海缆、登陆点、卫星、算力中心、BGP 事件、BGP 观测站。结果可快速定位并打开详情。
点击地球上的 BGP、算力中心、船只、卫星或海缆后,先从物件引出连接线,再沿同一方向把详情卡尽量放到屏幕边缘,中间给地球留出呼吸空间;卡片不能盖住自己的出线点。地球转动时,连线起点会自动平滑移向仍可见的高亮部分;整个对象转到背面后,保留球体轮廓上的方向提示,以较淡连线和空心端点区分。卡片位置保持稳定,仍可拖动,接点会随卡片沿边缘调整,无需手动编辑连线。手机上先显示精简卡片,点开后在抽屉中查看完整详情。下拉菜单与工具列表保持原有打开方式。
### 新闻直播源选择
打开媒体面板的直播页,点击当前频道可展开搜索菜单。搜索会检索完整频道库;列表每次加载 50 个频道,滚动到底部自动追加。底部固定显示已加载数量和搜索结果总数,加载失败时可在列表下方重试。默认新闻直播源为 Al Jazeera Mubasher(半岛电视台),使用 HLS 播放地址;其他频道仍取决于各自的播放服务是否可用。
缓冲和可恢复的播放错误不会切换频道。手动选定频道后,本次页面会话始终保留选择;失败时可刷新重试。自动启动的频道只有在致命错误重试耗尽、后台允许自动回退且存在可播放的备用源时才会切换。CCTV-4 内置源使用可直接播放的 WCETV 美洲版 HLS,频道名注明版本,节目编排可能与亚洲版不同;原内置 CDRM 地址不再直接交给通用播放器解码,控制台自定义的其他 CCTV-4 地址不受影响。
播放源信息在加载频道时短暂展开,随后沿用 HUD 的平滑折叠动画自动收起;手动展开和收起使用同一组件。展开高度按实际内容测量,拖动到屏幕底部后向上折叠仍保留动画与底边位置。
### 位置候选采集
算力中心和 BGP 观测站详情卡支持自动采集坐标候选。点击对象后用"自动采集坐标候选"或"重新自动采集坐标"按钮,后端会从源坐标、开放组织注册 API 和在线地理编码中整理候选;常规来源没有候选时使用当前默认 AI Provider 做 LLM factcheck 兜底。BGP 观测站的已存储位置只用于补齐查询上下文,不会作为候选直接返回。
候选可以直接在智能星球预览。算力中心候选点击"保存"后写入 `compute_center_locations` 维表并刷新图层。算力中心图层左上角的通知气泡显示无法渲染的待定位数量;点击查看列表,单条采集候选,或用"一键采用"从上到下保存最高置信候选。没有可用候选的记录会留在列表中,不会被国家中心点或硬编码 hint 伪造位置。
候选可以直接在智能星球预览。算力中心候选点击“保存”后写入 `compute_center_locations` 维表并刷新图层。`precise` 和 `site` 保持源坐标;城市级、estimated、待确认记录重叠时只在展示层按稳定的 source + source_id 分散,源坐标不会改写。tooltip 和详情卡显示“城市级近似位置(分散显示)”。算力中心图层左上角的通知气泡显示完整 unresolved 待定位数量;点击查看列表,单条采集候选,或用“一键采用”从上到下保存最高置信候选。没有可用候选的记录会留在列表中,不会被国家中心点或猜测总部伪造位置。
单个对象的推荐流程:
@@ -398,3 +408,19 @@ AIS 船只图例按船型显示颜色:货轮、油轮、客船、渔船、军
- [快速开始](/home/ray/dev/linkong/planet/docs/technical/zh/quickstart.md)
- [常见问题](/home/ray/dev/linkong/planet/docs/technical/zh/faq.md)
- [智能星球运维手册](/home/ray/dev/linkong/planet/docs/technical/zh/ops-runbook.md)
## 算力与资源态势
管理员可在控制台“智能星球内容 → 算力对比”维护卡片数据。先在“报告来源”录入报告标题、链接、发布机构、报告年份与页码,再在“年度观测”按年份选择指标、来源和国家数值,最后“保存并发布”。占比以全球为分母,绝对量统一为 FP32 EFlop/s;中美双边份额由同年全球份额计算,区域仅在成员数据完整时汇总。不要混入 FP16/FP64、地区估计值或设施记录数;空缺国家应不录入,明确的零值才填 0。
“创新指数分差”独立维护美国减中国的观测,不反推单国指数。来源被观测引用时不能直接移除;重复指标年份、无效来源、占比总和超过 100% 等内容会拒绝发布。多人同时编辑时,后保存者收到冲突提示,草稿保留以便核对。“恢复默认”需要确认,会恢复随版本提供的报告数据;保存和恢复均有操作审计。同源页面收到发布通知后更新,其他正在展示的地球页每分钟检查一次,后台标签页恢复可见时也会检查。数值仍需人工核验报告,配置链接不会自动抓取报告。
点击算力中心图层行的“算力态势分析”打开配置窗,再开启“显示区域卡片”,地球周围会先绘制到目标位置的连接线,随后数据卡平滑出现;再次转动时不会重播入场。关闭配置窗或按 Escape 仅收起配置,卡片继续显示;再次点击分析按钮可重开配置,关闭显示开关才会停用卡片。区域锚点随地球旋转,卡片保持朝向读者;背面区域有状态提示,不绘制穿过地球的连线。其他图层、新闻和媒体窗口保持可用。
选择器按规范目录分为中国、美国、欧洲、不含中国的亚太和其他五组;这些分组只用于展示,不参与统计分母,可改变参考期与指标口径。双边份额的分母仍固定为美国加中国,其他参与方没有该口径时显示缺失。区域中心只用于卡片定位,不代表设施位置。点击来源查看报告,点击设施按钮使用原有详情卡。手机通过底部抽屉的“算力”页查看紧凑地区概览:同一列表中的短卡采用手风琴,重复点击卡头收起,点击另一张先收起旧卡,最多同时展开一张长卡。展开详情保留趋势、区域成员、设施、媒体和来源,不设地区二级页面;设置仍是单独按需页。
默认主指标为全球计算设备总规模,拆分基础计算、智能计算和超级计算,观察期为 2020—2023。已核对的绝对量为 2022 年 302(证据 `caict2023`)和 2023 年 435(证据 `caict2024`)。日本全球份额为 2020/2021/2022/2023 = 6%/5%/4%/4%。产品分组 APAC、OTHER 不能直接等同报告里的日本或“其他”;EUROPE 成员集合尚未与报告对齐,仍显示缺失。双边份额分母固定为美国加中国,不受日本加入影响。主指标缺失时可显示最近有报告的年份(当前到 2023),明确选择 2026 仍显示缺失,不回填最近值。不可比性未确认时不展示同比变化。国家报告指标与公开设施样本独立,覆盖 count 只保留在辅助证据区,不能当作算力值或全球覆盖率。TOP500 与 Epoch 可能重复收录,来源记录数不代表全球设施总数。默认报告核验日期为 2026-09-22;后台发布后以维护的核验日期和实际观测为准。独立 Epoch 份额模式尚未实现。
当前原型最多同时显示六张内容卡。主卡围绕当前选择的算力指标:大号百分比或带单位的绝对量、观测年份、历史上下文和来源入口。缺少可比数据时明确显示“暂无可比数据”;没有观测值时不绘制空趋势图。设施记录和覆盖信息仍完整保留,但移到展开后的辅助区;样本数只是证据上下文,不能当作算力值或全球覆盖率。区域预览可以显示成员数,长列表仅在需要时展开。桌面卡片约宽 268 像素,高度随内容自适应;展开卡片支持用户调整至最高 480 像素。手机手风琴单卡也保持同一占比/绝对量语义,不把设施条数提升为主数字。位置和尺寸保存于本地,并可恢复自动布局。拖动或展开后先留出阅读时间;地区随地球明显转到另一侧或上下方后,卡片才会在能减少连线交叉、绕行的情况下成组平滑换位。鼠标正在卡片上操作时不移动,手动位置不会被立即抢回,也不会永久阻止必要调整。图片、视频等内容可以沿用内容区布局,当前尚未接入这些媒体数据。空间不足时卡片可以经过球体前景移动,不再靠瞬移避让或缩成小标签。
地图卡片开关关闭时仍会加载概览摘要,打开后共享地图加载事件;新请求会取消旧请求。抽屉事件使用 `composedPath()`,即使通过 innerHTML 换节点也不会误收起当前卡片。复用共享 HUD 折叠面板动画,并尊重 reduced-motion。设施名单使用最高 240px 的内部滚动区域,名称左对齐并正常换行,每行至少保留 42px 触控高度。

View File

@@ -60,6 +60,7 @@
| `traffic_stats` | 流量统计 | Cloudflare Radar 流量统计 |
| `as_stats` | 自治系统统计 | Cloudflare Radar AS 统计 |
| `compute_center` | 算力中心 | 智能星球聚合展示对象 |
| `compute_intelligence` | 算力与资源态势 | 国家观测、公开设施样本、历史与来源专题 |
| `generic` | 通用数据 | 通用采集输出 |
| `generic_records` | 通用记录 | 通用映射输出 |

View File

@@ -1,6 +1,20 @@
# planet.sh 启动性能优化
## 背景
## Windows 桌面启动器
启动窗口优先使用 Windows Terminal,并显式运行 PowerShell 7(`pwsh.exe`),不依赖 Terminal 默认配置文件。未安装 PowerShell 7 时回退到 Windows PowerShell;未安装 Terminal 时直接启动可用的 PowerShell。CMD 初始引导同样优先 PowerShell 7。普通入口请求提权,已经以管理员运行的入口也会进入新 Terminal 窗口,但不会再次请求提权;子窗口校验权限后直接执行 WSL,避免递归打开窗口。重启完成后仍保留原有 WSL shell。
首次在 Windows 文件资源管理器中打开实际项目目录,双击仓库根目录的 `planet.cmd`。它从自身位置识别 WSL 发行版和 Linux 项目目录,在 Windows 当前用户的实际桌面生成 `Planet.cmd`,随后进入正常重启流程。支持 `\\wsl$\发行版\...` 与 `\\wsl.localhost\发行版\...`;仓库位于 Windows 本地磁盘时,使用默认 WSL 发行版及 `wslpath` 映射。系统桌面位置由 Windows 获取,兼容不同用户名和 OneDrive 等桌面重定向;WSL、zsh 及项目依赖仍须先准备好。
后续双击桌面副本,使用已绑定的发行版与目录,申请原有管理员权限,并调用 `planet.sh restart --allow-lan`。不写入测试端口;默认前端 3000、后端 8000、AI Provider 8010,显式 `DEFAULT_*_PORT` 环境配置仍有效。成功后按实际 `ports.env` 端口打开 Earth,终端保留 WSL shell;失败只显示错误,不打开成功页面。源文件负责生成入口,`scripts/windows/planet-launcher.ps1` 负责 Windows 路径、桌面安装与提权,`scripts/windows/restart.zsh` 只衔接现有重启流程。
只生成或检查副本而不重启服务,可在 Windows 中运行 `planet.cmd --install-only`。从仓库再次运行会刷新同一目标的副本;已有其他项目或非托管 `Planet.cmd` 时另存带目标标识的文件,不覆盖。目录移动后从新位置重新运行仓库入口,旧副本不会猜测新目录。桌面副本依赖项目中的脚本,不是可脱离仓库运行的安装包。
不要把仓库原始 `planet.cmd` 单独复制到桌面,它需要相邻的 `scripts/windows/` 才能完成首次生成。换电脑时应从新电脑的项目目录重新生成,不能直接沿用旧电脑的桌面副本。服务器的 `/home/linkong/planet` 与个人电脑的其他路径均可使用,不必修改提交到 Git 的路径。应在拥有目标 WSL 发行版的 Windows 账号下运行;若提权时改用其他账号,其 WSL 发行版注册可能不同。首次生成不要求管理员权限,真正重启时才申请提权。
Windows 回归命令为 `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\harness\test_windows_launcher.ps1`,覆盖路径识别、特殊字符、生成副本及名称冲突。`scripts/harness/test_windows_restart.py` 用临时替身验证重启参数、成功打开页面和失败保留 shell,已接入 quick-check,不启动实际服务。
## 启动背景
日常启动使用 `zsh ./planet.sh start --non-motion-agent`;新环境首次准备才需要 `init`。排查耗时时,应区分首次依赖下载、容器就绪和应用初始化,结合阶段日志时间判断。

View File

@@ -67,6 +67,8 @@ docker buildx version
两个默认账号 `email_verified` 为 `true`,可直接登录控制台。任何在该列表之外的账号都必须走公开注册 + 邮箱验证流程(见使用手册),或用 `./planet.sh createuser` 命令式创建。
独立预览库若跳过应用 lifespan,不会自动初始化默认账户。默认账号返回 401 时,先确认后端实际连接的库及账户是否存在;仅在确认缺失后,对该库执行现有 `ensure_default_admin_user`,不会重置已有账户密码。登录验证应同时检查 `/api/v1/auth/login` 和携带访问令牌的 `/api/v1/auth/me`:身份加载保留完整的 `email_verified`、`created_at` 等字段,响应接受既有默认账户的 `.local` 邮箱,注册和修改邮箱仍严格校验。下表 AUTH 标记用于已人工确认的诊断,不能仅凭通用 401/500 推断原因。
可指定端口:
```bash
@@ -309,7 +311,11 @@ OTP 一次性验证码走 Redis,key 格式 `otp:{purpose}:{email}`,TTL 600
<!-- planet-error-catalog:start -->
| 错误编号 | 匹配片段(分号分隔) | 原因/已确认的故障现象 | 处理建议 |
| --- | --- | --- | --- |
| P_WINDOWS_LAUNCHER_TARGET_MISSING | P_WINDOWS_LAUNCHER_TARGET_MISSING | 桌面启动器绑定的 WSL 项目目录不可访问或缺少启动脚本,常见于项目移动、发行版不可用或复制旧机器的副本。 | 从当前项目目录运行仓库 planet.cmd 重新生成本机桌面副本,检查 WSL 发行版;不修改成其他电脑的固定路径。 |
| P_PROXY_EXTERNAL | PLANET_PROXY_EXTERNAL | 当前 Docker 代理由其他配置管理,或已被人工修改,不能安全自动覆盖。 | 检查 daemon.json、systemd 代理及 Planet 管理记录,确认归属后再调整;脚本保留现有配置。 |
| P_AUTH_BOOTSTRAP_MISSING | DEFAULT_LOGIN_USER_MISSING | 已确认当前后端数据库缺少默认账户,常见于跳过初始化的隔离预览。 | 核对数据库归属后运行现有默认账户初始化,不重置已有账户或清空数据库。 |
| P_AUTH_PROFILE_INVALID | AUTH_IDENTITY_RESPONSE_INVALID | 已确认登录成功但身份响应字段缺失,或响应错误拒绝既有本地邮箱。 | 从真实用户记录加载完整身份,区分响应序列化与注册输入校验;运行默认账户身份回归。 |
| P_EARTH_POLICY_MISSING | EARTH_DISPLAY_POLICY_MISSING | 后端镜像或发布目录遗漏共享地理规则,算力路由导入时无法读取必需文件。 | 同步更新 Dockerfile 与 .dockerignore,交付 config/earth-boundary-pov-policy.china-v1.json、国界构建脚本及审核几何;重新构建镜像,不清空数据库。 |
| P_PROXY_CHANGED | PLANET_PROXY_CONFIG_CHANGED | 检测后 Docker 代理配置发生了变化。 | 等待其他配置操作结束后重试,避免覆盖并发修改。 |
| P_PROXY_ROLLBACK | PLANET_PROXY_ROLLBACK_FAILED | 自动代理更新失败后,Docker 或原有容器未能完成恢复。 | 检查 Docker 服务日志和容器状态;原配置已还原,必要时依据 daemon.json.planet-proxy.bak 手动恢复服务。 |
| P_PROXY_CONFIG | PLANET_PROXY_CONFIG_FAILED;Docker 构建代理检测失败;Docker 构建代理更新失败 | 自动代理检测、配置校验或服务更新失败。 | 检查 Python 3、curl、Docker 状态和 sudo 权限;配置更新会尝试回滚,代理凭据不会写入错误输出。 |
@@ -472,3 +478,13 @@ uv run pytest backend/tests/test_otp_service.py
- [系统服务控制](/home/ray/dev/linkong/planet/docs/technical/zh/backend-system-service-control.md)
- [Docker + Compose + Buildx 升级](/home/ray/dev/linkong/planet/docs/technical/zh/ops-docker-compose-buildx-upgrade.md)
- [数据采集系统](/home/ray/dev/linkong/planet/docs/technical/zh/backend-collectors.md)
## Earth 多底图升级交付
从 0.74.7 升级这批改动不新增数据库迁移或依赖锁文件。开发环境按原有 Bun 启动流程重启前后端;静态或容器部署必须重新构建前端及相关镜像,不能只 pull 后继续使用旧 dist。新模块、四张运行底图、国旗、基础国界和共享规则均需进入同一提交;仅提交已跟踪文件会漏掉新资源。
旧的 `frontend/public/earth/assets/8k_earth_daymap.jpg` 路径保留但内容改为 Natural Earth I;0.74.7 的原始图片逐字节保存在 `frontend/public/earth/assets/maps/archived-original-20260921-8k.jpg`,对应菜单“原始底图”。拉取和启动不会删除地图归档。旧设置没有地图选择字段时采用“原始底图”默认值。`resources/earth/candidates` 是来源及对照资料,不属于页面加载路径;不要把这些原图作为前端运行依赖。
高精国界产物位于被 Git 忽略的生成目录,不会随拉取自动交付。旧产物缺少新规则元数据时会被拒绝,页面使用已交付的政策基础国界;需保持高精效果时,应按既有国界构建流程重新生成并发布对应静态产物。底图分辨率不受这一回退影响。
容器资源回归使用 `PLANET_HARNESS_DOCKER_SMOKE=1 .venv/bin/python scripts/harness/test_backend_runtime_assets.py`。它通过真实 Docker 构建上下文验证生产 COPY 规则、共享规则/几何及构建脚本,同时检查环境文件未打包;不启动整套服务。

View File

@@ -55,6 +55,7 @@
- 直播菜单可搜索完整频道库,滚到底部继续加载;默认频道为半岛电视台
- 鼠标拖动、滚轮缩放、缩放百分比提示工作正常
- 设置面板的旋转 / 巡航 / 动捕模式可以切换;动捕设置可以选择输入源和允许识别的动作;视图设置里可以切换悬停提示,卫星相关设置里可以打开或关闭真实高度分层和轨迹显示
- 底图默认使用“原始底图”,并将该选项放在首位;显示菜单可以切换并保存底图源。Natural Earth I 仍保留可用,NASA 21K 只作源归档,运行时同一时间只使用一张 8K 贴图。
## 5. 找回密码
@@ -70,3 +71,9 @@
- 排障与配置疑问:[常见问题](/home/ray/dev/linkong/planet/docs/technical/zh/faq.md)
- 智能星球坐标候选采集流程见 [智能星球使用手册](/home/ray/dev/linkong/planet/docs/technical/zh/manual.md) 的智能星球章节
- 部署 / 运维相关命令:[智能星球运维手册](/home/ray/dev/linkong/planet/docs/technical/zh/ops-runbook.md)
## 试用算力专题
维护卡片数值:管理员打开“智能星球内容 → 算力对比”,先维护报告来源,再填写年度国家观测并“保存并发布”。只录入同年同口径数据,缺失国家不填,设施数量不会替代算力份额。可恢复内置默认值,地球卡片自动检查已发布配置。
在算力中心旁打开“算力态势分析”,开启“显示区域卡片”,选择地区并旋转地球。关闭配置窗会保留卡片,显示开关才控制启停。桌面卡片可调整大小、收起并查看真实历史图;点击来源或设施进入详情。手机使用底部“算力”页。国家指标与设施样本分开,“—”表示缺数据,不代表算力为零。

View File

@@ -16,12 +16,15 @@
## Current Version
- `main` 当前主线历史推导到:`0.16.5`
- `dev` 当前开发分支历史推导到:`0.74.7`
- `dev` 当前开发分支历史推导到:`0.75.2`
## Timeline
| Version | Type | Branch | Commit | Summary |
| --- | --- | --- | --- | --- |
| `0.75.2` | bugfix | `dev` | `v0.75.2` | Windows 启动器优先 Terminal 与 PowerShell 7,修复管理员入口分流,补齐兼容回退与提权测试 |
| `0.75.1` | improvement | `dev` | `v0.75.1` | Windows 启动器自动生成本机桌面入口,后台维护算力比较数据,恢复小球惯性与大球跟手分流并更新 agent 策略 |
| `0.75.0` | feature | `dev` | `v0.75.0` | 四图切换默认原始底图,新增算力区域态势卡片,统一空间连线与重排,优化启动和缩放拖动并补齐地理规则交付 |
| `0.74.7` | improvement | `dev` | `v0.74.7` | Docker 镜像网络故障临时回退,精简副行进度与失败显示,修复数据库端口冲突及启动重试并补齐实际验证 |
| `0.74.6` | improvement | `dev` | `v0.74.6` | 自动检测 Docker 构建代理与直连,统一运维错误诊断,支持跳过 AI Provider 构建并修复 Compose 回退与失败退出状态 |
| `0.74.5` | improvement | `dev` | `v0.74.5` | 恢复模型预设持久化,统一官方目录刷新与连通性检查,修复 M3 思考模式和 OpenCode 协议路由 |

View File

@@ -1,6 +1,6 @@
{
"name": "planet-frontend",
"version": "0.74.7",
"version": "0.75.2",
"private": true,
"packageManager": "bun@1",
"dependencies": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 Panayiotis Lipiridis
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.

View File

@@ -0,0 +1,10 @@
# Country flag assets
This directory contains the 4:3 SVG files used by the Earth compute catalog.
The 45-file subset is copied from
[`lipis/flag-icons`](https://github.com/lipis/flag-icons/tree/v7.3.2/flags/4x3)
release `v7.3.2`.
The upstream project is licensed under the MIT License. See [LICENSE](LICENSE)
for the included license text. Regional comparison cards do not use invented
flags; they continue to use the Earth HUD's outlined Material symbol.

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ae" viewBox="0 0 640 480">
<path fill="#00732f" d="M0 0h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#000001" d="M0 320h640v160H0z"/>
<path fill="red" d="M0 0h220v480H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 266 B

View File

@@ -0,0 +1,81 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-af" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#000001" d="M0 0h640v480H0z"/>
<path fill="#090" d="M426.7 0H640v480H426.7z"/>
<path fill="#bf0000" d="M213.3 0h213.4v480H213.3z"/>
</g>
<g fill="#fff" fill-rule="evenodd" stroke="#bd6b00" stroke-width=".5" transform="translate(1 27.3)scale(1.06346)">
<path d="M319.5 225.8h8.3c0 3.2 2 6.6 4.5 8.5h-16c2.5-2.2 3.2-5 3.2-8.5z"/>
<path stroke="none" d="m266.7 178.5 4.6 5 57 .2 4.6-5-14.6-.3-7-5h-23l-6.6 5.1z"/>
<path d="M290 172.7h19.7c2.6-1.4 3.5-5.9 3.5-8.4 0-7.4-5.3-11-10.5-11.2q-1.4-.1-1.9-1.3c-.5-1.6-.4-2.7-1-2.6-.4 0-.3 1-.7 2.4q-.6 1.3-2 1.6c-6.4.3-10.6 5-10.5 11.1.1 4 .6 6.4 3.4 8.4z"/>
<path stroke="none" d="M257.7 242.8H342l-7.5-6.1h-69.4z"/>
<path d="m296.4 219.7 1.5 4.6h3.5l-2.8-4.6zm-2 4.6 1 4.6h4l-1.5-4.6zm7 0 2.8 4.6h5.9l-4.6-4.6zm-34.5 10.4c3.1-2.9 5.1-5.3 5.1-8.8h7.6q0 3 1.8 3h7.7v-4.5h-5.6v-24.7c-.2-8.8 10.6-13.8 15-13.8h-26.3v-.8h55.3v.8H301c7.9 0 15.5 7.5 15.6 13.8v7h-1l-.1-6.9c0-6.9-8.7-13.3-15.7-13.1-6 .1-15.4 5.9-15.3 13v2.2l14.3.1-.1 2.5 2.2 1.4 4.5 1.4v3.8l3.2.9v3.7l3.8 1.7v3.8l2.5 1.5-.1 3.9 3.3 2.3h-7.8l4.9 5.5h-7.3l-3.6-5.5h-4.7l2.1 5.4h-5l-1.3-5.4h-6.2v5.8H267zm22.2-15v4.6h5.3l-1-4.6H289z"/>
<path fill="none" d="M289.4 211.7h3.3v7.6h-3.3z"/>
<path fill="none" d="M284.7 219.8h3.2v-5.6c0-2.4 2.2-4.9 3.2-5 1.2 0 2.9 2.3 3 4.8v5.8h3.4v-14.4h-12.8zm25.6 3.3h4v3.2h-4zm-2.4-5.3h4v3.1h-4zm-3.9-5.4h4v3.1h-4zm-3.3-4.5h4v3.1h-4z"/>
<path fill="none" d="m298 219.8 4.2.2 7.3 6.4v-3.8l-2.5-1.8v-3l-3.6-2v-3.3l-3.5-1.2V207l-1.7-1.5z"/>
<path d="M315.4 210.3h1v7.1h-1z"/>
<g id="af-a">
<path d="M257.3 186.5c-1.2-2-2.7 2.8-7.8 6.3-2.3 1.6-4 5.9-4 8.7q.1 3 0 5.8c-.1 1.1-1.4 3.8-.5 4.5 2.2 1.6 5.1 5.4 6.4 6.7 1.2 1 2.2-5.3 3-8 1-3 .6-6.7 3.2-9.4 1.8-2 6.4-3.8 6-4.6z"/>
<path fill="#bf0000" d="M257 201.9a10 10 0 0 0-1.6-2.6 6 6 0 0 0-2.4-1.8 5 5 0 0 1-2.4-1.5 4 4 0 0 1-.8-1.5v-2l-.3.3c-2.3 1.6-4 5.9-4 8.7v2.3q.2.8.6 1.3l1.1.8 2.7.7a7 7 0 0 1 2.6 2 11 11 0 0 1 1.8 2.6l.2-.8c.8-2.7.7-5.9 2.6-8.5z"/>
<path fill="none" d="M249.8 192.4c-.5 3.3 1.4 4.5 3.2 5.1 1.8.7 3.3 2.6 4 4.4m-11.7 1.5c.8 3 2.8 2.6 4.6 3.2s3.7 3 4.5 4.8"/>
<path d="m255.6 184.5 1-.6 17.7 29.9-1 .6z"/>
<path d="M257.5 183.3a2 2 0 1 1-4 0 2 2 0 1 1 4 0zm15.2-24h7.2v1.6h-7.2zm0 3.1h7.2v13.8h-7.2zm-.4-5h8c.2-2.7-2.5-5.6-4-5.6-1.6.1-4.1 3-4 5.6z"/>
<path fill="#bd6b00" stroke="none" d="M292.6 155.8c-1.5.6-2.7 2.3-3.4 4.3s-1 4.3-.6 6.1q.1 1 .5 1.5.3.5.6.5.5 0 .7-.3l.2-.8q-.2-3 .3-5.4a8 8 0 0 1 3-4.4q.4-.3.5-.7l-.3-.7q-.7-.5-1.5-.1m.2.4q.6-.2 1 .1l.1.2q0 .1-.3.4a8 8 0 0 0-3.1 4.6 17 17 0 0 0-.3 5.6l-.2.6s0 .1-.2 0q0 0-.4-.3l-.4-1.2q-.4-2.9.7-6 1.1-2.9 3-4z"/>
<path fill="#bd6b00" stroke="none" d="M295.2 157.7q-2.3 1.2-3 4.2a14 14 0 0 0-.3 5.9q.5 2 1.6 2 .5.2.8-.3t.2-1q-.6-2.5-.3-5.1.4-2.6 2.2-4.1.5-.4.5-.8l-.2-.6q-.7-.5-1.5-.2m.2.5q.6-.2 1 0l.1.3-.3.4a7 7 0 0 0-2.4 4.4q-.4 2.8.2 5.2 0 .6 0 .8l-.5.1c-.3 0-1-.5-1.2-1.7-.3-1.7-.2-3.9.3-5.7q.8-2.9 2.8-3.8"/>
<path d="M272.3 187.4h8v11h-8zm.5 17.4h7.7v2.4h-7.7zm-.2 4.1h8v8.7h-8zm-.6 10.5h8.7v4.9H272zm1.1-16.6h7l1.4-2.4h-9.6zm9.4-8.6.1-6h4.8a17 17 0 0 0-4.9 6z"/>
<path fill="none" d="M273.6 196.7c0 1.3 1.5.8 1.5.1v-5.6c0-1 2.4-.8 2.4-.1v6c0 1 1.7.9 1.6 0v-7c0-2.2-5.5-2.1-5.5-.1zm0 13.3h5.7v7h-5.7z"/>
<path d="M277.2 213h2v1h-2zm-3.5 0h2v1h-2zm2-3h1.5v3h-1.5zm0 4h1.5v3.1h-1.5zM244 139c.4 5.5-1.4 8.6-4.3 8.1-.8-3 1-5.1 4.3-8.1zm-6.5 12.3c-2.6-1.3-.7-11.5.3-15.8.7 5.5 2 13.3-.3 15.8z"/>
<path d="M238.4 151.8c4.4 1.5 8-3.2 9.1-8.7-3.6 5-9.5 5-9 8.7zm-3.3 5.1c-3.4-.9-1.4-11.7-.7-16 .7 4.5 3.1 14.5.7 16zm1.2-.3c.2-3.7 3.9-2.7 6.5-4.7-.5 2-2 5.2-6.5 4.7zm-4.2 5c-3.4-1-1.4-12.6-1.6-17.4 1 4.2 4.2 16.3 1.6 17.4zm1.6-.5c2.8.9 6.5-1 6.8-4.3-2.5 1.7-6.3.4-6.8 4.3z"/>
<path d="M229.5 166.7c-3.2.3-1.8-9.6-1.8-18.8 1.2 8.6 4.5 16.5 1.8 18.8z"/>
<path d="M230.7 166.3c2.2 1 6.1-.7 7.2-4.4-4 1.7-6.6 0-7.2 4.4zm25.6-22.2c-.6 4.9-2.6 7.7-5.5 7.2-.8-3 1.6-5 5.5-7.2zm-7.8 12.4c4.9.7 6.6-3 10-7.9-4.7 3.4-10.2 4-10 8z"/>
<path d="M247 156c-2.6-3.2 0-7.3 2-10.7-.4 5.1 1.3 8-2 10.7zm-1 5.3c-.4-3.2 5-3.9 7.4-5.6-.9 1.8-2 6.7-7.5 5.6z"/>
<path d="M244.8 161.3c-3.7-.4-2.2-6.7.5-10.1-1.1 4.8 2 8.1-.5 10.1z"/>
<path d="M242 166.6c-4.2-2-1.5-7.2 0-10.3-.6 4.1 2.8 7.2 0 10.2z"/>
<path d="M242.8 166c2.2 3 6.5-.8 7.4-5.2-3.7 3.1-6.5 2.6-7.4 5.3zm-9.6 20.3c-.4-4.3 2.8-12 .5-16.2-.3-.6.7-2.1 1.4-1.2 1 1.5 2 5.7 2.5 4.1s.5-4.6 2-5.2c1-.3 2.3-.6 1.9 1-.4 1.4-1.2 3.4-.3 3.5.5 0 2-2 3.3-3 1-.8 2.6.6 1 1.8-4.8 4-9.5 5.9-12.3 15.2zm-8.7 64.5c-.6 0-1.3-.3-.6.6 5.7 7 7.3 9 15.6 8 8.3-1.1 10.3-3.4 16.2-6.7a15 15 0 0 1 11.2-1c1.6.5 2.6.5 1.4-.7s-2.5-2.7-4-3.8a18 18 0 0 0-12.7-2.7c-6 1-11.1 4.9-17.2 6.4a25 25 0 0 1-9.9 0zm47.8 12.5c1 .2 1.7 2.2 2.3.9.8-2.3.2-4-.8-3.9-1.2.3-3.1 3-1.5 3z"/>
<path stroke="none" d="M220.6 183q-1.8-2 1-1.9c1.4 0 4.2 1 5.3.1 1-.7.5-3.7 1-5 .2-.9.7-2 2-.2 3.6 5.8 8 12.8 10 19.6 1 3.8 0 9.8-3.4 13.8 0-3.4-1.2-5.7-2.7-8.6-2-3.7-9.1-14-13.2-17.9z"/>
<path d="M235.5 213.4c4 0 4.7-5.3 4.7-6.8-2 .4-5.4 3.7-4.7 6.8zm34.5 51.9c2.8.6 2.7-6.2-.2-9.1 1.3 4.4-2 8.4.1 9zm-1.2-.1c.2 3.2-8-.4-10-3 4.8 2.1 9.8.4 10 3zm-3.5-4.6c.3 3.1-7 .3-9.3-2.1 4.9 1.6 9-.5 9.3 2zm1.3.4c2.9.7 2.4-6.4-.4-8.8 1.4 4.7-1.8 8.1.4 8.8zm-3-4.3c2.9.7 1.2-5.4-.9-7.8.4 4.4-1 7.5 1 7.8zm-1.5 0c.3 3.2-5.4.8-7.6-2.3 4.8 1.5 7.3-.3 7.6 2.3zm-1.5-2.5c1.8-1.3-.1-4.8-3.7-4.6.4 2.1 1.6 5.9 3.7 4.6zm14 14.7c.1 3.2-8 1.6-10.6-1.8 5.2 1 10.3-.8 10.5 1.8zm-32.4-5.8c.3 3.2-8.6-.4-10.8-3.4 4.7 1.6 10.5.8 10.8 3.4zm5.4 1.3c1.9-1.3-1.9-4.7-5-5.5.4 2.1 3 6.8 5 5.6zm.6 2.3c.2 2.9-9.5 1.3-12-1.4 8.3 1.5 11.7-1.1 12 1.4z"/>
<path d="M252.8 268.6c1 2.7-8.3 2-11.6.5 5.3 0 10.8-2.4 11.6-.5z"/>
<path d="M257.1 270.6c1 2.4-7.6 2.4-11.8 1 5.6 0 10.8-3.4 11.8-1zm6.3 1.3c1.6 2.9-7.6 3.1-10.5 1.7 5.2-.7 9.2-4 10.5-1.7zm-10.7-4.9c-2.9 1.8-2.7-3.6-5-7.3 3.6 3.3 7 5.6 5 7.3z"/>
<path d="M257.9 269c-2.4 2.1-4.4-5.3-6.6-9.5 3.6 4 8.8 7.7 6.6 9.4zm6.8 2c-2 2.4-8-7-10.2-12 3.3 3.9 11.8 10 10.2 12zm-5.8 7.2c-1 3.6-16.2-3.4-18-7.1 8.8 4.6 18.2 3.6 18 7zm-48.7-73.8c-.4-.5-1.4 0-1.2 1.1.3 1.5 2.5 9.2 6.3 11.8 2.7 2 17 5.1 23.4 6.5q5.3 1 8.9 5.3a94 94 0 0 0-3-9.8c-1.2-3-4.4-6.2-7.8-6.3-6.1-.3-14.1-.8-20-3.3a16 16 0 0 1-6.7-5.3z"/>
<path d="M245.5 234.9c2 1.4 4.1-3.7 1.7-8.6-.1 4.7-3.8 6.3-1.7 8.6z"/>
<path d="M247.4 239.6c2.7.8 3.5-4 1.8-7.8.3 4.1-4.3 6.6-1.8 7.8z"/>
<path d="M249.5 243.4c2.6 1.3 3.5-3.6 1.7-7.1.2 4.5-3.7 5.9-1.7 7z"/>
<path d="M248.4 243.7c-1 3-7-2.7-8-5.8 3.7 3.7 8.7 3.2 8 5.7z"/>
<path d="M245.7 239c-1.2 3-8.7-5-10.4-8.7 3.7 3.7 11.2 6.5 10.4 8.6z"/>
<path d="M244.2 234.3c-1.2 3.5-9.3-5.8-11.7-9.1 4 3.6 12.6 6.6 11.7 9.1zm-.3-3.4c3-.6-.1-3-3.7-6.9-.1 4.1.5 7 3.7 6.9z"/>
<path d="M239 228.5c1.3-1.3-1.1-1.9-4.1-5.3-.5 2.3 2.8 6.5 4.2 5.3zm14 15.2c1.6 1 2.6-2.3.7-5.2-.5 3.2-2.1 4-.7 5.2zm-34.2-20.3c-3.3 2-8.6-6-10-9.3 2.9 3.8 10.6 7.2 10 9.3z"/>
<path d="M221.7 228c-1.9 2-7.7-3.5-9.7-6.3 3 2.7 10.5 3 9.7 6.3z"/>
<path d="M224.8 232.2c-.6 2.8-9-3.5-11-6.5 3.6 3.5 11.6 3.2 11 6.5z"/>
<path d="M223.5 235.3c-1.3 2.5-8.2-3.8-9.9-7 4.3 3.6 11 4.5 10 7zM220 223c2.1-2.3 1.2-3.4-.4-7-.8 3.7-2.1 5.2.4 7zm2.9 4.3c4 .2 0-4.6-1-8.7.4 4.6-1 8.3 1 8.7z"/>
<path d="M225.4 231.1c2.7-.6 2-4.5-.2-9.2.5 5.1-2.3 8 .2 9.2zm-1 7.7c-1 3-8.8-4-10-6.8 4 3.4 10.7 4.5 10 6.8z"/>
<path d="M229.1 243.6c-1.1 3-9.3-3.2-11.8-6.6 4.9 4 12.4 3.6 11.8 6.6z"/>
<path d="M233.9 248.5c-1.3 4.3-9.9-2.6-12.4-6 5.4 4.2 13 3 12.4 6zm-8-11c2.3 1.1 3.2-5.4 1.9-10.1 0 5-4.7 8.8-2 10z"/>
<path d="M229.8 242.7c2.8.8 2-6.3-.5-11-.3 4.7-2.3 9 .5 11zm5 4.9c3 .1 1-6.1-1.6-9.6.4 4.5-1 9 1.6 9.6zm-5.5 2.6c-1 1.6-3.2-1.3-7-3.5 3.4 1 7.4 2 7 3.5zm-1.8-52.7c3-2.2.7-6.2 0-10-1 3.6-3.4 8.4 0 10zm0 5.3c-4.5-.5-3.8-6.1-4-9.7 1.4 4.9 5 5.7 4 9.8zm.6-.7c3.7-.2 3.5-4.4 3.7-8.6-1.9 3.9-4 4.5-3.7 8.6z"/>
<path d="M228 207.3c-3 .3-4.4-2.6-5-7 2.7 4.1 5.1 2.8 5 7zm1-.3c3.7.5 3-3.8 3-7-1.2 3-4.2 4-3 7z"/>
<path d="M223.2 205.2c.3 2.8 2.1 7.6 5 6.5 1.1-3.4-2.6-4.1-5-6.5z"/>
<path d="M229 212c-1.2-2.4 3-3.7 3.8-6.9.5 4.6.1 7.6-3.8 7zm-11.9-29.2c2.3-2.4.3-6.4-.4-10.2-1 3.6-2.5 8.4.4 10.2zm0 4.6c-4 .5-5-7.7-5.5-11.3 1.4 4.9 6 7 5.5 11.4zm.8 0c2.8-1.5 2.2-4.7 3-7-1.8 2.9-3.6 3.3-3 7z"/>
<path d="M217 192.8c-4.1.3-6.6-8.8-6.8-12.4 1.3 4.9 7.4 7.5 6.9 12.4zm.9-.2c4-.9 3.5-3.5 2.9-7.6-1.3 4.2-3.5 3.3-2.9 7.6z"/>
<path d="M217 198c-4.6.8-4.3-6.6-8-11.9 3.2 4 9 9 8 11.9zm1-.3c3.6.2 4-5.1 3.8-7.3-.9 2.2-5 4.2-3.7 7.4z"/>
<path d="M209.8 192.3c1.7 5.7 4.2 11.4 7.2 11 1.5-3.3-2.9-3.7-7.2-11z"/>
<path d="M218.1 202.4c-1.2-2.5 3-3.7 3.8-6.9.5 4.6.1 7.6-3.8 6.9zm-7.1-3.6c2.5 5.1 3.6 11 7 10.1 1.3-4-3.8-4.8-7-10.1z"/>
<path d="M218.7 208c-1.5-2.8 2.7-3.7 3.8-7.4.5 4.8 0 8.3-3.8 7.3zm7.2-34.5c2.4.6 5-2.1 4.1-6.2-2.8.6-4 3.2-4.1 6.2zm-7.9-2.1c.2 1.2 1.7 1.3 1.2-.4a5 5 0 0 1 0-3.4 8 8 0 0 0 0-4.6c-.4-1-1.8-.4-1.2.4s.7 2.8.2 3.7q-.7 2.2-.2 4.3zm22.9 16c-1 1.3-2.9.4-1.4-1.5 1.2-1.5 3-2.8 3-4.4.2-2 1.3-5 2.4-6.1s2.4.4 1.2 1.2c-1.3.8-2.2 4.4-2.1 5.8-.1 2-2 3.5-3.1 5zm-3-2.3c-1 1.4-2.4.5-1.6-1.7.7-1.5.8-3.5 1.6-4.6 1.2-1.7 3-3.1 4.1-4.2 1.2-1 2 0 1 1a27 27 0 0 0-3.3 4c-1.4 2.2-.8 4-1.8 5.5zm-15.7-7.2c-.1 2 1.5 2.4 1.4-.4 0-3-2.2-5.8-1-10.3.8-2.2.8-6.3.4-8.4s-2-.8-1.3.9c.6 2-.1 5.6-.6 7.5-1.5 5.4 1.2 8 1 10.7zm4.3-11c-.2 1.9-1.8 2-1.3-.5q.6-2.9 0-5.3c-.6-2.1-.4-5.7 0-7.2.5-1.6 2-.7 1.4.5a10 10 0 0 0-.3 5.9c.6 2 .5 4.8.2 6.7zM210.9 204c.8.9 2 .3 1-1-1-1-.7-1.2-1.3-2.4-.6-1.4-.5-2.1-1.2-3-.7-1-1.6 0-1 .7.8 1 .6 1.6 1 2.5 1 1.5.7 2.3 1.5 3.2zm20.4 24.6a9 9 0 0 1 4.4 6.7 16 16 0 0 0 2 7.1c-2-.5-3-3.7-3.3-6.8-.3-3.2-2-4.5-3-7zm5.1 5.9c1.7 3.1 4 4.3 4.2 6.6.2 2.7.4 2.8 1.1 5.4-2-.5-2.5-.7-3-4.7-.3-2.8-2.6-4.7-2.3-7.3z"/>
<path stroke="none" d="M289 263.3c1 1.8 2 4.5 4 4 0-1.3-2.1-2.3-4-4m3 .6c3.7 1.6 7 1.2 7.5 3.6-3.6.4-5-1-7.6-3.6zm-16.1-12.7a14 14 0 0 1 5 7.7 29 29 0 0 0 3.6 7.8 13 13 0 0 1-5.3-7.4c-.7-3-1.6-5.3-3.3-8zm3.1 0c2.8 2.2 5.4 4.8 6.2 7.9.8 2.9 1.3 5.1 3.2 8-3-1.9-4.1-4.7-5-7.8-.7-3-2.5-5.2-4.4-8zm9.2 7.3a1 1 0 0 1 .7-1.2l2.6-.8c1-.3 1.6.4 1.6.9v2q0 .9-.7.9-1.2 0-2.4.7-1 .5-1.5-.5zm10.6 0q0-1-.6-1.2a5 5 0 0 0-2.4-.4q-1.3 0-1.1.6v2.1c0 .8 0 .8.4 1q1.3-.1 2.5.6.9.4 1.1-.6z"/>
</g>
<use xlink:href="#af-a" width="100%" height="100%" x="-600" transform="scale(-1 1)"/>
<g stroke="none">
<path d="M328.5 286.6q-.1 1.8 1 3.1a19 19 0 0 0-13.8 1.1c-1.8.8-4-1-1.9-2.7 3-2.3 9.7-1 14.7-1.5m-57.5 0a7 7 0 0 1-.4 3c4.4-1.7 9.1-.2 13.6 1.6 3 1.3 3.3-1 2.8-1.7a7 7 0 0 0-5-2.9zm3.8-21.7q-2-.7-4 1.4c-4.3 4.2-9.4 8.3-13.5 11.6-1.5 1.3-3 3.7 3.4 6 .3.2 5 2 8 2 1.3 0 1.3 1.8 1 2.3-.5 1-.1 1.4-1.1 2.3-1.1 1 0 2.1 1 1.3 3.6-3.2 9.6-1.1 15.3.7 1.4.4 3.8.3 3.8-1.6s1.5-3.4 2.4-3.5c2.4.4 14 .5 17.5.1 2-.3 2.2 2.9 3.3 4 .8.9 3.7 1.1 5.8.2 4-1.8 10-1.8 12.5 0 1 .7 1.9 0 1.3-.7-.8-1-.7-1.6-1.1-2.4-1-2-.2-2.4.8-2.5 11-1.5 14.6-5.2 11.2-8.3-4.4-3.8-9.2-7.7-13.4-12.2-1.2-1.2-2-1.7-4.3-.7a67 67 0 0 1-25.3 5.9 76 76 0 0 1-24.6-5.8z"/>
<path fill="#bd6b00" d="m326.6 265.5-1.6.4c-9 3.2-17.2 5.4-25.7 5.4-8.3 0-17-2.4-24.9-5.6a2 2 0 0 0-1.5 0q-.8.2-1.3.7a116 116 0 0 1-11.8 10.3c-.7.5-.6 1.8.5 2.2 8.3 3 16.4 8.5 39.6 8.3 23.5-.2 31.8-5.6 39.2-8.1q.8-.3 1.3-1l.1-.8-.6-.8c-4.3-3.5-8.8-6.3-11.8-10.4q-.5-.7-1.5-.5zm0 .5q.9 0 1.1.3c3 4.3 7.7 7 11.9 10.5l.4.7v.4q-.3.5-1 .7c-7.6 2.6-15.7 8-39 8.2-23.2.2-31.2-5.3-39.5-8.3-.8-.4-.7-1.2-.4-1.4q6.4-4.9 11.8-10.4l1.1-.6h1.2a68 68 0 0 0 25 5.6c8.7 0 17-2.2 26-5.3l1.5-.4z"/>
<path d="M269.7 114.6c0-1.4 2-1.5 1.8.4-.3 2.3 4.5 8.3 4.9 12 .3 2.5-1.5 4.6-3.2 6a7 7 0 0 1-6.8.5c-.9-.8-1.7-3.3-1-4.3.2-.3 1.3 3.7 3.7 3.7 3.3 0 6-2.5 6-4.7.2-3.8-5.3-9.8-5.4-13.6m9.5 9.4c.6-.4 1.4 1.3.8 1.7s-1.5-1.3-.8-1.8zm1.5-3.5c-.3.2-.8 0-.7-.2a12 12 0 0 1 3.6-3.3c.4-.2 1 .4.8.7a11 11 0 0 1-3.7 2.8m12.6-10c.3-.6 2.1-1.3 2.6-1.7.4-.5.6.4.4.7-.3.7-1.9 1.7-2.6 1.8q-.6-.1-.4-.7zm4.3.3a8 8 0 0 1 2.5-3.4c.5-.3 1.3 0 1.1.4a9 9 0 0 1-2.9 3.3c-.3.3-.8 0-.7-.3m-3.7 2.7q-.3.5.1.8 1 .3 2 0c.6-.4.3-2.9-.5-1.6-.6.8-1 .6-1.6.8m-7.3 5.6c-1.3-1 .4-2.4 1.7-1.4 2.7 2-4 9.8-7.6 13.4-.7.7-1.3-1-.4-1.9a34 34 0 0 0 6.7-7.6c.4-.5.7-1.6-.4-2.5m15.3-6.6c.1-1-1.6 0-1.6-1.3 0-.7 1.9-1.2 2.7-.4 1.3 1.4.3 3.7-2 3.9-1.8 0-5 2.7-4.5 3.2.5.7 5.4 1.1 8.3.7 1.8-.3 1.4 1.3-.4 1.5s-3.2 0-4.8.6c-2 .5-2.8 3-3.9 4-.2.2-.8-.8-.6-1.2.8-1.2 2-3 3.4-3.6.8-.3-2.4-.4-3.4-.7-.8-.2-.6-1.3-.3-1.9.4-.8 3.4-3.9 4.7-3.8 1.1 0 2.3-.3 2.4-1m5 .2q.8-.9 1.5-1.8c.3-.3.9 0 .8.8-.1.7-1 1.2-1.5 1.7-.5.3-1-.4-.7-.7zm6.5-2.3c.9 0 1 1.6.2 1.8-.6.2-1-1.7-.2-1.8m-2.1 5c0 1.5.7 1.4 2 1.3s2.4 0 2.4-1.2c0-1.3-.7-2.5-1-1.6-.1.8-.3 2.2-.8 1.6-.4-.5-.2-.6-1 .2-.5.5-.5-.2-.8-.6-.2-.3-.8.2-.8.4zm-9.2 7.2c-.3 1.9 0 4.5.9 4.5 1.2 0 3.6-4 4.8-6.2.7-1.2 1.8-1.4 1.3-.1-.7 1.9-.6 6 0 7.2.4.6 3-.6 3.4-1.5.8-1.7.1-4.8.4-6.7.1-1.2 1.3-1.5 1.2-.3l-.1 7.5c0 1 2.9 2.4 3.3-.6.2-1.8 1.2-3.7 0-5.7-.8-1.3 1.1-1.2 2.1.6.7 1.2-.6 3.2-.5 4.7 0 2.4-1.8 3.8-3.1 3.8-1.2 0-2-1.5-3-1.5s-2.2 1.7-3 1.6c-3.6-.2-1.7-5.3-2.8-5.4-1.2 0-2.5 5-4 4.9-1.4-.2-3-4.2-2.3-5.8.5-1.6 1.5-2 1.4-1m16.9-8c-1.7-1 0-3.7.9-2.8 1.6 2 3.2 6.5 4.4 6.9.7.2.6-3.4 1.1-5 .4-1.3 1.8-.9 1.6.7-.1.5-2 6.4-1.8 6.6a47 47 0 0 1 3.3 7.8c.3 1.2-1.1.4-1.3.2-.9-1.4-2.4-6.5-2.4-6.2l-1.7 7.7c-.2 1-1.7.8-1.3-1 .3-1.4 2.3-8.3 2.2-8.6a17 17 0 0 0-5-6.3"/>
<path d="M322 131.2c-.4 0-1.2 1 1.2 1.5 3.1.6 6.6-.5 7.6-3.6 1.3-3.7 2-7.2 2.7-8.5.8-1.5 1.8-1.4 1-3.6-.5-1.7-1.5-1.2-1.7-.3-.5 2.3-2.6 10-3.3 11.3q-1.8 3.8-7.5 3.2"/>
<path d="M328.4 119c-.4-.7-1.2 0-1 .7a1 1 0 0 0 1.2 1c.7 0 2.2.1 2.2-1 0-.8-.7-1.5-1.1-.6q-.8 1.1-1.3 0zm.7-3c-.2.2 0 1.1.3 1a7 7 0 0 0 3.3-.8c.2-.2.1-.7-.2-.7-1 0-2.6 0-3.4.5m8.8 2.3c.8-1.2 2.8-1.3 2 .4l-6.3 12.3c-.8 1.4-1.4.7-.8-.4.7-1.4 4.9-12 5.1-12.3"/>
<path d="M330.2 133c-.2-.8-1.5-2-1.3.2.2 3.8 5.5 2.6 7 1.3s.3 4.3 2.2 4.9c1 .3 3-1.1 4-2.4 2.7-3.5 4.5-8.6 7-12 1-1.4-.5-2.4-1-1.3-2.4 3.8-5.2 11.6-8.3 13.6-2.5 1.6-1.7-2-1.8-3.2-.1-.8-1.1-2-2.4-.9a6 6 0 0 1-3.7 1.2c-.7 0-1.4 0-1.7-1.4"/>
<path d="M339.6 126c0-.3-1.1-.4-1 .7 0 .8 1 1 1.1 1 1.5-1.2-.3-.6-.1-1.8zm-2.3 4.4c-.3 0-.6 1 .2 1.1l3.9-.2c.4 0 .6-.9-.4-.8-1.2 0-2.7-.3-3.7 0zm-62-16.6c.5 0 1.6 1.4 1.5 1.9 0 .2-1.2 0-1.5-.3s-.2-1.6 0-1.6m-5.3 10.4c-1 .6.2 1.7 1 1.2 2.8-1.9 7-3.8 8-7.5.3-1.2 1.4-3.1 2.5-3.5 1-.5 2.6 1.9 3.6 0 .6-1 2.7.7 3.2-.4.6-1.3.3-2 .3-3.4 0-.8-.7-1-1.2.3q-.1 1-.1 1.6-.4.4-1 .2c-.2-.2 0-.7-.6-1q-.4-.1-.8.2c-.7 1.3-1 2.5-2.1 1-.9-1-1.4-3.1-2-.3-.2 1-1.7 2.4-2.6 2.4-1.1 0-.8-3-3.2-2.5-1.3.3-1.2 2.7-1 3.5.3 1.3 4 .4 3.7 1.2-.6 2.7-4.4 5.4-7.7 7m-22.7 13.2c-.1.5.5 1.7 1.1 1.8.6 0 1-1.3.8-1.8-.2-.3-1.8-.3-1.9 0m3.3 4.9c-.4-.4-1.6.7-.6 1.5.5.5 2.5 1.1 3 .2.8-1.2-.7-5.5 0-6 .5-.5 2.8 2.8 4 3 2.7.4 2-4.6 5-4.2 1.9.2 2.1-2.2 1.8-3.8-.2-1.5-2.6-3.6-3.7-4.6-1.4-1.2-2.1 1-1.2 1.6 1.2 1 3.3 2.9 3.6 4.1.1.6-1.4 1.8-2 1.5-1.4-.8-2.6-4-3.8-4.7-.4-.2-1.4.3-1 1.3.6 1.1 3 2.7 3.1 3.9.1 1-1 3.2-1.8 3.2s-3-2.7-3.7-4c-.4-.5-1.5-.5-1.7.4a22 22 0 0 0 .5 5.5c.2 1.6-.9 1.7-1.5 1.1m-4-8.6c-.4.4.8 1.2 1 1 .4-.4 2.1-2.3 1.8-3-.3-.6-2.6-2-3-1.3-.7 1.1 2.2 1.7 1.7 2zm4.1-8.4s.8 2.5 1.4 1.4c.4-.7-1.4-1.4-1.4-1.4m1.2 4c-.2 0-1 .7-.5 1 .8.4 2.9.8 2.4-.7-.3-.9 3.2 0 2.3-2.4a4 4 0 0 0-1.7-1.7c-.4 0-1.5.5-.8.9.5.2 2 1.1 1.5 1.7-.7.6-1.1-.3-1.9-.1-.4 0-.1 1.2-.4 1.5 0 .2-.7-.4-.9-.3zm5.5-9.5a4 4 0 0 0-1.2 2q.1.5.5.5a3 3 0 0 0 1.2-1.9c0-.3-.2-.8-.5-.6m2.8-.3c-.8-1 1-2.6 1.7-.5.5 1.3 5.5 7.9 6.5 10.1.8 1.5 0 2.1-.9 1-2.5-3.2-4.6-7.2-7.3-10.6m5.2.1c.9-1 2.7-3 2.2-4s-1.5-1-1.7-.7c-1 1.3.8 1 .5 1.4q-.8 1.3-1.3 2.6c-.1.3.1.9.3.7m77.8 3.2c-.7-.5.6-3 1.5-2 2.3 2.7 3.4 11.6 4.1 18.3 0 0-1 .9-1 .7 0-3.5-1.5-14.4-4.6-17m-53.1-8.6c-.8-1.8 1.1-2.4 1.4-1.2 1.3 5.8 4.5 10.2 7 14.1.7 1.2 0 2-1.7.8-1.2-.8-2.5-3.9-3-4-1.2-.2-3.8 5-9.1 3.5-1.4-.4-1.3-4.5-1.4-6.3 0-.9 1-1 1 0 0 1.7 0 5.2 2.1 5.4 1.8 0 5.6-2.4 6.4-4.4s-1.9-5.9-2.7-8z"/>
<path d="M344.6 138.4c.4-1.2 6.1-10.8 6.9-12.9.4-1 2 1.8.4 3.3-1.4 1.2-5.5 8-6.3 10.4-.4 1-1.4.5-1-.8"/>
<path d="M354.3 129.3c1-4 3.6.6 1.3 2.8-3.4 3.4-4.5 9.9-10 10.9-1.4.3-4-.7-4.8-1.3-.3-.2.2-1.6 1.1-.9 1.3 1 4.1 1.3 5.6.1a25 25 0 0 0 6.8-11.6m-57 12.7c-.3.3-1 .3-1.1.7-.3 1.4 0 2.2-.3 3.6s-1.3 1.4-1.2.3c0-1.4 1.3-3.5.4-3.6-.6-.1-1-.9-.4-1.3q1.5-.9 2.4-.4.5.3.2.7"/>
<path d="M296.5 140c-1.4 1.4-2.8 1.9-4.1 3.5-.6.6-.5 1.5-.9 2.4-.3.9-1.4 1-1.7.9-.5-.4-.4-2-1-1.2s-.9 2-1.7 2-2-1.5-1.3-1.5c2.3-.3 2.2-2 3-2.2 1-.1 1 1.5 1.7 1.2.4-.2.7-2.1 1.2-2.6 1.5-1.6 2.7-2.4 4.3-3.6.7-.6 1.3.5.5 1.2zm5.3 5c-1.2.2-1 1.7-.6 1.8.5.3 1.4.4 1.7-1.3.2-.7.3 3.5 1.8 1.9 1-1 3.1.2 4-1 .7-.9 1-1.5.4-2.7-.2-.3-1-.2-1 .7s-.5 1.7-1.3 1.6c-.4-.1.2-1.9-.2-2.4a1 1 0 0 0-.7 0c-.3.4.3 2.2-.6 2.4-1.2.2-.6-1.2-1-1.4-1.7-.8-1.8.2-2.5.3zm9-3c.9-.2.6-.2 2-1.3.5-.4.6.8.5 1.3 0 .7-1 .2-1.3.9-.4.9-.2 3-.4 3.8 0 .4-.8.4-.8 0-.2-1 .1-2 0-3.3 0-.4-.5-1.1 0-1.3zm-5-2.5q-.2 1.3-.2 2.3c0 .5 1 .2 1 .1 0-.8.2-2 0-2.3q-.5-.3-.8-.1"/>
<path d="m299.5 130.2-1.4 5.6-2-3.8v3.9l-4.4-5.2 1.5 5.6-4-3.4 2.2 3.8-7-4.5 4.4 5.2-5.6-2.8 4 3.4-9-3.4 8.7 4.3a29 29 0 0 1 12.6-2.6q7.5.1 12.5 2.6l8.8-4.3-9 3.4 4-3.4-5.5 2.8 4.3-5.2-7 4.5 2.2-3.8-4 3.3 1.5-5.5-4.3 5.2V132l-2 3.8z"/>
</g>
</g>
<path fill="#fff" d="m311.3 295-.3 2.6h-.4l-.1-1.8-.5-1.6-.5-1.3-1-1.4.8-2.2a7 7 0 0 1 1.5 2.4 9 9 0 0 1 .5 3.2m7-4.2q0 1-.5 1.5-.3.5-1.3.7l.4 1.5v2l-.1 1.3h-.4l-.1-1.3-.2-1-.4-1-.7-1.4-1-1.7.6-2 1 1q.4.3 1 .3 1.2 0 1.2-1.3h.4v1.4m6.4 4.8-.5 2.1q-.6 0-.8-.7l-.4-1.3-.1-1.7-1 .2a2 2 0 0 1-1.3-.4 1 1 0 0 1-.5-1q0-1.4.7-2.3.8-1 1.5-1.1.7 0 1 .4l.3.9v2q0 1.3.3 1.9 0 .4.8 1m-2-3.5q0-.9-.8-.8l-.6.1q-.2.1-.2.3 0 .5 1 .5zm8.7 3-.3 2.6q-.8-.5-1.4-2l-1.3-4.1-1.8 5.5-.8.7v-2.5q.9-1 1-1.5l.8-1.7.5-2.7h.4l.9 2.7q.3 1 .9 1.6l1 1.4"/>
<path fill="#bf0000" d="M350.8 319.4q.6.6.7 1.2l.4 1.6-.8.1-1-1.5-1.1-1.2-1.7-1.5-2-1.7q-.6-.3-.6-.5l-.3-.8-.2-1.6 2.7 2.2 2.5 2.2zm-9.5-5.8-.2 2H338l.3-2zm8.4 8.9-7.6 2.3-1.3-2 6.5-2-.7-.8-.9-.6a1 1 0 0 1-.4 1l-1 .6a3 3 0 0 1-1.8 0 2 2 0 0 1-1.3-.7 4 4 0 0 1-.7-2.2q0-1.5.9-1.8 1.1-.3 3 .7a8 8 0 0 1 3 2.4zm-5.8-4-.8-.3h-.6l-.5.3v.6l.5.2h.6l.4-.3zm-8-1.6-.5 2-3.2-.3.5-2zm7.5 7.7-1.7.4H340l-1.5-.4q-.5.8-1.5 1.2l-1.6.6-1.2.3-1-2 1.1-.3 1.3-.4.9-.5-1-.5h-.9l-.2.3h-.5q-.8-1.2-.3-2c.5-.8.9-.8 2-1a7 7 0 0 1 2.6-.2q1.2.1 1.5.9.2.3.2.7l-.4 1.2h1.1l1.7-.3zm-8 1.8-1.6.3a3 3 0 0 1-2.2-.4 6 6 0 0 1-1.7-2.6l-.8-2.2a2 2 0 0 0-.8-1l-.9-.5.6-2.1q.9.4 1.4 1l1 1.7.5 1.5 1.1 2.2q.5.4 1 .3l1.7-.2zm-7-7.5-1 1.9-3-.7 1-1.9zm1.8 8.4-7.5.7-.4-2 6.2-.7-.6-.8-1-.6.5-2q1 .6 1.6 1.3.5.8.8 2.1zm-6 1-2.2-.2-1.7-.5-1.3.4h-3.7l-1.2-.3q-.4-.3-.8-1a4 4 0 0 1-1.5 1l-1.7.1h-1.7l.2-2.1h1.7q1.2.1 2.1-.4a2 2 0 0 0 1.3-1.8l.7.1-.1 1.3q0 .4.3.7.4.3 1 .3h1.5q1.5 0 2-.2.9-.2 1-1.1l.1-.4s.3 0 .5-.2l.5-.2v.7l-.3 1.1 2 .5q.1-.3-.1-.7l-.3-.6.1-.3.3-.2 1-.9.5 1v1zm-11.3-8.7-2 1.3-1.3-.9-1.4 1-1.9-1 1.8-1.3 1.5.8 1.5-1 1.8 1m-3 8.2-7.3-1.2.8-2 6.2 1q0-.6-.2-1l-.5-.8 1.6-1.7q.6.8.7 1.6t-.5 2.1zm-6.1-1-1.6-.3q-1.3-.3-1.5-1.2-.3-.9.8-2.8l1.2-2q.4-.7.3-1.2l-.3-.7 2.2-1.6q.4.8.3 1.4 0 .8-.7 1.8l-.8 1.4a6 6 0 0 0-.9 2.2q0 .6.5.7l1.6.4zm-3.8-8-2.5 1.1-1.8-1.7 2.6-1zm-1 6.6-1.6 1.4-1.7.6-2.4-.1-2.8-.7a8 8 0 0 1-3.4-2q-.9-1.2 0-2.2a7 7 0 0 1 2-1.6q1.1-.7 3.8-1.6l.4.5-2.8 1.2q-.8.4-1.3 1t.2 1.6a11 11 0 0 0 6.3 2.2q1.8 0 2.3-.7.4-.4.5-1l.2-1.6 2.5-1.5-.1 1.5a4 4 0 0 1-1 1.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ao" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="red" d="M0 0h640v243.6H0z"/>
<path fill="#000001" d="M0 236.4h640V480H0z"/>
</g>
<path fill="#ffec00" fill-rule="evenodd" d="M228.7 148.2c165.2 43.3 59 255.6-71.3 167.2l-8.8 13.6c76.7 54.6 152.6 10.6 174-46.4 22.2-58.8-7.6-141.5-92.6-150z"/>
<path fill="#ffec00" fill-rule="evenodd" d="m170 330.8 21.7 10.1-10.2 21.8-21.7-10.2zm149-99.5h24v24h-24zm-11.7-38.9 22.3-8.6 8.7 22.3-22.3 8.7zm-26-29.1 17.1-16.9 16.9 17-17 16.9zm-26.2-39.8 22.4 8.4-8.5 22.4-22.4-8.4zM316 270l22.3 8.9-9 22.2-22.2-8.9zm-69.9 70 22-9.3 9.5 22-22 9.4zm-39.5 2.8h24v24h-24zm41.3-116-20.3-15-20.3 14.6 8-23-20.3-15h24.5l8.5-22.6 7.8 22.7 24.7-.3-19.6 15.3z"/>
<path fill="#fe0" fill-rule="evenodd" d="M336 346.4c-1.2.4-6.2 12.4-9.7 18.2l3.7 1c13.6 4.8 20.4 9.2 26.2 17.5a8 8 0 0 0 10.2.7s2.8-1 6.4-5c3-4.5 2.2-8-1.4-11.1-11-8-22.9-14-35.4-21.3"/>
<path fill="#000001" fill-rule="evenodd" d="M365.3 372.8a4.3 4.3 0 1 1-8.7 0 4.3 4.3 0 0 1 8.6 0zm-21.4-13.6a4.3 4.3 0 1 1-8.7 0 4.3 4.3 0 0 1 8.7 0m10.9 7a4.3 4.3 0 1 1-8.7 0 4.3 4.3 0 0 1 8.7 0"/>
<path fill="#fe0" fill-rule="evenodd" d="M324.5 363.7c-42.6-24.3-87.3-50.5-130-74.8-18.7-11.7-19.6-33.4-7-49.9 1.2-2.3 2.8-1.8 3.4-.5 1.5 8 6 16.3 11.4 21.5A5288 5288 0 0 1 334 345.6c-3.4 5.8-6 12.3-9.5 18z"/>
<path fill="#ffec00" fill-rule="evenodd" d="m297.2 305.5 17.8 16-16 17.8-17.8-16z"/>
<path fill="none" stroke="#000" stroke-width="3" d="m331.5 348.8-125-75.5m109.6 58.1L274 304.1m18.2 42.7L249.3 322"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-au" viewBox="0 0 640 480">
<path fill="#00008B" d="M0 0h640v480H0z"/>
<path fill="#fff" d="m37.5 0 122 90.5L281 0h39v31l-120 89.5 120 89V240h-40l-120-89.5L40.5 240H0v-30l119.5-89L0 32V0z"/>
<path fill="red" d="M212 140.5 320 220v20l-135.5-99.5zm-92 10 3 17.5-96 72H0zM320 0v1.5l-124.5 94 1-22L295 0zM0 0l119.5 88h-30L0 21z"/>
<path fill="#fff" d="M120.5 0v240h80V0zM0 80v80h320V80z"/>
<path fill="red" d="M0 96.5v48h320v-48zM136.5 0v240h48V0z"/>
<path fill="#fff" d="m527 396.7-20.5 2.6 2.2 20.5-14.8-14.4-14.7 14.5 2-20.5-20.5-2.4 17.3-11.2-10.9-17.5 19.6 6.5 6.9-19.5 7.1 19.4 19.5-6.7-10.7 17.6zm-3.7-117.2 2.7-13-9.8-9 13.2-1.5 5.5-12.1 5.5 12.1 13.2 1.5-9.8 9 2.7 13-11.6-6.6zm-104.1-60-20.3 2.2 1.8 20.3-14.4-14.5-14.8 14.1 2.4-20.3-20.2-2.7 17.3-10.8-10.5-17.5 19.3 6.8L387 178l6.7 19.3 19.4-6.3-10.9 17.3 17.1 11.2ZM623 186.7l-20.9 2.7 2.3 20.9-15.1-14.7-15 14.8 2.1-21-20.9-2.4 17.7-11.5-11.1-17.9 20 6.7 7-19.8 7.2 19.8 19.9-6.9-11 18zm-96.1-83.5-20.7 2.3 1.9 20.8-14.7-14.8-15.1 14.4 2.4-20.7-20.7-2.8 17.7-11L467 73.5l19.7 6.9 7.3-19.5 6.8 19.7 19.8-6.5-11.1 17.6zM234 385.7l-45.8 5.4 4.6 45.9-32.8-32.4-33 32.2 4.9-45.9-45.8-5.8 38.9-24.8-24-39.4 43.6 15 15.8-43.4 15.5 43.5 43.7-14.7-24.3 39.2 38.8 25.1Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,45 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-br" viewBox="0 0 640 480">
<g stroke-width="1pt">
<path fill="#229e45" fill-rule="evenodd" d="M0 0h640v480H0z"/>
<path fill="#f8e509" fill-rule="evenodd" d="m321.4 436 301.5-195.7L319.6 44 17.1 240.7z"/>
<path fill="#2b49a3" fill-rule="evenodd" d="M452.8 240c0 70.3-57.1 127.3-127.6 127.3A127.4 127.4 0 1 1 452.8 240"/>
<path fill="#ffffef" fill-rule="evenodd" d="m283.3 316.3-4-2.3-4 2 .9-4.5-3.2-3.4 4.5-.5 2.2-4 1.9 4.2 4.4.8-3.3 3m86 26.3-3.9-2.3-4 2 .8-4.5-3.1-3.3 4.5-.5 2.1-4.1 2 4.2 4.4.8-3.4 3.1m-36.2-30-3.4-2-3.5 1.8.8-3.9-2.8-2.9 4-.4 1.8-3.6 1.6 3.7 3.9.7-3 2.7m87-8.5-3.4-2-3.5 1.8.8-3.9-2.7-2.8 3.9-.4 1.8-3.5 1.6 3.6 3.8.7-2.9 2.6m-87.3-22-4-2.2-4 2 .8-4.6-3.1-3.3 4.5-.5 2.1-4.1 2 4.2 4.4.8-3.4 3.2m-104.6-35-4-2.2-4 2 1-4.6-3.3-3.3 4.6-.5 2-4.1 2 4.2 4.4.8-3.3 3.1m13.3 57.2-4-2.3-4 2 .9-4.5-3.2-3.3 4.5-.6 2.1-4 2 4.2 4.4.8-3.3 3.1m132-67.3-3.6-2-3.6 1.8.8-4-2.8-3 4-.5 1.9-3.6 1.7 3.8 4 .7-3 2.7m-6.7 38.3-2.7-1.6-2.9 1.4.6-3.2-2.2-2.3 3.2-.4 1.5-2.8 1.3 3 3 .5-2.2 2.2m-142.2 50.4-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2M419 299.8l-2.2-1.1-2.2 1 .5-2.3-1.7-1.6 2.4-.3 1.2-2 1 2 2.5.5-1.9 1.5"/>
<path fill="#ffffef" fill-rule="evenodd" d="m219.3 287.6-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2"/>
<path fill="#ffffef" fill-rule="evenodd" d="m219.3 287.6-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m42.3 3-2.6-1.4-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .5-2.3 2.1m-4.8 17-2.6-1.5-2.7 1.4.6-3-2.1-2.3 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m87.4-22.2-2.6-1.6-2.8 1.4.6-3-2-2.3 3-.3 1.4-2.7 1.2 2.8 3 .5-2.2 2.1m-25.1 3-2.7-1.5-2.7 1.4.6-3-2-2.3 3-.3 1.4-2.8 1.2 2.9 3 .5-2.2 2.1m-68.8-5.8-1.7-1-1.7.8.4-1.9-1.3-1.4 1.9-.2.8-1.7.8 1.8 1.9.3-1.4 1.3m167.8 45.4-2.6-1.5-2.7 1.4.6-3-2.1-2.3 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m-20.8 6-2.2-1.4-2.3 1.2.5-2.6-1.7-1.8 2.5-.3 1.2-2.3 1 2.4 2.5.4-1.9 1.8m10.4 2.3-2-1.2-2.1 1 .4-2.3-1.6-1.7 2.3-.3 1.1-2 1 2 2.3.5-1.7 1.6m29.1-22.8-2-1-2 1 .5-2.3-1.6-1.7 2.3-.3 1-2 1 2.1 2.1.4-1.6 1.6m-38.8 41.8-2.5-1.4-2.7 1.2.6-2.8-2-2 3-.3 1.3-2.5 1.2 2.6 3 .5-2.3 1.9m.6 14.2-2.4-1.4-2.4 1.3.6-2.8-1.9-2 2.7-.4 1.2-2.5 1.1 2.6 2.7.5-2 2m-19-23.1-1.9-1.2-2 1 .4-2.2-1.5-1.7 2.2-.2 1-2 1 2 2.2.4-1.6 1.6m-17.8 2.3-2-1.2-2 1 .5-2.2-1.6-1.7 2.3-.2 1-2 1 2 2.1.4-1.6 1.6m-30.4-24.6-2-1.1-2 1 .5-2.3-1.6-1.6 2.2-.3 1-2 1 2 2.2.5-1.6 1.5m3.7 57-1.6-.9-1.8.9.4-2-1.3-1.4 1.9-.2.9-1.7.8 1.8 1.9.3-1.4 1.3m-46.2-86.6-4-2.3-4 2 .9-4.5-3.2-3.3 4.5-.6 2.2-4 1.9 4.2 4.4.8-3.3 3.1"/>
<path fill="#fff" fill-rule="evenodd" d="M444.4 285.8a125 125 0 0 0 5.8-19.8c-67.8-59.5-143.3-90-238.7-83.7a125 125 0 0 0-8.5 20.9c113-10.8 196 39.2 241.4 82.6"/>
<path fill="#309e3a" d="m414 252.4 2.3 1.3a3 3 0 0 0-.3 2.2 3 3 0 0 0 1.4 1.7q1 .8 2 .7.9 0 1.3-.7l.2-.9-.5-1q-.3-.5-1.5-1.8a8 8 0 0 1-1.8-3 4 4 0 0 1 2-4.4 4 4 0 0 1 2.3-.2 7 7 0 0 1 2.6 1.2q2.1 1.5 2.6 3.2a4 4 0 0 1-.6 3.3l-2.4-1.5q.5-1 .2-1.7-.2-.8-1.2-1.4a3 3 0 0 0-1.8-.7 1 1 0 0 0-.9.5q-.3.4-.1 1 .2.8 1.6 2.2t2 2.5a4 4 0 0 1-.3 4.2 4 4 0 0 1-1.9 1.5 4 4 0 0 1-2.4.3q-1.3-.3-2.8-1.3-2.2-1.5-2.7-3.3a5 5 0 0 1 .6-4zm-11.6-7.6 2.5 1.3a3 3 0 0 0-.2 2.2 3 3 0 0 0 1.4 1.6q1.1.8 2 .6.9 0 1.3-.8l.2-.8q0-.5-.5-1l-1.6-1.8q-1.7-1.6-2-2.8a4 4 0 0 1 .4-3.1 4 4 0 0 1 1.6-1.4 4 4 0 0 1 2.2-.3 7 7 0 0 1 2.6 1q2.3 1.5 2.7 3.1a4 4 0 0 1-.4 3.4l-2.5-1.4q.5-1 .2-1.7-.4-1-1.3-1.4a3 3 0 0 0-1.9-.6 1 1 0 0 0-.8.5q-.3.4-.1 1 .3.8 1.7 2.2 1.5 1.5 2 2.4a4 4 0 0 1 0 4.2 4 4 0 0 1-1.8 1.6 4 4 0 0 1-2.4.3 8 8 0 0 1-2.9-1.1 6 6 0 0 1-2.8-3.2 5 5 0 0 1 .4-4m-14.2-3.8 7.3-12 8.8 5.5-1.2 2-6.4-4-1.6 2.7 6 3.7-1.3 2-6-3.7-2 3.3 6.7 4-1.2 2zm-20.7-17 1.1-2 5.4 2.7-2.5 5q-1.2.3-3 .2a9 9 0 0 1-3.3-1 8 8 0 0 1-3-2.6 6 6 0 0 1-1-3.5 9 9 0 0 1 1-3.7 8 8 0 0 1 2.6-3 6 6 0 0 1 3.6-1.1q1.4 0 3.2 1 2.4 1.1 3.1 2.8a5 5 0 0 1 .3 3.5l-2.7-.8a3 3 0 0 0-.2-2q-.4-.9-1.6-1.4a4 4 0 0 0-3.1-.3q-1.5.5-2.6 2.6t-.7 3.8a4 4 0 0 0 2 2.4q.8.5 1.7.5h1.8l.8-1.6zm-90.2-22.3 2-14 4.2.7 1.1 9.8 3.9-9 4.2.6-2 13.8-2.7-.4 1.7-10.9-4.4 10.5-2.7-.4-1.1-11.3-1.6 11zm-14.1-1.7 1.3-14 10.3 1-.2 2.4-7.5-.7-.3 3 7 .7-.3 2.4-7-.7-.3 3.8 7.8.7-.2 2.4z"/>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M216.5 191.3q0-2.2.7-3.6a7 7 0 0 1 1.4-1.9 5 5 0 0 1 1.8-1.2q1.5-.5 3-.5 3.1.1 5 2a7 7 0 0 1 1.6 5.5q0 3.3-2 5.3a7 7 0 0 1-5 1.7 7 7 0 0 1-4.8-2 7 7 0 0 1-1.7-5.3"/>
<path fill="#f7ffff" d="M219.4 191.3q0 2.3 1 3.6t2.8 1.3a4 4 0 0 0 2.8-1.1q1-1.2 1.1-3.7.1-2.4-1-3.6a4 4 0 0 0-2.7-1.3 4 4 0 0 0-2.8 1.2q-1.1 1.2-1.2 3.6"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m233 198.5.2-14h6q2.2 0 3.2.5 1 .3 1.6 1.3c.6 1 .6 1.4.6 2.3a4 4 0 0 1-1 2.6 5 5 0 0 1-2.7 1.2l1.5 1.2q.6.6 1.5 2.3l1.7 2.8h-3.4l-2-3.2-1.4-2-.9-.6-1.4-.2h-.6v5.8z"/>
<path fill="#fff" d="M236 190.5h2q2.1 0 2.6-.2.5-.1.8-.5.4-.6.3-1 0-.9-.4-1.2-.3-.4-1-.6h-2l-2.3-.1z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m249 185.2 5.2.3q1.7 0 2.6.3a5 5 0 0 1 2 1.4 6 6 0 0 1 1.2 2.4q.4 1.4.3 3.3a9 9 0 0 1-.5 3q-.6 1.5-1.7 2.4a5 5 0 0 1-2 1q-1 .3-2.5.2l-5.3-.3z"/>
<path fill="#fff" d="m251.7 187.7-.5 9.3h3.8q.8 0 1.2-.5.5-.4.8-1.3t.4-2.6l-.1-2.5a3 3 0 0 0-.8-1.4l-1.2-.7-2.3-.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m317.6 210.2 3.3-13.6 4.4 1 3.2 1q1.1.6 1.6 1.9t.2 2.8q-.3 1.2-1 2a4 4 0 0 1-3 1.4q-1 0-3-.5l-1.7-.5-1.2 5.2z"/>
<path fill="#fff" d="m323 199.6-.8 3.8 1.5.4q1.6.4 2.2.3a2 2 0 0 0 1.6-1.5q0-.7-.2-1.3a2 2 0 0 0-1-.9l-1.9-.5-1.3-.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m330.6 214.1 4.7-13.2 5.5 2q2.2.8 3 1.4.8.7 1 1.8c.2 1.1.2 1.5 0 2.3q-.6 1.5-1.8 2.2-1.2.6-3 .3.6.7 1 1.6l.8 2.7.6 3.1-3.1-1.1-1-3.6-.7-2.4-.6-.8q-.3-.4-1.3-.7l-.5-.2-2 5.6z"/>
<path fill="#fff" d="m336 207.4 1.9.7q2 .7 2.5.7t.9-.3q.5-.3.6-.9.3-.6 0-1.2a2 2 0 0 0-.8-.9l-2-.7-2-.7-1.2 3.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M347 213.6a9 9 0 0 1 1.7-3.2 7 7 0 0 1 1.8-1.5l2-.7q1.5-.1 3.1.4a7 7 0 0 1 4.2 3.3q1.2 2.4.2 5.7a7 7 0 0 1-3.4 4.5q-2.3 1.3-5.2.4a7 7 0 0 1-4.2-3.3 7 7 0 0 1-.2-5.6"/>
<path fill="#fff" d="M349.8 214.4q-.7 2.3 0 3.8c.7 1.5 1.2 1.6 2.3 2q1.5.5 3-.4 1.4-.8 2.1-3.2.8-2.2 0-3.7a4 4 0 0 0-2.2-2 4 4 0 0 0-3 .3q-1.5.8-2.2 3.2"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m374.3 233.1 6.4-12.4 5.3 2.7a10 10 0 0 1 2.7 1.9q.8.7.8 1.9c0 1.2 0 1.5-.4 2.2a4 4 0 0 1-2 2q-1.5.4-3.1-.2.6 1 .8 1.7.3.9.4 2.8l.2 3.2-3-1.5-.4-3.7-.3-2.5-.5-1-1.2-.7-.5-.3-2.7 5.2z"/>
<path fill="#fff" d="m380.5 227.2 1.9 1q1.8 1 2.3 1t1-.2q.4-.2.7-.8t.2-1.2l-.7-1-1.8-1-2-1z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M426.1 258.7a9 9 0 0 1 2.5-2.6 7 7 0 0 1 2.2-.9 6 6 0 0 1 2.2 0q1.5.3 2.8 1.2a7 7 0 0 1 3 4.4q.4 2.6-1.4 5.5a7 7 0 0 1-4.5 3.3 7 7 0 0 1-5.2-1.1 7 7 0 0 1-3-4.4q-.4-2.7 1.4-5.4"/>
<path fill="#fff" d="M428.6 260.3q-1.4 2-1.1 3.6a4 4 0 0 0 1.6 2.5q1.5 1 3 .6t2.9-2.4q1.4-2.1 1.1-3.6t-1.6-2.6c-1.4-1.1-2-.8-3-.5q-1.5.3-3 2.4z"/>
</g>
<path fill="#309e3a" d="m301.8 204.5 2.3-9.8 7.2 1.7-.3 1.6-5.3-1.2-.5 2.2 4.9 1.1-.4 1.7-4.9-1.2-.6 2.7 5.5 1.3-.4 1.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ca" viewBox="0 0 640 480">
<path fill="#fff" d="M150.1 0h339.7v480H150z"/>
<path fill="#d52b1e" d="M-19.7 0h169.8v480H-19.7zm509.5 0h169.8v480H489.9zM201 232l-13.3 4.4 61.4 54c4.7 13.7-1.6 17.8-5.6 25l66.6-8.4-1.6 67 13.9-.3-3.1-66.6 66.7 8c-4.1-8.7-7.8-13.3-4-27.2l61.3-51-10.7-4c-8.8-6.8 3.8-32.6 5.6-48.9 0 0-35.7 12.3-38 5.8l-9.2-17.5-32.6 35.8c-3.5.9-5-.5-5.9-3.5l15-74.8-23.8 13.4q-3.2 1.3-5.2-2.2l-23-46-23.6 47.8q-2.8 2.5-5 .7L264 130.8l13.7 74.1c-1.1 3-3.7 3.8-6.7 2.2l-31.2-35.3c-4 6.5-6.8 17.1-12.2 19.5s-23.5-4.5-35.6-7c4.2 14.8 17 39.6 9 47.7"/>
</svg>

After

Width:  |  Height:  |  Size: 625 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ch" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="red" d="M0 0h640v480H0z"/>
<g fill="#fff">
<path d="M170 195h300v90H170z"/>
<path d="M275 90h90v300h-90z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 290 B

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-cn" viewBox="0 0 640 480">
<defs>
<path id="cn-a" fill="#ff0" d="M-.6.8 0-1 .6.8-1-.3h2z"/>
</defs>
<path fill="#ee1c25" d="M0 0h640v480H0z"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(71.9991 0 0 72 120 120)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(-12.33562 -20.5871 20.58684 -12.33577 240.3 48)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(-3.38573 -23.75998 23.75968 -3.38578 288 95.8)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(6.5991 -23.0749 23.0746 6.59919 288 168)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(14.9991 -18.73557 18.73533 14.99929 240 216)"/>
</svg>

After

Width:  |  Height:  |  Size: 813 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-co" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#ffe800" d="M0 0h640v480H0z"/>
<path fill="#00148e" d="M0 240h640v240H0z"/>
<path fill="#da0010" d="M0 360h640v120H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 286 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-cr" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#0000b4" d="M0 0h640v480H0z"/>
<path fill="#fff" d="M0 75.4h640v322.3H0z"/>
<path fill="#d90000" d="M0 157.7h640v157.7H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 290 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 640 480">
<path fill="#fc0" d="M0 320h640v160H0z"/>
<path fill="#000001" d="M0 0h640v160H0z"/>
<path fill="red" d="M0 160h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 221 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-eg" viewBox="0 0 640 480">
<path fill="#000001" d="M0 320h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#ce1126" d="M0 0h640v160H0z"/>
<g fill="#fff" stroke="#c09300" transform="translate(-40)scale(.8)">
<path stroke-linejoin="round" stroke-width="1.3" d="m450.8 302.4 68.5 63.6-4.9-115.5c-.7-17.5-15.9-13.5-27-7.2-11.1 7.2-24 7.2-37.4 2.5-13.5 4.7-26.3 4.7-37.4-2.5-11-6.3-26.3-10.3-27 7.2L380.7 366z"/>
<path id="eg-a" fill="#c09300" stroke="none" d="m393.5 246.5-4.8 112.3-8 7.2 4.9-115.5a24 24 0 0 1 7.9-4m9.6 8-4 94-8 8.2 4.8-108.5c1.6 1.6 6.3 5.5 7.2 6.3m8.7 7.2-3.1 78.4-6.5 6.3 4-89.4c1.6 1.5 4.8 3.8 5.6 4.7m9.5 4-3.1 66.8-6.3 5.1 3.1-74.3c1.6.7 4.7 2.4 6.3 2.4m8.8 0-2.3 55.7-6.5 6.3 2.5-61.3c1.5 0 5.6 0 6.3-.7"/>
<use xlink:href="#eg-a" width="100%" height="100%" transform="matrix(-1 0 0 1 900 0)"/>
<path fill="#c09300" stroke-width="1.1" d="m453.2 315 9.6 43.8-3.2 3.2-3.3-2.5-5.4-39 2.3 39-3.2 4-3.1-4 2.3-39-5.5 39-3.3 2.5-3.2-3.2 9.6-43.7h6.4z"/>
<g id="eg-b" fill="none" stroke-linejoin="round" stroke-width="1.3">
<path fill="#fff" stroke-width="1.2" d="m428.5 295.8-19.1 67.7 26.3 4 11.1-50.9z"/>
<path d="m422.2 319 2.3 5.5 12.4-11.8"/>
<path d="m430.8 305 2.6 24.3 7.9-10.4m-3.2 4 4.3 15m1.7-5.5-8.7 13.2m2.7 13.2-2.8-13.2-2.4-13.4-5.9 7.9-2.5-9.1-8.2 8.4 4.1 15.2 5.8-9.4 3.1 9.6 6-9.2"/>
<path d="m415 362 5.3-7.5 3.4 11.5 4.8-8 3.1 9.6"/>
</g>
<use xlink:href="#eg-b" width="100%" height="100%" transform="matrix(-1 0 0 1 900 0)"/>
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3">
<path stroke-width="2.4" d="M450 393.8c20 0 39-1.6 50.2-4.7 4.7-.9 4.7-3.3 4.7-6.5 4.8-1.6 2.4-7.2 5.7-7.2-3.4 1-4-5.5-8-4.7 0-5.6-5.7-6.3-10.4-4.7-9.5 3.1-26.3 3.9-42.2 3.9-16-.8-32.6-.8-42.2-4-4.7-1.5-10.3-.8-10.3 4.8-4-.8-4.7 5.6-8 4.7 3.3 0 .8 5.7 5.6 7.2 0 3.2 0 5.6 4.8 6.5 11 3.1 30.2 4.7 50.1 4.7"/>
<path d="M422.9 363.5c6.4.9 13.6 1.6 19.2.9 3.2 0 5.5 5.5-.9 6.3-5.5.7-14.3 0-19-.8a231 231 0 0 1-18.4-4c-5.6-2.4-1.6-7 1.6-6.4a105 105 0 0 0 17.5 4m54.2 0c-6.4.9-13.6 1.6-19 .9-3.4 0-5.7 5.5.7 6.3 5.6.7 14.3 0 19-.8 4-.8 12.8-2.3 18.4-4 5.6-2.4 1.6-7-1.6-6.4a105 105 0 0 1-17.5 4"/>
<path d="M403 360.4c-4.8-.9-7 4.7-5.5 7.9.7-1.6 4-1.6 4.7-3.2.9-2.4-.7-2.4.9-4.7zm19.2 14.7c0-3.2 3.1-2.8 3.1-6 0-1.5-.8-4-2.4-4a3.4 3.4 0 0 0-3.2 3.2c-.7 3.1 2.5 3.6 2.5 6.8m22.7-9.1c4.7 0 4.2 6.3 2 9.5 0-2.3-4-3.2-4-4.8 0-2.4 3.6-2.4 2-4.7m52-5.6c4.9-.9 7.2 4.7 5.6 7.9-.7-1.6-4-1.6-4.7-3.2-.9-2.4.7-2.4-.9-4.7M478 375c0-3.2-3.2-2.8-3.2-6 0-1.5.8-4 2.4-4a3.4 3.4 0 0 1 3.2 3.2c.7 3.1-2.5 3.6-2.5 6.8zm-23-9c-4.7 0-4.2 6.3-2 9.5 0-2.3 4-3.2 4-4.8 0-2.4-3.6-2.4-2-4.7"/>
<path stroke-width=".9" d="M404.7 362c1.6 0 4 .7 4.7 1.6zm7.9 2.4c.8 0 4 .7 5.5 1.6zm28.6 3.2c-1.5 0-4.7 0-5.5.7l5.5-.8zm-8.7 0c-.9-.9-4-.9-5.6 0zm62.8-5.6a8 8 0 0 0-4.7 1.6zm-7.8 2.4c-1 0-4 .7-5.6 1.6zm-28.7 3.2c1.5 0 4.7 0 5.6.7l-5.6-.8zm8.7 0c.9-.9 4-.9 5.6 0z"/>
<g fill="#c09300" stroke="none">
<path d="M403.3 374.6q-.8-.3-.6-1 .3-1 1-.8c.4 0 .9.6.9.8l-.4.7-.2.1q-.1.3-.7.2m55 3.9q-.4-.2-.6-.7c0-.4.6-1 1-1l.8.4q.5.6-.1 1.2-.5.3-1.1 0zm.3 2.5q-.5-.2-.6-.8t.5-.9l.4-.2.4.2q.6.2.7.8 0 .5-.7.8-.4.3-.7 0z"/>
<path d="M407.8 370q-.7.1-1.2.6c-.6.1-1.4.5-2 0-.6-.1-1.4 0-1.5.8.1.7 1 1 1.6.6.4-.5 1.5-.9 1.7 0-.5.8-.4 1.7-.8 2.4q-.1.7-.5 1.3-.8 0-1.4.3a2 2 0 0 0-1.6.8q-.7.9-.9 2c.1.6 1 .8 1.6.9l2 .6 3.2.9q2.4.6 4.8 1.1l.5.1c.7.2 1-.5 1-1l1-3.6c.2-.5.5-1.5-.4-1.3q-.7.6-1.6.8c-.9 0-.4 1 0 1.3q0 1-.5 1.8-.9.3-1.8-.2c-.5 0-1.7-.3-1.2-1l.5-1.8q.3-1 .5-2c-.4-.7-1 .3-1.5.4s-1.6.3-1 1c.6.4.2 1.1 0 1.7 0 .7-.8 1-1.4.7-.6 0-1.5-.5-1-1.1l.5-1.8.7-2q0-1 .6-1.8c0-.7.5-1.3.5-2q0-.4-.4-.4zm-3.7 7.3h.3q.3.3 0 .6l-.3.2q-.7 0-.6-.3 0-.1.3-.3zm-1 5q-.7-.5.3-1.3.4-.3.9.1.9.6 0 1.3-.2.3-.5.2t-.7-.2zm3 1a1 1 0 0 1-.6-1.1q.1-.6.9-.5.9-.1.9.7 0 .5-.3.7zm89.2 0q-.3-.1-.3-.5 0-.6.8-.9.9-.5 1.2.2.5.8-.1 1.2-.2.4-.8.3-.7 0-.8-.2zm-85 1.2q-.6-.1-.6-.8 0-.4.2-.6l.6-.2.6.1q.8.6.3 1.2t-1.2.3zm21.8 1-.2-.3q0-.9.2-1.7 0-.7.2-1.5l.4-2.8q0-.8.2-1.4 0-1.2.2-2.2c0-.3.3-1 .6-.6q.6.8 1.4 1.5c.4.3 0 .7-.3.8q-.6.3-.5 1l-.2 1.2q0 1-.2 2l-.1 1.8-.2 1.2q.1.7-.4 1.1-.6.3-1-.1zm29.7-9.8-1.3 1.2c-.6.5.5.7.6 1.1q.3.9.2 1.8.3.9.2 1.7c0 .7-.8.5-1.2.9q-.6.4-1 1l-.4 1.6c0 .5-.3 1 0 1.4l.1.2h.5l1.5-.1q1.9-.2 3.7-.3l2.2-.2c.6.1.9-.5.9-1-.4-.7-.1-1.4-.4-2q-.2-1.2-.2-2.2c0-.6-.7-.8-1-.4q-.7.4-1.2.9c-.3.6.6.6.8 1l.1 1.5c.1.6-.5.6-1 .7q-.9.4-1.5-.3-.2-.7-.2-1.5-.1-1.4-.3-2.7-.2-1.5-.3-3.2c0-.5-.1-1.2-.8-1.1m-.6 8.2h.3v.8l-.3.1-.4.1h-.2q-.3-.3.1-.8zm-30-9.2c-.3 0-.8.7-1.2.6-.9.1-.8 1-.1 1.3v.8c.1.8-.4 1.5-.3 2.3q-.1 1.2-.3 2.6-.4 1.4-.4 2.7c-.1.8-.7.5-1.2.3v-1q0-1.2-1.2-1.2-1-.1-.8-1.1.6-.4 1.5-.3c1 .2.9-1.1.4-1.6s-1.2-1-1.4-1.6q0-1.3-1.2-2.1c-1.1-.1-2 .8-2.3 1.8q-.8 0-1.4.4c-.7.2-1.7 1.4-.7 1.9.5.1 2.2.5 1.4 1.2q-.7 1-1.9.6c-.7 0-1.5-.4-1.5-1.2q0-1.2-.4-2.3c-.2-.8-1.1-.6-1.2.2q-1 1-.1 2 .3 1.2-.3 2.2c-.2 1-1.2 1-1.9 1.2-.3.2-1.6 0-1.1.7a4 4 0 0 0 2.4.3q1.2-.2 2-1.4c.5-.5 1.4-.2 2.2-.2.7 0 1.5.5 2.3.1.2-.5 1.2-1.5 1.5-.5q.2 1.4 1.5 1.2c.9 0 .5.6.5 1.2q.2 1.3 1.5 1.8h.8q1.2-.3 1.8-1.4.3-1.1.5-2.2.3-1.7.4-3.4.3-1.5.4-3.2l.3-2.3q0-.6-.5-.4m-6.7 4.1q.2 0 .2.5t.5.8v.3l-.8.2q-.7 0-1.2-.5l-.2-.2.3-.2.5-.5q.4-.4.7-.4m66-7.9-1.7.3c-1 0-1.5 1-.5 1.6.6 1.5 1.5-.4 2.5-.2 1.4.2 1.5 1.8 1.8 3q.1 1.6.7 3.3c1 1.1-.7 1.8-1.4 1-.6-.7-2-1.5-2.7-.6-.9.4-1 1.6-1.7 2-1.2.3-1.3-1.2-2-1.8-.6-.9-1.7-1-2.6-1.2-.4-.9-.2-2.4-1-3-.8.3-2.2 1.7-1 2.4 1 1-.5 1.4-1 2q-1.1 1.4-1 3c-1.3.7-1.5-.8-1.7-1.7 0-1.1-1-.8-1.7-.4q-1.3.8-2 2v2c.2.9 1.2.5 1.8.4 1-.4 1.4.7.6 1.3-.6.6-2.2.3-2 1.4h1.6c1-.3 2.2-1.1 2.3-2.3.2-1 1.7-.9 2.5-1.1 1-.3 2.3-.4 2.6 1 .7.7 2.2 1.5 3 .5a2 2 0 0 0 1.1-2.3c-.1-.8 1.3-.8 1.7-.3.5.8 2.1.6 2.9 0s1-1.6 2.2-1.6l5.4-1.5c1.4-.3-.2-1.2-.6-1.8-1-.5-2 1.7-3.2.6-1-.7-.9-2-1.2-3.1-.3-1.4-.2-3-1.2-4q-1-1-2.5-.9m-6.8 9.5q.2 0 .5.2.6.4.7 1 0 .2-.3.3l-.5.2q-.5-.1-.7-.5v-.4q-.3-.5 0-.6zm6.5.4q.5 0 .8.3.2.5-.4.8l-.5.2-.4-.3q-.3-.1-.3-.4.1-.5.8-.7zm-9 0q.3 0 .4.2.3.2.3.6v.6q.2.3-.6.2c-.8-.1-.7 0-.7-.6q0-.5.3-.7t.4-.2zm-5.4 1.5.4.4q0 .4-.2.6-.6.2-1 0v-.1q-.3-.2 0-.3v-.1l.2-.3a1 1 0 0 1 .6-.2m9.2 1.2.5.1v.6h-1.2l-.1-.2q0-.3.2-.4zm-17.1 4.8q-.3 0-.1-.2.3-.4.8-.6a3 3 0 0 0 1.4-1q.2-.5.4-1l-.1-1.4a2 2 0 0 0-.8-1q-.6 0-.8-.6.1-.4.4-.9l.6-1.2q.6-.3.8.2l.5 1q.4.4.6 1 .3.6.4 1l.2 1.3q0 .8-.2 1.6l-.6.8q-.4.5-1 .7-.4.3-.9.4H468zm-6.9.4q-.3-.2-.3-.6c0-.4 0-.3.3-.6q.4-.6 1.4 0t1 .1q.2-.4 1-.5l.5.2q.3.2.3.7c0 .5 0 .5-.3.8q-.3.2-.7.2a1 1 0 0 1-.8-.6q0-.4-.2-.1l-.2.1q-.4.1-.8.5l-.5.1q-.4 0-.7-.3m-19.8-8.9q-1.2.1-1.5 1.3c.2.7.8 1.5.4 2.2.3 1-.8 1.4-1.5.9q-.4-1.5-1.3-3c-.8-.2-1.1 1.1-1.7 1.6.3.7 1.1 1.4 1.2 2.3q.1 1.6-1.1 2.4c-.7.7-1.7.5-2.5.6-1 .6.7.8 1.2.8q1.6.3 3-.8c.7-.3.6-1.7 1.5-1.8l4.8.3c.8.2 2.1-.1 2.7.5 0 1 1 1.8 1.9 2.2.5.2 1-.5 1.5-.6 1-.3.7-1.5 1.3-2h5c.2 0 .3-.5.6-.7l-.2-1.9c-.3-.8 0-1.8-.5-2.5-.9-.2-1.6.5-2.4.6q-1.6.7-1.8 2.3c-.5.9-1.4-.2-1-1l-.1-1c-.5-.5-1.4-.3-2.1-.4l-3-.2q-1.5.1-2.7-.1c-1 0-1-1-1-1.8zm2 3.7 1 .1h1.3q1.5.3 3 .2.3 0 .6.4v2q0 .5-.2.8l-.2.2-.7-.5v-1.4l-.7-.3-2.6-.1-2-.1q-.5 0-.9-.4-.2-.4.4-.7zm11.3.3q.2 0 .2.3l.2.5q0 .2-.2.2-.8.3-1.2 0 0-.3.2-.6z"/>
</g>
</g>
<path stroke-width="1.1" d="M450 327.2c32.6-25 29.8-61.8 29.8-61.8l-2.5.2c-6.9 0-23.2-4-27-8.9-4 4.5-20.8 9-27.6 9l-2.5-.3s-2.9 36.7 29.8 61.8z"/>
<path stroke-width=".9" d="M477.2 268h-.8c-6.2 0-20.6-3.1-26.2-7.9-5.8 4.4-20.5 8-26.6 8l-.8-.1a73 73 0 0 0 2.6 18.7 71 71 0 0 0 24.6 37.1 71 71 0 0 0 24.6-37.2 73 73 0 0 0 2.6-18.6z"/>
<path fill="#c09300" stroke="none" d="M439.4 265a62 62 0 0 1-16.6 3l.1 4.1a73 73 0 0 0 2.5 14.5 71 71 0 0 0 14 26.8zm20.6 0v49.2a71 71 0 0 0 14.6-27.6 73 73 0 0 0 2.5-14.5l.1-4h-.8c-3.8 0-10.4-1.2-16.4-3.2z"/>
<g stroke-width="1.3">
<path stroke-width="1.2" d="M462.3 253c.7.1-.9-3.5-.9-3.5 1.8 1.8 8.4 2.3 8.4 2.3-4-1.8-8-15.1-7.5-25.8.4-10.6-1.5-14.8-3-16.4-2-2-8.5-3.8-12.7-4-2.5-.1-2 1.8-2 1.8-4.5-1.1-9-1.6-11-.2-1.8 1.2-2.2 7.5-.8 6.4 3.3-2.7 6.2-.2 8.2 2.7 1.8 2.5 1.7 9.7-.9 18.2a60 60 0 0 1-10 17.7c4 0 9.6-3.5 9.6-3.5l-1.3 5.5c4.2-2 7.5-5.1 7.5-5.1l4 4.2c1.3-1.8 4-4.2 4-4.2s3.3 3.5 8.4 4z"/>
<path fill="none" d="M446.1 227.6s-2.2 16.4-6.4 21m10-21.5s-1 16.7-3.8 22m6.9-21.3s0 18.2 1 21.3m3-20.4s.8 15.3 4.6 20.8"/>
<path fill="#c09300" stroke-width=".3" d="M442 219.6a8 8 0 0 0-1-3.3c-2-3-4.9-5.4-8.2-2.7 0 0 1.1-3.5 3.6-3.6 1.8-.1 6.1 1.4 9.9 7.8 0 0-2.8-.6-3.5 0-1.2 1-.7 1.8-.7 1.8z"/>
<path fill="#c09300" stroke-width=".3" d="M432.4 209.3q.4-1.6 1.3-2.1c2-1.4 6.4-1 10.9.2 0 0-.4-1.9 2-1.8 4.2.2 10.6 2 12.6 4a8 8 0 0 1 1.5 2.4c-1-1.4-3.8-1.3-4.5-1.2-1 .1-1.7 0-3.1.4q-1.2.3-2.3.8c-.4.4-.8 1.6-1.4 1.6-1 0-1-.2-1.3-.5q-.4-1-.9-1-1.6.2-5-2.5c-2.3-1.8-3.2-2.2-6-2-3 .2-3.8 1.9-3.8 1.9z"/>
<circle cx="448.8" cy="210.7" r="1.2" stroke="none"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -0,0 +1,544 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-es" viewBox="0 0 640 480">
<path fill="#AA151B" d="M0 0h640v480H0z"/>
<path fill="#F1BF00" d="M0 120h640v240H0z"/>
<path fill="#ad1519" d="m127.3 213.3-.8-.1-1-1-.7-.4-.6-.8s-.7-1.1-.4-2q.5-1.2 1.4-1.5l1.5-.5 1-.4 1.3-.3.5-.3q.4 0 1-.2l1-.2 1.6.1h4.8c.4 0 1.2.3 1.4.4l2 .7c.5.1 1.6.3 2.2.6q.8.5 1.1 1l.5 1v1.1l-.5.8-.6 1-.8.6s-.5.5-1 .4-4.8-.8-7.6-.8-7.3.9-7.3.9"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="m127.3 213.3-.8-.1-1-1-.7-.4-.6-.8s-.7-1.1-.4-2q.5-1.2 1.4-1.5l1.5-.5 1-.4 1.3-.3.5-.3q.4 0 1-.2l1-.2 1.6.1h4.8c.4 0 1.2.3 1.4.4l2 .7c.5.1 1.6.3 2.2.6q.8.5 1.1 1l.5 1v1.1l-.5.8-.6 1-.8.6s-.5.5-1 .4-4.8-.8-7.6-.8-7.3.9-7.3.9z"/>
<path fill="#c8b100" d="M133.3 207q.1-2.1 1.3-2.3 1.3.1 1.4 2.4c0 1.3-.6 2.4-1.4 2.4s-1.3-1.1-1.3-2.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M133.3 207q.1-2.1 1.3-2.3 1.3.1 1.4 2.4c0 1.3-.6 2.4-1.4 2.4s-1.3-1.1-1.3-2.5z"/>
<path fill="#c8b100" d="M134 207q0-2 .7-2.1c.3 0 .6 1 .6 2.1q0 2-.6 2.2c-.4 0-.6-1-.6-2.2"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134 207q0-2 .7-2.1c.3 0 .6 1 .6 2.1q0 2-.6 2.2c-.4 0-.6-1-.6-2.2z"/>
<path fill="#c8b100" d="M133.8 204.5q.1-.7.8-.8 1 0 1 .8-.1.8-1 .9c-.9.1-.8-.4-.8-.9"/>
<path fill="#c8b100" d="M135.3 204.2v.6h-1.4v-.6h.5V203h-.7v-.6h.7v-.5h.5v.5h.6v.6h-.6v1.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M135.3 204.2v.6h-1.4v-.6h.5V203h-.7v-.6h.7v-.5h.5v.5h.6v.6h-.6v1.2h.4"/>
<path fill="#c8b100" d="M135.9 204.2v.6h-2.5v-.6h1V203h-.7v-.6h.7v-.5h.5v.5h.6v.6h-.6v1.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M135.9 204.2v.6h-2.5v-.6h1V203h-.7v-.6h.7v-.5h.5v.5h.6v.6h-.6v1.2h1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134.9 203.7q.6.2.6.8-.1.8-.8.9-1 0-1-.9 0-.6.7-.8"/>
<path fill="#c8b100" d="M134.7 213.2H130v-1.1l-.3-1.2-.2-1.5c-1.3-1.7-2.5-2.8-2.9-2.5q.1-.5.5-.7c1.1-.7 3.5 1 5.2 3.6l.5.7h3.8l.4-.7c1.8-2.7 4.1-4.3 5.2-3.6q.4.2.5.7c-.4-.3-1.6.8-2.9 2.5l-.2 1.5-.2 1.2-.1 1.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134.7 213.2H130v-1.1l-.3-1.2-.2-1.5c-1.3-1.7-2.5-2.8-2.9-2.5q.1-.5.5-.7c1.1-.7 3.5 1 5.2 3.6l.5.7h3.8l.4-.7c1.8-2.7 4.1-4.3 5.2-3.6q.4.2.5.7c-.4-.3-1.6.8-2.9 2.5l-.2 1.5-.2 1.2-.1 1.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M126.8 206.8c1-.5 3 1.1 4.6 3.6m11-3.6c-.8-.5-2.8 1.1-4.5 3.6"/>
<path fill="#c8b100" d="m127.8 215.3-.5-1a27 27 0 0 1 14.7 0l-.5.8-.3.8a23 23 0 0 0-6.6-.8c-2.6 0-5.2.3-6.5.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m127.8 215.3-.5-1a27 27 0 0 1 14.7 0l-.5.8-.3.8a23 23 0 0 0-6.6-.8c-2.6 0-5.2.3-6.5.8l-.3-.6"/>
<path fill="#c8b100" d="M134.6 217.7c2.4 0 5-.4 5.9-.6q1-.3 1-.8 0-.3-.4-.4c-1.4-.5-4-.8-6.5-.8s-5 .3-6.4.8q-.3 0-.4.3 0 .6 1 .9c1 .2 3.5.6 5.8.6"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134.6 217.7c2.4 0 5-.4 5.9-.6q1-.3 1-.8 0-.3-.4-.4c-1.4-.5-4-.8-6.5-.8s-5 .3-6.4.8q-.3 0-.4.3 0 .6 1 .9c1 .2 3.5.6 5.8.6z"/>
<path fill="#c8b100" d="m142.1 213.2-.5-.5s-.6.3-1.3.2c-.6 0-.9-1-.9-1s-.7.7-1.3.7-1-.6-1-.6-.7.5-1.3.4-1.2-.8-1.2-.8-.6.8-1.2.8c-.6.1-1-.5-1-.5s-.4.6-1.1.7-1.4-.6-1.4-.6-.5.7-1 1c-.5 0-1.2-.4-1.2-.4l-.2.5-.3.1.2.5a27 27 0 0 1 7.2-.9q4.5 0 7.4 1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m142.1 213.2-.5-.5s-.6.3-1.3.2c-.6 0-.9-1-.9-1s-.7.7-1.3.7-1-.6-1-.6-.7.5-1.3.4-1.2-.8-1.2-.8-.6.8-1.2.8c-.6.1-1-.5-1-.5s-.4.6-1.1.7-1.4-.6-1.4-.6-.5.7-1 1c-.5 0-1.2-.4-1.2-.4l-.2.5-.3.1.2.5a27 27 0 0 1 7.2-.9q4.5 0 7.4 1z"/>
<path fill="#c8b100" d="M134.7 210.7h.2v.4q0 1 1 1a1 1 0 0 0 1-.7l.2-.3v.4q.3.8 1.1.8 1 0 1-1v-.1l.4-.4.2.5-.1.4a1 1 0 0 0 1 1q.6 0 .9-.5l.2-.2v.3q0 .5.4.7 0 .1 1-.4l.7-.7v.4s-.5.8-1 1q-.3.4-.8.3-.5 0-.7-.6-.3.3-.7.2-1 0-1.4-.8-.5.5-1.1.5a2 2 0 0 1-1.2-.6l-1 .4a2 2 0 0 1-1.3-.6 1.6 1.6 0 0 1-2.4.2 2 2 0 0 1-1.2.6 2 2 0 0 1-1.1-.5q-.4.8-1.4.8-.3 0-.7-.2-.2.5-.7.6t-.9-.2l-1-1 .1-.5.8.7q.8.5.9.4.4-.1.4-.7v-.3l.2.2q.3.5.9.5a1 1 0 0 0 1-1v-.9l.4.4v.1q.1 1 1 1t1.1-.9v-.3l.2.3q.3.6 1 .7 1 0 1.1-1v-.3h.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134.7 210.7h.2v.4q0 1 1 1a1 1 0 0 0 1-.7l.2-.3v.4q.3.8 1.1.8 1 0 1-1v-.1l.4-.4.2.5-.1.4a1 1 0 0 0 1 1q.6 0 .9-.5l.2-.2v.3q0 .5.4.7 0 .1 1-.4l.7-.7v.4s-.5.8-1 1q-.3.4-.8.3-.5 0-.7-.6-.3.3-.7.2-1 0-1.4-.8-.5.5-1.1.5a2 2 0 0 1-1.2-.6l-1 .4a2 2 0 0 1-1.3-.6 1.6 1.6 0 0 1-2.4.2 2 2 0 0 1-1.2.6 2 2 0 0 1-1.1-.5q-.4.8-1.4.8-.3 0-.7-.2-.2.5-.7.6t-.9-.2l-1-1 .1-.5.8.7q.8.5.9.4.4-.1.4-.7v-.3l.2.2q.3.5.9.5a1 1 0 0 0 1-1v-.9l.4.4v.1q.1 1 1 1t1.1-.9v-.3l.2.3q.3.6 1 .7 1 0 1.1-1v-.3h.3z"/>
<path fill="#c8b100" d="M134.6 213.3q-4.5 0-7.3 1l-.3-.2.1-.3a27 27 0 0 1 7.5-1q4.6 0 7.6 1 .1 0 .1.3l-.3.2a27 27 0 0 0-7.4-1"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M134.6 213.3q-4.5 0-7.3 1l-.3-.2.1-.3a27 27 0 0 1 7.5-1q4.6 0 7.6 1 .1 0 .1.3l-.3.2a27 27 0 0 0-7.4-1z"/>
<path fill="#fff" d="M131.8 214.4q0-.4.5-.4a.4.4 0 0 1 .4.4q0 .3-.4.4a.4.4 0 0 1-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M131.8 214.4q0-.4.5-.4a.4.4 0 0 1 .4.4q0 .3-.4.4a.4.4 0 0 1-.5-.4z"/>
<path fill="#ad1519" d="M134.7 214.5h-1q-.2 0-.3-.3l.3-.3h2l.2.3-.3.3h-1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M134.7 214.5h-1q-.2 0-.3-.3l.3-.3h2l.2.3-.3.3h-1"/>
<path fill="#058e6e" d="M130 214.9h-.7q-.2 0-.3-.2l.2-.3.7-.1.7-.1q.3 0 .4.2a.3.3 0 0 1-.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M130 214.9h-.7q-.2 0-.3-.2l.2-.3.7-.1.7-.1q.3 0 .4.2a.3.3 0 0 1-.3.4h-.7"/>
<path fill="#ad1519" d="m127.3 215.3.3-.4h.7l-.4.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m127.3 215.3.3-.4h.7l-.4.6-.6-.2"/>
<path fill="#fff" d="M136.6 214.4q0-.4.4-.4a.4.4 0 0 1 .5.4.4.4 0 0 1-.5.4.4.4 0 0 1-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M136.6 214.4q0-.4.4-.4a.4.4 0 0 1 .5.4.4.4 0 0 1-.5.4.4.4 0 0 1-.4-.4z"/>
<path fill="#058e6e" d="M139.3 214.9h.6a.3.3 0 0 0 .4-.2l-.3-.3-.6-.1-.7-.1q-.3 0-.4.2 0 .3.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M139.3 214.9h.6a.3.3 0 0 0 .4-.2l-.3-.3-.6-.1-.7-.1q-.3 0-.4.2 0 .3.3.4h.7"/>
<path fill="#ad1519" d="m142 215.4-.3-.5h-.7l.3.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m142 215.4-.3-.5h-.7l.3.6.6-.1"/>
<path fill="#ad1519" d="M134.6 217.1a25 25 0 0 1-6-.6 26 26 0 0 1 12.1 0q-2.5.6-6 .6"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M134.6 217.1a25 25 0 0 1-6-.6 26 26 0 0 1 12.1 0q-2.5.6-6 .6z"/>
<path fill="#c8b100" d="m142 212-.1-.3q-.3 0-.4.2 0 .4.2.4 0 0 .3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m142 212-.1-.3q-.3 0-.4.2 0 .4.2.4 0 0 .3-.3z"/>
<path fill="#c8b100" d="M137.3 211.2q0-.3-.2-.4-.1 0-.2.3 0 .3.2.4l.3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M137.3 211.2q0-.3-.2-.4-.1 0-.2.3 0 .3.2.4l.3-.3z"/>
<path fill="#c8b100" d="m132 211.2.1-.4q.3 0 .3.3t-.2.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m132 211.2.1-.4q.3 0 .3.3t-.2.4z"/>
<path fill="#c8b100" d="m127.3 212 .1-.3q.3 0 .4.2 0 .4-.2.4 0 0-.3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m127.3 212 .1-.3q.3 0 .4.2 0 .4-.2.4 0 0-.3-.3z"/>
<path fill="#c8b100" d="m134.6 208.5-.8.5.6 1.3.2.1.2-.1.7-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m134.6 208.5-.8.5.6 1.3.2.1.2-.1.7-1.3-.9-.5"/>
<path fill="#c8b100" d="m132.8 210.5.4.5 1.3-.4.1-.2-.1-.2-1.3-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m132.8 210.5.4.5 1.3-.4.1-.2-.1-.2-1.3-.3-.4.6"/>
<path fill="#c8b100" d="m136.4 210.5-.3.5-1.3-.4-.2-.2.2-.2 1.3-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m136.4 210.5-.3.5-1.3-.4-.2-.2.2-.2 1.3-.3.3.6"/>
<path fill="#c8b100" d="m129.3 209-.7.7.9 1 .2.1.1-.1.3-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m129.3 209-.7.7.9 1 .2.1.1-.1.3-1.3-.8-.3"/>
<path fill="#c8b100" d="m128 211.2.4.5 1.2-.6v-.2l-.1-.2-1.3-.1-.3.6"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m128 211.2.4.5 1.2-.6v-.2l-.1-.2-1.3-.1-.3.6"/>
<path fill="#c8b100" d="m131.5 210.5-.3.6H130l-.2-.2.1-.3 1.2-.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m131.5 210.5-.3.6H130l-.2-.2.1-.3 1.2-.6.5.5"/>
<path fill="#c8b100" d="M126.6 211.4v.6l-1.4.2-.2-.1v-.2l1-.9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M126.6 211.4v.6l-1.4.2-.2-.1v-.2l1-.9.6.4"/>
<path fill="#c8b100" d="M129.2 210.9q0-.5.5-.5t.5.5a1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M129.2 210.9q0-.5.5-.5t.5.5a1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4z"/>
<path fill="#c8b100" d="m140 209 .7.7-.9 1-.2.1-.1-.1-.3-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m140 209 .7.7-.9 1-.2.1-.1-.1-.3-1.3.8-.3"/>
<path fill="#c8b100" d="m141.4 211.2-.5.5-1.2-.6v-.2l.1-.2 1.3-.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m141.4 211.2-.5.5-1.2-.6v-.2l.1-.2 1.3-.1.3.6"/>
<path fill="#c8b100" d="m137.8 210.5.3.6h1.3l.2-.2-.1-.3-1.2-.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m137.8 210.5.3.6h1.3l.2-.2-.1-.3-1.2-.6-.5.5"/>
<path fill="#c8b100" d="m142.5 211.4.1.6 1.3.2.2-.1v-.2l-1-.9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m142.5 211.4.1.6 1.3.2.2-.1v-.2l-1-.9-.6.4"/>
<path fill="#c8b100" d="m134.2 210.4.4-.4q.5 0 .5.4a1 1 0 0 1-.5.5 1 1 0 0 1-.4-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m134.2 210.4.4-.4q.5 0 .5.4a1 1 0 0 1-.5.5 1 1 0 0 1-.4-.5z"/>
<path fill="#c8b100" d="M139.1 210.9q.1-.5.5-.5a1 1 0 0 1 .5.5 1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M139.1 210.9q.1-.5.5-.5a1 1 0 0 1 .5.5 1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4z"/>
<path fill="#c8b100" d="m124.8 212.2-.6-.7c-.2-.2-.7-.3-.7-.3q0-.2.6-.3l.4.2v-.2s.3 0 .4.3v1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m124.8 212.2-.6-.7c-.2-.2-.7-.3-.7-.3q0-.2.6-.3l.4.2v-.2s.3 0 .4.3v1z"/>
<path fill="#c8b100" d="M124.8 212q.3-.3.5 0t.2.5l-.5-.1q-.3-.2-.2-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M124.8 212q.3-.3.5 0t.2.5l-.5-.1q-.3-.2-.2-.5z"/>
<path fill="#c8b100" d="m144.3 212.2.6-.7q.5-.3.7-.3 0-.2-.6-.3l-.4.2v-.2s-.3 0-.4.3v.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m144.3 212.2.6-.7q.5-.3.7-.3 0-.2-.6-.3l-.4.2v-.2s-.3 0-.4.3v.7z"/>
<path fill="#c8b100" d="M144.3 212q-.1-.3-.5 0-.3.2-.1.5l.5-.1q.3-.2.1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M144.3 212q-.1-.3-.5 0-.3.2-.1.5l.5-.1q.3-.2.1-.5z"/>
<path fill="#c8b100" d="M124 223h21.4v-5.5H124z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M124 223h21.4v-5.5H124z"/>
<path fill="#c8b100" d="M126.2 226.8h16.9a1.4 1.4 0 0 1-1-1.2q.1-1 1-1.3h-16.9q1 .3 1 1.3a1.3 1.3 0 0 1-1 1.2"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M126.2 226.8h16.9a1.4 1.4 0 0 1-1-1.2q.1-1 1-1.3h-16.9q1 .3 1 1.3a1.3 1.3 0 0 1-1 1.2z"/>
<path fill="#c8b100" d="M126.6 226.8h16q1 0 1 .7t-1 .8h-16q-.9-.1-1-.8 0-1 1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M126.6 226.8h16q1 0 1 .7t-1 .8h-16q-.9-.1-1-.8 0-1 1-.8z"/>
<path fill="#c8b100" d="M126.6 223h16q1 .1 1 .7t-1 .6h-16q-.9 0-1-.6.1-.5 1-.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M126.6 223h16q1 .1 1 .7t-1 .6h-16q-.9 0-1-.6.1-.5 1-.6z"/>
<path fill="#005bbf" d="M149.6 317.4q-2.2 0-3.7-.8a8 8 0 0 0-3.8-.8q-2.1 0-3.7.8a8 8 0 0 1-3.8.8q-2.3 0-3.7-.8a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8 8 8 0 0 1-3.8.8v2.4q2.3 0 3.8-.9a8 8 0 0 1 3.7-.8q2.2 0 3.7.8c1.5.8 2.2.9 3.7.9a8 8 0 0 0 3.8-.9q1.6-.8 3.7-.8 2.3 0 3.8.8c1.5.8 2.2.9 3.7.9z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M149.6 317.4q-2.2 0-3.7-.8a8 8 0 0 0-3.8-.8q-2.1 0-3.7.8a8 8 0 0 1-3.8.8q-2.3 0-3.7-.8a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8 8 8 0 0 1-3.8.8v2.4q2.3 0 3.8-.9a8 8 0 0 1 3.7-.8q2.2 0 3.7.8c1.5.8 2.2.9 3.7.9a8 8 0 0 0 3.8-.9q1.6-.8 3.7-.8 2.3 0 3.8.8c1.5.8 2.2.9 3.7.9z"/>
<path fill="#ccc" d="M149.6 319.8a8 8 0 0 1-3.7-.9 8 8 0 0 0-3.8-.8q-2.1 0-3.7.8c-1.6.8-2.3.9-3.8.9s-2.8-.4-3.7-.9a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v2.3q2.3 0 3.8-.9a8 8 0 0 1 3.7-.7q2.2 0 3.7.7a8 8 0 0 0 7.5 0 9 9 0 0 1 7.5.1 8 8 0 0 0 3.7.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M149.6 319.8a8 8 0 0 1-3.7-.9 8 8 0 0 0-3.8-.8q-2.1 0-3.7.8c-1.6.8-2.3.9-3.8.9s-2.8-.4-3.7-.9a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v2.3q2.3 0 3.8-.9a8 8 0 0 1 3.7-.7q2.2 0 3.7.7a8 8 0 0 0 7.5 0 9 9 0 0 1 7.5.1 8 8 0 0 0 3.7.8v-2.3"/>
<path fill="#005bbf" d="M149.6 322a7 7 0 0 1-3.7-.8 8 8 0 0 0-3.8-.7q-2.1 0-3.7.7-1.5.9-3.8.9c-2.3 0-2.8-.4-3.7-.9a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v2.3q2.3 0 3.8-.9a10 10 0 0 1 7.4 0 7 7 0 0 0 3.7.9 8 8 0 0 0 3.8-.8q1.6-.8 3.7-.8 2.3 0 3.8.8c1.5.8 2.2.8 3.7.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M149.6 322a7 7 0 0 1-3.7-.8 8 8 0 0 0-3.8-.7q-2.1 0-3.7.7-1.5.9-3.8.9c-2.3 0-2.8-.4-3.7-.9a8 8 0 0 0-3.7-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v2.3q2.3 0 3.8-.9a10 10 0 0 1 7.4 0 7 7 0 0 0 3.7.9 8 8 0 0 0 3.8-.8q1.6-.8 3.7-.8 2.3 0 3.8.8c1.5.8 2.2.8 3.7.8V322"/>
<path fill="#ccc" d="M149.6 326.7a8 8 0 0 1-3.7-.8q-1.6-.8-3.7-.8a8 8 0 0 0-3.8.8q-1.5.8-3.8.8a7 7 0 0 1-3.7-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.7.8c-1.5.8-2.3.8-3.8.8v-2.3a8 8 0 0 0 3.8-.9 10 10 0 0 1 7.4 0 8 8 0 0 0 3.7.9 8 8 0 0 0 3.8-.8q1.5-.8 3.8-.8t3.7.8c1.5.8 2.3.8 3.7.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M149.6 326.7a8 8 0 0 1-3.7-.8q-1.6-.8-3.7-.8a8 8 0 0 0-3.8.8q-1.5.8-3.8.8a7 7 0 0 1-3.7-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.7.8c-1.5.8-2.3.8-3.8.8v-2.3a8 8 0 0 0 3.8-.9 10 10 0 0 1 7.4 0 8 8 0 0 0 3.7.9 8 8 0 0 0 3.8-.8q1.5-.8 3.8-.8t3.7.8c1.5.8 2.3.8 3.7.8v2.3"/>
<path fill="#005bbf" d="M149.6 329a8 8 0 0 1-3.7-.8q-1.6-.8-3.7-.8a8 8 0 0 0-3.8.8q-1.5.8-3.8.8a7 7 0 0 1-3.7-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.7.8c-1.5.8-2.3.8-3.8.8v-2.3a8 8 0 0 0 3.8-.8q1.5-.8 3.7-.8t3.7.7a8 8 0 0 0 7.5 0q1.5-.6 3.8-.7t3.7.8c1.5.8 2.2.8 3.7.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M149.6 329a8 8 0 0 1-3.7-.8q-1.6-.8-3.7-.8a8 8 0 0 0-3.8.8q-1.5.8-3.8.8a7 7 0 0 1-3.7-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.7.8c-1.5.8-2.3.8-3.8.8v-2.3a8 8 0 0 0 3.8-.8q1.5-.8 3.7-.8t3.7.7a8 8 0 0 0 7.5 0q1.5-.6 3.8-.7t3.7.8c1.5.8 2.2.8 3.7.8z"/>
<path fill="#c8b100" d="m126.2 308 .2.5c0 1.5-1.3 2.6-2.7 2.6h22a3 3 0 0 1-2.7-2.6v-.5h-16.8"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m126.2 308 .2.5c0 1.5-1.3 2.6-2.7 2.6h22a3 3 0 0 1-2.7-2.6v-.5h-16.8z"/>
<path fill="#c8b100" d="M126.6 306.5h16q1 0 1 .8 0 .6-1 .7h-16q-.9 0-1-.8.1-.6 1-.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M126.6 306.5h16q1 0 1 .8 0 .6-1 .7h-16q-.9 0-1-.8.1-.6 1-.7z"/>
<path fill="#c8b100" d="M123.7 316.7h22V311h-22z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M123.7 316.7h22V311h-22z"/>
<path fill="#ad1519" d="M122 286.7c-2.2 1.2-3.7 2.5-3.4 3.2q.2.8 1.8 1.6c1.5 1.1 2.5 3 1.7 4a5.5 5.5 0 0 0-.1-8.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M122 286.7c-2.2 1.2-3.7 2.5-3.4 3.2q.2.8 1.8 1.6c1.5 1.1 2.5 3 1.7 4a5.5 5.5 0 0 0-.1-8.8z"/>
<path fill="#ccc" d="M126.8 305.6h15.6V229h-15.6v76.5z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M138 229.2v76.3m1.7-76.3v76.3m-12.9 0h15.6v-76.4h-15.6v76.5z"/>
<path fill="#ad1519" d="M158.4 257.7a50 50 0 0 0-23.3-2c-9.4 1.6-16.5 5.3-15.9 8.4v.2l-3.5-8.2c-.6-3.3 7.2-7.5 17.6-9.2a43 43 0 0 1 9.2-.7c6.6 0 12.4.8 15.8 2.1v9.4"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M158.4 257.7a50 50 0 0 0-23.3-2c-9.4 1.6-16.5 5.3-15.9 8.4v.2l-3.5-8.2c-.6-3.3 7.2-7.5 17.6-9.2a43 43 0 0 1 9.2-.7c6.6 0 12.4.8 15.8 2.1v9.4"/>
<path fill="#ad1519" d="M126.8 267.3c-4.3-.3-7.3-1.4-7.6-3.2q-.3-2.2 3.8-4.5 1.8.2 3.8.3z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M126.8 267.3c-4.3-.3-7.3-1.4-7.6-3.2q-.3-2.2 3.8-4.5 1.8.2 3.8.3v7.4"/>
<path fill="#ad1519" d="M142.5 261.5q4.1.6 5.7 1.9l.1.2c.5 1-1.9 3-5.9 5.4v-7.5"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M142.5 261.5q4.1.6 5.7 1.9l.1.2c.5 1-1.9 3-5.9 5.4v-7.5"/>
<path fill="#ad1519" d="M117.1 282c-.4-1.2 3.8-3.6 9.8-5.8l7.8-3.2c8.3-3.7 14.4-7.9 13.6-9.4v-.2c.4.4 1 8 1 8 .8 1.3-4.8 5.5-12.4 9.1-2.5 1.2-7.6 3-10 4-4.4 1.4-8.7 4.3-8.3 5.3l-1.5-7.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M117.1 282c-.4-1.2 3.8-3.6 9.8-5.8l7.8-3.2c8.3-3.7 14.4-7.9 13.6-9.4v-.2c.4.4 1 8 1 8 .8 1.3-4.8 5.5-12.4 9.1-2.5 1.2-7.6 3-10 4-4.4 1.4-8.7 4.3-8.3 5.3l-1.5-7.7z"/>
<path fill="#c8b100" d="M125.8 254c1.9-.6 3.1-1.5 2.5-3q-.6-1.3-2.8-.6l-2.6 1 2.3 5.8.8-.3.8-.3zm-1.2-2.7.7-.3q1-.3 1.4.8.4.8-.5 1.5l-.6.3zm7.3-2.5-.9.3h-.8l1.3 6.1 4.3-.8-.2-.4v-.4l-2.5.6zm8.4 5.2q1.2-3.3 2.7-6.4h-1l-1.8 4.6-2.4-4.3-1 .1h-1l3.5 6zm8.8-4.7.4-.9a3 3 0 0 0-1.7-.6q-2.6 0-2.8 1.7c-.2 2.1 3.2 2 3 3.4q-.2 1-1.4.8-1.3 0-1.4-1.2h-.3l-.4 1.1a4 4 0 0 0 1.8.6q2.7.2 3.2-1.7c.2-2-3.3-2.1-3.1-3.4q0-.8 1.3-.7 1 0 1.2.9z"/>
<path fill="#ad1519" d="M277.9 211.6s-.7.8-1.3.9c-.5 0-1.1-.5-1.1-.5s-.5.5-1 .6-1.4-.6-1.4-.6l-1 1q-1-.2-1.1-.3c-.1-.1-.3.4-.7.6h-.4l-.6-.4-.7-.7-.5-.3-.4-1v-.5q0-1 2.2-1.7a4 4 0 0 1 2 0q.9-.8 3-.8c2.1 0 2.4.3 3 .7a6 6 0 0 1 2.9-.7q2.1 0 3 .8.8-.3 2 0 2.2.6 2.2 1.7v.5l-.4 1-.6.3-.6.7-.6.3s-.3.2-.4 0q-.6-.3-.7-.5c-.1-.2-.6.4-1 .2s-1-1-1-1-.9.8-1.4.7c-.6-.1-1-.6-1-.6s-.7.6-1.2.5-1.2-.9-1.2-.9"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.9 211.6s-.7.8-1.3.9c-.5 0-1.1-.5-1.1-.5s-.5.5-1 .6-1.4-.6-1.4-.6l-1 1q-1-.2-1.1-.3c-.1-.1-.3.4-.7.6h-.4l-.6-.4-.7-.7-.5-.3-.4-1v-.5q0-1 2.2-1.7a4 4 0 0 1 2 0q.9-.8 3-.8c2.1 0 2.4.3 3 .7a6 6 0 0 1 2.9-.7q2.1 0 3 .8.8-.3 2 0 2.2.6 2.2 1.7v.5l-.4 1-.6.3-.6.7-.6.3s-.3.2-.4 0q-.6-.3-.7-.5c-.1-.2-.6.4-1 .2s-1-1-1-1-.9.8-1.4.7c-.6-.1-1-.6-1-.6s-.7.6-1.2.5-1.2-.9-1.2-.9z"/>
<path fill="#c8b100" d="M276.5 207.6c0-1 .6-2 1.3-2s1.3 1 1.3 2-.5 1.8-1.3 1.8q-1.2-.1-1.3-1.9"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M276.5 207.6c0-1 .6-2 1.3-2s1.3 1 1.3 2-.5 1.8-1.3 1.8q-1.2-.1-1.3-1.9z"/>
<path fill="#c8b100" d="M277.3 207.6q0-1.7.5-1.8.6.2.7 1.8c.1 1.6-.3 1.7-.6 1.7q-.6-.2-.6-1.8"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.3 207.6q0-1.7.5-1.8.6.2.7 1.8c.1 1.6-.3 1.7-.6 1.7q-.6-.2-.6-1.8z"/>
<path fill="#c8b100" d="m271 215.3-.5-1a27 27 0 0 1 14.8 0l-.6.8-.3.8a23 23 0 0 0-6.6-.8c-2.6 0-5.2.3-6.6.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m271 215.3-.5-1a27 27 0 0 1 14.8 0l-.6.8-.3.8a23 23 0 0 0-6.6-.8c-2.6 0-5.2.3-6.6.8l-.2-.6"/>
<path fill="#c8b100" d="M277.8 217.7c2.4 0 5-.4 5.9-.6q1-.3 1-.8 0-.3-.4-.4a24 24 0 0 0-6.5-.8c-2.5 0-5 .3-6.4.8q-.3 0-.4.3 0 .6 1 .9c1 .2 3.5.6 5.8.6"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.8 217.7c2.4 0 5-.4 5.9-.6q1-.3 1-.8 0-.3-.4-.4a24 24 0 0 0-6.5-.8c-2.5 0-5 .3-6.4.8q-.3 0-.4.3 0 .6 1 .9c1 .2 3.5.6 5.8.6z"/>
<path fill="#fff" d="M283.5 208.4q0-.3.4-.4.5 0 .5.4t-.5.4a.4.4 0 0 1-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M283.5 208.4q0-.3.4-.4.5 0 .5.4t-.5.4a.4.4 0 0 1-.4-.4zm-.2-1.4a.4.4 0 0 1 .4-.4q.4 0 .4.4t-.4.4a.4.4 0 0 1-.4-.4zm-1.1-1q0-.3.4-.3t.4.4-.4.4a.4.4 0 0 1-.4-.5zm-1.4-.4q0-.3.4-.4.5 0 .5.4t-.4.4q-.5-.1-.5-.4zm-1.4 0q0-.3.5-.3t.4.4q0 .3-.4.4a.4.4 0 0 1-.5-.4z"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".3" d="m287.8 211.2.2-1a2.7 2.7 0 0 0-2.7-2.8q-.8 0-1.3.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m283 209.2.2-.8q-.2-1.8-2.5-2-1 0-1.6.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M288.2 210q0-.5.4-.5t.4.4-.4.4q-.5 0-.4-.4zm-.2-1.6q0-.3.4-.4a.4.4 0 0 1 .5.4q0 .3-.4.4-.5 0-.5-.4zm-1-1.1a.4.4 0 0 1 .5-.4q.3 0 .4.4a.4.4 0 0 1-.4.4.4.4 0 0 1-.5-.4zm-1.3-.7q0-.4.5-.4t.4.4q0 .5-.4.5a.4.4 0 0 1-.5-.5zm-1.4.1q0-.3.5-.4.6 0 .4.4t-.4.4q-.4-.1-.5-.4z"/>
<path fill="#c8b100" d="m285.3 213.2-.5-.5s-.6.3-1.3.2c-.6 0-.9-1-.9-1s-.7.7-1.3.7-1-.6-1-.6-.7.5-1.3.4-1.2-.8-1.2-.8-.6.8-1.2.8c-.6.1-1-.5-1-.5s-.3.6-1.1.7-1.4-.6-1.4-.6-.4.7-1 1c-.5 0-1.2-.4-1.2-.4l-.1.5-.3.1.1.5a27 27 0 0 1 7.3-.9q4.3 0 7.3 1l.2-.6"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m285.3 213.2-.5-.5s-.6.3-1.3.2c-.6 0-.9-1-.9-1s-.7.7-1.3.7-1-.6-1-.6-.7.5-1.3.4-1.2-.8-1.2-.8-.6.8-1.2.8c-.6.1-1-.5-1-.5s-.3.6-1.1.7-1.4-.6-1.4-.6-.4.7-1 1c-.5 0-1.2-.4-1.2-.4l-.1.5-.3.1.1.5a27 27 0 0 1 7.3-.9q4.3 0 7.3 1l.2-.6z"/>
<path fill="#fff" d="M271.3 208.4q0-.3.4-.4.5 0 .4.4a.4.4 0 0 1-.4.4.4.4 0 0 1-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M271.3 208.4q0-.3.4-.4.5 0 .4.4a.4.4 0 0 1-.4.4.4.4 0 0 1-.4-.4zm.2-1.4q0-.4.4-.4t.5.4q0 .5-.5.4a.4.4 0 0 1-.4-.4zm1-1q.1-.3.5-.3t.5.4q0 .3-.5.4a.4.4 0 0 1-.4-.5zm1.4-.4q0-.3.5-.4.6 0 .4.4t-.4.4q-.4-.1-.5-.4zm1.4 0q0-.3.5-.3.3 0 .4.4 0 .3-.4.4a.4.4 0 0 1-.5-.4z"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-width=".3" d="m267.8 211.2-.2-1a2.7 2.7 0 0 1 2.7-2.8q.8 0 1.4.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m272.7 209.2-.3-.8c0-1 1.2-2 2.6-2a3 3 0 0 1 1.5.4"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M266.6 210q0-.5.4-.5t.4.4a.4.4 0 0 1-.4.4q-.3 0-.4-.4zm.1-1.6q.1-.3.5-.4.5 0 .4.4t-.4.4q-.4-.1-.4-.4zm1-1.1q0-.4.5-.4a.4.4 0 0 1 .4.4.4.4 0 0 1-.4.4.4.4 0 0 1-.5-.4zm1.3-.7q0-.4.5-.4.3 0 .4.4 0 .5-.4.5a.4.4 0 0 1-.5-.5zm1.4.1q0-.3.5-.4a.4.4 0 0 1 .4.4.4.4 0 0 1-.4.4q-.5 0-.5-.4z"/>
<path fill="#c8b100" d="M277.9 210.7h.2v.4q.1 1 1 1a1 1 0 0 0 1-.7l.2-.3v.4q.3.8 1.1.8 1 0 1-1v-.1l.4-.4.2.5-.1.4a1 1 0 0 0 1 1q.6 0 .9-.5l.2-.2v.3q0 .5.4.7 0 .1 1-.4c1-.5.7-.7.7-.7v.4s-.5.8-1 1q-.3.4-.8.3-.5 0-.7-.6l-.7.2q-1 0-1.4-.8a2 2 0 0 1-1.1.5q-.8 0-1.2-.6a2 2 0 0 1-1 .4q-.9 0-1.4-.6-.4.6-1.2.6-.6 0-1-.4a2 2 0 0 1-1.3.6q-.6 0-1.1-.5-.4.8-1.4.8-.4 0-.7-.2-.2.5-.7.6t-.9-.2l-1-1 .1-.5.8.7q.8.5.9.4.4-.1.4-.7v-.3l.2.2q.3.5.9.5a1 1 0 0 0 1-1v-.9l.4.4v.1q.1 1 1 1t1.1-.9v-.3l.2.3q.4.6 1 .7 1 0 1.1-1v-.3h.2"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.9 210.7h.2v.4q.1 1 1 1a1 1 0 0 0 1-.7l.2-.3v.4q.3.8 1.1.8 1 0 1-1v-.1l.4-.4.2.5-.1.4a1 1 0 0 0 1 1q.6 0 .9-.5l.2-.2v.3q0 .5.4.7 0 .1 1-.4c1-.5.7-.7.7-.7v.4s-.5.8-1 1q-.3.4-.8.3-.5 0-.7-.6l-.7.2q-1 0-1.4-.8a2 2 0 0 1-1.1.5q-.8 0-1.2-.6a2 2 0 0 1-1 .4q-.9 0-1.4-.6-.4.6-1.2.6-.6 0-1-.4a2 2 0 0 1-1.3.6q-.6 0-1.1-.5-.4.8-1.4.8-.4 0-.7-.2-.2.5-.7.6t-.9-.2l-1-1 .1-.5.8.7q.8.5.9.4.4-.1.4-.7v-.3l.2.2q.3.5.9.5a1 1 0 0 0 1-1v-.9l.4.4v.1q.1 1 1 1t1.1-.9v-.3l.2.3q.4.6 1 .7 1 0 1.1-1v-.3h.2z"/>
<path fill="#c8b100" d="M277.8 213.3q-4.5 0-7.3 1l-.3-.2.1-.3q3-1 7.5-1t7.6 1q.1 0 .1.3l-.3.2a27 27 0 0 0-7.4-1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.8 213.3q-4.5 0-7.3 1l-.3-.2.1-.3q3-1 7.5-1t7.6 1q.1 0 .1.3l-.3.2a27 27 0 0 0-7.4-1z"/>
<path fill="#fff" d="M275 214.4q0-.4.5-.4a.4.4 0 0 1 .4.4.4.4 0 0 1-.4.4q-.5 0-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M275 214.4q0-.4.5-.4a.4.4 0 0 1 .4.4.4.4 0 0 1-.4.4q-.5 0-.5-.4z"/>
<path fill="#ad1519" d="M277.9 214.5h-1q-.2 0-.3-.3l.3-.3h2l.2.3-.3.3h-1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.9 214.5h-1q-.2 0-.3-.3l.3-.3h2l.2.3-.3.3h-1"/>
<path fill="#058e6e" d="M273.2 214.9h-.6a.3.3 0 0 1-.4-.2l.3-.3.6-.1.7-.1q.3 0 .4.2a.3.3 0 0 1-.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M273.2 214.9h-.6a.3.3 0 0 1-.4-.2l.3-.3.6-.1.7-.1q.3 0 .4.2a.3.3 0 0 1-.3.4h-.7"/>
<path fill="#ad1519" d="m270.5 215.3.3-.4h.7l-.4.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m270.5 215.3.3-.4h.7l-.4.6-.6-.2"/>
<path fill="#fff" d="M279.8 214.4q0-.4.4-.4t.5.4q0 .3-.5.4a.4.4 0 0 1-.4-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M279.8 214.4q0-.4.4-.4t.5.4q0 .3-.5.4a.4.4 0 0 1-.4-.4z"/>
<path fill="#058e6e" d="M282.5 214.9h.7l.3-.2-.2-.3-.7-.1-.7-.1q-.3 0-.4.2 0 .3.3.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M282.5 214.9h.7l.3-.2-.2-.3-.7-.1-.7-.1q-.3 0-.4.2 0 .3.3.4h.7"/>
<path fill="#ad1519" d="m285.1 215.4-.2-.5h-.7l.3.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m285.1 215.4-.2-.5h-.7l.3.6.6-.1"/>
<path fill="#ad1519" d="M277.8 217.1a25 25 0 0 1-6-.6 25 25 0 0 1 6-.7q3.6 0 6.1.7-2.5.6-6 .6"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M277.8 217.1a25 25 0 0 1-6-.6 25 25 0 0 1 6-.7q3.6 0 6.1.7-2.5.6-6 .6z"/>
<path fill="#c8b100" d="m285.2 212-.1-.3q-.3 0-.4.2l.1.4q.3 0 .4-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m285.2 212-.1-.3q-.3 0-.4.2l.1.4q.3 0 .4-.3z"/>
<path fill="#c8b100" d="M280.6 211.2q0-.3-.3-.4-.1 0-.2.3 0 .3.2.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M280.6 211.2q0-.3-.3-.4-.1 0-.2.3 0 .3.2.4z"/>
<path fill="#c8b100" d="M275.2 211.2q0-.3.2-.4l.3.3-.2.4q-.3 0-.3-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M275.2 211.2q0-.3.2-.4l.3.3-.2.4q-.3 0-.3-.3z"/>
<path fill="#c8b100" d="m270.5 212 .1-.3q.3 0 .4.2l-.1.4q-.3 0-.4-.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m270.5 212 .1-.3q.3 0 .4.2l-.1.4q-.3 0-.4-.3z"/>
<path fill="#c8b100" d="m277.8 208.5-.8.5.6 1.3.2.1.3-.1.6-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m277.8 208.5-.8.5.6 1.3.2.1.3-.1.6-1.3-.9-.5"/>
<path fill="#c8b100" d="m276 210.5.4.5 1.3-.4.1-.2-.1-.2-1.3-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m276 210.5.4.5 1.3-.4.1-.2-.1-.2-1.3-.3-.4.6"/>
<path fill="#c8b100" d="m279.6 210.5-.3.5-1.3-.4-.1-.2v-.2l1.4-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m279.6 210.5-.3.5-1.3-.4-.1-.2v-.2l1.4-.3.4.6"/>
<path fill="#c8b100" d="m272.5 209-.7.7.9 1 .2.1.2-.1.2-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m272.5 209-.7.7.9 1 .2.1.2-.1.2-1.3-.8-.3"/>
<path fill="#c8b100" d="m271.1 211.2.5.5 1.2-.6v-.2l-.1-.2-1.3-.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m271.1 211.2.5.5 1.2-.6v-.2l-.1-.2-1.3-.1-.3.6"/>
<path fill="#c8b100" d="m274.7 210.5-.3.6h-1.3l-.2-.2.1-.3 1.2-.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m274.7 210.5-.3.6h-1.3l-.2-.2.1-.3 1.2-.6.5.5"/>
<path fill="#c8b100" d="M269.8 211.4v.6l-1.4.2-.2-.1v-.2l1-.9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M269.8 211.4v.6l-1.4.2-.2-.1v-.2l1-.9.6.4"/>
<path fill="#c8b100" d="M272.4 210.9q0-.5.5-.5a1 1 0 0 1 .5.5 1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M272.4 210.9q0-.5.5-.5a1 1 0 0 1 .5.5 1 1 0 0 1-.5.4 1 1 0 0 1-.5-.4z"/>
<path fill="#c8b100" d="m283.2 209 .7.7-.9 1-.2.1-.1-.1-.3-1.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m283.2 209 .7.7-.9 1-.2.1-.1-.1-.3-1.3.8-.3"/>
<path fill="#c8b100" d="m284.6 211.2-.5.5-1.2-.6v-.2l.1-.2 1.3-.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m284.6 211.2-.5.5-1.2-.6v-.2l.1-.2 1.3-.1.3.6"/>
<path fill="#c8b100" d="m281 210.5.3.6h1.3l.2-.2-.1-.3-1.2-.6z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m281 210.5.3.6h1.3l.2-.2-.1-.3-1.2-.6-.5.5"/>
<path fill="#c8b100" d="M285.7 211.4v.6l1.4.2.2-.1v-.2l-1-.9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M285.7 211.4v.6l1.4.2.2-.1v-.2l-1-.9-.6.4"/>
<path fill="#c8b100" d="M277.4 210.4q0-.3.5-.4.4 0 .4.4t-.4.5a1 1 0 0 1-.5-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M277.4 210.4q0-.3.5-.4.4 0 .4.4t-.4.5a1 1 0 0 1-.5-.5z"/>
<path fill="#c8b100" d="M282.3 210.9q.1-.5.5-.5t.5.5q0 .6-.5.4a1 1 0 0 1-.5-.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M282.3 210.9q.1-.5.5-.5t.5.5q0 .6-.5.4a1 1 0 0 1-.5-.4z"/>
<path fill="#c8b100" d="M277 205.4q.1-.8.8-.8 1 0 1 .8c0 .8-.5.8-1 .8a1 1 0 0 1-.8-.8"/>
<path fill="#c8b100" d="M278.5 205.1v.6H277v-.6h.4v-1.3h-.5v-.5h.5v-.6h.6v.6h.6v.6h-.6v1.2h.4"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M278.5 205.1v.6H277v-.6h.4v-1.3h-.5v-.5h.5v-.6h.6v.6h.6v.6h-.6v1.2h.4z"/>
<path fill="#c8b100" d="M279 205.1v.6h-2.4v-.6h1v-1.3h-.7v-.5h.6v-.6h.6v.6h.6v.6h-.6v1.2h1"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M278.1 204.6q.6.1.6.8t-.9.8a1 1 0 0 1-.8-.8q0-.6.6-.8"/>
<path fill="#c8b100" d="m268 212.2-.6-.7-.7-.3q0-.2.6-.3.3 0 .4.2v-.2s.3 0 .4.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m268 212.2-.6-.7-.7-.3q0-.2.6-.3.3 0 .4.2v-.2s.3 0 .4.3z"/>
<path fill="#c8b100" d="M268 212q.3-.3.5 0 .4.2.1.5l-.5-.1q-.2-.2 0-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M268 212q.3-.3.5 0 .4.2.1.5l-.5-.1q-.2-.2 0-.5z"/>
<path fill="#c8b100" d="m287.5 212.2.6-.7c.2-.2.7-.3.7-.3q0-.2-.6-.3l-.4.2v-.2s-.3 0-.4.3v.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m287.5 212.2.6-.7c.2-.2.7-.3.7-.3q0-.2-.6-.3l-.4.2v-.2s-.3 0-.4.3v.7z"/>
<path fill="#c8b100" d="M287.5 212q-.2-.3-.5 0t-.1.5l.5-.1q.3-.2.1-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M287.5 212q-.2-.3-.5 0t-.1.5l.5-.1q.3-.2.1-.5z"/>
<path fill="#c8b100" d="M267.2 223h21.4v-5.5h-21.4z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M267.2 223h21.4v-5.5h-21.4z"/>
<path fill="#c8b100" d="M286.3 226.8h-16.9q1-.4 1-1.2 0-1-1-1.3h17-.1q-1 .3-1 1.3 0 .8 1 1.2"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M286.3 226.8h-16.9q1-.4 1-1.2 0-1-1-1.3h17-.1q-1 .3-1 1.3 0 .8 1 1.2z"/>
<path fill="#c8b100" d="M269.9 226.8h16q1 0 1 .7t-1 .8h-16q-1-.1-1-.8.1-1 1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M269.9 226.8h16q1 0 1 .7t-1 .8h-16q-1-.1-1-.8.1-1 1-.8z"/>
<path fill="#c8b100" d="M269.9 223h16q1 .1 1 .7t-1 .6h-16q-1 0-1-.6t1-.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M269.9 223h16q1 .1 1 .7t-1 .6h-16q-1 0-1-.6t1-.6z"/>
<path fill="#005bbf" d="M263 317.4q2.1 0 3.7-.8a8 8 0 0 1 3.7-.8q2.2 0 3.8.8c1.6.8 2.3.8 3.7.8q2.3 0 3.8-.8a8 8 0 0 1 3.6-.8 8 8 0 0 1 3.7.8q1.6.8 3.8.8v2.4a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.8q-2.2 0-3.6.8-1.5.8-3.8.9a8 8 0 0 1-3.7-.9 8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v-2.4"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M263 317.4q2.1 0 3.7-.8a8 8 0 0 1 3.7-.8q2.2 0 3.8.8c1.6.8 2.3.8 3.7.8q2.3 0 3.8-.8a8 8 0 0 1 3.6-.8 8 8 0 0 1 3.7.8q1.6.8 3.8.8v2.4a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.8q-2.2 0-3.6.8-1.5.8-3.8.9a8 8 0 0 1-3.7-.9 8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.9v-2.4z"/>
<path fill="#ccc" d="M263 319.8q2.1 0 3.7-.9c1.6-.9 2.3-.8 3.7-.8q2.2 0 3.8.8c1.6.8 2.3.9 3.7.9a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.8q2.3 0 3.7.8 1.6.8 3.8.9v2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.8-3.8.9a7 7 0 0 1-3.7-.9q-1.5-.6-3.8-.7a8 8 0 0 0-3.7.7 8 8 0 0 1-3.8.9v-2.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M263 319.8q2.1 0 3.7-.9c1.6-.9 2.3-.8 3.7-.8q2.2 0 3.8.8c1.6.8 2.3.9 3.7.9a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.8q2.3 0 3.7.8 1.6.8 3.8.9v2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.8-3.8.9a7 7 0 0 1-3.7-.9q-1.5-.6-3.8-.7a8 8 0 0 0-3.7.7 8 8 0 0 1-3.8.9v-2.3"/>
<path fill="#005bbf" d="M263 322q2.1 0 3.7-.8 1.6-.6 3.7-.7 2.2 0 3.8.7c1.6.7 2.3.9 3.7.9a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.8 8 8 0 0 1 3.7.8q1.6.8 3.8.9v2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.9-3.8.9t-3.7-.8a8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.8V322"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M263 322q2.1 0 3.7-.8 1.6-.6 3.7-.7 2.2 0 3.8.7c1.6.7 2.3.9 3.7.9a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.8 8 8 0 0 1 3.7.8q1.6.8 3.8.9v2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.9-3.8.9t-3.7-.8a8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.8V322"/>
<path fill="#ccc" d="M263 326.7a8 8 0 0 0 3.7-.8q1.6-.8 3.7-.8 2.2 0 3.8.8c1.6.8 2.3.8 3.7.8q2.3 0 3.8-.9a8 8 0 0 1 3.6-.7q2.3 0 3.7.8a8 8 0 0 0 3.8.8v-2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.8-3.8.9t-3.7-.8a8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.8v2.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M263 326.7a8 8 0 0 0 3.7-.8q1.6-.8 3.7-.8 2.2 0 3.8.8c1.6.8 2.3.8 3.7.8q2.3 0 3.8-.9a8 8 0 0 1 3.6-.7q2.3 0 3.7.8a8 8 0 0 0 3.8.8v-2.3a8 8 0 0 1-3.8-.9 8 8 0 0 0-3.7-.7q-2.2 0-3.6.7-1.5.8-3.8.9t-3.7-.8a8 8 0 0 0-3.8-.8 8 8 0 0 0-3.7.8q-1.5.8-3.8.8v2.3"/>
<path fill="#005bbf" d="M263 329a8 8 0 0 0 3.7-.8q1.6-.8 3.7-.8 2.2 0 3.8.8c1.6.8 2.3.8 3.7.8a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.7q2.3 0 3.7.8 1.6.8 3.8.8v-2.3a8 8 0 0 1-3.8-.8 8 8 0 0 0-3.7-.8 8 8 0 0 0-3.6.7 8 8 0 0 1-3.8.9q-2.2 0-3.7-.8t-3.8-.8q-2.1 0-3.7.8c-1.6.8-2.3.8-3.8.8v2.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M263 329a8 8 0 0 0 3.7-.8q1.6-.8 3.7-.8 2.2 0 3.8.8c1.6.8 2.3.8 3.7.8a8 8 0 0 0 3.8-.9 8 8 0 0 1 3.6-.7q2.3 0 3.7.8 1.6.8 3.8.8v-2.3a8 8 0 0 1-3.8-.8 8 8 0 0 0-3.7-.8 8 8 0 0 0-3.6.7 8 8 0 0 1-3.8.9q-2.2 0-3.7-.8t-3.8-.8q-2.1 0-3.7.8c-1.6.8-2.3.8-3.8.8v2.3z"/>
<path fill="#c8b100" d="m286.3 308-.1.5c0 1.5 1.2 2.6 2.7 2.6h-22c1.5 0 2.7-1.2 2.7-2.6l-.1-.5z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="m286.3 308-.1.5c0 1.5 1.2 2.6 2.7 2.6h-22c1.5 0 2.7-1.2 2.7-2.6l-.1-.5z"/>
<path fill="#c8b100" d="M269.9 306.5h16q1 0 1 .8 0 .6-1 .7h-16q-1 0-1-.8.1-.6 1-.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M269.9 306.5h16q1 0 1 .8 0 .6-1 .7h-16q-1 0-1-.8.1-.6 1-.7z"/>
<path fill="#c8b100" d="M266.9 316.7h22V311h-22z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M266.9 316.7h22V311h-22z"/>
<path fill="#ad1519" d="M290.6 286.7c2.1 1.2 3.6 2.5 3.4 3.2q-.2.8-1.8 1.6c-1.6 1.1-2.5 3-1.8 4a5.5 5.5 0 0 1 .2-8.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M290.6 286.7c2.1 1.2 3.6 2.5 3.4 3.2q-.2.8-1.8 1.6c-1.6 1.1-2.5 3-1.8 4a5.5 5.5 0 0 1 .2-8.8z"/>
<path fill="#ccc" d="M270.1 305.6h15.6V229h-15.6v76.5z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M281.4 229.1v76.3m1.8-76.3v76.3m-13 .2h15.5V229h-15.6v76.5z"/>
<path fill="#ad1519" d="M254.2 257.7a50 50 0 0 1 23.3-2c9.3 1.6 16.4 5.3 15.9 8.4v.2l3.5-8.2c.6-3.3-7.3-7.5-17.6-9.2a54 54 0 0 0-9.2-.7c-6.7 0-12.4.8-15.9 2.1z"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M254.2 257.7a50 50 0 0 1 23.3-2c9.3 1.6 16.4 5.3 15.9 8.4v.2l3.5-8.2c.6-3.3-7.3-7.5-17.6-9.2a54 54 0 0 0-9.2-.7c-6.7 0-12.4.8-15.9 2.1v9.4"/>
<path fill="#ad1519" d="M285.7 267.3c4.4-.3 7.3-1.4 7.7-3.2q.2-2.2-3.8-4.5-1.8.2-3.9.3z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M285.7 267.3c4.4-.3 7.3-1.4 7.7-3.2q.2-2.2-3.8-4.5-1.8.2-3.9.3v7.4"/>
<path fill="#ad1519" d="M270 261.5a13 13 0 0 0-5.7 1.9v.2c-.5 1 1.8 3 5.8 5.4v-7.5"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M270 261.5a13 13 0 0 0-5.7 1.9v.2c-.5 1 1.8 3 5.8 5.4v-7.5"/>
<path fill="#ad1519" d="M295.4 282c.4-1.2-3.8-3.6-9.7-5.8-2.8-1-5-2-7.8-3.2-8.3-3.7-14.4-7.9-13.6-9.4v-.2c-.4.4-1 8-1 8-.8 1.3 4.8 5.5 12.4 9.1 2.4 1.2 7.6 3 10 4 4.3 1.4 8.7 4.3 8.3 5.3l1.4-7.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M295.4 282c.4-1.2-3.8-3.6-9.7-5.8-2.8-1-5-2-7.8-3.2-8.3-3.7-14.4-7.9-13.6-9.4v-.2c-.4.4-1 8-1 8-.8 1.3 4.8 5.5 12.4 9.1 2.4 1.2 7.6 3 10 4 4.3 1.4 8.7 4.3 8.3 5.3l1.4-7.7z"/>
<path fill="#c8b100" d="M263.9 254.4q1-3.4 2.1-6.6h-.5l-.5.1-1.4 4.8q-1.6-2-2.7-4.1l-1 .2h-1l4 5.7h.5zm6-6.6h-1.8v6.2h4.2v-.7h-2.6zm6.8 1 2 .3v-.7l-5.8-.5v.8h2l-.4 5.6h1.6l.5-5.4m2.4 6q.4 0 .8.2l.8.2.7-2.9.6 1.2.8 2.1 1 .2q.6 0 1 .3l-.3-.7q-.7-1.5-1.3-2.9 1.6 0 2.1-1.2.3-.8-.7-1.5-.8-.4-1.7-.5l-2.4-.5zm3-5.2c.7.2 1.5.3 1.5 1v.5q-.5 1.3-2 .9zm8 7-.2 2 .8.5.9.5.5-7-.7-.3-6.1 3.8.5.3.4.2 1.7-1.2 2.3 1.3zm-1.7-1.5 2-1.4-.2 2.3z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M182.2 192.4c0-1 1-2 2-2s2.2 1 2.2 2c0 1.1-1 2-2.1 2a2 2 0 0 1-2.1-2z"/>
<path fill="#ad1519" stroke="#000" stroke-width=".3" d="M205.7 175.4c6.3 0 12 1 15.7 2.4a32 32 0 0 0 14.6 2.3c2.7 0 6.5.8 10.3 2.4a27 27 0 0 1 7.4 4.7l-1.5 1.4-.4 3.8-4.1 4.7-2 1.8-5 3.9-2.5.2-.7 2.1-31.6-3.7-31.7 3.7-.8-2.1-2.5-.2-4.9-4-2-1.7-4.1-4.7-.5-3.8-1.5-1.4a28 28 0 0 1 7.5-4.7 26 26 0 0 1 10.2-2.4q3 .3 6.6-.2a30 30 0 0 0 8-2q5.6-2.4 15.5-2.5z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M206.2 217.1c-11.8 0-22.4-1.4-29.9-3.6a1 1 0 0 1-.8-1.2q0-.8.8-1.2a109 109 0 0 1 29.9-3.6c11.7 0 22.3 1.4 29.8 3.6a1.3 1.3 0 0 1 0 2.4c-7.5 2.2-18 3.6-29.8 3.6"/>
<path fill="#ad1519" d="M206.1 215.6c-10.6 0-20.2-1.2-27.5-3.1 7.3-2 16.9-3 27.5-3.1a115 115 0 0 1 27.6 3c-7.3 2-17 3.2-27.6 3.2"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M206.9 215.7v-6.3m-1.7 6.3v-6.3"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M203.6 215.7v-6.3m-1.6 6.3v-6.3"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M200.6 215.7v-6.3m-2.8 5.9v-5.7m1.3 5.8v-6m-3.8 5.6v-5.2m1.3 5.4v-5.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M192 214.8V210m1 4.7V210m1.2 5v-5m-3.4 4.7v-4.5"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M189.7 214.5v-4.2m-1.2 4.1v-4"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M186 214v-3m1.3 3.2v-3.5m-2.5 3.1V211"/>
<path fill="none" stroke="#000" stroke-width=".7" d="M183.7 213.6v-2.3m-1.3 2v-1.8m-1.2 1.6v-1.3"/>
<path fill="none" stroke="#000" stroke-width=".9" d="M179.8 212.8v-.7"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M213.7 215.3v-5.8m-2.9 6v-6.1m-2.1 6.2v-6.3"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M206 207.4a108 108 0 0 0-30 3.9c.6-.3.5-1-.3-3-1-2.5-2.4-2.4-2.4-2.4 8.3-2.5 20-4 32.8-4a123 123 0 0 1 33 4s-1.5-.1-2.5 2.3q-1.2 2.8-.2 3c-7.5-2.2-18.4-3.7-30.3-3.7"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M206.1 201.9c-12.9 0-24.5 1.5-32.8 4a1 1 0 0 1-1.3-.6 1 1 0 0 1 .7-1.3 121 121 0 0 1 33.4-4.2c13.2 0 25.2 1.7 33.5 4.2q1 .4.7 1.3t-1.3.6c-8.4-2.5-20-4-32.9-4"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".4" d="M206.1 215.6c-10.6 0-20.2-1.2-27.5-3.1 7.3-2 16.9-3 27.5-3.1a115 115 0 0 1 27.6 3c-7.3 2-17 3.2-27.6 3.2z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M197 204.8q0-.9 1-1 .9.1 1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M206.1 205.6H203a1 1 0 0 1 0-2h6.4q.9.1 1 1 0 1.2-1 1h-3.2"/>
<path fill="#058e6e" stroke="#000" stroke-width=".4" d="m190.3 206.5-2.3.2q-.9 0-1.2-.8a1 1 0 0 1 1-1.1l2.2-.3 2.4-.3q.9 0 1.1.9 0 .9-.9 1l-2.3.4"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M181 206.7q.1-1 1.1-1t1 1-1 1a1 1 0 0 1-1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="m174 208.5 1.2-1.6 3.3.4-2.6 2-1.8-.8"/>
<path fill="#058e6e" stroke="#000" stroke-width=".4" d="m222 206.5 2.3.2q.9 0 1.1-.8a1 1 0 0 0-.9-1.1l-2.2-.3-2.4-.3a1 1 0 0 0-1.1.9q0 .9.9 1l2.3.4"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M213.3 204.8q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1m15.8 1.9q.1-1 1-1t1.1 1q0 .9-1 1a1 1 0 0 1-1-1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="m238.2 208.5-1.1-1.6-3.3.4 2.6 2 1.8-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M177.3 212.8c7.4-2.1 17.6-3.4 28.8-3.4 11.3 0 21.4 1.3 28.9 3.4"/>
<path fill="#c8b100" d="m182.3 183.8 1.4 1 2-3.2a7 7 0 0 1-3.6-7.2c.2-4.1 5.2-7.6 11.7-7.6q5 .1 8.5 2.4 0-.9.2-1.8a17 17 0 0 0-8.7-2.1c-7.4 0-13.2 4.1-13.5 9.1a9 9 0 0 0 3 7.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="m182.3 183.8 1.4 1 2-3.2a7 7 0 0 1-3.6-7.2c.2-4.1 5.2-7.6 11.7-7.6q5 .1 8.5 2.4 0-.9.2-1.8a17 17 0 0 0-8.7-2.1c-7.4 0-13.2 4.1-13.5 9.1a9 9 0 0 0 3 7.6l-1 1.8"/>
<path fill="#c8b100" d="M182.4 183.8a9 9 0 0 1-4-7.3q.2-5 5.3-8a9 9 0 0 0-3.4 6.8 9 9 0 0 0 3 6.7z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M182.4 183.8a9 9 0 0 1-4-7.3q.2-5 5.3-8a9 9 0 0 0-3.4 6.8 9 9 0 0 0 3 6.7l-.9 1.8"/>
<path fill="#c8b100" d="M160.1 187.1a9 9 0 0 1-2.3-5.9q0-2 1-3.8c2-4.2 8.4-7.2 16-7.2q3 0 5.9.6l-1 1.4a26 26 0 0 0-4.9-.4c-7 0-12.8 2.7-14.5 6.3a7 7 0 0 0-.7 3.1 7 7 0 0 0 2.7 5.6l-2.6 4.1-1.3-1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M160.1 187.1a9 9 0 0 1-2.3-5.9q0-2 1-3.8c2-4.2 8.4-7.2 16-7.2q3 0 5.9.6l-1 1.4a26 26 0 0 0-4.9-.4c-7 0-12.8 2.7-14.5 6.3a7 7 0 0 0-.7 3.1 7 7 0 0 0 2.7 5.6l-2.6 4.1-1.3-1z"/>
<path fill="#c8b100" d="M162.7 173.3a11 11 0 0 0-4 4.1 9 9 0 0 0-.9 3.8q.1 3.5 2.3 5.9l-1.5 2.5a10 10 0 0 1-2.3-6.5c0-4 2.5-7.5 6.4-9.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M162.7 173.3a11 11 0 0 0-4 4.1 9 9 0 0 0-.9 3.8q.1 3.5 2.3 5.9l-1.5 2.5a10 10 0 0 1-2.3-6.5c0-4 2.5-7.5 6.4-9.8z"/>
<path fill="#c8b100" d="M206 164.4c1.7 0 3.2 1.1 3.5 2.6q.4 2.1.4 4.5v1.1c.1 3.3.6 6.3 1.3 8.1l-5.2 5-5.2-5c.7-1.8 1.2-4.8 1.3-8.1v-1.1q0-2.4.4-4.5c.3-1.5 1.8-2.6 3.5-2.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M206 164.4c1.7 0 3.2 1.1 3.5 2.6q.4 2.1.4 4.5v1.1c.1 3.3.6 6.3 1.3 8.1l-5.2 5-5.2-5c.7-1.8 1.2-4.8 1.3-8.1v-1.1q0-2.4.4-4.5c.3-1.5 1.8-2.6 3.5-2.6z"/>
<path fill="#c8b100" d="M206 166q1.6.1 1.8 1.4.3 1.8.4 4.2v1q.2 5 1.2 7.7l-3.4 3.2-3.4-3.2q1-2.7 1.2-7.7v-1l.4-4.2a2 2 0 0 1 1.8-1.4"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M206 166q1.6.1 1.8 1.4.3 1.8.4 4.2v1q.2 5 1.2 7.7l-3.4 3.2-3.4-3.2q1-2.7 1.2-7.7v-1l.4-4.2a2 2 0 0 1 1.8-1.4z"/>
<path fill="#c8b100" d="m229.7 183.8-1.3 1-2-3.2a7 7 0 0 0 3.6-6.3v-.9c-.2-4.1-5.3-7.6-11.7-7.6a15 15 0 0 0-8.5 2.4l-.2-1.8a17 17 0 0 1 8.7-2.1c7.4 0 13.2 4.1 13.4 9.1a9 9 0 0 1-3 7.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="m229.7 183.8-1.3 1-2-3.2a7 7 0 0 0 3.6-6.3v-.9c-.2-4.1-5.3-7.6-11.7-7.6a15 15 0 0 0-8.5 2.4l-.2-1.8a17 17 0 0 1 8.7-2.1c7.4 0 13.2 4.1 13.4 9.1a9 9 0 0 1-3 7.6l1 1.8"/>
<path fill="#c8b100" d="M229.6 183.8a9 9 0 0 0 4.1-7.3c0-3.2-2.1-6.1-5.3-8a9 9 0 0 1 3.4 6.8 9 9 0 0 1-3.2 6.7z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M229.6 183.8a9 9 0 0 0 4.1-7.3c0-3.2-2.1-6.1-5.3-8a9 9 0 0 1 3.4 6.8 9 9 0 0 1-3.2 6.7l1 1.8"/>
<path fill="#c8b100" d="M252 187.1a9 9 0 0 0 2.2-5.9 9 9 0 0 0-.9-3.8c-2-4.2-8.4-7.2-16-7.2a29 29 0 0 0-6 .6l1 1.4a25 25 0 0 1 5-.4c7 0 12.8 2.7 14.4 6.3q.7 1.5.7 3.1a7 7 0 0 1-2.6 5.6l2.5 4.1 1.3-1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M252 187.1a9 9 0 0 0 2.2-5.9 9 9 0 0 0-.9-3.8c-2-4.2-8.4-7.2-16-7.2a29 29 0 0 0-6 .6l1 1.4a25 25 0 0 1 5-.4c7 0 12.8 2.7 14.4 6.3q.7 1.5.7 3.1a7 7 0 0 1-2.6 5.6l2.5 4.1 1.3-1z"/>
<path fill="#c8b100" d="M249.3 173.3a11 11 0 0 1 4 4.1 9 9 0 0 1 .9 3.8 9 9 0 0 1-2.3 5.9l1.6 2.5a10 10 0 0 0 2.3-6.5c0-4-2.6-7.5-6.5-9.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M249.3 173.3a11 11 0 0 1 4 4.1 9 9 0 0 1 .9 3.8 9 9 0 0 1-2.3 5.9l1.6 2.5a10 10 0 0 0 2.3-6.5c0-4-2.6-7.5-6.5-9.8z"/>
<path fill="#fff" d="M204.2 181.4q.1-1.7 1.8-1.8c1.7-.1 1.9.8 1.9 1.8s-.9 1.7-1.9 1.7a2 2 0 0 1-1.8-1.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M204.2 181.4q.1-1.7 1.8-1.8c1.7-.1 1.9.8 1.9 1.8s-.9 1.7-1.9 1.7a2 2 0 0 1-1.8-1.7z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M204.2 178q.1-1.7 1.8-1.8c1.7-.1 1.9.8 1.9 1.8s-.9 1.7-1.9 1.7a2 2 0 0 1-1.8-1.7m.4-3.7q.1-1.2 1.4-1.3 1.4.1 1.5 1.3-.1 1.3-1.5 1.4c-1.4.1-1.4-.6-1.4-1.4m.4-3.3q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1m.2-2.8q.1-.8.8-.8t.9.8q0 .7-.9.8a1 1 0 0 1-.8-.8"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="m206.2 191.8 1.2.2a4.6 4.6 0 0 0 4.5 6 5 5 0 0 0 4.4-3c.1 0 .5-1.7.7-1.7s.1 1.8.2 1.7c.3 2.3 2.4 3.8 4.7 3.8a4.6 4.6 0 0 0 4.7-5l1.5-1.5.7 2a4 4 0 0 0-.4 1.9 4.4 4.4 0 0 0 4.5 4.2q2.5 0 3.8-1.9l.9-1.2v1.5c0 1.5.6 2.8 2 3 0 0 1.7.1 4-1.6 2.1-1.7 3.3-3.1 3.3-3.1l.2 1.7s-1.8 2.8-3.8 4c-1 .6-2.7 1.3-4 1q-2.1-.5-3-2.6a7 7 0 0 1-3.3 1 7 7 0 0 1-6.1-3.7 7 7 0 0 1-10.4-.3 7 7 0 0 1-4.6 1.8 7 7 0 0 1-5.7-3 7 7 0 0 1-5.7 3 7 7 0 0 1-4.7-1.8 7 7 0 0 1-10.4.3 7 7 0 0 1-6 3.7 7 7 0 0 1-3.4-1q-.8 2.1-3 2.7c-1.2.2-2.9-.5-4-1.1-2-1.2-3.8-4-3.8-4l.2-1.7s1.2 1.4 3.4 3.1 3.9 1.6 3.9 1.6c1.4-.2 2-1.5 2-3v-1.5l1 1.2a5 5 0 0 0 3.7 2c2.5 0 4.5-2 4.5-4.3a4 4 0 0 0-.4-2l.8-1.9 1.5 1.5v.6c0 2.4 2 4.4 4.6 4.4 2.4 0 4.4-1.5 4.7-3.8 0 0 0-1.6.2-1.7s.6 1.7.7 1.6a5 5 0 0 0 4.5 3.1 4.6 4.6 0 0 0 4.5-6l1.2-.2"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M238.6 197.7q.4-1.4-.6-1.8-.9-.2-1.5 1.1-.4 1.4.6 1.8.9.2 1.5-1.1m-20.5-4c0-.8-.3-1.6-1-1.6q-.8 0-1.2 1.4 0 1.3.9 1.6 1-.1 1.3-1.4m-23.9 0c0-.8.4-1.6 1-1.6q1 0 1.2 1.4 0 1.3-.9 1.6-1-.1-1.2-1.4m-20.6 4q-.3-1.4.6-1.8 1-.2 1.5 1.1t-.5 1.8q-1 .2-1.6-1.1"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M182.7 184a5 5 0 0 1 2.2 2.9s0-.3.6-.6 1-.3 1-.3l-.1 1.3-.3 2.2-.7 1.6a2 2 0 0 0-1.5-.4 2 2 0 0 0-1.2.9s-.7-.6-1.2-1.3l-1.1-2-.7-1.1s.5-.2 1.1 0q.9.1.8.2a5 5 0 0 1 1-3.4m.4 9.8-.6-1q0-.7.3-1.2s-.9-.5-1.8-.7c-.7-.2-2-.2-2.3-.2h-1l.2.5q.4.7.5.7a5 5 0 0 0-3 2 5 5 0 0 0 3.5 1l-.2.8v.6l1-.4c.3-.1 1.5-.5 2-1 .8-.4 1.5-1.1 1.5-1.1m2.7-.5a2 2 0 0 0 .2-1.1 2 2 0 0 0-.6-1l1.4-1.3 2-.9 1.1-.4v.6l-.2.8a5 5 0 0 1 3.4 1 5 5 0 0 1-2.9 2l.7 1.2h-1c-.4 0-1.6 0-2.3-.2l-1.8-.7"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M182.2 192.4c0-1 1-2 2-2s2.2 1 2.2 2c0 1.1-1 2-2.1 2a2 2 0 0 1-2.1-2"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M206.1 180.8a6 6 0 0 1 1.9 3.7s.2-.3.9-.5q1.1-.3 1.2-.2l-.5 1.4-.8 2.4-1 1.7a2 2 0 0 0-1.7-.7q-1 0-1.6.7-.2-.1-1-1.7l-.8-2.4-.5-1.4 1.2.2q1 .4.9.5.2-2.2 1.8-3.7"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M204.6 191.8a2 2 0 0 1-.5-1.2q0-.8.4-1.3s-.8-.7-1.8-1c-.7-.4-2-.7-2.5-.7l-1.2-.2.2.6.4.9a6 6 0 0 0-3.7 1.7q1.6 1.5 3.7 1.6l-.4 1-.2.6 1.2-.2c.4-.1 1.8-.4 2.5-.7 1-.4 1.9-1 1.9-1m3 0a2 2 0 0 0 .1-2.6s.9-.7 1.8-1a8 8 0 0 1 2.5-.7l1.2-.3-.1.7-.4.9q2.2.2 3.6 1.7a6 6 0 0 1-3.6 1.6l.5 1.6-1.2-.2-2.5-.7c-1-.4-1.8-1-1.8-1m22-8a5 5 0 0 0-2.2 3l-.7-.6q-1-.4-1-.3l.2 1.3c0 .3 0 1.3.3 2.2q.4 1.6.6 1.6a2 2 0 0 1 1.5-.4q.9.2 1.3.9l1.1-1.3q1-1.4 1.1-2l.7-1.1s-.4-.2-1 0q-1 .1-1 .2a5 5 0 0 0-1-3.4m-.3 9.8q.5-.4.6-1a2 2 0 0 0-.2-1.2s.8-.5 1.7-.7c.7-.2 2-.2 2.3-.2h1.1l-.3.5-.4.7a5 5 0 0 1 2.9 2 5 5 0 0 1-3.5 1l.2.8v.6l-1-.4c-.3-.1-1.4-.5-2-1-.8-.4-1.4-1.1-1.4-1.1m-2.8-.5-.2-1.1q0-.7.6-1s-.6-.8-1.4-1.3c-.6-.4-1.7-.8-2-.9l-1-.4v.6q.1.8.2.8a5 5 0 0 0-3.5 1q1 1.4 3 2-.1 0-.5.7l-.3.5h1c.4 0 1.7 0 2.3-.2l1.8-.7"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M226 192.4c0-1 1-2 2-2s2.1 1 2.1 2a2 2 0 0 1-2 2 2 2 0 0 1-2.1-2m23.2 4.4c-.4-.5-1.4-.4-2.2.2q-1.2 1.2-.5 2.2 1 .8 2.3-.3c.7-.6 1-1.6.5-2"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="m246.3 198 .7-1c.7-.6 1.8-.7 2.3-.2l.1.2s1-2 2.3-2.6 3.4-.5 3.4-.5a3 3 0 0 0-2.9-2.8 3 3 0 0 0-2.4 1l-.2-1s-1.3.3-1.9 1.8 0 3.6 0 3.6-.3-.9-.7-1.5a8 8 0 0 0-2.4-1.6l-1.3-.7-.1.5v.8a8 8 0 0 0-3.7.5 5 5 0 0 0 2.5 2.2l-.8.7-.4.5 1.3.2 2.5.2 1.7-.2m-80.3 0q0-.6-.7-1c-.7-.7-1.7-.8-2.2-.3l-.2.3s-1-2-2.3-2.7-3.3-.5-3.3-.5a3 3 0 0 1 2.8-2.8q1.6 0 2.4 1l.2-1s1.3.3 2 1.8c.5 1.5-.1 3.6-.1 3.6s.3-.9.8-1.5a8 8 0 0 1 2.4-1.6l1.3-.7v1.3a8 8 0 0 1 3.7.5 5 5 0 0 1-2.5 2.2l.8.7.4.5-1.2.2-2.6.2-1.7-.2"/>
<path fill="#ad1519" stroke="#000" stroke-width=".4" d="M163 196.8q1-.7 2.4.3 1.2 1 .4 2c-.5.6-1.5.5-2.2-.2s-1-1.6-.5-2m41-6.3q.2-1.8 2-2c1.8-.2 2.1.9 2.1 2 0 1-1 2-2 2a2 2 0 0 1-2.1-2"/>
<path fill="#005bbf" stroke="#000" stroke-width=".3" d="M201.8 160.6c0-2.2 1.9-4 4.3-4s4.2 1.8 4.2 4-1.9 4-4.3 4a4 4 0 0 1-4.2-4"/>
<path fill="#c8b100" stroke="#000" stroke-width=".3" d="M205 149.3v2.2h-2.4v2.2h2.3v6.3H202l-.2.6q0 .9.3 1.6h7.9q.3-.7.3-1.6l-.2-.6h-2.8v-6.3h2.3v-2.2h-2.3v-2.2z"/>
<path fill="#ccc" d="M206.5 330.6a82 82 0 0 1-35.5-8.2 23 23 0 0 1-12.8-20.4v-32h96.4v32a23 23 0 0 1-12.8 20.4 81 81 0 0 1-35.3 8.2"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M206.5 330.6a82 82 0 0 1-35.5-8.2 23 23 0 0 1-12.8-20.4v-32h96.4v32a23 23 0 0 1-12.8 20.4 81 81 0 0 1-35.3 8.2z"/>
<path fill="#ccc" d="M206.3 270h48.3v-53.5h-48.3z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M206.3 270h48.3v-53.5h-48.3z"/>
<path fill="#ad1519" d="M206.3 302c0 12.6-10.7 22.9-24 22.9s-24.2-10.3-24.2-23v-32h48.2v32"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M168.6 320.9c1.5.8 3.6 2 5.8 2.6l-.1-54.7h-5.7z"/>
<path fill="#c8b100" stroke="#000" stroke-linejoin="round" stroke-width=".5" d="M158 301.6a24 24 0 0 0 5.5 15v-47.5h-5.4z"/>
<path fill="#c7b500" stroke="#000" stroke-width=".5" d="M179.4 324.7a27 27 0 0 0 5.6 0v-55.9h-5.6v56z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M190 323.5a19 19 0 0 0 5.8-2.5v-52.2H190z"/>
<path fill="#ad1519" d="M158.1 270h48.2v-53.5H158z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M158.1 270h48.2v-53.5H158z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".5" d="M201 316c2.4-2 4.6-6.8 5.4-12.2l.1-35H201l.1 47.3z"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M206.3 302c0 12.6-10.7 22.9-24 22.9s-24.2-10.3-24.2-23v-32h48.2v32"/>
<path fill="#ad1519" d="M254.6 270v32c0 12.6-10.8 22.9-24.1 22.9s-24.2-10.3-24.2-23v-32h48.3"/>
<path fill="none" stroke="#000" stroke-width=".5" d="M254.6 270v32c0 12.6-10.8 22.9-24.1 22.9s-24.2-10.3-24.2-23v-32h48.3"/>
<path fill="#c8b100" d="m215.1 294.1.1.5q-.1 1-1.1 1a1 1 0 0 1-1.1-1v-.5h-1.5a2.5 2.5 0 0 0 1.8 2.9v3.9h1.6V297a3 3 0 0 0 1.7-1.6h4.4v-1.2zm21.8 0v1.2h-4l-.3.6 4.6 5.2-1.2 1-4.6-5.3-.2.1v8.7h-1.6V297h-.2l-4.8 5.2-1.2-1 4.7-5.3-.2-.4h-4V294h13zm2.6 0v1.2h4.4q.5 1.2 1.7 1.6v3.9h1.6V297a3 3 0 0 0 1.8-2.4v-.5h-1.6l.1.5q-.1 1-1 1-1.1 0-1.2-1l.1-.5zm-6.7 22.1a16 16 0 0 0 3.7-1l.8 1.4a18 18 0 0 1-4.3 1.2 3 3 0 0 1-2.6 2 3 3 0 0 1-2.5-2 18 18 0 0 1-4.6-1.2l.8-1.4q2 .8 4 1a3 3 0 0 1 1.5-1.3v-6.7h1.6v6.7q1.1.3 1.6 1.4zm-11-2.2-.8 1.4a17 17 0 0 1-3.6-3.1q-1.4.3-2.5-.5a2.4 2.4 0 0 1-.3-3.5l.1-.1a15 15 0 0 1-1.3-4.8h1.7a13 13 0 0 0 1 4q.8 0 1.4.2l4.1-4.5 1.3 1-4.1 4.5q.8 1.5-.1 2.8a15 15 0 0 0 3.1 2.6m-6-4.8q.6-.7 1.5 0c.9.7.5 1 .1 1.4a1 1 0 0 1-1.6.1 1 1 0 0 1 0-1.5m-2.2-4.5-1.6-.3-.3-4.3 1.7-.6v2.5q0 1.4.2 2.7m1.4-5.3 1.7.4v2.2c0-.8.3 2.1.3 2.1l-1.7.6-.3-2.7zm5.6 13.7a16 16 0 0 0 4.8 2.6l.4-1.6a14 14 0 0 1-4-2zm-.8 1.4a17 17 0 0 0 4.8 2.6l-1.2 1.1a19 19 0 0 1-4-2zm2.2-9.4 1.6.7 3-3.3-1-1.4zm-1.3-1-1-1.4 3-3.3 1.6.7zm18.1 9.9.8 1.4a17 17 0 0 0 3.6-3.1q1.4.3 2.5-.5a2.4 2.4 0 0 0 .3-3.5l-.1-.1a15 15 0 0 0 1.3-4.8h-1.7a13 13 0 0 1-1 4l-1.4.2-4.1-4.5-1.3 1 4.1 4.5a2.4 2.4 0 0 0 .1 2.8 15 15 0 0 1-3.1 2.6m6-4.8a1 1 0 0 0-1.5 0 1 1 0 0 0-.1 1.4 1 1 0 0 0 1.6.1 1 1 0 0 0 0-1.5m2.2-4.5 1.6-.3.3-4.3-1.7-.6v2.5q0 1.5-.2 2.8zm-1.4-5.3-1.7.4v2.2c0-.8-.3 2.1-.3 2.1l1.7.6.3-2.7zm-5.6 13.7a16 16 0 0 1-4.8 2.6l-.4-1.6a14 14 0 0 0 4-2zm.8 1.4a17 17 0 0 1-4.8 2.6l1.2 1.1a19 19 0 0 0 4-2zm-2.2-9.4-1.6.7-2.9-3.3 1-1.4zm1.3-1 1-1.4-3-3.3-1.6.7zm-20.1-8.7.5 1.6h4.5l.5-1.6zm21.1 0-.5 1.6h-4.5l-.5-1.6zm-11.6 21.9q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1m1.9-7.8 1.7-.4v-4.3l-1.7-.5zm-1.6 0-1.7-.4v-4.3l1.7-.5z"/>
<path fill="#c8b100" d="M211.5 294.2q.4-1.5 1.8-2V287h1.6v5.3q1.3.5 1.7 1.6h4.4v.3h-6a1 1 0 0 0-1-.6q-.6 0-1 .6zm12.2 0v-.3h4.1l.2-.3-5-5.7 1.2-1 5 5.6.2-.1V285h1.6v7.3h.3l4.9-5.5 1.2 1-4.9 5.5.3.6h4v.3zm21.6 0a1 1 0 0 1 1-.6q.7 0 1 .6h1.6q-.4-1.5-1.8-2V287h-1.6v5.3q-1.2.4-1.7 1.6h-4.4v.3zm-30.2-15 6 6.8 1.3-1-6.1-6.7.3-.6h4.4V276h-4.4a3 3 0 0 0-2.5-1.7 2.6 2.6 0 0 0-2.7 2.5 3 3 0 0 0 1.8 2.4v5.2h1.6v-5.2zm32 0v5.3h-1.7v-5.2l-.4-.2-6 6.8-1.3-1 6.2-6.9-.1-.3h-4.5V276h4.5a3 3 0 0 1 2.4-1.7 2.6 2.6 0 0 1 2.7 2.5 2.5 2.5 0 0 1-1.9 2.4zm-16.1 0v3.3h-1.7v-3.2a3 3 0 0 1-1.7-1.6h-4V276h4a3 3 0 0 1 2.5-1.7q2 .1 2.5 1.7h4v1.6h-4a3 3 0 0 1-1.6 1.6m-17.8 4-1.7.4v4.3l1.7.5zm1.6 0 1.7.4v4.3l-1.7.5zm30.6 0-1.7.4v4.3l1.7.5zm1.6 0 1.7.4v4.3l-1.7.5zm-25.5.8 1.6-.7 2.9 3.3-1 1.4zm-1.3 1-1 1.4 3 3.3 1.6-.7zm18.5-1.1-1.6-.7-3 3.3 1 1.4zm1.2 1 1 1.4-3 3.3-1.5-.7zm-20.3 9 .5-1.6h4.5l.5 1.6zm-6.7-17q0-1 1.2-1a1 1 0 0 1 1 1q0 1-1 1a1 1 0 0 1-1.2-1m12.1.8-.5 1.6H220l-.5-1.6zm0-1.6-.5-1.6H220l-.5 1.6zm15.7 17.8-.5-1.6h-4.5l-.5 1.6zm4.4-17q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1m-16.1 0q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1.1 1a1 1 0 0 1-1.1-1m6.2.8.5 1.6h4.6l.5-1.6zm0-1.6.5-1.6h4.6l.5 1.6zm-5.9 5-1.7.5v4.3l1.7.5V281m1.7 0 1.6.5v4.3l-1.6.5z"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="M232.7 316.3a16 16 0 0 0 3.7-1.1l.8 1.4a18 18 0 0 1-4.3 1.2 3 3 0 0 1-2.6 2 3 3 0 0 1-2.5-2 18 18 0 0 1-4.6-1.2l.8-1.4q2 .8 4 1a3 3 0 0 1 1.5-1.3v-6.7h1.6v6.7q1.1.3 1.6 1.4zm-4.7-20.4-.2-.5h-4V294h4l.2-.4-5-5.6 1.2-1 5 5.5h.2V285h1.7v7.3h.2l4.9-5.5 1.2 1-4.9 5.5.3.6h4v1.5h-4q0 .3-.3.5l4.7 5.3-1.3 1-4.6-5.3-.2.1v8.7h-1.6V297l-.2-.1-4.8 5.3-1.2-1 4.7-5.3m-12.8-16.7 6 6.8 1.3-1-6.1-6.7.3-.6h4.4V276h-4.4a3 3 0 0 0-2.5-1.7 2.6 2.6 0 0 0-2.6 2.5 3 3 0 0 0 1.7 2.4v5.2h1.6v-5.2zm6.5 34.8-.8 1.4a17 17 0 0 1-3.6-3.1q-1.4.3-2.5-.5a2.4 2.4 0 0 1-.3-3.5l.1-.1a15 15 0 0 1-1.2-4.8h1.6a13 13 0 0 0 1 4q.8 0 1.4.2l4.1-4.5 1.3 1-4.1 4.5q.8 1.5-.1 2.8a15 15 0 0 0 3.1 2.6zm-8.4-13.1V297a3 3 0 0 1-1.8-2.4q.2-1.6 1.8-2.4V287h1.6v5.3q1.3.4 1.7 1.6h4.4v1.5h-4.4a3 3 0 0 1-1.6 1.6v3.9h-1.7m2.3 8.3q.7-.7 1.6 0c.9.7.5 1 .1 1.4a1 1 0 0 1-1.6.1 1 1 0 0 1 0-1.5zm-2-4.5-1.7-.3-.3-4.3 1.7-.6v2.5q0 1.4.3 2.7zm1.4-5.3 1.6.4v2.2c0-.8.3 2.1.3 2.1l-1.7.6-.3-2.7v-2.6zm5.5 13.7a16 16 0 0 0 4.8 2.6l.4-1.6a14 14 0 0 1-4-2l-1.2 1m-.8 1.4a17 17 0 0 0 4.8 2.6l-1.2 1.1a19 19 0 0 1-4-2l.4-1.7"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="m221.9 305.1 1.6.7 3-3.3-1-1.4-3.6 4m-1.3-1-1-1.4 3-3.3 1.6.7-3.6 4m-7.6-9.5q.1-1 1-1 1.1.1 1.2 1-.1 1-1.1 1.1a1 1 0 0 1-1.1-1zm25.7 19.4.8 1.4a17 17 0 0 0 3.6-3.1q1.4.3 2.6-.5a2.4 2.4 0 0 0 .2-3.5l-.1-.1a15 15 0 0 0 1.3-4.8h-1.7a13 13 0 0 1-1 4l-1.4.2-4.1-4.5-1.3 1 4.1 4.5a2.4 2.4 0 0 0 .1 2.8 15 15 0 0 1-3 2.6zm8.4-13.1V297a3 3 0 0 0 1.8-2.4q0-1.6-1.8-2.4V287h-1.6v5.3q-1.2.4-1.7 1.6h-4.4v1.5h4.4q.5 1.2 1.7 1.6v3.9zm-2.3 8.3a1 1 0 0 0-1.6 0 1 1 0 0 0-.1 1.4 1 1 0 0 0 1.6.1 1 1 0 0 0 0-1.5zm2-4.5 1.7-.3.3-4.3-1.7-.6v2.5q0 1.4-.2 2.7zm-1.3-5.3-1.7.4v2.2c0-.8-.3 2.1-.3 2.1l1.7.6.3-2.7v-2.6m1.6-20.1v5.2h-1.6v-5.2l-.4-.2-6 6.8-1.2-1 6-7v-.2h-4.5V276h4.4a3 3 0 0 1 2.5-1.7 2.6 2.6 0 0 1 2.6 2.5 3 3 0 0 1-1.8 2.4zm-16 0v3.2h-1.7v-3.2a3 3 0 0 1-1.7-1.6h-4V276h4q.7-1.6 2.5-1.7c1.8-.1 2.2.7 2.5 1.7h4v1.6h-4a3 3 0 0 1-1.6 1.6zm8.8 33.8a16 16 0 0 1-4.8 2.6l-.4-1.6a14 14 0 0 0 4-2l1.2 1m.8 1.4a17 17 0 0 1-4.8 2.6l1.2 1.1a19 19 0 0 0 4-2l-.4-1.7m-27.4-31.4-1.7.5v4.3l1.7.5v-5.2m1.7 0 1.6.4v4.3l-1.6.5V283m30.5 0-1.7.5v4.3l1.7.5V283"/>
<path fill="none" stroke="#c8b100" stroke-width=".3" d="m247.1 283.1 1.7.5v4.3l-1.7.5V283m-8.6 22-1.6.7-2.9-3.3 1-1.4 3.5 4m1.3-1 1-1.4-3-3.3-1.6.7 3.6 4m-18.2-20 1.6-.7 3 3.3-1 1.4-3.6-4m-1.3 1-1 1.4 3 3.3 1.6-.7-3.6-4m18.5-1.1-1.6-.7-3 3.3 1 1.4 3.6-4m1.2 1 1 1.4-3 3.2-1.5-.6 3.5-4m-20.3 9 .5-1.6h4.5l.5 1.6h-5.5m0 1.5.5 1.6h4.5l.5-1.6h-5.5M213 277q0-1 1.2-1 1 0 1 1c0 1-.4 1-1 1a1 1 0 0 1-1.2-1zm12.1.8-.5 1.6h-4.5l-.5-1.6h5.5m0-1.6-.5-1.6h-4.5l-.5 1.6h5.5m20.1 18.5q.1-.9 1.1-1 1 .1 1.1 1-.1 1-1 1.1a1 1 0 0 1-1.2-1zm-4.4-.7-.5-1.6h-4.5l-.5 1.6h5.5m0 1.5-.5 1.6h-4.5l-.5-1.6h5.5m-11.6 21.9q.1-1 1.1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.2-1zm1.9-7.8 1.7-.4v-4.3l-1.7-.5v5.2m-1.6 0-1.7-.4v-4.3l1.7-.5v5.2m15.7-32.6q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1zm-16.1 0q.1-1 1.1-1a1 1 0 0 1 1.1 1q-.1 1-1 1a1 1 0 0 1-1.2-1zm6.2.8.5 1.6h4.6l.5-1.6h-5.5m0-1.6.4-1.6h4.6l.5 1.6h-5.5m-6 5-1.6.5v4.3l1.6.5V281m1.7 0 1.6.5v4.3l-1.6.5V281"/>
<path fill="#058e6e" d="M227.7 294.7a2.6 2.6 0 0 1 2.6-2.5 2.6 2.6 0 0 1 2.6 2.5 2.6 2.6 0 0 1-2.6 2.4c-1.4 0-2.6-1-2.6-2.4"/>
<path fill="#db4446" d="M230.9 229.7v-.6l.1-.3-2.3-.1a6 6 0 0 1-2.3-1.2q-1-1-1.6-1.2c-1.3-.2-2.3.4-2.3.4s1 .4 1.7 1.3 1.5 1.3 1.8 1.4c.6.2 2.6 0 3.1.1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M230.9 229.7v-.6l.1-.3-2.3-.1a6 6 0 0 1-2.3-1.2q-1-1-1.6-1.2c-1.3-.2-2.3.4-2.3.4s1 .4 1.7 1.3 1.5 1.3 1.8 1.4c.6.2 2.6 0 3.1.1z"/>
<path fill="#ed72aa" stroke="#000" stroke-width=".4" d="M238.1 227.5v1.4c.2.6-.1 1.2 0 1.5q0 .5.3.9l.2.9-.7-.5-.6-.4v1q.2.5.6 1.1l1 1.3c.2.5.1 1.4.1 1.4s-.4-.7-.8-.8l-1.2-.7s.7.8.7 1.5q-.2 1.4-.3 1.6c-.1.2-.3-.7-.8-1.1l-1-.9s.4 1.2.4 2v2.3l-.9-1-1-.7c0-.2.5.6.6 1.1s.3 2.3 1.8 4.5c1 1.3 2.3 3.6 5.3 2.9s1.9-4.8 1.3-6.7a17 17 0 0 1-1-4.6c0-.8.6-2.9.5-3.3a8 8 0 0 1 .2-3.1q.6-1.8.9-2.3.3-.8.4-1.3l.1-1.3.7 1.3.1 1.5s.1-1 1-1.6 1.8-1.1 2-1.4q.4-.5.3-.5c-.1 0 0 1.8-.6 2.6l-1.7 2s.7-.3 1.2-.3h.9s-.6.4-1.4 1.6c-.8 1-.5 1.2-1 2.1s-1 1-1.7 1.5c-1 .8-.5 4.2-.4 4.7.2.5 2 4.5 2 5.5s.2 3.2-1.5 4.6c-1.1 1-3 1-3.4 1.2-.4.3-1.2 1.1-1.2 2.8s.6 2 1 2.4c.6.5 1.2.2 1.3.6q.2.4.5.7t.2.8c0 .3-.8 1.1-1.1 1.7l-.8 2.4c0 .2-.1 1 .1 1.3 0 0 .9 1 .3 1.2-.4.2-.8-.2-1-.2l-.9.5q-.3-.1-.4-.8l-.1-.7q-.3 0-.4.5c0 .2 0 .8-.3.8q-.3-.2-.8-.5c-.2 0-.8-.2-.8-.4q.1-.6.7-1 .8-.1.5-.5t-.7 0c-.3.4-.8 0-.7-.2v-.8c0-.2-.4-.5.1-.8s.8.2 1.4.1q.8 0 1-.6.3-.6-.2-1.4-.6-.6-.9-.8l-.3-.9v2.2l-.7-.8c-.3-.3-.6-1.3-.6-1.3v1.3q.2.6.2.8c-.1.1-.8-.7-1-.8l-1-1-.4-1.4v-1.5l.4-1h-1.4c-.7 0-1.2-.3-1.5.2q-.4.8.2 2.8.5 1.8.4 2.1l-.7.8h-.9l-1.2-.3h-1.3l-1.1-.3c-.3.1-.8.3-.6.7q.1.8-.5.7l-.9-.2q-.8 0-.8-.4t.4-.7q.3-.5 0-.5h-.6q-.3.4-.8.4-.3-.1-.4-1c-.1-.9-.7-1.2 0-1.1.5 0 1.3.4 1.4 0q.2-.4-.2-.7c-.4-.3-.8-.4-.3-.7l.7-.5c.1-.2.4-.8.7-.6.6.2 0 .7.6 1.3s1 1 2 .8q1.4 0 1.3-.5l-.1-1v-1s-.4.3-.5.6l-.4.8v-2l-.2-.8-.3.9-.1 1s-.7-.5-.5-1.5q0-1.2.1-2c.2-.3.7-1.5 2-1.6h2.6l2-.3s-2.8-1.4-3.5-1.9a10 10 0 0 1-2-2l-.6-1.6s-.5 0-1 .3l-1.2 1-.7 1 .1-1.2v-.8s-.4 1.2-1 1.7l-1.4 1v-.8l.2-1s-.4.8-1.1 1c-.7 0-1.8 0-1.9.4q.2.8 0 1.4c0 .3-.4.5-.4.5l-.8-.4q-.6.1-.7.2c-.1.1-.3-.4-.2-.7s.7-.6.5-.8l-.8.2c-.3.1-.8.3-.8-.2q.2-.6 0-1 0-.4.2-.6l1.2-.1q0-.4-.8-.6-1-.2-.5-.8.3-.2.5-.6c.1-.2.2-.7.7-.5.5.3.4.8 1 1a4 4 0 0 0 2-.2l1.5-1 1.5-1-1-.8q-.5-.7-1-1l-1.8-.6-1.7-.5.8-.3q.4-.5.8-.6h.3-1.4c-.3-.1-1-.6-1.3-.6l-.8.1s.8-.4 1.4-.5l1-.1s-.9-.3-1.1-.6l-.6-1q-.2-.3-.6-.5l-1 .3q-.6 0-.6-.6l-.1-.5c-.2-.3-.6-.8-.2-1h1.4q-.2-.4-.8-.8c-.4-.2-1-.5-.7-.8l.8-.5c.2-.3.3-1 .7-.7.4.2.8 1.2 1.1 1.1s.3-.8.3-1c0-.4 0-1 .2-.9q.3.2 1 .5.8-.1 1 .2 0 .5-.6 1-.4.6-.3 1.4.4 1 1.2 1.4t1.7.9c.5.3 1.7 1.2 2.1 1.3l.8.4s.5-.2 1.1-.2 2.1 0 2.6-.2 1.3-.6 1-1c-.1-.6-1.3-1-1.2-1.4q0-.5 1.2-.4c.8 0 1.8.1 2-1q.5-1.4-.8-1.8c-1-.2-1.8-.2-2-1q-.4-1-.2-1.1.3-.3 1.4-.4 1.3 0 1.9-.2.2-.4.6-.9c.3-.2 1.4-.4 1.4-.4s1.4.7 2.7 1.7l2.2 2.1"/>
<path d="m228.1 226.8-.2-.6v-.3s.8 0 .7.3q0 .2-.3.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m228.1 226.8-.2-.6v-.3s.8 0 .7.3q0 .2-.3.3z"/>
<path d="M232 225.4v-.4s.7 0 1 .3c.5.4.9 1 .9 1l-.8-.4h-.5l-.3-.1v-.3z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M232 225.4v-.4s.7 0 1 .3c.5.4.9 1 .9 1l-.8-.4h-.5l-.3-.1v-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m237.3 231.3-.4-.7-.3-.4"/>
<path fill="#db4446" d="M217.4 226.6s.5.4.8.4h.8s.2-.5.1-.8c-.2-1.2-1.2-1.4-1.2-1.4s.3.7.1 1z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M217.4 226.6s.5.4.8.4h.8s.2-.5.1-.8c-.2-1.2-1.2-1.4-1.2-1.4s.3.7.1 1z"/>
<path fill="#db4446" d="M215.2 227.6s-.4-.7-1.3-.6-1.4.8-1.4.8h1.2c.3.3.4 1 .4 1l.7-.6z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M215.2 227.6s-.4-.7-1.3-.6-1.4.8-1.4.8h1.2c.3.3.4 1 .4 1l.7-.6z"/>
<path fill="#db4446" d="M214.2 230.6s-.8.1-1.2.6-.3 1.3-.3 1.3.4-.5.9-.5l1 .2-.1-.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M214.2 230.6s-.8.1-1.2.6-.3 1.3-.3 1.3.4-.5.9-.5l1 .2-.1-.8z"/>
<path d="m228.2 230.5.3-.5.3.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m228.2 230.5.3-.5.3.5h-.7"/>
<path d="m229 230.5.3-.5.4.5z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m229 230.5.3-.5.4.5h-.8"/>
<path d="m228.6 227.3.8.3-.7.4-.1-.6"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m228.6 227.3.8.3-.7.4-.1-.6"/>
<path d="m229.5 227.6.7.2-.5.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m229.5 227.6.7.2-.5.4-.2-.6"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M224.2 233.7s-.7.2-1 .6q-.4.9-.3 1c.1.1.6-.5 1.5-.3l1.2.3 1.3-.3s-.7.8-.7 1.3l.2 1.1c0 .7-.6 1.6-.6 1.6l1-.3 1.7-.8.9-1s-.2 1 0 1.4l.2 1.6.8-.6q.5-.2.9-.7l.3-1s0 .8.4 1.3l.6 1.6s.3-.8.6-1.1q.6-.7.7-1l-.1-.9.4.8m-11 .6s.5-.8 1-1l1.1-.8.9-.4m1 5 1.3-.8 1-1"/>
<path fill="#db4446" d="M216.6 240.4s-.4-.5-1.1-.3c-.7 0-1.2.9-1.2.9s.6-.2 1-.1.6.4.6.4l.4-.4z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M216.6 240.4s-.4-.5-1.1-.3c-.7 0-1.2.9-1.2.9s.6-.2 1-.1.6.4.6.4l.4-.4z"/>
<path fill="#db4446" d="M215.8 243.2s-.6 0-1.1.3c-.5.4-.5 1.2-.5 1.2s.4-.4.8-.3l.9.2v-.6c.2-.4-.1-.8-.1-.8"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M215.8 243.2s-.6 0-1.1.3c-.5.4-.5 1.2-.5 1.2s.4-.4.8-.3l.9.2v-.6c.2-.4-.1-.8-.1-.8z"/>
<path fill="#db4446" d="M217.2 245.8s0 .8.3 1.3c.4.5 1.1.5 1.1.5l-.3-.7q.2-.7.3-.8t-.7-.3z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M217.2 245.8s0 .8.3 1.3c.4.5 1.1.5 1.1.5l-.3-.7q.2-.7.3-.8t-.7-.3zm16 1.3s2 1.2 1.9 2.2c0 1-1 2.3-1 2.3"/>
<path fill="#db4446" d="M224.2 252.6s-.4-.6-1.1-.6-1.4.7-1.4.7.8-.1 1 .2l.5.6.5-.3z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M224.2 252.6s-.4-.6-1.1-.6-1.4.7-1.4.7.8-.1 1 .2l.5.6.5-.3z"/>
<path fill="#db4446" d="M222.2 255.3s-1-.1-1.4.3-.4 1.3-.4 1.3.6-.6 1-.5q.9.1 1 .3v-.7l-.3-.7"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M222.2 255.3s-1-.1-1.4.3-.4 1.3-.4 1.3.6-.6 1-.5q.9.1 1 .3v-.7l-.3-.7z"/>
<path fill="#db4446" d="M224 258.1s-.3.7 0 1.1 1 .8 1 .8-.3-.4-.2-.8c.1-.3.7-.8.7-.8l-1.4-.2"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M224 258.1s-.3.7 0 1.1 1 .8 1 .8-.3-.4-.2-.8c.1-.3.7-.8.7-.8l-1.4-.2z"/>
<path fill="#db4446" d="M236 259.3s-.8-.2-1.2 0c-.5.3-.8 1.4-.8 1.4s.7-.6 1.2-.5q.9.1 1 .3v-.8z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M236 259.3s-.8-.2-1.2 0c-.5.3-.8 1.4-.8 1.4s.7-.6 1.2-.5q.9.1 1 .3v-.8z"/>
<path fill="#db4446" d="M236.4 262.2s-.6.6-.4 1.1l.6 1s0-.7.2-1l1-.3-.7-.5z"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M236.4 262.2s-.6.6-.4 1.1l.6 1s0-.7.2-1l1-.3-.7-.5z"/>
<path fill="#db4446" d="M239.4 263s-.3.8.2 1.3q1 .6 1 .5-.2-.3-.2-1.1.3-.7.5-.7l-.8-.2-.7.3"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M239.4 263s-.3.8.2 1.3q1 .6 1 .5-.2-.3-.2-1.1.3-.7.5-.7l-.8-.2-.7.3z"/>
<path fill="#ffd691" stroke="#000" stroke-width=".5" d="M208.8 316.4q3 1 3 3.8c0 2.3-2.2 4-5 4-3 0-5.3-1.7-5.3-4 0-1.7 1-3.6 3-3.8l-.2-.4-.7-.7h1.2l.8.5.5-.7q.5-.5.6-.5l.6.6.3.5.7-.4.8-.3s0 .4-.2.7l-.1.7"/>
<path fill="#058e6e" stroke="#000" stroke-width=".5" d="M206.3 326.7s-3.8-2.6-5.5-3c-2-.4-4.5 0-5.5 0 0 0 1.2.8 1.8 1.4.5.5 2.3 1.5 3.3 1.8 3 .8 6-.2 6-.2m1 .2s2.4-2.5 5-2.9c3-.4 5 .3 6.2.6l-1.5.8c-.5.3-2 1.5-4 1.6-2 0-4.4-.3-4.8-.2l-.9.1"/>
<path fill="#ad1519" stroke="#000" stroke-width=".5" d="M206.7 323.8a5 5 0 0 1 0-7.1 5 5 0 0 1 1.5 3.5 5 5 0 0 1-1.5 3.6"/>
<path fill="#058e6e" stroke="#000" stroke-width=".5" d="M205.7 329s.6-1.5.6-2.7l-.1-2.1h.8s.3 1.1.3 2l-.1 2.4-.7.1-.8.3"/>
<path fill="#fff" d="M254 190.7q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M254 190.7q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M255.4 188.2q.1-1 1.1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M255.4 188.2q.1-1 1.1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M256.4 185.2q.1-.9 1-1 1 .1 1.1 1c.1.9-.5 1-1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M256.4 185.2q.1-.9 1-1 1 .1 1.1 1c.1.9-.5 1-1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M256.5 182q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M256.5 182q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M255.7 179q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M255.7 179q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M254.1 176.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M254.1 176.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M252 173.8q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M252 173.8q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M249.4 171.8q.1-.9 1.1-1a1 1 0 0 1 0 2q-1 0-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M249.4 171.8q.1-.9 1.1-1a1 1 0 0 1 0 2q-1 0-1-1z"/>
<path fill="#fff" d="M246.5 170.3q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M246.5 170.3q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M243.3 169.1q.1-.9 1.1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M243.3 169.1q.1-.9 1.1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M239.9 168.5q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M239.9 168.5q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M236.6 168.3q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M236.6 168.3q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M233.3 168.5q.1-1 1-1t1.1 1c.1 1-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M233.3 168.5q.1-1 1-1t1.1 1c.1 1-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M230.1 168.5q.1-1 1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M230.1 168.5q.1-1 1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M231.7 171.2q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1m.6 3.1q0-1 1-1c1 0 1 .4 1 1q0 .9-1 1a1 1 0 0 1-1-1m0 3q.2-.9 1.1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1m-1 2.8q.1-.9 1-1 1 .1 1.1 1 0 1-1 1a1 1 0 0 1-1-1m-1.9 2.6q.1-.9 1-1 1.1.1 1.2 1 0 1.2-1.1 1-1 0-1-1"/>
<path fill="#fff" d="M227.6 166.5q.1-.9 1.1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M227.6 166.5q.1-.9 1.1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M224.8 165q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M224.8 165q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M221.6 164q.1-1 1-1t1.1 1q-.1.9-1 1-1-.1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M221.6 164q.1-1 1-1t1.1 1q-.1.9-1 1-1-.1-1.1-1z"/>
<path fill="#fff" d="M218.3 163.4q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M218.3 163.4q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M215 163.5q.1-1 1.1-1t1 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M215 163.5q.1-1 1.1-1t1 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M211.7 164q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M211.7 164q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M208.6 165.1q.1-.9 1-1 1 .1 1.1 1c.1.9-.5 1-1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M208.6 165.1q.1-.9 1-1 1 .1 1.1 1c.1.9-.5 1-1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M156 190.7q0-.9 1-1c1-.1 1 .5 1 1q0 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M156 190.7q0-.9 1-1c1-.1 1 .5 1 1q0 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M154.5 188.2q.1-1 1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M154.5 188.2q.1-1 1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M153.5 185.2q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M153.5 185.2q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M153.4 182q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M153.4 182q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M154.2 179q.1-1 1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M154.2 179q.1-1 1-1t1 1c0 1-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M155.8 176.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M155.8 176.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M158 173.8q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M158 173.8q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M160.5 171.8q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M160.5 171.8q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M163.5 170.3q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M163.5 170.3q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M166.6 169.1q.1-.9 1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M166.6 169.1q.1-.9 1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M170 168.5q.1-.9 1.1-1a1 1 0 0 1 0 2q-1 0-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M170 168.5q.1-.9 1.1-1a1 1 0 0 1 0 2q-1 0-1-1z"/>
<path fill="#fff" d="M173.4 168.3q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M173.4 168.3q0-.9 1-1c1-.1 1 .5 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M176.6 168.5q.1-1 1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M176.6 168.5q.1-1 1-1t1.1 1c.1 1-.5 1-1 1a1 1 0 0 1-1.1-1z"/>
<path fill="#fff" d="M179.8 168.5q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M179.8 168.5q.1-1 1-1 1.1 0 1.2 1c.1 1-.5 1-1.1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" stroke="#000" stroke-width=".4" d="M178.2 171.2q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1m-.7 3.1q0-1 1-1c1 0 1 .4 1 1q0 .9-1 1a1 1 0 0 1-1-1m-.2 3q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1m.9 2.8q.1-.9 1-1 1 .1 1.1 1-.1 1-1 1a1 1 0 0 1-1.1-1m1.8 2.6q.1-.9 1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1"/>
<path fill="#fff" d="M182.3 166.5q.1-.9 1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M182.3 166.5q.1-.9 1-1a1 1 0 0 1 0 2 1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M185.2 165q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M185.2 165q0-1 1-1c1 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M188.3 164q.1-1 1-1t1.1 1q0 .9-1 1c-1 .1-1-.5-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M188.3 164q.1-1 1-1t1.1 1q0 .9-1 1c-1 .1-1-.5-1-1z"/>
<path fill="#fff" d="M191.6 163.4q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M191.6 163.4q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M194.9 163.5q0-1 1-1c1 0 1 .4 1 1q0 .9-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M194.9 163.5q0-1 1-1c1 0 1 .4 1 1q0 .9-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M198.2 164q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M198.2 164q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#fff" d="M201.3 165.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1"/>
<path fill="none" stroke="#000" stroke-width=".4" d="M201.3 165.1q.1-.9 1-1 1 .1 1.1 1 0 1.2-1 1a1 1 0 0 1-1-1z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M174.7 228.9h-1v-1h-1.5v3.6h1.6v2.5h-3.4v7h1.8v14.3h-3.5v7.3h27.2v-7.3h-3.5V241h1.8v-7h-3.4v-2.5h1.6V228h-1.6v.9h-.8v-1h-1.6v1h-1.1v-1h-1.6v3.6h1.6v2.5H184v-7.8h1.7v-3.5H184v.9h-1v-1h-1.5v1h-.9v-1H179v3.6h1.7v7.8h-3.3v-2.5h1.6V228h-1.6v.9h-.9v-1h-1.8zm-6 33.7H196m-27.3-1.8H196m-27.3-1.8H196m-27.3-1.7H196m-27.3-2H196m-23.8-1.6h20.2m-20.2-1.8h20.2m-20.2-2h20.2m-20.2-1.7h20.2m-20.2-1.8h20.2m-20.2-1.8h20.2m-20.2-1.7h20.2m-22-1.8h23.8m-23.8-1.8h23.8m-23.8-1.8h23.8m-23.8-1.8h23.8m-20.4-1.7h17m-10.2-1.8h3.4m-3.4-1.8h3.4m-3.4-1.8h3.4m-3.4-1.7h3.4m-5.1-2.2h6.8m-12 7.5h3.6m-5-2.2h6.6m-6.7 32.6v-1.8m0-1.8v-1.7m-1.8 1.7v1.8m3.4 0V259m1.7 3.6v-1.8m0-1.8v-1.7m0-2v-1.6m0-1.8v-2m-1.7 7.4v-2m-3.4 2v-2m7 0v2m1.5-2v-1.6m-5.1-1.8v1.8m3.5-1.8v1.8m3.3-1.8v1.8M179 252v-2m1.7-1.7v1.7m0-5.3v1.8m-1.7-3.6v1.8m1.7-3.5v1.7m-3.3-1.7v1.7m-3.5-1.7v1.7m-1.6-3.5v1.8m3.3-1.8v1.8m3.4-1.8v1.8m1.7-3.6v1.8m-3.3-1.8v1.8m-3.5-1.8v1.8m-1.6-3.6v1.8m6.7-1.8v1.8m-3.4-5.3v1.8m15.3-1.8h-3.5m5-2.2h-6.6m6.7 32.6v-1.8m0-1.8v-1.7m1.8 1.7v1.8m-3.4 0V259m-1.7 3.6v-1.8m0-1.8v-1.7m0-2v-1.6m0-1.8v-2m1.7 7.4v-2m3.4 2v-2m-7 0v2m-1.5-2v-1.6m5.1-1.8v1.8m-3.5-1.8v1.8m-3.3-1.8v1.8m1.7-1.8v-2m-1.7-1.7v1.7m0-5.3v1.8m1.7-3.6v1.8m-1.7-3.5v1.7m3.3-1.7v1.7m3.5-1.7v1.7m1.6-3.5v1.8m-3.3-1.8v1.8m-3.4-1.8v1.8m-1.7-3.6v1.8m3.3-1.8v1.8m3.5-1.8v1.8m1.6-3.6v1.8m-6.7-1.8v1.8m3.4-5.3v1.8m-7 18v-2m0-5.4v-1.8m0 5.4v-1.8m0-5.3v-1.8m0-1.8v-1.7m0-3.6v-1.8m0-1.7v-1.8m-8.3 4.6h3.5m3.3-5.3h3.4m3.3 5.3h3.5"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="M186.8 262.6v-4.7c0-.8-.4-3.5-4.6-3.5-4 0-4.4 2.7-4.4 3.5v4.7z"/>
<path fill="#c8b100" stroke="#000" stroke-width=".4" d="m179.3 258.2-2.2-.3c0-.9.2-2.2.9-2.6l2 1.5c-.3.2-.7 1-.7 1.4zm6 0 2.2-.3c0-.9-.2-2.2-.9-2.6l-2 1.5c.3.2.7 1 .7 1.4zm-2.2-2.3 1-2-2-.4-1.7.4 1.1 2zm-4.2-5.5v-4.9c0-1.3-1-2.4-2.5-2.4s-2.4 1-2.4 2.4v4.9zm6.8 0v-4.9c0-1.3 1-2.4 2.5-2.4s2.4 1 2.4 2.4v4.9zm-1.7-12 .4-4.4h-4.2l.2 4.4zm3.3 0-.4-4.4h4.4l-.5 4.4zm-10 0 .2-4.4h-4.2l.5 4.4z"/>
<path fill="#0039f0" d="M185.3 262.6v-4c0-.7-.5-2.7-3.1-2.7-2.4 0-2.9 2-2.9 2.7v4zm-6.9-12.7v-4.2c0-1-.6-2.2-2-2.2s-2 1.1-2 2.2v4.3h4zm7.8 0v-4.2c0-1 .7-2.2 2-2.2s2 1.1 2 2.2v4.3h-4z"/>
<path fill="#ad1519" d="M190.8 269.8c0-9.7 7-17.6 15.6-17.6s15.6 7.9 15.6 17.6-7 17.5-15.6 17.5-15.6-7.8-15.6-17.5"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M190.8 269.8c0-9.7 7-17.6 15.6-17.6s15.6 7.9 15.6 17.6-7 17.5-15.6 17.5-15.6-7.8-15.6-17.5z"/>
<path fill="#005bbf" d="M195.4 269.7c0-7 5-12.8 11-12.8s11 5.7 11 12.8-5 13-11 13-11-5.8-11-13"/>
<path fill="none" stroke="#000" stroke-width=".6" d="M195.4 269.7c0-7 5-12.8 11-12.8s11 5.7 11 12.8-5 13-11 13-11-5.8-11-13z"/>
<path fill="#c8b100" d="M201.2 260.9s-1.3 1.4-1.3 2.7a6 6 0 0 0 .6 2.4q-.4-.8-1.4-.8-1.3.1-1.4 1.3l.2.8.5.9q.2-.5 1-.5 1.1 0 1 1v.2h-1.2v1h1l-.8 1.5 1-.4.8.9.8-.9 1 .4-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.6 0 1 .6l.4-1 .2-.7a1.4 1.4 0 0 0-1.4-1.3q-1 0-1.4.9s.6-1.2.6-2.5-1.4-2.7-1.4-2.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M201.2 260.9s-1.3 1.4-1.3 2.7a6 6 0 0 0 .6 2.4q-.4-.8-1.4-.8-1.3.1-1.4 1.3l.2.8.5.9q.2-.5 1-.5 1.1 0 1 1v.2h-1.2v1h1l-.8 1.5 1-.4.8.9.8-.9 1 .4-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.6 0 1 .6l.4-1 .2-.7a1.4 1.4 0 0 0-1.4-1.3q-1 0-1.4.9s.6-1.2.6-2.5-1.4-2.7-1.4-2.7z"/>
<path fill="#c8b100" d="M199.2 269.9h4.1v-1h-4.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M199.2 269.9h4.1v-1h-4.1z"/>
<path fill="#c8b100" d="M211.4 260.9s-1.3 1.4-1.3 2.7.6 2.4.6 2.4q-.3-.8-1.4-.8-1.3.1-1.4 1.3l.2.8.5.9q.3-.5 1-.5a1 1 0 0 1 1 1v.2h-1.2v1h1l-.8 1.5 1-.4.8.9.8-.9 1 .4-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.7 0 1 .6l.4-1 .2-.7a1.4 1.4 0 0 0-1.4-1.3q-1 0-1.4.9s.6-1.2.6-2.5-1.4-2.7-1.4-2.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M211.4 260.9s-1.3 1.4-1.3 2.7.6 2.4.6 2.4q-.3-.8-1.4-.8-1.3.1-1.4 1.3l.2.8.5.9q.3-.5 1-.5a1 1 0 0 1 1 1v.2h-1.2v1h1l-.8 1.5 1-.4.8.9.8-.9 1 .4-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.7 0 1 .6l.4-1 .2-.7a1.4 1.4 0 0 0-1.4-1.3q-1 0-1.4.9s.6-1.2.6-2.5-1.4-2.7-1.4-2.7z"/>
<path fill="#c8b100" d="M209.4 269.9h4.1v-1h-4.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M209.4 269.9h4.1v-1h-4.1z"/>
<path fill="#c8b100" d="M206.3 269.6s-1.3 1.5-1.3 2.8.6 2.4.6 2.4q-.3-.8-1.4-.9-1.3.1-1.4 1.4l.2.7.5 1q.2-.6 1-.6a1 1 0 0 1 1 1v.3h-1.2v1h1l-.8 1.5 1-.4.8.9.8-1 1 .5-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.6 0 .9.6l.5-1 .2-.7a1.4 1.4 0 0 0-1.4-1.4q-1 0-1.4 1s.6-1.2.6-2.5-1.4-2.7-1.4-2.7"/>
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-width=".3" d="M206.3 269.6s-1.3 1.5-1.3 2.8.6 2.4.6 2.4q-.3-.8-1.4-.9-1.3.1-1.4 1.4l.2.7.5 1q.2-.6 1-.6a1 1 0 0 1 1 1v.3h-1.2v1h1l-.8 1.5 1-.4.8.9.8-1 1 .5-.7-1.5h1v-1h-1.1v-.3a1 1 0 0 1 1-1q.6 0 .9.6l.5-1 .2-.7a1.4 1.4 0 0 0-1.4-1.4q-1 0-1.4 1s.6-1.2.6-2.5-1.4-2.7-1.4-2.7z"/>
<path fill="#c8b100" d="M204.3 278.6h4.1v-1h-4.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M204.3 278.6h4.1v-1h-4.1z"/>
<path fill="#c8b100" d="M237.6 223.4h-.3l-.3.4q-.4.3-.8 0l-.1-.4h-.5q-.4-.3-.1-.7v-.5h-.3l-.1.2q-.3.4-.7.2v-.2h-.3c-.5.2-.7-1-.7-1.2l-.2.2s.2.7.1 1.2q-.2 1-.3 1.2a9 9 0 0 1 2.9 1.6 9 9 0 0 1 2.2 2.3l1.2-.5c.6-.2 1.3-.2 1.3-.2l.2-.2c-.3 0-1.5.1-1.5-.4v-.2h-.2q-.3-.3 0-.7l.2-.1v-.3h-.3l-.2.1q-.4.5-.8 0l-.1-.4h-.5q-.4-.3 0-.8l.2-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M237.6 223.4h-.3l-.3.4q-.4.3-.8 0l-.1-.4h-.5q-.4-.3-.1-.7v-.5h-.3l-.1.2q-.3.4-.7.2v-.2h-.3c-.5.2-.7-1-.7-1.2l-.2.2s.2.7.1 1.2q-.2 1-.3 1.2a9 9 0 0 1 2.9 1.6 9 9 0 0 1 2.2 2.3l1.2-.5c.6-.2 1.3-.2 1.3-.2l.2-.2c-.3 0-1.5.1-1.5-.4v-.2h-.2q-.3-.3 0-.7l.2-.1v-.3h-.3l-.2.1q-.4.5-.8 0l-.1-.4h-.5q-.4-.3 0-.8l.2-.3z"/>
<path d="M235.4 224h.2v.3h-.1q-.1-.1 0-.2"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M235.4 224h.2v.3h-.1q-.1-.1 0-.2z"/>
<path d="m236.3 224.8-.3-.2v-.2h.1l.4.3.3.2v.2h-.2z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m236.3 224.8-.3-.2v-.2h.1l.4.3.3.2v.2h-.2l-.3-.3"/>
<path d="m234.6 223.7-.2-.2s-.1 0 0-.1l.3.1.3.1v.2h-.1z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m234.6 223.7-.2-.2s-.1 0 0-.1l.3.1.3.1v.2h-.1l-.3-.1"/>
<path d="M233.7 223h.2v.2h-.2s-.1-.1 0-.2"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M233.7 223h.2v.2h-.2s-.1-.1 0-.2z"/>
<path d="M237.3 225.5v-.2h-.3l.1.2z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M237.3 225.5v-.2h-.3l.1.2z"/>
<path d="m237.9 226.2.2.2h.1q0 0 0-.2l-.2-.2-.2-.2h-.1v.2z"/>
<path fill="none" stroke="#000" stroke-width=".1" d="m237.9 226.2.2.2h.1q0 0 0-.2l-.2-.2-.2-.2h-.1v.2l.2.2"/>
<path d="M238.8 227v-.3h-.3v.2h.3"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M238.8 227v-.3h-.3v.2h.3z"/>
<path fill="#c8b100" d="M236.2 221.1h-.6l-.1.9v.1h.2l.7-.5-.3-.5"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M236.2 221.1h-.6l-.1.9v.1h.2l.7-.5-.3-.5"/>
<path fill="#c8b100" d="M234.6 221.6v.5l.9.1h.1v-.2l-.5-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M234.6 221.6v.5l.9.1h.1v-.2l-.5-.7-.5.3"/>
<path fill="#c8b100" d="m236.4 222.6-.4.3-.6-.7v-.1h1.1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m236.4 222.6-.4.3-.6-.7v-.1h1.1v.5"/>
<path fill="#c8b100" d="M235.3 222h.4v.3h-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M235.3 222h.4v.3h-.3z"/>
<path fill="#c8b100" d="m233.2 221.1-.2-.7-.4-.4s.4-.2.8.1 0 .9 0 .9z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m233.2 221.1-.2-.7-.4-.4s.4-.2.8.1 0 .9 0 .9z"/>
<path fill="#c8b100" d="m234.2 221.4-.4.4-.6-.6v-.2h1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m234.2 221.4-.4.4-.6-.6v-.2h1v.4"/>
<path fill="#c8b100" d="m233.1 221 .3-.1v.3q0 .3-.2.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m233.1 221 .3-.1v.3q0 .3-.2.2z"/>
<path fill="#c8b100" d="M238.3 222.5h-.5l-.3.7v.2h.2l.8-.4z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M238.3 222.5h-.5l-.3.7v.2h.2l.8-.4-.2-.5"/>
<path fill="#c8b100" d="M236.7 222.8v.5l.8.2h.1v-.2l-.4-.7z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M236.7 222.8v.5l.8.2h.1v-.2l-.4-.7-.5.2"/>
<path fill="#c8b100" d="m238.4 224-.5.2-.4-.7v-.2h.1l.9.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m238.4 224-.5.2-.4-.7v-.2h.1l.9.2-.1.5"/>
<path fill="#c8b100" d="M237.3 223.2h.4v.4h-.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M237.3 223.2h.4v.4h-.3z"/>
<path fill="#c8b100" d="m240.2 224.3.1.5-.8.3h-.2v-.2l.4-.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m240.2 224.3.1.5-.8.3h-.2v-.2l.4-.8.5.2"/>
<path fill="#c8b100" d="m240 225.8-.5.1-.3-.8v-.1h.2l.8.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m240 225.8-.5.1-.3-.8v-.1h.2l.8.3-.1.5"/>
<path fill="#c8b100" d="m238.6 224.3-.2.5.9.3h.1v-.1l-.3-.8z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m238.6 224.3-.2.5.9.3h.1v-.1l-.3-.8-.5.1"/>
<path fill="#c8b100" d="M239.5 225.2v-.3h-.4v.3z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M239.5 225.2v-.3h-.4v.3z"/>
<path fill="#c8b100" d="M240.8 227h.8l.5.3s.1-.4-.3-.7c-.3-.3-.8.2-.8.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M240.8 227h.8l.5.3s.1-.4-.3-.7c-.3-.3-.8.2-.8.2z"/>
<path fill="#c8b100" d="m240.3 226.1-.3.5.8.5v-.1h.2l-.1-1z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="m240.3 226.1-.3.5.8.5v-.1h.2l-.1-1-.6.1"/>
<path fill="#c8b100" d="M241 227s.1-.1 0-.2h-.3q-.3 0-.1.2z"/>
<path fill="none" stroke="#000" stroke-width=".3" d="M241 227s.1-.1 0-.2h-.3q-.3 0-.1.2zm38-21.9v.6h-2.4v-.6h1v-1.3h-.7v-.5h.6v-.6h.6v.6h.6v.6h-.6v1.2h1"/>
<path fill="none" d="M134.4 217.1v-1.2m-.4 1.2v-1.2m-.2 1.2v-1.2m-.3 1.2v-1.2"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M133.2 217.1v-1.2m-.5 1.1v-1m.2 1v-1m-.7 1v-1m.2 1v-1m-.9 1v-1m.2 1v-1m.3 1v-1m-.7 1v-1m-.3.9v-.8m-.1.8v-.8m-.5.7v-.6m.2.6v-.6m-.4.5v-.5m-.2.5v-.4m-.3.3v-.3m-.3.3v-.2"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M129.2 216.6v-.2"/>
<path fill="none" d="M135.7 217v-1m-.5 1v-1m-.4 1.2V216m143 1.1V216m-.4 1.1V216m-.3 1.1V216m-.3 1.2V216"/>
<path fill="none" stroke="#000" stroke-width=".1" d="M276.6 217.1V216m-.6 1v-1m.3 1v-1m-.8 1v-1m.3 1v-1m-.9 1v-1m.2 1v-1m.2 1v-1m-.6 1v-1m-.3.9v-.8m-.2.8v-.8m-.4.7v-.6m.2.6v-.6m-.5.6v-.6m-.2.5v-.4m-.3.4v-.4m-.2.3v-.2"/>
<path fill="none" stroke="#000" stroke-width=".2" d="M272.6 216.6v-.2"/>
<path fill="none" d="M279.1 217v-1m-.6 1v-1m-.4 1.1V216"/>
</svg>

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-fi" viewBox="0 0 640 480">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#002f6c" d="M0 174.5h640v131H0z"/>
<path fill="#002f6c" d="M175.5 0h130.9v480h-131z"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-fr" viewBox="0 0 640 480">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#000091" d="M0 0h213.3v480H0z"/>
<path fill="#e1000f" d="M426.7 0H640v480H426.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 231 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 640 480">
<path fill="#012169" d="M0 0h640v480H0z"/>
<path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"/>
<path fill="#C8102E" d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"/>
<path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z"/>
<path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 504 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-hu" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#fff" d="M640 480H0V0h640z"/>
<path fill="#388d00" d="M640 480H0V320h640z"/>
<path fill="#d43516" d="M640 160.1H0V.1h640z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 271 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-id" viewBox="0 0 640 480">
<path fill="#e70011" d="M0 0h640v240H0Z"/>
<path fill="#fff" d="M0 240h640v240H0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 178 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ie" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#009A49" d="M0 0h213.3v480H0z"/>
<path fill="#FF7900" d="M426.7 0H640v480H426.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-in" viewBox="0 0 640 480">
<path fill="#f93" d="M0 0h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#128807" d="M0 320h640v160H0z"/>
<g transform="matrix(3.2 0 0 3.2 320 240)">
<circle r="20" fill="#008"/>
<circle r="17.5" fill="#fff"/>
<circle r="3.5" fill="#008"/>
<g id="in-d">
<g id="in-c">
<g id="in-b">
<g id="in-a" fill="#008">
<circle r=".9" transform="rotate(7.5 -8.8 133.5)"/>
<path d="M0 17.5.6 7 0 2l-.6 5z"/>
</g>
<use xlink:href="#in-a" width="100%" height="100%" transform="rotate(15)"/>
</g>
<use xlink:href="#in-b" width="100%" height="100%" transform="rotate(30)"/>
</g>
<use xlink:href="#in-c" width="100%" height="100%" transform="rotate(60)"/>
</g>
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(120)"/>
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(-120)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-it" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#009246" d="M0 0h213.3v480H0z"/>
<path fill="#ce2b37" d="M426.7 0H640v480H426.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-jp" viewBox="0 0 640 480">
<defs>
<clipPath id="jp-a">
<path fill-opacity=".7" d="M-88 32h640v480H-88z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="1pt" clip-path="url(#jp-a)" transform="translate(88 -32)">
<path fill="#fff" d="M-128 32h720v480h-720z"/>
<circle cx="523.1" cy="344.1" r="194.9" fill="#bc002d" transform="translate(-168.4 8.6)scale(.76554)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-ke" viewBox="0 0 640 480">
<defs>
<path id="ke-a" stroke-miterlimit="10" d="m-28.6 47.5 1.8 1 46.7-81c2.7-.6 4.2-3.2 5.7-5.8 1-1.8 5-8.7 6.7-17.7a58 58 0 0 0-11.9 14.7c-1.5 2.6-3 5.2-2.3 7.9z"/>
</defs>
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#000001" d="M0 0h640v144H0z"/>
<path fill="#060" d="M0 336h640v144H0z"/>
<g id="ke-b" transform="matrix(3 0 0 3 320 240)">
<use xlink:href="#ke-a" width="100%" height="100%" stroke="#000"/>
<use xlink:href="#ke-a" width="100%" height="100%" fill="#fff"/>
</g>
<use xlink:href="#ke-b" width="100%" height="100%" transform="matrix(-1 0 0 1 640 0)"/>
<path fill="#b00" d="M640.5 168H377c-9-24-39-72-57-72s-48 48-57 72H-.2v144H263c9 24 39 72 57 72s48-48 57-72h263.5z"/>
<path id="ke-c" d="M377 312c9-24 15-48 15-72s-6-48-15-72c-9 24-15 48-15 72s6 48 15 72"/>
<use xlink:href="#ke-c" width="100%" height="100%" transform="matrix(-1 0 0 1 640 0)"/>
<g fill="#fff" transform="matrix(3 0 0 3 320 240)">
<ellipse rx="4" ry="6"/>
<path id="ke-d" d="M1 5.8s4 8 4 21-4 21-4 21z"/>
<use xlink:href="#ke-d" width="100%" height="100%" transform="scale(-1)"/>
<use xlink:href="#ke-d" width="100%" height="100%" transform="scale(-1 1)"/>
<use xlink:href="#ke-d" width="100%" height="100%" transform="scale(1 -1)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-kr" viewBox="0 0 640 480">
<defs>
<clipPath id="kr-a">
<path fill-opacity=".7" d="M-95.8-.4h682.7v512H-95.8z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#kr-a)" transform="translate(89.8 .4)scale(.9375)">
<path fill="#fff" d="M-95.8-.4H587v512H-95.8Z"/>
<g transform="rotate(-56.3 361.6 -101.3)scale(10.66667)">
<g id="kr-c">
<path id="kr-b" fill="#000001" d="M-6-26H6v2H-6Zm0 3H6v2H-6Zm0 3H6v2H-6Z"/>
<use xlink:href="#kr-b" width="100%" height="100%" y="44"/>
</g>
<path stroke="#fff" d="M0 17v10"/>
<path fill="#cd2e3a" d="M0-12a12 12 0 0 1 0 24Z"/>
<path fill="#0047a0" d="M0-12a12 12 0 0 0 0 24A6 6 0 0 0 0 0Z"/>
<circle cy="-6" r="6" fill="#cd2e3a"/>
</g>
<g transform="rotate(-123.7 191.2 62.2)scale(10.66667)">
<use xlink:href="#kr-c" width="100%" height="100%"/>
<path stroke="#fff" d="M0-23.5v3M0 17v3.5m0 3v3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-lu" viewBox="0 0 640 480">
<path fill="#ed2939" d="M0 0h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#00a1de" d="M0 320h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,70 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-md" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#00319c" d="M0 0h213.3v480H0z"/>
<path fill="#ffde00" d="M213.3 0h213.4v480H213.3z"/>
<path fill="#de2110" d="M426.7 0H640v480H426.7z"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width=".7" d="m360.4 320.4 24.2 35c1.4 2 2.7 3.9.7 5.2s-1.4 6.4 0 8.4c2.7 3.9 6 4.5 8 3.2l18.4-11.6c2-1.2 2.7-4.5 0-8.4-1.5-2-6-4.5-8-3.2-2.1 1.3-3.5-.7-4.8-2.6l-24.3-35"/>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="2.5" 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(.52728 -.42386 .44358 .50386 -32.4 20)"/>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width=".7" d="m262 306.1-21.5 36.7c-1.2 2-2.4 4-4.5 3-2.1-1.2-6.6 1.7-7.8 3.8-2.4 4-1.5 7.2.7 8.4l19.2 10.2c2 1.1 5.4.2 7.8-3.8 1.1-2.2 1.4-7.3-.7-8.4s-1-3.2.3-5.2l21.4-36.7"/>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="1.7" d="M331.2 280.6h-19.6c-.6.6-6.2 35.6-12.2 49-7.3 16.3-29.2 30.7-29.2 30.7s13.1 4 17 6.6c1-.3 22-4.7 24.4-30.3 2.4 25.6-14.6 37.3-14.6 37.3s24.4 7 24.4 23.3c0-16.3 24.4-23.3 24.4-23.3s-12.2-11.7-14.7-37.3c5 25.6 23.5 30 24.5 30.3 3.9-2.6 17-6.6 17-6.6s-21.9-14.4-29.2-30.7c-6-13.4-11.6-48.4-12.2-49zm-83-116.7 36.6 11.7c12.1 0 24.4-11.7 24.4-23.4.4-26-9.8-35-12.2-35l4.8-4.6-7.3-7s2.5-12.1 26.9-11.7 24.4 11.7 24.4 23.3-12.2 11.7-12.2 35c0 11.7 12.2 23.4 24.4 23.4l36.6-11.7v116.7H248.1z"/>
<g fill="none" stroke="#000">
<path stroke-width=".5" 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(.16672 0 0 .46773 253.9 31.9)"/>
<path stroke-width=".2" d="m432.3 435.8-7.1 39m10.6-39v39m7.1-39v39m7.1-39 3.5 39m3.6-39 3.5 39" transform="matrix(.68905 0 0 .65843 16.2 -144)"/>
</g>
<g stroke="#000" stroke-width="2.5">
<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(-.68905 0 0 .65843 443.4 -144)"/>
<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(-.68905 0 0 .65843 443.4 -237.3)"/>
<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(-.68905 0 0 -.65843 443.4 530.2)"/>
<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(-.68905 0 0 .65843 443.4 -190.7)"/>
<path fill="none" d="m301.4 557-.2 158.8" transform="matrix(-.68905 0 0 1.24911 443.4 -566.8)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(-.68905 0 0 1.12111 431.4 -464)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(-.68905 0 0 1.2559 419.2 -548.3)"/>
</g>
<g stroke="#000" stroke-width="2.5">
<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(.68905 0 0 .65843 199.3 -144)"/>
<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(.68905 0 0 .65843 199.3 -237.3)"/>
<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(.68905 0 0 -.65843 199.3 530.2)"/>
<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(.68905 0 0 .65843 199.3 -190.7)"/>
<path fill="none" d="m301.4 557-.2 158.8" transform="matrix(.68905 0 0 1.24911 199.3 -566.8)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(.68905 0 0 1.12111 211.4 -464)"/>
<path fill="none" d="m301.4 539.3-.2 176.5" transform="matrix(.68905 0 0 1.2559 223.6 -548.3)"/>
</g>
<path fill="#a77b3b" fill-rule="evenodd" stroke="#000" stroke-width="2.5" 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(.63605 .25323 -.26502 .60778 361.1 -165.2)"/>
<path fill="red" fill-rule="evenodd" d="M248.1 175.6h146.5v70H248.1z"/>
<path fill="#564dff" fill-rule="evenodd" d="M248.1 245.6h146.5v35c0 23.3-36.6 23.3-73.2 46.6C284.7 304 248 304 248 280.6z"/>
<path fill="none" stroke="#ff0" stroke-width="2" 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(.68905 0 0 .65843 16.2 -144)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="2.5" 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(.48234 0 0 .30917 108.5 140.1)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="2.5" 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(.48234 0 0 .30917 110.4 201.6)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="2.5" 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(-.48234 0 0 .30917 532.4 201.6)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="2.5" d="M414.6 228.5a16 16 0 1 1-32 0 16 16 0 0 1 32 0z" transform="matrix(.5544 0 0 .38542 82.7 183.7)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="2.5" d="M414.6 228.5a16 16 0 1 1-32 0 16 16 0 0 1 32 0z" transform="matrix(.55441 0 0 .38543 118 183.7)"/>
<path fill-rule="evenodd" d="M336.8 307.5c0 1.5-2 2.7-4.3 2.7-2.4 0-4.3-1.2-4.3-2.7s2-2.7 4.3-2.7 4.3 1.2 4.3 2.7m-22.3 0c0 1.5-1.9 2.7-4.2 2.7s-4.3-1.2-4.3-2.7 1.9-2.7 4.3-2.7c2.3 0 4.2 1.2 4.2 2.7"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" 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(.67225 0 0 .64237 26 -133.3)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="m496 591.7 21.3 10.7 21.3-10.7-21.3-10.6z" transform="matrix(.68905 0 0 .65843 16.2 -144)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="m496 591.7 21.3 10.7 21.3-10.7-21.3-10.6z" transform="matrix(.68905 0 0 .65843 -88.8 -144)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" 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(.59753 -.3279 .34316 .57097 -165.2 86.5)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 13.7 -155.7)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 8.8 -139.4)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 6.4 -148.7)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 18.6 -139.4)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 21 -148.7)"/>
<path fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width="1.1" d="M386.2 652a7 7 0 1 1-14.1 0 7 7 0 0 1 14.1 0z" transform="matrix(.68905 0 0 .65843 13.7 -146.4)"/>
<g fill-rule="evenodd">
<path fill="#da4500" d="M297.8 120.4c-12.4 0-14 2-19.6 3.9 0-5.4 13.8-11.7 26-11.7 0 3.9-3.2 7.8-6.4 7.8"/>
<path fill="#cac9c7" d="M316.5 108c0 2.5-2.2 4.6-4.9 4.6s-4.9-2.1-4.9-4.7 2.2-4.6 4.9-4.6 4.9 2 4.9 4.6z"/>
<path fill="#ff0" stroke="#000" stroke-width="1.1" d="M361.4 357.9v10.6h17.7v53.1h10.7v-53.1h17.7v-10.6h-17.7v-14.2H379v14.2z" transform="matrix(.68905 0 0 .65843 16.2 -144)"/>
<path fill="#da4500" d="M304.3 112.6a79 79 0 0 0-29.3 3.9c0-7.2 8.4-15.6 22.8-15.6 3.2 0 6.5 7.8 6.5 11.7"/>
</g>
<g fill-rule="evenodd">
<path fill="#008500" stroke="#000" stroke-width=".7" 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(.38821 .29653 -.14073 .23692 237.4 71.3)"/>
<path fill="#008f00" d="M228.4 330.4c.8-10.6-18.6-22-19.4-21.3-.7.8 1.2 7 4.9 10.1-7-3.6-14.4-5.8-14.6-4.3-.6 2.2 11 7.2 12 9.2 0 1.4-8.6 1.2-8.4 2.6s24.3 13.6 25.5 3.7M209 299.6c.8-10.6-18.6-22-19.3-21.3-.8.7 1.1 6.9 4.8 10.1-7-3.6-14.4-5.9-14.6-4.3-.5 2.1 11 7.2 12 9.2.1 1.4-8.5 1.2-8.4 2.6.2 1.4 24.3 13.5 25.5 3.7m-14.8-28c.8-10.6-18.6-22-19.4-21.3s1.2 6.9 4.9 10.1c-7-3.6-14.4-5.9-14.6-4.3-.5 2.1 11 7.2 12 9.2 0 1.4-8.5 1.2-8.4 2.6.2 1.4 24.3 13.6 25.5 3.7m-14.6-30.4c.7-10.5-18.6-22-19.4-21.3-.8.8 1.2 7 4.8 10.2-7-3.7-14.4-5.9-14.5-4.3-.6 2.1 11 7.2 11.9 9.1.1 1.4-8.5 1.3-8.3 2.7s24.2 13.5 25.5 3.6"/>
<path fill="#008f00" d="M184.6 239.9c9.4-5.7 7.5-27.4 6.5-27.6-1.1-.1-5.2 5-5.7 9.8-1.1-7.6-3.6-14.6-5-13.8-2.1.8.4 12.7-.7 14.6-1 1-6-5.8-7-4.8-1.2 1 2.8 26.7 12 21.8zm13.6 26.6c10.8-2.4 16.7-23.4 15.7-23.9s-6.6 3-8.7 7.4c1.6-7.5 1.7-14.9 0-14.6-2.3.1-4 12.1-5.7 13.5-1.3.6-3.7-7.3-5-6.8-1.4.6-6.6 26.1 3.7 24.4m16.2 27.2c9.9-4.8 10-26.6 9-26.8-1-.3-5.5 4.5-6.5 9.2-.4-7.7-2.2-14.8-3.7-14.1-2.2.6-.8 12.6-2 14.4-1.2.9-5.5-6.3-6.7-5.4s.4 26.8 10 22.7zm17.6 35.6c10.3-3.9 12.8-25.5 11.8-25.9s-6 4-7.5 8.6c.4-7.7-.6-15-2.2-14.4-2.3.4-2.1 12.5-3.5 14.2-1.3.7-4.8-6.8-6-6-1.3.6-2.5 26.7 7.4 23.5"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1.1" 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(.60164 .32097 -.3359 .5749 389.6 -146.8)"/>
<g fill="#ff0" fill-rule="evenodd" stroke="#000" stroke-width=".5">
<path d="M156 414.6v-7.1h7v-7.1h7v7h7.2v7.2H170v17.7h-7v-17.7zm7 60.2h7v205.5h-7z" transform="matrix(.49887 .1571 -.1826 .4713 451.4 -3.7)"/>
<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(.49887 .1571 -.1826 .4713 451.4 -3.7)"/>
</g>
<path fill="#ff1900" fill-rule="evenodd" stroke="#000" stroke-width="1.1" 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(.57449 -.36357 .38048 .54895 -2.3 4.2)"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,382 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-mx" viewBox="0 0 640 480">
<defs>
<radialGradient xlink:href="#mx-a" id="mx-b" cx="842.3" cy="103.7" r="25.9" gradientTransform="matrix(.14152 .03595 -.03453 .14198 213.1 162.4)" gradientUnits="userSpaceOnUse"/>
<radialGradient xlink:href="#mx-a" id="mx-c" cx="651.5" cy="550.5" r="25.9" gradientTransform="matrix(-.13441 -.05384 .04964 -.12489 397.9 -24.3)" gradientUnits="userSpaceOnUse"/>
<radialGradient xlink:href="#mx-a" id="mx-d" cx="380.8" cy="740.4" r="25.9" gradientTransform="matrix(.07536 .00282 -.00343 .14804 412.4 -203.6)" gradientUnits="userSpaceOnUse"/>
<linearGradient id="mx-a">
<stop offset="0" stop-color="#fff"/>
<stop offset="1" stop-color="#f15770"/>
</linearGradient>
</defs>
<path fill="#ce1126" d="M426.7 0H640v480H426.7z"/>
<path fill="#fff" d="M213.3 0h213.4v480H213.3z"/>
<path fill="#006847" d="M0 0h213.3v480H0z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m355.8 289.4.2 4.5 1.7-1.1-1.3-3.7z"/>
<circle cx="355.6" cy="288.2" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m361.1 296.4-3.2-3.1-1.5 1.2 4.5 2.6z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M360.9 298.2q-.7-1 .3-2 1.2-.7 2 .2.7 1-.3 2-1.2.7-2-.2z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m386.3 249.6 3.4 3.3.4-1.7-3.1-2z"/>
<circle cx="385.9" cy="248.7" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="M395.2 251.6 390 253l.5-1.7 4.4-.4z"/>
<circle cx="396" cy="250.8" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m378 276.8-3.2-4.8.5-.3 3.5 4.2z"/>
<circle cx="374.5" cy="270.8" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m378.1 277 4 .7.1-.5-3.3-1.4z"/>
<circle cx="383.3" cy="277.7" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M284.6 288q-.1 1.2-1 1.2-1.2 0-1-1.2c.2-1.2.5-1.3 1-1.3s1 .7 1 1.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m284.6 290.3 1 5-1.3-.5-.4-4.3z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M285.7 288.6c.6.7-.4 1.9-1.4 2.2s-2.4-.2-2.4-1.2 1.6-.5 2-.6c.6-.2 1.2-1.2 1.8-.4z"/>
<ellipse cx="277" cy="296.3" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx="1.6" ry="1.1"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m279.6 296 4.8-.2-.8-1-4 .4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M280 295.4c.5 1.3.5 3-.9 2.7-1.4-.1-1-1.4-1.2-1.8-.2-.9-1-1.7-.2-2.5s2 .4 2.3 1.6z"/>
<ellipse cx="264.4" cy="269.2" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx=".9" ry="1.4"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m264.4 272.4.1 4.6-1.2-1v-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M266.2 271c.3 1-1.3 1.6-2.4 1.4s-1.9-.7-1.7-1.7 1.5-.8 2-.5 1.8-.8 2.1.7z"/>
<ellipse cx="256.2" cy="276.5" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx="1.6" ry=".7"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m259.1 276.5 3.6-.3 1.6 1.2-5.3-.2z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M257.8 274.5c1 0 1.6 1.1 1.5 2.3q-.4 1.8-2 2c-.9-.2-.8-1-.8-1.2 0-.3.5-.7.6-1s-.3-1.3-.1-1.6q0-.5.8-.5zm-3-28.3c-.4.6-1.2 1.1-1.6.9s-.2-1.2.2-1.8q.7-1 1.4-.8.6.6 0 1.7z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m250.7 253.5 2-4.8-.2-.3-2.4 3.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M252.4 248.7q-1.4-1-1-2.3c.4-.7 1.2-.2 1.2-.2l.8.7c.4.2 1 0 1.4.6.5.6.2 1.2-.1 1.4s-1.5.4-2.3-.2zm-8.5-.6q1.1.7.9 1.5-.5.7-1.8 0-1-.6-.8-1.5.5-.7 1.7 0z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m246 250.2 3.8 2.2-.1 1.8-4.1-3.5z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M246 250.6q-1.2 1.3-2.3.7c-.7-.5-.1-1.2-.1-1.2l.8-.7c.2-.4 0-1 .7-1.4s1.2 0 1.3.3c.1.4.3 1.5-.4 2.3z"/>
<path fill="#aa8c30" d="m356.6 289.8-.4.4-.3 1v-1.5l.5-.3zm.2-.9s-.3.5-1.1.5-1.2-.4-1.3-.8q-.2-.5.2-1.2c.2-.5-.4.4-.4.4v1l.6.6.5.2h.4l.7-.2.4-.3zm4.2 7.3s-.4.2-.7.1c-1.9-.6-3.5-1.8-3.5-1.8l3.8 2.4zm2.4 1.1s0 .7-.8 1q-1 .4-1.5-.1c-.5-.5-.4-.6-.4-1.2l-.1.6.4.8.8.4.5-.1.5-.2.4-.5.3-.5zm24.3-47.6-.1.4v.7l-.9-1 .4-.5zm-.5-1.2c0 .7-.9 1.6-1.6 1.3q-1.2-.5-.9-1.8l-.2.3.1 1.1.7.6h.9l.6-.3.4-.7zm10 1.8s-.1-.4 0 0-.6 1.7-1.4 1.6q-1-.3-1-1.4c0-.6-.1.2-.1.2v.8l.5.4.6.4.6-.2.7-.3.2-.6v-.9z"/>
<path fill="#aa8c30" d="M393.8 251s0 .4-.4.7l-1.3.7 2.8-.8-.2-.7zm-17.1 22.5-.1.5v.8l-1.6-2.5.5-.3zm-.9-2.4c-.2.7-1.4 1-2 .8-.5-.4-1-1.6-.3-2l-.4.2-.1.7.2.7.6.7.5.1.8-.2.6-.4s.4-1.3.1-.6m5.6 5.7s0 .3-.5.3h-1.6l2.6.6.2-.6z"/>
<path fill="#aa8c30" d="M384.6 277.5c.3.8-.5 1.7-1.5 1.4s-1.3-1.3-1-1.8l-.2.6.2.8.6.6h1.3l.6-.7.2-.7-.1-.2zm-99 11.1c.3.3-.1 1.5-1.2 2q-1.7.4-2-.4-.4-.7-.3-.8l-.2.2.2.6.5.5 1 .2.8-.1.7-.5.4-.3.3-.5v-.6z"/>
<path fill="#aa8c30" d="m284.6 291.1-.3.4-.3.8v-1.4l.5-.3zm-3.8 4s0 .3.2.5l1 .3h-1.8l-.2-.7h.8zm-.6 1.2v.2l-.1.5q-.1.9-1 .9a1 1 0 0 1-1-1.1q-.1-.6-.2-.6v.6l.4 1 .7.3h.5l.5-.2.2-.6.1-.5zm-4.7-.1s.2.8 1.2.8 1.2-.2 1.2-.2v.3l-.2.2H276l-.3-.3-.3-.5v-.3zm-9.4-25.4v.4c-.1.3-.7 1-1.8 1q-1.4 0-1.8-.7t-.4-.8v.5l.4.7 1.1.5h1l1-.3.5-.5.1-.6zm-1.7 2.3s-.4 0-.6.2l-.4.4v-1.3h1zm-4.7 3.4.5.3c.5.3 3.1.5 3.1.5h-4.1l.1-.8zm-.5 0-.2.6c-.2.5-.7 1.6-1.6 1.4-.9-.1-.7-.8-.7-.8v-.4l-.2.7.2.4.4.2.7.1.4-.2.6-.5.2-.5.2-.6zm-2.2.2s0 .2-.7.2-1.7-.3-1.7-.3l.4.4.5.2h1.3zm-2-29s0 .5-.3.8c-.4.3-1.3.5-1.8.2s-1.5-1.3-1.5-1.3l.4.8.7.6 1 .3 1-.2.4-.4.2-.3z"/>
<path fill="#aa8c30" d="M252 249.1v.6l-.2 1.1.9-2-.4-.2zm-5.4 1.5.2.5c.2.5 2.3 2.6 2.3 2.6l-3.3-2.9.4-.5zm-1 .3s-.5.3-1 .3c-.3 0-1 0-1-.4q0-.7.1-.7l-.3.6.1.4.5.3h.7l.6-.3.2-.2zm-1.5-1.4h-.5c-.7 0-1.4-.8-1.4-.8l.5.8 1 .5z"/>
<path fill="#9ca168" d="M399.9 240.2c-.3 3.8-4.1 5.8-6.1 7.2s-3 3.2-3 3.2l-.6 2.2-.3 1.5-.1.5q.2.6.3 2l-.1 4.3 2.7-1.9 1.9-.7.6-.1s-2.2 2.1-3 4.4-2.4 7.2-5.3 8.8c-3 1.6-4.8 1-5.7 1.8s-1 1-1 1l-1.3 1.8-1 1.2-.9.7-.6.4-.2 1.7-.7 2.4s.6-.4 1.3-.5h1.3s-.5.6-.7 1.3c-.2.6.1 4.6-3.6 7-3.8 2.3-13.4 2-13.4 2l-1.8.7-1.7 1.3-1.6 1.7v.4s-1.3 1.5-2.1 2l-2.8 1.9 2.2.1 3.5 1.5s-2.2 0-3.6.5c-1.5.6-8.2 4.4-11.4 4.3s-8-4.9-8-4.9l-2-1.4-3.5-.7-4.3-.2v-.6l.1-.7s1.5-.2 3.8.1c2 .2 2.7 1 4.4 1.1q2.6.2 3.5-.3c.7-.3 5.9-4.7 5.9-4.7l5.8-2 2.3.3 1 .2.9.3-1 1-1.4 1.1.7.6 3.7-.6 1.2.3.3.2q0-.5.6-1.3c.4-.6 2.5-2.3 3.4-2.9l1.4-1c.3-.3 1.5-3.3 1.5-3.3l.1-1.6 4.1-4 2.8-2.9 1.2-3.3-.2-.7s1 1 .9 3c-.2 2.2-.7 2.9-.7 2.9s2.8-2 4.3-2.6q1.8-.6 2.4-.5c.4-.2 1.5-.9 2.1-1.8.8-1.2 1-1.5 1-2l.2-2.8-.3-7 2.4-5.4 3.5-3.1.7-.4-.4 1.2v1.5s1.8-2.5 2.7-2.9l.5-.3.8-2 .3-3v-3l-.5-2.3-1.7-4.3v-5.8l-1.2-1.6s1.1 0 2.5 2a14 14 0 0 1 1.9 5l3.3-10.3s.9 1.2 1.5 3.3l.8 3.3 1.4-2.8.1 1c.2 1 2.3 2.1 2 6m-67.3 65s-.7-1.6-3.9-3.2c-3.1-1.5-5.5-1.8-5.5-1.8v1.1l5 2.2 2.4 2z"/>
<path fill="#717732" d="M355.4 295.6c.3-.4 3-2.8 4.4-3.5s3.2-.8 3.2-.8c1.9-.4 2.2-.1 6.6-2.5s5.3-5.2 6.2-5.9c.8-.7 2.3-.8 2.3-.8l-3.4 3.9a29 29 0 0 1-5.8 4.5 18 18 0 0 1-7.6 1.8 8 8 0 0 0-4 2c-1.3 1-2 2.2-2 2.2s-.2-.5 0-.9zm12-12.7c-4 3.7-5.3 7.2-5.3 7.2l5.4-4.9c1.9-1.8 4.5-2.6 5.6-3.7 1.2-1.1 1.3-2 2-2.7l1.3-1s-1.8-1.4-9 5.1m-8.5 8.6s1.7-.8 2.4-2.3c.5-1.2.2-1.5 1-2.8 0 0 4.2-4 5.7-5.8 2.6-3.1 1.4-5.7 1.4-5.7s.3 1.3-.8 2.7c-1 1.4-6.5 5-7.2 7.2-.6 2.2-.3 2.3-.6 3.4-.6 2.6-1.9 3.3-1.9 3.3m-2.8 10.5s-2.1-1.4-4.5-1.3c-4.9.4-9 3.4-12.2 3.4-3.1.1-4.4-1.5-6.9-3.2-2.6-1.8-9.2-1.5-9.2-1.5v.3s4.4 0 6.4.6c3.6 1 5.6 4.8 9.7 4.6 5.5-.3 9.9-3.6 12-3.6 3.8 0 4.7.7 4.7.7m-24-2.4s3.7.6 6.6-1.5c3-2.1 6.4-5 8.3-5.2 2 0 4 .4 4 .4s-2.5-1-4.4-1q-3-.2-5.8 1.3c-1.8 1.1-2.7 3-4.6 4.3a13 13 0 0 1-4.1 1.7m-.7 5q-.9.1-1.3.3c-.2 0-1.3-1.2-3-2-1.6-1-4-1.4-4-1.4s-.3 0 .3.2l3.8 1.5a9 9 0 0 1 2.6 2.1c.1.3.6.9 1.5.8q1.4-.1 1.2-1-.2-.5-1.1-.5m2.6-3.6s1.5.8 4 .8c5.2-.2 8.6-3.8 12-5 3.4-1.4 5.2-.3 5.2-.3s.1-.2 0-.2a7 7 0 0 0-3.9-1.2c-5.7 0-11.2 3.7-13.5 4.7q-3.6 1.3-3.8 1.2m56-39.9c-1.8 1.9-4.5 7.7-5.6 9.3-1.2 1.6-2.7 2-3.3 2.7-.6.6-2.7 3.5-3.2 4.1-.5.7-.6.5-1 .8q-.6.4.1.2c.6-.1.8-.5 1.6-1.5 1-1 .9-1.4 2.3-2.7 1.4-1.2 3.9-2.6 5.1-4.2 1.3-1.5 3.8-7.5 5.2-9s4-2.4 4-2.4-2-.6-5.3 2.7zm-9 11.1s-.1-1 1.2-2.6 1.7-1.3 2.8-3.6q1.3-3.4 2.3-6.9c.9-2.4 2.3-4.8 2.3-4.8s-1.3.2-2.7 2a35 35 0 0 0-4.8 8.7c-1.1 3.4-1.1 7.2-1.1 7.2m-1.7 2.2s.5-.2.5-3.2c.1-2.9-.1-7 .7-9 .9-2 5.4-7 5.4-7s-1.9.5-4.5 2.5-3.7 4.7-3.6 6.7 1.3 4.2 1.4 6.1l.1 4zm10.5-20.1s1.1-4 1.6-5 .5-1.5 2.7-4c1.3-1.5 2.6-2.4 3.1-4.4s.6-7.7.6-7.7-.6.5-1.1 1.5-.1 4.7-1 6.4-2.7 5.6-3.7 6.5c0 0-.2-2.9.2-5.7.3-3 1.3-3.8 1.7-5.7s.2-6.7.2-6.7-1.5 1.5-2.3 3.4-1.2 5-1.2 7.5.5 4.5.6 5.9.2 2-.4 3.6l-.6 2-.7 2.6zm-3.5-21.6s1.4 1.7 1.2 3.6c-.3 2-1 4.5-.2 6.5.7 2 1.9 2.2 2.2 3.4s.3 3.4.3 3.4.6-4.3.2-5.5c-.5-1.1-.7-.6-1.3-1.8s0-4.7-.4-6.7-2-2.9-2-2.9"/>
<path fill="#9ca168" d="M306.7 304.8s.4-1 3.1-2.4a34 34 0 0 1 7.3-3c.3.3-.3 1.8-.3 1.8l-2.6.8-2.1 1.2-2.5 1.6z"/>
<path fill="#9ca168" d="M313.6 297.7c-3.4.1-5.3 1-6 1-.2.1-.7.5-1.2.3a5 5 0 0 1-1.5-1.5l-.7-.7-.2 2.5-5-4.4-.6 3-.7 1.3-5-4.8.2 3.7-1.4.1-3.6-2.5-1 .4 1.2 2.2-4.6.3-1 .8-1 1v.4h1.9c.4-.2.6-.7.7-.5.2.2.3 1.2.9 1.1s2.7-1.7 4.2-1c1.7.8-2.2 2-1.8 3.1s3.2.7 4 .3c.6-.3 2.7-3 3.6-2 1.2 1.4-2.5 2.5-1.8 3.9s2.7 1 3.7.4 3.4-4.1 3.9-3.4c.9 1.4-2 2.7-1.3 3.7.8 1 2.3 0 3.4-.8s1.5-2.5 3-3.3c1.4-.8 1.3-.5 1.7-.5s1.5-1.1 1.5-1.1l3.5-1.5 1.6.2.9.4 1.7.2.2-1.4s-2-1-3.3-1zm-27.4 0 .7-1.3-.4-.6s-1.2-.7-2.3-1.8a7 7 0 0 0-2-1.6L280 291l-.8-2.8-.5-.9-1.3.6-1.7-6.1-.3-1.1h-.7l-1.5 3.6-1.5-2.3-.4-5-1 1-1.3 1.4-2.3-4.6s-.1-.1-.3.3c0 .3-.3 1.8-.1 2.6a26 26 0 0 1-6-5.2l-1-2.5 1-2 .5-2.6-2.2.9-.4-5.7-.3-1.8-2.7 3.8-1-1.7v-3.6l-.7-.2-1 2s-1.1-2-1.6-2.5c.1-.8.2-2.9-.3-4.5-.6-2-1.3-3.1-1-4.8.2-1.6.9-1.8.8-2.5 0-.8-1.2.5-.8-.5s3.8-3.2 3.1-4.4c-.7-1.1-4 1.9-3.3-.1s4-2 4.2-4.9c.1-1.7-3 1.1-3.3.2-.3-.8 2-2.6 1.9-3.8 0-1 .4-1.2-.2-1.8s-2.6 2.3-2.6 2.3l-2-.7-.8 3-.6 2.5-2.6-1.5.7 3.1.6 3-2.7-.7 1.4 2.6 1.9 2 1.1 1.5.9.4 1 1.1.5 1.7.6 1.9.1 1.8v2.4l-.1.4v1.1c-.5 0-1.4-.8-1.7-.3s1.6 2 1.2 2.4c-.3.4-3-.5-3.2.3-.2 1 .2 2.2 1.8 2.6s5.3 1.2 4.7 2c-.6.7-4.7-2.3-4.5-.1a4 4 0 0 0 2.6 3.4c1 .4 5.1 0 4.9 1-.3.9-3.3-.4-3.6 1s2.1 1.6 2.6 1.6 2.2-.1 2.9.4l4.3 4.1 4.6 3.4c-.8 0-2.4-.3-2.6.3s6 3 4.2 3.8-3.8-2-4.3-.4 1.2 3 2.2 3.6 6.7.1 5.6 1.4-5.3-.8-5.3.7 2.7 4 4.2 3.9c1.4 0 3-2.4 3.6-1.1s-1 1.7.3 2 2.3-1.4 4-1c1.5.3 4 1.3 5.4 2.5z"/>
<path fill="#717732" d="M308.4 304.1c1 0 1 .6 1.2.6s1.8-1.5 3.3-2.3a18 18 0 0 1 4-1.4l.1.2s-2.8.7-4.2 1.7l-3 2.1q-.3.5-1.7.7c-1 0-1.4-.6-1.4-.9 0-.2.7-.7 1.7-.7m8.5-4.3s-.9.1-1.3-.3a4 4 0 0 0-2.6-.8 7 7 0 0 0-4 1.8q.1.2-1.4 1.3.6 0 2-1a7 7 0 0 1 3.6-1.4c1-.2 1.7.4 2.2.7.5.4 1.4.3 1.4.3zm-50.5-20.6v-.7l-2.2-1.1c-1.6-1-5-3.8-5-3.8l2.8 2.7c1.4 1.3 4 2.9 4 2.9zm35.6 25.2 1.5-2.1q1.5-2 1.9-2.2 0-.5-.7-1.4l-.2-1.2s.4.8 1 1.3l1.1 1s1.4-.2 1.4-.5q0-.3-.2-.5c-.3-.1-.5.2-1.1 0-1.3-.6-1.8-2.6-2.6-2.6-.8-.1-.2 2-.6 2-1.1.2-2-4.3-5-4.5-2 0-2.3.3-2.4.7 0 .5 2.5 3.1 1.6 3.5s-3.5-4.2-5.5-4.2c-1.9 0-2 .7-2 1.1.2.4 2.2.8 1.8 2.2s-2.5-2-4.3-1.9-2 .3-2 .9c-.1.5.6 1.2.3 1.4s-1.4.1-2.3.7-2.1 2.4-2.1 2.4 1.2-1.7 2.7-1.9h5.6l-1.1-.8c-.6-.5-1.1-1.6-1.1-1.6l1.7 1.5c.8.7 1.9 1.2 1.9 1.2s1.7.2 1.8.4q.3.3-.8 1.3l-1.7 1.7 2-1.6 1.5-1.2 1.4.2q.3 0-.8-1.7l-1.6-2.5s1 .9 2 2.3c1.1 1.3 1 2 1.4 2q.7.1 1.4.1t-.4 1.7q-1.4 2.5-1.2 2.5c.2 0 .8-1.2 1.6-2 .7-.8 1.1-1.7 1.5-1.8h1.5l-.8-1.6c-.6-1-.8-2.6-.8-2.6s.6 1.4 1.4 2.5l1.3 1.9 1.4-.1.3.1c0 .5-.2.7-.7 1.8zM280.5 292q1.7.6 1.7.5c0-.1-1.7-1-2.1-2.4s0-4.4-.8-4.3c-.7 0-1.1 2.1-1.8 1.7-.8-.4.2-4.4-.6-6-.8-1.7-2.6-2.9-3-2.3s-.3 4.2-1.5 3.5.1-4-.2-5.3c-.4-1.5-1.2-2.5-1.9-2.1s.3 3-.7 3-.9-1.9-1.3-2-.6.3-.9 0c-.2-.4 0-1.7-.6-1.6s-.5 1.2-.3 1.7q.3 1 .7 1.8c.2.4 1 .9 1 1.2q-.2.5-1 .6h-1.9s1.4.4 1.9.4 1.4-.2 1.7.2c.2.5 1.4 2 1.4 2s.3-.8.4-2.2 0-2.7 0-2.7.5 1.6.4 2.7c0 1-.4 3-.4 3s1.1.8.8 1c-.3.3-1.5.3-2.8.3-1.4 0-3.3-.5-3.3-.5a14 14 0 0 0 6.7 1.3l1.8 2s.9-1.5 1-3v-3l.4 2.8c0 1.3-.4 3.6-.6 3.8a5 5 0 0 1-1.9 1l-3.5.5s2.2.3 3.8 0c1.5-.4 1.7-.9 2.2-.6l.8.7 1.3 1c.1.1-.7.4-1 .7l-1.5.7 2.2-.7 1.1-.4.5.2-.1-1.2-.3-1.8s.5.8.7 1.6l.1 1.6s.2.2 1.4.6m-31.1-35.6s.3-1.7.3-3.6a14 14 0 0 0-1.9-5.8l1-1.4-1.2 1-1.2-.5-.9-1 1.2.8q.8.2.7.1l-.5-2-1.9-1c-1.3-.6-2.5-1.8-2.5-1.8l2.8 1.5q1.5.5 1.6.4l1.6-.6 1.7-1.1s-1 .6-1.8.7l-1.6.3-.3-2.4-1.1-1.1c-.9-1.2-1.6-2.9-1.6-2.9s1 1.2 1.7 1.7 1.4 1.5 1.4 1.5l1.6-1 2.8-2-2.7 1.3-1.6.7s-.2-1 0-1.5.8-1.2.7-1.6c0-.5-.4-.3-.5-.7-.2-.3.4-2.6.4-2.6l.1 1.2c0 .5-.2 1 .4 1s3.2-2.7 3.6-3.2.8-1.8-.6-1.3-1 1.8-2.2 1.7c-.4 0-.8-1.5-1.2-1.2s-1.4 1.3-1.6 2.4.2 2.6-.4 3.1-1-1.8-2.2-1.5-1.5 2-1.3 2.6 2.7 3.5 2 4c-.9.3-2.9-2.6-4-.6-1 1.9 3.4 4 4 4.4.5.5 0 .6.8 1.6s1.8 1.2 2.4 1.8a13 13 0 0 1 2.1 5.9zm11.7 17.1s-1-1.2-.7-3.3 1.8-5.7 1.3-6.2-2 2.2-2.4 1.6.9-4.3.2-5.8-.7-2.5-2-2.3c-1.3.1-1.8 4.4-2.5 3.6-.6-.7.4-2.6 0-3.7q-.6-1.4-1.3-1.2c-.7.2-1 2.2-1.4 2.1-.5 0-1.2-2.3-2.2-2-.9.3 2.3 4.5 2.3 4.5s.5-.5.8-1.2l.6-1.5s.3 1.3 0 1.9-.3 1.7-.3 1.7-.8.3-2.1 0l-2.3-.7s.8.6 2.1 1l2.7.7 2 2.7.8-2.1 1.3-3.5-.6 3.9-.7 3s-1 0-2.6-.3l-3.6-.8 3.6 1.2 2.8.7 1.4 1.9 1-1.5 1-1.8s-.3 1.4-.7 2.3l-.7 1.4-1.8.2h-2.5a20 20 0 0 0 4.5.9s0 .5.7 1.3z"/>
<path fill="#fff" d="M314.4 310.8s-.6-.6-1.4-1q-1.3-.6-1.2-.4l-.4-.1.8-2 5.5-5.3.9-5.3h3.4v7.2l1 .5 6.2 3.7v1.6l-1 .4-.7.4-2.3.2-4-3-1.8-1.7-3 4z"/>
<path fill="#016848" d="m318.8 296.7-.1 1c0 .9 0 5.2-.2 5.8s-3.2 1.8-4.9 3.3-1.8 2.6-1.8 2.6-.5 0-1.6.4c-1 .4-1.5 1-1.5 1s.6-2.6 3.4-5c2.7-2.6 4-2.8 4.4-3.2.3-.4 0-5 0-5.5q.3-.5.7-.4zm7 15.7.8-1.1 1-.8-1.5-.8c-1.5-.6-2.3-.4-3.3-1.2l-2.2-2-1.5 1.2 1.7 2c.9.8 2.7 1.1 3.5 1.6l1.6 1.1z"/>
<path fill="#cd202a" d="M321.5 296.6s1.7 0 1.9.2l.2.6c0 .1 0 4-.2 5.3a8 8 0 0 1-1.4 3l-4 3.7c-1.2 1.2-2.4 2.8-2.4 2.8l-.8-1q-.6-.5-.6-.9a10 10 0 0 1 3.3-3.4c2-1.3 3.6-2.8 4-4.9.3-2 0-5.4 0-5.4"/>
<path fill="#cd202a" d="M332.5 310s-.4-.4-1.8-.4l-1.5.1s-.8-1-2-1.7c-1.2-.8-2.1-.7-3.5-1.6-1.3-.8-2-2.1-2-2.1l1-2s1.2 1.5 2.4 2.4 3.7 1.7 4.6 2.4z"/>
<path fill="#30c2dc" stroke="#0872a7" stroke-width=".5" d="M345 286.4s-3.6-1.4-3.4-2.7c.3-1.2 8.3-3.3 8.3-3.3l.1-2.1s-1.2-.1-3.2.5-5.2 1.6-8.6 1.6l-34.4-2.2c-3.7-.3-6-6-6-6l-1.8.4s.5 2.7-.4 3a65 65 0 0 1-13.9-6l-.6 2.6s7.3 4 7.1 5.5c-.2 1.4-2.4 1.3-2.4 1.3l1 1.8c.3 0 12 .6 12.3 4.1 0 1.5-3 2.3-3 2.3l1 1.2v.6s6.7 0 8.5 1 2.7 2.6 5 3.8c2.2 1.3 17.3 1 19.6.3 2.7-.7 4.3-3.7 8.7-5 4.3-1.4 5.7-1.4 5.7-1.4z"/>
<circle cx="284.5" cy="280.7" r="2.1" fill="#fff" stroke="#0872a7"/>
<circle cx="296.4" cy="270.9" r="2.1" fill="#fff" stroke="#0872a7"/>
<circle cx="346.5" cy="286.6" r="2.1" fill="#fff" stroke="#0872a7"/>
<path fill="#f8c83c" d="M275.1 267.8c-.7 1.6 2 4.6 4 5q2.6.3 3.2-1.1a3 3 0 0 0-.4-2.5c-1.2-1.7-6-3-6.8-1.4"/>
<path fill="#fff" d="M281 270.2c0-1-1.6-1.9-2.6-1.9s-2 0-1.8.5 2.7 1.3 2.8 1.6-.8.6-.4 1q.8.4 1.4-.2.6-.3.7-1z"/>
<path fill="#f8c83c" d="M297.7 288.3c.4 1.4-.7 2-2 2.7-1.5.6-4.3.2-5-.9-.6-1.1 1-3 2.8-3.4s3.8.3 4.2 1.6"/>
<path fill="#fff" d="M294.8 289c.5 0 .6 1 1 .8.5 0 1-.8.8-1.3s-1-1.3-1.8-1.2c-.8 0-3 2-2.8 2.5q.4.7 1.2.3c.4-.2 1-1 1.6-1z"/>
<path fill="#f8c83c" d="M349.2 281c1.3 1.7 3.6.2 4.2-.5s2.4-1.7 1.7-2.8c-.8-1.1-2-1-3-1-.8 0-2.6 1.4-2.9 2-.3.5-.6 1.6 0 2.4z"/>
<path fill="#fff" d="M349.6 280s.1-1.9 1.8-2c1 0 1 .3 1.8.7.7.4 1-.5 1-.5s0 1.3-1.1 1.3-.8-.5-2-.7c-1-.2-1.5 1.1-1.5 1.1z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="m321.9 276.4-.9-.6h-2.8l-3.4.1 3.4 9.9 4 5.8 1.6.6 3.1-.2.6-1.6-1.2-9.5z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="m310.6 277 .2-1.1 1.8-.1 2.2.1s2.3 2.9 3.1 4.7 1.7 4.8 2.7 6.6c1 1.9 3.2 5 3.2 5h-4l-2-.6-5.6-9z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="M308.2 275.8h2.6a14 14 0 0 1 3.9 5.4c1 2.8.5 3.2 1.8 6.2 1.4 2.9 3.2 4.9 3.2 4.9s-3.9.2-6-.2c-2.3-.4-3-.3-3.6-1l-1.7-1.3h-2.2l.5-3.1-.6-6.9.1-3.5zm23.4 1.8-3.7-1.5-7-.4.6 3.6a30 30 0 0 0 2.6 7.7c1.1 2 2.2 4.4 2.8 5s4.6-1.1 4.6-1.1l2.6-.4-.3-2.8-.4-1 .6-8-.6-1.2z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="M307.1 277.7c0 .8 1 .9 1.2.8s1-.3 1-1.5-.7-2.1-2.2-2.2c-1.4 0-2.5 1.7-2.5 3 0 1.2 1.2 1.8 1.2 2.3 0 0-1.2 1.1-1.1 3.1s1.6 3.6 1.6 3.6-1.7 1.3-1.7 2.7 1.3 2.3 2.6 2.3c1.2 0 2.8-.8 2.8-1.8s-1-1.7-1.7-1.7q-1.1.2-1.1.8m25.6-10.9c0 .9-.6 1-1 1s-1.2-.4-1.2-1.5c0-1 1.4-1.6 2.4-1.6s2.4 1.2 2.4 2.7-1 2.6-1 2.6.6.3.6 2.2-1.2 3.3-1.2 3.3 1.6.6 1.6 2.6-1.4 2.6-2.3 2.6c-1 0-2.6-.5-2.6-1.9s.8-1.8 1.5-1.8q1.1.2 1.3 1.5"/>
<path fill="#953220" d="m309.5 288.6.4.6q.2.7.8 1.4c.8.7 7.2 1 9.2 1s7.8.2 8.9-.4c1-.7 1.2-1.8 1.9-2.3l.9-.4-.9.7v1.4l.5.8s-.1.4-.8.8q-1 .5-2.3.6c-1 .1-13.2.1-15.2-.2s-1.8-.3-2.4-.7l-1-1 .3-.8-.3-1.4zm20-11.8 1 .5s-.9.3-1.6 2.3-.1 2.5-.6 2.7-5.8 0-5.8 0l-1-2.1z"/>
<path fill="#231f20" d="M346.4 276s-.9-.3-1.3-1q-.8-1.4-.5-2c.4-.1 1 .5 1 1.1s.8 2 .8 2zm10.4-2.8s.7 1.2 1 1.3l1.2.6s-1.5 0-2-.4l-1-1.5z"/>
<path fill="#231f20" d="M360 274.2s-1 .5-1.6.2q-1.4-.5-1-1t.8.2c.4.4 1.8.6 1.8.6m5.3-4.5s-.8.6-1.3.6-1.5-.2-1.5-.4 1.3-.4 1.6-.3zm-8-10.5s-1.1.4-1.5 1-.3 1.2.2 1.2.6-.7.6-1zm-5.2 2.9s-.9.5-1 1c-.3.5-.5 1.6 0 1.6s.7-.8.7-1.2c0-.5.3-1.4.3-1.4"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="m342.8 268.4-2.9 3s6.6 3.7 11.7 3.5c5-.1 10.8-4 11.2-5 .4-.7 0-4.3-.6-5.4s-4.2-3.8-5.5-3.7-3.7 1.9-5.8 4c-2.2 2.1-2 3.9-5 3.7z"/>
<path fill="#0c8489" d="M342 269.6s7 1 9.1-.8 4.3-5.3 5.7-6c1.4-.9 2-.9 2-.9l1.6 1.1 1.8 1.5.6 3.4v1.8l-2.2 1.8-4 1.9-4 1.2-3.2-.1-6.7-2-1.7-1.1-.4-.4z"/>
<path fill="#04534e" d="M352.8 265.5q0-.7 1-.6c1 .1 1 .2 1 .8s-.5 1.4-1 1.4-1-1-1-1.6m2.8 1.8q0-.9 1-.8c.7 0 1.3.7 1.3 1.2s-.5 1-1.1 1-1.2-.9-1.2-1.4m-1 4.2q.1-1 1.1-.8 1 0 1 1a1.2 1.2 0 0 1-1.1 1.2q-1-.2-1-1.4m-3.7-1.3c0-.7.7-1 1.3-1q1 .2 1 1.4c0 1.2-.4 1.3-1 1.3s-1.3-1-1.3-1.7m-5.9.7a1.4 1.4 0 1 1 3 0 1.4 1.4 0 0 1-3 0"/>
<path fill="#8cbebf" d="M355.6 267.3c0-.5.7-.8 1-.8q.8.1.8.8 0 1-.7 1-.8-.1-1-1zm-1 4q.2-.8 1.2-.8c1 0 1 .3 1 .8s-.5 1-1 1q-1 0-1.1-1zm-3.7-1.2c0-.6 1-.9 1.5-.9q.4 0 .3 1 0 1.1-.7 1.2c-.7.1-1-.7-1-1.3zm1.8-4.7q.1-.6.8-.5c.7.1.6.1.6.7s-.3 1-.6 1q-.7-.2-.8-1.2m-7.7 5.1q.1-1.1 1.4-1.3 1.8-.2 1.7 1c0 .6-1 1.8-1.8 1.8s-1.3-.8-1.3-1.5"/>
<path fill="#231f20" d="M347.7 269.4s-1 .1-1.5.6-.5 1-.3 1.2.7-.3.8-.6c0-.2 1-1.2 1-1.2m4.7-1s-1 .6-1 1.3q-.1 1 .2 1c.3 0 .5-.9.4-1.2s.4-1.2.4-1.2z"/>
<path fill="#231f20" d="M353.2 269.3s-.6 0-1 .4q-.9.9-.6 1 .6 0 .8-.5c0-.2.8-.9.8-.9m1-5.4-.8.6c-.2.2-.5 1-.2 1q.4.3.8-.5.2-1 .2-1.1m3.5 2.3s-.9 0-1.2.5q-.6.7 0 .8.4 0 .6-.5c.1-.4.6-.8.6-.8m-.5 4.1s-.2.7-.8 1q-.7.6-1 .1 0-.6.4-.7z"/>
<path fill="#04534e" d="M362.8 267.5s.4 2-1.3 3-6.4 4.2-10.9 3.8-9.6-3-9.6-3l-.7.4 1.2.5 3.4 1.4 4 1.2 2.5.1 2.1-.2 4-1.3 3.3-1.7 1.8-1.4.4-.6v-1.9z"/>
<path fill="#231f20" d="M274.9 242.2s1 .9 1 1.6q-.3 1.2-.7 1c-.3-.2-.3-1-.3-1.3zm7.7 7.1s-.3 1.4-.9 1.5c-.5.1-.9-.4-.8-.7q.3-.1.8-.2zm-16.8 1s.4.8 1 1q1.1-.2 1-.6-.2-.2-.8-.2zm3 6s.7.6 1.5.7a1 1 0 0 0 1.3-.7c0-.2-1-.2-1.3 0s-1.5 0-1.5 0m5.3 4.4s1.4-.2 1.6-.6.6-1 .3-1.3-.5.5-.7.9c-.1.3-1.2 1-1.2 1"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M282.6 257.1s1-6-4-10.4c-5-4.3-8.1-3.2-9.8-1.6s-3.2 6.7 2 11.3c5.3 4.5 11.7 3 11.7 3z"/>
<path fill="#0c8489" d="M267.6 250.7s-.1-3.4 1.5-4.4c1.5-1 6.8-1.8 9.3 3.1s2.7 7.4 2 10h-2.7l-4.3-1.8-3.5-2.6z"/>
<path fill="#04534e" d="M282 258s-.9 1-2.7 1c-1.7-.2-10-2.3-12-9.8v1.7l.1.5.6 1.5 1.7 2.5 2.2 1.9 2.8 1.4 2 .6 2.5.4h1.5z"/>
<path fill="#04534e" d="M277.7 255.9q0-.9.8-.7c.8.2 1-.2 1 .4s-.4 1.6-1 1.6q-.7-.2-.8-1.3m.3-4q0-.7 1-.8.8 0 .9.9 0 .7-1 .8-.8-.1-.9-.8zm-3.7 4.4q0-.6 1-.7c1-.1 1 .3 1 .7s-1 .8-1.5.8-.5-.5-.5-.8m-.4-4.6c0-.6.3-.4.8-.4s1.2.1 1.2.7-.7 1.4-1.2 1.4-.8-1.2-.8-1.7m-3.4.7q0-1 .9-1.1c.9-.1 1.3.6 1.3 1.1q-.1.9-1.1 1-1-.1-1.1-1m.6-4q0-.7 1-.5 1 0 1.1.7c0 .5-.7 1.2-1.4 1.2s-.7-1-.7-1.5zm3.7-.5q.1-.5 1-.3 1 0 1.1 1c.1 1-.7 1-1.3 1s-.8-1.2-.8-1.6z"/>
<path fill="#8cbebf" d="M271 248.3q.2-.6.8-.7.8-.1.9.7-.1.8-.8.9-1-.1-.8-1zm-.6 4q0-.9.7-1 1-.2 1 .5-.1 1-1 1.1c-.9.1-.7-.1-.7-.6m4.4-4.3q0-.5.8-.4 1 0 .8.5-.1.7-.8.7-1-.1-.8-.8m-.9 3.9q.1.9 1 1 1.2 0 1-1c0-.6-.6-1-1-1s-1 .4-1 1m4.3-.6q0 .8 1 .9.8 0 .9-.9c.1-.9-.5-.9-1-.9s-1 .4-1 1zm-4.1 5q.2-.8.9-.9 1 0 .9.7c0 .4-.8 1-1.2 1q-.7 0-.6-.9zm3.6-.7q0-.7 1-.6.8 0 .8.6-.1 1-1 1c-.9 0-.8-.5-.8-1"/>
<path fill="#231f20" d="M278.7 249.4s.5.7.6 1.3v1.2q-.5 0-.7-.9c-.1-.5 0-1.6 0-1.6zm-3.2-3.3s.4.5.4 1 0 1-.3 1-.3-.4-.3-.8zm-3.7.5s.4.5.4 1 0 1-.3 1-.3-.4-.3-.8z"/>
<path fill="#231f20" d="m270.5 247.3 1 .4q.5.7.3 1-.3.2-.6-.4zm2.8 3.8s.7 0 1.1.5q.8.4.5.9-.4.1-1-.5zm-3.4 0 1 .3q.5.5.2.8-.2.2-.6-.4zm8.3 2.5.2 1.4q.1.9-.2 1c-.3.1-.3-.7-.3-1 0-.5.3-1.4.3-1.4"/>
<path fill="#231f20" d="m277.3 254.9.8.5q.4.4.1.6l-.4-.5zm-3.7.2.9.3c.2.2.3 1 .2 1.1q-.1.1-.6-.3z"/>
<path fill="#04534e" d="M272.1 243.8s-3.2.7-3.8 2.9c-.6 2-.7 4.4-.7 4.4l-.4-2.7.6-1.7 1-1.6 1.4-1 1.3-.4zm10.2 12.5s.4-.8-.6-3.4-3-6-3-6l1.6 1.8 1.2 2 .6 1.8.5 1.7v1.6l-.3.6z"/>
<path fill="#231f20" d="M285.6 252.2s1.2.7 1.3 1.1q.3.6-.1.7-.6 0-1-.7zm-4.8 12.8s.6-1 1.3-1.3q1-.6 1.4-.1.1.4-1 .9zm8.1 3.1s.8-.4 1.3-1q.7-.8.4-1-.5-.4-1.1.5c-.3.4-.6 1.5-.6 1.5m-.9-.7s.3-1 .6-1.4q.4-.5 1-.3.2.4-.2.9z"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M301.5 261.2s-5-7.2-12.8-7.6a7.4 7.4 0 0 0-8 7c.2 1.6 2.6 5.2 10.9 5.8s9.6-3.9 10-4.1c.3-.2-.1-1-.1-1z"/>
<path fill="#0c8489" d="M295.6 256.3s2 1.5 3 2.8q1.2 2 .5 2.3c-.5.2-5-4-8.8-4.9-3.7-.9-6.9 0-7.6 1.7s-.4 4.5 1.2 5.6c1.8 1 9.7 2.3 9.7 2.3l3.7-.5 2.3-1.3.5-1.3.6-1.6.2-.7-1.9-2z"/>
<path fill="#04534e" d="M289.2 257.3q-.1.6-1 .7c-.9.1-.9-.3-.9-.7q0-.8 1-.9c1-.1 1 .4 1 .9zm-4.6 1.6q.2-.6 1-.4 1 0 1 .8c0 .8-.8 1.1-1.3.9q-.8-.5-.7-1.3m5 2.7q0-1 1-1 1.1 0 1.2 1c.1 1-.8 1.2-1.5 1.2s-.7-.6-.7-1.2"/>
<path fill="#8cbebf" d="M285.8 262.7q.2-.7 1-.6t1 1c.1.9-.5.9-1 .9s-1-.8-1-1.3"/>
<path fill="#04534e" d="M292.5 264.5q0-.7.7-.8.8 0 1 .7-.1.8-1 .9a1 1 0 0 1-.7-.8m3-2.3q0-.6.8-.4 1.1.1 1 .8-.3 1-1.1.8-.7-.2-.7-1.2"/>
<path fill="#8cbebf" d="M289 256.9q0 .6-.7.6-1 0-.9-.6t.6-.6q1 0 1 .6m-2.5 1.9c0 .5-.7.7-1 .7q-.8 0-.8-.7t.8-.6q1.1 0 1 .6m1 3.8q.1.6-.5.7-1 0-1-.7-.1-.6.5-.7 1 0 1.1.7zm4-1.3c0 .4-.7 1-1.2 1s-.6-.9-.6-1.3.4-.7.8-.7 1 .6 1 1m5.8 1.3c0 .4-.7.4-1 .4q-.8-.1-.8-1-.2-.7 1-.7c.5 0 .8.8.8 1.3m-3.4 1.7q0 .8-.7.8a1 1 0 0 1-.8-.8q.1-.6.8-.6.9 0 .7.6"/>
<path fill="#231f20" d="M294.4 262s.8-.3 1.5-.3q.8.1.6.4t-.7.2l-1.4-.4zm-3.3 2.8s.6-.4 1.1-.5q.8-.2.8.4 0 .4-.6.3zm-2.2-5s1.4.6 1.7 1.1q.5.8.1.9t-.8-.5z"/>
<path fill="#231f20" d="m288.5 261.6 1.6-.3q.7 0 .7.2t-.5.3zm-3.4 0s1.2.2 1.5.5q.4.5.1.7c0 .2-.6.1-.8 0z"/>
<path fill="#231f20" d="M285 263.3s.4-.7.8-.8 1-.1 1 .1c.1.2-.4.5-.7.6zm-1-5.1 1.2.3q.6.5.4.7t-.8 0c-.5-.4-.7-1-.7-1zm2.6-3s1.2.4 1.6 1q.5.8.1.9l-.9-.4c-.3-.3-.8-1.4-.8-1.4z"/>
<path fill="#04534e" d="M300 263.7s-2.3 2-7.7 1.9-10.4-2.5-11.6-5l.5 1.2 1.2 1.6 3.8 2 4.3.9 3.3.2 3.2-.6 2.5-1.1.4-.7zm.8-2.5s-.9-1.2-2.2-2.3c-1.2-1.2-6.2-4.4-6.2-4.4l4 1.8 2.7 2.2 1.8 2z"/>
<path fill="#231f20" d="M301 256.4s1.4.5 1.6.8l.5.6s-.3 1-.6.6l-.8-.9zm4-2.2s1.3.5 1.8 1.1q.6 1.1.2 1.3c-.2 0-1-.7-1.2-1l-.7-1.4z"/>
<path fill="#231f20" d="M306.4 254s1.2.8 1.4 1.2.5 1 .1 1c-.3.2-.9-.4-1-.8l-.6-1.5zm6.8 1.5s1 .4 1 1.2v1.3l-.7-.7v-.7l-.3-1zm1.8.7s.8.7.9 1.2.2.8-.3 1q-.6-.1-.6-1zm-12.3 15s.6.2 1.2 0q.9-.4.9-1-.2-.8-.8-.2c-.4.4-.2.6-.5.8zm6.8 1.8s.9-.2 1.5-.9q1-.9.5-1.2c-.4-.2-1 0-1 .5zm5.5.1 1.3-.9q.8-.6.4-1c-.3-.5-.6-.2-.8 0l-.2.7z"/>
<path fill="#231f20" d="M316.7 273.1s.8-.9 1-1.4q.3-.9-.1-1-.7.1-.8.9z"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M299.9 264.1c0-2.4 3.5-8 7.5-8 1.6 0 4.9 1 7.5 2.1 2.2 1 3 2 4.4 2.4s3.7.1 3.7.1l5-.5-4 5.7-1.4 1.2s-1.2 2.4-4.7 3.8c-3.6 1.4-9.5.4-12.2-.3s-6-3.2-5.8-6.5z"/>
<path fill="#04534e" d="M299.9 264.1h.2q0-.8.6-2.1a12 12 0 0 1 2.7-3.8q1.7-1.7 3.9-1.8 1.3 0 3.4.6l4.1 1.4q1.5.8 2.4 1.5 1 .6 2 1l2 .2 1.8-.1 4.4-.5-3.6 5.2-1.4 1.2v.1a9 9 0 0 1-4.6 3.7q-2 .6-4.5.6c-2.8 0-5.9-.5-7.6-1a8 8 0 0 1-3.8-2.1 6 6 0 0 1-1.8-4h-.5q.1 2.6 2 4.4a9 9 0 0 0 4 2.3c1.7.4 4.8 1 7.7 1a14 14 0 0 0 4.7-.7 9 9 0 0 0 4.9-4h-.3l.2.1 1.4-1.3 4.3-6.2-5.6.7h-.1l-3.4-.1a6 6 0 0 1-1.9-1q-.9-.7-2.5-1.4l-4.2-1.5q-2-.6-3.4-.6h-.1q-1.5 0-3 1a11 11 0 0 0-3.3 3.5 8 8 0 0 0-1.4 3.7z"/>
<path fill="#0c8489" d="M324 264.9q-.7.4-1.4.3c-.5 0-11.6-6.7-15-6.4s-6.8 1.7-6.9 5.3c0 3.5.8 3.5 1.5 4.5s7.5 2.5 7.5 2.5h4.1l3.3-.4 3-1.6 1.5-1.3 1.1-1.3z"/>
<path fill="#04534e" d="M306.5 263q0 1.3-1 1.3t-1.1-1c-.1-1 .4-1 1-1q1-.1 1 .8zm1.1-3q.2-.9 1.2-.8 1.1 0 1.2.8 0 1-1.2 1-1 0-1.2-1m6.7 1.8q0-.8 1-.9.8 0 .9 1c.1 1-.4.8-1 .8a1 1 0 0 1-1-.9zm1.3 4q.1-1 1-1t1.2.8q.2 1.3-.8 1.3c-.6 0-1.4-.4-1.4-1zm-3.2 2.7q.1-1 1-1 1.1 0 1.2 1-.1.8-1 .9-1.1 0-1.2-1zm-6-1q0-1 .9-1a1 1 0 1 1 0 1.9 1 1 0 0 1-1-1z"/>
<path fill="#8cbebf" d="M306.4 263q0 .9-1 1a1 1 0 0 1-1-1q0-.9 1-.8c1 .1 1 .2 1 .7zm3.3-3.2q0 .8-.8.9c-.5 0-1.3-.4-1.3-.9s.5-1 1-1q1 .1 1 1zm6.4 1.7q0 .8-1 .9c-1 .1-.9-.4-.9-.9q0-.7 1-.8c1-.1.9.4.9.8"/>
<path fill="#04534e" d="M312.7 264.2q0 1.1-1 1.2t-1.1-1c-.1-1 .3-1 1-1s1 .2 1 .8z"/>
<path fill="#8cbebf" d="M308 267.2q0 .9-1 1c-1 .1-1-.5-1-1s.6-.8 1.2-.8.8.3.8.8m4.5-3a1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1 1 1 0 0 1 1 1m5.2 1.3q0 1-1 1t-1.1-1c-.1-1 .2-.8.8-.8q1.1 0 1.3.8m-3.3 2.7q0 1-1 1-1.3 0-1.1-1 .1-.8 1-.8t1 .8z"/>
<path fill="#231f20" d="M314.8 265s1.6-.2 1.9 0q.4.6.1 1c-.1 0-.8.1-1-.1 0-.3-1-1-1-1zm-1.7-5.4s1.3.3 1.8.9q.6.9.4 1.1-.4.3-1-.2zm-3.7 2.6s1.5.8 1.8 1.3q.5.6.2 1c-.1.2-.8 0-1-.3 0-.3-1-2-1-2"/>
<path fill="#231f20" d="M308.7 263.3s1.7.2 2 .5q.8.4.8.6-.1.4-.5.4l-1.2-.5-1-1zm-1.9-4.8s1.7.2 2 .5.5 1 .1 1.2q-.6.1-1-.5l-1-1.2zm-3.6 2.5s1.5.8 2 1.4q.6.6 0 1c-.4 0-.9-.7-.9-1z"/>
<path fill="#231f20" d="m302.8 263 1.5-.2c.5 0 1.2-.1 1.2.2s-.6.5-1 .4zm8.6 5.6s1.1-.7 1.7-.6q.7 0 .7.4c0 .4-.7.3-.9.3zm-6-.9s.4-.6.9-.7q.6 0 .7.2-.1.4-.6.6h-1z"/>
<path fill="#04534e" d="M322.6 266.6s-1.6 2-4 3a14 14 0 0 1-7.2 1.1 15 15 0 0 1-11.2-5.2l.4 1.1 2 2.6 2.3 1 3.5 1 3.3.3h2.5l3-.5 1.9-.7 1.3-.8 1-.8 1.2-2zm-3-5.9s-1 .3-3-.9-4.3-2.8-8.8-2.8-5.9 3-6.2 3.2l2-2.6 2-1.1 1.7-.3h1.5l2 .6 1.7.6 2.1.7 1.8 1 2.2 1.1zm6 1.4h-3.4c-.8 0-.8.2-1 .4l-.2.4-.8-1.7 3.5-.5 2 .7-.2.6z"/>
<path fill="#0c8489" stroke="#04534e" stroke-width=".5" d="M320.7 280.4q.3.9 1.1 1h1.6l3.6-.3 1.2-1.4.8-3.2 1-1.2 3.3-1.9 2.8-.8 1.9-.2 2.5-.8 1.9-1 .8-1.2.8-1.8-.8-2.5-2-2.7-3.4-1.3H335l-3 1.1-5.5 1.3s-3 1.3-4.3 3.7c-1.3 2.3-1.2 3-1.5 6-.2 3.2 0 7.2 0 7.2z"/>
<path fill="#8cbebf" d="M322.2 279.2c1 0 .2-1.7 1.4-3.8s2.6-4.6 3-4.6.4.4.9.3 1.2-.8 1.2-1 0-1.3 1-2c.8-.8 2.1-1.9 2.7-2q1-.3 1 0c.1.3-.3.7.1.8s1.4.1 1.6-.5l.2-1s.6-.5 1.8-.3 1.3.2 1.4.7-.5.5-.4.8v.6c.2.2.4 1 1.2 1 .8-.1 1.1-.6 1.1-.6l.3-.4s.2-.5-.1-.7q-.4-.4 0-.3c.4.1 1.6 1.8 2 1.1s.5-1.7.2-2l-1.9-2.7-3-1-4 .3-4 2.2-3.6 2s-.2 2.9-.8 2.3c-.7-.5-1.6-2-1.9-1.5s-.9 2.7-1.3 3.8c-.3 1-1.4 4.4-1.3 5.6s.8 2.9 1.2 2.9"/>
<path fill="#8cbebf" d="M325.8 274.4q-.6.1-.8.5-.3.3-.2 1c0 .3.7.7.7.7h.5l1-.3.3-.8q-.1-.7-.4-.6zm6.2-5c-.6-.3-.8.1-1 .3v.5s.2.8.4 1c.3 0 1.1.3 1.3 0q.5-.2.5-.9c0-.7-.7-.7-.7-.7zm5 1.1q-.1-.3-.9-.2-.6 0-.6 1 .4.9 1.2.6c.5-.1.4-.8.4-.9zm-14.4 1c0 .3.5 1 1.2.9a1.3 1.3 0 0 0 1.2-1c0-.4-.4.3-1 .5s-.7.2-1 0q-.3-.5-.4-.4"/>
<path fill="#04534e" d="M327.3 275.5s0 .6-.4 1q-.7.4-1 .3-.5 0-.8-.3l-.3-.7s.5.8 1.3.6a2 2 0 0 0 1.2-.9m13.3-8.3s0 .6-.5.9q-.6.4-1 .3-.4 0-.7-.4c-.6-1-.3-1.5-.3-1.5s.4 1.6 1.3 1.5a2 2 0 0 0 1.2-.8m-11.9 2.9s-.4 1.2-1.2 1.2c-.9 0-.8-.2-.7-.4 0-.1.3.2.7 0 .4 0 1.2-.8 1.2-.8m2.3-.3s0 1 .2 1.2q.5.5 1 .5.6.1.9-.4.3-.8.1-.8l-.2.5q-.4.4-.8.3-.6 0-.8-.3zm4.3-4.3s.2.7-.3 1.2-1.5.2-1.5.2.8.3 1.2-.2.6-1.2.6-1.2"/>
<path fill="#04534e" d="M321.7 281.1s5.3 0 5.7-.8c.4-.7-.4-3.6 2.7-5.8 3.2-2.2 5.7-2.2 7.4-2.3 1.7-.2 5.3-1.3 6-4.5.3-2-2.3-2.2-3.3-4-1-2-3.6-1.2-3.8-1.2s-3 0-4.3 1.3c-.9.8-.8 1.8-1.1 2.2s-4 0-4.3.6c-.4.6.1 1.7-.2 2-.2.3-1-.8-1-.8l.2-2.8 4.5-2.6 5-2.8 3.5.1 3.5 1.8 1.7 2s.3.8.5 2.3l-.2 2c-.8 2.8-3.7 4.8-6.7 5a14 14 0 0 0-7 2.5c-1.4 1-2.1 5.3-2.1 5.9 0 .5-6.6.1-6.6.1z"/>
<path fill="#231f20" d="M324 270.8q-.6.4-.8 0c-.2-1 .3-2.2.3-2.2v1.6q.3.1.5.3zm3.2-.6c-.2-.1-.1-1 0-1.3.2-.3 1.7-1.4 1.7-1.4l-.6 1.5c-.2.4-.7 1.3-1 1.2zm-1.2 5.3q-.4 0-.3-1c.1-1 1-1.6 1-1.6l-.4 1.2.2 1q-.1.4-.5.4"/>
<path fill="#231f20" d="M328.4 274s-.5.6-1 .9q-.9.6-1.4.6c-.4 0 .1-.8.3-.9zm4.5-6.4s-.6.5-.9 1.2q-.5 1.2-.1 1.4c.3.2.7-.8.8-1.1z"/>
<path fill="#231f20" d="M334.6 269.9s-.5-.4-1.2-.6q-1-.2-1.2.2c0 .2-.6.5-.3.7s.6-.3.8-.3q.3-.3.9-.2zm-1-4.5c.3.3.7-.2 1-.6s.7-1.2.7-1.2l-1 .7c-.4.3-.9 1-.7 1.1m6.9-.1s-1 .4-1.2.8q-.6.6-.4 1c.3.3.6-.2.8-.5zm-2 4.6s-.3.6-.9 1q-1 .6-1.3.3 0-.4.3-.7l.8-.2z"/>
<ellipse cx="328.8" cy="207.4" fill="url(#mx-b)" rx="2.9" ry="4.3" transform="rotate(8.7)"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m295.4 247-1.7 4.2 1.4-.2.7-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M296.1 245.1q-1 0-1.2 1 0 1.2 1 1.4 1 0 1.2-1 0-1.2-1-1.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m369.7 255.8-4.2 4 1.7.4 2.9-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M371.6 254.1a1.5 1.5 0 0 0-2 .6q-.7 1.1.4 2 1.2.4 2-.7c.4-.6.2-1.5-.4-1.9z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m262.8 233 2.2 4.9 1-1.4-2.6-3.7z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M261.9 230.9q-.9.8-.1 1.9c.4.6 1.3.7 1.8.2q.9-.9.1-2a1.3 1.3 0 0 0-1.8-.1z"/>
<path fill="#aa8c30" d="M296.6 245.3s.3.1 0 0c-.5 0-1.4.4-1.4 1a1 1 0 0 0 1 1c.5 0-.2.2-.2.2l-.7-.2-.2-.4-.2-.5.1-.6.4-.5.5-.2h.3z"/>
<path fill="#aa8c30" d="M295.6 248.1s-.3 0-.5.3l-.7 1.1 1-2.3.4.2zm76.4 6.4s.3.4-.1 0c-.4-.3-1.8-.2-2.1.5q-.5 1.2.6 1.7c.6.2-.2 0-.2 0l-.7-.5-.1-.6v-.7l.5-.6.7-.3h.7l.3.2z"/>
<path fill="#aa8c30" d="M369.4 257.3s-.3-.2-.8 0l-1.4.9 2.3-2.2.5.6zm-107-26.7s.4-.1 0 .1-.8 1.4-.3 2q1 .7 1.7 0c.4-.5 0 .2 0 .2l-.7.4h-.6l-.6-.3-.4-.6v-.8l.2-.5.3-.3.3-.2z"/>
<path fill="#aa8c30" d="M264 233.8s-.3.1-.2.6l.3 1.5-1.2-2.7.7-.2z"/>
<ellipse cx="337.6" cy="-128.1" fill="url(#mx-c)" rx="4.6" ry="3.1" transform="rotate(62.9)"/>
<path fill="#e92736" d="M262.6 241.2s1 .2 1.5 0 .6-.7.7-.8.3-.6.2-1l-1-1.4-1-.1q0-.2-.2-.5c-.3 0-1.7 1.6-1.5 1.8.3.3.4 0 .5.1v1.1c0 .3.8.8.8.8"/>
<path fill="#e92736" d="M264 238.1c.1.3.7 1 .9 1q.5.3 1 0c.3 0 1-.7 1-1l-.1-.7c-.2-.3-.1-1-.4-1.1q-.2 0-.5-.3v-.7c-.3-.3-2.6 1.1-2.4 1.4q.3.1.5.1z"/>
<path fill="#e92736" d="M269.2 237.6c.2-.4.1-1.5-.1-1.8s-.6-.3-.7-.4q.1-.1.3-.5c.2-.4-2.6.1-2.4.4s.6.1.6.3q-.2.3-.5.6l.3 1.1q0 .3.2.5l.6.2h1.4q.2 0 .3-.5zm22 12.8s.3.7.8 1h1l.6-.5c.2-.2.4-1.2.3-1.4 0-.2-.5-.6-.5-.8q.2-.2.2-.4c-.1-.2-2-.2-2 .2q.2.3.2.3c0 .2-.5.5-.6.7-.2.1 0 1 0 1z"/>
<path fill="#e92736" d="M293.8 249.5c0 .2-.2 1 0 1.1q.1.5.5.8h1.2l.4-.6c.1-.3.6-.7.5-.9q-.2-.2-.1-.4.2-.2.3-.5c.1-.3-2.1-1-2.2-.7q.2.3.2.4c0 .2-.7.6-.8.8"/>
<path fill="#e92736" d="M297.3 252.5c.3-.2 1-.9 1-1.1q-.2-.6-.2-.7l.5-.1c.1-.2-1.6-1.6-1.7-1.3q.2.4.2.5l-.7.1q-.3.3-.5.8-.2.2-.2.5l.2.4c.1.2.8.9 1 .9zm66 5.5s0 .8.4 1.2q.5.4.8.4h.7l1-1q-.2-.6-.2-1c0-.4.3 0 .3-.2s-1.6-1.1-1.7-.8c-.1.2.2.3 0 .4l-.8.2c-.2 0-.5.8-.5.8"/>
<path fill="#e92736" d="m366 258.5-.8.8q0 .5.2.8c0 .2.7.6 1 .5q.2 0 .5-.2c.2-.1.8-.2.8-.4l.1-.4q.2 0 .6-.2c.2-.2-1.2-1.7-1.4-1.5q-.1.3 0 .4c-.2 0-.9 0-1 .2"/>
<path fill="#e92736" d="M366.4 262.2h1.4l.4-.5q0 .1.4.2c.2 0 0-2-.3-1.8q-.2.4-.2.4-.2-.2-.5-.3l-1 .1s-.2 0-.3.2l-.2.4v1q0 .2.3.3"/>
<path fill="#f7e204" d="M264.2 239c.2.4-.1 1.2-.3 1.4s-1.3-.1-1.3-.3q0-.4 0-.8c.2-.2.6-.7.9-.6q.5.1.7.4zm.3-1.3q-.2.5.2.7c.1.2 1 .4 1.2.3s.6-1.5.4-1.7q-.4-.2-.7-.2c-.2 0-1 .6-1.1.9m2.7-1.3v.7q.4.3 1 .5c.3 0 .5-1 .5-1.1q0-.3-.3-.4l-1.2.2zm26.1 13.7c0 .4-.7.7-.9.7s-.7-1-.6-1q.2-.4.5-.5.5-.1.9.2zm1.1-.6q-.3.2-.3.6c0 .2.3.8.5.9s1.2-.5 1.3-.7q0-.4-.4-.6l-1-.2zm2.4 1q-.3.1-.4.5s0 .6.3.8.8-.2.9-.3v-.4c0-.2-.8-.6-.8-.6m68.6 8.2c-.2.2-.9.2-1 0-.3 0-.3-1-.1-1q.2-.3.6-.2.5 0 .6.6zm1 .1q-.5 0-.6.3c-.1.1-.2.7 0 .9.1.1 1.2.2 1.3 0v-.5zm1 2h-.5q-.3.1-.4.7c0 .2.7.3.8.3l.3-.2-.1-.8z"/>
<ellipse cx="438.6" cy="-93" fill="url(#mx-d)" rx="2.2" ry="4.5" transform="rotate(47.9)"/>
<path fill="#a8ac71" d="M285.8 199.3s-3.8.3-6.5-.1-2-7.3-2.1-7.5c0-.2-1.6-1.8-1.4-3.3s5.2-5.5 5.8-5.8c.5-.1 1.5.3 1.5.3s1.2-1.2 1.6-1.2.9.7.8 1c-.2.3-1.9 1.3-2 2-.2.8 0 1.5-.5 2.3s-1.4 1.7-1.5 2.4-.4 1.1 0 1.4 2.1 0 3.5-.6 2-1.5 2.1-1.4c.2 0-.8 1.7-2.3 2.4s-2.5 1.3-3.3 1.2c0 0-.8 2 1.8 2.2 2.6 0 4-.6 4-.6zm-15 16s2 .8 2 3-2.1 4.1-2 8c0 4 .6 4 5.8 8.3a6 6 0 0 1 1.6 2.5c.3 1.3 1.2 8.9 6.2 8.9s5.8-3.3 5.8-3.3l-2.2-3.1s-1.7.5-2.7.3a3 3 0 0 1-2.3-2.1c-.6-1.2-.7-4.1-1.6-5.4-2-3.2-5.8-3.7-5.9-7 0-3.4 2.3-3.7 1.7-7.7-.7-4-5.4-5.8-5.4-5.8l-1.9-.8-.7 4z"/>
<path fill="#f1a720" d="M284.7 187.6c1.7-.4.7-4.8.7-4.8l-1.2 1s.8 1 .8 2-.3 1.8-.3 1.8"/>
<path fill="#a8ac71" d="m287.8 231.5-.9-.7c-.6-.4-3.2-2.6-2.8-8.9.5-7.2 11-11 11.6-13.8.8-3.2 3.1-4.5-5.2-11l-2 2.2 3.5 2.6s.7-.4.8.6c0 1-1 1.7-1.2 2.5-.2.7-5.4 4.3-5.8 4.6l-3 2.4s-1.3 1.3-2.5 4.1c-1.3 2.8-.7 6.4-.7 6.4s0 7.6 8.2 9"/>
<path fill="#78732e" d="m282.7 235.5.6 2.7-1.7.6h-.7l-.5.2-1.3 1.2h-.3s-.2 0-.3-.8q-.1-1.4-.2-1.5l1.6.7h.6l.3-.1.7-1.5zm-4.8.5-.8-1-1-.9 1.2-.6h1.2l-.2 1.5zm2.7-4.3s.8.3 1 1l.6 1.2-2-.2-1.4-.4 1-1.4zm-.7 11.9s-.4-.3-.7-.9l-.4-1.6 1.3-.3 1.5.7h.4l-.3.8-1.5 1.3zm5-4.1s-1-.4-1.2-1q-.3-.6-.4-.4l-.3 1.5-.5 1.5.2.2 1.1-.3.9-.9.3-.6zm-4.7 4.8 1.4.7q1.2.6 1.2.5l.6-.6.7-1.3v-.1l-3 .3zm4.4-1.2 2.7-1.6.1-1.4-.5-.4h-1.5l-.5 1.8zm-1.8 2.5s1.8.8 5-.4c1.4-.5 1.7-1.2 1.7-1.2l-1.3-.5-1.2 1zm-2.5-20.3-.4-1.4q-.2-1.2-.1-1.1l.9.5 1.3 1-1 .7zm2.2 3.7-.9-1-.6-1.2 1 .2 1.1.6z"/>
<path fill="#c6c7a6" d="M276.6 187c-.2.7-.4 1.9.5 2 .4.2 2.3 0 4.5-2.4 1-1.2 1-2.4 1-2.4l.5-1.3s-.9-.6-2.2.2-4.3 4-4.3 4z"/>
<circle cx="282.5" cy="183.6" r=".4" fill="#1c242f"/>
<path fill="#a8ac71" d="m279.7 184-1.7 1.7s1.4-.4 2.6-1.5c.6-.6 2-.8 2.6-.7.4.1.2-.4 0-.5-.3-.3-1.4-.4-1.6-.3z"/>
<path fill="#78732e" d="M284.1 224.5v-2.2l-.9.8-.8 1.1 1.3.5zm-4.5-2.6v-2.3c0-1 .5-3.1.5-3.1l.6 1.4 1.5 2.4.5.4-1.3 1-1.4.4zm4.5-.6.3-1c.1-.6.5-1.4.5-1.4v.1l-2 1.7.8.7h.5m-3.7-6s.2-.6 1-1.5l1.3-1.8.6 2.4.4 2v.4l-.7.2c-.2 0-1.6 0-1.8-.3zm4.8 2.9.8-.9 1-1-3.2.6.5 1zm-2-6.7s.3 0 1.2-1c.8-.8 1.3-.7 1.3-.7l.3 3.4-1.6-.5z"/>
<path fill="#78732e" d="m287.3 216.2 2-2-3.3-1 .6 2.3zm1.4-5.2.2-3.3s-.7.3-1.5 1l-1.2.9.4.6 1 .7h1zm2.8-3.2V205s-.5.8-1 1.2l-.9.8.9.6h1zm1.2 3.5 1.6-1.3 1.5-1.7-4.2-.3.2 1.7 1 1.6zm-9.6-13.8-.2 1.8h3.5l-.9-1.2-1-.6-1-1zm6.6 16.2c.2 0 1.1-.8 1.6-1.2.4-.5 1.2-1 1.2-1l-3.3-.3v2.2zm6.3-5.9s.4-1.3.3-2.6q-.1-1.8-.2-1.8l-2.5 1.7.4 1.2 1 1z"/>
<path fill="#78732e" d="M291.8 204.8s.7-1 .8-1.4v-.4h2l1.1.2h.2l-.9 1.1-1.2.7-1.5.2zm4-2-1.3-2q-.7-1-.8-1l-1 2.7v.3h3z"/>
<path fill="#fff" d="m271.6 222.2-.3.8s1.3-.7 2.3-.7 2.1 1.3 2.1 1.3l.2-.9s-1.3-1.3-2.2-1.3c-.8-.1-2 .8-2 .8zm.7-5.8.3.4c0 .1.1-1 1.4-1.5 1.4-.6 2-.1 2-.1s-.2-.8-.8-1q-.8-.2-1.8.5a4 4 0 0 0-1.1 1.7m4.4 4.4s-.6-1.4-1.6-1.6c-1.1-.3-2.5.5-2.5.5l-.2.7s1.5-.8 2.5-.4c1.1.5 1.5 1.7 1.5 1.7l.3-1zm-5-5.1.4.5s-.2-1.4.5-2.1l1.1-1-.6-.3s-.9.5-1.2 1.1a3 3 0 0 0-.2 1.8m-.5-2.3c.3-.8 1.2-1 1.2-1l-.3-.3h-.5s-.5.3-.9 1c-.3.9-.3 2-.3 2l.6.3s-.1-1.4.2-2m7.8-19.2q1.4-.4 1.5-.3c.1.1-1.3 1.3-.7 2.9q-1 2.1-.9 2.3h.7l.4-1.8c.8 1.3 2 2 2 2h1s0-1.3.4-2.4h.1a5 5 0 0 0 2 1.5v-.4s-1.1-.6-1.7-1.5l-.1-.1a4 4 0 0 1 1.8-1.7l.7.6.2-.2-.5-.7h-.6l-1.8 1.7-.8-1.5-.4-.2s-1.1.1-2 1.7c-.2-1.2 1.1-2.4 1.1-2.4v-.5l-2.4.6c-1 .3-1.7 1.2-1.7 1.2v.8s1-1.1 1.7-1.6m1.5 2.2c.6-1 2-1.5 2-1.5s0 .7.7 1.7c-.5.9-.8 2.2-.8 2.2s-1-.6-1.9-2l-.2-.3zm-7.6 21.1v.8s.8-.9 2-1 2.3 1 2.3 1 0-1-.2-1.2c-.1 0-1.1-.6-2-.6s-2.1 1-2.1 1m21.4-11.2-.5-1 .6-.3 1.7-1.6-.4-.6h-1.6l-1.4-.1.5-1.7.6-1.4-.6-.5s.2.5-.8.6-2.8-.8-2.8-.8v.4l2.5.7q.9 0 1.2-.3l-.5 1.3-.3 1 .2.2v.8l1.4.1 1.6.1s-.8 1-1.7 1.5h-.3l-1-1.5-.1.5.7 1.2-1.5-.1-.4.4v2.5l-1.8-.6-.3.3v-.1l-.4.4s-.4 1.4-.3 3.1v.1a5 5 0 0 1-2.4-1.3l-.4.3s-.3 1.4 0 3.2c-1.3-.3-2.5-1.5-2.5-1.5l-.5.5.7 4.7h-.7c-1.4 0-2.3-1.3-2.3-1.3l-.4 1s.6 2.7 2 4l.4.2c-1.3 1-2.8 1.2-2.8 1.2v.9l2.2 1.5-1.6 1 .2.5 1.8-1.3 1.8.5v-.5l-1.5-.3.1-.1c.5-.4 1.4-1.8 1.4-1.8v-1s-.5 0-1.1-.6c1.8-1.2 1.9-1.8 1.9-1.8l.4-.6s-.7-.3-1.1-1.2l2.9-.7.2-.2s-.7-1.2-1-2.6l3 .6.4-.4-.5-2.4c1.8 0 3.2.2 3.2.2l.3-.3s-1-1.9-1-3h4l.3-.5s-1.4-1-1.7-1.5m-10.5 15.5s-.5 1-1.4 2l-.2.2-.5-.2q-1.5-1.1-1.4-1.3l2.5-1.6zm.9-3.2-2 2-.4-.5c-.8-1.2-2-2.1-1.8-3.8 0 0 .7 1.2 2.2 1l1-.1c.4 1 1 1.4 1 1.4m2-2.5-2.7.7-.1-.5c-.5-1.6-.5-4-.5-4s1 .8 2.6 1.2zm-.5-3c-.2-1.4.1-2.9.1-2.9s.8.8 2.3 1q.2 2.2.3 2.1zm5.8-2.2s-1 .4-2.8.1v-3.5l2.3.7zm0-3.1h-.2V205s1 .3 1.7.2c.2.4 0 .5.5 1.2s1.1 1.3 1.1 1.3-2.3.2-3.2 0zm-7.6 18.6-.1-.5s-1 .7-1.2 1.4l-.3-.1-2-.8.2.4 1.8.8h.2l-.5 1.4.2.3.5-1.7 1.2.1-.2.8-.4 1.5.5.3h2l-.3-.4-.7-.1h-1.1l.3-1.2.3-.8-.3-.7-1 .2zm-11.3-1.3c1.3 0 2.3.9 2.3.9v-.6s-1-1.4-2.1-1.4-2.5 1.2-2.5 1.2v1.2s1-1.3 2.3-1.3m13.3 19.4c-1.3.6-2.7.6-2.7.6l.8-1.5c2.5-.8 3-2.4 3-2.4l-.2-.7s-.6 1.6-2.6 2.4c.4-1.2.7-3.2.7-3.2l-.7-.2s-1 1.3-2 1.6a8 8 0 0 0 .8-2.7l-.3-.5s-.7.6-1.7.8h-.6c.6-.8 1.7-3.2 1.7-3.2l-.2-.5s-.8 2.1-1.7 3.2l-.3.4c-1-.3-2.2-1-2.2-1l.1.3s.8.6 1.8 1l-1.3 1.2v1l3 .4c-1 1.6-2 2.1-2 2.1l.5.7s1.3 0 3.7-.6q-1 1.8-1.2 1.8c-.2 0 2.2.2 3.7-.6s2-1.6 2-1.6l-.4-.4s-.4 1-1.7 1.6m-6.9-4 1.2-1.5c1.6.3 2-.1 2-.1s.3.8-.6 2.3c-1.1 0-2.6-.8-2.6-.8zm1.4 3 1.3-1.6.3-.3c1.5 0 2.3-1.3 2.3-1.3s.2 1.2-.5 2.7c-1.9.7-3.4.5-3.4.5m-1.9-9.8c1.5 0 3.3.7 3.3.7l-.2-.7-2.9-.6a4 4 0 0 1 1.6-1.6l-.5-.3s-.6.1-1.4 1.5l-.2.4h-.3a5 5 0 0 0-2.4.7l.2.4s1.1-.5 2.3-.5l-.7 2.4.3.6zm-3-6.2-.3-.7s-1.5-.6-2.7 0c-1.1.5-2 1.9-2 1.9l.5.9s.8-1.5 1.9-1.9 2.6-.2 2.6-.2m3 2.4-1-.6s-1.7.5-2.2 1.1-1.1 2.1-1 2.5q.5.7.7.7c.2 0 .3-1.5 1.1-2.3s2.4-1.4 2.4-1.4m-3.9-.5a5 5 0 0 1 2-1l-.7-.5s-1.5.2-2.2.9-1.8 2.1-1.8 2.1l.9.9s1.2-1.8 1.8-2.4m5.2-44.7c.8-1 1.6-1 1.9-1h.6l-.8 2.5c-.2 1-.7 1.2-1 1.9s-1.1 1.8-1.2 2.8q.1 1.5.5 1.7c.3.1.8-.4.7-.7 0-.3-.3 0-.4 0q-.2 0-.3-.9c-.1-.6.7-2.2 1-2.8s1-1.1 1-2l.7-2.5q0-.1-1.3-.3-1.1 0-1.9 1.2c-.4.6-1.7 1.1-1.7 1.1s1.4 0 2.2-1"/>
<path fill="#78732e" d="M285.2 194.5h-2.5l.7 2zm-3-.1-.5-.4-.3-.3-.9.5-.5 1.9.3.3 2-2zm-.3 5-1.8-2.7-1 2.2.5.2 1.2.2h1.1zm7.4 44.7a9 9 0 0 1-4.8 1.5 7 7 0 0 1-3.4-1 4 4 0 0 1-1.5-1.7q-.8-1.7-1-4-.3-2-1.1-3.6c-.8-1.3-2.4-2.1-3.8-3.3a7 7 0 0 1-2.8-5.2v-.8q.1-2.5 1-4.5c.9-2 1.2-2.5 1.2-3.5v-.5q-.6-1.1-1.5-1.9c-.9-.8-1.2-.8-1.2-.8l-1-.2-.1.3 1 .2v-.1.1l1.3 1q.9.7 1.1 1.5v.4q-.1 1.4-1 3.3a12 12 0 0 0-1.2 4.7v.8a8 8 0 0 0 3 5.5c1.5 1.1 3 2 3.7 3.2q1 1.9 1.3 5 .3 1.3.8 2.5c.5 1.2.9 1.5 1.6 2a7 7 0 0 0 3.7 1c2.7 0 4.9-1.6 4.9-1.6z"/>
<path fill="#78732e" d="m287.2 239.2-1.3.4q-.8 0-1.7-.8a4 4 0 0 1-1.2-2.3q-.1-1.3-1-3.3c-.5-1.5-2-2.6-3.5-3.7-1.3-1.1-2.6-2.2-2.9-3.4v-.7q.1-1.5.8-3.1a11 11 0 0 0 .9-4.8c-.2-2-1.7-3.5-3.2-4.5l-2.8-1.4-.1.3h.2c.5.3 1.8.9 3 1.8q2.1 1.4 2.6 3.8v.8q0 2.2-.9 3.9c-.9 1.7-.9 2.2-.9 3.2l.1.8c.3 1.3 1.6 2.4 3 3.6 1.5 1 3 2.2 3.5 3.5q.8 2 .9 3.2.1 1.4 1.2 2.5 1 1 2 .9l1.4-.4z"/>
<path fill="#a8ac71" d="M296.4 231.6s1.7.4 1.6 1.8a13 13 0 0 1-.8 3.4l-2.4-2.2 1.3-1.2.5-.7z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M266.4 214.5s-1.7.6-2.1-.1c-.4-.8-.2-1.4-.2-1.5s-1-.3-1-1.3c.1-1 1.4-.9 1.6-.9s.4-1.5 1.3-1.4c.8 0 1.2 1 1.2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M268.3 215s-1.5.8-2 0c-.4-.7 0-1.6-.1-1.8-.2-.1-1-.4-.9-1.3.2-.9 1.1-.6 1.4-.8.3-.1.6-1.4 1.5-1.2.9.3 1 1 1 1l-1 4.1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M270.8 215.3s-.5 1-1.5.8-.7-1.7-.9-2c-.1-.3-.9-.5-.8-1.4.2-1 1.1-.9 1.5-1s.7-1.6 1.4-1.4 1.3 1.1 1.2 1.5c0 0-1.5.5-1.7 1.5s.8 2 .8 2z"/>
<path fill="#af7029" d="m293.7 231.2-2.2-2.6c-.9-1.3-2.3-2.7-2.3-2.7l-2.2-1.1-1.4-2.4-.2-2.8.6-1.6.8-1.2s-4.6 1.7-2.2 10.5c1 3.8 3.5 4.3 3.5 4.3l4.3-.3h1.3z"/>
<path fill="#816c2a" d="M291.6 201.7q.5 0 .8.4l.2.6v.4c-1.6 3.4-4.8 5-7.8 7a14 14 0 0 0-3.8 4 12 12 0 0 0-1.6 6.8c.1 5 2 7.8 4.2 9.3a10 10 0 0 0 5.2 1.5h.4l1.4-.1 3-.2 1.8.1.9.4.2.5q0 .6-.6 1.2l-1 .7.2.3 1-.8q.6-.6.7-1.4 0-.3-.3-.7t-1.1-.5l-1.8-.1c-2 0-4.5.3-4.5.3v.2-.2h-.3a9 9 0 0 1-5-1.5c-2.1-1.4-4-4-4-9v-.4q0-4 1.5-6.3c1.4-2.4 3.7-3.8 6-5.3a14 14 0 0 0 5.5-5.7v-.5q0-.4-.2-.8t-1-.5z"/>
<path fill="#fff" d="M297.8 232.7s-1 2-1.4 2c-.2 0 1 .3 1.3.8l-.3.5s-1.2-.9-2-1v-.6l1.1-.7a4 4 0 0 0 .9-1.7z"/>
<path fill="#fcca3d" d="m302 234-2.7.8-1.3-1s0-1-.5-1.5l-1.6-1c-.6-.2-2.5-.2-2.5-.2l-5-5.3 2 .2 2.8-.4 2.7-.2 1.4.2 1.6.4.8.7.8.8.8 1.3.6 1.6.2 1.7v1.9z"/>
<path fill="#af7029" d="M302.2 233.8s-1 .3-2.2.3q-2-.1-2-.2l-.2.9q-.4 1.3-.5 1.3v3.2l2.6.5 1.4-.5.7-3.5z"/>
<path fill="#fcca3d" d="M301.5 232.5v1.2l.6.2v-1.2zm-2.5-30-.3-2.1-.8-1.5-1.1-1.5-1.2-1.3-.6-.5-.3-.5-2.9.2-1.4 1.7s3.8 2.8 5.3 5.8a7 7 0 0 1 2.8 1.7l.1-.2z"/>
<path fill="#af7029" d="M296.3 205q-.2 3.1-.8 3.7l1.8-2 1.2-2.2a7 7 0 0 0-2.8-1.7q.6 1 .6 2.1z"/>
<path fill="#816c2a" d="m300.3 237.9-1.4-1.6q-1.1-1-1.3-1l-.2.4h.1l1.2.8 1.3 1.6z"/>
<path fill="#6f5b24" d="M302 235.5s-.3 1-1.3 1.8c-1 1-2.2 1.5-2.2 1.5l2.8 1 1.9-1.7z"/>
<path fill="#404118" d="m302.4 237.8-.2.3-.5.5-.4.5.6-3 .6 1v.4z"/>
<path fill="none" stroke="#78732e" stroke-width=".2" d="M285.8 199.3s-3.8.3-6.5-.1-2-7.3-2.1-7.5c0-.2-1.6-1.8-1.4-3.3s5.2-5.5 5.8-5.8c.5-.1 1.5.3 1.5.3s1.2-1.2 1.6-1.2.9.7.8 1c-.2.3-1.9 1.3-2 2-.2.8 0 1.5-.5 2.3s-1.4 1.7-1.5 2.4-.4 1.1 0 1.4 2.1 0 3.5-.6 2-1.5 2.1-1.4c.2 0-.8 1.7-2.3 2.4s-2.5 1.3-3.3 1.2c0 0-.8 2 1.8 2.2 2.6 0 4-.6 4-.6z"/>
<path fill="#ab6d29" stroke="#4d2a15" stroke-width=".4" d="M318.7 170.6s-7-1.3-7 4.5c.1 1.9.5 2.5 2 3 1.5.3 4.4.5 7 2.7s2.7 4 2.7 5.3c0 1.4-.7 2.6-.7 2.6l1.6 7.7 1.6 5.7 4.8-2.7 2.8-8.5-3.6-15.2z"/>
<path fill="#4d2a15" d="M325.6 200s2.5-1 3-3c.3-.5-2.4-2.9-2.4-2.9-.1 0 1.3.7 2 1.3q1 1 1.3.8c.2-.1 1.3-2.7.9-3.3s-1.1-2-2.1-2.6-2.4-.6-2.4-.6 1.9-.6 2.8 0c1 .5 1 1 1.1.9.1-.3-.2-2.9-.6-3.7q-.4-1.3-2-1.6c-1.1-.2-2.3-.1-2.3-.1s1.6-.9 2.8-.6c1.3.3 1.3 1 1.4.8s-.6-2.4-1-3a7 7 0 0 0-2.1-1.8l-1.8-.2h-.6s1-.6 2.1-.5 1.3.5 1.4.4q.3 0-.8-.7c-1.1-.7-4.2-2-4.2-2h-3.3s0-.8 1-1q1.3 0 1-.1c0-.3-2.6-.8-2.6-.8H316l-3 .3s.2-.5.7-.9q.6-.5 1.3-.6c1-.3-.6-1.1-.6-1.1l.9-1.3 3.9.3 8.2 4.8 3.9 3.3 2.4 6.2-.7 10.8-5.5 4.5-2.4.3z"/>
<path fill="#d2a567" d="M318.4 173.3s-.2-.7-.2-1.7.2-.9.2-.9-2.7-.3-4.6 1.2-.8 4.1-.8 4.1.8-.8 1.9-1.2 3-.3 3-.3c.2-.2-.4-1-1.1-1.1s-2.1-.2-2.1-.2.8-.5 2.2-.4 1.6.5 1.6.5z"/>
<path fill="#8f4620" d="m326.7 200.9-1.8.6 1 7.5 7.2 12.1 3.7-.6 1.3-2.3 1 .5c.4.4 1.8 2.8 3 3.3s1.7.2 2.7 1c1 1 4.9 6.6 5 6.8.3.2-.1-2.4-.1-2.4l-1.2-3.4-1.4-2.4-.3-.6h2l1.2.8 2 1.5.9 1.2.5 1.2.2 1 .5 1.6 1 2.3 1.3 2.1.4-.9-.6-2.5-2-6-1-2 1.9 1.2c.4.1 1.8 2.2 1.8 2.2l.9 2.2.6 2 .4 1.7.7 1.9 1-.7-.1-1.3-1-4.2-.4-4.5s.6 0 1.2.7l1.2 2.3.5 4.1.7 3.8 1.2 3.8 1.2 2.4.9 1.3v-2l-1.6-16 .2-.6s.2-.1.8.7c.5.7 1 1.9 1 1.9l.5 2.5.2 1.7.3 3.3.5 3.5 1.6 3.8.9 2 .5-2.4-.8-17.1.4-.5s.9.7 1.2 1.3l.8 1 .6 3.8.4 10.7.4 2.8 3.2-28.2 2-.4.2-.6-1.5-1-3.2-5-2.1-4.9-3.8-7-6.6-8-5.2-4.3-1.9-1.3.4-1 1-1.7-.1-.1h-.9l-2.2.4-2.2-.7-4.4-3-4.6-2.3-2.8-1-6.6-1.6-6.6-1h-3l-2.6.5s-1.4.6-1.8 1.5-.5 1.4-.4 2.6c.2 1.3 1 1.9 2.8 2.7 1.8.7 6.2 3.6 6.2 3.6l2 2.1.8 1 .2.8 1.4 11.1-2.8 6z"/>
<path fill="#ab6d29" d="M333.8 218.2s1.2 2 2.9.5c1.7-1.6-.8-6-.7-6.3 0-.3.7-.3.7-.3l1.3 2.2s.7 1.3.4 3.2-2.1 3.7-2.1 3.7l-2.2-.5z"/>
<path fill="#4d2a15" d="m375.3 244.2-1.6-4.5-.4-3.5c0-.6-.3-7.9-.5-9.3 0-1.4 0-3-.2-3.1l-1.6-2.2-.7-.7.5-.3s.6.6.7.3q.3-.7-.6-4.5c-.7-2.5-3.9-8.3-3.9-8.3l.4.2.9 1 1.8 1.5 4.1 1.5 2.2.3 1.4 2.9s1 4.9 1 7.4q.2 3.4-1.2 7c-1 2.4-1.2 10.3-1.3 11.5-.1 1.3-.6 2.5-.6 2.5l-.5.3z"/>
<path fill="#8f4620" d="M375.3 231.2s.8-1 1.2-2.4.5-12.4-.8-13.7a10 10 0 0 0-4-2.3c-.2 0 1 3.5 1 3.5l.8 1.4.6 1.8c.1 1 .9 3.1 1 6z"/>
<path fill="#4d2a15" d="M369.3 238s.5-5.1.3-9c-.3-3.7-.6-4.2-1-4.9a32 32 0 0 1-2.5-6.3c-.9-3.2-1.5-5.6-2-6.2s-1.7-2-2-1.6-.3 1.1-.3 1.1l-1.5-.4-.4-1 .5-1.6v-.7l-.3-1.1 2 1.6 4.2 2.8 2 8.6 2 5.3.2 9-.2 8-1-3.5zm2.4-24.7s1 1 1.7 1.4 1.8 1.1 1.6 1.2l-2-.8-1.2-.8v-1z"/>
<path fill="#4d2a15" d="M363.7 236s.8-1.8.7-4.1a49 49 0 0 0-1.6-8.6c-.4-.7-2-1.7-3.5-4.8s-1.3-3.8-2.3-5c-1-1-2-2.4-2-2.4l.3-3.3 3.6 1.8 1.6.8 1.8 8.7 1.6 2.5.7 3.5.9 8 .4 6.6-.2.5zm-4.2-3.7s.1-2.5-.5-4.8q-.9-3.5-1.9-4.8c-.7-.7-2.5-2.6-4.1-5-1.6-2.5-3.9-5-3.9-5v-3.3l2 1.5 2.4 1.2 1 3.7 1.2 2.7 1.8 2.4 1.2 1.1.7 4 .4 2.3 1 3.2-.4 1.7zm-3 0-1-.9s.6-.6.3-2q-.4-1.8-1.2-3.7-.7-1.8-1-2.3l-4.5-4c-1-1-2.7-2.6-3.7-3.3q-1.7-.8-1.8-.8l-.5-4s1.3 1.8 2.1 2l2.1.8 2.8 5 1 1.3 2.6 2 1 2.2.9 2.5 1 3.4.1 2zm-5.5-1s-1.7-2-2-3.2c-.2-1.2.2-1.2-.3-2.6s-2.3-3.5-3.7-4.6c-1.4-1.2-3.4-2.8-3.3-3.8q.3-1.2.4-1l.8.8 4.5 4.7 1.4 2.5 1 3.1.7 1.5.7 1.8zm16-23.3s3.1 3.8 4 .3c.3-1.6-1.2-4.6-1.2-4.6s-2.8-5-3.8-6.2l-5.7-7.3-3.5-1.6 1.2 2s2.6 2.6 5.9 8.4c3.3 5.7 2.7 8.9 2.7 8.9zm-17.8-23s6.1 5.2 10 10.8 5.5 9.4 4.7 10.2c-1 .7-3.4-.4-4.4-2s-1.8-4.4-5.5-8.6-5.5-4.8-5.5-4.8l-2.7-4.3zm-3.2 12.5s3.6 3 6 5.4l3.4 4.2 1.4 1.4s-.9-1.5-.4-2 1.1-.3 1.1-.3l-11.5-12zm-.7.3s1.2 2.3 3 4.1l4.1 4 .8 2.9s-5.2-4-5.2-3 1.2 3 1.2 3l-1.6-1-2.2-3.1-1.3-2.8-1.5-3.8z"/>
<path fill="#4d2a15" d="M329.5 210.2s2.7 2.4 4.2 0c1.5-2.3.3-5 .3-5s3.9 5.6 5 .3c.2-1-2.4-4.6-2.4-4.6l1-.9 1.9 3.3s1.8-.8 1.8-3-1.9-5.3-1.9-5.3l1.8 2s3.6 0 3.7-2c0-2.2-3-4.5-3-4.5s2.4.1 2.4-1.3-3.4-2.4-3.4-3.3 1-2.4 2.5-1.5 3.5 1.4 4.4-.2c.8-1.6-.1-2-.1-2l-2.1-.4-3.4-1.8-2.7-1-2.8-.4 1 2 .8 5.8v2.6l.5 5.8-1.4 4.7-.7.9-.5.6-1.2 2.6-1.6 1.8-2.2 2.7z"/>
<path fill="#ab6d29" d="M348.8 220.9s-.7-1.7-2.2-2.9a8 8 0 0 0-3.8-1.2l4 4.2zm5.6 1s-1.3-2.6-2.8-4.2-2.5-3.6-3-3.6h-.6l.4 3.3 2.7 2.7s2.6 2 3.3 1.8m4.6-.2s-.6-3-2.2-6c-1.5-2.8-3.1-4-3.1-4l.4 3 1.2 3 2 3zm3.8-1.7s1-6.2 0-8c-.9-1.6-2.7-2-2.7-2l-.6 1 .6 4.3 1.5 3.4zm3.7-10.3s.9.4 2 2.5a40 40 0 0 1 2.6 8.6c0 .3-.5.2-.7.2-.2-.1-3.9-6-3.9-6l-.6-5.4z"/>
<path fill="#d2a567" d="M340.4 216.8s2-.4 2-1c0-.8-2.4-2.2-2.2-3.3 0-.5 2 .1 2.1-.3s-2-3.6-1.3-4.4 2.4 3.5 2.4 3.5 1 2 .7 3.2c-1.2 3.6-3.7 2.3-3.7 2.3m4.6-4.4c.1-.3 3.1.4 3.1.2.2-.7-2.7-3-2.7-3s-.6-1.2-.4-1.4 2 .2 2 0c.2-.3-2-3.2-2-3.7s.6 0 .6 0 1.7 1.7 2.6 3.2c1 1.4 1.8 2.6 1.5 4.9-.4 2.5-5 .6-4.7-.2m13.6-4.6s-.5-2-.2-2.3l1.1-.5s-1.6-1-2.4-1q-.9-.2-1 0c-.2.4 1.3 2.4 1.3 2.4z"/>
<path fill="#d2a567" d="M349.4 209.4s4.7 1.2 4.9.8-3.7-4-3.7-4-.3-.6-.2-.8 1.7-.3 2.2.1 0 1.2.1 1.5c.2.3 1.7 2.5 2 2.1.2-.3-.4-3-.3-3.3s1.8 1.6 2.3 1.9 3 2.3 3.3 1.4c.3-1 .4-1.1.3-1.7 0-.6.9 2.6-.3 3s-2.6-.4-2.6-.4l-1.1-.8-.6-.7s.7 2.5-.2 2.7-1.7.2-1.7.2l-2-.2zm10.7-3.2s5 3 5.4 2.5-1.2-7.5-1-8.2c.3-.6 4 7.9 2.2 9-1.4 1-2 .3-2 .3l-2.5-1.3zm13.8 12.6 1 .5.7.2-1.4.2-.3-1zm-4.8-16.4.7.3.7.3s-.4.3-.6.2h-.4zm-1.4-2.5s.5.3 1.1-.2q.9-.7.6-1c0-.2-1-.2-1.4-.5q-.4-.4-.3-.6h-1.1l-.4.1zm4.3 14.4s1.5 1 2 1 1.1.5 1 .6c0 .1-1.2.2-1.8-.1q-.8-.3-.7-.1zm-8.1-22.5h.6q.5-.1.6 0c.2.2 0 1.2-.3 1.5l-1 .5-1.6-2.2zm-7.1-6.4h1.5q.8 0 1.5.6c.4.3.9 3.3.6 3.8-.2.6-3.6-2-3.7-2.1 0-.3-1.5-2.5-1.5-2.5zm-3.2-1.4s2.5.2 2.2-1.3a3 3 0 0 0-1.3-2.3l-2.9 2.1zm-6.5-2.8s.7.4 1.4 0c.7-.2 2.6-2.7 4-3s1.7-.3 2 0c.2.4 0 1.8-.2 2.2s-4.4 2-4.4 2l-1.5-.1-1.3-.5zm-.7-4.5c-.7-1.4-1.7-2-2.2-1.9-.4.1 1.4 1.5 1.7 3.5q.3 3.1.2 3.4l.8.1.2-.7.1-.5v-.5l-.1-1-.2-.9-.2-.6zm-5.8-2a15 15 0 0 0-5.5-3.7c-.2 0 4 2.5 5 4 .9 1.7.9 3.9 1.3 4.2s.8-.2.9-.2.3-.5.2-.7l-.2-1-.7-1-.4-.8zm-5.5-2.3c-2.4-2-6.6-3-10-3-3.5-.2-5.3.2-4.8 2s2 2.8 5 2.8c2.9 0 3.6-.3 5.2.2a10 10 0 0 1 3.6 2c.5.5-1.1-1.5-2.6-2.6q-2.4-1.7-2.5-1.7c-.1 0-.3-.7 1.7-.4s4.7 1.6 5.5 3.3.6 2.6.6 3 .7-.2.7-.2l.3-.9s0-.6-.3-1.4c-.4-.7-.5-1-1.4-2-.9-1.1-1-1-1-1zm-9.6 28.5c1.5 0 4.8-2.8 5.2-6.7s-1.9-11.9-2.5-12.7c-.5-.9 1-.4 1.7.4a25 25 0 0 1 2.5 10.6c0 4.2-.8 5.3-3 7.5-2.2 2-4 1.8-4 1.8zm5.1 11.6s.7 1 1.6.6c1.5-.5 3.8-1.7 3.8-3.8 0-1.4-1.8-2.7-1.9-3.2v-1l1.6.4.5 2.2s.6.7 1.3.4c.6-.2 2.5-1.6 2.2-3-.3-1.2-2-2.9-2.2-3.5q-.1-1.1 0-1.2l1.6.2.4 2s.4.6 1.3.4 2.4-1.7 2-2.5l-1.5-2.6v-.9l.5.4s.6.2 1.8-.1c1.1-.4 2.3-1.2 2.3-2s-1-1.6-1-2v-.6h.8l1 1.2.1 1.4-.3 1.2-1.4 1.6-1.4.6h-.4l.8 1.4-.2 2-1.3 1.2-1.7 1-.1 1.9-1 2-2 1.2h-.7l-.2 1.2-1.4 2-2.3 1.5-1.7.3-1-1.4z"/>
<path fill="#d2a567" d="M343.4 191.4s4 1.1 3.8-1.4c0-1.3-3.8-4.5-4-5s-.2-1.3-.2-1.3l1.9 1.8s2.5.6 3.2-.9c.7-1.4.1-2.3 0-2.3l.6-.1 1.1.2-.1 2.2-1.3 5.5s.3 2.2-1.4 2.3c-1.7.2-3.6-1-3.6-1m3.7 29.5s0-1.1-.8-2c-.9-.7-2.5-1.4-2.8-1.6l2.8 3.5zm5.6.2s-2-2.4-2.3-3.3c-.5-.8-1.5-2.6-2.3-2.7-.8 0 .8 3 .8 3l3.2 2.9zm5.4 0-2.1-4.2c-.4-.9-1.3-3.4-2.3-4.1s.1 1.1.1 1.1l1.1 3.3 1.9 2.9zm3.9-2.4s-.1-2.7-.5-4.6c-.3-1.8-1.7-3.4-1.8-3.3v3.3zm8.1 2-2-4.7c-.5-1.6-1.8-4.1-2-4.2l.4 3 1.7 4.7zm7-8.5s-1.2-1.2-3.9-2.3-5.4-1.2-5.4-1.2l.3.4 1.3 1 2.8 1.5 3.7.9 1.3-.3z"/>
<path fill="#202020" d="M348.2 213.8s0 2.4 1.3 3.8c1.2 1.5 4 4 4.6 4.6s1 3.4 1.9 5.8 1 4.4 1.4 5.1 1.1 1.8.5 1.7q-.8-.2-1.4-1.7c-.3-.9-.2-.9-.2-2 0-1.3-1.8-7.5-2.7-8.1-.9-.7-4.8-4-6.4-6.3-1.5-2.3-1.2-2.8-1.2-2.8z"/>
<path fill="#202020" d="M354.6 229.3q-1-2.7-1.2-4.3a16 16 0 0 0-4.4-4.2c-.6-.3-1.9 0-2.4-.3l-3.7-4s-.5.5-.3.6c.1.1 3.4 3.7 4.6 4.8 1.2 1.2 1.9 3.7 2.7 5.7s.5 1.6.6 2.5q.3 1.5 1.3 1.5t.6-.4a10 10 0 0 1-2.2-3.5c-.7-1.7-1.5-4.9-2.5-6l-.3-.3 1.3-.2c.8 0 4.1 3 4.4 4 .3.8 0 1.2 1.4 4.2s2 3.7 2 3.7h.2s-1.2-2-2-3.8zm-5.2-19.9s2.5 1.5 3.2 1.7l1.2.3s.4 4.4 2.1 6.8c1.8 2.5 3.2 3.4 3.2 3.4s0 4.8 1.4 8 1.5 7.3 1.2 8c-.3.6-1.4-2-2-4.3l-2-6.2a13 13 0 0 0-2.5-4.4c-.8-.7-.2-.6.3-.2s1.7 2 2.5 4.5 1.1 5 1.5 5q.8 0 .8-.9c0-.4-.6-1.8-1-4.1a17 17 0 0 0-1.2-4.7c-.4-.6-1-.7-2.4-3.4a20 20 0 0 1-2.5-5.3c-.2-1.2 0-1-.2-1.3-.2-.1-.4 0-1.6-.6-1.1-.6-2-2.3-2-2.3"/>
<path fill="#202020" d="M355.7 208.5s1.4 1.2 2.3 1.5 2 .4 2 .4-.3 4.2 1.3 6.9 3.3 4.2 3.3 4.2.3 1 .8 6.8l.7 8.3c.1 2.3.2 10.6-.1 10.3-1.3-1-.2-5.4-.9-7.3s-1.6-2.1-2.8-7.4c-1.2-5.2-.4-6.4-1.3-8.3s-2-2.2-2-2.2 1.6.4 2.3 2 .7 6 1.3 8.2c1.2 4.9 2.9 7.4 2.9 7.4s-.4-6.2-.8-9.7-.5-6.4-1.1-7.7c-.7-1.4-2.1-2.4-3-4.2s-1.1-5.3-1.5-6.2-.6-.6-1.2-.8c-2-.8-2.2-2.2-2.2-2.2"/>
<path fill="#202020" d="M360.1 206.2s1.3 1.5 3.1 2.6c1.8 1 2.8.7 3 1 .3.2-.3 3.8 1.6 7.2s2.6 3.5 2.6 4l.6 13.9c.3 3.4.5 14.8-.9 14.8s-.1-8.5-.8-10.4-.7-1.4-1.3-3-.7-7.8-1.8-11.5c-.8-2.8-1.6-3.2-1.6-3.2s1.4.7 2 3 .8 10 1.5 11.1c.8 1.3 1.8 2.8 2 4a157 157 0 0 0-.4-15.6c-.3-1-3.5-6.6-3.7-9.7-.3-3.2-.4-3.5-.7-3.7s-1.1 0-2.6-1.5c-1.5-1.4-2.6-3-2.6-3"/>
<path fill="#202020" d="M367.3 208c0-.3 1 1.7 3.7 2.8 2.9 1.2 5.6 1 6 1.3.3.3 2 4.5 2.1 10.2s-1 5.1-1.8 10-1 14.8-1.5 16.3c-.2.5-.5.8-.8 1.7-.3 1 .2 2.8-.7 2.8s-1-1.4-1-2.1 1-3.5 1-6.2-.6-3-1-6.2-.4-13.5-.8-14.5c-.3-1-2.1-3.1-2.1-3.1s1.9 1.2 2.4 2.6c.4 1.3.5 12.7 1 15s.3-.5.5-1.8.9-7 .8-10-1.4-9.4-2.4-10.5q.1 0 1.1 2 .1.3.8.7l1 .5s-.6 0-1-.3q-.6-.3-.6-.4a24 24 0 0 1 1.3 8c.1 5.2-.7 16.4.1 16.5s.7-6 1.5-11.2c.8-5 2-5.8 1.6-9.9s-1.4-8.6-2.3-9.2-3.4-.3-5.6-1.8c-2.2-1.4-3.4-2.9-3.3-3.2m-35.8 5.7s.8.4 1.7 0c1-.5 3.5-2.5 3.5-3.3v-1.6s0 .3.7.4c.8.1 2.8-1.4 3-2.4a8 8 0 0 0-.1-3.1c-.2-.4.6.3 1 0 .4-.4 2.4-1.7 2.4-3s-1.1-2.6-1.1-2.6 1 .2 1.6 0c.5-.1 2.5-1.3 2.4-2.4 0-1.2-.7-3-2.3-3.2 0 0 1.2.3 2.3-.1q1.5-.5 1.5-2.3c0-1.2-2.3-4-2.6-4.2s2.4.8 3-.3q1.1-1.6.7-2.5c-.2-.7-.8-.8-.8-.8s1.2.3 2.8-.5 3-2 3.8-1.9c.8 0-2.3 1.7-2.5 2.6q.1.6 2.3 2 0 .1.2.3c.7.3 3 .4 3.3.5.2.2-2.4.1-2.2.3a34 34 0 0 1 5.7 5.4q.2.2 1 .5l1.3.3h-1.6l3.3 4.7q0 .3.9.8.9.2 1.1.2c.1.2-1.4 0-1.3.2l3.3 6s-3-5.3-6.7-9.5-4.7-4.7-4.8-4.4c-.2.4 2.7 4.1 4.3 6.7s3 5.9 3 5.9-3.7-7.8-9-12.6-5.8-5.5-6.3-5.4c-.6.2-1 3.8-.7 4.5s4.2 3.6 7.4 7.5c3.3 3.8 7.5 10.4 7.5 10.4s-3-4.7-8-10-6.9-6.6-7.3-6.5-.4 1-1 1.8c-.6.7-1.8.6-1.8 1s4.2 4.3 5 4.7c.9.5 1.4.3 2 .6l1.4 1-1.6-.5q-1.1-.4-1.3 0a21 21 0 0 0 4 4.6c.6.3 1 .1 1.7.4.8.3 2 1.1 2 1.1s-1.3-.7-2.1-.8q-1.3-.2-1.2.2c.1.4 2.6 3.6 2.5 3.6s-4-4.6-7.3-7.5-4.8-3.6-5-3.5 0 1-.9 1.3q-1.4.3-1.5.7c0 .3 3 3 5 5.4l4 4.5-4.5-4.3q-3.4-3.1-3.8-3c-.2.2-.2 1.3 0 1.6s4.6 5.4 4.4 5.5c-.1.2-5-6-5.4-6-.3 0-.5.7-.8 1s-1.8.4-1.8 1c0 .4 7 7.3 6.9 7.5 0 0-5.8-5-6-4.7s2.4 5 2.2 5.2c-.1 0-3.4-5-3.7-5s-.2.6-1 1.3c-1 .8-2 .4-2 .8 0 .3 4.8 5.7 4.7 5.8-.2.1-5-5.1-5-4.8q-.3 0-.2.7c.3 1.3 1.4 3.6 1.3 3.8 0 0-1.6-3.2-2-3-.4 0-.4.7-1.2 1.1s-1.3.4-1.3.7.8 1 1.2 1.7.7 2.4.7 2.4-.5-1.4-1.1-2.3q-1-1.4-1.9-1.5c-.9-.1-.8.5-.8.5z"/>
<path fill="#202020" d="M377 212s1.5 0 1.7-.4c.2-.3-.6 0-2-1.6s-5-12.1-9-17.4c-6.6-8.7-12.7-12.7-12.7-12.7s7.3 4.2 13 12.4c5.6 8.4 7 14.9 8.6 16.6s3.2 2 3 2.5q0 .7-.9 1l-2 .1zm-48-2.2.3.1c.3 0 2.3-1.9 2.2-2.5 0 0-1-.3-1.6-1-.7-.8-.9-1.9-.9-1.9s.7 1.3 1.2 1.6q1 .7 1.8.3c.3-.2 3.2-2.4 3-3.2 0 0-.9 0-1.8-.6s-1-2-1-2 .6 1.1 1.5 1.5q1.4.5 2 0c.2-.3 2-3 1.8-3.7 0 0-1 0-1.8-.5s-1.3-2.2-1.3-2.2.6 1.3 1.7 1.5c1.1.3 2-.4 2.2-1.2s.8-3.7-.2-4.5c0 0-1.4.5-2.4 0s-1.4-1.9-1.4-1.9.9 1.2 2 1.4q1.7 0 1.9-1a6 6 0 0 0-.1-3c-.4-1-1.1-.8-1.3-2.2 0 0-.3.5-1.8.5s-2.3-1.6-2.3-1.6 1.2 1 2.4 1a2 2 0 0 0 1.6-1.9c0-.7-.6-1.4-1-2.1-.2-.8-.3-1.7-.5-1.6s-.5.8-2 .6-1.8-1.3-1.8-1.3 1 .7 2 .7q1.3-.1 1.3-1c0-.6-.3-1.7-2.2-3.3l-3.5-2.7a22 22 0 0 0 6.6 5.3s1.7.6 2 0q.3-.9-.5-2.4c-.5-1-2-2.6-2-2.6s1.6 1.3 2.3 2.7c.8 1.3.4 2.2.5 2.6s3.3 1.6 4 1.1q1-.8-.2-2.3l-1-1.8s.7.8 1.2 1.8q1 1.5.8 2.1-.4.7-.6.9l2.3 1.3c1 .6 2.3 1 2.5.8q.3-.6.2-2.2c-.1-1-.7-2.7-.7-2.7s.7 1.6.8 2.7-.1 2 .1 2.4 1.1.5 1.1.5-.6.4-2 0c-1.5-.4-7.7-3.6-8-3.3s2.9 2.3 4.3 3.7c1.5 1.3 2.2 2.8 2.2 2.8s-1.1-1-2.2-1.6-4.4-2.3-4.7-2 1.3.5 1.4.7c.1.3-1 1.3-1 1.8s4.7 3.7 4.5 4c-.1.4-1.2 1.3-1.4 1.2-.1 0-2.5-1.8-2.5-1.6s1.4 1.5 1.4 1.7c0 .3-1 .8-.8 1.4s2.6 2.6 2.6 3.3-1.4 1.3-1.1 1.7l1.5 2s-1.4-.9-1.8-1.4-1.3-1.5-1.5-1.3-1 2-1 2.5c.2.4 1.3 1 1.5 2.3.2 1.1-.2 2.4-.3 2.4s-1.6-2-2-2.1q-.4 0-.9.6c-.5.6-1.2 2.1-1 2.4 0 .3 1.6.7 1.6 1.9a3 3 0 0 1-1 2.3l-2.1-2.4c-.2 0-2.2 2.3-2.3 2.6 0 .2 1.4 1.3 1.2 1.7-.3.4-1.3-.2-1.8.1l-1.7.7c-.2 0-.5-.6-.5-.6"/>
<path fill="#202020" d="m355 180 .5-1c.3-.3 1.5-1.7 1-2-.4-.2-2 .3-3 .2s-1.7.2-4.6-2.3c0 0-9.3-6.4-23.6-7.3-4.6-.2-6.6 1.4-6.8 2.2 0 .2 1.2-1.8 5.7-1.7 2 0 16.5 1.1 24 7 .4.3 2.9 2.7 5.2 2.4 2.3-.2 2.8-.5 2.7-.2 0 .4-.7 1.2-.9 1.6l-.3 1.1zm-25 2v-.2c-.2-.4-1.1-1.7-2.7-3.1a37 37 0 0 0-8-4.3c-.3-.1 6 2.9 7.7 4.5z"/>
<path fill="#d2a567" d="M330 192.2s-.4-.6-1.7-1.4l-2.5-1.1 3 .7a5 5 0 0 1 1.1 1.8zm-.8-5.3s-.4-.6-2.1-1.2-2.2-.5-2.2-.5 1.6-.7 2.8 0a3 3 0 0 1 1.5 1.6zm-1-4.5s-.4-.8-2-1.3q-2.4-.8-2.6-.7c-.2.1 1.6-.4 3.2.2 1.5.6 1.4 1.8 1.4 1.8m-2-2.8s-.4-.6-3.3-1.3c-3-.7-4-.7-4-.7s4.1-.4 5.2.1 2.2 1.9 2.2 1.9zm-5.3-3s-1.8-.6-3.5-.7-4.4.1-4.4.1 5-.8 6.3-.5c1.4.3 1.6 1 1.6 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M304.5 238.4s.9 1.6 2.6 1.6c1.8.1 3.1 2.6 3.1 2.6l-1.9 1.9-4.6.2-1.5-2.4zm0-1.6c.2-.3-1.3-3.3-2.4-3.8s.4 1 .2 1.6-.4 1.4-.3 1.5c0 0 .9 1.4.4 2-.4.5 2.2-1.3 2.2-1.3z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M303.8 241.4s.8.4 1.4.2c.7-.2 1.3-.9 1.3-.9m-3.6-.5s2.6-1.9 2.4-2.9-.7-1-1-.9a7 7 0 0 0-2.1 1.7c-.1.3.7 2.1.7 2.1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M301 241.5s.6 1.3 1.4 1.3 2-.7 2-1.2c.1-.5-.8-1.5-1.3-1.7-.6-.3-1.9.4-1.9.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M303.4 239.8c0 .4-1.7 1.2-2.3 1.2s-1.6-1.3-1.2-1.5l1.5-.4c.4-.2.5-.8.7-.8.3 0 1 .1 1.1.5q.4.6.2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M301.8 240.9s-2.6 3.4-3.7 3.3c-1-.2.5-5 .5-5l1.2.6q1.4.3 2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M297.2 238.4s3.3.6 3.4 1.1-1.5 1.3-2.5 1.3-.9-2.4-.9-2.4z"/>
<path fill="#202220" d="M289 243.4s.5 1.6 1.9 1.7 2.8-1.5 2.8-1.5l-3.4-1.3z"/>
<path fill="#4b4139" d="m289 243.4.4.9q.4.7 1.5.9h.1c1.4 0 2.7-1.6 2.7-1.6l-3.4-1.4zl1.3-1 3.3 1.3v-.1l-.2.2c-.4.4-1.4 1.2-2.4 1.2h-.1a2 2 0 0 1-1.4-.8l-.3-.6v-.1l-.1-.1z"/>
<path fill="#202220" d="M290 243s.5 1.2 2.4 1.2 3.3-1.5 3.3-1.5l-3-2.3z"/>
<path fill="#4b4139" d="M290 243s.5 1.2 2.4 1.3c2 0 3.4-1.5 3.4-1.5l-3-2.5-3 2.7zl2.8-2.5 2.9 2.3v-.1a5 5 0 0 1-3.2 1.4 3 3 0 0 1-2-.6l-.3-.4z"/>
<path fill="#4b4139" d="M290.2 243.3s.9.6 1.8.7 2.2-.3 2.2-.3l-1.1.4-.8.1-.8-.1-.6-.3zv-.2z"/>
<path fill="#202220" d="M292.4 241.6s.8 2 2.6 1.8c1.7-.2 1.7-1.2 1.7-1.2l-2-2.8z"/>
<path fill="#4b4139" d="m292.4 241.6.5 1q.6.8 1.8 1l.3-.1q1.2-.2 1.5-.7t.3-.6l-2-3zl2.4-2.1 1.9 2.7c0 .2-.3 1-1.8 1.1h-.2a2 2 0 0 1-1.7-.8l-.4-.6v-.2z"/>
<path fill="#4b4139" d="M292.6 242s1 1.3 2 1.3a3 3 0 0 0 1.5-.4v.1l-.5.3-.8.1h-.8l-.7-.5zm-3.2 2s.8.9 1.5.9a5 5 0 0 0 1.9-.6h.1l-.2.1-.8.4-.9.3-.7-.1-.6-.4-.3-.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M297 238.3c.8-.2 2.4 2 2.4 3.2s-.5 2.7-1.3 2.7-2.6-1.7-3-2.5q-.8-1.2-.5-1.9c.3-.4 2.4-1.5 2.4-1.5zm-8 .2s-1.4-1-1.9-.5-.3 2.3 0 2.6c.4.4 2-.8 2-.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M288.8 239.7s-1.8.2-1.8 1 .9 1.6 1 1.8c.2.1 1.8-.7 1.8-.7z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M289.5 241.1s-1.8.6-1.8 1.2.8 1.7 1.3 1.7 1-1 1.2-1.3c.3-.2-.7-1.6-.7-1.6zm.8-5.1s-.3-.5-1-.2c-.8.3-1.6 1-1.5 2.3.1 1.1.6 2 .6 2l2.7-1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M290.5 238s-2.2.8-2.3 1.7q0 1.4 1 2c.7 0 2.6-1.8 2.6-1.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M292.5 241.7s-2.2 1.5-2.5 1.5-1.2-1.2-1-2 1.7-1.3 2.3-1.6c.6-.2 1.2 2.1 1.2 2.1zm2-8.2s-.7-.6-2-.5-2.4 2-2.6 2.8c-.2.9 0 2.6.5 2.8.5.3 3.3-2 3.3-2l.8-3z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M291.8 240.3c-1-.1-1.8-1.5-1.7-2.3s2-2.2 3-2c1 .1.8 2.5.8 2.5s-1.2 1.9-2.1 1.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M294.7 240s-.4 1.2-1.4 1.7q-1.4.5-2-.7c-.6-.8.2-1.2.6-1.6.5-.4 1.7-.7 1.7-.7l1.1 1.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M293.2 237.2s-1 .8-.4 1.8 1 1.4 1.8 1.5q1.1.2 1.9-.9c.6-.7 1.5-1 1.4-2q-.1-1.4-1-1.8c-.5-.3-2.2 0-3.7 1.4z"/>
<path fill="#202220" d="M296.2 233s-1-.5-1.9 0q-1.3.9-1.5 2.4l.2 2.3s1.2-.4 2-1q1-.8.8-1.1c0-.2-.4-.4-.4-1.2 0-1.4.8-1.5.8-1.5z"/>
<path fill="#4b4139" d="M296.2 233s-1.2-.3-1.9.1a3 3 0 0 0-1.3 1.9q-.3 1.1-.1 1v-1.4q.6-1.3 1.6-1.7c.8-.3 1.7 0 1.7 0z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="m328.8 257 1.6-.8 2-1.2.4 2.2-1.8 1.4h-1.5l-.9-.7.2-1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M326.4 257.2h1c1.1 0 1.2-.3 1.4-.3.2.1.5 1.3.5 1.3l-.4.7-1.5.9-1.3-.8-.1-1.8zm12.1-.8s-.3-.5-.9-1l-2.2-1.9v5l2.3-.1 1.1-1.5z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M326.4 257.2s-.4-.5-1.3-.4-2 .9-2.1 1.5 0 2.6.5 3q.7.6 2.2.3c1-.3 6.4-3.6 6.4-3.6l-1.4.3c-.7.2-1.4-.1-1.4-.1s-.7.8-1.6 1q-1-.2-1.2-.9z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="m330 258.9-1.2.5-1.3.6.4 1.6 2.3.5 1.4-1.1v-1.6l-1.7-.5zm10.4-1.2-.8-.8q-.8-.5-1-.5l-1.3 1.5v1l1.2.7.9.2 1.1-.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M332.6 257s-1 .4-1.5.8l-1.2 1c0 .2 1.1 1.4 1.1 1.4l1.2.1 2.5-1.7-.2-1.6zm9.5 2-.5-.7q-.7-.6-1.2-.6c-.3 0-1.3 1.5-1.3 1.5v.4l.8 1 1.6.5 1.1-1.2z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-linecap="round" stroke-linejoin="round" stroke-width=".3" d="m338 260.8 1.2.2s0 .3.3.5.3 0 .3 0 .2 1.2 1.4 2c0 0 1.4.6 2 .2.8-.4.7 0 .7 0l2-2.4s0-.5-.5-.5c0 0 .4-1.6-1-2-1.2-.2-2.2.2-2.2.2s-1 1.8-1.6 1.6l-.8-.5-.7-.9s-.4.1-.9-.2-.9-1.1-.9-1.1-.7-.1-1-.4l-.5-.8-1.9 1.4s0 .5-1.1 1.3-1.7.8-1.7.8-.4 1.7-1.6 1.4-2-1.6-2-1.6-.9-.2-1.4.6c-.6.7-.9 1-.9 1.7 0 .8.5 1.8 1 2.2.3.3 1.5 1.3 3 .6 1.4-.8.9-2 .9-2l.8-.5c.9-.6 1.8-1.5 2.5-1.7"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M335.8 250.8v3.2s.4 2.9-.2 3.7c-.6 1-2.9.7-3.2 0-.3-.8-.2-3.2-.2-3.2z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-linecap="round" stroke-linejoin="round" stroke-width=".3" d="M333.3 260.5s-.5.8 1 1q.6 0 .9-.4.4-.7.3-.8c-.1-.1-.2 1.2 1.2 1.2 1.4.1 1.3-1.3 1.3-1.3"/>
<path fill="#202220" d="M321.2 263.1s-.8-1.4.1-1.9 1.5 0 2.1 0 .4-1.4.3-1.7l-.7-1.2c-.2-.2-3.5.2-3.4 2.3 0 2.1 1.6 2.5 1.6 2.5m5 5.2s-1-1.5-.4-2.4 1-.3 1.2-.4q.5 0 0-1.6-.9-1.4-1.4-1.5c-.4 0-2.3 1.4-2 3.2a4 4 0 0 0 2.6 2.7m18.2-1.8s3.5 1.5 3.7-2c.2-2-1.5-3.3-2-3.3q-.5 0-1.3 1-1.1 1.2-.9 1.6c.3.3 1.3-.3 1.8.8.7 2-1.3 1.9-1.3 1.9"/>
<path fill="#4b4139" d="M326.2 268.3s-2.7-.6-2.7-3.3c0-1.8 2-2.6 2-2.6s-1.8 1-1.8 2.6c-.1 2.7 2.5 3.3 2.5 3.3m-5-5.2s-1.6-.3-1.7-2.6c-.2-2.3 3.5-2.2 3.5-2.2s-3.5.2-3.4 2.4 1.6 2.4 1.6 2.4m23.2 3.4s.7.3 1.5.3 1.8-.3 2-1.6c.4-1.3.3-2-.6-3.1-.6-.9-1.2-.8-1.2-.8s.6 0 1 .8c.6.8 1.3 1.6.8 3.1s-2 1.5-2.5 1.5z"/>
<path fill="#904720" d="m302.6 192.4-.6-.2.4-1 1.9-.7 1.8-.1 2-.1.8.6 1 2.3-3.6 2.2-.6.2-.2-1-.6-1.4z"/>
<path fill="#202020" d="m337 245.5-.3-5.5-14.8 4.6 5.8 5.5z"/>
<path fill="#d2a567" d="m309.4 235.9-1.2-1-1.4-.8h-1.1l-.2.3.2.5 1.3 1.6 2.3 1.2.8.7.5.2-1.1-2.7zm-.3 2.3.8.2.9 1 .5 1 .7 1.3.2.8-1.6-.3-4.8-1.5.4-1.2.8-.7.7-.3 1-.2z"/>
<path fill="#d2a567" d="M312.2 242.2h.8l.6-.5-.3-1.8.3-5.8-3.9-4.6-.8 2.6 1.1 4.7.9 2.8z"/>
<path fill="#ab6d29" d="M309.4 231.7s-.2.4.1 2c.3 1.5.9 4.9 2.2 7.2l-1.3-1.9s-2-4.6-1.7-8.2.7.8.7.8zm2.9 2.9s-.9 1-1 1.9v2.2s0-1 .8-1.3.7 2.3.8 2.6l.5-4.7zm.4 9.6-4.4-1q-3.5-1.5-3.4-2.3c0-.6 4 .1 4.6.2l2.4 1h1l1.9-.8.2 2zm-7.3-.8s1 .8.5 1a4 4 0 0 1-2.3.3l4.2 1 .9-.7z"/>
<path fill="#d2a567" d="M304.2 242.2c.2-.2.3.7 1.8.8s2.2-.2 2.2-.2l.7.4.8.2-.7 1.1-2.8-.8-1.5-.8z"/>
<path fill="#ab6d29" d="m314 240.3.8 1 .5-2.4v-2.5l-1.6-1z"/>
<path fill="#4d2a15" d="m309.7 236.1-1.3-2.1-1.7-3.1 1-1.8 1.3 1.8s-.4 1.3-.1 2.5c.2 1.2.8 2.7.8 2.7"/>
<path fill="#d2a567" d="M300.8 244s.6-.9 1.9-1l2.4.2 2.6 1.4.6.9-5-.7z"/>
<path fill="#8f4620" d="m314 235 .2 4c0 1 .7 2 .6 2.3q-.2.5-1 .9c-.6.2-2 0-2 0s1.3-.2 1.4-.5-.4-1.4-.5-2.4.1-2.2-.3-2.2q-.5.2-.8.6s.4-3.8.8-4.2 1.6 1.6 1.6 1.6z"/>
<path fill="#ab6d29" d="M304.7 243s-.5-.4-.5-.8c0-.3.6 1.6 3.4 1.5 2.8 0 2.6-.2 2.6-.2h1.3s-.3 1.3-.5 1.4l-2.2 1.4-.7.4v-.2l-.3-2.1-2.4-1z"/>
<path fill="#8f4620" d="M309.7 236.1s-3.2-3.3-4.3-5.5-.4-5.2-.4-5.2l3 4s-.4.7-.3 2c.2 1.2 2 4.7 2 4.7"/>
<path fill="#ab6d29" d="M307.6 248.3c0-.3-.2-1 .4-1.8 2-2.4 6.8-4 6.8-4v2.8l-6.2 3z"/>
<path fill="#8f4620" d="M308 246c.3-.1 1.4-1 1.4-1.3q0-.4-2.1-.7c-1.5-.3-2.6-1-2.6-1s1.7 1.4 2.4 1.7q.9.3.5.7zm8.3.7s-.9-1.3-1.2-2.1-.3-2.1-.3-2.2l-1 .7-2 .7s2 0 2 .6-3 1-3 1c0 .1 2.4-.4 2.3.1s-1.2.7-2.5 1.2l-2.3 1-.7.5s.6.4 1.5.3c.9 0 2.9-.5 2.9-.5l3.8-1.5z"/>
<path fill="#ab6d29" d="M316.3 246.7s-1-.3-1.8-4.8-.6-5.7-.6-5.7l1.8 1.3 1.7 3z"/>
<path fill="#8f4620" d="M316.9 239.3s-.9 2.5-.9 4q-.2 2 .3 3.4c.5 1.4 1.8 2.3 1.8 2.3l.7-.6.3-2 .1-3.4z"/>
<path fill="#202020" d="M319.4 242.7s.4 6.3-1.2 6.5c-.7 0-1.9-2.4-1.9-2.5 0 0 1.4 2.3 2 2.1 1-.4.6-6.6.6-6.6zm-3.1 4s-.3 0-.7-.8c-.4-.7 0 .6-3.4 1.8s-4.6.5-4.6.5 1.1 1 4.5 0 3.1-1.3 3.6-1.4z"/>
<path fill="#ab6d29" d="M310 238.7s.6.5 1.2 1.7l1 2h.2s-.4-1.3-1-2.2l-2.2-3.1z"/>
<path fill="#8f4620" d="M308 246.5s.3-1.1 0-1.4-.7 0-3-.2l-4.2-.9s1.6 1 3 1.4l3.5.8.5.1z"/>
<path fill="#202020" d="m308 246.5-.2-.5c-.3-.3-.7.1-3.5-.7s-3.5-1.3-3.5-1.3 1 1 3.4 1.5z"/>
<path fill="#ab6d29" d="M309.3 238.2s-2.1.1-2.8.7-1.6 2-1.6 2l1.3.1s-.1-1.2.5-1.9q1.1-.7 2.6-.9"/>
<path fill="#8f4620" d="M305.3 234.2s.8 1.2 1.7 1.5q1.5.6 2.2 1.2c.4.4.9 1.8.9 1.8s-3-1.3-3.7-2z"/>
<path fill="#8f4620" d="M308.2 235s-.3-.5-1.1-.9-1.4-.3-1.7-.1-.1.2-.1.2l1 1.7.7-.2s-1.6-1-1.3-1.3c.8-.7 2.5.5 2.5.5z"/>
<path fill="#202020" d="m316.3 231.1 17-8.7 8 11.1-14.7 4-7.7.6-3.8-2.7z"/>
<path fill="#4d2a15" d="m339 235.9 21 18.2-2.3 1.6-5-1.4-10.2-10.1-3.8-3.8z"/>
<path fill="#4d2a15" d="m366 250.5-12-9.1-15.3-11.5.4 5.5 17.4 16 5.7 2.7 3-.8 1.4-1.6z"/>
<path fill="#803f1d" d="M339 237.6s6 6.9 10.6 10.4 9 7.3 10 6.7c.8-.5.5-1 .5-1l-21.3-17.5z"/>
<path fill="#4d2a15" d="m353.8 255.1-2.1 1.7-2.5-1-13-13.2.7-2.5 1.8.3z"/>
<path fill="#202020" d="M366 250.5s-1.6 3.8-5 2.9-22-18.5-22-18.5v1.8s18.8 17.3 22.5 18 6.6-2.5 6.2-2.8z"/>
<path fill="#803f1d" d="M336.9 240.4s6 7.7 9.1 10.5c3.2 2.9 6 5 7.2 4.8s-.7-1.7-.7-1.7l-9.1-9-4.7-4.6s-1.8-.7-1.8 0"/>
<path fill="#202020" d="M338.4 240.4s15.2 15.5 16.4 15.9c2.5.7 6.2-2 6.2-2l-1.3-.7-.2.5s-.7 1.6-4 .9-16.6-15.1-16.6-15.1z"/>
<path fill="#4d2a15" d="m344 255.3-5.5-6.4-1.2-2.1-.3-2.7 11 11.1-1.5 1-1.4-.4z"/>
<path fill="#202020" d="M354.1 255.5s-1.3 2-3.2 1.6a5 5 0 0 1-2.8-1.7L337.5 245l-.3-1.6s11.7 12.3 13.7 12.8c1.6.4 2.7-1.4 2.7-1.4z"/>
<path fill="#202020" d="M348.2 255.1s-.5 1.6-2 1.4c-1.6-.3-2-.9-2-.9l-6.6-7.7-.1-2s6.8 9.3 8.3 9.8 2-1 2-1z"/>
<path fill="#b07229" d="M341.3 234.9s6.3 5.4 6.8 5.6c.5.3 2.7.4 2.8.7.2.3-1.4 0-1.5.3-.1.4 3.2 3 3.7 3.1.5 0 3.2 0 3.1.4 0 .3-1.8 0-1.9.4s2.9 2.7 3.4 2.8 3.9 0 3.9.4c0 .3-2.7 0-2.7.5.1.6 4.6 3.4 4.4 3.8s-5.4-3.5-5.5-3.3c-.2.2-.4 1.4-.7 1.6s0-1.6-.2-2.4-3.4-3.1-3.7-2.9 0 1.7-.4 1.7.1-1.7-.2-2.3a16 16 0 0 0-3.6-3c-.4 0-.1 1.3-.4 1.5s-.2-1.5-.6-2-7-6.5-6.7-7z"/>
<path fill="#4d2a15" d="M330.5 252.3s0 2.4-1 2.8c-1.1.3-3.1-4.4-3.1-4.4l-.5-4.4 3.1 3zm8.4-1.2V250c0-.6-.3-1.5-.3-1.5l-.2-2.4-.6-1.7-.4-1h-.7l-1.7 1.2-.5 1.6.8 3 .8 2.7 1.3.4 1.5-1z"/>
<path fill="#4d2a15" d="M336.8 255.3c.3-.1-.4-3.4-.4-3.4l-2-4.8-.5-1.7-1.5-.6-1 1.2.2 1.9s1.5 3.2 2.2 4.3 2.3 3.3 3 3.1"/>
<path fill="#202020" d="M335 245a6 6 0 0 1 1.4 2.6s1.3 3.6 1.1 4-1 .3-1 .3 1.8 1.8 2.3 1.6.3-2 .1-2.4-.7 0-1-.5c-.1-.4-.8-2.7-.8-4.2v-2.7c.2-.3 1.5 3 1.5 4.7l.6.7-.6-3.2-.7-2.3q.1-.4.3-1.5v-1s.6.5.9.5q.5.2.8-2c.2-1.5-.1-6-.1-6l-6.8 9z"/>
<path fill="#202020" d="M335 244.3s-.3 2.4.6 4.8l1.3 3c.2.3.2 2.9-.1 3.2 0 0-1.8-4.4-2.4-6.6q-.7-3-1-3.2l-.8-.2z"/>
<path fill="#4d2a15" d="M334.4 257c.2 0 0-1 0-1l-.2-2.3-2.4-4.9-2.6-2.3-1.4-2-.2 4s2 4 3.1 5c1.3 1.2 3.3 3.6 3.7 3.5"/>
<path fill="#202020" d="M324.7 246s.4 1.1.4 1.8v1.4l1.8 3.5c.9 1.5 1.7 3.1 2.4 2.8s1-1.5 1-1.7c.1-.1-.8 1.3-1.2 1.1a40 40 0 0 1-2.3-6.6l-1-.8-1.1-1.4zm7 5.5-1.7-3.1q-1-1.6-1.4-1.7c-.2 0-.5-2-.5-2s.7 1.3 1.6 1.7q1.4.8 2 .7l.2 2z"/>
<path fill="#5c3a1d" d="M327.4 248.3s.5 1.4 1.4 2.7 2.3 2.9 2.3 2.9-1.6-1.6-2.5-2.8l-1.4-2.8z"/>
<path fill="#202020" d="M332 246.7s.5-1.2.8-1.3l1.2-.3s-.7-.1-1.2-.8l-1.3-1.3.4 2zm-.4.4s2 4 2.4 5.5c.5 1.3.4 3.4.4 3.4s-.2-1.5-.6-2.7l-2.4-5c-.1-.3.2-1.2.2-1.2"/>
<path fill="#4d2a15" d="m334.2 244.4.5-6-.5-2.2-2.7-6s-.1.8-.8.8a6 6 0 0 1-3.1-1.8 9 9 0 0 0-.2 4.8l5.3 9.9z"/>
<path fill="#8b441f" d="M323.8 248.3s.8 1 1.3 1c.4-.2.3-2.1-.3-3.2-.6-1-7-9.6-8-10.1s7 12.3 7 12.3"/>
<path fill="#8b441f" stroke="#5c3a1d" stroke-width=".1" d="M328.1 244.8c.2.4.6 3.1-.5 3.4-1.5.3-8.5-9.4-9.6-10.7 0 0-4.4-2.8-4.5-3.5s4.7 1 4.7 1l9.5 9z"/>
<path fill="#202020" d="M323.8 248.3s-.8-1.3-.9-2.3q-.2-1.4-.4-1.9c0-.3-5.2-7.7-5.7-8.2s-2.3-1.4-2.3-1.4l.3 2.4s1.6 3 4 5.7z"/>
<path fill="#4d2a15" d="M331.6 247c.7-.3.4-2.7.1-3.5l-4.4-9.4-1.3.4-2.3-1.8-1.4-1.5s.5 2.1.4 3.1v1.6l3.3 5.9zm4.7-5.2 1-1.3-.5-4.4-2.8-9.4s-.7.1-1.4-.3-1.4-1.3-1.4-1.3l.7 5.7 2.9 8 .2 2.3z"/>
<path fill="#202020" d="M327.7 244s-2.8-3-3.7-4.5l-1.7-3s-.8.3-1.8 0c-1-.5-2.2-1.8-2.2-1.8s-.8 1.5 3.2 5.6c4.6 4.6 6.2 3.7 6.2 3.7"/>
<path fill="#8b441f" d="M334.4 245c1.2-.4 1.1-5.4 0-8.7-1-3.3-2.9-6.2-2.9-6.2s2.3 5.4 2.8 7.5c.4 2.2 0 3.7-.4 3.6s-.4-1.3-1.3-3.4q-1.6-2.8-1.6-2.6c0 .2 3.3 8.8 2.3 8.6s-5.3-9.9-5.5-10.2-.4.4-.4.4 4 12.2 7 11"/>
<path fill="#4d2a15" d="m337 221.3 2 4.2.6 4.4.2 3.7s0 7.4-.8 7.6c-1 .1-1.4-.7-1.4-.9l-1.7-8.3-1.7-8.5.6-.6.2-1.2 1.3-.1z"/>
<path fill="#8b441f" d="M331.6 247c.8 0 0-2 0-2s-.6 1-1.2.3-1.3-3.1-1.3-3.1l-.5-1.5s-.6 1.6-1.3 1.6-4.6-6.4-4.6-6.4l-.4.6s5.4 11.1 9.3 10.6z"/>
<path fill="#202020" d="M329.1 242.2s.7-1.6.4-3c-.3-1.5-2.2-5.1-2.2-5.1s-.6.1-1.4-.2l-2.2-1.2a38 38 0 0 0 5.4 9.5m3.1-2.3s-.5-3.4-2-6.6c-1.6-3.1-2.6-4.1-2.6-4.1v3.5s1.5.5 2.6 2.4c1 2 2 4.8 2 4.8m4.3-5.8s-.3-3.7-.8-5.8l-1.2-5s0-.2.3-.4h.6c.8.3 1.1 2.2 1.1 2.2l-.1-3.1-.3-.5h-1.4s.3 1-.1 1.3c-.3.3-.6-.2-.7-.3l-.2.4.4 2-.1 1.7s.5 2.2 1.2 3.5c.6 1.4 1.3 4 1.3 4m3.3-.5s.6-5.3-.6-8.7-2.2-4.2-2.2-4.2l-.6.6s1.8 3 2.4 5.6zm-5.4 2.7s-.6-4.1-1.3-6.8l-1-3.6-1.1-1s.7 2.7.6 3.6l-.1 1.4s.5 1.4 1.3 2.6z"/>
<path fill="#904720" d="M337.2 242.9c1 0 .1-6-.3-7.5-.4-1.6-3-8.8-3-8.8s3 10.3 2.8 10.8c-.3.8-1.5-3.1-1.7-3-.2 0 2.2 6.9 1.3 7-.8 0-1.3-1.5-1.3-1.5l.1 1.5s.6 1.4 2.1 1.5"/>
<path fill="#904720" d="M338.5 239.9c.7 0-.2-5.7-.7-8.2l-2-7.5s1.5 3.5 2.3 7.5 1.4 9 .6 9.2c-.8 0-1.2-.8-1.2-.8v-.9s.3.7 1 .7"/>
<path fill="#312317" d="M328.8 246.5c0 .4 0 2.4-1 2.2s-2.9-1.9-3.3-3c0 0 2.2 2.5 3 2.4.8 0 .8-2.5.6-3.2 0-.6.6 1.6.6 1.6z"/>
<path fill="#d2a567" d="M334.1 219.5s1.4.9 2.4.3a4 4 0 0 0 1.8-3q.2-1.1-.7-2.4c-.7-.9 1 .7 1 2.3s0 4.3-2.5 5c-2.5.5-1.4-.2-1.4-.2z"/>
<path fill="#8f4620" d="M332.1 214.7s1.6 2.6 2.2 5q.9 3.3.2 3.3t-.7-.9c0-.6-1.7-7.4-1.7-7.4"/>
<path fill="#1e2121" d="m304.1 218 10.6 15.3 13.2-15-8.3-8.6z"/>
<path fill="#4d2a15" d="m317.5 208.5-.6 2.4 1.3 4.1 2.2 2.3 2.5 1.7.6-2 .6-2.2.7-.2 1.6.6 1-1.5 1.8-3.1-.4-1.2-.4-.9-2.7-4.6-.9 1.7-.8.5-1.6-.8-1.8 2.4-2.3-1z"/>
<path fill="#5c3818" d="M323 219s.4-.3.6-1.6c.2-1.2.2-4.9-.1-6.4s-1.3-4.4-1.5-4.2 1.5 3.3 1.3 7c-.3 3.6-.2 4.6-1 5-.7.3.6.2.6.2z"/>
<path fill="#5c3818" d="M323.6 212.8s1.7 2.5 2.5 1.6.5-4 0-5.3l-1.2-3 .5-.6.9 3.4c.3 1.4 1.3 5.2.1 6-1.1.6-2.8-1.5-2.8-1.5z"/>
<path fill="#8f4620" d="M317.9 214.6s.6-1 1.4-1q1.1.2 1 .3l.6 2 1.2 2.2 1 1-.5.6-.9.2-2.5-1.4-.7-1-.6-3z"/>
<path fill="#d2a567" d="M322 236.3c-.8.5-2.7-.5-3.7-1.4l-6.8-6.7-.8-3.2 2.2.6 3.9-1.7.7-1.3 4.6-2.4 2.6-1.4 5.5-.1 1.4-2.7s1.8 3.2 2.1 4.5c.4 1.4.3 5.4-.2 5.7s-2.3-1.2-2.6-1.3c-.2-.1 1.4 5.7 0 6s-3.6-2.2-3.6-2.2 1 5.2-.2 5.2a10 10 0 0 1-5-2.6s1.3 4.3 0 5z"/>
<path fill="#8f4620" d="m321 229.6 1.1 1.7c.3.4 0 2.9-.2 3.4-.3.9-1.6-1.2-2.3-3l-2.2-3.8c0-.4 3.5 1.7 3.5 1.7zm10.8-12.9s2 8.1 1 8.4-3.6-2.6-4-3l-4-6.4-.8-.8.2-.4 1.7.6z"/>
<path fill="#8f4620" d="M317.9 232.3c-.4.3-1.3-1-1.7-1.4l-3-3.3a12 12 0 0 1-2.2-2.5c0-.3-.8-1.1-.8-1.1l.7-1h.9s5.4 4.2 5.7 5c.4.6.6 4.1.4 4.3m8 0c-.2.3-3.7-2.3-5.2-4.4s-3-4-3.5-5l-.6-1.9 1 .2s8.8 10.8 8.4 11.2zm1.3-3.7c-.2.3-1.7-.9-2.7-2.1s-.4-2.9-.4-2.9 3.3 4.8 3.1 5m2-.5c-.5.3-2.5-2.2-4.2-5-1.3-2-1.6-2.3-1.8-2.5-.5-.3-1.4-.4-1.4-.4l-.2-.3 1-.2.6-.8 1-1.3 4.3 8.5s1 1.9.7 2"/>
<path fill="#ab6d29" d="M318.3 234.9c0 .3 0 .6-1.2.5a21 21 0 0 1-9.9-7.8l-3-5 1.1-1.4 9.6 11z"/>
<path fill="#8f4620" d="M318.3 234.9q-.1.7-1.1.5c-.6-.2-3-1-6.9-5.3a33 33 0 0 1-5.3-7.4l-.2-1.8 1.2-.5 6.5 10z"/>
<path fill="#4d2a15" d="M324.6 230.6s-2-1.5-3.2-3a24 24 0 0 1-3.8-5.7c.1-.7 1.2-.7 1.2-.7v-1.8l.2-.6 1.9.8 2.3 2.9.9 1.1s1.5 3.7 1.2 4-2.4-2-2.7-1.7 2.4 4.4 2 4.7m-3.7-1s.7 1 .4 1.7-.5 1-1.5-.4a16 16 0 0 0-2.3-3c0 .1.1 2.3-.3 2.3s-2-1.9-3-3.3l-2.5-3.9v-2l-.1-2.5.3.9.4.7 3.6 4.2 1-.7 2.1 4zm-13.5-6.1s2.8 4.8 5.3 7.5a17 17 0 0 0 5.6 3.9l-8.4-9.9zm21.2 2.8a12 12 0 0 1-3-3.4c-1-1.6-1.9-4-1.8-5s.4-3.4.4-3.4 2.8 3 3.4 4.3c.5 1.4 1.7 3.3 1.4 3.5-.2.2-2.1-1.3-2.4-1s2.3 4.8 2 5m3.2-3c-.3.1-1.8-1.6-2.1-2.6l-.6-3.2-1.2-3v-3.3l1.4-.2 1.9 2.7s.7 1.5.7 3.7.3 5.6 0 5.8zm-22.5 8a42 42 0 0 1-4-6l-1-2.9.5.4s.8 3.2 4.7 7.1c4 4 4.9 4.2 4.9 4.2s.7 2.2.4 3c0 0-2.9-2.6-5.5-5.7z"/>
<path fill="#202020" d="M313.6 235.9s-3.4-3-6-6.4a47 47 0 0 1-4.4-6.6l1.2-1a46 46 0 0 0 9.2 14m4.7-1-6-6.1c-2-2.2-3-4.4-3-4.4l1.6.6s.7 1.7 2 3.5zm-.8-7s-2.1-2.6-3.1-4.2l-2-3.8s2.8 3.3 3.6 3.5.9 0 .9 0-.4 1.2-.1 2.2zm.9-10.6s.3 1.9.1 3c-.2 1-.8 1-.8 1a16 16 0 0 0 4 4l-1.8-2.7c-.5-1-1.3-2-1-2.8.4-1 2.1.6 2.6 1l2.6 2.8s-.8-2.5-1.8-3.4z"/>
<path fill="#202020" d="m327 222.8-1.7-4.2-1.6-5.2v2.8c-.1 1.2-.5 3-1 3.3s-.7.3-1.7 0 .7.5.7.5 1 0 1.4-.5.5-1.5 1-1.4 1 1.7 1.3 2.4z"/>
<path fill="#202020" d="M323.7 213.4s2 2.3 3 1.3c.8-1 .3-4 .3-4s1.2.5 1.6.2c.4-.4.2-1.5.2-1.5s2.3 3.2 3.3 5.3c1.1 2 1.6 5.8 1.6 5.8s-1.8-3-2.7-5.8c-.9-2.6-2.1-3.8-2.3-3.5a5 5 0 0 0-.3 3c.2 1 1 2.1 1.5 3.5l1.1 3.4-1.6-2.5q-1-1.1-1.7-1.6c-.4-.4-1-1.2-1.7-1.4-.6-.3-1.8-.9-1.8-.4s-.3-1-.3-1z"/>
<path fill="#171717" d="M316.8 223.3a27 27 0 0 0 4.2 6.3s-2-1.8-2.8-3a16 16 0 0 1-1.4-3.3"/>
<path fill="#8f4620" d="M330.2 223.9c-.3 0-1.3-.3-1.4 0 0 .4.6 1.8 1.3 2.5q1 .9 1.1.6.2-.2-.2-1.4v-1.7c-.1-.3-.8 0-.8 0"/>
<path fill="#d2a567" d="m306 209.4-1.3.7-1.4 3 .4 5.3 2.1 3c.8 1.2 3.5 4.2 4.2 4s1.4-2.3 1.5-3.4l.2-3.2 1.8 2.3c1.1 1.3 2 2.4 2.6 2.3.5 0 2-3.2 2-3.6.1-.4-3.8-10-3.8-10l-1.8-1-6.6.6z"/>
<path fill="#ab6d29" d="m314.8 217.4 1 3.7c.5 1.2 0 2.3.3 2.3s2.3-2.7 2.3-4 0-2.5-.5-6.3.2-5 .2-5l-1.2-1-2 4.2zm-7-2.2.7 5c.3 1.2 1 3.3 1.4 3.2s1.6-3.3 1.7-4.4c0 0-.9-1.6-.8-5.5 0-4-.4-4.4-.4-4.4z"/>
<path fill="#45392d" d="M302.5 207.5s-3.1 4.6-3 7c0 2.5.4 2 1.1 4.2s1.1 5.6 1.2 5.9c0 .2.5-.2.5-.5z"/>
<path fill="#ab6d29" d="M302.5 208s-2.7 4.3-2.5 6.6.5 2.3 1.3 4.4c.8 2 .8 5.4.8 5.4s1-.2 1.9-.9c1-.7 1.8-2.1 1.8-2.1l-1-2.1-.6-2-.8-1 .2-4.1.7-3v-1l-1.8-.1z"/>
<path fill="#dbad6c" d="M317.2 215.3s-1.3-.3-1.9-.6-.8-1.1-.8-1.1l.4-.8z"/>
<path fill="#d2a567" d="M303 208.5s-2 4.3-1.7 7.2 1.9 6.6 2 6.7q.1 0 .1-1v-8.7l.9-2.8v-1.5z"/>
<path fill="#8f4620" d="M307.8 209.5s.3 1.2.1 1.8c-.2.5-1.2 1.2-1.6 2s-1 2.8-1 2.8.4-1.1 1-1.8q1-1.2 1-.8c.2.4.4 2 .3 2.3q-.4.3-1 2c-.4 1.3 0 2.4 0 2.4l.5-2.2c.2-.6.7-1 .7-1l1 2.4s0-1.1.3-2c.1-.8 1-2 1.3-2s.8-.1-.3-.7c-.4-.2-.1-4.2.1-4.4s1.9-.2 1.7-.8l-1.6-2.2c-.1-.1-2.5 2.2-2.5 2.2"/>
<path fill="#dbad6c" d="m317 218.1-1.5-.3c-.5 0-.6-.6-.6-.6l.2-.6 1.2.7z"/>
<path fill="#8f4620" d="M304.4 218.6q-.1.6-.5 1.5c-.3.7-.4 1.6-.4 1.6l-.4-3.3-.3-2.4c-.2 0-.7.7-.7.7s0-1.6.3-2.3.5-1.8.3-2q-.6 0-1 .7l-.8 1.3s.3-1.3.6-1.9 1.8-2.2 2-2.8q.5-1.2.4-1.4h1s1 .8 1 1.5c0 .4-1 .9-1 1s-.8-.1-1 3.2.6 3.2.5 4.6m9.4-8.7s.2 1.8 0 2.4q-.4.8-1.1 1.8c-.4.7-.4 2.4-.4 2.4l.5-1.4.7-1q.3.1.8 1.7l.8 2.5s-.2-1.2.1-1.2.5-.1.9.2l.9.8s0-.5-.7-1.1-1-.6-1.1-1.5q-.3-1.6-.2-2.2c0-.3.5.3.9.7l1.5 1.4-.7-1v-3q-.1-1.7.4-2.1c.3-.3.4-1.6.2-1.8-.1-.2-3.5 2.4-3.5 2.4"/>
<path fill="#1e2121" d="M324.6 206.1a24 24 0 0 0 1.5 7.1s-1-2-1.6-3.6-.9-3.5-1.3-3.6-1 .8-1 1.5.5 3.3 1.1 4.2-1-1.2-1.3-2.3l-.7-2.2s-.4 1-.3 2c.2 1.1 1.1 2.7 1.1 2.7l-1-1-.6-.7s0 1.5.4 2.1l1.2 1.7-1.3-1-.4-.8s.2 2.1.6 3.3l1.5 3s-1.2-1-2-2.9-1.2-7.3-1-8.1l1.1-1.9c.6-.8 1.3-4.7 1.3-4.7s.9 3.6 1.5 4.3q1 1 1.2 1zm-8.6-1 .8 1.3.6 1s-1 1.1-2.3 2c-1.2 1-2.8 2-3.1 1.5q-.5-.9-.4-1c.1-.1 1.4-.8 2.4-1.8a7 7 0 0 0 1.6-2.2q.4-.9.5-.9zm-10.9 3.5s.2 2.2 1.2 2.2 3.7-2.9 3.9-3 .4-.8.4-1l-.2-.8s-3 3-4 3.2c-.9 0-1.3-.6-1.3-.6m.1-3-.3 2q.1.8.2.9c.1.1-2.2.7-2.5 0-.4-.6-.1-1.6-.1-1.6h1.5c.6-.2 1.2-1.3 1.2-1.3m20.3-3.1 1.5 3.2 1.4 2.8s-1.4-1.7-1.9-2l-1.2-1 .2-1.1z"/>
<path fill="#dbad6c" d="M302.1 207h1.3q.8-.1.8-.2l.9-1.2s-.5 1.4-.2 2.5c.3 1 1 1.1 1.2 1.1a4 4 0 0 0 2.2-1.1c.9-1 2.1-1.9 2.1-1.9s-.3 1.3.2 2.7q.7 1.8 2.2.4c1-.8 2-2.2 2.6-2.8l.7-1 1.1-1 1-5.4-.5-.7-1.4.7-1.7-.7-.5-.7-1 1.7-1 .6-.5.1-1.2.3-.5-1.1-.3-.8-.6-.3-2.4 2.5-1.4-.1-1.3 2.2-1 1.3-1 2.4z"/>
<path fill="#8f4620" d="M313.9 201.5s.2 1.3-.5 3.6-1.6 3.9-1.6 3.9 2.4-1.5 3.6-4.2c1.1-2.6.9-5.7.9-5.7zm3-2.5s.2.4 0 2.4l-.8 4 .6 1.2c.4.7 1.2 1.8 1.7 1.6s1.6-.9 2.2-2.2 1.1-2.7 1.4-2.9q.3-.1.4.2c0 .1.3 1.3.9 1.8.5.5 1.3 1.3 1.6 1 .4-.2.8-.9.7-1.2l-1.3-4-1.6-4-1-1.6-2.8.3z"/>
<path fill="#ab6d29" d="M321.7 195.3s.8 1.7.9 3.6c0 1.8-.4 3.8-.4 3.8s.2-2.3 0-3.7q-.6-2.1-.7-2.7z"/>
<path fill="#4d2a15" d="M325.6 202.6v2.3c-.2.8-1.3-.5-2-3s-.5-3-1-4.3l-.9-2.2 1.5-.3zm-7 4.6s2.8-1.5 3.1-6.2c.2-2.1-1.2-5.7-1.2-5.7s0 3.8-1 6.2l-1.9 4s.7-.2 1-.6l.8-1.1s.1.7-.1 1.6l-.6 1.8zm-2.3-8.1s-.1 1-1.3 2c-1 .9-3.1 1.9-3.1 1.9s1.5-1.4 2-2.6.1-2 .1-2-.2.6-.8 1.1-1.6.6-1.6.6 0-.6.7-1.3q0-.2.2-.7c.3-1 1.1-2.3 1.1-2.3s.7 1.4 1.3 2z"/>
<path fill="#1e2121" d="M324.7 193.6a18 18 0 0 0 .9 9s-1.4-2.2-1.7-3c-.4-1-.4-2.3-.7-3l-1-1c-.3-.2-1-.1-1-.1s.8-.4.8-1.1v-1l1.4.3h1.3zm-6 9.8s1.4-2 1.8-4.6 0-3.5 0-3.5-.6 0-1.1-.5-1.3-1.8-1.3-1.8.2 1.4 0 3c-.4 1.6-1.8 3-1.8 3s.8.4 1.4 0c1.5-.9 1.4-2.5 1.4-2.5s.3.2.3 2.2-.7 4.7-.7 4.7"/>
<path fill="#8f4620" d="M301.2 206.9c-.3 0 1-2 1.8-3.4l2.1-3.4s.3.3.8.3a4 4 0 0 0 2.4-1.7 7 7 0 0 0 1-2.5c0-.3-.1 1.8 1 3 1 1.1.9 1 1.3 1 0 0 0 1.4-.4 2.7l-.8 3.3s.2-2.5-.3-3.2-.6 0-1 1c-.3.9-1.1 1.5-1.1 1.5s.6-1.1 1-2.3.2-1.2 0-1.2q0-.2-.6.4-.8.7-.9.6c-.1-.1.5-.4.7-1l1-1.8q.4-.5.4-1t-.2-.7-1.4 1.2l-1.3 1.3-.9 2.6-1 2.8-.6.4.9-2.4 1-3.4q-.5-.5-.8 0l-2 2.8c-1 1.4-1 3.1-1.2 3.2l-1-.1z"/>
<path fill="#874f20" d="M325.7 192.1c0 .2 0 1.2-1 1.5a4 4 0 0 1-2.3-.2c-.2 0-.2-1.4-.2-1.4z"/>
<path fill="#b27129" d="M306 195.4s-.2.9-.8 2c-.5 1.2-1.4 2.2-1.3 2.4s.6.7 1.4.6 1.1-4.8 1.1-4.8l-.1-.2z"/>
<path fill="#dbad6c" d="M318.4 192.7s-.3 6.4-2.1 6.4-2.7-3.2-2.7-3.2-.1 4.3-2.4 4.3c-1 0-2.1-3.4-1.9-4 0 0-.4 2.3-1.5 3.2-2.2 1.7-3.5.9-3.6.6 0-.3 1.8-2 2-4.6 0 0 .4.1 1-.6s.7-1.6 1.3-1.5c.5.2 3-1 3-1l1.4-1.7s.2.5 1.2-.2a4 4 0 0 0 1.3-1.7l2.7 2.3z"/>
<path fill="#b27129" d="m311.4 186.6 2.7 3.8.6-.5q.7.8.4 1.2c-.4 1-1.3 2.3-.5 3.7 0 0-.3-.8.1-1.8s.7-1.4 1-1.3c.2 0 .6 5.8 1 5.8.3 0 .9-2.7.9-4.5s.3-.3.5 0c.2.4 1.8 3 3.1 2.6s1.2-2.8 1.2-3.3-1-3.7-1-3.7l-6.1-3.9-.5 1.6c-.3 1-3.4.3-3.4.3"/>
<path fill="#8f4620" d="M320 186s.4.4 1 1.9.7 6.4-.1 6.4-2.3-1-2.6-1.6 0-3.1-.4-3.8a8 8 0 0 1-1.2-2.7l.1-2 1.4-.5z"/>
<path fill="#4d2a15" d="m319.6 184.5 2.6 3.9 2.3 3.3 1.2.4a2.5 2.5 0 0 1-3.2.5c-.9-.5-.4-2-1.5-4.5s-3.2-3.6-3.2-3.6l.7-.7z"/>
<path fill="#1e2121" d="M318.2 182.4s2.3 1.5 3.1 3.2 1.3 2.7 2.3 4.3 2.1 2.1 2 2.2c0 .1-.7.3-1.2 0a5 5 0 0 1-1.4-1.6l-2.4-4.2c-.5-.7-2.3-2-2.3-1.9s.5.5 1.5 2.1a7 7 0 0 1 1.2 3.1l-1.1-2c-1-1.5-2.8-3.3-2.9-3s1 1.9 1.5 2.9l1.3 3.3-1.8-3c-1-1.5-1.2-1.3-1.6-1.8l-1.4-1.8s1 .3 1.8-.3c1-.6 1.4-1.5 1.4-1.5"/>
<path fill="#8f4620" d="M312.3 186.7s1.5.3 2.3.2h1.1s.5.6.5 1.4-.5 2.1-.8 2.2c-.2 0-.2-1.3-.5-1.8z"/>
<path fill="#b27129" d="M313.1 190.6s.5 1.6.1 3.9-1.7 4.6-2 4.6-.2-2.6-.4-4v-.4l-.5 1.6.4-2.6-.1-.8 1.6-2z"/>
<path fill="#8f4620" d="M310.3 192.3c-.5.5-1.2.4-1.8.6s-.9 1-.9 1 .5-.3.7-.2c.3.1.5.1.1 1.2s-2.2 3-2.1 3c0 0 2-1.5 2.5-2.5.7-.9.6-1.4.8-1.8 0-.3.8-.7 1-.7q.3-.2.6.7l.6 2.6.8-2.6c.2-1 .2-2.9.2-2.9l-.6-.1c-.5-.1-1.5-1-1.5-1s.2 2-.5 2.7z"/>
<path fill="#dbad6c" d="M318.3 180.8s.6 1.5-.7 2.6q-.5.5-1.4.6l-1.7-.6-4-1.6-2-.6-1-.3.2-1.1h3l3 1.2 1.3.5 1.1.2h.9l.7-.2.4-.3z"/>
<path fill="#ab6d29" d="M318.3 180.8c0 .3-.3 1.1-.6 1.3-.3 0-.8.3-2 0s-2.3-1-3.6-1.3q-1.9-.6-3.3-.7c-1.4-.1-.5-.4-.5-.4s2.3-.3 4.3.5c2 .7 2.9 1.2 4 1.2 1.4 0 1.7-.6 1.7-.6"/>
<path fill="#4d2a15" d="M307.6 179.6h.9q.6-.2.7-.3c.1-.1 0 .5-.2.7s-1 .1-1.2.1z"/>
<path fill="#dbad6c" d="M315.3 184.4s1.3 1.3 1 2.4l-2.7-1.2-4.2-1.7-2.3-.7-.3-.4.6-.8.2-.8 2.8.4 3 1z"/>
<path fill="#dbad6c" d="M312.4 186.3s2.4 3.1 1.8 4.1l-5.6-3.5-2 3.3-4.7-2.3s3.8-3 4.2-3.5l.5-.7 2 .4 3.4 1z"/>
<path fill="#ab6d29" d="M311 181.6s2 .5 3.3 1.2 3 .8 3 .8-.6.7-2 .8c-.4 0-1.4-.8-2-1.3s-2.6-1-2.6-1z"/>
<path fill="#4d2a15" d="M307.6 180.6s1.4.3 2.2.7c1 .3 1 .2 1.2.1q.4-.1.3-.2c-.1-.1.2 1-.5 1s-1-.3-1.8-.7c-1-.4-1.5 0-1.5 0v-1z"/>
<path fill="#ab6d29" d="M315.4 186s1 .7.9.9q-.3.1-1.6.4a3 3 0 0 1-2-.4c-.6-.2-1-1.3-2.5-2l-2.8-.9-1.3-.2 1-.4 3.2.6 2.8.9 1.7.8.6.4zm-5.4 2 .6 2.6-.4 1.5-.8.6-.8-.3-.3-.5s.4-.6 0-1.1c-.3-.6-2-1.2-2-1.2s.7-1.3 1.4-1.6c.8-.3.4-1.2.4-1.2z"/>
<path fill="#4d2a15" d="M306.8 182.8s.5.4 1.5.5q.8 0 2.2.5l1-.3-.5.4c.9.2 2 .7 2.5.9a6 6 0 0 1 2 1.3l-2-1-3-.9c-2-.3-4-.2-4.3-.4-.4 0 .1-.1.4-.3.2-.2.2-.7.2-.7"/>
<path fill="#ab6d29" d="M314 189.8s.7.7 0 .9-2.4.2-3.2-.7l-.6-1.2-2.4-1.7c-.7-.5.3-.5.3-.5l3.6 1.5z"/>
<path fill="#4d2a15" d="M314 189.9s-1.2-1.8-4.9-3.3l1.2-.2h-2l-.8-.3c-2-.6-2-.6-2.2-.8q-.2-.3-.6.3l-.7 1a5 5 0 0 1 2.5-.1c1.1.3 1 .7 1 1-.1.5-1.4 2-1.3 2.1.2 0 1-1.5 2-1.3q1.3.6 1.2.8c-.1.2 0-.6-.2-.8s-1.3-1.3-1-1.3q.6 0 3.3 1.2c1.8.9 2 1.1 2.6 1.7z"/>
<path fill="#dbad6c" d="M308.1 187s2.8.8 2.7 3.8-2.3 2.4-2.5 1.3.3.5 1 .4q1.2-.3 1-2.2 0-1.4-1-2.2l-1.2-1z"/>
<path fill="#6c3f18" d="M296 179.9s.4.2-.4 2.3-1.3 2.8-2.5 4.2c-2.2 2.4-3.2 2.5-3 3.5s1.1.8 1.3.8 4.3-6 4.3-6l.9-3.5-.2-1-.3-.3z"/>
<path fill="#dbad6c" d="M306.1 175.6s1.2 1.8.8 5c-.3 3.4-4.7 6.3-4.7 6.3l-6.4 4.4-2.7-.2h-.8q-.7-.3-.9-.5l-.5-1.2.9-1 2.3-2.3 1.4-2s.7-1.3.8-2.5q-.1-1.7-.2-1.7l.5.8.3 1.8-.2 2 1.6-1.2 2.2-1s.7 0 1-1.3c.5-1.2.9-3 .9-3.8l-.1-1.8h.3c.2 0 .7 1.2.7 1.7l.4 2.8 1-1 1-1.5q.3-.9.4-1.8"/>
<path fill="#904720" d="M306.8 192.2s.5.4.3 1.6-1.1 1.8-1.6 1.8V194l-1.4-1.2-1.5-.4.3-.3c.1-.2 1.7-.8 1.7-.8z"/>
<path fill="#ab6d29" d="M305.7 192.2s.5.4 1.2.3 1-1.3.6-1.8-.2 1-.7 1.3c-.4.1-1-1.2-1.5-1.3-.7-.1-1.6.3-1.9.7s1 0 1 0l.7.3h.5z"/>
<path fill="#904720" d="M296.5 187.6s.8.3 1.7.1l3.1-1 1.8-1.3c.7-.7 3.1-2.2 3.4-5.1.2-3-.4-4.7-.4-4.7s3.5 3.8.5 8.2c-2 2.8-3.8 3.7-3.8 3.7s3.4-1 3.7-.2-.1 2-.2 2.3c0 0 1.9.4 2.2 1.4.1.6-2.2-.7-4-.2s-2.5 1.4-2.5 1.4-.4-.5-2-.5c-1.5 0-2.2.7-3.1.7-1 0-3.7-.6-4.2-1.4l1.8-2.2c.8-1.6 2-1.2 2-1.2"/>
<path fill="#ab6d29" d="M303.8 179.7s1.3-1.3 1.8-2.3q.5-1.6.5-1.8c0-.2.2 1-.6 2.3s-1.7 2-1.7 2z"/>
<path fill="#904720" d="M303.4 176.7s1.1 4.4 0 6.1c-1.3 1.8-6.5 4.1-6.5 4.1s4-2.1 5.2-4.6 1.1-4.5 1.1-4.5z"/>
<path fill="#1e2121" d="M298.2 187.7s1.7 0 2.6-.6 2.3-1.7 2.3-1.7-1.2 1-1.6 1q-.6-.1-.5-.9s-.3.8-1 1.4c-.8.5-1.8.8-1.8.8m3.9-5.4s1.4-1.4 1.4-4.4-1.2-2.5-1.2-2.5 1 .7.7 3c-.2 2.3-1 4-1 4zm1.7.3s1.6-.3 2.1-1.3.6-2.7.6-2.7-.2 1.7-.7 2.3-2 1.7-2 1.7m-1 9.5s.8-1.5 1.9-1.1 1 1.2 1 1.2-.4-.6-1.2-.7-1.6.6-1.6.6zm-2.9-2.7 3.7-1 3-1s-1.2 1.3-3 2.3c-1.6 1-3 1-3 1s3.2-1.4 3.4-2c0 0-3 .9-4 .7z"/>
<path fill="#fff" d="M296.2 185.8a2.6 2.7 67.8 0 1-3 4.4"/>
<path fill="#f16e16" d="M296.1 186.1a2.3 2.4 67.8 0 1-2.7 3.8"/>
<path d="M295 187.4a1 1 0 1 1-.8 1.4"/>
<path fill="#d5d3ca" d="M295.2 188.6a.3.3 0 1 1-.6-.1l.3-.3a.3.3 0 0 1 .3.4"/>
<path fill="#ab6d29" d="M296.8 184.2s1.6-1.5 1.9-3.5q.3-3.1.1-3.2l.9 1 .3 1.4-.5 2.6 1.6-.8c.6-.8 1.5-4.2 1.2-6.3 0 0 .4 1-.2 3.8-.6 2.9-1 3.2-3.2 4.4a10 10 0 0 0-3.8 4.2l-2.3 3 1.6-2.6c.8-1.8 1.7-2.8 1.7-2.8z"/>
<path fill="#904720" d="M296.1 185.4s.7-2 .7-3.1-.7-2.4-.7-2.4 1-.1 1 2.2c.1 2.4-.4 2.6-1 3.3"/>
<path fill="#4d2a15" d="M299.2 182.8s.7-1.7.7-3c0-1.1-1-2.3-1-2.3s1.1.3 1.2 2.4c.1 2.2-.2 2.2-1 3z"/>
<path fill="#ab6d29" d="M292.1 191s-.6-.8-.5-1.3 0-.7 1.4-2.2a13 13 0 0 0 2.5-3c.3-.9 1.5-3 .6-4.6 0 0 .6 2.4-.8 4.3a16 16 0 0 1-3.3 3.7q-1.5 1-1.2 1.7 0 .9.3 1c.4.3 1 .3 1 .3z"/>
<path fill="#6c4119" d="M305.5 195.6s.3 0 .5-.3.2-1-.3-1.6a3 3 0 0 0-1.7-1.1l-2-.4s.4.8 1.3 1.3c.4.2 1.2 0 1.6.4s.6 1.7.6 1.7"/>
<path fill="#6c4119" d="M305.5 195.6s1.5-.5 1.6-2l-.1-1.2s.2.5.8.7l.6-.2s-1 2.8-3 2.7z"/>
<path fill="#bf802d" d="m294.8 195.3.8.8.9 1 3.7-1 1.2-1.2-1-1.6h-2.7l-3 2z"/>
<path fill="#f9c83a" stroke="#8f4620" stroke-width=".4" d="m303.8 194-.7-.9-.7-.6c-.6-.3-2.8-.3-2.8-.3l-1.6.3s-.5.5-1.3.3l-2.6-1-1.9-.7s-1.3-.6-1.6-.5-1.5 1.3-1.5 1.3-.2.7.3.7-.7.2-.7.2-2.8 1.6-3.2 4.4c-.5 2.7 4.6 6.8 6.5 4.7 0 0-2.8-2-2.2-3.7q.7-2.6 4-3c2.1 0 2.3-.3 3.2-1s2.7-1 4 .3-5 2.7-5 2.7l.7 1s7.7-2.7 7-4.1z"/>
<path fill="#fcf3d8" d="M289.4 200.7s-3.1-.8-2.9-3.7c.3-2.8 3-3.9 3.4-4s.3-1.6.8-1.8a2 2 0 0 1 2 .5l1.4.8s-5.5 2.4-5.5 5.6c0 2.1.8 2.5.8 2.5z"/>
<path fill="#fdeaaf" d="M303.8 193.8s-.4.2-.5 0c-.6-1-2-1.4-3-1.3-1.5 0-2.4.5-3.4.5s-.7-.1-1.8-.2c-1.1 0-3.5-1.8-4-1.6q-.9.5-.8 1.1c.1.4-1 .4-1 .1-.2-.2.7-1.7 1.7-1.7 2.6 0 4.6 1.7 5.8 1.7s1.7-.7 3.4-.7 3.5.8 3.6 2z"/>
<path fill="#513625" d="M295.2 195.8s1.3-.2 2.2-.6l2.2-.8-2 1.2-2 .5z"/>
<path fill="#f9c83a" d="M290.8 202.3c-.2 0-1.3 0-3-1.1-1.8-1.2-2.3-3.2-2.3-3.2s-.4-1.9 1.4-3.8 2.1-1.3 2.2-1.2q0 0 0 .1l-1.7 1.3-.7 1.3-.7 1.2v1.4l.8 1.5 2.5 1.5z"/>
<path fill="#8b5122" d="M289.8 191.4v-.2.3-.3.3-.3l-.3.2h.2v-.2l-.2.2.2-.2h-.2v.2l.2-.2h-.2.2-.2.2-.2.2l-.2-.1.2.1-.2-.1-.1.1-.4.5-.2.6v.1q0 .5.4.6h1.4l.1-.4-.3-.2-.7.1h-.3v-.4l.3-.5.2-.1zl.1-.2-.3-.2-.2.3.3.2z"/>
<path fill="#f9c83a" d="m295.1 194.1-3.8 1.1c-.3.2 1.2 0 2.4 0l1.3.1.9-.2c1.4-.5 4.4-1.6 4.7-.3.2 1-4.1 2-4.1 2v.5l3.4-.9 1.9-1 .5-.9-1.7-1.2H298l-1.2.3z"/>
<path fill="#8f4620" d="M289.7 199.5c.3.8.9 2 1.6 2.3 0 .1-.5.5-1.7.1-1.2-.3-3-.7-4.1-4v.7l.7 1.3 1.1 1.2 1.9 1.1 1.2.3 1-.2.6-.4-1.1-1-1.4-2.3z"/>
<path fill="#fcca3d" d="M302.1 232.6v1.2h-.7v-1.5zm-4.7-34.4-1.2-1.4-.8.1 1.2 1.8z"/>
<path fill="#816c2a" d="m302 235.5-1.7-1-2.4-.6v.3h.2l2.1.6 1.7 1zm0-3.1h-3l-1.3.7.1.2a4 4 0 0 1 2.6-.7l1.4.1v-.3zm-.4-2.7-2.6 1.4-1.6 1.2.2.2.5-.4 2.6-1.6 1-.5zm-1.7-2.7s0 .7-.3 1.2l-.6.8-2.5 2.6.2.3 2.5-2.7.7-.8q.4-1 .3-1.4zm-2.1-1.2a4 4 0 0 1-.2 2.2l-1 2.2-.4.9-.2.3.2.2s1.3-2.2 1.7-3.5q.3-.7.2-1.5v-.9zm-2.1-.3v1.2q0 1-.2 1.7l-.7 1.8-.3 1 .3.1 1-2.8q.3-.8.2-1.8v-1.2zm-1.6 0v1.2l-.3 2-.5 2.5h.3l.2-.8.3-1.7c.3-1 .3-3.2.3-3.2zm-1.8.3v2.9l-.4 2.5h.3l.4-2.4.1-1.4v-1.6zm-1.3.2v.2l-.3 2.2-.6 1.9-.2 1h.3l.8-2.9.3-2.2v-.2zm-1.6 0v.7q.1 1-.4 2l-1.3 1.6-.7.4-.2.2.1.3s1.7-1 2.4-2.3q.5-1.2.5-2.2v-.7zm-.9-.3v.2l-1 1.9q-.6.6-1.3 1.1l-.8.5.2.3s1.4-.8 2.1-1.7l1.1-2.2h-.3zm-1.5-.7v.1l-1 1.3q-.2.3-.9.6l-.6.4.1.3.7-.4 1-.7.7-.9.3-.6zm-.7-1-.3.3-.9 1-.5.4-.4.3.1.3.7-.4c.7-.6 1.5-1.7 1.5-1.7zm-.4-.6-.2.2-.9.6-.8.3v.4l.6-.3c.7-.3 1.5-1 1.5-1zm-.2-.7-.8.4-.6.3-.4.2v.3q.3 0 .5-.2l1.4-.7zm-.4-1.3-.5.5-.9.3v.3a2 2 0 0 0 1.2-.5l.5-.4zm.1-2.2-.6.5q-.5.4-.7 1h-.1v.5l.2-.2.2-.2q.1-.4.7-.9l.5-.5zm10.7-12.8h1.6l.1-.2v-.1h-.1v.3-.1h-.2.1-.1v-.2l.1.1v-.1.1-.1h-1.5zm0-1h.2q.3 0 .6-.2.6-.2 1-.6l.5-.3h.3v-.4l-.6.2-1 .7-.8.3zm0-1.5.7-.2 1.2-.8q.6-.4 1-.4v-.3q-.4 0-.8.2l-1.2.7-1 .5v.3zm-.6-1.4 1.5-1 1.6-1.2-.1-.3-1.7 1.2q-.8.8-1.5 1l.2.4zm-1-1.5 3.2-2.3-.2-.3-3.2 2.3zm-1.5-1.6a21 21 0 0 1 3.4-2.3l-.2-.2-1.5 1-1.9 1.3zm-1-1 2.6-1.8.8-.5-.1-.3-.8.5-2.7 2z"/>
<path fill="#78732e" d="m294.5 195.4.1.1 2.5 2.5q1.6 1.9 1.8 4.1v.5a10 10 0 0 1-2.5 5c-2 2.3-4.8 4.4-7 6.4a18 18 0 0 0-3 3.1 6 6 0 0 0-1.2 3.7 6 6 0 0 0 1.1 3.4 4 4 0 0 0 3.9 2c1.8 0 3.8-.7 5.6-.7q2.4-.2 4.4 1.9c1.3 1.4 1.7 3.6 1.7 5.7q0 2.4-.4 4.2l-.4 1.8h.3s.8-2.8.8-6c0-2.1-.4-4.5-1.8-6a6 6 0 0 0-4.6-1.9c-1.9 0-3.9.6-5.6.6a4 4 0 0 1-3.6-1.8 6 6 0 0 1-1-3.3v-.1q0-2.4 1.9-4.3c1.8-2 4.5-4 7-6.2 2.3-2.2 4.4-4.5 4.7-7.5v-.5c0-1.8-1.1-3.5-2.3-4.8l-2.3-2.2z"/>
<path fill="#a8ac71" d="m292.1 198.4-.4-.3-.3.1.6.5zm1 1-.3-.3h-.1l.2.4z"/>
<path fill="#78732e" d="m294.5 201-.2-.4-.2.2zm1 1.5-.1-.3h-.3l.4.5zm.7 1.5-.2-.4v.4zm0 1.5v-.4l-.2-.1v.5zm0 1v-.5l-.1-.1v.6zM284 221.1l.1-.7c.1-.3-.3.1-.3.1v.5z"/>
<path fill="#fff" d="m284 222.1-.3.1.1.5.1-.2z"/>
<path fill="#78732e" d="M284 223.9v-.4h-.2v.6z"/>
<path fill="#fff" d="M284 224.9v-.4h-.1v.4zm.3 1.4v-.4l-.2.1zm.4 1.4-.1-.5h-.2l.2.6z"/>
<path fill="#a8ac71" d="m285.6 229.7-.2-.3h-.2zm1.5 1.4-.5-.3c-.3-.3-.1.2-.1.2l.4.2zm9.8.8-.4-.3q-.2-.1.2.4c.1.3.2-.1.2-.1"/>
<path fill="#fff" d="m297.7 232.5-.3-.3v.3h.2z"/>
<path fill="#a8ac71" d="M297.9 233q-.2-.1-.2.1l.2.3zm0 1.3v-.5q0-.1-.1 0v.5z"/>
<path fill="#fff" d="m297.4 235.7.2-.4h-.2zm-14.8-52s.5-.2.1.4l.3-.5h-.5z"/>
<path fill="#f9c83a" d="m294.6 195 .4.3h-.5z"/>
<path fill="#8f4620" d="m295 195.3-.7.1c-.2 0 .2-.2.2-.2l.4.1z"/>
<path fill="#977c2e" d="M301 239.1s.3 0 .5-.3-.1.4-.1.4l-.3.1-.2-.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-mz" viewBox="0 0 640 480">
<defs>
<clipPath id="mz-a">
<path fill-opacity=".7" d="M0 0h682.7v512H0z"/>
</clipPath>
</defs>
<g clip-path="url(#mz-a)" transform="scale(.9375)">
<path fill="#009a00" fill-rule="evenodd" d="M0 0h768v160H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 160h768v16H0z"/>
<path fill="#000001" fill-rule="evenodd" d="M0 176h768v160H0z"/>
<path fill="#fff" fill-rule="evenodd" d="M0 336h768v16H0z"/>
<path fill="#ffca00" fill-rule="evenodd" d="M0 352h768v160H0z"/>
<path fill="red" fill-rule="evenodd" d="M0 0v512l336-256z"/>
<path fill="#ffca00" fill-rule="evenodd" d="m198.5 333-51.2-37.5L96.1 333l19.9-60.3-51.5-37.1 63.5.2 19.3-60.4 19.4 60.5 63.5-.3-51.5 37.1z"/>
<path fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M102.8 290.9h37c3 3.3 9.5 4.7 15.8 0 11.6-6.4 34 0 34 0l4.4-4.7-10.7-35.2-3.9-4.2s-8.3-5-24-3.3-21.2-.5-21.2-.5-13.7 1.6-17.6 3.6l-4.4 4.4z"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M110.3 281.8s35.2-4.4 45.4 9.1c-5.7 4-10.8 4.3-16.2.3.8-1.5 12.6-13.8 42.7-9.7"/>
<path fill="none" stroke="#000" stroke-width="1.2" d="m148 246.6-.3 38.8m31.7-38.3L186 278"/>
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="m117 246.6-3.7 16"/>
<path fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="m78.9 295.1 8.6 10.2q1.5.9 2.9 0l12.8-15.4 5.4-6.7q1.3-1.6 1-3l10.4-9.3 2.2.2c-1-.2-1.7-.7-1-1.8l2.4-1.8 1.8 2.3s-2.6 3.4-2.9 3.4h-2.8l-5.4 4.9 2.4 2 3.5 9.8 4.4-3.1-2.8-10 6.1-6.7-2.3-3.6 1.6-2s21.3 13.4 29.6 9.8c.2 0 .5-9.6.5-9.6s-22.2-2.3-22.7-6.7 5-5 5-5l-2.4-3.2.5-1.8 3.9 4.8 8.7-7.4 51.5 58.6c2.8-1.1 3.4-1.8 3.6-4.6L155 241.5l3.8-4.1c.8-.9 1-1.2 1-2.6l6-5.1a7 7 0 0 1 3.8 3L186 219c.4.4 1.7.8 2.6.4l26.9-25.9-29.3 20.7-1-.7c0-.9 1-1 0-2.6-1.2-1.4-2.9 1.3-3.1 1.3s-4.3-1.4-5.2-3.2l-.2 4.7-7.5 7-5.7-.3-8.2 8-1 3 1.3 2.7s-4.4 3.8-4.4 3.6c0-.3-.9-1.2-1-1.3l3.8-3.4.5-2.3-1.2-2c-.4.3-5.2 5.4-5.5 4.8l-14-15.5.8-2.9-8.7-9.5c-3.2-1.1-8.3-1.3-9.3 5.7-.8 1.6-7.4.2-7.4.2l-3.6.8L85.2 241l11.3 13.6 23.2-29.3.7-8.3 4.8 5.4q2.5.5 4.7-.5l13.7 15.3-2.3 2.3 2 2.2 2.4-1.6.9 1.3q-1.6 1-3.1 2.1c-1.8-1.2-3.6-2.7-3.5-5l-7.7 6.4-.3 1.2-22.9 19-2 .3-.5 6 14.9-12.4v-1.8l1.5 1.3 11.6-9.3s.8 1 .5 1-10.3 9.3-10.3 9.3l-.2 1-1.8 1.6-1-.8-14 12.4h-2l-7.7 7.7c-2 .2-3.7.4-5.4 1.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ng" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#008753" d="M426.6 0H640v480H426.6zM0 0h213.3v480H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-nl" viewBox="0 0 640 480">
<path fill="#ae1c28" d="M0 0h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#21468b" d="M0 320h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-np" viewBox="0 0 640 480">
<defs>
<clipPath id="np-a">
<path fill-opacity=".7" d="M0-16h512v512H0z"/>
</clipPath>
</defs>
<g clip-path="url(#np-a)" transform="translate(0 15)scale(.9375)">
<g fill-rule="evenodd">
<path fill="#ce0000" stroke="#000063" stroke-width="13.8" d="M6.5 489.5h378.8L137.4 238.1l257.3.3L6.6-9.5v499z"/>
<path fill="#fff" d="m180.7 355.8-27 9 21.2 19.8-28.5-1.8 11.7 26.2-25.5-12.3.5 28.6-18.8-20.9-10.7 26.6-9.2-26.3-20.3 20.6 1.8-27.7L49 409l12.6-25-29.3.6 21.5-18.3-27.3-10.5 27-9L32.2 327l28.4 1.8L49 302.6l25.6 12.3-.5-28.6 18.8 20.9 10.7-26.6 9.1 26.3 20.4-20.6-1.9 27.7 27-11.4-12.7 25 29.4-.6-21.5 18.3zm-32.4-184.7-11.3 8.4 5.6 4.6a94 94 0 0 0 30.7-36c1.8 21.3-17.7 69-68.7 69.5a70.6 70.6 0 0 1-71.5-70.3c10 18.2 16.2 27 32 36.5l4.7-4.4-10.6-8.9 13.7-3.6-7.4-12.4 14.4 1-1.8-14.4 12.6 7.4 4-13.5 9 10.8 8.5-10.3 4.6 14 11.8-8.2-1.5 14.3 14.2-1.7-6.7 13.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1005 B

Some files were not shown because too many files have changed in this diff Show More