Compare commits

...

11 Commits

Author SHA1 Message Date
Disco DeDisco
f1e9a9657b fixed SRG5-8 channels FTs: multi-browser sig-card OK flow now uses execute_async_script to iterate cards until a non-conflicting reserve succeeds (bypasses ElementNotInteractableException + 409 same-card conflicts); added wait_for_slow for 12s countdown in SRG8; added browser=None param to ChannelsFunctionalTest.wait_for/wait_for_slow
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 02:31:00 -04:00
Disco DeDisco
32d8d97360 wired PICK SKY server-side polarity countdown via threading.Timer (tasks.py); fixed polarity_done overlay gating on refresh; cleared sig-select floats on overlay dismiss; filtered Redact events from Most Recent applet
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 00:34:05 -04:00
Disco DeDisco
df421fb6c0 added PICK SKY ready gate: SigReservation.ready + countdown_remaining fields, Room.SKY_SELECT status + sig_select_started_at, sig_ready + sig_confirm views, WS notifiers for countdown_start/cancel/polarity_room_done/pick_sky_available, migration 0031, PICK SKY btn in hex center at SKY_SELECT, tray cell 2 sig card placeholder; FTs SRG1-8 written (pending JS/consumer)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 01:17:24 -04:00
Disco DeDisco
3800c5bdad fixed attribution of .fa-hand-pointer cursor color scheme to ordering according to token-drop sequence instead of seat sequence; updates to accomodate this throughout apps.epic.models & .views, plus new apps.epic migration; assigned #id_sig_cursor_portal a z-index value corresponding to a high position but still beneath the #id_tray apparatus; minor semantic reordering of INSTALLED_APPS in core.settings
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-08 22:53:44 -04:00
Disco DeDisco
12d575a84b fixed seeding problem w. setUp helper causing same FTs to persistently fail
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-08 13:34:22 -04:00
Disco DeDisco
c14b6d7062 fixed some old data in two pipeline errors pointing to new Middle Arcana labels still as Minor Arcana
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-08 12:51:46 -04:00
Disco DeDisco
a7c5468cbc fixed failing channels FT related to Sig select; FT fix only, code written as intended
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-08 12:18:20 -04:00
Disco DeDisco
4da8750c60 fixed tooltip illegibility due to similar color to bg on .sig-overlay when data-polarity='gravity'
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-08 11:57:44 -04:00
Disco DeDisco
cf40f626e6 Sig select: _card-deck.scss extract, WS cursor fixes, own-role indicators, role icon refresh
- New _card-deck.scss: sig select styles moved out of _room.scss + _game-kit.scss
- sig-select.js: 3 WS bug fixes — thumbs-up deferred to window.load (layout settled
  before getBoundingClientRect), hover cursor cleared for all cards on reservation
  (not just the reserved card), applyHover guards against already-reserved roles
- Own-role indicators: gamer now sees their own role-coloured card outline + thumbs-up
- Reservation glow: replaced blurry role+ninUser double-shadow with crisp 2px outline
- Gravity qualifier: Graven text set to --terUser (matches Leavened/--quiUser pattern)
- Role card SVGs refreshed; starter-role-Blank removed
- FTs + Jasmine specs extended for sig select WS behaviour
- setup_sig_session management command for multi-browser manual testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 11:52:49 -04:00
Disco DeDisco
99a826f6c9 FT: pin AppletMenuDismissTest to portrait viewport (800×1200)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Landscape layout activates sidebar CSS which causes #id_dash_content to
overlap the base-template h2 in CI headless Firefox, triggering
ElementClickInterceptedException. Portrait viewport sidesteps all
landscape breakpoints so the h2 sits safely above #id_dash_content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 01:26:35 -04:00
Disco DeDisco
51fe2614fa overruling other scss specificity in .btn-disabled 2026-04-07 00:43:26 -04:00
44 changed files with 3708 additions and 947 deletions

View File

@@ -97,6 +97,19 @@ python src/manage.py test src/functional_tests
python src/manage.py test src python src/manage.py test src
``` ```
### Multi-user manual testing — `setup_sig_session`
`src/functional_tests/management/commands/setup_sig_session.py`
Creates (or reuses) a room with all 6 gate slots filled, roles assigned, and `table_status=SIG_SELECT`. Prints one pre-auth URL per gamer for pasting into 6 Firefox Multi-Account Container tabs.
```bash
python src/manage.py setup_sig_session
python src/manage.py setup_sig_session --base-url http://localhost:8000
python src/manage.py setup_sig_session --room <uuid> # reuse existing room
```
Fixed gamers: `founder@test.io` (discoman), `amigo@test.io`, `bud@test.io`, `pal@test.io`, `dude@test.io`, `bro@test.io` — all created as superusers with Earthman deck equipped. URLs use `/lyric/dev-login/<session_key>/` pre-auth pattern.
**Test tags:** The only tag in use is `channels` — for async consumer tests that require a live Redis channel layer. **Test tags:** The only tag in use is `channels` — for async consumer tests that require a live Redis channel layer.
- Exclude channels tests: `python src/manage.py test src/apps --exclude-tag=channels` - Exclude channels tests: `python src/manage.py test src/apps --exclude-tag=channels`
- Channels tests only: `python src/manage.py test src/apps --tag=channels` - Channels tests only: `python src/manage.py test src/apps --tag=channels`

View File

@@ -27,7 +27,13 @@ def billboard(request):
.first() .first()
) )
recent_events = ( recent_events = (
list(recent_room.events.select_related("actor").order_by("-timestamp")[:36])[::-1] list(
recent_room.events
.select_related("actor")
.exclude(verb=GameEvent.SIG_UNREADY)
.exclude(verb=GameEvent.SIG_READY, data__retracted=True)
.order_by("-timestamp")[:36]
)[::-1]
if recent_room else [] if recent_room else []
) )

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0 on 2026-04-12 23:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('drama', '0002_scrollposition'),
]
operations = [
migrations.AlterField(
model_name='gameevent',
name='verb',
field=models.CharField(choices=[('room_created', 'Room created'), ('slot_reserved', 'Gate slot reserved'), ('slot_filled', 'Gate slot filled'), ('slot_returned', 'Gate slot returned'), ('slot_released', 'Gate slot released'), ('invite_sent', 'Invite sent'), ('role_select_started', 'Role select started'), ('role_selected', 'Role selected'), ('roles_revealed', 'Roles revealed'), ('sig_ready', 'Sig claim staked'), ('sig_unready', 'Sig claim withdrawn')], max_length=30),
),
]

View File

@@ -1,6 +1,12 @@
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
# ── Default gender-neutral pronouns (Baltimore original) ──────────────────────
# Later: replace with per-actor lookup when User model gains a pronouns field.
PRONOUN_SUBJ = "yo"
PRONOUN_OBJ = "yo"
PRONOUN_POSS = "yos"
class GameEvent(models.Model): class GameEvent(models.Model):
# Gate phase # Gate phase
@@ -14,6 +20,9 @@ class GameEvent(models.Model):
ROLE_SELECT_STARTED = "role_select_started" ROLE_SELECT_STARTED = "role_select_started"
ROLE_SELECTED = "role_selected" ROLE_SELECTED = "role_selected"
ROLES_REVEALED = "roles_revealed" ROLES_REVEALED = "roles_revealed"
# Sig Select phase
SIG_READY = "sig_ready"
SIG_UNREADY = "sig_unready"
VERB_CHOICES = [ VERB_CHOICES = [
(ROOM_CREATED, "Room created"), (ROOM_CREATED, "Room created"),
@@ -25,6 +34,8 @@ class GameEvent(models.Model):
(ROLE_SELECT_STARTED, "Role select started"), (ROLE_SELECT_STARTED, "Role select started"),
(ROLE_SELECTED, "Role selected"), (ROLE_SELECTED, "Role selected"),
(ROLES_REVEALED, "Roles revealed"), (ROLES_REVEALED, "Roles revealed"),
(SIG_READY, "Sig claim staked"),
(SIG_UNREADY, "Sig claim withdrawn"),
] ]
room = models.ForeignKey( room = models.ForeignKey(
@@ -71,13 +82,36 @@ class GameEvent(models.Model):
"PC": "Player", "BC": "Builder", "SC": "Shepherd", "PC": "Player", "BC": "Builder", "SC": "Shepherd",
"AC": "Alchemist", "NC": "Narrator", "EC": "Economist", "AC": "Alchemist", "NC": "Narrator", "EC": "Economist",
} }
_chair_order = ["PC", "NC", "EC", "SC", "AC", "BC"]
_ordinals = ["1st", "2nd", "3rd", "4th", "5th", "6th"]
code = d.get("role", "?") code = d.get("role", "?")
role = d.get("role_display") or _role_names.get(code, code) role = d.get("role_display") or _role_names.get(code, code)
return f"elects to start as the {role}, and will enjoy affinity with this Role for the remainder of the game." try:
ordinal = _ordinals[_chair_order.index(code)]
except ValueError:
ordinal = "?"
return f"assumes {ordinal} Chair; yo will start the game as the {role}."
if self.verb == self.ROLES_REVEALED: if self.verb == self.ROLES_REVEALED:
return "All roles assigned" return "All roles assigned"
if self.verb == self.SIG_READY:
card_name = d.get("card_name", "a card")
corner_rank = d.get("corner_rank", "")
suit_icon = d.get("suit_icon", "")
if corner_rank:
icon_html = f' <i class="fa-solid {suit_icon}"></i>' if suit_icon else ""
abbrev = f" ({corner_rank}{icon_html})"
else:
abbrev = ""
return f"embodies as {PRONOUN_POSS} Significator the {card_name}{abbrev}."
if self.verb == self.SIG_UNREADY:
return f"disembodies {PRONOUN_POSS} Significator."
return self.verb return self.verb
@property
def struck(self):
"""True when this SIG_READY event was subsequently retracted (WAIT NVM)."""
return self.data.get("retracted", False)
def to_activity(self, base_url): def to_activity(self, base_url):
"""Serialise this event as an AS2 Activity dict, or None if unsupported.""" """Serialise this event as an AS2 Activity dict, or None if unsupported."""
if not self.actor or not self.actor.username: if not self.actor or not self.actor.username:

View File

@@ -40,6 +40,49 @@ class GameEventModelTest(TestCase):
self.assertIn("actor@test.io", str(event)) self.assertIn("actor@test.io", str(event))
self.assertIn(GameEvent.ROLE_SELECTED, str(event)) self.assertIn(GameEvent.ROLE_SELECTED, str(event))
# ── to_prose — ROLE_SELECTED ──────────────────────────────────────────
def test_role_selected_prose_uses_ordinal_chair(self):
for role, ordinal in [("PC", "1st"), ("NC", "2nd"), ("EC", "3rd"),
("SC", "4th"), ("AC", "5th"), ("BC", "6th")]:
with self.subTest(role=role):
event = record(self.room, GameEvent.ROLE_SELECTED, actor=self.user,
role=role, role_display="")
self.assertIn(f"assumes {ordinal} Chair", event.to_prose())
def test_role_selected_prose_includes_role_name(self):
event = record(self.room, GameEvent.ROLE_SELECTED, actor=self.user,
role="PC", role_display="Player")
prose = event.to_prose()
self.assertIn("Player", prose)
self.assertIn("yo will start the game", prose)
# ── to_prose — SIG_READY ─────────────────────────────────────────────
def test_sig_ready_prose_embodies_card_with_rank_and_icon(self):
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
card_name="Maid of Brands", corner_rank="M",
suit_icon="fa-wand-sparkles")
prose = event.to_prose()
self.assertIn("embodies as yos Significator the Maid of Brands", prose)
self.assertIn("(M", prose)
self.assertIn("fa-wand-sparkles", prose)
def test_sig_ready_prose_omits_icon_when_none(self):
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
card_name="The Wanderer", corner_rank="0", suit_icon="")
prose = event.to_prose()
self.assertIn("embodies as yos Significator the The Wanderer (0)", prose)
self.assertNotIn("fa-", prose)
def test_sig_ready_prose_degrades_without_corner_rank(self):
# Old events recorded before this change have no corner_rank key
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
card_name="Maid of Brands")
prose = event.to_prose()
self.assertIn("embodies as yos Significator the Maid of Brands", prose)
self.assertNotIn("(", prose)
def test_str_without_actor_shows_system(self): def test_str_without_actor_shows_system(self):
event = record(self.room, GameEvent.ROLES_REVEALED) event = record(self.room, GameEvent.ROLES_REVEALED)
self.assertIn("system", str(event)) self.assertIn("system", str(event))

View File

@@ -78,5 +78,17 @@ class RoomConsumer(AsyncJsonWebsocketConsumer):
async def sig_reserved(self, event): async def sig_reserved(self, event):
await self.send_json(event) await self.send_json(event)
async def countdown_start(self, event):
await self.send_json(event)
async def countdown_cancel(self, event):
await self.send_json(event)
async def polarity_room_done(self, event):
await self.send_json(event)
async def pick_sky_available(self, event):
await self.send_json(event)
async def cursor_move(self, event): async def cursor_move(self, event):
await self.send_json(event) await self.send_json(event)

View File

@@ -0,0 +1,23 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('epic', '0029_fix_schizo_cautions'),
]
operations = [
migrations.AddField(
model_name='sigreservation',
name='seat',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='sig_reservation',
to='epic.tableseat',
),
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 6.0 on 2026-04-09 04:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('epic', '0030_sigreservation_seat_fk'),
]
operations = [
migrations.AddField(
model_name='room',
name='sig_select_started_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='sigreservation',
name='countdown_remaining',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='sigreservation',
name='ready',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='room',
name='table_status',
field=models.CharField(blank=True, choices=[('ROLE_SELECT', 'Role Select'), ('SIG_SELECT', 'Significator Select'), ('SKY_SELECT', 'Sky Select'), ('IN_GAME', 'In Game')], max_length=20, null=True),
),
]

View File

@@ -33,10 +33,12 @@ class Room(models.Model):
ROLE_SELECT = "ROLE_SELECT" ROLE_SELECT = "ROLE_SELECT"
SIG_SELECT = "SIG_SELECT" SIG_SELECT = "SIG_SELECT"
SKY_SELECT = "SKY_SELECT"
IN_GAME = "IN_GAME" IN_GAME = "IN_GAME"
TABLE_STATUS_CHOICES = [ TABLE_STATUS_CHOICES = [
(ROLE_SELECT, "Role Select"), (ROLE_SELECT, "Role Select"),
(SIG_SELECT, "Significator Select"), (SIG_SELECT, "Significator Select"),
(SKY_SELECT, "Sky Select"),
(IN_GAME, "In Game"), (IN_GAME, "In Game"),
] ]
@@ -50,6 +52,7 @@ class Room(models.Model):
table_status = models.CharField( table_status = models.CharField(
max_length=20, choices=TABLE_STATUS_CHOICES, null=True, blank=True max_length=20, choices=TABLE_STATUS_CHOICES, null=True, blank=True
) )
sig_select_started_at = models.DateTimeField(null=True, blank=True)
renewal_period = models.DurationField(null=True, blank=True, default=timedelta(days=7)) renewal_period = models.DurationField(null=True, blank=True, default=timedelta(days=7))
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
board_state = models.JSONField(default=dict) board_state = models.JSONField(default=dict)
@@ -359,12 +362,18 @@ class SigReservation(models.Model):
gamer = models.ForeignKey( gamer = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='sig_reservations' settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='sig_reservations'
) )
seat = models.ForeignKey(
'TableSeat', null=True, blank=True,
on_delete=models.SET_NULL, related_name='sig_reservation',
)
card = models.ForeignKey( card = models.ForeignKey(
'TarotCard', on_delete=models.CASCADE, related_name='sig_reservations' 'TarotCard', on_delete=models.CASCADE, related_name='sig_reservations'
) )
role = models.CharField(max_length=2) role = models.CharField(max_length=2)
polarity = models.CharField(max_length=7, choices=POLARITY_CHOICES) polarity = models.CharField(max_length=7, choices=POLARITY_CHOICES)
reserved_at = models.DateTimeField(auto_now_add=True) reserved_at = models.DateTimeField(auto_now_add=True)
ready = models.BooleanField(default=False)
countdown_remaining = models.IntegerField(null=True, blank=True)
class Meta: class Meta:
constraints = [ constraints = [

View File

@@ -3,7 +3,7 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #cfa969; fill: #354a9c;
} }
.cls-2 { .cls-2 {
@@ -11,95 +11,55 @@
} }
.cls-3 { .cls-3 {
fill: #ddb86c; stroke-width: 2.75px;
} }
.cls-4 { .cls-3, .cls-4 {
fill: #e2c27a;
}
.cls-5 {
fill: #f0d89d;
}
.cls-6 {
fill: #c9ad93;
}
.cls-7 {
fill: #dfb86f;
}
.cls-8 {
fill: #c49d60;
}
.cls-9 {
fill: #c39c5d;
}
.cls-10 {
fill: #3d180d;
}
.cls-11 {
fill: #ceaa67;
}
.cls-12 {
stroke-width: 2.2px;
}
.cls-12, .cls-13 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
stroke-linejoin: round; stroke-linejoin: round;
} }
.cls-14 { .cls-5 {
fill: #e8c884; fill: #4f66d4;
} }
.cls-15 { .cls-6 {
fill: #4258b8;
}
.cls-7 {
fill: #3d180d;
}
.cls-8 {
fill: #3a1709; fill: #3a1709;
} }
.cls-13 { .cls-4 {
stroke-width: 2.75px; stroke-width: 2.2px;
}
.cls-16 {
fill: #f7e5b6;
}
.cls-17 {
fill: #d6bda4;
}
.cls-18 {
fill: #d0a967;
} }
</style> </style>
</defs> </defs>
<path class="cls-5" d="M185.31,203.37l.12.54,1.16,5.4-2.22.49c-.08,8.17-.62,15.76-1.54,24.08-.26,2.33.18,7.31,3.04,7.58,4.78.44,9.33-1.88,14.1-2.1,1.59-.07,3.44-.05,4.69-.32l.98,9.54c.24,2.25,2.12,2.5,3.74,3.16,1.99.81,2.31,3.55,3.46,5.16l3.75,5.23c.62.86,1.45,1.66,2.51,1.36-.12,2.28.15,4.61.11,7.14l-.44,1.49c-1.47-.84-2.37,1.63-3.55,1.77l-16.08,1.97-21.66,2.39c4.99,1.47,9.61,1.45,14.55,2.03l23.5,2.78c.22,1.66.49.84,1.47.31,1.01,6.14,2.22,12.45,2.53,19.3,1.2,4.17.5,8.59-3.88,10.35l-3.91,2.35c.65.86,1.46,1.29,1.81,2.27,2.77,1.35,3.4,4.57,3.09,7.75,2.44,5.7,2.42,11.51,1.87,17.57l-.91,9.96c-.14,1.55-1.69,1.51-2.77,1.41l-17.57-1.6c-4.26-.39-8.71.46-12.16-2.84l-5.53-7.04c-.25.2-.35.47-.36.58,0-.11.11-.38.36-.58-.25.2-.35.47-.36.58-.28,3.07-.81,5.92-2.85,8.97l-21.21,1.95c-.12-1.76-.26-3.48-1.01-5.15l-4.42-9.91-3.75-25.3c-.14-.97-.63-1.8-1.07-2.16.43-1.73-.03-4.16-.58-6.18-1.9-4.98-3.59-9.83-4.83-15.35.18-.51-1.33-1.69-.39-2.35l1.62-1.16c1-.71-.63-1.33-.17-2.07.4-.66,1.75-.46,1.97-1.2.33-1.08-.35-2.18-1.64-2.16l-4.2.07-9.67-35.31c-.2-.75-.95-.91-1.34-1.05-.54-.19-1.5,0-1.53.93-.02.66-.64,1.72-1.09,2.78-1.2-1.34-4.74-2.35-5.46-.22l-12.57,37.25-7.39,1.96-1.8.57c-.58.18-.63,2.12,0,2.14,1.81.06,3.73-1.13,5.06.44-1.62.37-4.59.86-3.54,2.77.86,1.55,3.62.48,4.95.31l-4.78,15.62c-.24.78.03,1.76.57,2.34.4.43,1.33.72,2.04-.13,1.08-1.29,1.25-3.2,1.82-4.45.97-.38,2.22.37.73.97-.13,1.21-.23,2.19.18,3.14.23.53.84.92,2.01.61,2.71-6.4,4.7-12.72,6.8-19.73l18.11-2.9,5.96,21.86c.44,1.61,1.47,1.6,2.91,1.32,1.12-.22.51-1.69.51-2.99v-1.31c0-.37.34-.39,1.2-.31-1.04,1.12-.03,2.08.5,3.48.36.96,1.5,1.25,2.74,1.23l-.5,20.05c-.09,3.58-.56,6.95-.22,10.34l-21.98.14-2.92-9.49c2.68-4.69,5.77-9.41,3.04-13.61l-6.91,1.61c-1.59.37.86,2.08.68,2.8l-1.98,7.57c-.27,1.01.11,2.26-.46,3.03-1.37,1.88-6.3,1.61-9.49.15-1.3-2.98,1.24-7.91.16-13.45-.44-2.25-3.52-1.29-5.21-1.15l-8.08.67-6.35-5.57c.37-4.82-6.34-6.88-5.95-12.46.07-.96,1.36-1.31,1.96-2.42l5.08-9.34c.18-1.24-1.67-1.79-2.25-2.13l-3.45-1.97c-1-.57-1.54-1.46-1.61-2.3-.11-1.23,3.32-3.74,6.12-4.08l23.77-2.92c-4.84-1.4-9.6-.46-14.47-.77l-20.58-1.25-1.75-17.45c-.27-2.73-1.52-5.99.31-8.64,1.48-2.15,4.9-2.45,7.04-3.85,3.01-1.97-4.02-5.01-3.67-6.6,1.58-7.04-3.55-9.14-2.92-17.11l1.35-16.91c19.69,3.11,39.13,3.08,58.78.65,2.37-.29,3.87.88,5.27,2.25l.56.54-.56-.54-2.23,1.33c3.23,4.36,4.37,8.99,5.14,13.85l2.71,17.1,3.13,17.22c.82-.18.95-1.1.85-1.6-.94-4.55-.24-9.14-.6-13.84-.32-4.1-.56-7.97.06-12.11.92-6.26,1.04-12.4-.43-18.5-.18-.76-1.4-.95-1.7-.74.48-1.08.49-3.87,2.16-4.43l15.4-1.26,24.35-2.21ZM187.63,257.75c.4.93.52,1.9,1.14,2.91,2.36,3.88,4.35-8.06-7.31-13.58-7.5-3.55-15.29-1.54-21.1,4.83-13.04,14.31-17.28,39.69-4.52,53.8,5.7,6.31,14.78,8.03,22.8,5.87,13.32-3.6,20.27-20.17,13.32-16.11-.83,1.08-1.92.91-1.28-.45.23-1.19-.5-2.25-1.19-2.44-2.65-.75-3.54,8.1-13.46,10.47-5.92,1.42-11.86-1.51-14.55-6.83-4.2-8.32-4.34-17.68-1.05-26.64,3.45-9.39,10.35-17.68,18.54-13.94,4.41,2.01,4.83,8.81,5.68,8.88s1.2-.15,1.64-.32c.27-.1,1.75-2.67.35-6.47.34.07.6.1.97.03Z"/> <path class="cls-5" d="M185.31,203.37l.12.54,1.16,5.4-2.22.49c-.08,8.17-.62,15.76-1.54,24.08-.26,2.33.18,7.31,3.04,7.58,4.78.44,9.33-1.88,14.1-2.1,1.59-.07,3.44-.05,4.69-.32l.98,9.54c.24,2.25,2.12,2.5,3.74,3.16,1.99.81,2.31,3.55,3.46,5.16l3.75,5.23c.62.86,1.45,1.66,2.51,1.36-.12,2.28.15,4.61.11,7.14l-.44,1.49c-1.47-.84-2.37,1.63-3.55,1.77l-16.08,1.97-21.66,2.39c4.99,1.47,9.61,1.45,14.55,2.03l23.5,2.78c.22,1.66.49.84,1.47.31,1.01,6.14,2.22,12.45,2.53,19.3,1.2,4.17.5,8.59-3.88,10.35l-3.91,2.35c.65.86,1.46,1.29,1.81,2.27,2.77,1.35,3.4,4.57,3.09,7.75,2.44,5.7,2.42,11.51,1.87,17.57l-.91,9.96c-.14,1.55-1.69,1.51-2.77,1.41l-17.57-1.6c-4.26-.39-8.71.46-12.16-2.84l-5.53-7.04c-.25.2-.35.47-.36.58,0-.11.11-.38.36-.58-.25.2-.35.47-.36.58-.28,3.07-.81,5.92-2.85,8.97l-21.21,1.95c-.12-1.76-.26-3.48-1.01-5.15l-4.42-9.91-3.75-25.3c-.14-.97-.63-1.8-1.07-2.16.43-1.73-.03-4.16-.58-6.18-1.9-4.98-3.59-9.83-4.83-15.35.18-.51-1.33-1.69-.39-2.35l1.62-1.16c1-.71-.63-1.33-.17-2.07.4-.66,1.75-.46,1.97-1.2.33-1.08-.35-2.18-1.64-2.16l-4.2.07-9.67-35.31c-.2-.75-.95-.91-1.34-1.05-.54-.19-1.5,0-1.53.93-.02.66-.64,1.72-1.09,2.78-1.2-1.34-4.74-2.35-5.46-.22l-12.57,37.25-7.39,1.96-1.8.57c-.58.18-.63,2.12,0,2.14,1.81.06,3.73-1.13,5.06.44-1.62.37-4.59.86-3.54,2.77.86,1.55,3.62.48,4.95.31l-4.78,15.62c-.24.78.03,1.76.57,2.34.4.43,1.33.72,2.04-.13,1.08-1.29,1.25-3.2,1.82-4.45.97-.38,2.22.37.73.97-.13,1.21-.23,2.19.18,3.14.23.53.84.92,2.01.61,2.71-6.4,4.7-12.72,6.8-19.73l18.11-2.9,5.96,21.86c.44,1.61,1.47,1.6,2.91,1.32,1.12-.22.51-1.69.51-2.99v-1.31c0-.37.34-.39,1.2-.31-1.04,1.12-.03,2.08.5,3.48.36.96,1.5,1.25,2.74,1.23l-.5,20.05c-.09,3.58-.56,6.95-.22,10.34l-21.98.14-2.92-9.49c2.68-4.69,5.77-9.41,3.04-13.61l-6.91,1.61c-1.59.37.86,2.08.68,2.8l-1.98,7.57c-.27,1.01.11,2.26-.46,3.03-1.37,1.88-6.3,1.61-9.49.15-1.3-2.98,1.24-7.91.16-13.45-.44-2.25-3.52-1.29-5.21-1.15l-8.08.67-6.35-5.57c.37-4.82-6.34-6.88-5.95-12.46.07-.96,1.36-1.31,1.96-2.42l5.08-9.34c.18-1.24-1.67-1.79-2.25-2.13l-3.45-1.97c-1-.57-1.54-1.46-1.61-2.3-.11-1.23,3.32-3.74,6.12-4.08l23.77-2.92c-4.84-1.4-9.6-.46-14.47-.77l-20.58-1.25-1.75-17.45c-.27-2.73-1.52-5.99.31-8.64,1.48-2.15,4.9-2.45,7.04-3.85,3.01-1.97-4.02-5.01-3.67-6.6,1.58-7.04-3.55-9.14-2.92-17.11l1.35-16.91c19.69,3.11,39.13,3.08,58.78.65,2.37-.29,3.87.88,5.27,2.25l.56.54-.56-.54-2.23,1.33c3.23,4.36,4.37,8.99,5.14,13.85l2.71,17.1,3.13,17.22c.82-.18.95-1.1.85-1.6-.94-4.55-.24-9.14-.6-13.84-.32-4.1-.56-7.97.06-12.11.92-6.26,1.04-12.4-.43-18.5-.18-.76-1.4-.95-1.7-.74.48-1.08.49-3.87,2.16-4.43l15.4-1.26,24.35-2.21ZM187.63,257.75c.4.93.52,1.9,1.14,2.91,2.36,3.88,4.35-8.06-7.31-13.58-7.5-3.55-15.29-1.54-21.1,4.83-13.04,14.31-17.28,39.69-4.52,53.8,5.7,6.31,14.78,8.03,22.8,5.87,13.32-3.6,20.27-20.17,13.32-16.11-.83,1.08-1.92.91-1.28-.45.23-1.19-.5-2.25-1.19-2.44-2.65-.75-3.54,8.1-13.46,10.47-5.92,1.42-11.86-1.51-14.55-6.83-4.2-8.32-4.34-17.68-1.05-26.64,3.45-9.39,10.35-17.68,18.54-13.94,4.41,2.01,4.83,8.81,5.68,8.88s1.2-.15,1.64-.32c.27-.1,1.75-2.67.35-6.47.34.07.6.1.97.03Z"/>
<path class="cls-4" d="M142.89,343.17l.29,2.88c.86.07,1.58.05,2.04.23.67.25,1.91,1.3,1.32,2.05-1.46,1.19-1.67,3.08-2.56,5.44l-38.54,2.82c-2.19.16-4,.22-5.07-1.87-.06.09-.12.09-.17.04l-31.82-30.7.34-.51,6.2,1.52c6.89,2.24,14.28,1.74,21.71,1.33,1.56-.09,1.37,2.99,1.43,3.85-.3,7.59-.1,14.62,2.15,21.47.05.41.41.65.83.61.39-.4.95-.17.83-.59l-.37-1.33.4-13.65c.01-.34.23-.76.19-.97-.05-.23.41-.49.79-.42,3.19,1.46,8.11,1.72,9.49-.15.56-.77.19-2.01.46-3.03l1.98-7.57c.19-.72-2.26-2.43-.68-2.8l6.91-1.61c2.74,4.2-.36,8.92-3.04,13.61l2.92,9.49,21.98-.14Z"/> <path class="cls-6" d="M142.89,343.17l.29,2.88c.86.07,1.58.05,2.04.23.67.25,1.91,1.3,1.32,2.05-1.46,1.19-1.67,3.08-2.56,5.44l-38.54,2.82c-2.19.16-4,.22-5.07-1.87-.06.09-.12.09-.17.04l-31.82-30.7.34-.51,6.2,1.52c6.89,2.24,14.28,1.74,21.71,1.33,1.56-.09,1.37,2.99,1.43,3.85-.3,7.59-.1,14.62,2.15,21.47.05.41.41.65.83.61.39-.4.95-.17.83-.59l-.37-1.33.4-13.65c.01-.34.23-.76.19-.97-.05-.23.41-.49.79-.42,3.19,1.46,8.11,1.72,9.49-.15.56-.77.19-2.01.46-3.03l1.98-7.57c.19-.72-2.26-2.43-.68-2.8l6.91-1.61c2.74,4.2-.36,8.92-3.04,13.61l2.92,9.49,21.98-.14Z"/>
<path class="cls-3" d="M89.74,321.43c-1.43.12-3.07.88-4.9.94l-9.56.29c-.16,1.21.7,1.9-.37,2.41l-6.2-1.52-.57-.13-.57-.14c.06-.16.14-.32.15-.48l1.45-15.57,1.33-17.08,1.52-13.95,20.58,1.25c4.86.31,9.63-.63,14.47.77l-23.77,2.92c-2.8.34-6.23,2.85-6.12,4.08.07.84.61,1.73,1.61,2.3l3.45,1.97c.58.33,2.43.89,2.25,2.13l-5.08,9.34c-.6,1.11-1.89,1.46-1.96,2.42-.4,5.59,6.32,7.64,5.95,12.46l6.35,5.57Z"/> <path class="cls-6" d="M89.74,321.43c-1.43.12-3.07.88-4.9.94l-9.56.29c-.16,1.21.7,1.9-.37,2.41l-6.2-1.52-.57-.13-.57-.14c.06-.16.14-.32.15-.48l1.45-15.57,1.33-17.08,1.52-13.95,20.58,1.25c4.86.31,9.63-.63,14.47.77l-23.77,2.92c-2.8.34-6.23,2.85-6.12,4.08.07.84.61,1.73,1.61,2.3l3.45,1.97c.58.33,2.43.89,2.25,2.13l-5.08,9.34c-.6,1.11-1.89,1.46-1.96,2.42-.4,5.59,6.32,7.64,5.95,12.46l6.35,5.57Z"/>
<path class="cls-14" d="M185.31,203.37l.41.36,32.83,32.44c.15.15.23.32.17.52l-3.36-.08c-.18,0-.37-.17-.52.11-9.2-3.04-24.62.27-26.12-.81-2.61-1.89,1.62-13.42-2.11-26.59l-1.16-5.4-.12-.54Z"/> <path class="cls-5" d="M185.31,203.37l.41.36,32.83,32.44c.15.15.23.32.17.52l-3.36-.08c-.18,0-.37-.17-.52.11-9.2-3.04-24.62.27-26.12-.81-2.61-1.89,1.62-13.42-2.11-26.59l-1.16-5.4-.12-.54Z"/>
<path class="cls-7" d="M219.09,263.48c-1.07.3-1.89-.5-2.51-1.36l-3.75-5.23c-1.15-1.61-1.47-4.34-3.46-5.16-1.62-.66-3.5-.91-3.74-3.16l-.98-9.54c2.56-.56,5.24-.75,8.24-.37l1.71-.29c.49-.08,1.13-1.43.74-1.77-.18,0-.37-.17-.52.11.15-.28.34-.11.52-.11l3.36.08c.06-.2-.01-.36-.17-.52.15.15.23.32.17.52,2.85,1.17,1.38,7.41,1.07,13.48l-.68,13.31Z"/> <path class="cls-6" d="M219.09,263.48c-1.07.3-1.89-.5-2.51-1.36l-3.75-5.23c-1.15-1.61-1.47-4.34-3.46-5.16-1.62-.66-3.5-.91-3.74-3.16l-.98-9.54c2.56-.56,5.24-.75,8.24-.37l1.71-.29c.49-.08,1.13-1.43.74-1.77-.18,0-.37-.17-.52.11.15-.28.34-.11.52-.11l3.36.08c.06-.2-.01-.36-.17-.52.15.15.23.32.17.52,2.85,1.17,1.38,7.41,1.07,13.48l-.68,13.31Z"/>
<path class="cls-11" d="M137.02,209.09l5.09,4.92c1.03-.68.94-1.93,1.29-2.74.3-.21,1.51-.02,1.7.74,1.47,6.1,1.35,12.23.43,18.5-.61,4.14-.37,8.01-.06,12.11.37,4.7-.33,9.3.6,13.84.1.5-.03,1.42-.85,1.6l-3.13-17.22-2.71-17.1c-.77-4.87-1.91-9.49-5.14-13.85l2.23-1.33.56.54Z"/> <path class="cls-6" d="M137.02,209.09l5.09,4.92c1.03-.68.94-1.93,1.29-2.74.3-.21,1.51-.02,1.7.74,1.47,6.1,1.35,12.23.43,18.5-.61,4.14-.37,8.01-.06,12.11.37,4.7-.33,9.3.6,13.84.1.5-.03,1.42-.85,1.6l-3.13-17.22-2.71-17.1c-.77-4.87-1.91-9.49-5.14-13.85l2.23-1.33.56.54Z"/>
<path class="cls-18" d="M155.13,354.34l-6.55-5.25c-.5-.4-.58-1.25-1.2-1.09-.28.07-.6.17-.85.32.59-.75-.65-1.8-1.32-2.05-.47-.17-1.18-.15-2.04-.23l-.29-2.88c-.34-3.39.13-6.76.22-10.34l.5-20.05c.35-.26.61-.87,1.26-.95.44.36.93,1.18,1.07,2.16l3.75,25.3,4.42,9.91c.75,1.67.89,3.39,1.01,5.15Z"/> <path class="cls-1" d="M155.13,354.34l-6.55-5.25c-.5-.4-.58-1.25-1.2-1.09-.28.07-.6.17-.85.32.59-.75-.65-1.8-1.32-2.05-.47-.17-1.18-.15-2.04-.23l-.29-2.88c-.34-3.39.13-6.76.22-10.34l.5-20.05c.35-.26.61-.87,1.26-.95.44.36.93,1.18,1.07,2.16l3.75,25.3,4.42,9.91c.75,1.67.89,3.39,1.01,5.15Z"/>
<path class="cls-1" d="M218.76,272.12c.55,2.96-1.28,5.06-3.13,7.88-.92,1.4,1.16,2.13,1.36,3.36-.98.53-1.25,1.36-1.47-.31l-23.5-2.78c-4.93-.58-9.56-.56-14.55-2.03l21.66-2.39,16.08-1.97c1.17-.14,2.07-2.61,3.55-1.77Z"/> <path class="cls-1" d="M218.76,272.12c.55,2.96-1.28,5.06-3.13,7.88-.92,1.4,1.16,2.13,1.36,3.36-.98.53-1.25,1.36-1.47-.31l-23.5-2.78c-4.93-.58-9.56-.56-14.55-2.03l21.66-2.39,16.08-1.97c1.17-.14,2.07-2.61,3.55-1.77Z"/>
<path class="cls-15" d="M144.88,311.82c-.66.08-.92.69-1.26.95-1.24.01-2.37-.28-2.74-1.23-.53-1.4-1.54-2.36-.5-3.48-.86-.08-1.19-.06-1.2.31v1.31c0,1.3.6,2.76-.52,2.99-1.43.29-2.47.29-2.91-1.32l-5.96-21.86-18.11,2.9c-2.1,7.01-4.09,13.33-6.8,19.73-1.17.31-1.78-.08-2.01-.61-.41-.95-.31-1.94-.18-3.14,1.5-.6.24-1.35-.73-.97-.56,1.24-.74,3.16-1.82,4.45-.71.85-1.64.57-2.04.13-.54-.58-.8-1.56-.57-2.34l4.78-15.62c-1.33.17-4.1,1.25-4.95-.31-1.05-1.92,1.92-2.4,3.54-2.77-1.34-1.57-3.26-.38-5.06-.44-.62-.02-.58-1.96,0-2.14l1.8-.57,7.39-1.96,12.57-37.25c.72-2.13,4.25-1.12,5.46.22.45-1.06,1.07-2.12,1.09-2.78.03-.94.98-1.12,1.53-.93.39.13,1.13.3,1.34,1.05l9.67,35.31,4.2-.07c1.29-.02,1.98,1.07,1.64,2.16-.23.73-1.57.54-1.97,1.2-.45.74,1.17,1.36.17,2.07l-1.62,1.16c-.93.67.57,1.85.39,2.35,1.24,5.52,2.93,10.37,4.83,15.35.55,2.02,1.01,4.45.58,6.18ZM122.28,263.01l-7.68,21.2,13.37-1.5-5.69-19.69Z"/> <path class="cls-8" d="M144.88,311.82c-.66.08-.92.69-1.26.95-1.24.01-2.37-.28-2.74-1.23-.53-1.4-1.54-2.36-.5-3.48-.86-.08-1.19-.06-1.2.31v1.31c0,1.3.6,2.76-.52,2.99-1.43.29-2.47.29-2.91-1.32l-5.96-21.86-18.11,2.9c-2.1,7.01-4.09,13.33-6.8,19.73-1.17.31-1.78-.08-2.01-.61-.41-.95-.31-1.94-.18-3.14,1.5-.6.24-1.35-.73-.97-.56,1.24-.74,3.16-1.82,4.45-.71.85-1.64.57-2.04.13-.54-.58-.8-1.56-.57-2.34l4.78-15.62c-1.33.17-4.1,1.25-4.95-.31-1.05-1.92,1.92-2.4,3.54-2.77-1.34-1.57-3.26-.38-5.06-.44-.62-.02-.58-1.96,0-2.14l1.8-.57,7.39-1.96,12.57-37.25c.72-2.13,4.25-1.12,5.46.22.45-1.06,1.07-2.12,1.09-2.78.03-.94.98-1.12,1.53-.93.39.13,1.13.3,1.34,1.05l9.67,35.31,4.2-.07c1.29-.02,1.98,1.07,1.64,2.16-.23.73-1.57.54-1.97,1.2-.45.74,1.17,1.36.17,2.07l-1.62,1.16c-.93.67.57,1.85.39,2.35,1.24,5.52,2.93,10.37,4.83,15.35.55,2.02,1.01,4.45.58,6.18ZM122.28,263.01l-7.68,21.2,13.37-1.5-5.69-19.69Z"/>
<path class="cls-10" d="M187.63,257.75l-1.21-2.81c-.33-.78-.83-2.11-2.39-2.23l-.53-1.37c-.16-.41-.82-.21-1.64-.58-1.01-.56-1.29.55.06.58,2.16,2.07,3.86,4.01,4.73,6.38,1.4,3.8-.08,6.37-.35,6.47-.45.17-.81.39-1.64.32s-1.27-6.87-5.68-8.88c-8.19-3.73-15.09,4.55-18.54,13.94-3.29,8.97-3.16,18.32,1.05,26.64,2.69,5.33,8.63,8.25,14.55,6.83,9.92-2.37,10.81-11.22,13.46-10.47.69.19,1.42,1.25,1.19,2.44-.64,1.37.45,1.54,1.28.45,6.95-4.06,0,12.51-13.32,16.11-8.02,2.17-17.1.44-22.8-5.87-12.75-14.1-8.52-39.49,4.52-53.8,5.8-6.37,13.59-8.37,21.1-4.83,11.66,5.51,9.67,17.45,7.31,13.58-.61-1.01-.74-1.98-1.14-2.91ZM157.46,302.61l3.6,3.77c.64.67.95.15,1.82.3.52.09-.23-.61-.29-.93-.07-.41-1.13-.1-1.37-.35-1.17-1.26-1.91-3.37-3.77-2.78Z"/> <path class="cls-7" d="M187.63,257.75l-1.21-2.81c-.33-.78-.83-2.11-2.39-2.23l-.53-1.37c-.16-.41-.82-.21-1.64-.58-1.01-.56-1.29.55.06.58,2.16,2.07,3.86,4.01,4.73,6.38,1.4,3.8-.08,6.37-.35,6.47-.45.17-.81.39-1.64.32s-1.27-6.87-5.68-8.88c-8.19-3.73-15.09,4.55-18.54,13.94-3.29,8.97-3.16,18.32,1.05,26.64,2.69,5.33,8.63,8.25,14.55,6.83,9.92-2.37,10.81-11.22,13.46-10.47.69.19,1.42,1.25,1.19,2.44-.64,1.37.45,1.54,1.28.45,6.95-4.06,0,12.51-13.32,16.11-8.02,2.17-17.1.44-22.8-5.87-12.75-14.1-8.52-39.49,4.52-53.8,5.8-6.37,13.59-8.37,21.1-4.83,11.66,5.51,9.67,17.45,7.31,13.58-.61-1.01-.74-1.98-1.14-2.91ZM157.46,302.61l3.6,3.77c.64.67.95.15,1.82.3.52.09-.23-.61-.29-.93-.07-.41-1.13-.1-1.37-.35-1.17-1.26-1.91-3.37-3.77-2.78Z"/>
<path class="cls-8" d="M186.59,209.31c3.74,13.17-.49,24.7,2.11,26.59,1.5,1.08,16.92-2.22,26.12.81.15-.28.34-.11.52-.11.4.33-.25,1.68-.74,1.77l-1.71.29c-3-.38-5.68-.19-8.24.37-1.25.27-3.1.25-4.69.32-4.76.22-9.32,2.54-14.1,2.1-2.86-.26-3.3-5.24-3.04-7.58.92-8.32,1.46-15.9,1.54-24.08l2.22-.49Z"/> <path class="cls-1" d="M186.59,209.31c3.74,13.17-.49,24.7,2.11,26.59,1.5,1.08,16.92-2.22,26.12.81.15-.28.34-.11.52-.11.4.33-.25,1.68-.74,1.77l-1.71.29c-3-.38-5.68-.19-8.24.37-1.25.27-3.1.25-4.69.32-4.76.22-9.32,2.54-14.1,2.1-2.86-.26-3.3-5.24-3.04-7.58.92-8.32,1.46-15.9,1.54-24.08l2.22-.49Z"/>
<path class="cls-9" d="M102.87,335.37c-.38-.07-.84.19-.79.42.05.21-.18.63-.19.97l-.4,13.65.37,1.33c.12.42-.44.19-.83.59-.42.03-.78-.2-.83-.61-2.25-6.85-2.45-13.87-2.15-21.47-.06-.86.13-3.93-1.43-3.85-7.43.41-14.82.91-21.71-1.33,1.07-.51.21-1.2.37-2.41l9.56-.29c1.83-.06,3.48-.82,4.9-.94l8.08-.67c1.68-.14,4.77-1.1,5.21,1.15,1.08,5.55-1.46,10.47-.16,13.45Z"/> <path class="cls-1" d="M102.87,335.37c-.38-.07-.84.19-.79.42.05.21-.18.63-.19.97l-.4,13.65.37,1.33c.12.42-.44.19-.83.59-.42.03-.78-.2-.83-.61-2.25-6.85-2.45-13.87-2.15-21.47-.06-.86.13-3.93-1.43-3.85-7.43.41-14.82.91-21.71-1.33,1.07-.51.21-1.2.37-2.41l9.56-.29c1.83-.06,3.48-.82,4.9-.94l8.08-.67c1.68-.14,4.77-1.1,5.21,1.15,1.08,5.55-1.46,10.47-.16,13.45Z"/>
<path class="cls-6" d="M187.63,257.75c-.37.07-.64.04-.97-.03-.88-2.38-2.57-4.31-4.73-6.38-1.35-.04-1.07-1.15-.06-.58.82.37,1.48.17,1.64.58l.53,1.37c1.56.12,2.06,1.45,2.39,2.23l1.21,2.81Z"/> <path class="cls-1" d="M187.63,257.75c-.37.07-.64.04-.97-.03-.88-2.38-2.57-4.31-4.73-6.38-1.35-.04-1.07-1.15-.06-.58.82.37,1.48.17,1.64.58l.53,1.37c1.56.12,2.06,1.45,2.39,2.23l1.21,2.81Z"/>
<polygon class="cls-16" points="122.28 263.01 127.97 282.71 114.6 284.21 122.28 263.01"/> <polygon class="cls-5" points="122.28 263.01 127.97 282.71 114.6 284.21 122.28 263.01"/>
<path class="cls-17" d="M157.46,302.61c1.86-.59,2.59,1.52,3.77,2.78.23.25,1.3-.06,1.37.35.05.32.81,1.02.29.93-.87-.15-1.18.38-1.82-.3l-3.6-3.77Z"/> <path class="cls-1" d="M157.46,302.61c1.86-.59,2.59,1.52,3.77,2.78.23.25,1.3-.06,1.37.35.05.32.81,1.02.29.93-.87-.15-1.18.38-1.82-.3l-3.6-3.77Z"/>
<path class="cls-2" d="M210.46,277.3l6.02,1.81c1.38.43.78,2.5-.62,2.11,0,0-6.03-1.81-6.03-1.81-1.97-.86-4.17-1.86-6.07-2.91,1.33.16,5.03.6,6.7.81h0Z"/> <path class="cls-2" d="M210.46,277.3l6.02,1.81c1.38.43.78,2.5-.62,2.11,0,0-6.03-1.81-6.03-1.81-1.97-.86-4.17-1.86-6.07-2.91,1.33.16,5.03.6,6.7.81h0Z"/>
<path class="cls-12" d="M185.31,203.37l-24.35,2.21-15.4,1.26c-1.66.57-1.68,3.35-2.16,4.43-.36.81-.27,2.06-1.29,2.74l-5.09-4.92-.56-.54c-1.41-1.37-2.9-2.54-5.27-2.25-19.66,2.42-39.1,2.45-58.78-.65l-1.35,16.91c-.64,7.97,4.5,10.07,2.92,17.11-.36,1.59,6.68,4.62,3.67,6.6-2.14,1.4-5.56,1.7-7.04,3.85-1.83,2.66-.58,5.92-.31,8.64l1.75,17.45-1.52,13.95-1.33,17.08-1.45,15.57-.15.48"/> <path class="cls-4" d="M185.31,203.37l-24.35,2.21-15.4,1.26c-1.66.57-1.68,3.35-2.16,4.43-.36.81-.27,2.06-1.29,2.74l-5.09-4.92-.56-.54c-1.41-1.37-2.9-2.54-5.27-2.25-19.66,2.42-39.1,2.45-58.78-.65l-1.35,16.91c-.64,7.97,4.5,10.07,2.92,17.11-.36,1.59,6.68,4.62,3.67,6.6-2.14,1.4-5.56,1.7-7.04,3.85-1.83,2.66-.58,5.92-.31,8.64l1.75,17.45-1.52,13.95-1.33,17.08-1.45,15.57-.15.48"/>
<path class="cls-12" d="M185.32,203.34l.4.39,32.83,32.44c.15.15.23.32.17.52l-3.36-.08c-.18,0-.37-.17-.52.11-9.2-3.04-24.62.27-26.12-.81-2.61-1.89,1.62-13.42-2.11-26.59l-1.16-5.4-.11-.58Z"/> <path class="cls-4" d="M185.32,203.34l.4.39,32.83,32.44c.15.15.23.32.17.52l-3.36-.08c-.18,0-.37-.17-.52.11-9.2-3.04-24.62.27-26.12-.81-2.61-1.89,1.62-13.42-2.11-26.59l-1.16-5.4-.11-.58Z"/>
<path class="cls-12" d="M213.54,317.63c2.77,1.35,3.4,4.57,3.09,7.75,2.44,5.7,2.42,11.51,1.87,17.57l-.91,9.96c-.14,1.55-1.69,1.51-2.77,1.41l-17.57-1.6c-4.26-.39-8.71.46-12.16-2.84l-5.53-7.04c-.25.2-.35.47-.36.58-.28,3.07-.81,5.92-2.85,8.97l-21.21,1.95-6.55-5.25c-.5-.4-.58-1.25-1.2-1.09-.28.07-.6.17-.85.32-1.46,1.19-1.67,3.08-2.56,5.44l-38.54,2.82c-2.19.16-4,.22-5.07-1.87-.62-.8-.65-1.96-.17-3.01-2.25-6.85-2.45-13.87-2.15-21.47-.06-.86.13-3.93-1.43-3.85-7.43.41-14.82.91-21.71-1.33l-6.2-1.52-.57-.13-.57-.14"/> <path class="cls-4" d="M213.54,317.63c2.77,1.35,3.4,4.57,3.09,7.75,2.44,5.7,2.42,11.51,1.87,17.57l-.91,9.96c-.14,1.55-1.69,1.51-2.77,1.41l-17.57-1.6c-4.26-.39-8.71.46-12.16-2.84l-5.53-7.04c-.25.2-.35.47-.36.58-.28,3.07-.81,5.92-2.85,8.97l-21.21,1.95-6.55-5.25c-.5-.4-.58-1.25-1.2-1.09-.28.07-.6.17-.85.32-1.46,1.19-1.67,3.08-2.56,5.44l-38.54,2.82c-2.19.16-4,.22-5.07-1.87-.62-.8-.65-1.96-.17-3.01-2.25-6.85-2.45-13.87-2.15-21.47-.06-.86.13-3.93-1.43-3.85-7.43.41-14.82.91-21.71-1.33l-6.2-1.52-.57-.13-.57-.14"/>
<polyline class="cls-13" points="100.19 354.76 68.38 324.06 67.57 323.28"/> <polyline class="cls-3" points="100.19 354.76 68.38 324.06 67.57 323.28"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -3,30 +3,22 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #e7c781;
}
.cls-2 {
fill: #39170a; fill: #39170a;
} }
.cls-2 {
fill: #6b1f65;
}
.cls-3 { .cls-3 {
fill: #f7e4b2; fill: #852f7e;
} }
.cls-4 { .cls-4 {
fill: #b59683; fill: #3d1a0d;
} }
.cls-5 { .cls-5 {
fill: #f6e4b3;
}
.cls-6 {
fill: #e3c075;
}
.cls-7 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
@@ -34,67 +26,31 @@
stroke-width: 2.2px; stroke-width: 2.2px;
} }
.cls-8 { .cls-6 {
fill: #3d1a0d; fill: #9e3d96;
}
.cls-9 {
fill: #9b7b6a;
}
.cls-10 {
fill: #dbb76d;
}
.cls-11 {
fill: #cfa864;
}
.cls-12 {
fill: #bea28d;
}
.cls-13 {
fill: #f4dda3;
}
.cls-14 {
fill: #f0d7bb;
}
.cls-15 {
fill: #e9cb88;
}
.cls-16 {
fill: #c49d5e;
}
.cls-17 {
fill: #dfbd72;
} }
</style> </style>
</defs> </defs>
<path class="cls-13" d="M134.56,198.73c-.18.4-.63,1.06-.86,1.01l-1.53-.32,2.73,6.11c3.16,4.21,3.45,9.15,4.36,14.26l5.43,30.42-.31-26.42.69-6.77-.39-15.14c.78.13,1.2.48,1.86.21.09,1.59-.38,3.25-.01,4.91,3.95,1.89,3.31,4.73,5.82,4.74,1.17,0,3.35.51,3.8-.95.88-2.85,3.04-3.98,3.96-8.34,2.01-.62,4.59.12,5.2,2.41l2.94,4.65.47,2.59c.11.63,3.07.5,5.91,4.7.74-2.1,2.63-2.9,4.18-3.47,2.1-.76,3.81.92,5.23,2.46l1.28.36-1.2,10.28c-.16,1.36.06,4.75,1.34,5.26l12.32-1.33c1.3-.14,2.73-.73,4.27-.46-.74,2.06,2.02,3.53,2.3,4.95.63,3.15.89,5.85,3.93,7.95,1.18.81,2.14,2.65,4.4,1.82l2.14,4.34c-1.97,3.85-2.69,7.52-1.18,11.55l-7.95,3.92-9.59,1.55-24.02,4.56,31.72.18c3.86-1.21,7.59-.06,10.11,3.3-3.28,1.87-2.97,4.67-2,7.19,3.17,8.23-.69,15.5-7.22,20.39-2.27,2.33-3.26,5.96-4.66,8.69l-14.97-1.08-1.98.67c-1.17.4-1.4,6.07-1.2,10.28l-5.97.64c.01,1.31.89,2.27,1.35,3.52l-3.55,5.37c-5.53-.62-8.28,7-13.89,10.9-1.98,1.37-3.73-.25-5.2-.67-1.79-.51-3.25-.83-4.12-2.65-1.17.63-2.28.31-3.09,0-.93-.36-1.45-1.42-1.58-2.63l-1.24-12.07-.54-21.41-3.37,26.93-2.32,11.58c-1.35-.6-2.56-.79-3.93-.64-2.87.32-5.45-.38-8.31-.41-1.55-.02-1.88-1.53-3.11-3.07l2.02-2.29c-1.1-2.21-3.48-2.22-5.28-3.2-.75-.41-1.27-1.25-2.14-1.2-2.2.13.67,6.44-2.89,6.37l-6.18-.11c-.97-.02-1.52-.97-1.46-1.38l.36-2.39c-2.16-3.31-6.64-4.97-10.34-6.05l-4.61-1.35,9.71-11.1c1.03-1.17,2.56-2.27,2.2-3.91l-10.97,8.35c-4.57-1.25-8.28-3.55-8.21-8.02-.63-.81-1.9-1.86-1.64-2.9.76-2.95,3.93-2.39,4-6.01.03-1.5-1.05-2.01-2.21-3.07-1.5-1.37-2.59-3.79-4.86-4.28-1.89-.41-6.44-4.24-7.02-6.09-1.01-3.25,2.85-5.63.92-8.2l8.46-2.25,18.21-3.46.55,22.34-4.07-1.09c-.62-.17-1.15.71-1.26,1.11-.71,2.66,6.57,4.49,6.46,4.96-.06.26-.4.99-.55.77-.26-.37-.68-.81-1.29-.89l-2.57-.33c-.63-.08-1.82,2.04-.78,2.4,13.51,4.72,30.52,4.52,40.19-6.04,4.26-4.65,7-10.96,5.44-17.11-1.88-7.43-8.07-12.69-15.99-13.65,4.61-3.97,8.07-9.52,6.06-15.48s-7.82-9.61-13.78-10.33c-13.8-1.66-26.43,7.86-23.9,11.48l1.82.27c.83.77.9,1.54,1.58,1.28l2.21-.86-.14,17.59-10.69.46-11.02-.53c-3.1-.15-5.69-2.07-8.52-1.76l-2.44-19.92c-1.31-2.93-.76-6.46,2.46-7.96,3.42-1.59,4.59-3.37,5.99-2.92.03-2.33-2.37-3.38-4.36-4.69l-.32-6.53c-5.46-9.7-.92-17.59-1.51-28.43l16.23,2.03,23.56-.02,16.48-1.23c2.97-.22,5.24-1.39,7.61.8ZM185.54,293.84c2.42-3.82,5.39-7.88,3.02-9.23s-3.38,6.71-11.7,9.93c-4.18,1.62-8.99.92-12.68-1.81-3.38-2.5-5.13-6.55-6.15-11.09-3.58-15.97,6.89-36.59,18.87-32.85,6.82,2.13,4.36,12.37,8.6,8.12.92-1.52.02-3.6.33-5.64l2.51,3.38c1.54-.91,1.02-2.87.63-4.33-1.61-6.12-7.34-9.87-13.12-11.04-6.3-1.28-11.96,1.46-16.42,6.15-6.23,6.56-9.49,15.21-11.29,23.96-2.95,14.33,3.3,32.61,19.18,34.63,11.19,1.43,22.11-4.68,26.13-15.27.19-.49.04-1.09.04-1.41,0-.73-2.81-.21-2.68-.3-1.54,1.13-1.96,6.72-5.28,6.8Z"/> <path class="cls-6" d="M134.56,198.73c-.18.4-.63,1.06-.86,1.01l-1.53-.32,2.73,6.11c3.16,4.21,3.45,9.15,4.36,14.26l5.43,30.42-.31-26.42.69-6.77-.39-15.14c.78.13,1.2.48,1.86.21.09,1.59-.38,3.25-.01,4.91,3.95,1.89,3.31,4.73,5.82,4.74,1.17,0,3.35.51,3.8-.95.88-2.85,3.04-3.98,3.96-8.34,2.01-.62,4.59.12,5.2,2.41l2.94,4.65.47,2.59c.11.63,3.07.5,5.91,4.7.74-2.1,2.63-2.9,4.18-3.47,2.1-.76,3.81.92,5.23,2.46l1.28.36-1.2,10.28c-.16,1.36.06,4.75,1.34,5.26l12.32-1.33c1.3-.14,2.73-.73,4.27-.46-.74,2.06,2.02,3.53,2.3,4.95.63,3.15.89,5.85,3.93,7.95,1.18.81,2.14,2.65,4.4,1.82l2.14,4.34c-1.97,3.85-2.69,7.52-1.18,11.55l-7.95,3.92-9.59,1.55-24.02,4.56,31.72.18c3.86-1.21,7.59-.06,10.11,3.3-3.28,1.87-2.97,4.67-2,7.19,3.17,8.23-.69,15.5-7.22,20.39-2.27,2.33-3.26,5.96-4.66,8.69l-14.97-1.08-1.98.67c-1.17.4-1.4,6.07-1.2,10.28l-5.97.64c.01,1.31.89,2.27,1.35,3.52l-3.55,5.37c-5.53-.62-8.28,7-13.89,10.9-1.98,1.37-3.73-.25-5.2-.67-1.79-.51-3.25-.83-4.12-2.65-1.17.63-2.28.31-3.09,0-.93-.36-1.45-1.42-1.58-2.63l-1.24-12.07-.54-21.41-3.37,26.93-2.32,11.58c-1.35-.6-2.56-.79-3.93-.64-2.87.32-5.45-.38-8.31-.41-1.55-.02-1.88-1.53-3.11-3.07l2.02-2.29c-1.1-2.21-3.48-2.22-5.28-3.2-.75-.41-1.27-1.25-2.14-1.2-2.2.13.67,6.44-2.89,6.37l-6.18-.11c-.97-.02-1.52-.97-1.46-1.38l.36-2.39c-2.16-3.31-6.64-4.97-10.34-6.05l-4.61-1.35,9.71-11.1c1.03-1.17,2.56-2.27,2.2-3.91l-10.97,8.35c-4.57-1.25-8.28-3.55-8.21-8.02-.63-.81-1.9-1.86-1.64-2.9.76-2.95,3.93-2.39,4-6.01.03-1.5-1.05-2.01-2.21-3.07-1.5-1.37-2.59-3.79-4.86-4.28-1.89-.41-6.44-4.24-7.02-6.09-1.01-3.25,2.85-5.63.92-8.2l8.46-2.25,18.21-3.46.55,22.34-4.07-1.09c-.62-.17-1.15.71-1.26,1.11-.71,2.66,6.57,4.49,6.46,4.96-.06.26-.4.99-.55.77-.26-.37-.68-.81-1.29-.89l-2.57-.33c-.63-.08-1.82,2.04-.78,2.4,13.51,4.72,30.52,4.52,40.19-6.04,4.26-4.65,7-10.96,5.44-17.11-1.88-7.43-8.07-12.69-15.99-13.65,4.61-3.97,8.07-9.52,6.06-15.48s-7.82-9.61-13.78-10.33c-13.8-1.66-26.43,7.86-23.9,11.48l1.82.27c.83.77.9,1.54,1.58,1.28l2.21-.86-.14,17.59-10.69.46-11.02-.53c-3.1-.15-5.69-2.07-8.52-1.76l-2.44-19.92c-1.31-2.93-.76-6.46,2.46-7.96,3.42-1.59,4.59-3.37,5.99-2.92.03-2.33-2.37-3.38-4.36-4.69l-.32-6.53c-5.46-9.7-.92-17.59-1.51-28.43l16.23,2.03,23.56-.02,16.48-1.23c2.97-.22,5.24-1.39,7.61.8ZM185.54,293.84c2.42-3.82,5.39-7.88,3.02-9.23s-3.38,6.71-11.7,9.93c-4.18,1.62-8.99.92-12.68-1.81-3.38-2.5-5.13-6.55-6.15-11.09-3.58-15.97,6.89-36.59,18.87-32.85,6.82,2.13,4.36,12.37,8.6,8.12.92-1.52.02-3.6.33-5.64l2.51,3.38c1.54-.91,1.02-2.87.63-4.33-1.61-6.12-7.34-9.87-13.12-11.04-6.3-1.28-11.96,1.46-16.42,6.15-6.23,6.56-9.49,15.21-11.29,23.96-2.95,14.33,3.3,32.61,19.18,34.63,11.19,1.43,22.11-4.68,26.13-15.27.19-.49.04-1.09.04-1.41,0-.73-2.81-.21-2.68-.3-1.54,1.13-1.96,6.72-5.28,6.8Z"/>
<path class="cls-6" d="M74.46,278.72c1.93,2.57-1.93,4.94-.92,8.2.57,1.85,5.13,5.69,7.02,6.09,2.27.49,3.37,2.91,4.86,4.28,1.15,1.06,2.24,1.57,2.21,3.07-.07,3.62-3.24,3.06-4,6.01-.26,1.03,1.01,2.08,1.64,2.9-.07,4.47,3.65,6.77,8.21,8.02l10.97-8.35c.36,1.64-1.18,2.73-2.2,3.91l-9.71,11.1,4.61,1.35c3.7,1.08,8.18,2.75,10.34,6.05l-.36,2.39c-.06.4.49,1.36,1.46,1.38l6.18.11c3.56.07.69-6.24,2.89-6.37.86-.05,1.39.79,2.14,1.2,1.8.99,4.18.99,5.28,3.2l-2.02,2.29c1.23,1.55,1.56,3.06,3.11,3.07,2.87.03,5.44.73,8.31.41,1.37-.15,2.57.04,3.93.64l2.32-11.58,3.37-26.93.54,21.41,1.24,12.07c.12,1.21.65,2.27,1.58,2.63.81.32,1.92.64,3.09,0,.86,1.82,2.33,2.14,4.12,2.65,1.47.42,3.22,2.04,5.2.67,5.6-3.9,8.36-11.52,13.89-10.9l3.55-5.37c-.47-1.25-1.34-2.21-1.35-3.52l5.97-.64.85,18.06-.17,3.08c.67-.03,1.46-.09,2.22.11l-1.57,4.51-.4,1.16-.74-.15-9.48-.44-13.17-.04c-5.51-.02-10.81.2-15.91-2.37-4.17,3.3-8.82,3.85-13.75,4.04l-11.57.44c-4.22.16-8-1.47-12.1-2.44l-9.23-2.18-2.37-2.53-1-1.14c-.19-.22-.42.16-1.49,0l-2.88,3.29c-1.64,3.38-4.27,3.48-7.57,2.93l-8.76-1.47c-1.06-.18-1.68-1.56-2.63-2.24l-.25-10.04c-.09-3.55-2.33-6.92-2.12-10.04l.33-4.87,1.13-9.81c.44-3.77.39-7.85,0-11.53l-.45-4.37c-.88-2.66-.01-5.19,1.15-7.52l3.1-6.18c.48,0,.8.42,1.38.27Z"/> <path class="cls-3" d="M74.46,278.72c1.93,2.57-1.93,4.94-.92,8.2.57,1.85,5.13,5.69,7.02,6.09,2.27.49,3.37,2.91,4.86,4.28,1.15,1.06,2.24,1.57,2.21,3.07-.07,3.62-3.24,3.06-4,6.01-.26,1.03,1.01,2.08,1.64,2.9-.07,4.47,3.65,6.77,8.21,8.02l10.97-8.35c.36,1.64-1.18,2.73-2.2,3.91l-9.71,11.1,4.61,1.35c3.7,1.08,8.18,2.75,10.34,6.05l-.36,2.39c-.06.4.49,1.36,1.46,1.38l6.18.11c3.56.07.69-6.24,2.89-6.37.86-.05,1.39.79,2.14,1.2,1.8.99,4.18.99,5.28,3.2l-2.02,2.29c1.23,1.55,1.56,3.06,3.11,3.07,2.87.03,5.44.73,8.31.41,1.37-.15,2.57.04,3.93.64l2.32-11.58,3.37-26.93.54,21.41,1.24,12.07c.12,1.21.65,2.27,1.58,2.63.81.32,1.92.64,3.09,0,.86,1.82,2.33,2.14,4.12,2.65,1.47.42,3.22,2.04,5.2.67,5.6-3.9,8.36-11.52,13.89-10.9l3.55-5.37c-.47-1.25-1.34-2.21-1.35-3.52l5.97-.64.85,18.06-.17,3.08c.67-.03,1.46-.09,2.22.11l-1.57,4.51-.4,1.16-.74-.15-9.48-.44-13.17-.04c-5.51-.02-10.81.2-15.91-2.37-4.17,3.3-8.82,3.85-13.75,4.04l-11.57.44c-4.22.16-8-1.47-12.1-2.44l-9.23-2.18-2.37-2.53-1-1.14c-.19-.22-.42.16-1.49,0l-2.88,3.29c-1.64,3.38-4.27,3.48-7.57,2.93l-8.76-1.47c-1.06-.18-1.68-1.56-2.63-2.24l-.25-10.04c-.09-3.55-2.33-6.92-2.12-10.04l.33-4.87,1.13-9.81c.44-3.77.39-7.85,0-11.53l-.45-4.37c-.88-2.66-.01-5.19,1.15-7.52l3.1-6.18c.48,0,.8.42,1.38.27Z"/>
<path class="cls-10" d="M219.05,227.87l.79.78-.21.25.6.14-.13.88-1.61,30.29-6.75,7.18,6.67,3.15,1.66,35.78c.09,1.99,0,4.05-1.49,5-.19.12-.4.28-.59.29l.1.11-.1-.11-3.17.11c-.29-.45-.37-1.65-1.08-1.62l-5.37.21c-2.72.1-5.69.2-8.32.01,1.39-2.73,2.38-6.36,4.66-8.69,6.53-4.89,10.39-12.17,7.22-20.39-.97-2.52-1.28-5.32,2-7.19-2.53-3.36-6.25-4.51-10.11-3.3l-31.72-.18,24.02-4.56,9.59-1.55,7.95-3.92c-1.51-4.02-.8-7.7,1.18-11.55l-2.14-4.34c-2.26.84-3.21-1-4.4-1.82-3.04-2.1-3.3-4.8-3.93-7.95-.28-1.42-3.04-2.89-2.3-4.95l3.85-.57,8.82.23c.74-.32,1.1.42,1.09-.02-.02-.58.56-1.21.18-1.42l2.43.46.62-.74Z"/> <path class="cls-3" d="M219.05,227.87l.79.78-.21.25.6.14-.13.88-1.61,30.29-6.75,7.18,6.67,3.15,1.66,35.78c.09,1.99,0,4.05-1.49,5-.19.12-.4.28-.59.29l.1.11-.1-.11-3.17.11c-.29-.45-.37-1.65-1.08-1.62l-5.37.21c-2.72.1-5.69.2-8.32.01,1.39-2.73,2.38-6.36,4.66-8.69,6.53-4.89,10.39-12.17,7.22-20.39-.97-2.52-1.28-5.32,2-7.19-2.53-3.36-6.25-4.51-10.11-3.3l-31.72-.18,24.02-4.56,9.59-1.55,7.95-3.92c-1.51-4.02-.8-7.7,1.18-11.55l-2.14-4.34c-2.26.84-3.21-1-4.4-1.82-3.04-2.1-3.3-4.8-3.93-7.95-.28-1.42-3.04-2.89-2.3-4.95l3.85-.57,8.82.23c.74-.32,1.1.42,1.09-.02-.02-.58.56-1.21.18-1.42l2.43.46.62-.74Z"/>
<path class="cls-17" d="M188.5,197.92c-.14-.14-.26-.35-.42-.36.16.02.28.22.42.36l-.8,2.07.09,1.09-1.96.21c.42,5.17.06,10.03-.5,14.87l-1.28-.36c-1.42-1.53-3.13-3.22-5.23-2.46-1.55.57-3.44,1.37-4.18,3.47-2.84-4.2-5.79-4.07-5.91-4.7l-.47-2.59-2.94-4.65c-.61-2.29-3.19-3.03-5.2-2.41-.92,4.36-3.08,5.48-3.96,8.34-.45,1.46-2.64.96-3.8.95-2.51,0-1.87-2.85-5.82-4.74-.37-1.66.1-3.32.01-4.91-.66.27-1.08-.08-1.86-.21l.39,15.14-.69,6.77.31,26.42-5.43-30.42c-.91-5.11-1.2-10.05-4.36-14.26l-2.73-6.11,1.53.32c.22.05.68-.6.86-1.01,2.07,1.91,4.3,4.1,6.23,6.93l3.69-7.11,37.01-2.68c2.54-1.25,5.05-.41,6.58,1.69.16.02.28.22.42.36Z"/> <path class="cls-3" d="M188.5,197.92c-.14-.14-.26-.35-.42-.36.16.02.28.22.42.36l-.8,2.07.09,1.09-1.96.21c.42,5.17.06,10.03-.5,14.87l-1.28-.36c-1.42-1.53-3.13-3.22-5.23-2.46-1.55.57-3.44,1.37-4.18,3.47-2.84-4.2-5.79-4.07-5.91-4.7l-.47-2.59-2.94-4.65c-.61-2.29-3.19-3.03-5.2-2.41-.92,4.36-3.08,5.48-3.96,8.34-.45,1.46-2.64.96-3.8.95-2.51,0-1.87-2.85-5.82-4.74-.37-1.66.1-3.32.01-4.91-.66.27-1.08-.08-1.86-.21l.39,15.14-.69,6.77.31,26.42-5.43-30.42c-.91-5.11-1.2-10.05-4.36-14.26l-2.73-6.11,1.53.32c.22.05.68-.6.86-1.01,2.07,1.91,4.3,4.1,6.23,6.93l3.69-7.11,37.01-2.68c2.54-1.25,5.05-.41,6.58,1.69.16.02.28.22.42.36Z"/>
<path class="cls-15" d="M217.99,311.6l-.39.42-33.59,33.88-.77.03,1.57-4.51c2.75-7.94,3.11-16.57,1.63-25.15-.21-1.2,1.02-2.43,2.16-2.43l11.01.06c5.33.03,10.37-.9,15.23-2.19l3.17-.11Z"/> <path class="cls-6" d="M217.99,311.6l-.39.42-33.59,33.88-.77.03,1.57-4.51c2.75-7.94,3.11-16.57,1.63-25.15-.21-1.2,1.02-2.43,2.16-2.43l11.01.06c5.33.03,10.37-.9,15.23-2.19l3.17-.11Z"/>
<path class="cls-1" d="M219.05,227.87l-.62.74-2.43-.46c-8.06-2.53-16.09-3.31-24.73-2.08-1.96.28-2.28-4.19-1.56-5.38.71-6.92-.38-13.29-1.92-19.6l-.09-1.09.8-2.07c-.14-.14-.26-.35-.42-.36.16.02.28.22.42.36l30.55,29.94Z"/> <path class="cls-3" d="M219.05,227.87l-.62.74-2.43-.46c-8.06-2.53-16.09-3.31-24.73-2.08-1.96.28-2.28-4.19-1.56-5.38.71-6.92-.38-13.29-1.92-19.6l-.09-1.09.8-2.07c-.14-.14-.26-.35-.42-.36.16.02.28.22.42.36l30.55,29.94Z"/>
<path class="cls-11" d="M101.08,269.43l.04,3.58-18.21,3.46-8.46,2.25c-.58.15-.9-.27-1.38-.27l2.44-3.67c-.82-2.73-4.33-4.52-4.66-7.18,2.83-.32,5.42,1.61,8.52,1.76l11.02.53,10.69-.46Z"/> <path class="cls-2" d="M101.08,269.43l.04,3.58-18.21,3.46-8.46,2.25c-.58.15-.9-.27-1.38-.27l2.44-3.67c-.82-2.73-4.33-4.52-4.66-7.18,2.83-.32,5.42,1.61,8.52,1.76l11.02.53,10.69-.46Z"/>
<path class="cls-2" d="M101.08,269.43l.14-17.59-2.21.86c-.67.26-.75-.52-1.58-1.28l-1.82-.27c-2.52-3.62,10.1-13.15,23.9-11.48,5.96.72,11.76,4.34,13.78,10.33s-1.45,11.51-6.06,15.48c7.92.96,14.11,6.22,15.99,13.65,1.56,6.15-1.18,12.46-5.44,17.11-9.67,10.56-26.68,10.76-40.19,6.04-1.04-.36.15-2.48.78-2.4l2.57.33c.61.08,1.03.52,1.29.89.15.22.49-.51.55-.77.11-.47-7.17-2.29-6.46-4.96.11-.41.64-1.28,1.26-1.11l4.07,1.09-.55-22.34-.04-3.58ZM124.8,255.68c1.58-3.1.42-5.63-2.19-7.08-3.93-2.19-8.3-2.63-13.25-1.34v16.49c0,.62.35,1.06.86,1.37.26.15.45-.22,1.12-.47,5.49-1.09,10.87-3.89,13.45-8.97ZM106.69,248.96l-1.41-.03.17,8.81c.23-.03.44-.47.56-.3l.68-8.48ZM129.95,257.42c.65-1.58.8-4.23,1.16-5.86-1.76-.16-.51-1.16-.52-1.51,0-.22-.65-.01-1.42-.09,1.09,3.53.8,7.12-1.99,10.11,1.03.81,1.77-.21,1.99-.75l.77-1.9ZM133.96,284.81c.89-6.65-3.22-11.63-9.46-12.87-5.07-1.01-9.78.28-14.97,1.72l.7,23.53c5.81,1,11.07-.12,16.57-2.3,3.82-1.51,6.61-6.02,7.16-10.08ZM105.85,278.54c-.96,2.12-1.03,4.41.28,6.27.22-1.99.88-3.84-.28-6.27ZM138.89,279.18h-1.19s.2,6.6.2,6.6c.2-.03.41-.45.51-.3.97-1.95.1-4,.47-6.3Z"/> <path class="cls-1" d="M101.08,269.43l.14-17.59-2.21.86c-.67.26-.75-.52-1.58-1.28l-1.82-.27c-2.52-3.62,10.1-13.15,23.9-11.48,5.96.72,11.76,4.34,13.78,10.33s-1.45,11.51-6.06,15.48c7.92.96,14.11,6.22,15.99,13.65,1.56,6.15-1.18,12.46-5.44,17.11-9.67,10.56-26.68,10.76-40.19,6.04-1.04-.36.15-2.48.78-2.4l2.57.33c.61.08,1.03.52,1.29.89.15.22.49-.51.55-.77.11-.47-7.17-2.29-6.46-4.96.11-.41.64-1.28,1.26-1.11l4.07,1.09-.55-22.34-.04-3.58ZM124.8,255.68c1.58-3.1.42-5.63-2.19-7.08-3.93-2.19-8.3-2.63-13.25-1.34v16.49c0,.62.35,1.06.86,1.37.26.15.45-.22,1.12-.47,5.49-1.09,10.87-3.89,13.45-8.97ZM106.69,248.96l-1.41-.03.17,8.81c.23-.03.44-.47.56-.3l.68-8.48ZM129.95,257.42c.65-1.58.8-4.23,1.16-5.86-1.76-.16-.51-1.16-.52-1.51,0-.22-.65-.01-1.42-.09,1.09,3.53.8,7.12-1.99,10.11,1.03.81,1.77-.21,1.99-.75l.77-1.9ZM133.96,284.81c.89-6.65-3.22-11.63-9.46-12.87-5.07-1.01-9.78.28-14.97,1.72l.7,23.53c5.81,1,11.07-.12,16.57-2.3,3.82-1.51,6.61-6.02,7.16-10.08ZM105.85,278.54c-.96,2.12-1.03,4.41.28,6.27.22-1.99.88-3.84-.28-6.27ZM138.89,279.18h-1.19s.2,6.6.2,6.6c.2-.03.41-.45.51-.3.97-1.95.1-4,.47-6.3Z"/>
<path class="cls-8" d="M185.54,293.84c3.32-.08,3.73-5.67,5.28-6.8-.13.1,2.68-.43,2.68.3,0,.32.14.92-.04,1.41-4.02,10.58-14.94,16.69-26.13,15.27-15.88-2.03-22.13-20.3-19.18-34.63,1.8-8.74,5.06-17.4,11.29-23.96,4.45-4.69,10.12-7.43,16.42-6.15,5.78,1.17,11.5,4.93,13.12,11.04.38,1.46.9,3.42-.63,4.33l-2.51-3.38c-.3,2.04.6,4.12-.33,5.64-4.24,4.25-1.78-5.98-8.6-8.12-11.98-3.75-22.45,16.87-18.87,32.85,1.02,4.54,2.77,8.58,6.15,11.09,3.68,2.73,8.49,3.43,12.68,1.81,8.32-3.22,9.32-11.28,11.7-9.93s-.59,5.41-3.02,9.23Z"/> <path class="cls-4" d="M185.54,293.84c3.32-.08,3.73-5.67,5.28-6.8-.13.1,2.68-.43,2.68.3,0,.32.14.92-.04,1.41-4.02,10.58-14.94,16.69-26.13,15.27-15.88-2.03-22.13-20.3-19.18-34.63,1.8-8.74,5.06-17.4,11.29-23.96,4.45-4.69,10.12-7.43,16.42-6.15,5.78,1.17,11.5,4.93,13.12,11.04.38,1.46.9,3.42-.63,4.33l-2.51-3.38c-.3,2.04.6,4.12-.33,5.64-4.24,4.25-1.78-5.98-8.6-8.12-11.98-3.75-22.45,16.87-18.87,32.85,1.02,4.54,2.77,8.58,6.15,11.09,3.68,2.73,8.49,3.43,12.68,1.81,8.32-3.22,9.32-11.28,11.7-9.93s-.59,5.41-3.02,9.23Z"/>
<path class="cls-16" d="M187.78,201.08c1.54,6.31,2.64,12.68,1.92,19.6-.72,1.2-.4,5.66,1.56,5.38,8.64-1.23,16.67-.45,24.73,2.08.38.2-.2.83-.18,1.42.02.44-.35-.31-1.09.02l-8.82-.23-3.85.57c-1.54-.27-2.98.32-4.27.46l-12.32,1.33c-1.29-.51-1.5-3.9-1.34-5.26l1.2-10.28c.57-4.84.93-9.7.5-14.87l1.96-.21Z"/> <path class="cls-2" d="M187.78,201.08c1.54,6.31,2.64,12.68,1.92,19.6-.72,1.2-.4,5.66,1.56,5.38,8.64-1.23,16.67-.45,24.73,2.08.38.2-.2.83-.18,1.42.02.44-.35-.31-1.09.02l-8.82-.23-3.85.57c-1.54-.27-2.98.32-4.27.46l-12.32,1.33c-1.29-.51-1.5-3.9-1.34-5.26l1.2-10.28c.57-4.84.93-9.7.5-14.87l1.96-.21Z"/>
<path class="cls-16" d="M200.05,310.31c2.64.19,5.6.09,8.32-.01l5.37-.21c.71-.03.79,1.17,1.08,1.62-4.86,1.29-9.9,2.22-15.23,2.19l-11.01-.06c-1.13,0-2.36,1.22-2.16,2.43,1.48,8.57,1.13,17.21-1.63,25.15-.76-.2-1.55-.15-2.22-.11l.17-3.08-.85-18.06c-.2-4.21.03-9.88,1.2-10.28l1.98-.67,14.97,1.08Z"/> <path class="cls-2" d="M200.05,310.31c2.64.19,5.6.09,8.32-.01l5.37-.21c.71-.03.79,1.17,1.08,1.62-4.86,1.29-9.9,2.22-15.23,2.19l-11.01-.06c-1.13,0-2.36,1.22-2.16,2.43,1.48,8.57,1.13,17.21-1.63,25.15-.76-.2-1.55-.15-2.22-.11l.17-3.08-.85-18.06c-.2-4.21.03-9.88,1.2-10.28l1.98-.67,14.97,1.08Z"/>
<path class="cls-5" d="M133.96,284.81c-.55,4.07-3.34,8.57-7.16,10.08-5.5,2.17-10.76,3.29-16.57,2.3l-.7-23.53c5.2-1.44,9.9-2.72,14.97-1.72,6.24,1.24,10.35,6.22,9.46,12.87Z"/> <path class="cls-6" d="M133.96,284.81c-.55,4.07-3.34,8.57-7.16,10.08-5.5,2.17-10.76,3.29-16.57,2.3l-.7-23.53c5.2-1.44,9.9-2.72,14.97-1.72,6.24,1.24,10.35,6.22,9.46,12.87Z"/>
<path class="cls-3" d="M124.8,255.68c-2.58,5.08-7.97,7.88-13.45,8.97-.67.25-.86.62-1.12.47-.51-.3-.87-.74-.87-1.37v-16.49c4.96-1.3,9.32-.85,13.25,1.34,2.61,1.45,3.76,3.98,2.19,7.08Z"/> <path class="cls-6" d="M124.8,255.68c-2.58,5.08-7.97,7.88-13.45,8.97-.67.25-.86.62-1.12.47-.51-.3-.87-.74-.87-1.37v-16.49c4.96-1.3,9.32-.85,13.25,1.34,2.61,1.45,3.76,3.98,2.19,7.08Z"/>
<path class="cls-4" d="M129.95,257.42l-.77,1.9c-.22.54-.96,1.56-1.99.75,2.79-3,3.08-6.58,1.99-10.11.77.08,1.42-.13,1.42.09,0,.36-1.24,1.36.52,1.51-.36,1.63-.51,4.27-1.16,5.86Z"/> <path class="cls-2" d="M129.95,257.42l-.77,1.9c-.22.54-.96,1.56-1.99.75,2.79-3,3.08-6.58,1.99-10.11.77.08,1.42-.13,1.42.09,0,.36-1.24,1.36.52,1.51-.36,1.63-.51,4.27-1.16,5.86Z"/>
<path class="cls-14" d="M106.69,248.96l-.68,8.48c-.13-.17-.33.27-.56.3l-.17-8.81,1.41.03Z"/> <path class="cls-6" d="M106.69,248.96l-.68,8.48c-.13-.17-.33.27-.56.3l-.17-8.81,1.41.03Z"/>
<path class="cls-12" d="M138.89,279.18c-.37,2.3.5,4.35-.47,6.3-.1-.15-.32.27-.51.3l-.2-6.6h1.19Z"/> <path class="cls-2" d="M138.89,279.18c-.37,2.3.5,4.35-.47,6.3-.1-.15-.32.27-.51.3l-.2-6.6h1.19Z"/>
<path class="cls-9" d="M105.85,278.54c1.16,2.43.51,4.28.28,6.27-1.31-1.86-1.24-4.15-.28-6.27Z"/> <path class="cls-2" d="M105.85,278.54c1.16,2.43.51,4.28.28,6.27-1.31-1.86-1.24-4.15-.28-6.27Z"/>
<path class="cls-7" d="M220.24,229.03l-.6-.14-1.21-.28-2.43-.46c-8.06-2.53-16.09-3.31-24.73-2.08-1.96.28-2.28-4.19-1.56-5.38.71-6.92-.38-13.29-1.92-19.6l-.09-1.09"/> <path class="cls-5" d="M220.24,229.03l-.6-.14-1.21-.28-2.43-.46c-8.06-2.53-16.09-3.31-24.73-2.08-1.96.28-2.28-4.19-1.56-5.38.71-6.92-.38-13.29-1.92-19.6l-.09-1.09"/>
<path class="cls-7" d="M76.87,236.81c.03-2.33-2.37-3.38-4.36-4.69l-.32-6.53c-5.46-9.7-.92-17.59-1.51-28.43l16.23,2.03,23.56-.02,16.48-1.23c2.97-.22,5.24-1.39,7.61.8,2.07,1.91,4.3,4.1,6.23,6.93l3.69-7.11,37.01-2.68c2.54-1.25,5.05-.41,6.58,1.69.16.02.28.22.42.36l30.55,29.94.79.78.4.39"/> <path class="cls-5" d="M76.87,236.81c.03-2.33-2.37-3.38-4.36-4.69l-.32-6.53c-5.46-9.7-.92-17.59-1.51-28.43l16.23,2.03,23.56-.02,16.48-1.23c2.97-.22,5.24-1.39,7.61.8,2.07,1.91,4.3,4.1,6.23,6.93l3.69-7.11,37.01-2.68c2.54-1.25,5.05-.41,6.58,1.69.16.02.28.22.42.36l30.55,29.94.79.78.4.39"/>
<path class="cls-7" d="M220.24,229.03l-.13.88-1.61,30.29-6.75,7.18,6.67,3.15,1.66,35.78c.09,1.99,0,4.05-1.49,5l-.49.39-.5.31-33.59,33.88-1.17,1.19"/> <path class="cls-5" d="M220.24,229.03l-.13.88-1.61,30.29-6.75,7.18,6.67,3.15,1.66,35.78c.09,1.99,0,4.05-1.49,5l-.49.39-.5.31-33.59,33.88-1.17,1.19"/>
<path class="cls-7" d="M182.84,347.08l-.74-.15-9.48-.44-13.17-.04c-5.51-.02-10.81.2-15.91-2.37-4.17,3.3-8.82,3.85-13.75,4.04l-11.57.44c-4.22.16-8-1.47-12.1-2.44l-9.23-2.18-2.37-2.53"/> <path class="cls-5" d="M182.84,347.08l-.74-.15-9.48-.44-13.17-.04c-5.51-.02-10.81.2-15.91-2.37-4.17,3.3-8.82,3.85-13.75,4.04l-11.57.44c-4.22.16-8-1.47-12.1-2.44l-9.23-2.18-2.37-2.53"/>
<path class="cls-7" d="M182.84,347.08l.4-1.16,1.57-4.51c2.75-7.94,3.11-16.57,1.63-25.15-.21-1.2,1.02-2.43,2.16-2.43l11.01.06c5.33.03,10.37-.9,15.23-2.19l3.17-.11"/> <path class="cls-5" d="M182.84,347.08l.4-1.16,1.57-4.51c2.75-7.94,3.11-16.57,1.63-25.15-.21-1.2,1.02-2.43,2.16-2.43l11.01.06c5.33.03,10.37-.9,15.23-2.19l3.17-.11"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -3,26 +3,14 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #e9c985; fill: #006d30;
} }
.cls-2 { .cls-2 {
fill: #3a160a; fill: #00873e;
} }
.cls-3 { .cls-3 {
fill: #3d180d;
}
.cls-4 {
fill: #cda662;
}
.cls-5 {
fill: #ddb973;
}
.cls-6 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
@@ -30,34 +18,30 @@
stroke-width: 2.75px; stroke-width: 2.75px;
} }
.cls-7 { .cls-4 {
fill: #dfbd72; fill: #3a160a;
} }
.cls-8 { .cls-5 {
fill: #947864; fill: #3d180d;
} }
.cls-9 { .cls-6 {
fill: #cfa862; fill: #00a04b;
}
.cls-10 {
fill: #f1dca4;
} }
</style> </style>
</defs> </defs>
<path class="cls-10" d="M153.66,195.96l-.46-.48-2.25.71,3.07,18.95.16,18.83c.02,1.82-1.11,3.58.04,5.52.86-1.54,1.21-3.1,1.51-4.81l4.63-26.09c.51-2.89.63-6.32-1.68-7.91,3.44-.33,1.92-4.68,4.63-6.02,1.06-.53,2.58-.34,3.85-.57l.57-.11.09,1.97c-1.23.02-2.43-.35-3.03.56,1.72-.21,4.69,1.64,3.78,3.07-1.78,2.8-2.55,5.21-1.44,8.53.2.59-.73,1.95-.05,2.92.42.6,1.37.66,2.45,1.35l.89,1.5c.22.37,1.97.27,1.95-.45-.06-2.39-4.42-4.98-2.14-5.98.89-.39,1.59-.33,2.08.47l3.78,6.21,3.2-1.66,6.99.65c-.45,5.85-2.86,9.72.48,15.56l8.02-1.38c4.46-.77,3.87,5.08,6.83,6.44-.37,2.19.28,4.05,1.64,6.45-6.86,3.73-8.55.88-9.78,1.54-2.84,1.52-2.36,6.78,0,8.07,2.28-5.08,5.7-2.99,8.83-5.88l3.1,3.95c.13.5-.52.62-.77.88-.49.54-1.76-.53-2.23-.28-1.18.63-.37,1.39-.08,1.77.39.5,2.4.95,4.35,1.17.61,1.74-1.58,2.73-2.4,3.52.4,2.18,3.63,1.16,5.22,1.12,1.87-.05,3.1,2.33,5.08,1.26l-.3,8.25c-7.48,2.34-14.03,3.49-21.14,4.16l-23.27,2.2c.09.33.06.66.03.83.7.61,1.85-.06,2.86-.16l30.5,2.09c2.57.18,5.12.05,7.54,1.41-6.47,3.24-11.29,0-12.25.87-.59.54-1.48,2.27-.13,2.72.91.3,1.87.35,2.3.69,1.2.95-1.2,5.83,1.12,6.74.78.3,2.4-.64,2.93.56.31.71.23,2.04-.94,2.69l-1.39,1.83c-1.39.83-1.28,1.3-1.07,2.69.24,1.57,1.51,1.36,3.53.89v3.28s4.12.29,4.12.29l.1,2.95c-4.9.84-9.59,2.46-12.77,6.65l-8.81-1.19c-7.44-1-2.19,11.29-2.46,19.21l-4.89-.16c-2.63-.09-6.35,0-7.58-2.4,1.86-2.75-1.3-5.62-.12-9.66-1.36-.25-2.17-1.48-3.52-1.43-2.58,1.83-5.92,2.68-6.55,5.79-.14.7,1.35,1.6.78,2.39-.88,1.22-5.4.29-5.27,3.28.14,3.32,3.47,1.65,4.91,3.68-1.55,1.93-3.69,4.09-5.51,5.41-.67.04-2.1-.08-2.75-.12l-7.79-.48-1.91-10.68-.87-20.54c-.02-.38.98-.89.38-1.07-.21-.06-.37-.3-.58-.52-.15-.16-.64.62-.67.95l-1.71,16.74c-.72,7.06-2.02,13.71-4.34,20.67-4.06.6-8.21-.87-12.16-2.67-.9-.41-2.41-.75-2.22-2.2.46-.15,1.4-.34,1.49-.82.06-.36.32-1.32-.43-1.33l-9.76-.09c-.43,0-.7-1.15-.36-1.47.61-.58,1.76-.61,2.03-1.46.31-.96.25-1.75.13-3.16l-14.56.05c-3.39.01-6.59.16-10.04-.64l12.59-14.11c-.78-.51-1.89.32-2.63,1.01-4.11,3.83-8.08,7.52-13.71,8.92.38-1.11,1.48-1.61,2.04-2.49l-1.39-4.29c-2.64.06-2.8-2.85-3.9-3.74-2.66-2.14-5.75-3.87-5.86-7.47-.13-4.69,5.58-4.68,4.87-7.46-1.89-.09-2.79-.22-3.53-1.67-2.76-5.4-5.55-10.74-6.13-16.95l.26-3.84c1.78-1.26,3.89-1.22,6.04-1.61l19.34-3.5c-7.89-1.44-15.42-1.63-22.88-4.1l-3.59-.27c-1.87-2.75-1.64-5.92-2.07-9l-1.2-8.62-.43-6.7c-.53-8.33-.99-6.87,3.53-10.65l3.35-2.87.41.38-.41-.38-6.25-5.77,1.99-24.45,29.01,2.92c.3,2.58-.26,4.92.22,7,.52-.09,1.76-.17,2.02.58.15.44.4.77.33,1.31l3.48,3.71c1.73,1.85,4.57,3.61,5.86,5.97,1.68,3.06,3.62,8.54,4.74,8.09.28-.11.94-.29,1.08-1.02.16-.85-.78-2.14-.85-3.37l-.49-9.68c-1.06-2.47-3.15-4.98-2.29-7.94l7.42-2.99.74-.79c.25-.26.12-.53-.1-.59-.25-.07-.86-.46-1.49.09l-.35-1.99,28.3-2.23c1.58-.12,3.1-.91,3.9,1l.46.48ZM186.99,284.59c.05-.8-.21-2.93-.89-3.46-1.03-.8-2.09-.08-2.67.96-2.92,5.19-8.02,9.43-14.12,9.6-10.92.3-16.23-12.43-14.77-24.11,1.5-12,9.42-26.8,19.63-23.04,6.4,2.36,4.16,10.81,8.14,8.64,1.03-.56,1.08-3.75.59-6.65,1.56,1.07,1.13,2.55,1.92,3.73.35.51,1.07.12,1.41-.26,1.54-1.69-1.42-11.67-11.3-14.47-18.44-5.21-31.37,21.26-30.31,39.87.46,8.02,4.55,18.71,12.73,22.74,12.45,6.13,27.46.08,33.05-12.33.29-.63.45-1.54.17-1.99s-.96-1.1-1.65-.81c-.59.25-1.21.83-1.92,1.58ZM101.07,245.27c-.97-.28-1.36,2.51-.27,3.04.84.41,1.82.3,3.43.02l-.02,15.84.52,28.42c.02,1.26-3.05.8-3.99,2.17-.37.54-.43,1.53.14,1.9.48.31,1.1.34,2.1.38l1.74.07c.21,0-.06,1.21-.78.9-1.53-.66-2.37,1.04-2.06,2.1.54,1.85,3.63,1.46,6.98.54.54.72.85,2.18,1.64,2.22.74.04,1.45-.09,1.73-.57.43-.71.32-1.49.26-2.52l13.46-1.99,15.07-1.37c.5-.05,1.21-.93,1.18-1.32-.02-.33-.33-.94-.83-.93l-6.64.11v-.92s7.21-.58,7.21-.58c.67-.05,1.07-1,1.09-1.41s-.74-1.12-1.09-1.32l-2.18-1.26c-7.01-.15-13.57.87-20.29,1.77l-6.33.85-.28-18.72c1.4-.01,2.1-.11,2.8-.21l14.99-2.18c1.84-.27,2.77-2.22,2.67-4.08-1.51-.62-2.4-.02-3.68-.25.35-1.33,1.22-1.8.93-2.28-.46-.75-.97-1.35-1.81-1.22l-16.69,2.55v-17.6c8.76-1.96,17.15-2.99,25.82-3.25.13,0,3.38-2.78,2.38-4.14-.56-.77-2.05-.63-3.18-.4-.1-1.28.99-1.47.68-1.91-.6-.86-1.04-1.38-1.97-1.52-8.39.05-16.34,1.7-24.78,2.69-.47-.45-.8-1.64-1.29-1.61-.68.04-.97.86-1.59,1.72-.59-.64-1.13-1.26-1.75-1.19-.85.11-1.69,0-1.57,1.15.13,1.23-2,1.2-2.96,1.52l-2.84.93c-1.36.44-1.02,1.83-.51,2.86l5.73-.22-1.66.49c-.18.57-.19,1.1-1.51.72Z"/> <path class="cls-6" d="M153.66,195.96l-.46-.48-2.25.71,3.07,18.95.16,18.83c.02,1.82-1.11,3.58.04,5.52.86-1.54,1.21-3.1,1.51-4.81l4.63-26.09c.51-2.89.63-6.32-1.68-7.91,3.44-.33,1.92-4.68,4.63-6.02,1.06-.53,2.58-.34,3.85-.57l.57-.11.09,1.97c-1.23.02-2.43-.35-3.03.56,1.72-.21,4.69,1.64,3.78,3.07-1.78,2.8-2.55,5.21-1.44,8.53.2.59-.73,1.95-.05,2.92.42.6,1.37.66,2.45,1.35l.89,1.5c.22.37,1.97.27,1.95-.45-.06-2.39-4.42-4.98-2.14-5.98.89-.39,1.59-.33,2.08.47l3.78,6.21,3.2-1.66,6.99.65c-.45,5.85-2.86,9.72.48,15.56l8.02-1.38c4.46-.77,3.87,5.08,6.83,6.44-.37,2.19.28,4.05,1.64,6.45-6.86,3.73-8.55.88-9.78,1.54-2.84,1.52-2.36,6.78,0,8.07,2.28-5.08,5.7-2.99,8.83-5.88l3.1,3.95c.13.5-.52.62-.77.88-.49.54-1.76-.53-2.23-.28-1.18.63-.37,1.39-.08,1.77.39.5,2.4.95,4.35,1.17.61,1.74-1.58,2.73-2.4,3.52.4,2.18,3.63,1.16,5.22,1.12,1.87-.05,3.1,2.33,5.08,1.26l-.3,8.25c-7.48,2.34-14.03,3.49-21.14,4.16l-23.27,2.2c.09.33.06.66.03.83.7.61,1.85-.06,2.86-.16l30.5,2.09c2.57.18,5.12.05,7.54,1.41-6.47,3.24-11.29,0-12.25.87-.59.54-1.48,2.27-.13,2.72.91.3,1.87.35,2.3.69,1.2.95-1.2,5.83,1.12,6.74.78.3,2.4-.64,2.93.56.31.71.23,2.04-.94,2.69l-1.39,1.83c-1.39.83-1.28,1.3-1.07,2.69.24,1.57,1.51,1.36,3.53.89v3.28s4.12.29,4.12.29l.1,2.95c-4.9.84-9.59,2.46-12.77,6.65l-8.81-1.19c-7.44-1-2.19,11.29-2.46,19.21l-4.89-.16c-2.63-.09-6.35,0-7.58-2.4,1.86-2.75-1.3-5.62-.12-9.66-1.36-.25-2.17-1.48-3.52-1.43-2.58,1.83-5.92,2.68-6.55,5.79-.14.7,1.35,1.6.78,2.39-.88,1.22-5.4.29-5.27,3.28.14,3.32,3.47,1.65,4.91,3.68-1.55,1.93-3.69,4.09-5.51,5.41-.67.04-2.1-.08-2.75-.12l-7.79-.48-1.91-10.68-.87-20.54c-.02-.38.98-.89.38-1.07-.21-.06-.37-.3-.58-.52-.15-.16-.64.62-.67.95l-1.71,16.74c-.72,7.06-2.02,13.71-4.34,20.67-4.06.6-8.21-.87-12.16-2.67-.9-.41-2.41-.75-2.22-2.2.46-.15,1.4-.34,1.49-.82.06-.36.32-1.32-.43-1.33l-9.76-.09c-.43,0-.7-1.15-.36-1.47.61-.58,1.76-.61,2.03-1.46.31-.96.25-1.75.13-3.16l-14.56.05c-3.39.01-6.59.16-10.04-.64l12.59-14.11c-.78-.51-1.89.32-2.63,1.01-4.11,3.83-8.08,7.52-13.71,8.92.38-1.11,1.48-1.61,2.04-2.49l-1.39-4.29c-2.64.06-2.8-2.85-3.9-3.74-2.66-2.14-5.75-3.87-5.86-7.47-.13-4.69,5.58-4.68,4.87-7.46-1.89-.09-2.79-.22-3.53-1.67-2.76-5.4-5.55-10.74-6.13-16.95l.26-3.84c1.78-1.26,3.89-1.22,6.04-1.61l19.34-3.5c-7.89-1.44-15.42-1.63-22.88-4.1l-3.59-.27c-1.87-2.75-1.64-5.92-2.07-9l-1.2-8.62-.43-6.7c-.53-8.33-.99-6.87,3.53-10.65l3.35-2.87.41.38-.41-.38-6.25-5.77,1.99-24.45,29.01,2.92c.3,2.58-.26,4.92.22,7,.52-.09,1.76-.17,2.02.58.15.44.4.77.33,1.31l3.48,3.71c1.73,1.85,4.57,3.61,5.86,5.97,1.68,3.06,3.62,8.54,4.74,8.09.28-.11.94-.29,1.08-1.02.16-.85-.78-2.14-.85-3.37l-.49-9.68c-1.06-2.47-3.15-4.98-2.29-7.94l7.42-2.99.74-.79c.25-.26.12-.53-.1-.59-.25-.07-.86-.46-1.49.09l-.35-1.99,28.3-2.23c1.58-.12,3.1-.91,3.9,1l.46.48ZM186.99,284.59c.05-.8-.21-2.93-.89-3.46-1.03-.8-2.09-.08-2.67.96-2.92,5.19-8.02,9.43-14.12,9.6-10.92.3-16.23-12.43-14.77-24.11,1.5-12,9.42-26.8,19.63-23.04,6.4,2.36,4.16,10.81,8.14,8.64,1.03-.56,1.08-3.75.59-6.65,1.56,1.07,1.13,2.55,1.92,3.73.35.51,1.07.12,1.41-.26,1.54-1.69-1.42-11.67-11.3-14.47-18.44-5.21-31.37,21.26-30.31,39.87.46,8.02,4.55,18.71,12.73,22.74,12.45,6.13,27.46.08,33.05-12.33.29-.63.45-1.54.17-1.99s-.96-1.1-1.65-.81c-.59.25-1.21.83-1.92,1.58ZM101.07,245.27c-.97-.28-1.36,2.51-.27,3.04.84.41,1.82.3,3.43.02l-.02,15.84.52,28.42c.02,1.26-3.05.8-3.99,2.17-.37.54-.43,1.53.14,1.9.48.31,1.1.34,2.1.38l1.74.07c.21,0-.06,1.21-.78.9-1.53-.66-2.37,1.04-2.06,2.1.54,1.85,3.63,1.46,6.98.54.54.72.85,2.18,1.64,2.22.74.04,1.45-.09,1.73-.57.43-.71.32-1.49.26-2.52l13.46-1.99,15.07-1.37c.5-.05,1.21-.93,1.18-1.32-.02-.33-.33-.94-.83-.93l-6.64.11v-.92s7.21-.58,7.21-.58c.67-.05,1.07-1,1.09-1.41s-.74-1.12-1.09-1.32l-2.18-1.26c-7.01-.15-13.57.87-20.29,1.77l-6.33.85-.28-18.72c1.4-.01,2.1-.11,2.8-.21l14.99-2.18c1.84-.27,2.77-2.22,2.67-4.08-1.51-.62-2.4-.02-3.68-.25.35-1.33,1.22-1.8.93-2.28-.46-.75-.97-1.35-1.81-1.22l-16.69,2.55v-17.6c8.76-1.96,17.15-2.99,25.82-3.25.13,0,3.38-2.78,2.38-4.14-.56-.77-2.05-.63-3.18-.4-.1-1.28.99-1.47.68-1.91-.6-.86-1.04-1.38-1.97-1.52-8.39.05-16.34,1.7-24.78,2.69-.47-.45-.8-1.64-1.29-1.61-.68.04-.97.86-1.59,1.72-.59-.64-1.13-1.26-1.75-1.19-.85.11-1.69,0-1.57,1.15.13,1.23-2,1.2-2.96,1.52l-2.84.93c-1.36.44-1.02,1.83-.51,2.86l5.73-.22-1.66.49c-.18.57-.19,1.1-1.51.72Z"/>
<path class="cls-7" d="M189.09,192.96c.05.24.06.17.06-.01l.12.59,1.48,13.12.05,15.42c9.75-1.12,18.49.18,27.17,2.92l.66-.85,1.15,1.22.02.65-.12,6.48c-.06,3.28-.1,6.15-.34,9.4l-1.49,20.28c-.14,1.92.53,4.27-.65,6.12l-5.41,3.97,6.02,6.36,1.49,21.56c.37,5.41,2.59,8.94-3.33,12.4.34.27.29,1.3.03,1.53s-.16.34-.97.66c-.37,1.66-2.22,1.95-3.2,2.95l-20.55,20.92c-1.09,1.11-1.62,2.3-2.99,2.91-1.44.64-2.89,1.38-4.34,2.88l-11.83-.23-21.18-.57c-2.81-.08-5.69-.28-7.16-3.49l-2.05-.16c-3.58,7.25-10.93,5.75-18.03,6.21-3.56.23-7.24,1.2-10.75.28l-13.96-3.64c-3.32-.87-4.74-4.5-6.28-4.44-1.22.05-1.49,1.13-2.18,1.92l-3.06,3.53c-1.81,2.08-4.75,1.11-7.01.74l-7.91-1.31c-.88-.14-1.36-1.64-2.36-2.17.88-6.23-.67-11.82-2.56-17.57-.32-.99.84-5.27,1.01-8.28l.29-4.88.53-6.71.52-27.67,1.82-3.79c.68.02,1.42.1,2.03.75l-.26,3.84c.58,6.2,3.37,11.55,6.13,16.95.74,1.45,1.64,1.58,3.53,1.67.71,2.78-5.01,2.77-4.87,7.46.1,3.59,3.19,5.33,5.86,7.47,1.1.88,1.26,3.8,3.9,3.74l1.39,4.29c-.55.88-1.66,1.38-2.04,2.49,5.64-1.4,9.6-5.08,13.71-8.92.74-.69,1.84-1.52,2.63-1.01l-12.59,14.11c3.44.8,6.65.65,10.04.64l14.56-.05c.11,1.41.17,2.2-.13,3.16-.27.85-1.42.88-2.03,1.46-.33.32-.07,1.46.36,1.47l9.76.09c.76,0,.5.97.43,1.33-.08.47-1.03.66-1.49.82-.2,1.44,1.31,1.79,2.22,2.2,3.95,1.79,8.1,3.27,12.16,2.67,2.32-6.96,3.62-13.61,4.34-20.67l1.71-16.74c.03-.33.52-1.11.67-.95.21.23.36.46.58.52.6.17-.4.69-.38,1.07l.87,20.54,1.91,10.68,7.79.48c.64.04,2.08.16,2.75.12,1.82-1.32,3.96-3.47,5.51-5.41-1.45-2.03-4.77-.36-4.91-3.68-.13-2.98,4.38-2.06,5.27-3.28.57-.79-.92-1.69-.78-2.39.63-3.11,3.98-3.96,6.55-5.79,1.36-.05,2.16,1.19,3.52,1.43-1.18,4.04,1.98,6.9.12,9.66,1.23,2.39,4.95,2.31,7.58,2.4l4.89.16c.28-7.91-4.98-20.21,2.46-19.21l8.81,1.19c3.18-4.19,7.87-5.81,12.77-6.65l-.1-2.95-4.12-.29v-3.28c-2.02.46-3.28.68-3.52-.89-.21-1.38-.32-1.86,1.07-2.69l1.39-1.83c1.17-.65,1.25-1.97.94-2.69-.53-1.2-2.15-.26-2.93-.56-2.32-.9.09-5.78-1.12-6.74-.43-.34-1.39-.39-2.3-.69-1.35-.45-.46-2.18.13-2.72.95-.87,5.78,2.37,12.25-.87-2.42-1.36-4.97-1.23-7.54-1.41l-30.5-2.09c-1,.11-2.15.77-2.86.16.03-.17.07-.51-.03-.83l23.27-2.2c7.11-.67,13.66-1.83,21.14-4.16l.3-8.25c-1.98,1.07-3.21-1.31-5.08-1.26-1.6.04-4.83,1.06-5.22-1.12.82-.79,3.01-1.78,2.4-3.52-1.94-.22-3.96-.67-4.35-1.17-.3-.39-1.1-1.14.08-1.77.47-.25,1.75.82,2.23.28.24-.27.9-.39.77-.88l-3.1-3.95c-3.13,2.9-6.55.8-8.83,5.88-2.36-1.3-2.84-6.55,0-8.07,1.24-.66,2.93,2.18,9.78-1.54-1.36-2.4-2.01-4.25-1.64-6.45-2.96-1.36-2.37-7.21-6.83-6.44l-8.02,1.38c-3.34-5.83-.94-9.71-.48-15.56l-6.99-.65-3.2,1.66-3.78-6.21c-.48-.79-1.18-.86-2.08-.47-2.29,1,2.08,3.59,2.14,5.98.02.72-1.73.82-1.95.45l-.89-1.5c-1.07-.69-2.02-.75-2.45-1.35-.68-.97.25-2.32.05-2.92-1.11-3.33-.34-5.73,1.44-8.53.91-1.43-2.05-3.28-3.78-3.07.6-.91,1.8-.54,3.03-.56l-.09-1.97-.57.11.57-.11,13.46-1.04c2.81-.22,5.44-.4,7.96,0,0,.25,0,.18-.06.01Z"/> <path class="cls-2" d="M189.09,192.96c.05.24.06.17.06-.01l.12.59,1.48,13.12.05,15.42c9.75-1.12,18.49.18,27.17,2.92l.66-.85,1.15,1.22.02.65-.12,6.48c-.06,3.28-.1,6.15-.34,9.4l-1.49,20.28c-.14,1.92.53,4.27-.65,6.12l-5.41,3.97,6.02,6.36,1.49,21.56c.37,5.41,2.59,8.94-3.33,12.4.34.27.29,1.3.03,1.53s-.16.34-.97.66c-.37,1.66-2.22,1.95-3.2,2.95l-20.55,20.92c-1.09,1.11-1.62,2.3-2.99,2.91-1.44.64-2.89,1.38-4.34,2.88l-11.83-.23-21.18-.57c-2.81-.08-5.69-.28-7.16-3.49l-2.05-.16c-3.58,7.25-10.93,5.75-18.03,6.21-3.56.23-7.24,1.2-10.75.28l-13.96-3.64c-3.32-.87-4.74-4.5-6.28-4.44-1.22.05-1.49,1.13-2.18,1.92l-3.06,3.53c-1.81,2.08-4.75,1.11-7.01.74l-7.91-1.31c-.88-.14-1.36-1.64-2.36-2.17.88-6.23-.67-11.82-2.56-17.57-.32-.99.84-5.27,1.01-8.28l.29-4.88.53-6.71.52-27.67,1.82-3.79c.68.02,1.42.1,2.03.75l-.26,3.84c.58,6.2,3.37,11.55,6.13,16.95.74,1.45,1.64,1.58,3.53,1.67.71,2.78-5.01,2.77-4.87,7.46.1,3.59,3.19,5.33,5.86,7.47,1.1.88,1.26,3.8,3.9,3.74l1.39,4.29c-.55.88-1.66,1.38-2.04,2.49,5.64-1.4,9.6-5.08,13.71-8.92.74-.69,1.84-1.52,2.63-1.01l-12.59,14.11c3.44.8,6.65.65,10.04.64l14.56-.05c.11,1.41.17,2.2-.13,3.16-.27.85-1.42.88-2.03,1.46-.33.32-.07,1.46.36,1.47l9.76.09c.76,0,.5.97.43,1.33-.08.47-1.03.66-1.49.82-.2,1.44,1.31,1.79,2.22,2.2,3.95,1.79,8.1,3.27,12.16,2.67,2.32-6.96,3.62-13.61,4.34-20.67l1.71-16.74c.03-.33.52-1.11.67-.95.21.23.36.46.58.52.6.17-.4.69-.38,1.07l.87,20.54,1.91,10.68,7.79.48c.64.04,2.08.16,2.75.12,1.82-1.32,3.96-3.47,5.51-5.41-1.45-2.03-4.77-.36-4.91-3.68-.13-2.98,4.38-2.06,5.27-3.28.57-.79-.92-1.69-.78-2.39.63-3.11,3.98-3.96,6.55-5.79,1.36-.05,2.16,1.19,3.52,1.43-1.18,4.04,1.98,6.9.12,9.66,1.23,2.39,4.95,2.31,7.58,2.4l4.89.16c.28-7.91-4.98-20.21,2.46-19.21l8.81,1.19c3.18-4.19,7.87-5.81,12.77-6.65l-.1-2.95-4.12-.29v-3.28c-2.02.46-3.28.68-3.52-.89-.21-1.38-.32-1.86,1.07-2.69l1.39-1.83c1.17-.65,1.25-1.97.94-2.69-.53-1.2-2.15-.26-2.93-.56-2.32-.9.09-5.78-1.12-6.74-.43-.34-1.39-.39-2.3-.69-1.35-.45-.46-2.18.13-2.72.95-.87,5.78,2.37,12.25-.87-2.42-1.36-4.97-1.23-7.54-1.41l-30.5-2.09c-1,.11-2.15.77-2.86.16.03-.17.07-.51-.03-.83l23.27-2.2c7.11-.67,13.66-1.83,21.14-4.16l.3-8.25c-1.98,1.07-3.21-1.31-5.08-1.26-1.6.04-4.83,1.06-5.22-1.12.82-.79,3.01-1.78,2.4-3.52-1.94-.22-3.96-.67-4.35-1.17-.3-.39-1.1-1.14.08-1.77.47-.25,1.75.82,2.23.28.24-.27.9-.39.77-.88l-3.1-3.95c-3.13,2.9-6.55.8-8.83,5.88-2.36-1.3-2.84-6.55,0-8.07,1.24-.66,2.93,2.18,9.78-1.54-1.36-2.4-2.01-4.25-1.64-6.45-2.96-1.36-2.37-7.21-6.83-6.44l-8.02,1.38c-3.34-5.83-.94-9.71-.48-15.56l-6.99-.65-3.2,1.66-3.78-6.21c-.48-.79-1.18-.86-2.08-.47-2.29,1,2.08,3.59,2.14,5.98.02.72-1.73.82-1.95.45l-.89-1.5c-1.07-.69-2.02-.75-2.45-1.35-.68-.97.25-2.32.05-2.92-1.11-3.33-.34-5.73,1.44-8.53.91-1.43-2.05-3.28-3.78-3.07.6-.91,1.8-.54,3.03-.56l-.09-1.97-.57.11.57-.11,13.46-1.04c2.81-.22,5.44-.4,7.96,0,0,.25,0,.18-.06.01Z"/>
<path class="cls-1" d="M189.09,192.96c.13-.05.29.17.44.4l29.1,30.8-.66.85c-8.68-2.74-17.43-4.04-27.17-2.92l-.05-15.42-1.48-13.12-.12-.59c0,.18-.01.25-.06.01Z"/> <path class="cls-6" d="M189.09,192.96c.13-.05.29.17.44.4l29.1,30.8-.66.85c-8.68-2.74-17.43-4.04-27.17-2.92l-.05-15.42-1.48-13.12-.12-.59c0,.18-.01.25-.06.01Z"/>
<path class="cls-5" d="M121,196.71l.35,1.99c.63-.55,1.25-.16,1.49-.09.22.06.34.33.1.59l-.74.79-7.42,2.99c-.86,2.97,1.23,5.48,2.29,7.94l.49,9.68c.06,1.23,1,2.52.85,3.37-.14.73-.8.91-1.08,1.02-1.11.45-3.05-5.03-4.74-8.09-1.29-2.35-4.13-4.12-5.86-5.97l-3.48-3.71c.07-.54-.17-.88-.33-1.31-.26-.75-1.5-.67-2.02-.58-.48-2.08.07-4.42-.22-7,3.74.38,6.19,6.49,8.76,5.54,2.12-.78.47-6.55,7.47-6.94l4.09-.23Z"/> <path class="cls-2" d="M121,196.71l.35,1.99c.63-.55,1.25-.16,1.49-.09.22.06.34.33.1.59l-.74.79-7.42,2.99c-.86,2.97,1.23,5.48,2.29,7.94l.49,9.68c.06,1.23,1,2.52.85,3.37-.14.73-.8.91-1.08,1.02-1.11.45-3.05-5.03-4.74-8.09-1.29-2.35-4.13-4.12-5.86-5.97l-3.48-3.71c.07-.54-.17-.88-.33-1.31-.26-.75-1.5-.67-2.02-.58-.48-2.08.07-4.42-.22-7,3.74.38,6.19,6.49,8.76,5.54,2.12-.78.47-6.55,7.47-6.94l4.09-.23Z"/>
<path class="cls-9" d="M153.66,195.96c1.59,1.69,3.16,3.44,5.02,4.72,2.32,1.59,2.2,5.02,1.68,7.91l-4.63,26.09c-.3,1.71-.65,3.27-1.51,4.81-1.16-1.94-.03-3.7-.04-5.52l-.16-18.83-3.07-18.95,2.25-.71.46.48Z"/> <path class="cls-1" d="M153.66,195.96c1.59,1.69,3.16,3.44,5.02,4.72,2.32,1.59,2.2,5.02,1.68,7.91l-4.63,26.09c-.3,1.71-.65,3.27-1.51,4.81-1.16-1.94-.03-3.7-.04-5.52l-.16-18.83-3.07-18.95,2.25-.71.46.48Z"/>
<path class="cls-4" d="M73.83,272.96c-.61-.65-1.35-.72-2.03-.75l3.58-4.84-2.64-3.89,3.59.27c7.46,2.46,14.99,2.66,22.88,4.1l-19.34,3.5c-2.15.39-4.26.34-6.04,1.61Z"/> <path class="cls-1" d="M73.83,272.96c-.61-.65-1.35-.72-2.03-.75l3.58-4.84-2.64-3.89,3.59.27c7.46,2.46,14.99,2.66,22.88,4.1l-19.34,3.5c-2.15.39-4.26.34-6.04,1.61Z"/>
<path class="cls-2" d="M101.07,245.27c1.32.37,1.33-.16,1.51-.72l1.66-.49-5.73.22c-.51-1.03-.85-2.41.51-2.86l2.84-.93c.97-.32,3.09-.29,2.96-1.52-.12-1.15.72-1.05,1.57-1.15.61-.08,1.16.55,1.75,1.19.62-.86.91-1.67,1.59-1.72.49-.03.82,1.16,1.29,1.61,8.43-1,16.39-2.64,24.78-2.69.93.14,1.37.67,1.97,1.52.31.44-.79.63-.68,1.91,1.13-.23,2.61-.37,3.18.4,1,1.35-2.25,4.13-2.38,4.14-8.67.26-17.05,1.29-25.81,3.25v17.6s16.68-2.55,16.68-2.55c.84-.13,1.36.47,1.81,1.22.29.47-.58.95-.93,2.28,1.28.22,2.17-.38,3.68.25.1,1.86-.83,3.81-2.67,4.08l-14.99,2.18c-.7.1-1.4.2-2.8.21l.28,18.72,6.33-.85c6.72-.9,13.28-1.91,20.29-1.77l2.18,1.26c.35.2,1.11.91,1.09,1.32s-.42,1.36-1.09,1.41l-7.21.58v.92s6.65-.11,6.65-.11c.5,0,.81.59.83.93.02.4-.69,1.28-1.18,1.32l-15.07,1.37-13.46,1.99c.07,1.03.17,1.8-.26,2.52-.29.48-.99.61-1.73.57-.79-.04-1.1-1.5-1.64-2.22-3.36.92-6.44,1.31-6.98-.54-.31-1.06.53-2.77,2.06-2.1.72.31,1-.89.78-.9l-1.74-.07c-.99-.04-1.62-.08-2.1-.38-.57-.37-.51-1.36-.14-1.9.94-1.37,4.01-.91,3.99-2.17l-.52-28.42.02-15.84c-1.61.28-2.59.39-3.43-.02-1.08-.53-.7-3.32.27-3.04Z"/> <path class="cls-4" d="M101.07,245.27c1.32.37,1.33-.16,1.51-.72l1.66-.49-5.73.22c-.51-1.03-.85-2.41.51-2.86l2.84-.93c.97-.32,3.09-.29,2.96-1.52-.12-1.15.72-1.05,1.57-1.15.61-.08,1.16.55,1.75,1.19.62-.86.91-1.67,1.59-1.72.49-.03.82,1.16,1.29,1.61,8.43-1,16.39-2.64,24.78-2.69.93.14,1.37.67,1.97,1.52.31.44-.79.63-.68,1.91,1.13-.23,2.61-.37,3.18.4,1,1.35-2.25,4.13-2.38,4.14-8.67.26-17.05,1.29-25.81,3.25v17.6s16.68-2.55,16.68-2.55c.84-.13,1.36.47,1.81,1.22.29.47-.58.95-.93,2.28,1.28.22,2.17-.38,3.68.25.1,1.86-.83,3.81-2.67,4.08l-14.99,2.18c-.7.1-1.4.2-2.8.21l.28,18.72,6.33-.85c6.72-.9,13.28-1.91,20.29-1.77l2.18,1.26c.35.2,1.11.91,1.09,1.32s-.42,1.36-1.09,1.41l-7.21.58v.92s6.65-.11,6.65-.11c.5,0,.81.59.83.93.02.4-.69,1.28-1.18,1.32l-15.07,1.37-13.46,1.99c.07,1.03.17,1.8-.26,2.52-.29.48-.99.61-1.73.57-.79-.04-1.1-1.5-1.64-2.22-3.36.92-6.44,1.31-6.98-.54-.31-1.06.53-2.77,2.06-2.1.72.31,1-.89.78-.9l-1.74-.07c-.99-.04-1.62-.08-2.1-.38-.57-.37-.51-1.36-.14-1.9.94-1.37,4.01-.91,3.99-2.17l-.52-28.42.02-15.84c-1.61.28-2.59.39-3.43-.02-1.08-.53-.7-3.32.27-3.04Z"/>
<path class="cls-3" d="M186.99,284.59c.71-.75,1.33-1.33,1.92-1.58.69-.29,1.36.35,1.65.81s.12,1.36-.17,1.99c-5.59,12.41-20.6,18.47-33.05,12.33-8.18-4.03-12.28-14.72-12.73-22.74-1.06-18.61,11.87-45.08,30.31-39.87,9.88,2.79,12.84,12.78,11.3,14.47-.34.38-1.07.77-1.41.26-.8-1.18-.36-2.66-1.92-3.73.49,2.89.44,6.09-.59,6.65-3.98,2.17-1.74-6.28-8.14-8.64-10.22-3.76-18.14,11.04-19.63,23.04-1.45,11.68,3.85,24.41,14.77,24.11,6.1-.17,11.2-4.41,14.12-9.6.59-1.04,1.65-1.76,2.67-.96.68.53.94,2.66.89,3.46-.24,0-.82-.26-1.15.32l-2.56,4.45.51.54c1.45-1.23,3.14-4.22,3.13-4.06l.07-1.24Z"/> <path class="cls-5" d="M186.99,284.59c.71-.75,1.33-1.33,1.92-1.58.69-.29,1.36.35,1.65.81s.12,1.36-.17,1.99c-5.59,12.41-20.6,18.47-33.05,12.33-8.18-4.03-12.28-14.72-12.73-22.74-1.06-18.61,11.87-45.08,30.31-39.87,9.88,2.79,12.84,12.78,11.3,14.47-.34.38-1.07.77-1.41.26-.8-1.18-.36-2.66-1.92-3.73.49,2.89.44,6.09-.59,6.65-3.98,2.17-1.74-6.28-8.14-8.64-10.22-3.76-18.14,11.04-19.63,23.04-1.45,11.68,3.85,24.41,14.77,24.11,6.1-.17,11.2-4.41,14.12-9.6.59-1.04,1.65-1.76,2.67-.96.68.53.94,2.66.89,3.46-.24,0-.82-.26-1.15.32l-2.56,4.45.51.54c1.45-1.23,3.14-4.22,3.13-4.06l.07-1.24Z"/>
<path class="cls-8" d="M186.99,284.59l-.07,1.24c0-.16-1.68,2.83-3.13,4.06l-.51-.54,2.56-4.45c.33-.57.91-.32,1.15-.32Z"/> <path class="cls-1" d="M186.99,284.59l-.07,1.24c0-.16-1.68,2.83-3.13,4.06l-.51-.54,2.56-4.45c.33-.57.91-.32,1.15-.32Z"/>
<path class="cls-6" d="M219.78,225.37l-1.81-.37c-8.68-2.74-17.43-4.04-27.17-2.92l-.05-15.42-1.48-13.12-.19-.58"/> <path class="cls-3" d="M219.78,225.37l-1.81-.37c-8.68-2.74-17.43-4.04-27.17-2.92l-.05-15.42-1.48-13.12-.19-.58"/>
<path class="cls-6" d="M219.78,225.37l.02.65-.12,6.48c-.06,3.28-.1,6.15-.34,9.4l-1.49,20.28c-.14,1.92.53,4.27-.65,6.12l-5.41,3.97,6.02,6.36,1.49,21.56c.37,5.41,2.59,8.94-3.33,12.4-7.99.64-14.33,1.69-23.27.55-5.4-.69-.74,10.29-4.65,22.7-.68,2.15-.83,4.01.26,5.73"/> <path class="cls-3" d="M219.78,225.37l.02.65-.12,6.48c-.06,3.28-.1,6.15-.34,9.4l-1.49,20.28c-.14,1.92.53,4.27-.65,6.12l-5.41,3.97,6.02,6.36,1.49,21.56c.37,5.41,2.59,8.94-3.33,12.4-7.99.64-14.33,1.69-23.27.55-5.4-.69-.74,10.29-4.65,22.7-.68,2.15-.83,4.01.26,5.73"/>
<path class="cls-6" d="M189.15,192.95c-2.52-.41-5.15-.22-7.96,0l-13.46,1.04-.57.11c-1.27.24-2.79.05-3.85.57-2.71,1.35-1.19,5.7-4.63,6.02-1.86-1.28-3.42-3.03-5.02-4.72l-.46-.48c-.8-1.91-2.32-1.12-3.9-1l-28.3,2.23-4.09.23c-7.01.39-5.35,6.16-7.47,6.94-2.57.94-5.02-5.17-8.76-5.54l-29.01-2.92-1.99,24.45,6.25,5.77.41.38.41.37"/> <path class="cls-3" d="M189.15,192.95c-2.52-.41-5.15-.22-7.96,0l-13.46,1.04-.57.11c-1.27.24-2.79.05-3.85.57-2.71,1.35-1.19,5.7-4.63,6.02-1.86-1.28-3.42-3.03-5.02-4.72l-.46-.48c-.8-1.91-2.32-1.12-3.9-1l-28.3,2.23-4.09.23c-7.01.39-5.35,6.16-7.47,6.94-2.57.94-5.02-5.17-8.76-5.54l-29.01-2.92-1.99,24.45,6.25,5.77.41.38.41.37"/>
<polyline class="cls-6" points="189.15 192.95 189.53 193.36 218.63 224.15 219.78 225.37"/> <polyline class="cls-3" points="189.15 192.95 189.53 193.36 218.63 224.15 219.78 225.37"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -3,42 +3,10 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #b59783;
}
.cls-2 {
fill: #39170a; fill: #39170a;
} }
.cls-3 { .cls-2 {
fill: #cfaa63;
}
.cls-4 {
fill: #deb96d;
}
.cls-5 {
fill: #e8cb86;
}
.cls-6 {
fill: #e7ca87;
}
.cls-7 {
fill: #f1d89b;
}
.cls-8 {
fill: #3d180b;
}
.cls-9 {
fill: #d8b26b;
}
.cls-10 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
@@ -46,51 +14,43 @@
stroke-width: 2.75px; stroke-width: 2.75px;
} }
.cls-11 { .cls-3 {
fill: #cda462; fill: #3d180b;
} }
.cls-12 { .cls-4 {
fill: #d0a965; fill: #a88a21;
} }
.cls-13 { .cls-5 {
fill: #dfbb75; fill: #d3ac2c;
} }
.cls-14 { .cls-6 {
fill: #c5a062; fill: #ffcf34;
}
.cls-15 {
fill: #b9a08a;
}
.cls-16 {
fill: #cfa664;
} }
</style> </style>
</defs> </defs>
<path class="cls-7" d="M179.77,205.02l.77,4.04c.04.21.1.45.08.59.02-.14-.04-.38-.08-.59-.85,1.19-1.1-.58-2.67-.25l.79,8.42-.89,10.4c-.41,4.82-1.24,9.97,1.41,14.45,1.05,1.78,9.63-.31,17.16-.75,1.02,1.74,4.84.68,6.63,4.83.88,2.04,2.91,3.45,3.86,5.38,1.4,2.81,2.25,5.73,4.84,7.45,1.02-.82,1.88-.07,2.66-.28l.07,6.27c.03,2.58.2,4.71-.47,6.93-.75.09-2.1-.23-2.94.31-2.87,1.83-6.08,2.33-9.57,2.78l-6.5.84-16.99.61c-3.66.13-7.28-.69-11.12,1.38l16.47,2.61,22.92,3.07,7.37.46c.9,1.94.62,4.11.83,6.29l1.64,17c.53,5.45-8.08,6.66-5.27,8.93,2.01,1.62,5.55,12.81,5.16,16.41l-2.13,19.73c-8.18,1.27-16.4,2.09-24.72,1.88l-6.37-8.74c-.46-.63-1.12-1.06-1.64-1.08-.67-.03-1.56.51-2.14,1.26l-5.23,6.74-16.53,1.49c-2.64.24-5.37.61-7.42-.64l-.56-.34c.37,0,.52-.6.99-.96.23-.18-.52-.27-.45-.87l-1.23-17.67c-.63-9.07-.23-17.95-2.92-26.95l-2.35,17.78-4.21,27.18,3.26.18c-1.16,2.56-3.95,2.42-6.56,2.51l-13.67.49-16.87,1.08c-.05-.45-.49-.46-.65-.67l-.4-.38-.4-.38-.4-.38.58-.54.2.56c2.01-.21.19-3.36.22-9.43,0-2.13-.72-4.28.05-6.44.35-.98,2.07-.69,2.87-.56.43-1.64-1.95-1.67-2.13-2.33l.95-10.42c.09-.94.1-1.71-.31-2.02-1.64-1.25-3.51.15-5.41-.22.06-.7-.29-1.6-.74-2.1-.64-.71-1.77-.71-2.91-.62l-12.92,1.07.18-1.34s-.25-1-.63-1.37l-3.78-3.63c-1.39-1.34-2.03-2.83-1.58-5.16l-3.26-3.67c-1.51-1.7-2.88-4.39-.81-6.29,1.1-.32,3.6.81,4.46-.47,2.86-4.24-1.31-5.58-.49-7.74,1.33-3.48-4.58-6-4.26-8.5l13.01-2.5c3.67-1.59,6.78-3.8,10.59-3.22l.1,32.81c0,1.01,1.11,1.93,1.68,2.01,2.03.27,1.28-3.03,2.08-6.24,2.13.46-1.02,4.51,1.57,8.01.45.61,1.46,0,1.84-.26.62-.41.57-1.41.77-2.39l1.09-.2c.09-.02.41-.44.4-.93l-.43-14.35-.45-23.53c-.01-.52-.02-1.19.34-1.48.58-.46,1.53-.32.75.63l8.86,15.58,13.62,24.51c.37.67,1.86,1.46,2.5,1.41.85-.07,1.58-1.73,1.05-2.73-.13-1.47.35-1.05,1.48-.63,1.09-.52,1.88.67,2.48.34.91-.51,1.18-1.31,1.03-2.48-1.7-13.24-2.12-26.26-1.41-39.55l.91-17.03c.05-.93-.07-1.64-.43-2.01-.49-.49-1.44-.78-1.91-.55-.75.36-1.15,1.14-1.13,2.02l.19,7.68h-.86s-.48-6.72-.48-6.72c-.08-1.09-1.4-1.77-2.14-1.88-1.31-.2-2.03,1.16-2.03,2.59l-.14,40.7c-1.18.02-.63-.49-.97-1.13-7.29-13.26-14.57-26.19-20.99-39.77-.21-.45-.86-.62-1.11-.66-.4-.05-.83.64-1.21.96l-.5-2.31c-.16-.75-1.63-1.47-2.45-.7-1.35,1.27-.5,3.3-.84,4.65-1.25-.1-.52-1.14-.93-1.47-.51-.4-1.14-.55-2.26-.76l-.37,3.69c-2.89-2.03-5.73-4.3-9.04-6.32-.72-.34-2.29.61-2.32,1.39-.03,1.15-.89,2.45.54,3.02l10.66,3.95.41,19.15-14.09-.14c-5.14-.17-9.74-2.39-14.67-4.07-.85.62-1.32,1.36-2.39.69l-1.67-18.92c-.18-2.03.78-3.84,2.65-4.71l7.42-3.44-5.93-5.13.16-5.46c-1.39-3.37-3.58-6.69-3.35-10.39l1.05-16.95c.05-.8-.56-1.46-.02-2.1s1.18.05,2.02.15l15.08,1.79,14.48.61,27.87-1.53c2.78-.15,4.85.21,6.45,2.28-.81,1.03-1.69-.01-3.26,0l4.13,7.93c4.58,11.64,3.97,31.83,8.32,46.37l-.45-23.47,1.02-30.81c-.29-.23-.77.13-1.3.05-.68-.1.4-.6.29-1.64l-.63.29.63-.29,16.21-1.53,9.43-.31c4.27-.14,8.52-1.66,12.53-.39l.66.23c-.03.16-.03.22-.03-.04ZM183.3,260.1c.11.72.67,3.38,2.19,2.35,1.76-7.21-5.95-14.22-14.14-15.14-14.41-1.63-23.52,15.84-26.38,29.41-2.46,11.72.92,27.85,11.7,33.33,3.81,1.94,8.55,3.2,12.6,2.71,17.38-2.11,23.73-18.78,19.2-17.57-1.69.45-1.96,3.32-2.63,3.58-2.37.93.71-2.54.04-4.79-.24-.81-1.17-1.31-1.71-.96-2.42,1.56-3.83,8.02-11.84,10.21-7.31,2.01-14.11-2.14-16.6-9.5-6.23-18.48,5.85-40.89,17.78-36.88,5.29,1.78,5.29,9.24,6.76,9.31,2.39.13,2.79-4.52,2.18-6.12l.85.05Z"/> <path class="cls-6" d="M179.77,205.02l.77,4.04c.04.21.1.45.08.59.02-.14-.04-.38-.08-.59-.85,1.19-1.1-.58-2.67-.25l.79,8.42-.89,10.4c-.41,4.82-1.24,9.97,1.41,14.45,1.05,1.78,9.63-.31,17.16-.75,1.02,1.74,4.84.68,6.63,4.83.88,2.04,2.91,3.45,3.86,5.38,1.4,2.81,2.25,5.73,4.84,7.45,1.02-.82,1.88-.07,2.66-.28l.07,6.27c.03,2.58.2,4.71-.47,6.93-.75.09-2.1-.23-2.94.31-2.87,1.83-6.08,2.33-9.57,2.78l-6.5.84-16.99.61c-3.66.13-7.28-.69-11.12,1.38l16.47,2.61,22.92,3.07,7.37.46c.9,1.94.62,4.11.83,6.29l1.64,17c.53,5.45-8.08,6.66-5.27,8.93,2.01,1.62,5.55,12.81,5.16,16.41l-2.13,19.73c-8.18,1.27-16.4,2.09-24.72,1.88l-6.37-8.74c-.46-.63-1.12-1.06-1.64-1.08-.67-.03-1.56.51-2.14,1.26l-5.23,6.74-16.53,1.49c-2.64.24-5.37.61-7.42-.64l-.56-.34c.37,0,.52-.6.99-.96.23-.18-.52-.27-.45-.87l-1.23-17.67c-.63-9.07-.23-17.95-2.92-26.95l-2.35,17.78-4.21,27.18,3.26.18c-1.16,2.56-3.95,2.42-6.56,2.51l-13.67.49-16.87,1.08c-.05-.45-.49-.46-.65-.67l-.4-.38-.4-.38-.4-.38.58-.54.2.56c2.01-.21.19-3.36.22-9.43,0-2.13-.72-4.28.05-6.44.35-.98,2.07-.69,2.87-.56.43-1.64-1.95-1.67-2.13-2.33l.95-10.42c.09-.94.1-1.71-.31-2.02-1.64-1.25-3.51.15-5.41-.22.06-.7-.29-1.6-.74-2.1-.64-.71-1.77-.71-2.91-.62l-12.92,1.07.18-1.34s-.25-1-.63-1.37l-3.78-3.63c-1.39-1.34-2.03-2.83-1.58-5.16l-3.26-3.67c-1.51-1.7-2.88-4.39-.81-6.29,1.1-.32,3.6.81,4.46-.47,2.86-4.24-1.31-5.58-.49-7.74,1.33-3.48-4.58-6-4.26-8.5l13.01-2.5c3.67-1.59,6.78-3.8,10.59-3.22l.1,32.81c0,1.01,1.11,1.93,1.68,2.01,2.03.27,1.28-3.03,2.08-6.24,2.13.46-1.02,4.51,1.57,8.01.45.61,1.46,0,1.84-.26.62-.41.57-1.41.77-2.39l1.09-.2c.09-.02.41-.44.4-.93l-.43-14.35-.45-23.53c-.01-.52-.02-1.19.34-1.48.58-.46,1.53-.32.75.63l8.86,15.58,13.62,24.51c.37.67,1.86,1.46,2.5,1.41.85-.07,1.58-1.73,1.05-2.73-.13-1.47.35-1.05,1.48-.63,1.09-.52,1.88.67,2.48.34.91-.51,1.18-1.31,1.03-2.48-1.7-13.24-2.12-26.26-1.41-39.55l.91-17.03c.05-.93-.07-1.64-.43-2.01-.49-.49-1.44-.78-1.91-.55-.75.36-1.15,1.14-1.13,2.02l.19,7.68h-.86s-.48-6.72-.48-6.72c-.08-1.09-1.4-1.77-2.14-1.88-1.31-.2-2.03,1.16-2.03,2.59l-.14,40.7c-1.18.02-.63-.49-.97-1.13-7.29-13.26-14.57-26.19-20.99-39.77-.21-.45-.86-.62-1.11-.66-.4-.05-.83.64-1.21.96l-.5-2.31c-.16-.75-1.63-1.47-2.45-.7-1.35,1.27-.5,3.3-.84,4.65-1.25-.1-.52-1.14-.93-1.47-.51-.4-1.14-.55-2.26-.76l-.37,3.69c-2.89-2.03-5.73-4.3-9.04-6.32-.72-.34-2.29.61-2.32,1.39-.03,1.15-.89,2.45.54,3.02l10.66,3.95.41,19.15-14.09-.14c-5.14-.17-9.74-2.39-14.67-4.07-.85.62-1.32,1.36-2.39.69l-1.67-18.92c-.18-2.03.78-3.84,2.65-4.71l7.42-3.44-5.93-5.13.16-5.46c-1.39-3.37-3.58-6.69-3.35-10.39l1.05-16.95c.05-.8-.56-1.46-.02-2.1s1.18.05,2.02.15l15.08,1.79,14.48.61,27.87-1.53c2.78-.15,4.85.21,6.45,2.28-.81,1.03-1.69-.01-3.26,0l4.13,7.93c4.58,11.64,3.97,31.83,8.32,46.37l-.45-23.47,1.02-30.81c-.29-.23-.77.13-1.3.05-.68-.1.4-.6.29-1.64l-.63.29.63-.29,16.21-1.53,9.43-.31c4.27-.14,8.52-1.66,12.53-.39l.66.23c-.03.16-.03.22-.03-.04ZM183.3,260.1c.11.72.67,3.38,2.19,2.35,1.76-7.21-5.95-14.22-14.14-15.14-14.41-1.63-23.52,15.84-26.38,29.41-2.46,11.72.92,27.85,11.7,33.33,3.81,1.94,8.55,3.2,12.6,2.71,17.38-2.11,23.73-18.78,19.2-17.57-1.69.45-1.96,3.32-2.63,3.58-2.37.93.71-2.54.04-4.79-.24-.81-1.17-1.31-1.71-.96-2.42,1.56-3.83,8.02-11.84,10.21-7.31,2.01-14.11-2.14-16.6-9.5-6.23-18.48,5.85-40.89,17.78-36.88,5.29,1.78,5.29,9.24,6.76,9.31,2.39.13,2.79-4.52,2.18-6.12l.85.05Z"/>
<path class="cls-4" d="M97.21,275.95c.18.35.62.79.03.98-3.81-.59-6.93,1.63-10.59,3.22l-13.01,2.5c-.32,2.5,5.59,5.02,4.26,8.5-.82,2.16,3.35,3.5.49,7.74-.86,1.28-3.37.15-4.46.47-2.07,1.9-.69,4.59.81,6.29l3.26,3.67c-.45,2.32.19,3.82,1.58,5.16l3.78,3.63c.38.37.64,1.34.63,1.37l-.18,1.34c-5.9.49-11.78-1.34-17.6-2.56,0,.16,0,.27,0,.02l-.69-.73c.06-1.46-.73-4.12-.11-5.67.88-2.2.34-3.61.48-5.56l1.71-23.37c.25-3.49-1.22-7.09-1.53-10.53,1.07.67,1.54-.07,2.39-.69,4.93,1.69,9.53,3.9,14.67,4.07l14.09.14Z"/> <path class="cls-5" d="M97.21,275.95c.18.35.62.79.03.98-3.81-.59-6.93,1.63-10.59,3.22l-13.01,2.5c-.32,2.5,5.59,5.02,4.26,8.5-.82,2.16,3.35,3.5.49,7.74-.86,1.28-3.37.15-4.46.47-2.07,1.9-.69,4.59.81,6.29l3.26,3.67c-.45,2.32.19,3.82,1.58,5.16l3.78,3.63c.38.37.64,1.34.63,1.37l-.18,1.34c-5.9.49-11.78-1.34-17.6-2.56,0,.16,0,.27,0,.02l-.69-.73c.06-1.46-.73-4.12-.11-5.67.88-2.2.34-3.61.48-5.56l1.71-23.37c.25-3.49-1.22-7.09-1.53-10.53,1.07.67,1.54-.07,2.39-.69,4.93,1.69,9.53,3.9,14.67,4.07l14.09.14Z"/>
<path class="cls-6" d="M83.8,320.81l12.92-1.07c1.13-.09,2.26-.09,2.91.62.45.5.8,1.4.74,2.1-.91,10.45-.01,20.93,3.56,30.87l-.58.54-36.74-35.2-.4-.41c5.82,1.22,11.7,3.05,17.6,2.56Z"/> <path class="cls-6" d="M83.8,320.81l12.92-1.07c1.13-.09,2.26-.09,2.91.62.45.5.8,1.4.74,2.1-.91,10.45-.01,20.93,3.56,30.87l-.58.54-36.74-35.2-.4-.41c5.82,1.22,11.7,3.05,17.6,2.56Z"/>
<path class="cls-5" d="M179.77,205.02c0,.18,0,.24.03.04l34.21,33.44-.49.54-1.92-.35c-.22-.04-.45-.09-.64-.11l-2.66-.27c-13.98-3.98-24.18.77-25.99-.65-.73-.57-.59-1.87-.82-2.93,1.42-8.61.58-16.85-.87-25.09.02-.14-.04-.38-.08-.59l-.77-4.04Z"/> <path class="cls-6" d="M179.77,205.02c0,.18,0,.24.03.04l34.21,33.44-.49.54-1.92-.35c-.22-.04-.45-.09-.64-.11l-2.66-.27c-13.98-3.98-24.18.77-25.99-.65-.73-.57-.59-1.87-.82-2.93,1.42-8.61.58-16.85-.87-25.09.02-.14-.04-.38-.08-.59l-.77-4.04Z"/>
<path class="cls-3" d="M149.77,353.23l-.56-.34-3.01-2.1c-1.49-1.04-3.18-.66-3.91.8l-3.26-.18,4.21-27.18,2.35-17.78c2.69,9,2.29,17.87,2.92,26.95l1.23,17.67c-.07.59.69.68.45.87-.47.37-.62.97-.99.96l.56.34Z"/> <path class="cls-5" d="M149.77,353.23l-.56-.34-3.01-2.1c-1.49-1.04-3.18-.66-3.91.8l-3.26-.18,4.21-27.18,2.35-17.78c2.69,9,2.29,17.87,2.92,26.95l1.23,17.67c-.07.59.69.68.45.87-.47.37-.62.97-.99.96l.56.34Z"/>
<path class="cls-12" d="M140.36,207.35l.63-.29c.11,1.04-.97,1.54-.29,1.64.53.08,1.01-.28,1.3-.05l-1.02,30.81.45,23.47c-4.34-14.54-3.74-34.72-8.32-46.37l-4.13-7.93c1.57-.02,2.45,1.02,3.26,0,1.15,1.48,3.05,3.31,5.03,3.99l3.08-5.27Z"/> <path class="cls-5" d="M140.36,207.35l.63-.29c.11,1.04-.97,1.54-.29,1.64.53.08,1.01-.28,1.3-.05l-1.02,30.81.45,23.47c-4.34-14.54-3.74-34.72-8.32-46.37l-4.13-7.93c1.57-.02,2.45,1.02,3.26,0,1.15,1.48,3.05,3.31,5.03,3.99l3.08-5.27Z"/>
<path class="cls-16" d="M213.94,271.89c-1.02,3.42-5.64,5.2-4.81,6.04l3.77,3.83c.8.81.39,1.56.68,2.19l-7.37-.46-22.92-3.07-16.47-2.61c3.84-2.08,7.46-1.25,11.12-1.38l16.99-.61,6.5-.84c3.49-.45,6.7-.95,9.57-2.78.84-.53,2.19-.22,2.94-.31Z"/> <path class="cls-5" d="M213.94,271.89c-1.02,3.42-5.64,5.2-4.81,6.04l3.77,3.83c.8.81.39,1.56.68,2.19l-7.37-.46-22.92-3.07-16.47-2.61c3.84-2.08,7.46-1.25,11.12-1.38l16.99-.61,6.5-.84c3.49-.45,6.7-.95,9.57-2.78.84-.53,2.19-.22,2.94-.31Z"/>
<path class="cls-13" d="M214.34,258.7c-.77.21-1.64-.54-2.66.28-2.59-1.73-3.44-4.65-4.84-7.45-.96-1.92-2.99-3.34-3.86-5.38-1.79-4.15-5.61-3.09-6.63-4.83l14.51-.85c-.03-.62-.15-1.25.1-1.89.19.02.43.07.64.11l1.92.35.49-.54.4.39c.14.14.38.23.4.39l.1.72c.48,3.66-.57,7.44-.57,11.31v7.39Z"/> <path class="cls-5" d="M214.34,258.7c-.77.21-1.64-.54-2.66.28-2.59-1.73-3.44-4.65-4.84-7.45-.96-1.92-2.99-3.34-3.86-5.38-1.79-4.15-5.61-3.09-6.63-4.83l14.51-.85c-.03-.62-.15-1.25.1-1.89.19.02.43.07.64.11l1.92.35.49-.54.4.39c.14.14.38.23.4.39l.1.72c.48,3.66-.57,7.44-.57,11.31v7.39Z"/>
<path class="cls-2" d="M96.95,254.77l.37-3.69c1.12.21,1.75.37,2.26.76.42.33-.32,1.37.93,1.47.34-1.34-.52-3.38.84-4.65.82-.77,2.29-.05,2.45.7l.5,2.31c.37-.32.81-1.01,1.21-.96.25.03.9.2,1.11.66,6.43,13.58,13.7,26.51,20.99,39.77.33.64-.22,1.15.97,1.13l.14-40.7c0-1.44.72-2.79,2.03-2.59.75.11,2.07.79,2.14,1.88l.48,6.73h.86s-.19-7.69-.19-7.69c-.02-.88.38-1.66,1.13-2.02.47-.23,1.41.06,1.91.55.36.36.48,1.08.43,2.01l-.91,17.03c-.71,13.29-.29,26.3,1.41,39.55.15,1.17-.12,1.97-1.03,2.48-.6.34-1.39-.86-2.48-.34-1.13-.42-1.61-.84-1.48.63.53,1-.2,2.66-1.05,2.73-.63.05-2.13-.74-2.5-1.41l-13.62-24.51-8.86-15.58c.78-.95-.17-1.09-.75-.63-.36.29-.35.96-.34,1.48l.45,23.53.43,14.35c.01.49-.31.91-.4.93l-1.09.2c-.2.98-.16,1.98-.77,2.39-.38.26-1.39.87-1.84.26-2.59-3.5.56-7.55-1.57-8.01-.8,3.22-.05,6.51-2.08,6.24-.58-.08-1.68-1-1.68-2.01l-.1-32.81c.59-.19.15-.63-.03-.98l-.41-19.15c-.01-.67.09-1.35.16-2.03ZM131.61,294.87c.56.17.86.1,1.15.05l-.14-5.48h-.88s-.12,5.43-.12,5.43Z"/> <path class="cls-1" d="M96.95,254.77l.37-3.69c1.12.21,1.75.37,2.26.76.42.33-.32,1.37.93,1.47.34-1.34-.52-3.38.84-4.65.82-.77,2.29-.05,2.45.7l.5,2.31c.37-.32.81-1.01,1.21-.96.25.03.9.2,1.11.66,6.43,13.58,13.7,26.51,20.99,39.77.33.64-.22,1.15.97,1.13l.14-40.7c0-1.44.72-2.79,2.03-2.59.75.11,2.07.79,2.14,1.88l.48,6.73h.86s-.19-7.69-.19-7.69c-.02-.88.38-1.66,1.13-2.02.47-.23,1.41.06,1.91.55.36.36.48,1.08.43,2.01l-.91,17.03c-.71,13.29-.29,26.3,1.41,39.55.15,1.17-.12,1.97-1.03,2.48-.6.34-1.39-.86-2.48-.34-1.13-.42-1.61-.84-1.48.63.53,1-.2,2.66-1.05,2.73-.63.05-2.13-.74-2.5-1.41l-13.62-24.51-8.86-15.58c.78-.95-.17-1.09-.75-.63-.36.29-.35.96-.34,1.48l.45,23.53.43,14.35c.01.49-.31.91-.4.93l-1.09.2c-.2.98-.16,1.98-.77,2.39-.38.26-1.39.87-1.84.26-2.59-3.5.56-7.55-1.57-8.01-.8,3.22-.05,6.51-2.08,6.24-.58-.08-1.68-1-1.68-2.01l-.1-32.81c.59-.19.15-.63-.03-.98l-.41-19.15c-.01-.67.09-1.35.16-2.03ZM131.61,294.87c.56.17.86.1,1.15.05l-.14-5.48h-.88s-.12,5.43-.12,5.43Z"/>
<path class="cls-8" d="M183.3,260.1l-.32-2.09c-.11-.72-1.11-1.15-.97-1.75l-3.04-3.05c-.51-.51-.92-.45-1.63-.42,2.53,1.87,4.03,4.39,5.11,7.26.61,1.6.21,6.25-2.18,6.12-1.47-.08-1.46-7.54-6.76-9.31-11.93-4-24.01,18.41-17.78,36.88,2.48,7.36,9.28,11.51,16.6,9.5,8-2.2,9.41-8.66,11.84-10.21.54-.35,1.47.15,1.71.96.66,2.24-2.41,5.72-.04,4.79.67-.26.94-3.13,2.63-3.58,4.53-1.21-1.83,15.46-19.2,17.57-4.05.49-8.79-.77-12.6-2.71-10.78-5.48-14.16-21.61-11.7-33.33,2.86-13.58,11.96-31.05,26.38-29.41,8.2.93,15.9,7.93,14.14,15.14-1.52,1.03-2.08-1.63-2.19-2.35Z"/> <path class="cls-3" d="M183.3,260.1l-.32-2.09c-.11-.72-1.11-1.15-.97-1.75l-3.04-3.05c-.51-.51-.92-.45-1.63-.42,2.53,1.87,4.03,4.39,5.11,7.26.61,1.6.21,6.25-2.18,6.12-1.47-.08-1.46-7.54-6.76-9.31-11.93-4-24.01,18.41-17.78,36.88,2.48,7.36,9.28,11.51,16.6,9.5,8-2.2,9.41-8.66,11.84-10.21.54-.35,1.47.15,1.71.96.66,2.24-2.41,5.72-.04,4.79.67-.26.94-3.13,2.63-3.58,4.53-1.21-1.83,15.46-19.2,17.57-4.05.49-8.79-.77-12.6-2.71-10.78-5.48-14.16-21.61-11.7-33.33,2.86-13.58,11.96-31.05,26.38-29.41,8.2.93,15.9,7.93,14.14,15.14-1.52,1.03-2.08-1.63-2.19-2.35Z"/>
<path class="cls-14" d="M180.62,209.65c1.45,8.24,2.29,16.48.87,25.09.23,1.06.09,2.35.82,2.93,1.81,1.42,12.02-3.33,25.99.65l2.66.27c.19.02.43.07.64.11-.22-.04-.45-.09-.64-.11-.26.64-.13,1.27-.1,1.89l-14.51.85c-7.53.44-16.11,2.54-17.16.75-2.65-4.48-1.82-9.62-1.41-14.45l.89-10.4-.79-8.42c1.56-.33,1.82,1.45,2.67.25.04.21.1.45.08.59Z"/> <path class="cls-4" d="M180.62,209.65c1.45,8.24,2.29,16.48.87,25.09.23,1.06.09,2.35.82,2.93,1.81,1.42,12.02-3.33,25.99.65l2.66.27c.19.02.43.07.64.11-.22-.04-.45-.09-.64-.11-.26.64-.13,1.27-.1,1.89l-14.51.85c-7.53.44-16.11,2.54-17.16.75-2.65-4.48-1.82-9.62-1.41-14.45l.89-10.4-.79-8.42c1.56-.33,1.82,1.45,2.67.25.04.21.1.45.08.59Z"/>
<path class="cls-11" d="M100.37,322.45c1.9.37,3.77-1.03,5.41.22.41.31.4,1.07.31,2.02l-.95,10.42c.18.66,2.56.69,2.13,2.33-.8-.12-2.52-.42-2.87.56-.77,2.16-.04,4.31-.05,6.44-.03,6.07,1.79,9.23-.22,9.43l-.2-.56c-3.58-9.94-4.47-20.41-3.56-30.87Z"/> <path class="cls-4" d="M100.37,322.45c1.9.37,3.77-1.03,5.41.22.41.31.4,1.07.31,2.02l-.95,10.42c.18.66,2.56.69,2.13,2.33-.8-.12-2.52-.42-2.87.56-.77,2.16-.04,4.31-.05,6.44-.03,6.07,1.79,9.23-.22,9.43l-.2-.56c-3.58-9.94-4.47-20.41-3.56-30.87Z"/>
<path class="cls-9" d="M96.95,254.77c-.07.68-.17,1.36-.16,2.03l-10.66-3.95c-1.43-.57-.57-1.87-.54-3.02.02-.78,1.6-1.73,2.32-1.39,3.31,2.02,6.15,4.3,9.04,6.32Z"/> <path class="cls-5" d="M96.95,254.77c-.07.68-.17,1.36-.16,2.03l-10.66-3.95c-1.43-.57-.57-1.87-.54-3.02.02-.78,1.6-1.73,2.32-1.39,3.31,2.02,6.15,4.3,9.04,6.32Z"/>
<path class="cls-15" d="M183.3,260.1l-.85-.05c-1.08-2.87-2.59-5.4-5.11-7.26.71-.03,1.12-.09,1.63.42l3.04,3.05c-.15.6.86,1.03.97,1.75l.32,2.09Z"/> <path class="cls-4" d="M183.3,260.1l-.85-.05c-1.08-2.87-2.59-5.4-5.11-7.26.71-.03,1.12-.09,1.63.42l3.04,3.05c-.15.6.86,1.03.97,1.75l.32,2.09Z"/>
<path class="cls-1" d="M131.61,294.87l.12-5.43h.88s.14,5.47.14,5.47c-.29.05-.59.12-1.15-.05Z"/> <path class="cls-4" d="M131.61,294.87l.12-5.43h.88s.14,5.47.14,5.47c-.29.05-.59.12-1.15-.05Z"/>
<path class="cls-10" d="M179.77,205.02l-.62-.2c-4-1.27-8.26.25-12.53.39l-9.43.31-16.21,1.53-.63.29-3.08,5.27c-1.99-.68-3.89-2.51-5.03-3.99-1.6-2.07-3.66-2.43-6.45-2.28l-27.87,1.53-14.48-.61-15.08-1.79c-.84-.1-1.46-.81-2.02-.15s.07,1.3.02,2.1l-1.05,16.95c-.23,3.7,1.96,7.02,3.35,10.39l-.16,5.46,5.93,5.13-7.42,3.44c-1.86.87-2.83,2.67-2.65,4.71l1.67,18.92c.3,3.43,1.78,7.04,1.53,10.53l-1.71,23.37c-.14,1.96.39,3.36-.48,5.56-.62,1.55.17,4.21.11,5.67l.69.73"/> <path class="cls-2" d="M179.77,205.02l-.62-.2c-4-1.27-8.26.25-12.53.39l-9.43.31-16.21,1.53-.63.29-3.08,5.27c-1.99-.68-3.89-2.51-5.03-3.99-1.6-2.07-3.66-2.43-6.45-2.28l-27.87,1.53-14.48-.61-15.08-1.79c-.84-.1-1.46-.81-2.02-.15s.07,1.3.02,2.1l-1.05,16.95c-.23,3.7,1.96,7.02,3.35,10.39l-.16,5.46,5.93,5.13-7.42,3.44c-1.86.87-2.83,2.67-2.65,4.71l1.67,18.92c.3,3.43,1.78,7.04,1.53,10.53l-1.71,23.37c-.14,1.96.39,3.36-.48,5.56-.62,1.55.17,4.21.11,5.67l.69.73"/>
<path class="cls-10" d="M214.8,239.27l-1.28-.24-1.92-.35c-.22-.04-.45-.09-.64-.11l-2.66-.27c-13.98-3.98-24.18.77-25.99-.65-.73-.57-.59-1.87-.82-2.93,1.42-8.61.58-16.85-.87-25.09.02-.14-.04-.38-.08-.59l-.77-4.04"/> <path class="cls-2" d="M214.8,239.27l-1.28-.24-1.92-.35c-.22-.04-.45-.09-.64-.11l-2.66-.27c-13.98-3.98-24.18.77-25.99-.65-.73-.57-.59-1.87-.82-2.93,1.42-8.61.58-16.85-.87-25.09.02-.14-.04-.38-.08-.59l-.77-4.04"/>
<path class="cls-10" d="M104.54,355.01l-.41-1.13-.2-.56c-3.58-9.94-4.47-20.41-3.56-30.87.06-.7-.29-1.6-.74-2.1-.64-.71-1.77-.71-2.91-.62l-12.92,1.07c-5.9.49-11.78-1.34-17.6-2.56"/> <path class="cls-2" d="M104.54,355.01l-.41-1.13-.2-.56c-3.58-9.94-4.47-20.41-3.56-30.87.06-.7-.29-1.6-.74-2.1-.64-.71-1.77-.71-2.91-.62l-12.92,1.07c-5.9.49-11.78-1.34-17.6-2.56"/>
<polyline class="cls-10" points="179.8 205.06 214.01 238.49 214.41 238.88 214.8 239.27"/> <polyline class="cls-2" points="179.8 205.06 214.01 238.49 214.41 238.88 214.8 239.27"/>
<path class="cls-10" d="M214.8,239.27l.1.72c.48,3.66-.57,7.44-.57,11.31v7.39s.07,6.27.07,6.27c.03,2.58.2,4.71-.47,6.93-1.02,3.42-5.64,5.2-4.81,6.04"/> <path class="cls-2" d="M214.8,239.27l.1.72c.48,3.66-.57,7.44-.57,11.31v7.39s.07,6.27.07,6.27c.03,2.58.2,4.71-.47,6.93-1.02,3.42-5.64,5.2-4.81,6.04"/>
<polyline class="cls-10" points="66.21 318.28 66.6 318.66 103.34 353.86 103.74 354.24 104.14 354.63 104.54 355.01"/> <polyline class="cls-2" points="66.21 318.28 66.6 318.66 103.34 353.86 103.74 354.24 104.14 354.63 104.54 355.01"/>
<path class="cls-10" d="M210.79,316.19c2.01,1.62,5.55,12.81,5.16,16.41l-2.13,19.73c-8.18,1.27-16.4,2.09-24.72,1.88l-6.37-8.74c-.46-.63-1.12-1.06-1.64-1.08-.67-.03-1.56.51-2.14,1.26l-5.23,6.74-16.53,1.49c-2.64.24-5.37.61-7.42-.64l-.56-.34-3.01-2.1c-1.49-1.04-3.18-.66-3.91.8-1.16,2.56-3.95,2.42-6.56,2.51l-13.67.49-16.87,1.08-.65-.67"/> <path class="cls-2" d="M210.79,316.19c2.01,1.62,5.55,12.81,5.16,16.41l-2.13,19.73c-8.18,1.27-16.4,2.09-24.72,1.88l-6.37-8.74c-.46-.63-1.12-1.06-1.64-1.08-.67-.03-1.56.51-2.14,1.26l-5.23,6.74-16.53,1.49c-2.64.24-5.37.61-7.42-.64l-.56-.34-3.01-2.1c-1.49-1.04-3.18-.66-3.91.8-1.16,2.56-3.95,2.42-6.56,2.51l-13.67.49-16.87,1.08-.65-.67"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -3,75 +3,47 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #d9b36c; fill: #9b1f0f;
} }
.cls-2 { .cls-2 {
fill: #f2dda5;
}
.cls-3 {
fill: #e0bb6f;
}
.cls-4 {
fill: #3a160a; fill: #3a160a;
} }
.cls-5 { .cls-3 {
fill: #c29b5e; fill: #e93525;
} }
.cls-6 { .cls-4 {
fill: #c5a78e;
}
.cls-7 {
fill: #3d180d; fill: #3d180d;
} }
.cls-8 { .cls-5 {
fill: #f6e4b3; fill: #c12b1c;
} }
.cls-9 { .cls-6 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
stroke-linejoin: round; stroke-linejoin: round;
stroke-width: 2.2px; stroke-width: 2.2px;
} }
.cls-10 {
fill: #e0b86c;
}
.cls-11 {
fill: #e6c67e;
}
.cls-12 {
fill: #c49d5e;
}
.cls-13 {
fill: #cfa664;
}
</style> </style>
</defs> </defs>
<path class="cls-2" d="M141.35,207.84c-.15-.1-.38-.08-.57-.14-.5.53.56,1.73-.35,2.63,2.2,6.52,2.73,13.17,3.12,20.16l1.18,20.95c.3-.07.67-.04.84-.02.7-.89-.2-2.18-.08-3.4l3.51-37.22c.07-.78-.41-1.44-.5-1.88l19.5-2.14c1.96.84,1.89,4.16,2.43,5.54-.19-.36-1.21.01-1.82.21-.39.13.11.72-.4,1.5.58,10.03-3.74,18.54-4.28,28.36,3.88-6.54,14.81-30.94,15.78-31.51,1.1-.65,1.54.47,2.43.78,1.28.45,3.34-.37,3.73,1.34.25,1.09,1.66.7,2.44,1.56-.46,1.49-1.33,2.83-1.48,4.39,3.42.29,7.69-.88,11.02.3,2.37.84-.91,3.18-1.22,4.37.17,4.04,4.98,4.47,10.21,3.99.82.43.92,2.65,2.23,2.64l-4.61,3.64c4.87-.22,8.3,1.84,11.15,1.44l-.61,4.42c-.69.13-1.97-.34-2.8.24-6.06,4.22-12.42,7.63-18.95,11.04l-6.29,4.39c4.87.92,9.07-1.38,13.76-2.48,3.75-.88,7.71-1.15,11.61-1.17,1.47,0,3.02,1.79,4.47.65l-1.19,12.44c-.25,2.67-.6,5.14-1.34,7.45-.83-.98-1.63-.31-2.63.07-6.58,2.5-13.24,3.89-20.13,5l-17.39,2.78c1.75,1.3,3.52.34,5.24.34h32.08c-1.46,8.73-7.23,6.14-7.99,8.08-1.21,3.1,2.92,5.92,1.74,7.9s-3.15,3.27-4.07,5.7c2.08.02,5.13-3.57,7.39-2.41.77.39,1.39,1.77,1.11,3.19-3.36,4.96-9.42,5.44-8.61,13.35l-.74,3.75c-1.58.06-2.1,1.03-2.21,2.08l-12.24-1.78c-1.47-.21-4.87-.09-4.63,1.94l1.6,13.76c.17,1.43.76,2.77-.25,3.79l-5.27,2.54-2.14.63-1.93-1.16c-6.66,3.78-4.71,7.07-15.02,6.31l-11.95,2.56-1.39-7.83c-3.15-7.67-3.81-15.27-3.95-23.41l-.15-8.71c-.38.04-.44-.34-.87-.53l-1.62,20.27c-.21,2.63-.8,4.53-1.38,7.39l-20.37-.61c-2.55-.08-4.58-3.05-7.89-1.97-.6-2.94-2.88-3.36-5.33-3.82.34-4.53,4.71-6.78,3.51-8.96l-7.49,7.73-1.13-2.09c-1.15-2.13-1.4-5.14-4.15-6.09-.73-1.72-2.1-3.05-4.58-2.9-.17-1.75-2.45-1.79-2.86-2.71-.24-.54.43-.87-.61-.57-.2.06-3.02-3.72-3.62-7.19-3.05-.69-.25-4.22-2.63-4.24,3.16-3.19-3.84-1.11-4.81-3.41-.81-1.91-.04-3.39.74-4.96.26-.51-.56-4.29,2.44-9.1,6.33-2.1,12.4-3.33,18.8-4.23.25-.04.88.64,1.17.5.34-.16.76-.41.89-1.14l-22.09-1.45-1.42-.42c.68.2-2.27-4.82-.93-7.13,1.88-.95,4.57-.3,6.65-.77.49-.11,4.68-5.32,4.6-4.85.15-.81-.35-1.49-.9-1.62-1.08-.25-1.49,2.01-5.22,2.29-1.4.11-3.82-3.52-3.86-3.9-.09-.81.64-.76,1.09-1.56,3.25-5.81,2.03-5.22,1.61-9.67-.13-1.35-1.69-2.4-.19-3.82,2.65-2.49,5.15-5.35,6.94-8.32.25-.1.64-.51,1.11-.47l13.86,1.31c3.55-2.76,1.46-9.93,1.27-15.68l-.49-14.85c.39-.69.33-1.35.37-1.85.06-.7-.98-1.5-1.55-.94l.55-2.93.11-.58.22-1.16-.39.4.39-.34c4.32.66,9.46,1.37,13.92,1.69l14.97,1.06c3.88.27,7.66-.03,11.21,1.08.19.06.42.03.57.14ZM102.09,290.2l.93,21.66c.06,1.44-.52,3.82,1.01,4.64.98.52,2.05,1,2.45.38l1.25-1.93c.34-.52-.14-1.15.09-1.4.18-.19.64-.12.85-.03.32.12.23.16.26.39.08.53-.64.83-.5,1.16.41.96,1.63,1.3,2.11.96.43-.3,1.27-1.06,1.24-1.95l-.87-22.21c8.59-.6,16.52-3.59,22.83-8.98,7.59-6.48,9.09-17.7,2.59-25.38s-21.36-8.59-30.78-5.14c-.19-1.38-.35-2.08-.8-2.15l-2.07-.29-.32,3.56c-9.69,4.25-8.22,8.19-6.55,7.13.37-.23.75-.79,2.02-.94l-1.7,2.61c-.33.5-.28,1.36.15,1.65.29.2,1.12.33,1.58,0l4.17-3.06-.03,24.78c-1.77.34-3.13-1.12-4.55-.4-1.95.98.56,3.2,4.63,4.93ZM183.9,295.43c-2.54-1.64-4.08,8.31-13.8,10.5-5.05,1.14-10.5-1.01-13.47-5.57-4.78-7.32-5.07-16.43-2.58-24.6,3.13-10.28,10.83-20.05,19.27-15.53,5.81,3.12,2.84,9.38,6.76,8.1.76-.25,1.62-3.66.55-6.94,1.78.38,1.35,3.89,2.76,4.16,2.82.54.95-11.97-10.88-15.11-17.71-4.7-30.43,20.87-29.61,38.76.36,7.72,2.8,15.24,8.44,20.4,5.98,5.47,14.37,6.55,22.33,4.17,9.83-2.94,16.54-13.86,14.45-15.95-.52-.53-1.42-.6-1.75-.07l-.88,1.4c-.87.17-.68.78-.87.89-1.52.83,1.03-3.48-.73-4.61Z"/> <path class="cls-3" d="M141.35,207.84c-.15-.1-.38-.08-.57-.14-.5.53.56,1.73-.35,2.63,2.2,6.52,2.73,13.17,3.12,20.16l1.18,20.95c.3-.07.67-.04.84-.02.7-.89-.2-2.18-.08-3.4l3.51-37.22c.07-.78-.41-1.44-.5-1.88l19.5-2.14c1.96.84,1.89,4.16,2.43,5.54-.19-.36-1.21.01-1.82.21-.39.13.11.72-.4,1.5.58,10.03-3.74,18.54-4.28,28.36,3.88-6.54,14.81-30.94,15.78-31.51,1.1-.65,1.54.47,2.43.78,1.28.45,3.34-.37,3.73,1.34.25,1.09,1.66.7,2.44,1.56-.46,1.49-1.33,2.83-1.48,4.39,3.42.29,7.69-.88,11.02.3,2.37.84-.91,3.18-1.22,4.37.17,4.04,4.98,4.47,10.21,3.99.82.43.92,2.65,2.23,2.64l-4.61,3.64c4.87-.22,8.3,1.84,11.15,1.44l-.61,4.42c-.69.13-1.97-.34-2.8.24-6.06,4.22-12.42,7.63-18.95,11.04l-6.29,4.39c4.87.92,9.07-1.38,13.76-2.48,3.75-.88,7.71-1.15,11.61-1.17,1.47,0,3.02,1.79,4.47.65l-1.19,12.44c-.25,2.67-.6,5.14-1.34,7.45-.83-.98-1.63-.31-2.63.07-6.58,2.5-13.24,3.89-20.13,5l-17.39,2.78c1.75,1.3,3.52.34,5.24.34h32.08c-1.46,8.73-7.23,6.14-7.99,8.08-1.21,3.1,2.92,5.92,1.74,7.9s-3.15,3.27-4.07,5.7c2.08.02,5.13-3.57,7.39-2.41.77.39,1.39,1.77,1.11,3.19-3.36,4.96-9.42,5.44-8.61,13.35l-.74,3.75c-1.58.06-2.1,1.03-2.21,2.08l-12.24-1.78c-1.47-.21-4.87-.09-4.63,1.94l1.6,13.76c.17,1.43.76,2.77-.25,3.79l-5.27,2.54-2.14.63-1.93-1.16c-6.66,3.78-4.71,7.07-15.02,6.31l-11.95,2.56-1.39-7.83c-3.15-7.67-3.81-15.27-3.95-23.41l-.15-8.71c-.38.04-.44-.34-.87-.53l-1.62,20.27c-.21,2.63-.8,4.53-1.38,7.39l-20.37-.61c-2.55-.08-4.58-3.05-7.89-1.97-.6-2.94-2.88-3.36-5.33-3.82.34-4.53,4.71-6.78,3.51-8.96l-7.49,7.73-1.13-2.09c-1.15-2.13-1.4-5.14-4.15-6.09-.73-1.72-2.1-3.05-4.58-2.9-.17-1.75-2.45-1.79-2.86-2.71-.24-.54.43-.87-.61-.57-.2.06-3.02-3.72-3.62-7.19-3.05-.69-.25-4.22-2.63-4.24,3.16-3.19-3.84-1.11-4.81-3.41-.81-1.91-.04-3.39.74-4.96.26-.51-.56-4.29,2.44-9.1,6.33-2.1,12.4-3.33,18.8-4.23.25-.04.88.64,1.17.5.34-.16.76-.41.89-1.14l-22.09-1.45-1.42-.42c.68.2-2.27-4.82-.93-7.13,1.88-.95,4.57-.3,6.65-.77.49-.11,4.68-5.32,4.6-4.85.15-.81-.35-1.49-.9-1.62-1.08-.25-1.49,2.01-5.22,2.29-1.4.11-3.82-3.52-3.86-3.9-.09-.81.64-.76,1.09-1.56,3.25-5.81,2.03-5.22,1.61-9.67-.13-1.35-1.69-2.4-.19-3.82,2.65-2.49,5.15-5.35,6.94-8.32.25-.1.64-.51,1.11-.47l13.86,1.31c3.55-2.76,1.46-9.93,1.27-15.68l-.49-14.85c.39-.69.33-1.35.37-1.85.06-.7-.98-1.5-1.55-.94l.55-2.93.11-.58.22-1.16-.39.4.39-.34c4.32.66,9.46,1.37,13.92,1.69l14.97,1.06c3.88.27,7.66-.03,11.21,1.08.19.06.42.03.57.14ZM102.09,290.2l.93,21.66c.06,1.44-.52,3.82,1.01,4.64.98.52,2.05,1,2.45.38l1.25-1.93c.34-.52-.14-1.15.09-1.4.18-.19.64-.12.85-.03.32.12.23.16.26.39.08.53-.64.83-.5,1.16.41.96,1.63,1.3,2.11.96.43-.3,1.27-1.06,1.24-1.95l-.87-22.21c8.59-.6,16.52-3.59,22.83-8.98,7.59-6.48,9.09-17.7,2.59-25.38s-21.36-8.59-30.78-5.14c-.19-1.38-.35-2.08-.8-2.15l-2.07-.29-.32,3.56c-9.69,4.25-8.22,8.19-6.55,7.13.37-.23.75-.79,2.02-.94l-1.7,2.61c-.33.5-.28,1.36.15,1.65.29.2,1.12.33,1.58,0l4.17-3.06-.03,24.78c-1.77.34-3.13-1.12-4.55-.4-1.95.98.56,3.2,4.63,4.93ZM183.9,295.43c-2.54-1.64-4.08,8.31-13.8,10.5-5.05,1.14-10.5-1.01-13.47-5.57-4.78-7.32-5.07-16.43-2.58-24.6,3.13-10.28,10.83-20.05,19.27-15.53,5.81,3.12,2.84,9.38,6.76,8.1.76-.25,1.62-3.66.55-6.94,1.78.38,1.35,3.89,2.76,4.16,2.82.54.95-11.97-10.88-15.11-17.71-4.7-30.43,20.87-29.61,38.76.36,7.72,2.8,15.24,8.44,20.4,5.98,5.47,14.37,6.55,22.33,4.17,9.83-2.94,16.54-13.86,14.45-15.95-.52-.53-1.42-.6-1.75-.07l-.88,1.4c-.87.17-.68.78-.87.89-1.52.83,1.03-3.48-.73-4.61Z"/>
<path class="cls-11" d="M100.35,205.56l-.55,2.93c-1.08,5.75-2.71,11.68-2.27,17.86.25,3.54.85,6.96.03,10.68-8.89.06-16.45-2.38-25.75.39-.64.15-.28,1.16-.01,1.44.24.26.66-.03,1.36.22l11.41,1.34c.18.5.36.67.67.55-1.8,2.97-4.29,5.82-6.94,8.32-1.5,1.41.06,2.46.19,3.82.42,4.45,1.64,3.85-1.61,9.67-.45.81-1.18.76-1.09,1.56.04.39,2.46,4.01,3.86,3.9,3.73-.29,4.14-2.55,5.22-2.29.56.13,1.05.81.9,1.62.09-.47-4.1,4.74-4.6,4.85-2.08.48-4.77-.18-6.65.77-1.35,2.31,1.61,7.33.93,7.13l1.42.42,22.09,1.45c-.13.74-.55.98-.89,1.14-.29.14-.91-.53-1.17-.5-6.4.9-12.47,2.12-18.8,4.23-3,4.81-2.18,8.59-2.44,9.1-.78,1.57-1.55,3.04-.74,4.96.97,2.29,7.97.21,4.81,3.41,2.38.02-.42,3.55,2.63,4.24.6,3.47,3.42,7.25,3.62,7.19,1.04-.3.38.02.61.57.4.92,2.69.96,2.86,2.71,2.48-.16,3.85,1.18,4.58,2.9,2.75.95,3,3.96,4.15,6.09l1.13,2.09,7.49-7.73c1.2,2.18-3.16,4.43-3.51,8.96,2.45.45,4.73.87,5.33,3.82,3.31-1.07,5.34,1.9,7.89,1.97l20.37.61c.58-2.85,1.17-4.75,1.38-7.39l1.62-20.27c.43.19.49.57.87.53l.15,8.71c.14,8.14.8,15.74,3.95,23.41l1.39,7.83,11.95-2.56c10.32.76,8.36-2.53,15.02-6.31l1.93,1.16,2.14-.63,5.27-2.54c.4-.02.63.4.83.64.16.18-.17.6-.2,1.04l-.67,10.12c.82-.14,1.5-.45,2.27,0,2.81-6.65,3.02-13.87,2.67-21.4.15-.92-.09-2.34.39-2.85.45-.48,1.84-1.22,2.79-1.11,7.02.82,13.69.47,20.29-1.35,1.03.54,2.17.4,3.18.19.08.61.02,1.31-.5,1.83l-14.08,13.99c-3.04,3.02-6.73,5.26-9.07,8.88l-3.1,3.24c-1.03-1.01-2.03-.45-2.82-.97l-2.06,4.85-35.37-1.32c-4.34-1.79-6.54-8.04-7.73-7.92s-2.8,6.38-5.48,6.54l-10.41.64c-5.38.33-10.15.72-15.49,2.19-3.14.87-7.62.86-10.83.08-2.95-1.18-3.85-5.14-6.2-6.63-2.58.57-3.1,5.36-5.17,6.12s-15.98-.69-16.94-1.64c-1.04-1.02-1.66-2.74-1.93-4.36l-1.46-8.74c-.26-1.57-1.4-2.96-.05-5l.25-3.57c1.08-2.07.21-4.64.52-7.05l1.27-9.71c.24-1.86.5-4.12-.13-5.95l-.38-7.85c-.09-1.91-.47-3.76-.13-5.55l1.4-7.4c.42-2.24,1.09-4.38,1.82-6.51-.7-1.43-2.17-2.49-2.25-3.91l-.41-7.84-.51-7.74-.6-8.71-.3-7.65c-.86-4.02-1.08-8.37,2.47-11.45.28-.11.66-.01.92.02l30.19-31.05.84.55Z"/> <path class="cls-5" d="M100.35,205.56l-.55,2.93c-1.08,5.75-2.71,11.68-2.27,17.86.25,3.54.85,6.96.03,10.68-8.89.06-16.45-2.38-25.75.39-.64.15-.28,1.16-.01,1.44.24.26.66-.03,1.36.22l11.41,1.34c.18.5.36.67.67.55-1.8,2.97-4.29,5.82-6.94,8.32-1.5,1.41.06,2.46.19,3.82.42,4.45,1.64,3.85-1.61,9.67-.45.81-1.18.76-1.09,1.56.04.39,2.46,4.01,3.86,3.9,3.73-.29,4.14-2.55,5.22-2.29.56.13,1.05.81.9,1.62.09-.47-4.1,4.74-4.6,4.85-2.08.48-4.77-.18-6.65.77-1.35,2.31,1.61,7.33.93,7.13l1.42.42,22.09,1.45c-.13.74-.55.98-.89,1.14-.29.14-.91-.53-1.17-.5-6.4.9-12.47,2.12-18.8,4.23-3,4.81-2.18,8.59-2.44,9.1-.78,1.57-1.55,3.04-.74,4.96.97,2.29,7.97.21,4.81,3.41,2.38.02-.42,3.55,2.63,4.24.6,3.47,3.42,7.25,3.62,7.19,1.04-.3.38.02.61.57.4.92,2.69.96,2.86,2.71,2.48-.16,3.85,1.18,4.58,2.9,2.75.95,3,3.96,4.15,6.09l1.13,2.09,7.49-7.73c1.2,2.18-3.16,4.43-3.51,8.96,2.45.45,4.73.87,5.33,3.82,3.31-1.07,5.34,1.9,7.89,1.97l20.37.61c.58-2.85,1.17-4.75,1.38-7.39l1.62-20.27c.43.19.49.57.87.53l.15,8.71c.14,8.14.8,15.74,3.95,23.41l1.39,7.83,11.95-2.56c10.32.76,8.36-2.53,15.02-6.31l1.93,1.16,2.14-.63,5.27-2.54c.4-.02.63.4.83.64.16.18-.17.6-.2,1.04l-.67,10.12c.82-.14,1.5-.45,2.27,0,2.81-6.65,3.02-13.87,2.67-21.4.15-.92-.09-2.34.39-2.85.45-.48,1.84-1.22,2.79-1.11,7.02.82,13.69.47,20.29-1.35,1.03.54,2.17.4,3.18.19.08.61.02,1.31-.5,1.83l-14.08,13.99c-3.04,3.02-6.73,5.26-9.07,8.88l-3.1,3.24c-1.03-1.01-2.03-.45-2.82-.97l-2.06,4.85-35.37-1.32c-4.34-1.79-6.54-8.04-7.73-7.92s-2.8,6.38-5.48,6.54l-10.41.64c-5.38.33-10.15.72-15.49,2.19-3.14.87-7.62.86-10.83.08-2.95-1.18-3.85-5.14-6.2-6.63-2.58.57-3.1,5.36-5.17,6.12s-15.98-.69-16.94-1.64c-1.04-1.02-1.66-2.74-1.93-4.36l-1.46-8.74c-.26-1.57-1.4-2.96-.05-5l.25-3.57c1.08-2.07.21-4.64.52-7.05l1.27-9.71c.24-1.86.5-4.12-.13-5.95l-.38-7.85c-.09-1.91-.47-3.76-.13-5.55l1.4-7.4c.42-2.24,1.09-4.38,1.82-6.51-.7-1.43-2.17-2.49-2.25-3.91l-.41-7.84-.51-7.74-.6-8.71-.3-7.65c-.86-4.02-1.08-8.37,2.47-11.45.28-.11.66-.01.92.02l30.19-31.05.84.55Z"/>
<path class="cls-3" d="M215.61,235.34c-2.85.4-6.28-1.66-11.15-1.44l4.61-3.64c-1.3,0-1.41-2.21-2.23-2.64-5.23.47-10.04.05-10.21-3.99.31-1.19,3.58-3.53,1.22-4.37-3.33-1.18-7.6,0-11.02-.3.14-1.57,1.01-2.91,1.48-4.39-.78-.86-2.19-.47-2.44-1.56-.39-1.71-2.45-.89-3.73-1.34-.89-.32-1.33-1.43-2.43-.78-.97.57-11.91,24.97-15.78,31.51.54-9.82,4.86-18.33,4.28-28.36.51-.78,0-1.37.4-1.5.61-.2,1.63-.57,1.82-.21.52,1.34,1.39,2.32,2.25,3.36l.35.42-.35-.42c2.99-4.01,4.32-9.78,6.79-11.04,1.99-1.01,6.98.63,10.05.99,2.25.26,4.67-.67,7.13.45l5.57.52c4.86.45,13.52,1.2,14.2,1.84.95.89.45,1.58.67,2.86,1.43,8.35.85,16.35-1.48,24.06Z"/> <path class="cls-5" d="M215.61,235.34c-2.85.4-6.28-1.66-11.15-1.44l4.61-3.64c-1.3,0-1.41-2.21-2.23-2.64-5.23.47-10.04.05-10.21-3.99.31-1.19,3.58-3.53,1.22-4.37-3.33-1.18-7.6,0-11.02-.3.14-1.57,1.01-2.91,1.48-4.39-.78-.86-2.19-.47-2.44-1.56-.39-1.71-2.45-.89-3.73-1.34-.89-.32-1.33-1.43-2.43-.78-.97.57-11.91,24.97-15.78,31.51.54-9.82,4.86-18.33,4.28-28.36.51-.78,0-1.37.4-1.5.61-.2,1.63-.57,1.82-.21.52,1.34,1.39,2.32,2.25,3.36l.35.42-.35-.42c2.99-4.01,4.32-9.78,6.79-11.04,1.99-1.01,6.98.63,10.05.99,2.25.26,4.67-.67,7.13.45l5.57.52c4.86.45,13.52,1.2,14.2,1.84.95.89.45,1.58.67,2.86,1.43,8.35.85,16.35-1.48,24.06Z"/>
<path class="cls-10" d="M214.27,272.31c-.4,1.25-2.02,2.41-1.74,4.07l2.61,3.32,1.18,21.98,1.27,18.14c.18,2.6-1.32,4.68-3.5,5.32-1,.22-2.14.35-3.18-.19.51-.44.48-1.12.22-2.05-4.36.28-8.77-.13-13.07-.76.11-1.05.62-2.02,2.21-2.08l.74-3.75c-.81-7.92,5.25-8.4,8.61-13.35.28-1.42-.34-2.79-1.11-3.19-2.26-1.16-5.3,2.44-7.39,2.41.92-2.43,2.85-3.65,4.07-5.7s-2.95-4.8-1.74-7.9c.76-1.94,6.53.65,7.99-8.08h-32.08c-1.72,0-3.48.96-5.24-.34l17.39-2.78c6.89-1.1,13.55-2.49,20.13-5,1-.38,1.8-1.05,2.63-.07Z"/> <path class="cls-5" d="M214.27,272.31c-.4,1.25-2.02,2.41-1.74,4.07l2.61,3.32,1.18,21.98,1.27,18.14c.18,2.6-1.32,4.68-3.5,5.32-1,.22-2.14.35-3.18-.19.51-.44.48-1.12.22-2.05-4.36.28-8.77-.13-13.07-.76.11-1.05.62-2.02,2.21-2.08l.74-3.75c-.81-7.92,5.25-8.4,8.61-13.35.28-1.42-.34-2.79-1.11-3.19-2.26-1.16-5.3,2.44-7.39,2.41.92-2.43,2.85-3.65,4.07-5.7s-2.95-4.8-1.74-7.9c.76-1.94,6.53.65,7.99-8.08h-32.08c-1.72,0-3.48.96-5.24-.34l17.39-2.78c6.89-1.1,13.55-2.49,20.13-5,1-.38,1.8-1.05,2.63-.07Z"/>
<path class="cls-13" d="M148.5,208.91c.09.44.57,1.1.5,1.88l-3.51,37.22c-.12,1.22.79,2.51.08,3.4-.17-.02-.55-.05-.84.02l-1.18-20.95c-.39-6.98-.92-13.64-3.12-20.16.91-.89-.15-2.09.35-2.63.19.06.42.03.57.14,2.12,1.41,4.66,1.34,7.15,1.07Z"/> <path class="cls-1" d="M148.5,208.91c.09.44.57,1.1.5,1.88l-3.51,37.22c-.12,1.22.79,2.51.08,3.4-.17-.02-.55-.05-.84.02l-1.18-20.95c-.39-6.98-.92-13.64-3.12-20.16.91-.89-.15-2.09.35-2.63.19.06.42.03.57.14,2.12,1.41,4.66,1.34,7.15,1.07Z"/>
<path class="cls-1" d="M215,239.76c-.37,2.69-1.06,4.75-4.09,5.88,0,1.06-1.32,1.19-1.19,1.7.25,1,1.38,1.43,2.04,1.7,1.6.65,5.23,1.29,5.03,3.38-1.45,1.14-3-.66-4.47-.65-3.89.02-7.85.3-11.61,1.17-4.69,1.1-8.9,3.39-13.76,2.48l6.29-4.39c6.53-3.41,12.89-6.83,18.95-11.04.83-.58,2.12-.11,2.8-.24Z"/> <path class="cls-5" d="M215,239.76c-.37,2.69-1.06,4.75-4.09,5.88,0,1.06-1.32,1.19-1.19,1.7.25,1,1.38,1.43,2.04,1.7,1.6.65,5.23,1.29,5.03,3.38-1.45,1.14-3-.66-4.47-.65-3.89.02-7.85.3-11.61,1.17-4.69,1.1-8.9,3.39-13.76,2.48l6.29-4.39c6.53-3.41,12.89-6.83,18.95-11.04.83-.58,2.12-.11,2.8-.24Z"/>
<path class="cls-4" d="M102.09,290.2c-4.07-1.73-6.58-3.95-4.63-4.93,1.42-.72,2.78.75,4.55.4l.03-24.78-4.17,3.06c-.46.33-1.29.2-1.58,0-.43-.29-.48-1.15-.15-1.65l1.7-2.61c-1.27.15-1.65.71-2.02.94-1.68,1.06-3.15-2.89,6.55-7.13l.32-3.56,2.07.29c.45.06.61.77.8,2.15,9.43-3.46,24.09-2.78,30.78,5.14s5,18.9-2.59,25.38c-6.31,5.39-14.24,8.38-22.83,8.98l.87,22.21c.03.89-.81,1.65-1.24,1.95-.48.34-1.7,0-2.11-.96-.15-.34.58-.64.5-1.16-.04-.23.06-.27-.26-.39-.21-.08-.67-.16-.85.03-.23.25.25.88-.09,1.4l-1.25,1.93c-.4.62-1.47.15-2.45-.38-1.53-.82-.95-3.2-1.01-4.64l-.93-21.66ZM131.84,268.69c-.17-9.31-11.39-12.74-20.83-10.93-.29,9.08-1.11,17.3-.24,26.28,9.58-2.04,21.23-6.33,21.06-15.35Z"/> <path class="cls-2" d="M102.09,290.2c-4.07-1.73-6.58-3.95-4.63-4.93,1.42-.72,2.78.75,4.55.4l.03-24.78-4.17,3.06c-.46.33-1.29.2-1.58,0-.43-.29-.48-1.15-.15-1.65l1.7-2.61c-1.27.15-1.65.71-2.02.94-1.68,1.06-3.15-2.89,6.55-7.13l.32-3.56,2.07.29c.45.06.61.77.8,2.15,9.43-3.46,24.09-2.78,30.78,5.14s5,18.9-2.59,25.38c-6.31,5.39-14.24,8.38-22.83,8.98l.87,22.21c.03.89-.81,1.65-1.24,1.95-.48.34-1.7,0-2.11-.96-.15-.34.58-.64.5-1.16-.04-.23.06-.27-.26-.39-.21-.08-.67-.16-.85.03-.23.25.25.88-.09,1.4l-1.25,1.93c-.4.62-1.47.15-2.45-.38-1.53-.82-.95-3.2-1.01-4.64l-.93-21.66ZM131.84,268.69c-.17-9.31-11.39-12.74-20.83-10.93-.29,9.08-1.11,17.3-.24,26.28,9.58-2.04,21.23-6.33,21.06-15.35Z"/>
<path class="cls-7" d="M183.9,295.43c1.76,1.14-.79,5.45.73,4.61.19-.11,0-.72.87-.89l.88-1.4c.33-.52,1.23-.45,1.75.07,2.08,2.09-4.63,13.02-14.45,15.95-7.96,2.38-16.35,1.3-22.33-4.17-5.64-5.17-8.09-12.68-8.44-20.4-.82-17.89,11.9-43.46,29.61-38.76,11.84,3.14,13.7,15.66,10.88,15.11-1.41-.27-.98-3.78-2.76-4.16,1.07,3.28.2,6.69-.55,6.94-3.93,1.28-.95-4.98-6.76-8.1-8.45-4.53-16.14,5.24-19.27,15.53-2.49,8.17-2.2,17.28,2.58,24.6,2.98,4.56,8.43,6.7,13.47,5.57,9.71-2.19,11.26-12.13,13.8-10.5ZM177.02,256.36l3.08,4.12c1.58-.8-.62-4.44-3.08-4.12Z"/> <path class="cls-4" d="M183.9,295.43c1.76,1.14-.79,5.45.73,4.61.19-.11,0-.72.87-.89l.88-1.4c.33-.52,1.23-.45,1.75.07,2.08,2.09-4.63,13.02-14.45,15.95-7.96,2.38-16.35,1.3-22.33-4.17-5.64-5.17-8.09-12.68-8.44-20.4-.82-17.89,11.9-43.46,29.61-38.76,11.84,3.14,13.7,15.66,10.88,15.11-1.41-.27-.98-3.78-2.76-4.16,1.07,3.28.2,6.69-.55,6.94-3.93,1.28-.95-4.98-6.76-8.1-8.45-4.53-16.14,5.24-19.27,15.53-2.49,8.17-2.2,17.28,2.58,24.6,2.98,4.56,8.43,6.7,13.47,5.57,9.71-2.19,11.26-12.13,13.8-10.5ZM177.02,256.36l3.08,4.12c1.58-.8-.62-4.44-3.08-4.12Z"/>
<path class="cls-12" d="M198.07,322.14c4.3.63,8.71,1.03,13.07.76.25.92.29,1.6-.22,2.05-6.61,1.82-13.27,2.17-20.29,1.35-.95-.11-2.34.63-2.79,1.11-.48.51-.23,1.94-.39,2.85.34,7.53.14,14.75-2.67,21.4-.77-.46-1.44-.14-2.27,0l.67-10.12c.03-.44.36-.86.2-1.04-.2-.24-.43-.66-.83-.64,1.02-1.02.42-2.37.25-3.79l-1.6-13.76c-.24-2.03,3.16-2.15,4.63-1.94l12.24,1.78Z"/> <path class="cls-1" d="M198.07,322.14c4.3.63,8.71,1.03,13.07.76.25.92.29,1.6-.22,2.05-6.61,1.82-13.27,2.17-20.29,1.35-.95-.11-2.34.63-2.79,1.11-.48.51-.23,1.94-.39,2.85.34,7.53.14,14.75-2.67,21.4-.77-.46-1.44-.14-2.27,0l.67-10.12c.03-.44.36-.86.2-1.04-.2-.24-.43-.66-.83-.64,1.02-1.02.42-2.37.25-3.79l-1.6-13.76c-.24-2.03,3.16-2.15,4.63-1.94l12.24,1.78Z"/>
<path class="cls-5" d="M85.24,240.96c-.31.13-.49-.05-.67-.55l-11.41-1.34c-.71-.24-1.12.04-1.36-.22-.26-.28-.63-1.29.01-1.44,9.3-2.77,16.87-.34,25.75-.39.82-3.72.22-7.14-.03-10.68-.44-6.17,1.19-12.1,2.27-17.86.58-.57,1.61.24,1.55.94-.04.5.02,1.16-.37,1.85l.49,14.85c.19,5.76,2.28,12.92-1.27,15.68l-13.86-1.31c-.47-.04-.87.37-1.11.47Z"/> <path class="cls-1" d="M85.24,240.96c-.31.13-.49-.05-.67-.55l-11.41-1.34c-.71-.24-1.12.04-1.36-.22-.26-.28-.63-1.29.01-1.44,9.3-2.77,16.87-.34,25.75-.39.82-3.72.22-7.14-.03-10.68-.44-6.17,1.19-12.1,2.27-17.86.58-.57,1.61.24,1.55.94-.04.5.02,1.16-.37,1.85l.49,14.85c.19,5.76,2.28,12.92-1.27,15.68l-13.86-1.31c-.47-.04-.87.37-1.11.47Z"/>
<path class="cls-8" d="M131.84,268.69c.17,9.02-11.48,13.31-21.06,15.35-.87-8.98-.05-17.2.24-26.28,9.44-1.81,20.65,1.63,20.83,10.93Z"/> <path class="cls-3" d="M131.84,268.69c.17,9.02-11.48,13.31-21.06,15.35-.87-8.98-.05-17.2.24-26.28,9.44-1.81,20.65,1.63,20.83,10.93Z"/>
<path class="cls-6" d="M177.02,256.36c2.46-.32,4.66,3.32,3.08,4.12l-3.08-4.12Z"/> <path class="cls-1" d="M177.02,256.36c2.46-.32,4.66,3.32,3.08,4.12l-3.08-4.12Z"/>
<path class="cls-9" d="M211.77,249.04c1.6.65,5.23,1.29,5.03,3.38l-1.19,12.44c-.25,2.67-.6,5.14-1.34,7.45-.4,1.25-2.02,2.41-1.74,4.07l2.61,3.32,1.18,21.98,1.27,18.14c.18,2.6-1.32,4.68-3.5,5.32-1,.22-2.14.35-3.18-.19-6.61,1.82-13.27,2.17-20.29,1.35-.95-.11-2.34.63-2.79,1.11-.48.51-.23,1.94-.39,2.85.34,7.53.14,14.75-2.67,21.4-.09.1-.19.28-.26.44l-2.06,4.85-35.37-1.32c-4.34-1.79-6.54-8.04-7.73-7.92s-2.8,6.38-5.48,6.54l-10.41.64c-5.38.33-10.15.72-15.49,2.19-3.14.87-7.62.86-10.83.08-2.95-1.18-3.85-5.14-6.2-6.63"/> <path class="cls-6" d="M211.77,249.04c1.6.65,5.23,1.29,5.03,3.38l-1.19,12.44c-.25,2.67-.6,5.14-1.34,7.45-.4,1.25-2.02,2.41-1.74,4.07l2.61,3.32,1.18,21.98,1.27,18.14c.18,2.6-1.32,4.68-3.5,5.32-1,.22-2.14.35-3.18-.19-6.61,1.82-13.27,2.17-20.29,1.35-.95-.11-2.34.63-2.79,1.11-.48.51-.23,1.94-.39,2.85.34,7.53.14,14.75-2.67,21.4-.09.1-.19.28-.26.44l-2.06,4.85-35.37-1.32c-4.34-1.79-6.54-8.04-7.73-7.92s-2.8,6.38-5.48,6.54l-10.41.64c-5.38.33-10.15.72-15.49,2.19-3.14.87-7.62.86-10.83.08-2.95-1.18-3.85-5.14-6.2-6.63"/>
<path class="cls-9" d="M100.69,203.88c4.32.66,9.46,1.37,13.92,1.69l14.97,1.06c3.88.27,7.66-.03,11.21,1.08.19.06.42.03.57.14,2.12,1.41,4.66,1.34,7.15,1.07l19.5-2.14c1.96.84,1.89,4.16,2.43,5.54s1.39,2.32,2.25,3.36l.35.42"/> <path class="cls-6" d="M100.69,203.88c4.32.66,9.46,1.37,13.92,1.69l14.97,1.06c3.88.27,7.66-.03,11.21,1.08.19.06.42.03.57.14,2.12,1.41,4.66,1.34,7.15,1.07l19.5-2.14c1.96.84,1.89,4.16,2.43,5.54s1.39,2.32,2.25,3.36l.35.42"/>
<path class="cls-9" d="M100.68,203.82l-.39.4-.78.79-30.19,31.05c.27,1.12,1.58,1.24,2.49,1.36,9.3-2.77,16.87-.34,25.75-.39.82-3.72.22-7.14-.03-10.68-.44-6.17,1.19-12.1,2.27-17.86l.55-2.93.11-.58.22-1.16Z"/> <path class="cls-6" d="M100.68,203.82l-.39.4-.78.79-30.19,31.05c.27,1.12,1.58,1.24,2.49,1.36,9.3-2.77,16.87-.34,25.75-.39.82-3.72.22-7.14-.03-10.68-.44-6.17,1.19-12.1,2.27-17.86l.55-2.93.11-.58.22-1.16Z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -3,34 +3,22 @@
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: #deb96d; fill: #0db3c8;
} }
.cls-2 { .cls-2 {
fill: #d5b06b; fill: #007988;
} }
.cls-3 { .cls-3 {
fill: #d0ab66; fill: #0c96a8;
} }
.cls-4 { .cls-4 {
fill: #e6c680; fill: #3a170d;
} }
.cls-5 { .cls-5 {
fill: #c49d60;
}
.cls-6 {
fill: #d4ad6b;
}
.cls-7 {
fill: #efd79c;
}
.cls-8 {
fill: none; fill: none;
stroke: #381507; stroke: #381507;
stroke-linecap: round; stroke-linecap: round;
@@ -38,39 +26,23 @@
stroke-width: 2.2px; stroke-width: 2.2px;
} }
.cls-9 { .cls-6 {
fill: #3a170d;
}
.cls-10 {
fill: #cfa864;
}
.cls-11 {
fill: #d0a967;
}
.cls-12 {
fill: #d4b270;
}
.cls-13 {
fill: #3c1b0d; fill: #3c1b0d;
} }
</style> </style>
</defs> </defs>
<path class="cls-7" d="M214.65,239.1l-2.58-.1c-3.79,2.1-7.73,4.08-11.61,6.32l-6.56,3.79c-2.2,1.27-5.02,2.22-6.12,5.03l12.97-3.01,11.9-1.19c.44-.63.06-1.58.21-2.2,1.11.52,3.9,1.16,3.71,3.1l-1.82,18.71c-1.5,1.84-3.42,2.56-4.11,4.45-.39.04-.84-.28-1.1-.57-.2-.23.18-1.3-.21-1.23l-13.32,2.67-24.66,3.99c2.94.91,5.47.34,8.11.31l30.61-.32c.58,0,1.24.32,1.55.45s.06,1.13-.11,1.49l-5.94,12.1-2.34,5.89c1.31.57,2.15-1.03,3.19-1.23,1.71-.34,2.18,1.87,2.29,2.91.19,1.76-.21,2.53-1.63,3.75l-3.61,3.1c-2.25,1.93-2.51,5.48-3.11,8.25l-2.36,4.83-14.21-1.65c-1.16-.13-2.3.56-2.59,1.21-1.13,2.5,3.49,19.87,1.51,29.66l2.38-.32.4,1.88c-.02,1.11-.76,2.05-1.49,2.86-.9,1-2.37.8-3.88.8l-24.17.07c-3.64.01-6.98.15-10.19-1.01l-.55-.2c.38-.35-.12-1.32,1-2.02l-3.02-10.07c-.55-1.82-.65-4.03-.68-6.05l-.38-23.49c0-.35.21-1.3-.12-1.78-.19-.26,1.36-1.02-.61-1.17l-4.19,32.02c-.53,4.08-2.01,7.72-1.64,11.98,1.56-.87,2.59-.64,3.6-.38-1.06.63-1.9,2.74-3.63,3-8.84,1.34-17.96,1.85-26.77.44l-13.88-3.27c-1.56-1.74-2.19-3.54-3.98-4.71l-6.41,7.6c-5.24-.33-10.27-1.31-15.49-2.21-2.15-4.26-.12-10.16-1.62-14.2-1.64-4.39-1.98-8.67-1.04-13.21,1.9-9.16,1.37-17.8.49-27.02-.43-4.52,3.2-9.56,5.02-12.83l.8-.58.55-.55-.55.55-.8.58c.11.87,1.36,1.26,2.07.83.33-.2.87.26,1.48-.62l30.23-5.39c1.79-.32,6.53.08,5.59-1.14-.12-.15-.41-.79-.78-.81l-16.48-.56-11.54.04c-3.89-.56-7.57.1-11.44-1.87.21,1.09-.13,1.57-.76,1.25-.22-.11-.39.11-.92.03l-2.78-19.95c-.32-2.3-1.51-5.82-.08-7.61l6.94-4.85c.64-.45.02-1.82-.8-1.97-2.71-1.37-4.11-3.91-2.9-7.27-2.45-4.5-2.75-9.25-2.39-14.17l.97-13.21c.38-5.16,5.65-2.67,12.92-.43,3.25.54,6.3-.23,9.33-1.63,8.42.55,16.41-.06,24.8-.67l16.36-1.2,8.59,1.05c.2.02.38.14.56.21-.19-.07-.37-.18-.56-.21-.54,4.1,1.56,7.36,1.72,9.19l1.11,13.13.8,17.66-.2,8.37c.25,0,.57-.14.92.02l3.92-47.24-.57.22.57-.22,19.53-1.91c1.57.6,1.43,3.49,2.12,4.44.09.12.09.4.12.6-.03-.2-.03-.48-.12-.6l-2.15.91c.17,8.51-1.44,13.81-3.15,21.63l-2.13,9.75c2.92-2.92,3.58-6.64,5.27-10.02l10.81-21.63c.85-1.69.49-4.05,3.09-4.69-.5-.7-.42-1.53-.6-2.46,1.54.33,3.13-.5,4.85.26l5.72.4,10.88,1.12,6.13.57c2.16.2,6.59-.53,8.1,2.25.4,6.45,1.14,13.48-.1,19.79l-2.24,11.36ZM168.83,303.16c-6.94.18-12.13-5.11-13.68-11.47-1.72-5.03-2.28-10.32-.88-15.55,1.12-10.54,9.73-25.45,20.19-20.69,5.19,2.36,4.68,11.87,7.33,8.34-.13.18.87-3.46,1.06-3.05-.47-1.02-.68-2.77-.56-2.97.38-.62.92-.03.9.33-.01.16.1.13.26.77.43.68.42,1.72.95,1.95,3.46,1.48,1.8-9.41-6.67-13.59-5.47-2.7-10.86-3.08-15.84-.29-7.33,4.11-11.48,11.04-14.58,18.6-3.51,8.58-5.17,17.76-2.65,26.66,1.55,5.5,3.59,11.18,8.36,15.03,6.06,4.89,14.44,6.2,22.41,3.63,13.87-4.48,18.3-21.76,11.29-15.14-.28.27.49.89.09,1.03-.25.08-.69-.15-1.14.58-.32.51.58,1.26-.3,1.42l-1.48.26,1.93-3.85c.54-1.09.15-2.4-.76-2.62-3.3-.79-4.86,10.32-16.21,10.62ZM99.04,299.48l-2.33-4.68-3.47.03c-.04,6.06,3.08,10.63,7.43,14.01,4.93,3.83,10.3,5.14,16.54,4.87,10.18-.44,19.57-6.79,21.53-16.96,1.57-8.14-2.54-15.62-9.31-19.96-7.16-4.59-16.77-5.53-20.09-10.13-1.83-2.53-1.29-5.65.09-8.51,1.08-2.23,3.67-3.76,6.68-4.4,8.68-1.84,13.61,5.15,15,4.52.46-.2,1.46-.79,1.17-1.39l-1.53-3.17c2.36.38,3.63,3.38,4.81,1.76,1.42-1.94-9.35-13.75-24.73-9.15-8.64,2.58-13.65,10.74-11.83,19.53.96,4.64,3.52,8.84,8.3,10.67l12.22,4.68c6.12,2.34,10.3,8.86,8.33,15.46-1.45,4.86-6.35,7.18-11.25,7.51-5.75.39-12.14-2.11-13.82-7.94-.66-2.3-.6-5.38-4.17-5.51-.75,4.05,1.16,6.89.42,8.75ZM103.94,315l-7.75,6.79c.97.96,1.64.88,2.27.52-.28.16,5.09-5.27,5.48-7.31Z"/> <path class="cls-1" d="M214.65,239.1l-2.58-.1c-3.79,2.1-7.73,4.08-11.61,6.32l-6.56,3.79c-2.2,1.27-5.02,2.22-6.12,5.03l12.97-3.01,11.9-1.19c.44-.63.06-1.58.21-2.2,1.11.52,3.9,1.16,3.71,3.1l-1.82,18.71c-1.5,1.84-3.42,2.56-4.11,4.45-.39.04-.84-.28-1.1-.57-.2-.23.18-1.3-.21-1.23l-13.32,2.67-24.66,3.99c2.94.91,5.47.34,8.11.31l30.61-.32c.58,0,1.24.32,1.55.45s.06,1.13-.11,1.49l-5.94,12.1-2.34,5.89c1.31.57,2.15-1.03,3.19-1.23,1.71-.34,2.18,1.87,2.29,2.91.19,1.76-.21,2.53-1.63,3.75l-3.61,3.1c-2.25,1.93-2.51,5.48-3.11,8.25l-2.36,4.83-14.21-1.65c-1.16-.13-2.3.56-2.59,1.21-1.13,2.5,3.49,19.87,1.51,29.66l2.38-.32.4,1.88c-.02,1.11-.76,2.05-1.49,2.86-.9,1-2.37.8-3.88.8l-24.17.07c-3.64.01-6.98.15-10.19-1.01l-.55-.2c.38-.35-.12-1.32,1-2.02l-3.02-10.07c-.55-1.82-.65-4.03-.68-6.05l-.38-23.49c0-.35.21-1.3-.12-1.78-.19-.26,1.36-1.02-.61-1.17l-4.19,32.02c-.53,4.08-2.01,7.72-1.64,11.98,1.56-.87,2.59-.64,3.6-.38-1.06.63-1.9,2.74-3.63,3-8.84,1.34-17.96,1.85-26.77.44l-13.88-3.27c-1.56-1.74-2.19-3.54-3.98-4.71l-6.41,7.6c-5.24-.33-10.27-1.31-15.49-2.21-2.15-4.26-.12-10.16-1.62-14.2-1.64-4.39-1.98-8.67-1.04-13.21,1.9-9.16,1.37-17.8.49-27.02-.43-4.52,3.2-9.56,5.02-12.83l.8-.58.55-.55-.55.55-.8.58c.11.87,1.36,1.26,2.07.83.33-.2.87.26,1.48-.62l30.23-5.39c1.79-.32,6.53.08,5.59-1.14-.12-.15-.41-.79-.78-.81l-16.48-.56-11.54.04c-3.89-.56-7.57.1-11.44-1.87.21,1.09-.13,1.57-.76,1.25-.22-.11-.39.11-.92.03l-2.78-19.95c-.32-2.3-1.51-5.82-.08-7.61l6.94-4.85c.64-.45.02-1.82-.8-1.97-2.71-1.37-4.11-3.91-2.9-7.27-2.45-4.5-2.75-9.25-2.39-14.17l.97-13.21c.38-5.16,5.65-2.67,12.92-.43,3.25.54,6.3-.23,9.33-1.63,8.42.55,16.41-.06,24.8-.67l16.36-1.2,8.59,1.05c.2.02.38.14.56.21-.19-.07-.37-.18-.56-.21-.54,4.1,1.56,7.36,1.72,9.19l1.11,13.13.8,17.66-.2,8.37c.25,0,.57-.14.92.02l3.92-47.24-.57.22.57-.22,19.53-1.91c1.57.6,1.43,3.49,2.12,4.44.09.12.09.4.12.6-.03-.2-.03-.48-.12-.6l-2.15.91c.17,8.51-1.44,13.81-3.15,21.63l-2.13,9.75c2.92-2.92,3.58-6.64,5.27-10.02l10.81-21.63c.85-1.69.49-4.05,3.09-4.69-.5-.7-.42-1.53-.6-2.46,1.54.33,3.13-.5,4.85.26l5.72.4,10.88,1.12,6.13.57c2.16.2,6.59-.53,8.1,2.25.4,6.45,1.14,13.48-.1,19.79l-2.24,11.36ZM168.83,303.16c-6.94.18-12.13-5.11-13.68-11.47-1.72-5.03-2.28-10.32-.88-15.55,1.12-10.54,9.73-25.45,20.19-20.69,5.19,2.36,4.68,11.87,7.33,8.34-.13.18.87-3.46,1.06-3.05-.47-1.02-.68-2.77-.56-2.97.38-.62.92-.03.9.33-.01.16.1.13.26.77.43.68.42,1.72.95,1.95,3.46,1.48,1.8-9.41-6.67-13.59-5.47-2.7-10.86-3.08-15.84-.29-7.33,4.11-11.48,11.04-14.58,18.6-3.51,8.58-5.17,17.76-2.65,26.66,1.55,5.5,3.59,11.18,8.36,15.03,6.06,4.89,14.44,6.2,22.41,3.63,13.87-4.48,18.3-21.76,11.29-15.14-.28.27.49.89.09,1.03-.25.08-.69-.15-1.14.58-.32.51.58,1.26-.3,1.42l-1.48.26,1.93-3.85c.54-1.09.15-2.4-.76-2.62-3.3-.79-4.86,10.32-16.21,10.62ZM99.04,299.48l-2.33-4.68-3.47.03c-.04,6.06,3.08,10.63,7.43,14.01,4.93,3.83,10.3,5.14,16.54,4.87,10.18-.44,19.57-6.79,21.53-16.96,1.57-8.14-2.54-15.62-9.31-19.96-7.16-4.59-16.77-5.53-20.09-10.13-1.83-2.53-1.29-5.65.09-8.51,1.08-2.23,3.67-3.76,6.68-4.4,8.68-1.84,13.61,5.15,15,4.52.46-.2,1.46-.79,1.17-1.39l-1.53-3.17c2.36.38,3.63,3.38,4.81,1.76,1.42-1.94-9.35-13.75-24.73-9.15-8.64,2.58-13.65,10.74-11.83,19.53.96,4.64,3.52,8.84,8.3,10.67l12.22,4.68c6.12,2.34,10.3,8.86,8.33,15.46-1.45,4.86-6.35,7.18-11.25,7.51-5.75.39-12.14-2.11-13.82-7.94-.66-2.3-.6-5.38-4.17-5.51-.75,4.05,1.16,6.89.42,8.75ZM103.94,315l-7.75,6.79c.97.96,1.64.88,2.27.52-.28.16,5.09-5.27,5.48-7.31Z"/>
<path class="cls-1" d="M210.63,274c-.34,2.98,4.2,2.59,4.27,3.76l2.51,42.37c.1,1.67-2.35,3.33-3.58,3.16l.11-2.9c-5.22.96-10.62.62-15.97,0l2.36-4.83c.6-2.77.86-6.32,3.11-8.25l3.61-3.1c1.42-1.22,1.82-1.99,1.63-3.75-.11-1.05-.58-3.25-2.29-2.91-1.04.2-1.88,1.81-3.19,1.23l2.34-5.89,5.94-12.1c.18-.36.42-1.36.11-1.49s-.97-.45-1.55-.45l-30.61.32c-2.64.03-5.17.59-8.11-.31l24.66-3.99,13.32-2.67c.39-.08,0,1,.21,1.23.25.29.71.61,1.1.57Z"/> <path class="cls-3" d="M210.63,274c-.34,2.98,4.2,2.59,4.27,3.76l2.51,42.37c.1,1.67-2.35,3.33-3.58,3.16l.11-2.9c-5.22.96-10.62.62-15.97,0l2.36-4.83c.6-2.77.86-6.32,3.11-8.25l3.61-3.1c1.42-1.22,1.82-1.99,1.63-3.75-.11-1.05-.58-3.25-2.29-2.91-1.04.2-1.88,1.81-3.19,1.23l2.34-5.89,5.94-12.1c.18-.36.42-1.36.11-1.49s-.97-.45-1.55-.45l-30.61.32c-2.64.03-5.17.59-8.11-.31l24.66-3.99,13.32-2.67c.39-.08,0,1,.21,1.23.25.29.71.61,1.1.57Z"/>
<path class="cls-4" d="M213.84,323.29c.69.99.45,1.15-.19,1.78l-17.39,17.12c-3.16,3.11-6.44,5.57-9.4,9.05-.38.45-.97.07-1.39-.06l-.4-1.88c1.94-6.68,3.23-13.66,2.17-20.91-.22-1.52-.28-2.52.82-3.51.9-.81,1.78-.11,3.06-.13l14.27-.23c3.03-.05,5.64-1.6,8.45-1.22Z"/> <path class="cls-1" d="M213.84,323.29c.69.99.45,1.15-.19,1.78l-17.39,17.12c-3.16,3.11-6.44,5.57-9.4,9.05-.38.45-.97.07-1.39-.06l-.4-1.88c1.94-6.68,3.23-13.66,2.17-20.91-.22-1.52-.28-2.52.82-3.51.9-.81,1.78-.11,3.06-.13l14.27-.23c3.03-.05,5.64-1.6,8.45-1.22Z"/>
<path class="cls-11" d="M69.29,278.12c.52.08.69-.14.92-.03.63.32.97-.16.76-1.25,3.87,1.96,7.55,1.31,11.44,1.87l11.54-.04,16.48.56c.38.01.66.65.78.81.93,1.22-3.8.82-5.59,1.14l-30.23,5.39c-.61.87-1.15.42-1.48.62-.71.43-1.96.04-2.07-.83l.8-.58.55-.55-.55.55.55-.55c.13-.13.21-.35.38-.39.47-.12.74-1.1.12-1.45-.29-.17-.73,0-.96-1.07-.92-1.33-3.2-2.39-3.45-4.18Z"/> <path class="cls-3" d="M69.29,278.12c.52.08.69-.14.92-.03.63.32.97-.16.76-1.25,3.87,1.96,7.55,1.31,11.44,1.87l11.54-.04,16.48.56c.38.01.66.65.78.81.93,1.22-3.8.82-5.59,1.14l-30.23,5.39c-.61.87-1.15.42-1.48.62-.71.43-1.96.04-2.07-.83l.8-.58.55-.55-.55.55.55-.55c.13-.13.21-.35.38-.39.47-.12.74-1.1.12-1.45-.29-.17-.73,0-.96-1.07-.92-1.33-3.2-2.39-3.45-4.18Z"/>
<path class="cls-10" d="M145.75,353.91l-.55-.2c-1.16-.42-2.27-1.18-3.54-1.9-1.2-.68-1.89,1.02-2.51.94-1.01-.26-2.05-.49-3.6.38-.38-4.26,1.1-7.9,1.64-11.98l4.19-32.02c1.97.15.43.91.61,1.17.33.47.11,1.42.12,1.78l.38,23.49c.03,2.02.13,4.23.68,6.05l3.02,10.07c-1.11.7-.61,1.67-1,2.02l.55.2Z"/> <path class="cls-3" d="M145.75,353.91l-.55-.2c-1.16-.42-2.27-1.18-3.54-1.9-1.2-.68-1.89,1.02-2.51.94-1.01-.26-2.05-.49-3.6.38-.38-4.26,1.1-7.9,1.64-11.98l4.19-32.02c1.97.15.43.91.61,1.17.33.47.11,1.42.12,1.78l.38,23.49c.03,2.02.13,4.23.68,6.05l3.02,10.07c-1.11.7-.61,1.67-1,2.02l.55.2Z"/>
<path class="cls-6" d="M181.3,203.36c.18.94.09,1.76.6,2.46-2.6.64-2.24,3-3.09,4.69l-10.81,21.63c-1.69,3.38-2.35,7.1-5.27,10.02l2.13-9.75c1.71-7.83,3.31-13.12,3.15-21.63l2.15-.91c.09.12.09.4.12.6.19,1.19.46,2.37,1.32,3.3.37-.75,1.52-.99,1.94-1.77l3.72-6.88c.54-1,.67-1.36,1.64-1.89,1.02-.56,1.31-.09,2.4.14Z"/> <path class="cls-2" d="M181.3,203.36c.18.94.09,1.76.6,2.46-2.6.64-2.24,3-3.09,4.69l-10.81,21.63c-1.69,3.38-2.35,7.1-5.27,10.02l2.13-9.75c1.71-7.83,3.31-13.12,3.15-21.63l2.15-.91c.09.12.09.4.12.6.19,1.19.46,2.37,1.32,3.3.37-.75,1.52-.99,1.94-1.77l3.72-6.88c.54-1,.67-1.36,1.64-1.89,1.02-.56,1.31-.09,2.4.14Z"/>
<path class="cls-3" d="M147.94,207.55l.57-.22-3.92,47.24c-.34-.16-.67-.01-.92-.02l.2-8.37-.8-17.66-1.11-13.13c-.15-1.82-2.26-5.09-1.72-9.19.2.02.38.14.56.21,2.24.82,4.78.99,7.14,1.13Z"/> <path class="cls-3" d="M147.94,207.55l.57-.22-3.92,47.24c-.34-.16-.67-.01-.92-.02l.2-8.37-.8-17.66-1.11-13.13c-.15-1.82-2.26-5.09-1.72-9.19.2.02.38.14.56.21,2.24.82,4.78.99,7.14,1.13Z"/>
<path class="cls-2" d="M214.65,239.1c-.45,2.26-2.11,3.94-3.91,5.65-.84.16-1.05.39-1.13.79-.15.82.08,1.71,1.03,1.66.76-.04,1.5.2,2.23.54-.16.62.22,1.57-.21,2.2l-11.9,1.19-12.97,3.01c1.1-2.81,3.92-3.76,6.12-5.03l6.56-3.79c3.88-2.24,7.82-4.22,11.61-6.32l2.58.1Z"/> <path class="cls-2" d="M214.65,239.1c-.45,2.26-2.11,3.94-3.91,5.65-.84.16-1.05.39-1.13.79-.15.82.08,1.71,1.03,1.66.76-.04,1.5.2,2.23.54-.16.62.22,1.57-.21,2.2l-11.9,1.19-12.97,3.01c1.1-2.81,3.92-3.76,6.12-5.03l6.56-3.79c3.88-2.24,7.82-4.22,11.61-6.32l2.58.1Z"/>
<path class="cls-9" d="M99.04,299.48c.73-1.86-1.18-4.7-.42-8.75,3.57.13,3.51,3.2,4.17,5.51,1.68,5.83,8.07,8.33,13.82,7.94,4.9-.33,9.79-2.65,11.25-7.51,1.97-6.6-2.21-13.11-8.33-15.46l-12.22-4.68c-4.78-1.83-7.34-6.03-8.3-10.67-1.82-8.79,3.19-16.95,11.83-19.53,15.38-4.6,26.15,7.2,24.73,9.15-1.18,1.62-2.45-1.38-4.81-1.76l1.53,3.17c.29.6-.71,1.18-1.17,1.39-1.39.62-6.32-6.36-15-4.52-3.02.64-5.61,2.16-6.68,4.4-1.38,2.86-1.93,5.98-.09,8.51,3.33,4.59,12.93,5.53,20.09,10.13,6.77,4.34,10.89,11.82,9.31,19.96-1.96,10.18-11.36,16.52-21.53,16.96-6.25.27-11.61-1.04-16.54-4.87-4.35-3.38-7.47-7.95-7.43-14.01l3.47-.03,2.33,4.68Z"/> <path class="cls-4" d="M99.04,299.48c.73-1.86-1.18-4.7-.42-8.75,3.57.13,3.51,3.2,4.17,5.51,1.68,5.83,8.07,8.33,13.82,7.94,4.9-.33,9.79-2.65,11.25-7.51,1.97-6.6-2.21-13.11-8.33-15.46l-12.22-4.68c-4.78-1.83-7.34-6.03-8.3-10.67-1.82-8.79,3.19-16.95,11.83-19.53,15.38-4.6,26.15,7.2,24.73,9.15-1.18,1.62-2.45-1.38-4.81-1.76l1.53,3.17c.29.6-.71,1.18-1.17,1.39-1.39.62-6.32-6.36-15-4.52-3.02.64-5.61,2.16-6.68,4.4-1.38,2.86-1.93,5.98-.09,8.51,3.33,4.59,12.93,5.53,20.09,10.13,6.77,4.34,10.89,11.82,9.31,19.96-1.96,10.18-11.36,16.52-21.53,16.96-6.25.27-11.61-1.04-16.54-4.87-4.35-3.38-7.47-7.95-7.43-14.01l3.47-.03,2.33,4.68Z"/>
<path class="cls-13" d="M168.83,303.16c11.35-.3,12.91-11.42,16.21-10.62.9.22,1.3,1.53.76,2.62l-1.93,3.85,1.48-.26c.88-.16-.02-.91.3-1.42.46-.73.9-.5,1.14-.58.4-.14-.37-.76-.09-1.03,7.01-6.62,2.58,10.66-11.29,15.14-7.97,2.58-16.35,1.27-22.41-3.63-4.76-3.85-6.8-9.53-8.36-15.03-2.51-8.9-.86-18.09,2.65-26.66,3.1-7.56,7.25-14.49,14.58-18.6,4.98-2.79,10.38-2.41,15.84.29,8.47,4.18,10.13,15.07,6.67,13.59-.52-.22-.52-1.27-.95-1.95-.16-.64-.27-.62-.26-.77.02-.36-.52-.95-.9-.33-.12.2.09,1.95.56,2.97-.19-.41-1.19,3.23-1.06,3.05-2.65,3.53-2.14-5.98-7.33-8.34-10.46-4.76-19.07,10.15-20.19,20.69-1.4,5.23-.84,10.52.88,15.55,1.55,6.36,6.74,11.66,13.68,11.47Z"/> <path class="cls-6" d="M168.83,303.16c11.35-.3,12.91-11.42,16.21-10.62.9.22,1.3,1.53.76,2.62l-1.93,3.85,1.48-.26c.88-.16-.02-.91.3-1.42.46-.73.9-.5,1.14-.58.4-.14-.37-.76-.09-1.03,7.01-6.62,2.58,10.66-11.29,15.14-7.97,2.58-16.35,1.27-22.41-3.63-4.76-3.85-6.8-9.53-8.36-15.03-2.51-8.9-.86-18.09,2.65-26.66,3.1-7.56,7.25-14.49,14.58-18.6,4.98-2.79,10.38-2.41,15.84.29,8.47,4.18,10.13,15.07,6.67,13.59-.52-.22-.52-1.27-.95-1.95-.16-.64-.27-.62-.26-.77.02-.36-.52-.95-.9-.33-.12.2.09,1.95.56,2.97-.19-.41-1.19,3.23-1.06,3.05-2.65,3.53-2.14-5.98-7.33-8.34-10.46-4.76-19.07,10.15-20.19,20.69-1.4,5.23-.84,10.52.88,15.55,1.55,6.36,6.74,11.66,13.68,11.47Z"/>
<path class="cls-5" d="M197.98,320.39c5.36.62,10.75.96,15.97,0l-.11,2.9c-2.81-.38-5.42,1.17-8.45,1.22l-14.27.23c-1.27.02-2.16-.67-3.06.13-1.1.99-1.04,1.99-.82,3.51,1.06,7.25-.23,14.24-2.17,20.91l-2.38.32c1.98-9.79-2.64-27.16-1.51-29.66.29-.65,1.43-1.35,2.59-1.21l14.21,1.65Z"/> <path class="cls-2" d="M197.98,320.39c5.36.62,10.75.96,15.97,0l-.11,2.9c-2.81-.38-5.42,1.17-8.45,1.22l-14.27.23c-1.27.02-2.16-.67-3.06.13-1.1.99-1.04,1.99-.82,3.51,1.06,7.25-.23,14.24-2.17,20.91l-2.38.32c1.98-9.79-2.64-27.16-1.51-29.66.29-.65,1.43-1.35,2.59-1.21l14.21,1.65Z"/>
<path class="cls-12" d="M103.94,315c-.39,2.04-5.76,7.47-5.48,7.31-.63.36-1.3.44-2.27-.52l7.75-6.79Z"/> <path class="cls-3" d="M103.94,315c-.39,2.04-5.76,7.47-5.48,7.31-.63.36-1.3.44-2.27-.52l7.75-6.79Z"/>
<path class="cls-8" d="M210.64,247.2c.76-.04,1.5.2,2.23.54,1.11.52,3.9,1.16,3.71,3.1l-1.82,18.71c-1.5,1.84-3.42,2.56-4.11,4.45-.34,2.98,4.2,2.59,4.27,3.76l2.51,42.37c.1,1.67-2.35,3.33-3.58,3.16-2.81-.38-5.42,1.17-8.45,1.22l-14.27.23c-1.27.02-2.16-.67-3.06.13-1.1.99-1.04,1.99-.82,3.51,1.06,7.25-.23,14.24-2.17,20.91l.4,1.88c-.02,1.11-.76,2.05-1.49,2.86-.9,1-2.37.8-3.88.8l-24.17.07c-3.64.01-6.98.15-10.19-1.01l-.55-.2c-1.16-.42-2.27-1.18-3.54-1.9-1.2-.68-1.89,1.02-2.51.94-1.06.63-1.9,2.74-3.63,3-8.84,1.34-17.96,1.85-26.77.44l-13.88-3.27c-1.56-1.74-2.19-3.54-3.98-4.71l-6.41,7.6c-5.24-.33-10.27-1.31-15.49-2.21-2.15-4.26-.12-10.16-1.62-14.2-1.64-4.39-1.98-8.67-1.04-13.21,1.9-9.16,1.37-17.8.49-27.02-.43-4.52,3.2-9.56,5.02-12.83l.8-.58.55-.55c.13-.13.21-.35.38-.39"/> <path class="cls-5" d="M210.64,247.2c.76-.04,1.5.2,2.23.54,1.11.52,3.9,1.16,3.71,3.1l-1.82,18.71c-1.5,1.84-3.42,2.56-4.11,4.45-.34,2.98,4.2,2.59,4.27,3.76l2.51,42.37c.1,1.67-2.35,3.33-3.58,3.16-2.81-.38-5.42,1.17-8.45,1.22l-14.27.23c-1.27.02-2.16-.67-3.06.13-1.1.99-1.04,1.99-.82,3.51,1.06,7.25-.23,14.24-2.17,20.91l.4,1.88c-.02,1.11-.76,2.05-1.49,2.86-.9,1-2.37.8-3.88.8l-24.17.07c-3.64.01-6.98.15-10.19-1.01l-.55-.2c-1.16-.42-2.27-1.18-3.54-1.9-1.2-.68-1.89,1.02-2.51.94-1.06.63-1.9,2.74-3.63,3-8.84,1.34-17.96,1.85-26.77.44l-13.88-3.27c-1.56-1.74-2.19-3.54-3.98-4.71l-6.41,7.6c-5.24-.33-10.27-1.31-15.49-2.21-2.15-4.26-.12-10.16-1.62-14.2-1.64-4.39-1.98-8.67-1.04-13.21,1.9-9.16,1.37-17.8.49-27.02-.43-4.52,3.2-9.56,5.02-12.83l.8-.58.55-.55c.13-.13.21-.35.38-.39"/>
<path class="cls-8" d="M172.5,214.73l-.9-.97c-.85-.92-1.12-2.1-1.32-3.3-.03-.2-.03-.48-.12-.6-.7-.95-.55-3.83-2.12-4.44l-19.53,1.91-.57.22c-2.36-.14-4.9-.31-7.14-1.13-.19-.07-.37-.18-.56-.21l-8.59-1.05-16.36,1.2c-8.38.62-16.38,1.23-24.8.67-3.03,1.4-6.08,2.17-9.33,1.63-7.26-2.24-12.54-4.74-12.92.43l-.97,13.21c-.36,4.92-.07,9.67,2.39,14.17-1.22,3.36.19,5.9,2.9,7.27"/> <path class="cls-5" d="M172.5,214.73l-.9-.97c-.85-.92-1.12-2.1-1.32-3.3-.03-.2-.03-.48-.12-.6-.7-.95-.55-3.83-2.12-4.44l-19.53,1.91-.57.22c-2.36-.14-4.9-.31-7.14-1.13-.19-.07-.37-.18-.56-.21l-8.59-1.05-16.36,1.2c-8.38.62-16.38,1.23-24.8.67-3.03,1.4-6.08,2.17-9.33,1.63-7.26-2.24-12.54-4.74-12.92.43l-.97,13.21c-.36,4.92-.07,9.67,2.39,14.17-1.22,3.36.19,5.9,2.9,7.27"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -47,11 +47,11 @@
} }
</style> </style>
</defs> </defs>
<path class="cls-9" d="M176.79,209.43l-2.01.07-2.45,11.42c.4,3.9-.63,6.69-1.9,10.36l-3.61,10.39c.59.78.74,1.77,1.37.7l7.89-16.39,7.62-10.14c2.27.08,3.91.46,5.82.02-.48,1.32-.34,1.86-.19,3.24,2.27-.16,4.39.54,6.42,1.88,1.42-1.07,2.79-1.65,4.6-1.66h3.49c.68.88-.13,2.84-1.05,3.79-.72.74.87,3.35,1.92,3.32l6.17-.17c1.75,1.96,1.05,4.36.98,6.82-.21,7.02-6.11,2.65-10.61,6.97-.68.65-.91,2.18-.23,2.68.84.62,4.26-1.19,5.18,1.09.05.12-2.19,4.54-4.01,7.24l3.5,4.78c.67.91,1.01,1.73,1.7,1.94,2.5.74,3.42-1.07,5.16.15l-.16-6.2c5.06-.66.3,10.94,7.05,9.19-.39,1.97-.65,4.21-1.67,6.09l-.88,1.61-23.67,3.92-26.09,5.6,28.81-1.28,21.9,1.79,2.66,21.59c.23,1.91.61,4.63,0,6.52-.9,2.75-4.75,3.45-6.85,4.52-.11-.6-.55-.77-.29-.96.27-.2.75-.49.46-.76-1.69-1.58-3.7-.88-5.42-1.5l-25.61-9.14c-2.01-.72-4.18-2.11-5.81.36,9.47,1.99,17.53,6.65,23.36,13.99,3.22,1.6,5.73,3.69,8.19,6.23,2.46,1.69,4.62,3.8,5.34,6.49,1.07-.18.92.5,1.05.83.15.38-.61.67-.62,1-.12,4.43-3.89,4.36-7.11,9.5-2.55,4.08-7.38,4.35-9.21,6.15l-.94.92c-.16.16.8.55.24.65-.67.12-1.28.23-1.51.37-.2.12.32.78-.29.87-1.03.15-2.52-.07-3.24.26-.61.28-1.54,1.08-1.53,2.3s3.21,1.69,4.07.81c-.22,1.01.07,1.8.09,2.48l-6.88-.51c-3.33-.25-6.05-1.19-8.14-3.56l1.22-1.88c.25-.38-.05-1.81-.78-2.58l-7.92-8.31-1.79-6.55c1.07-.94.71-1.26-.44-1.1.06,2.16-1.07,5.3.01,7.63,2.52,5.41,2.99,11.11,2.18,16.92l-13.46,1.33c-2.91.29-5.41-.37-7.43-2.26l-.56-.52c.46-.08,1.32-.85.89-.92-.9-.15-.17-.72-.3-1.29-2.17-9.59-2.64-19.05-3.8-28.66l-2.19-18.19c-.03-.27-.5-.91-.37-1.27.06-.16-.25-.18-.89-.27.05,8.57-.38,17.06-1.41,25.88l-2.92,25.11c-2.05-1.12-1.73-2.64-2.66-3.77-2.42-1.3-5.35-.57-7.72.19-.82-1.15-2.95-.7-3.79-1.24l-7.41-4.74c-1.04-.66-1.4-2.15-1.42-3.1-.05-2.5,5.64-4.26,5.39-8.93l-4.61,2.69c-2.5,1.46-5.16,2.36-7.42,4.49,1.1,1.73,2.39,6.23.43,7.73-2.2,1.69-4.11-1.83-4.14-3.67-.04-2.86-4.05-4.49-6.8-4.27-2.19-1.72-4.47-2.69-7.41-2.81,2.92-5.58,6.84-9.71,11.07-13.92l9.26-9.22c-4.84,1.85-8.54,5.02-12.9,7.76l-13.73,8.66c-2.37-3.34-2.85-6.15-7.7-5.29-.62-.03-1.45-1.19-2.31-.96-.3-2.98.95-6.63,3.72-8.23,2.3-1.71,4.51-3.28,7.48-2.66l9.81-4.84-3.17-6.89c-.54-1.17-2.95-1.86-4.17-1.27-1.03.5-3.22,1.58-2.74,3.27.24.84,2.09,1.72,1.17,2.66-1.77,1.82-4.46,1.82-7,1.48-3.12-5.27-4.99-4.03-6.2-5.44-1.11-1.28-.17-2.47-1.16-4.71-.95-.19-2.17.03-3.09-.52l.47-8.25c5.41-1.54,11-1.66,16.65-2.11l13.76-1.11,23.43-2.65c.72-.08,1.55-.2,1.17-1.12l-34.67-.55-10.78-.52-6.8-1.66c.07.32.09.89.3,1.07.4.35-.32.87-.61,1.11-.78-.59-2.15-1.21-2.35-2.59l-1.83-12.66c-.25-1.72.79-3.24,1.79-4.12.41-.03,1.02.46.89.93-.09.34-.92.63-.78,1.42l24.02-6.49,13.85-1.37,22.02-.39-2.6-11.6c1.4-.26,3.69-1.85,4.49-2.9.89-1.16-.7-2.67-.56-3.76l1.42-10.88c2.44.37,3.7.45,5.42.41l.57,3.78,1.87,20.67c.26,2.86-.56,5.85,1.49,8.37.13-9.58.68-18.19,1.32-27.3l1.78-21.14c-.18-.34.15-.71.44-.92.45-.32.07-.8-.36-1.29.1.71-.38.67-.55.8.16-.14.65-.09.55-.8l26.18-2.61c2.68-.27,3.68,4.3,4.17,6.14Z"/> <path class="cls-9" d="M176.79,209.43l-2.01.07-2.45,11.42c.4,3.9-.63,6.69-1.9,10.36l-3.61,10.39c.59.78.74,1.77,1.37.7l7.89-16.39,7.62-10.14c2.27.08,3.91.46,5.82.02-.48,1.32-.34,1.86-.19,3.24,2.27-.16,4.39.54,6.42,1.88,1.42-1.07,2.79-1.65,4.6-1.66h3.49c.68.88-.13,2.84-1.05,3.79-.72.74.87,3.35,1.92,3.32l6.17-.17c1.75,1.96,1.05,4.36.98,6.82-.21,7.02-6.11,2.65-10.61,6.97-.68.65-.91,2.18-.23,2.68.84.62,4.26-1.19,5.18,1.09.05.12-2.19,4.54-4.01,7.24l3.5,4.78c.67.91,1.01,1.73,1.7,1.94,2.5.74,3.42-1.07,5.16.15l-.16-6.2c5.06-.66.3,10.94,7.05,9.19-.39,1.97-.65,4.21-1.67,6.09l-.88,1.61-23.67,3.92-26.09,5.6,28.81-1.28,21.9,1.79,2.66,21.59c.23,1.91.61,4.63,0,6.52-.9,2.75-4.75,3.45-6.85,4.52-.11-.6-.55-.77-.29-.96.27-.2.75-.49.46-.76-1.69-1.58-3.7-.88-5.42-1.5l-25.61-9.14c-2.01-.72-4.18-2.11-5.81.36,9.47,1.99,17.53,6.65,23.36,13.99,3.22,1.6,5.73,3.69,8.19,6.23,2.46,1.69,4.62,3.8,5.34,6.49,1.07-.18.92.5,1.05.83.15.38-.61.67-.62,1-.12,4.43-3.89,4.36-7.11,9.5-2.55,4.08-7.38,4.35-9.21,6.15l-.94.92c-.16.16.8.55.24.65-.67.12-1.28.23-1.51.37-.2.12.32.78-.29.87-1.03.15-2.52-.07-3.24.26-.61.28-1.54,1.08-1.53,2.3.01,1.24,3.21,1.69,4.07.81-.22,1.01.07,1.8.09,2.48l-6.88-.51c-3.33-.25-6.05-1.19-8.14-3.56l1.22-1.88c.25-.38-.05-1.81-.78-2.58l-7.92-8.31-1.79-6.55c1.07-.94.71-1.26-.44-1.1.06,2.16-1.07,5.3.01,7.63,2.52,5.41,2.99,11.11,2.18,16.92l-13.46,1.33c-2.91.29-5.41-.37-7.43-2.26l-.56-.52c.46-.08,1.32-.85.89-.92-.9-.15-.17-.72-.3-1.29-2.17-9.59-2.64-19.05-3.8-28.66l-2.19-18.19c-.03-.27-.5-.91-.37-1.27.06-.16-.25-.18-.89-.27.05,8.57-.38,17.06-1.41,25.88l-2.92,25.11c-2.05-1.12-1.73-2.64-2.66-3.77-2.42-1.3-5.35-.57-7.72.19-.82-1.15-2.95-.7-3.79-1.24l-7.41-4.74c-1.04-.66-1.4-2.15-1.42-3.1-.05-2.5,5.64-4.26,5.39-8.93l-4.61,2.69c-2.5,1.46-5.16,2.36-7.42,4.49,1.1,1.73,2.39,6.23.43,7.73-2.2,1.69-4.11-1.83-4.14-3.67-.04-2.86-4.05-4.49-6.8-4.27-2.19-1.72-4.47-2.69-7.41-2.81,2.92-5.58,6.84-9.71,11.07-13.92l9.26-9.22c-4.84,1.85-8.54,5.02-12.9,7.76l-13.73,8.66c-2.37-3.34-2.85-6.15-7.7-5.29-.62-.03-1.45-1.19-2.31-.96-.3-2.98.95-6.63,3.72-8.23,2.3-1.71,4.51-3.28,7.48-2.66l9.81-4.84-3.17-6.89c-.54-1.17-2.95-1.86-4.17-1.27-1.03.5-3.22,1.58-2.74,3.27.24.84,2.09,1.72,1.17,2.66-1.77,1.82-4.46,1.82-7,1.48-3.12-5.27-4.99-4.03-6.2-5.44-1.11-1.28-.17-2.47-1.16-4.71-.95-.19-2.17.03-3.09-.52l.47-8.25c5.41-1.54,11-1.66,16.65-2.11l13.76-1.11,23.43-2.65c.72-.08,1.55-.2,1.17-1.12l-34.67-.55-10.78-.52-6.8-1.66c.07.32.09.89.3,1.07.4.35-.32.87-.61,1.11-.78-.59-2.15-1.21-2.35-2.59l-1.83-12.66c-.25-1.72.79-3.24,1.79-4.12.41-.03,1.02.46.89.93-.09.34-.92.63-.78,1.42l24.02-6.49,13.85-1.37,22.02-.39-2.6-11.6c1.4-.26,3.69-1.85,4.49-2.9.89-1.16-.7-2.67-.56-3.76l1.42-10.88c2.44.37,3.7.45,5.42.41l.57,3.78,1.87,20.67c.26,2.86-.56,5.85,1.49,8.37.13-9.58.68-18.19,1.32-27.3l1.78-21.14c-.18-.34.15-.71.44-.92.45-.32.07-.8-.36-1.29.1.71-.38.67-.55.8.16-.14.65-.09.55-.8l26.18-2.61c2.68-.27,3.68,4.3,4.17,6.14Z"/>
<path class="cls-2" d="M70.64,293.55c.93.55,2.14.33,3.09.52.99,2.25.05,3.43,1.16,4.71,1.21,1.41,3.09.17,6.2,5.44,2.54.34,5.22.34,7-1.48.92-.95-.93-1.82-1.17-2.66-.48-1.69,1.71-2.77,2.74-3.27,1.22-.6,3.63.1,4.17,1.27l3.17,6.89-9.81,4.84c-2.97-.61-5.17.95-7.48,2.66-2.77,1.6-4.02,5.25-3.72,8.23.86-.23,1.7.94,2.31.96,4.85-.86,5.33,1.94,7.7,5.29l13.73-8.66c4.36-2.75,8.06-5.91,12.9-7.76l-9.26,9.22c-4.23,4.21-8.15,8.34-11.07,13.92,2.94.13,5.21,1.09,7.41,2.81,2.75-.22,6.76,1.41,6.8,4.27.03,1.84,1.94,5.36,4.14,3.67,1.95-1.5.67-6-.43-7.73,2.26-2.13,4.92-3.03,7.42-4.49l4.61-2.69c.25,4.67-5.44,6.43-5.39,8.93.02.95.39,2.43,1.42,3.1l7.41,4.74c.84.54,2.97.08,3.79,1.24,2.37-.76,5.3-1.48,7.72-.19.94,1.13.61,2.65,2.66,3.77l2.92-25.11c1.03-8.82,1.46-17.32,1.41-25.88.64.09.95.11.89.27-.13.37.34,1,.37,1.27l2.19,18.19c1.16,9.61,1.63,19.07,3.8,28.66.13.57-.6,1.14.3,1.29.43.07-.43.84-.89.92l.56.52-.56-.52c-1-.94-2.21-2.22-3.88-2.29-1.16,1.06-1.06,3.19-2.51,4-2.11,1.17-23.76,4.92-29.64,4.22l-26.91-3.21c-4.28-.51-8.3-1.54-12.64-.83-1.01.17-1.84.08-2.48-.35-1.08-.72-2.51-11.79-5.03-19.97-.63-2.06-.71-4.42-.42-6.58l2.23-17.05,1.07-15.1Z"/> <path class="cls-2" d="M70.64,293.55c.93.55,2.14.33,3.09.52.99,2.25.05,3.43,1.16,4.71,1.21,1.41,3.09.17,6.2,5.44,2.54.34,5.22.34,7-1.48.92-.95-.93-1.82-1.17-2.66-.48-1.69,1.71-2.77,2.74-3.27,1.22-.6,3.63.1,4.17,1.27l3.17,6.89-9.81,4.84c-2.97-.61-5.17.95-7.48,2.66-2.77,1.6-4.02,5.25-3.72,8.23.86-.23,1.7.94,2.31.96,4.85-.86,5.33,1.94,7.7,5.29l13.73-8.66c4.36-2.75,8.06-5.91,12.9-7.76l-9.26,9.22c-4.23,4.21-8.15,8.34-11.07,13.92,2.94.13,5.21,1.09,7.41,2.81,2.75-.22,6.76,1.41,6.8,4.27.03,1.84,1.94,5.36,4.14,3.67,1.95-1.5.67-6-.43-7.73,2.26-2.13,4.92-3.03,7.42-4.49l4.61-2.69c.25,4.67-5.44,6.43-5.39,8.93.02.95.39,2.43,1.42,3.1l7.41,4.74c.84.54,2.97.08,3.79,1.24,2.37-.76,5.3-1.48,7.72-.19.94,1.13.61,2.65,2.66,3.77l2.92-25.11c1.03-8.82,1.46-17.32,1.41-25.88.64.09.95.11.89.27-.13.37.34,1,.37,1.27l2.19,18.19c1.16,9.61,1.63,19.07,3.8,28.66.13.57-.6,1.14.3,1.29.43.07-.43.84-.89.92l.56.52-.56-.52c-1-.94-2.21-2.22-3.88-2.29-1.16,1.06-1.06,3.19-2.51,4-2.11,1.17-23.76,4.92-29.64,4.22l-26.91-3.21c-4.28-.51-8.3-1.54-12.64-.83-1.01.17-1.84.08-2.48-.35-1.08-.72-2.51-11.79-5.03-19.97-.63-2.06-.71-4.42-.42-6.58l2.23-17.05,1.07-15.1Z"/>
<path class="cls-1" d="M138.74,206.71c-.44,1.03-1.52,1.13-2.79.95l2.89,11.57.5,4.51c-1.72.04-2.98-.04-5.42-.41l-1.42,10.88c-.14,1.09,1.45,2.61.56,3.76-.8,1.04-3.08,2.63-4.49,2.9-2.41-10.77-4.78-21.47-4.96-32.84-.01-.81-.89-1-1.61-.47-.4.29-.56-.28-.69.82-.91,7.34-.87,14.61.45,22.21l1.81,10.39c.2,1.16-.14,1.74-.66,2.45-.66.89-1.51.05-2.58.15l-6.62.66c-11.9,1.2-23.41,4.28-34.02,9.94-1.49.79-2.59,1.69-4.04,1.19l-.45-.56-.41.43-.35.37.35-.37.41-.43,47.79-50.52c5.56-.03,11.1.34,15.74,2.42Z"/> <path class="cls-1" d="M138.74,206.71c-.44,1.03-1.52,1.13-2.79.95l2.89,11.57.5,4.51c-1.72.04-2.98-.04-5.42-.41l-1.42,10.88c-.14,1.09,1.45,2.61.56,3.76-.8,1.04-3.08,2.63-4.49,2.9-2.41-10.77-4.78-21.47-4.96-32.84-.01-.81-.89-1-1.61-.47-.4.29-.56-.28-.69.82-.91,7.34-.87,14.61.45,22.21l1.81,10.39c.2,1.16-.14,1.74-.66,2.45-.66.89-1.51.05-2.58.15l-6.62.66c-11.9,1.2-23.41,4.28-34.02,9.94-1.49.79-2.59,1.69-4.04,1.19l-.45-.56-.41.43-.35.37.35-.37.41-.43,47.79-50.52c5.56-.03,11.1.34,15.74,2.42Z"/>
<path class="cls-7" d="M219.43,260.9c-6.75,1.75-1.99-9.85-7.05-9.19l.16,6.2c-1.73-1.22-2.65.59-5.16-.15-.69-.2-1.03-1.02-1.7-1.94l-3.5-4.78c1.82-2.7,4.06-7.11,4.01-7.24-.92-2.28-4.34-.47-5.18-1.09-.68-.5-.45-2.03.23-2.68,4.5-4.32,10.41.05,10.61-6.97.07-2.46.77-4.86-.98-6.82l-6.17.17c-1.05.03-2.64-2.58-1.92-3.32.92-.95,1.73-2.91,1.05-3.8h-3.49c-1.81.02-3.18.6-4.6,1.67-2.03-1.33-4.15-2.04-6.42-1.88-.15-1.38-.29-1.92.19-3.24-1.91.44-3.55.07-5.82-.02l-7.62,10.14-7.89,16.39c-.62,1.07-.78.08-1.37-.7l3.61-10.39c1.27-3.66,2.3-6.46,1.9-10.36l2.45-11.42,2.01-.07c.39,1.47,1.68,1.42,2.71,3.07l2.72-3.52c1.28-1.66,2.13-2.36,4.21-2.02,4.76.8,11.51-5.31,15.84-3.2l5.09.33,6.04.66c1.5.16,3.24-.48,4.43,1.23.92,1.32.41,2.84.7,4.58l2.01,12.15c.75,4.53-.52,9.03-.78,12.69l-.41,5.63-.11,12.64c-.02,2.58.65,4.91.18,7.23Z"/> <path class="cls-7" d="M219.43,260.9c-6.75,1.75-1.99-9.85-7.05-9.19l.16,6.2c-1.73-1.22-2.65.59-5.16-.15-.69-.2-1.03-1.02-1.7-1.94l-3.5-4.78c1.82-2.7,4.06-7.11,4.01-7.24-.92-2.28-4.34-.47-5.18-1.09-.68-.5-.45-2.03.23-2.68,4.5-4.32,10.41.05,10.61-6.97.07-2.46.77-4.86-.98-6.82l-6.17.17c-1.05.03-2.64-2.58-1.92-3.32.92-.95,1.73-2.91,1.05-3.8h-3.49c-1.81.02-3.18.6-4.6,1.67-2.03-1.33-4.15-2.04-6.42-1.88-.15-1.38-.29-1.92.19-3.24-1.91.44-3.55.07-5.82-.02l-7.62,10.14-7.89,16.39c-.62,1.07-.78.08-1.37-.7l3.61-10.39c1.27-3.66,2.3-6.46,1.9-10.36l2.45-11.42,2.01-.07c.39,1.47,1.68,1.42,2.71,3.07l2.72-3.52c1.28-1.66,2.13-2.36,4.21-2.02,4.76.8,11.51-5.31,15.84-3.2l5.09.33,6.04.66c1.5.16,3.24-.48,4.43,1.23.92,1.32.41,2.84.7,4.58l2.01,12.15c.75,4.53-.52,9.03-.78,12.69l-.41,5.63-.11,12.64c-.02,2.58.65,4.91.18,7.23Z"/>
<path class="cls-5" d="M194.84,352.09c-.02-.68-.3-1.47-.09-2.48-.86.88-4.06.43-4.07-.81s.92-2.02,1.53-2.3c.72-.33,2.21-.11,3.24-.26.61-.09.09-.75.29-.87.23-.14.84-.25,1.51-.37.55-.1-.4-.49-.24-.65l.94-.92c1.83-1.8,6.66-2.07,9.21-6.15,3.22-5.14,6.99-5.06,7.11-9.5,0-.33.77-.62.62-1-.13-.33.02-1.01-1.05-.83-.72-2.69-2.89-4.8-5.34-6.49-2.46-2.53-4.96-4.63-8.19-6.23-5.84-7.33-13.89-12-23.36-13.99,1.63-2.47,3.8-1.08,5.81-.36l25.61,9.14c1.72.61,3.72-.09,5.42,1.5.29.27-.19.56-.46.76-.25.19.18.36.29.96l-2.06,1.95c1.07.86,1.21,1.79,2.05,2.05,1.4.43,2.37,1.45,3.11,2.99.49,1.04-.89,2.78-.4,4.19l3.22,9.28-1.02,21.79-10.21-.23-13.46-1.16Z"/> <path class="cls-5" d="M194.84,352.09c-.02-.68-.3-1.47-.09-2.48-.86.88-4.06.43-4.07-.81,0-1.21.92-2.02,1.53-2.3.72-.33,2.21-.11,3.24-.26.61-.09.09-.75.29-.87.23-.14.84-.25,1.51-.37.55-.1-.4-.49-.24-.65l.94-.92c1.83-1.8,6.66-2.07,9.21-6.15,3.22-5.14,6.99-5.06,7.11-9.5,0-.33.77-.62.62-1-.13-.33.02-1.01-1.05-.83-.72-2.69-2.89-4.8-5.34-6.49-2.46-2.53-4.96-4.63-8.19-6.23-5.84-7.33-13.89-12-23.36-13.99,1.63-2.47,3.8-1.08,5.81-.36l25.61,9.14c1.72.61,3.72-.09,5.42,1.5.29.27-.19.56-.46.76-.25.19.18.36.29.96l-2.06,1.95c1.07.86,1.21,1.79,2.05,2.05,1.4.43,2.37,1.45,3.11,2.99.49,1.04-.89,2.78-.4,4.19l3.22,9.28-1.02,21.79-10.21-.23-13.46-1.16Z"/>
<path class="cls-3" d="M128.56,240.87l2.6,11.6-22.02.39-13.85,1.37-24.02,6.49c-.14-.79.69-1.08.78-1.42.12-.46-.48-.95-.89-.93l3.19-2.78.1.02.35-.37.41-.43.45.56c1.45.49,2.55-.4,4.04-1.19,10.61-5.66,22.12-8.74,34.02-9.94l6.62-.66c1.07-.11,1.92.74,2.58-.15.52-.71.86-1.29.66-2.45l-1.81-10.39c-1.32-7.6-1.36-14.87-.45-22.21.14-1.1.29-.53.69-.82.72-.53,1.6-.34,1.61.47.18,11.37,2.55,22.07,4.96,32.84Z"/> <path class="cls-3" d="M128.56,240.87l2.6,11.6-22.02.39-13.85,1.37-24.02,6.49c-.14-.79.69-1.08.78-1.42.12-.46-.48-.95-.89-.93l3.19-2.78.1.02.35-.37.41-.43.45.56c1.45.49,2.55-.4,4.04-1.19,10.61-5.66,22.12-8.74,34.02-9.94l6.62-.66c1.07-.11,1.92.74,2.58-.15.52-.71.86-1.29.66-2.45l-1.81-10.39c-1.32-7.6-1.36-14.87-.45-22.21.14-1.1.29-.53.69-.82.72-.53,1.6-.34,1.61.47.18,11.37,2.55,22.07,4.96,32.84Z"/>
<path class="cls-8" d="M216.88,268.6l-1.26,4.05c1.5,1.91,1.96,3.93,2.21,5.96l-21.9-1.79-28.81,1.28,26.09-5.6,23.67-3.92Z"/> <path class="cls-8" d="M216.88,268.6l-1.26,4.05c1.5,1.91,1.96,3.93,2.21,5.96l-21.9-1.79-28.81,1.28,26.09-5.6,23.67-3.92Z"/>
<path class="cls-8" d="M73.54,277.75c.29-.24,1.01-.76.61-1.11-.21-.18-.23-.75-.3-1.07l6.8,1.66,10.78.52,34.67.55c.38.92-.45,1.04-1.17,1.12l-23.43,2.65-13.76,1.11c-5.64.46-11.23.57-16.65,2.11.22-3.96,6.26-5.09,2.43-7.55Z"/> <path class="cls-8" d="M73.54,277.75c.29-.24,1.01-.76.61-1.11-.21-.18-.23-.75-.3-1.07l6.8,1.66,10.78.52,34.67.55c.38.92-.45,1.04-1.17,1.12l-23.43,2.65-13.76,1.11c-5.64.46-11.23.57-16.65,2.11.22-3.96,6.26-5.09,2.43-7.55Z"/>

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -8,7 +8,14 @@ var SigSelect = (function () {
var overlay, deckGrid, stage, stageCard, statBlock; var overlay, deckGrid, stage, stageCard, statBlock;
var cautionEl, cautionEffect, cautionPrev, cautionNext, cautionIndexEl; var cautionEl, cautionEffect, cautionPrev, cautionNext, cautionIndexEl;
var _flipBtn, _cautionBtn, _flipOrigLabel, _cautionOrigLabel; var _flipBtn, _cautionBtn, _flipOrigLabel, _cautionOrigLabel;
var reserveUrl, userRole, userPolarity; var reserveUrl, readyUrl, userRole, userPolarity;
var _isReady = false;
var _takeSigBtn = null;
var _glowTimer = null;
var _glowPeak = false;
var _countdownTimer = null;
var _countdownSecondsLeft = 0;
var _cautionData = []; var _cautionData = [];
var _cautionIdx = 0; var _cautionIdx = 0;
@@ -18,6 +25,10 @@ var SigSelect = (function () {
var _stageFrozen = false; // true after OK — stage locks on reserved card var _stageFrozen = false; // true after OK — stage locks on reserved card
var _requestInFlight = false; var _requestInFlight = false;
var _floatingCursors = {}; // key: cardId+posClass → portal <i> element (hover)
var _reservedFloats = {}; // key: role → portal <i> element (thumbs-up, frozen)
var _cursorPortal = null;
function getCsrf() { function getCsrf() {
var m = document.cookie.match(/csrftoken=([^;]+)/); var m = document.cookie.match(/csrftoken=([^;]+)/);
return m ? m[1] : ''; return m ? m[1] : '';
@@ -104,9 +115,15 @@ var SigSelect = (function () {
} }
}); });
stageCard.querySelector('.fan-card-name-group').textContent = group; stageCard.querySelector('.fan-card-name-group').textContent = group;
stageCard.querySelector('.fan-card-name').textContent = title;
stageCard.querySelector('.fan-card-arcana').textContent = arcana; stageCard.querySelector('.fan-card-arcana').textContent = arcana;
stageCard.querySelector('.fan-card-correspondence').textContent = corr; stageCard.querySelector('.fan-card-correspondence').textContent = ''; // shown in game-kit only
var qualifier = userPolarity === 'levity' ? 'Leavened' : 'Graven';
var isMajor = arcana.toLowerCase().indexOf('major') !== -1;
// Major arcana: qualifier sits below the title — append comma so it reads as a subtitle.
stageCard.querySelector('.fan-card-name').textContent = isMajor ? title + ',' : title;
stageCard.querySelector('.sig-qualifier-above').textContent = isMajor ? '' : qualifier;
stageCard.querySelector('.sig-qualifier-below').textContent = isMajor ? qualifier : '';
// Populate stat block keyword faces and reset to upright // Populate stat block keyword faces and reset to upright
statBlock.classList.remove('is-reversed'); statBlock.classList.remove('is-reversed');
@@ -178,6 +195,38 @@ var SigSelect = (function () {
// ── Apply reservation state (local + from WS) ───────────────────────── // ── Apply reservation state (local + from WS) ─────────────────────────
function _placeReservedFloat(cardId, cardEl, role) {
// Remove any pre-existing reserved float for this role (e.g. page-load replay)
if (_reservedFloats[role]) { _reservedFloats[role].remove(); }
// Retire ALL hover floats for this role — may be on a different card than reserved
var roles = POLARITY_ROLES[userPolarity] || [];
var idx = roles.indexOf(role);
var posClass = ['--left', '--mid', '--right'][idx] || '--left';
Object.keys(_floatingCursors).forEach(function (key) {
if (key.slice(-posClass.length) === posClass) {
_floatingCursors[key].remove();
var hCid = key.slice(0, key.length - posClass.length);
var hEl = deckGrid.querySelector('.sig-card[data-card-id="' + hCid + '"]');
if (hEl) {
var a = hEl.querySelector('.sig-cursor' + posClass);
if (a) a.classList.remove('active');
}
delete _floatingCursors[key];
}
});
var rect = cardEl.getBoundingClientRect();
var xFractions = [0.15, 0.5, 0.85];
var fc = document.createElement('i');
fc.className = 'fa-solid fa-thumbs-up sig-cursor-float sig-cursor-float--reserved';
fc.dataset.role = role;
fc.style.left = (rect.left + rect.width * xFractions[idx < 0 ? 1 : idx]) + 'px';
fc.style.top = rect.bottom + 'px';
_ensureCursorPortal().appendChild(fc);
_reservedFloats[role] = fc;
}
function applyReservation(cardId, role, reserved) { function applyReservation(cardId, role, reserved) {
var cardEl = deckGrid.querySelector('.sig-card[data-card-id="' + cardId + '"]'); var cardEl = deckGrid.querySelector('.sig-card[data-card-id="' + cardId + '"]');
if (!cardEl) return; if (!cardEl) return;
@@ -194,7 +243,10 @@ var SigSelect = (function () {
updateStage(cardEl); updateStage(cardEl);
_stageFrozen = true; _stageFrozen = true;
stage.classList.add('sig-stage--frozen'); stage.classList.add('sig-stage--frozen');
_showTakeSigBtn();
} }
// Thumbs-up float for all reservations — own role sees their own indicator too
_placeReservedFloat(cardId, cardEl, role);
} else { } else {
delete cardEl.dataset.reservedBy; delete cardEl.dataset.reservedBy;
cardEl.classList.remove('sig-reserved', 'sig-reserved--own'); cardEl.classList.remove('sig-reserved', 'sig-reserved--own');
@@ -202,30 +254,296 @@ var SigSelect = (function () {
_reservedCardId = null; _reservedCardId = null;
_stageFrozen = false; _stageFrozen = false;
stage.classList.remove('sig-stage--frozen'); stage.classList.remove('sig-stage--frozen');
_hideTakeSigBtn();
}
// Remove thumbs-up float for all releases — own role included
if (_reservedFloats[role]) {
_reservedFloats[role].remove();
delete _reservedFloats[role];
} }
} }
} }
// ── Apply hover cursor (WS only — own hover is CSS :hover) ──────────── // ── Apply hover cursor (WS only — own hover is CSS :hover) ────────────
//
// Cursor icons are portaled to document root so they escape overflow/clip
// contexts in the deck grid. The in-card anchor elements only carry the
// .active class (for test assertions and the :has() z-index rule).
function _ensureCursorPortal() {
if (!_cursorPortal || !document.body.contains(_cursorPortal)) {
_cursorPortal = document.getElementById('id_sig_cursor_portal');
if (!_cursorPortal) {
_cursorPortal = document.createElement('div');
_cursorPortal.id = 'id_sig_cursor_portal';
document.body.appendChild(_cursorPortal);
}
}
return _cursorPortal;
}
function applyHover(cardId, role, active) { function applyHover(cardId, role, active) {
if (role === userRole) return; if (role === userRole) return;
if (_reservedFloats[role]) return; // role already has thumbs-up — ignore hover
var cardEl = deckGrid.querySelector('.sig-card[data-card-id="' + cardId + '"]'); var cardEl = deckGrid.querySelector('.sig-card[data-card-id="' + cardId + '"]');
if (!cardEl) return; if (!cardEl) return;
var roles = POLARITY_ROLES[userPolarity] || []; var roles = POLARITY_ROLES[userPolarity] || [];
var idx = roles.indexOf(role); var idx = roles.indexOf(role);
var posClass = ['--left', '--mid', '--right'][idx] || '--left'; var posClass = ['--left', '--mid', '--right'][idx] || '--left';
var cursor = cardEl.querySelector('.sig-cursor' + posClass); var anchor = cardEl.querySelector('.sig-cursor' + posClass);
if (!cursor) return; if (!anchor) return;
var key = cardId + posClass;
if (active) { if (active) {
cursor.classList.add('active'); anchor.classList.add('active'); // kept for test assertions + :has() z-index
// Place a fixed-position clone in the portal, positioned from card bounds
var rect = cardEl.getBoundingClientRect();
var xFractions = [0.15, 0.5, 0.85];
var fc = document.createElement('i');
fc.className = 'fa-solid fa-hand-pointer sig-cursor-float';
fc.dataset.role = role;
fc.style.left = (rect.left + rect.width * xFractions[idx]) + 'px';
fc.style.top = rect.bottom + 'px';
_ensureCursorPortal().appendChild(fc);
_floatingCursors[key] = fc;
} else { } else {
cursor.classList.remove('active'); anchor.classList.remove('active');
if (_floatingCursors[key]) {
_floatingCursors[key].remove();
delete _floatingCursors[key];
}
} }
} }
// ── Reposition floats after resize ────────────────────────────────────
//
// Both reserved (thumbs-up) and hover (hand-pointer) floats are stamped with
// fixed pixel coords at placement time. When the viewport changes size the
// cards reflow but the icons stay put. Re-measure from the card's current
// bounding rect and update left/top in-place.
var _posClasses = ['--left', '--mid', '--right'];
var _xFractions = [0.15, 0.5, 0.85];
function _repositionFloats() {
if (!deckGrid) return;
var roles = POLARITY_ROLES[userPolarity] || [];
Object.keys(_reservedFloats).forEach(function (role) {
var cardEl = deckGrid.querySelector('.sig-card[data-reserved-by="' + role + '"]');
if (!cardEl) return;
var rect = cardEl.getBoundingClientRect();
var idx = roles.indexOf(role);
var fc = _reservedFloats[role];
fc.style.left = (rect.left + rect.width * _xFractions[idx < 0 ? 1 : idx]) + 'px';
fc.style.top = rect.bottom + 'px';
});
Object.keys(_floatingCursors).forEach(function (key) {
var posClass = _posClasses.find(function (p) {
return key.slice(-p.length) === p;
});
if (!posClass) return;
var cardId = key.slice(0, key.length - posClass.length);
var cardEl = deckGrid.querySelector('.sig-card[data-card-id="' + cardId + '"]');
if (!cardEl) return;
var rect = cardEl.getBoundingClientRect();
var idx = _posClasses.indexOf(posClass);
var fc = _floatingCursors[key];
fc.style.left = (rect.left + rect.width * _xFractions[idx]) + 'px';
fc.style.top = rect.bottom + 'px';
});
}
// ── TAKE SIG / WAIT NVM button ─────────────────────────────────────────
function _onTakeSigClick() {
if (_isReady) {
var body = 'action=unready';
if (_countdownTimer !== null) {
body += '&seconds_remaining=' + _countdownSecondsLeft;
}
fetch(readyUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRFToken': getCsrf() },
body: body,
}).then(function (res) {
if (res.ok) {
_isReady = false;
if (_countdownTimer !== null) {
clearInterval(_countdownTimer);
_countdownTimer = null;
if (_takeSigBtn) _takeSigBtn.style.fontSize = '';
}
if (_takeSigBtn) _takeSigBtn.textContent = 'TAKE SIG';
_stopWaitNoGlow();
_stopCountdownGlow();
}
});
} else {
fetch(readyUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRFToken': getCsrf() },
body: 'action=ready',
}).then(function (res) {
if (res.ok) {
_isReady = true;
// countdown_start WS may arrive before this response for the
// gamer who triggered the countdown — don't clobber the numeral.
if (_countdownTimer === null) {
if (_takeSigBtn) _takeSigBtn.textContent = 'WAIT NVM';
_startWaitNoGlow();
}
}
});
}
}
function _showTakeSigBtn() {
if (_takeSigBtn || !stage) return;
_takeSigBtn = document.createElement('button');
_takeSigBtn.id = 'id_take_sig_btn';
_takeSigBtn.className = 'btn btn-primary sig-take-sig-btn';
_takeSigBtn.type = 'button';
_takeSigBtn.textContent = 'TAKE SIG';
_takeSigBtn.addEventListener('click', _onTakeSigClick);
stage.appendChild(_takeSigBtn);
}
function _startWaitNoGlow() {
if (_glowTimer !== null) return;
_glowPeak = false;
_glowTimer = setInterval(function () {
if (!_takeSigBtn) { _stopWaitNoGlow(); return; }
_glowPeak = !_glowPeak;
if (_glowPeak) {
_takeSigBtn.classList.add('btn-cancel');
_takeSigBtn.style.boxShadow =
'0 0 0.8rem 0.2rem rgba(var(--terOr), 0.75), ' +
'0 0 2rem 0.4rem rgba(var(--terOr), 0.35)';
} else {
_takeSigBtn.classList.remove('btn-cancel');
_takeSigBtn.style.boxShadow = '';
}
}, 600);
}
function _stopWaitNoGlow() {
if (_glowTimer !== null) { clearInterval(_glowTimer); _glowTimer = null; }
if (_takeSigBtn) {
_takeSigBtn.classList.remove('btn-cancel');
_takeSigBtn.style.boxShadow = '';
}
_glowPeak = false;
}
function _startCountdownGlow() {
if (_glowTimer !== null) return;
_glowPeak = false;
_glowTimer = setInterval(function () {
if (!_takeSigBtn) { _stopCountdownGlow(); return; }
_glowPeak = !_glowPeak;
if (_glowPeak) {
_takeSigBtn.classList.add('btn-danger');
_takeSigBtn.style.boxShadow =
'0 0 0.8rem 0.2rem rgba(var(--terRd), 0.75), ' +
'0 0 2rem 0.4rem rgba(var(--terRd), 0.35)';
} else {
_takeSigBtn.classList.remove('btn-danger');
_takeSigBtn.style.boxShadow = '';
}
}, 600);
}
function _stopCountdownGlow() {
if (_glowTimer !== null) { clearInterval(_glowTimer); _glowTimer = null; }
if (_takeSigBtn) {
_takeSigBtn.classList.remove('btn-danger');
_takeSigBtn.style.boxShadow = '';
}
_glowPeak = false;
}
function _hideTakeSigBtn() {
if (!_takeSigBtn) return;
_stopWaitNoGlow();
_takeSigBtn.removeEventListener('click', _onTakeSigClick);
_takeSigBtn.remove();
_takeSigBtn = null;
_isReady = false;
}
// ── Polarity countdown ────────────────────────────────────────────────
function _showCountdown(seconds) {
_countdownSecondsLeft = seconds;
_stopWaitNoGlow();
if (_takeSigBtn) {
_takeSigBtn.textContent = _countdownSecondsLeft;
_takeSigBtn.style.fontSize = '2em';
}
_startCountdownGlow();
if (_countdownTimer !== null) clearInterval(_countdownTimer);
_countdownTimer = setInterval(function () {
_countdownSecondsLeft -= 1;
if (_takeSigBtn) _takeSigBtn.textContent = _countdownSecondsLeft;
if (_countdownSecondsLeft <= 0) {
clearInterval(_countdownTimer);
_countdownTimer = null;
_stopCountdownGlow(); // server drives the transition via Celery task
}
}, 1000);
}
function _hideCountdown() {
if (_countdownTimer !== null) {
clearInterval(_countdownTimer);
_countdownTimer = null;
}
_stopCountdownGlow();
if (_takeSigBtn) {
_takeSigBtn.style.fontSize = '';
if (_isReady) {
// Countdown cancelled by another gamer — restore WAIT NVM state
_takeSigBtn.textContent = 'WAIT NVM';
_startWaitNoGlow();
}
}
}
// ── Overlay dismiss + waiting message ─────────────────────────────────
function _dismissSigOverlay() {
_hideCountdown();
_hideTakeSigBtn();
var backdrop = document.querySelector('.sig-backdrop');
if (backdrop) backdrop.remove();
if (overlay) { overlay.remove(); overlay = null; }
// Remove all floating cursors (hover + thumbs-up) from the portal
Object.keys(_reservedFloats).forEach(function (role) {
_reservedFloats[role].remove();
});
_reservedFloats = {};
Object.keys(_floatingCursors).forEach(function (key) {
_floatingCursors[key].remove();
});
_floatingCursors = {};
}
function _showWaitingMsg(pendingPolarity) {
if (document.getElementById('id_hex_waiting_msg')) return;
var msg = document.createElement('p');
msg.id = 'id_hex_waiting_msg';
msg.textContent = pendingPolarity === 'gravity'
? 'Gravity settling . . .'
: 'Levity appraising . . .';
var center = document.querySelector('.table-center');
if (center) center.appendChild(msg);
}
// ── WS events ───────────────────────────────────────────────────────── // ── WS events ─────────────────────────────────────────────────────────
window.addEventListener('room:sig_reserved', function (e) { window.addEventListener('room:sig_reserved', function (e) {
@@ -238,6 +556,33 @@ var SigSelect = (function () {
applyHover(String(e.detail.card_id), e.detail.role, e.detail.active); applyHover(String(e.detail.card_id), e.detail.role, e.detail.active);
}); });
window.addEventListener('room:countdown_start', function (e) {
if (!overlay) return;
_showCountdown(e.detail.seconds);
});
window.addEventListener('room:countdown_cancel', function (e) {
_hideCountdown();
_countdownSecondsLeft = e.detail.seconds_remaining;
});
window.addEventListener('room:polarity_room_done', function (e) {
if (!overlay) return;
if (e.detail.polarity !== userPolarity) return;
var pendingPolarity = userPolarity === 'levity' ? 'gravity' : 'levity';
_dismissSigOverlay();
_showWaitingMsg(pendingPolarity);
});
window.addEventListener('room:pick_sky_available', function () {
var msg = document.getElementById('id_hex_waiting_msg');
if (msg) msg.remove();
var btn = document.getElementById('id_pick_sky_btn');
if (btn) btn.style.display = '';
});
window.addEventListener('resize:end', _repositionFloats);
// ── WS send ─────────────────────────────────────────────────────────── // ── WS send ───────────────────────────────────────────────────────────
function sendHover(cardId, active) { function sendHover(cardId, active) {
@@ -293,15 +638,43 @@ var SigSelect = (function () {
}); });
reserveUrl = overlay.dataset.reserveUrl; reserveUrl = overlay.dataset.reserveUrl;
readyUrl = overlay.dataset.readyUrl;
userRole = overlay.dataset.userRole; userRole = overlay.dataset.userRole;
userPolarity= overlay.dataset.polarity; userPolarity= overlay.dataset.polarity;
// Restore reservations from server-rendered JSON (page-load state) // PICK SKY btn is rendered hidden during SIG_SELECT; reveal on pick_sky_available
var pickSkyBtn = document.getElementById('id_pick_sky_btn');
if (pickSkyBtn) {
pickSkyBtn.addEventListener('click', function () {
if (typeof Tray !== 'undefined') Tray.open();
});
}
// Restore reservations from server-rendered JSON (page-load state).
// Deferred to 'load' so sizeSigModal() (also a 'load' listener, registered
// in room.js before this script) has already applied paddingBottom and
// --sig-card-w before _placeReservedFloat calls getBoundingClientRect().
try { try {
var existing = JSON.parse(overlay.dataset.reservations || '{}'); var existing = JSON.parse(overlay.dataset.reservations || '{}');
Object.keys(existing).forEach(function (cardId) { if (Object.keys(existing).length) {
applyReservation(cardId, existing[cardId], true); var _replayReservations = function () {
}); Object.keys(existing).forEach(function (cardId) {
applyReservation(cardId, existing[cardId], true);
});
// Restore WAIT NVM state if gamer was already ready before page load
if (overlay.dataset.ready === 'true' && _takeSigBtn) {
_isReady = true;
_takeSigBtn.textContent = 'WAIT NVM';
_startWaitNoGlow();
}
};
if (document.readyState === 'complete') {
_replayReservations();
} else {
window.addEventListener('load', _replayReservations, { once: true });
}
}
} catch (e) { /* malformed JSON — ignore */ } } catch (e) { /* malformed JSON — ignore */ }
// Hover: update stage preview + broadcast cursor // Hover: update stage preview + broadcast cursor
@@ -369,6 +742,16 @@ var SigSelect = (function () {
_requestInFlight = false; _requestInFlight = false;
_cautionData = []; _cautionData = [];
_cautionIdx = 0; _cautionIdx = 0;
Object.keys(_floatingCursors).forEach(function (k) { _floatingCursors[k].remove(); });
_floatingCursors = {};
Object.keys(_reservedFloats).forEach(function (k) { _reservedFloats[k].remove(); });
_reservedFloats = {};
_cursorPortal = null;
_isReady = false;
_stopWaitNoGlow();
_hideTakeSigBtn();
_hideCountdown();
_countdownSecondsLeft = 0;
init(); init();
}, },
_setFrozen: function (v) { _stageFrozen = v; }, _setFrozen: function (v) { _stageFrozen = v; },

95
src/apps/epic/tasks.py Normal file
View File

@@ -0,0 +1,95 @@
"""
Countdown scheduler for the polarity-room TAKE SIG gate.
Uses threading.Timer so no separate Celery worker is needed in development.
Single-process only — swap for a Celery task if production uses multiple
web workers (gunicorn -w N with N > 1).
"""
import threading
import uuid
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.cache import cache
_LEVITY_ROLES = {'PC', 'NC', 'SC'}
_GRAVITY_ROLES = {'BC', 'EC', 'AC'}
# In-process registry of pending timers: "{room_id}_{polarity}" → Timer
_timers = {}
def _cache_key(room_id, polarity):
return f'sig_countdown_{room_id}_{polarity}'
def _group_send(room_id, msg):
async_to_sync(get_channel_layer().group_send)(f'room_{room_id}', msg)
def _fire(room_id, polarity, token):
"""Callback run by threading.Timer after the countdown expires."""
# Token guard: if cancelled or superseded, cache entry will differ
if cache.get(_cache_key(room_id, polarity)) != token:
return
from apps.epic.models import Room, SigReservation
try:
room = Room.objects.get(id=room_id)
except Room.DoesNotExist:
return
if room.table_status != Room.SIG_SELECT:
return
polarity_roles = _LEVITY_ROLES if polarity == SigReservation.LEVITY else _GRAVITY_ROLES
# Idempotency: seats already assigned
if not room.table_seats.filter(role__in=polarity_roles, significator__isnull=True).exists():
return
# Safety: all three must still be ready
ready_reservations = list(
SigReservation.objects.filter(room=room, polarity=polarity, ready=True)
.select_related('seat', 'card')
)
if len(ready_reservations) < 3:
return
for res in ready_reservations:
if res.seat:
res.seat.significator = res.card
res.seat.save(update_fields=['significator'])
SigReservation.objects.filter(room=room, polarity=polarity).update(countdown_remaining=None)
_group_send(room_id, {'type': 'polarity_room_done', 'polarity': polarity})
if not room.table_seats.filter(significator__isnull=True).exists():
Room.objects.filter(id=room_id).update(table_status=Room.SKY_SELECT)
_group_send(room_id, {'type': 'pick_sky_available'})
cache.delete(_cache_key(room_id, polarity))
_timers.pop(f'{room_id}_{polarity}', None)
def schedule_polarity_confirm(room_id, polarity, seconds):
"""Schedule a polarity confirm `seconds` seconds from now. Cancels any prior timer."""
cancel_polarity_confirm(room_id, polarity)
token = str(uuid.uuid4())
cache.set(_cache_key(room_id, polarity), token, timeout=int(seconds) + 60)
timer = threading.Timer(seconds, _fire, args=[str(room_id), polarity, token])
timer.daemon = True
timer.start()
_timers[f'{room_id}_{polarity}'] = timer
def cancel_polarity_confirm(room_id, polarity):
"""Cancel any pending confirm for this room + polarity."""
timer = _timers.pop(f'{room_id}_{polarity}', None)
if timer:
timer.cancel()
cache.delete(_cache_key(room_id, polarity))

View File

@@ -530,3 +530,66 @@ class TarotCardCautionsTest(TestCase):
for caution in schizo.cautions: for caution in schizo.cautions:
self.assertIn("reverse", caution) self.assertIn("reverse", caution)
self.assertNotIn("transform", caution) self.assertNotIn("transform", caution)
# ── SigReservation ready gate ─────────────────────────────────────────────────
class SigReservationReadyGateTest(TestCase):
"""SigReservation.ready and countdown_remaining fields."""
def setUp(self):
self.earthman = DeckVariant.objects.get(slug="earthman")
owner = User.objects.create(email="owner@test.io")
room = Room.objects.create(name="R", owner=owner)
card = TarotCard.objects.get(
deck_variant=self.earthman, arcana="MIDDLE", suit="BRANDS", number=11
)
self.res = SigReservation.objects.create(
room=room, gamer=owner, card=card, role="PC", polarity="levity"
)
def test_ready_defaults_to_false(self):
self.assertFalse(self.res.ready)
def test_countdown_remaining_defaults_to_none(self):
self.assertIsNone(self.res.countdown_remaining)
def test_ready_can_be_set_true(self):
self.res.ready = True
self.res.save()
self.res.refresh_from_db()
self.assertTrue(self.res.ready)
def test_countdown_remaining_can_be_saved(self):
self.res.countdown_remaining = 7
self.res.save()
self.res.refresh_from_db()
self.assertEqual(self.res.countdown_remaining, 7)
# ── Room SKY_SELECT status ────────────────────────────────────────────────────
class RoomSkySelectStatusTest(TestCase):
"""Room.SKY_SELECT constant and sig_select_started_at field."""
def setUp(self):
owner = User.objects.create(email="owner@test.io")
self.room = Room.objects.create(name="R", owner=owner)
def test_sky_select_constant_value(self):
self.assertEqual(Room.SKY_SELECT, "SKY_SELECT")
def test_sky_select_is_valid_table_status_choice(self):
choices = [c[0] for c in Room.TABLE_STATUS_CHOICES]
self.assertIn(Room.SKY_SELECT, choices)
def test_sig_select_started_at_defaults_to_none(self):
self.assertIsNone(self.room.sig_select_started_at)
def test_sig_select_started_at_can_be_set(self):
from django.utils import timezone
now = timezone.now()
self.room.sig_select_started_at = now
self.room.save()
self.room.refresh_from_db()
self.assertIsNotNone(self.room.sig_select_started_at)

View File

@@ -1306,3 +1306,309 @@ class SigReserveViewTest(TestCase):
args, kwargs = mock_notify.call_args args, kwargs = mock_notify.call_args
self.assertEqual(args[1], self.card.pk) # card_id must not be None self.assertEqual(args[1], self.card.pk) # card_id must not be None
self.assertFalse(kwargs['reserved']) # reserved=False self.assertFalse(kwargs['reserved']) # reserved=False
# ── sig_ready view ────────────────────────────────────────────────────────────
def _make_levity_reservations(room, gamers, earthman, ready=False):
"""Create SigReservations for the three levity gamers (PC, NC, SC).
Returns the three reservations in PC→NC→SC order."""
cards = [
TarotCard.objects.get(deck_variant=earthman, arcana="MIDDLE", suit="BRANDS", number=n)
for n in (11, 12, 13)
]
roles = ["PC", "NC", "SC"]
# gamers[0]=PC, gamers[1]=NC, gamers[3]=SC
gamer_indices = [0, 1, 3]
reservations = []
for gamer_idx, role, card in zip(gamer_indices, roles, cards):
seat = TableSeat.objects.get(room=room, role=role)
res = SigReservation.objects.create(
room=room, gamer=gamers[gamer_idx], card=card,
role=role, polarity="levity", seat=seat, ready=ready,
)
reservations.append(res)
return reservations
class SigReadyViewTest(TestCase):
"""sig_ready — toggle ready/unready for the polarity-room countdown."""
def setUp(self):
self.room, self.gamers, self.earthman, _ = _full_sig_setUp(self)
self.reservations = _make_levity_reservations(self.room, self.gamers, self.earthman)
self.url = reverse("epic:sig_ready", kwargs={"room_id": self.room.id})
def _post(self, action="ready", seconds_remaining=None, client=None):
c = client or self.client
data = {"action": action}
if seconds_remaining is not None:
data["seconds_remaining"] = seconds_remaining
return c.post(self.url, data=data)
# ── guards ────────────────────────────────────────────────────────────
def test_sig_ready_requires_login(self):
self.client.logout()
response = self._post()
self.assertEqual(response.status_code, 302)
self.assertIn("/accounts/login/", response.url)
def test_sig_ready_requires_seated_gamer(self):
outsider = User.objects.create(email="outsider@test.io")
outsider_client = self.client.__class__()
outsider_client.force_login(outsider)
response = self._post(client=outsider_client)
self.assertEqual(response.status_code, 403)
def test_sig_ready_wrong_phase_returns_400(self):
self.room.table_status = Room.ROLE_SELECT
self.room.save()
response = self._post()
self.assertEqual(response.status_code, 400)
def test_sig_ready_without_reservation_returns_400(self):
"""Can't go ready without an OK'd card."""
SigReservation.objects.filter(room=self.room, gamer=self.gamers[0]).delete()
response = self._post()
self.assertEqual(response.status_code, 400)
# ── happy-path ready ──────────────────────────────────────────────────
def test_sig_ready_sets_ready_true_on_reservation(self):
self._post(action="ready")
res = SigReservation.objects.get(room=self.room, gamer=self.gamers[0])
self.assertTrue(res.ready)
def test_sig_ready_returns_200(self):
response = self._post(action="ready")
self.assertEqual(response.status_code, 200)
# ── unready ──────────────────────────────────────────────────────────
def test_sig_unready_sets_ready_false(self):
self.reservations[0].ready = True
self.reservations[0].save()
self._post(action="unready")
res = SigReservation.objects.get(room=self.room, gamer=self.gamers[0])
self.assertFalse(res.ready)
def test_sig_unready_when_not_ready_is_harmless(self):
response = self._post(action="unready")
self.assertEqual(response.status_code, 200)
# ── countdown mechanics ───────────────────────────────────────────────
def test_sig_ready_broadcasts_countdown_start_when_all_three_polarity_ready(self):
"""When all three levity gamers are ready, countdown_start broadcasts."""
# Make NC and SC ready first
for res in self.reservations[1:]:
res.ready = True
res.save()
# PC (founder) goes ready — triggers all-three condition
with patch("apps.epic.views._notify_countdown_start") as mock_notify:
self._post(action="ready")
mock_notify.assert_called_once()
args = mock_notify.call_args[0]
self.assertIn("levity", args) # polarity in call
def test_sig_ready_does_not_broadcast_countdown_when_only_two_ready(self):
self.reservations[1].ready = True
self.reservations[1].save()
with patch("apps.epic.views._notify_countdown_start") as mock_notify:
self._post(action="ready")
mock_notify.assert_not_called()
def test_sig_unready_saves_seconds_remaining_on_all_polarity_reservations(self):
for res in self.reservations:
res.ready = True
res.save()
self._post(action="unready", seconds_remaining=7)
for res in self.reservations:
res.refresh_from_db()
self.assertEqual(res.countdown_remaining, 7)
def test_sig_unready_broadcasts_countdown_cancel(self):
for res in self.reservations:
res.ready = True
res.save()
with patch("apps.epic.views._notify_countdown_cancel") as mock_notify:
self._post(action="unready", seconds_remaining=7)
mock_notify.assert_called_once()
def test_sig_ready_uses_saved_seconds_for_countdown_restart(self):
"""If countdown_remaining is saved (e.g. 7), countdown_start sends 7 not 12."""
for res in self.reservations:
res.ready = True
res.countdown_remaining = 7
res.save()
# One unreadied; now goes ready again — all 3 ready → start from 7
self.reservations[0].ready = False
self.reservations[0].save()
with patch("apps.epic.views._notify_countdown_start") as mock_notify:
self._post(action="ready")
mock_notify.assert_called_once()
args, kwargs = mock_notify.call_args
seconds_sent = kwargs.get("seconds") or args[1]
self.assertEqual(seconds_sent, 7)
# ── sig_confirm view ──────────────────────────────────────────────────────────
def _make_gravity_reservations(room, gamers, earthman, ready=False):
"""Create SigReservations for the three gravity gamers (EC, AC, BC)."""
cards = [
TarotCard.objects.get(deck_variant=earthman, arcana="MIDDLE", suit="GRAILS", number=n)
for n in (11, 12, 13)
]
roles = ["EC", "AC", "BC"]
# gamers[2]=EC, gamers[4]=AC, gamers[5]=BC
gamer_indices = [2, 4, 5]
reservations = []
for gamer_idx, role, card in zip(gamer_indices, roles, cards):
seat = TableSeat.objects.get(room=room, role=role)
res = SigReservation.objects.create(
room=room, gamer=gamers[gamer_idx], card=card,
role=role, polarity="gravity", seat=seat, ready=ready,
)
reservations.append(res)
return reservations
class SigConfirmViewTest(TestCase):
"""sig_confirm — finalize polarity group once countdown reaches zero."""
def setUp(self):
self.room, self.gamers, self.earthman, _ = _full_sig_setUp(self)
# All three levity gamers are ready
self.lev_res = _make_levity_reservations(
self.room, self.gamers, self.earthman, ready=True
)
# founder (PC) is already logged in from _full_sig_setUp
self.url = reverse("epic:sig_confirm", kwargs={"room_id": self.room.id})
def _post(self, polarity="levity", client=None):
c = client or self.client
return c.post(self.url, data={"polarity": polarity})
# ── guards ────────────────────────────────────────────────────────────
def test_sig_confirm_requires_login(self):
self.client.logout()
response = self._post()
self.assertEqual(response.status_code, 302)
self.assertIn("/accounts/login/", response.url)
def test_sig_confirm_requires_seated_gamer(self):
outsider = User.objects.create(email="outsider@test.io")
outsider_client = self.client.__class__()
outsider_client.force_login(outsider)
response = self._post(client=outsider_client)
self.assertEqual(response.status_code, 403)
def test_sig_confirm_wrong_phase_returns_400(self):
self.room.table_status = Room.ROLE_SELECT
self.room.save()
response = self._post()
self.assertEqual(response.status_code, 400)
def test_sig_confirm_not_all_polarity_ready_returns_400(self):
"""If any of the three in the polarity group isn't ready, reject."""
self.lev_res[1].ready = False
self.lev_res[1].save()
response = self._post()
self.assertEqual(response.status_code, 400)
# ── happy-path ────────────────────────────────────────────────────────
def test_sig_confirm_sets_significator_on_seats_from_reservations(self):
self._post()
for res in self.lev_res:
seat = TableSeat.objects.get(room=self.room, role=res.role)
self.assertEqual(seat.significator, res.card)
def test_sig_confirm_returns_200(self):
response = self._post()
self.assertEqual(response.status_code, 200)
def test_sig_confirm_broadcasts_polarity_room_done(self):
with patch("apps.epic.views._notify_polarity_room_done") as mock_notify:
self._post()
mock_notify.assert_called_once()
args = mock_notify.call_args[0]
self.assertIn("levity", args)
def test_sig_confirm_is_idempotent_if_significators_already_set(self):
"""Second call from another browser returns 200 without re-running logic."""
self._post()
response = self._post()
self.assertEqual(response.status_code, 200)
# ── both polarities done ──────────────────────────────────────────────
def test_sig_confirm_broadcasts_pick_sky_available_when_both_polarities_done(self):
"""After both levity and gravity confirm, pick_sky_available fires."""
# Pre-set gravity seats to already have significators (simulating earlier confirm)
grav_cards = [
TarotCard.objects.get(deck_variant=self.earthman, arcana="MIDDLE", suit="GRAILS", number=n)
for n in (11, 12, 13)
]
for role, card in zip(["EC", "AC", "BC"], grav_cards):
seat = TableSeat.objects.get(room=self.room, role=role)
seat.significator = card
seat.save()
with patch("apps.epic.views._notify_pick_sky_available") as mock_notify:
self._post(polarity="levity")
mock_notify.assert_called_once()
def test_sig_confirm_sets_room_to_sky_select_when_both_polarities_done(self):
grav_cards = [
TarotCard.objects.get(deck_variant=self.earthman, arcana="MIDDLE", suit="GRAILS", number=n)
for n in (11, 12, 13)
]
for role, card in zip(["EC", "AC", "BC"], grav_cards):
seat = TableSeat.objects.get(room=self.room, role=role)
seat.significator = card
seat.save()
self._post(polarity="levity")
self.room.refresh_from_db()
self.assertEqual(self.room.table_status, Room.SKY_SELECT)
def test_sig_confirm_does_not_broadcast_pick_sky_available_when_only_one_polarity_done(self):
with patch("apps.epic.views._notify_pick_sky_available") as mock_notify:
self._post(polarity="levity")
mock_notify.assert_not_called()
# ── SKY_SELECT rendering ──────────────────────────────────────────────────────
class PickSkyRenderingTest(TestCase):
"""Room page at SKY_SELECT renders PICK SKY btn and sig card in tray cell 2."""
def setUp(self):
self.room, self.gamers, self.earthman, _ = _full_sig_setUp(self)
self.room.table_status = Room.SKY_SELECT
self.room.save()
self.sig_card = TarotCard.objects.get(
deck_variant=self.earthman, arcana="MIDDLE", suit="BRANDS", number=11
)
pc_seat = TableSeat.objects.get(room=self.room, role="PC")
pc_seat.significator = self.sig_card
pc_seat.save()
self.url = reverse("epic:room", kwargs={"room_id": self.room.id})
def test_pick_sky_btn_present_in_sky_select_phase(self):
response = self.client.get(self.url)
self.assertContains(response, "id_pick_sky_btn")
def test_tray_cell_2_contains_sig_card_icon_in_sky_select(self):
response = self.client.get(self.url)
self.assertContains(response, "tray-sig-card")
def test_pick_sky_btn_hidden_during_sig_select(self):
# Rendered hidden (display:none) so JS can reveal it on pick_sky_available WS event
self.room.table_status = Room.SIG_SELECT
self.room.save()
response = self.client.get(self.url)
self.assertContains(response, 'id="id_pick_sky_btn"')
self.assertContains(response, 'style="display:none"')

View File

@@ -17,6 +17,8 @@ urlpatterns = [
path('room/<uuid:room_id>/select-role', views.select_role, name='select_role'), path('room/<uuid:room_id>/select-role', views.select_role, name='select_role'),
path('room/<uuid:room_id>/select-sig', views.select_sig, name='select_sig'), path('room/<uuid:room_id>/select-sig', views.select_sig, name='select_sig'),
path('room/<uuid:room_id>/sig-reserve', views.sig_reserve, name='sig_reserve'), path('room/<uuid:room_id>/sig-reserve', views.sig_reserve, name='sig_reserve'),
path('room/<uuid:room_id>/sig-ready', views.sig_ready, name='sig_ready'),
path('room/<uuid:room_id>/sig-confirm', views.sig_confirm, name='sig_confirm'),
path('room/<uuid:room_id>/gate/invite', views.invite_gamer, name='invite_gamer'), path('room/<uuid:room_id>/gate/invite', views.invite_gamer, name='invite_gamer'),
path('room/<uuid:room_id>/gate/status', views.gate_status, name='gate_status'), path('room/<uuid:room_id>/gate/status', views.gate_status, name='gate_status'),
path('room/<uuid:room_id>/delete', views.delete_room, name='delete_room'), path('room/<uuid:room_id>/delete', views.delete_room, name='delete_room'),

View File

@@ -10,8 +10,11 @@ from django.shortcuts import redirect, render
from django.utils import timezone from django.utils import timezone
from apps.drama.models import GameEvent, record from apps.drama.models import GameEvent, record
from django.db.models import Case, IntegerField, Value, When
from apps.epic.models import ( from apps.epic.models import (
GateSlot, Room, RoomInvite, SigReservation, TableSeat, TarotCard, TarotDeck, GateSlot, Room, RoomInvite, SIG_SEAT_ORDER, SigReservation, TableSeat,
TarotCard, TarotDeck,
active_sig_seat, debit_token, levity_sig_cards, gravity_sig_cards, active_sig_seat, debit_token, levity_sig_cards, gravity_sig_cards,
select_token, sig_deck_cards, select_token, sig_deck_cards,
) )
@@ -90,8 +93,52 @@ def _notify_sig_reserved(room_id, card_id, role, reserved):
) )
def _notify_countdown_start(room_id, polarity, *, seconds):
async_to_sync(get_channel_layer().group_send)(
f'cursors_{room_id}_{polarity}',
{'type': 'countdown_start', 'polarity': polarity, 'seconds': seconds},
)
def _notify_countdown_cancel(room_id, polarity, *, seconds_remaining):
async_to_sync(get_channel_layer().group_send)(
f'cursors_{room_id}_{polarity}',
{'type': 'countdown_cancel', 'polarity': polarity, 'seconds_remaining': seconds_remaining},
)
def _notify_polarity_room_done(room_id, polarity):
async_to_sync(get_channel_layer().group_send)(
f'room_{room_id}',
{'type': 'polarity_room_done', 'polarity': polarity},
)
def _notify_pick_sky_available(room_id):
async_to_sync(get_channel_layer().group_send)(
f'room_{room_id}',
{'type': 'pick_sky_available'},
)
SLOT_ROLE_LABELS = {1: "PC", 2: "NC", 3: "EC", 4: "SC", 5: "AC", 6: "BC"} SLOT_ROLE_LABELS = {1: "PC", 2: "NC", 3: "EC", 4: "SC", 5: "AC", 6: "BC"}
_SIG_SEAT_ORDERING = Case(
*[When(role=r, then=Value(i)) for i, r in enumerate(SIG_SEAT_ORDER)],
default=Value(99),
output_field=IntegerField(),
)
def _canonical_user_seat(room, user):
"""Return the user's seat whose role comes first in PC→NC→EC→SC→AC→BC order.
In normal play (one user = one seat) this is equivalent to .first().
For Carte Blanche (one user = all seats) it returns the PC seat, ensuring
sig-select cursor placement is seat-based, not position/slot-based.
"""
return room.table_seats.filter(gamer=user).order_by(_SIG_SEAT_ORDERING).first()
_ROLE_SCRAWL_NAMES = { _ROLE_SCRAWL_NAMES = {
"PC": "Player", "NC": "Narrator", "EC": "Economist", "PC": "Player", "NC": "Narrator", "EC": "Economist",
"SC": "Shepherd", "AC": "Alchemist", "BC": "Builder", "SC": "Shepherd", "AC": "Alchemist", "BC": "Builder",
@@ -241,8 +288,12 @@ def _role_select_context(room, user):
"gate_positions": _gate_positions(room), "gate_positions": _gate_positions(room),
"slots": room.gate_slots.order_by("slot_number"), "slots": room.gate_slots.order_by("slot_number"),
} }
# Tray cell 2: sig card (set once polarity group confirms)
_canonical_seat = _canonical_user_seat(room, user) if user.is_authenticated else None
ctx["my_tray_sig"] = _canonical_seat.significator if _canonical_seat else None
if room.table_status == Room.SIG_SELECT: if room.table_status == Room.SIG_SELECT:
user_seat = room.table_seats.filter(gamer=user).first() if user.is_authenticated else None user_seat = _canonical_user_seat(room, user) if user.is_authenticated else None
user_role = user_seat.role if user_seat else None user_role = user_seat.role if user_seat else None
user_polarity = None user_polarity = None
if user_role in _LEVITY_ROLES: if user_role in _LEVITY_ROLES:
@@ -250,10 +301,24 @@ def _role_select_context(room, user):
elif user_role in _GRAVITY_ROLES: elif user_role in _GRAVITY_ROLES:
user_polarity = 'gravity' user_polarity = 'gravity'
user_reservation = SigReservation.objects.filter(
room=room, gamer=user
).first() if user.is_authenticated else None
ctx["user_seat"] = user_seat ctx["user_seat"] = user_seat
ctx["user_polarity"] = user_polarity ctx["user_polarity"] = user_polarity
ctx["user_ready"] = bool(user_reservation and user_reservation.ready)
ctx["sig_reserve_url"] = f"/gameboard/room/{room.id}/sig-reserve" ctx["sig_reserve_url"] = f"/gameboard/room/{room.id}/sig-reserve"
# Has this gamer's polarity already had significators assigned?
# (Other polarity still in progress — stay in SIG_SELECT but skip the overlay.)
if user_polarity:
_polarity_roles = _LEVITY_ROLES if user_polarity == 'levity' else _GRAVITY_ROLES
ctx["polarity_done"] = not room.table_seats.filter(
role__in=_polarity_roles, significator__isnull=True
).exists()
else:
ctx["polarity_done"] = False
# Pre-load existing reservations for this polarity so JS can restore # Pre-load existing reservations for this polarity so JS can restore
# grabbed state on page load/refresh. Keyed by str(card_id) → role. # grabbed state on page load/refresh. Keyed by str(card_id) → role.
if user_polarity: if user_polarity:
@@ -583,7 +648,7 @@ def sig_reserve(request, room_id):
if room.table_status != Room.SIG_SELECT: if room.table_status != Room.SIG_SELECT:
return HttpResponse(status=400) return HttpResponse(status=400)
user_seat = room.table_seats.filter(gamer=request.user).first() user_seat = _canonical_user_seat(room, request.user)
if not user_seat or not user_seat.role: if not user_seat or not user_seat.role:
return HttpResponse(status=403) return HttpResponse(status=403)
@@ -592,6 +657,21 @@ def sig_reserve(request, room_id):
if action == "release": if action == "release":
existing = SigReservation.objects.filter(room=room, gamer=request.user).first() existing = SigReservation.objects.filter(room=room, gamer=request.user).first()
released_card_id = existing.card_id if existing else None released_card_id = existing.card_id if existing else None
if existing and existing.ready:
# Gamer released while ready — treat as an implicit WAIT NVM
prior = room.events.filter(
actor=request.user, verb=GameEvent.SIG_READY
).last()
if prior and not prior.data.get("retracted"):
prior.data["retracted"] = True
prior.save(update_fields=["data"])
record(room, GameEvent.SIG_UNREADY, actor=request.user)
polarity = existing.polarity
all_ready = SigReservation.objects.filter(
room=room, polarity=polarity, ready=True
).count() == 3
if all_ready:
_notify_countdown_cancel(room_id, polarity, seconds_remaining=12)
SigReservation.objects.filter(room=room, gamer=request.user).delete() SigReservation.objects.filter(room=room, gamer=request.user).delete()
_notify_sig_reserved(room_id, released_card_id, user_seat.role, reserved=False) _notify_sig_reserved(room_id, released_card_id, user_seat.role, reserved=False)
return HttpResponse(status=200) return HttpResponse(status=200)
@@ -622,12 +702,111 @@ def sig_reserve(request, room_id):
SigReservation.objects.create( SigReservation.objects.create(
room=room, gamer=request.user, card=card, room=room, gamer=request.user, card=card,
role=user_seat.role, polarity=polarity, seat=user_seat, role=user_seat.role, polarity=polarity,
) )
_notify_sig_reserved(room_id, card.pk, user_seat.role, reserved=True) _notify_sig_reserved(room_id, card.pk, user_seat.role, reserved=True)
return HttpResponse(status=200) return HttpResponse(status=200)
@login_required
def sig_ready(request, room_id):
"""Toggle ready/unready for the polarity-room countdown.
POST body: action=ready|unready [, seconds_remaining=<int>]
"""
if request.method != "POST":
return HttpResponse(status=405)
room = Room.objects.get(id=room_id)
if room.table_status != Room.SIG_SELECT:
return HttpResponse(status=400)
user_seat = _canonical_user_seat(room, request.user)
if user_seat is None:
return HttpResponse(status=403)
action = request.POST.get("action", "ready")
reservation = SigReservation.objects.filter(room=room, gamer=request.user).first()
if action == "ready":
if reservation is None:
return HttpResponse(status=400)
if reservation.ready:
return HttpResponse(status=200) # idempotent — already ready, don't re-trigger countdown
reservation.ready = True
reservation.save(update_fields=["ready"])
card = reservation.card
if card and card.arcana == TarotCard.MIDDLE:
_pol_prefix = "Leavened" if reservation.polarity == SigReservation.LEVITY else "Graven"
_card_display = f"{_pol_prefix} {card.name_title}"
elif card and card.arcana == TarotCard.MAJOR:
_base = card.name_title.removeprefix("The ")
_pol_suffix = "of Light" if reservation.polarity == SigReservation.LEVITY else "from the Grave"
_card_display = f"{_base} {_pol_suffix}"
else:
_card_display = card.name_title if card else "a card"
record(room, GameEvent.SIG_READY, actor=request.user,
card_name=_card_display,
corner_rank=card.corner_rank if card else "",
suit_icon=card.suit_icon if card else "")
# Retract the most recent un-retracted SIG_UNREADY (cancellation is now moot)
prior_unready = room.events.filter(
actor=request.user, verb=GameEvent.SIG_UNREADY
).last()
if prior_unready and not prior_unready.data.get("retracted"):
prior_unready.data["retracted"] = True
prior_unready.save(update_fields=["data"])
# Check if all three in this polarity are now ready
polarity = reservation.polarity
polarity_roles = _LEVITY_ROLES if polarity == SigReservation.LEVITY else _GRAVITY_ROLES
ready_count = SigReservation.objects.filter(
room=room, polarity=polarity, ready=True
).count()
if ready_count == 3:
from apps.epic.tasks import schedule_polarity_confirm
# Use saved countdown_remaining if a pause was recorded, else 12
saved = SigReservation.objects.filter(
room=room, polarity=polarity
).exclude(countdown_remaining__isnull=True).values_list(
"countdown_remaining", flat=True
).first()
seconds = saved if saved is not None else 12
schedule_polarity_confirm(str(room_id), polarity, seconds)
_notify_countdown_start(room_id, polarity, seconds=seconds)
else: # unready
if reservation is not None:
reservation.ready = False
reservation.save(update_fields=["ready"])
# Mark the most recent un-retracted SIG_READY event for this actor
prior = room.events.filter(
actor=request.user, verb=GameEvent.SIG_READY
).last()
if prior and not prior.data.get("retracted"):
prior.data["retracted"] = True
prior.save(update_fields=["data"])
record(room, GameEvent.SIG_UNREADY, actor=request.user)
polarity = reservation.polarity
# Save remaining seconds on all polarity reservations
try:
seconds_remaining = int(request.POST.get("seconds_remaining", 12))
except (TypeError, ValueError):
seconds_remaining = 12
SigReservation.objects.filter(room=room, polarity=polarity).update(
countdown_remaining=seconds_remaining
)
_notify_countdown_cancel(room_id, polarity, seconds_remaining=seconds_remaining)
from apps.epic.tasks import cancel_polarity_confirm
cancel_polarity_confirm(str(room_id), polarity)
return HttpResponse(status=200)
@login_required
def sig_confirm(request, room_id):
"""No-op: polarity confirmation is now driven server-side by threading.Timer in tasks.py."""
return HttpResponse(status=200)
@login_required @login_required
def select_sig(request, room_id): def select_sig(request, room_id):
if request.method != "POST": if request.method != "POST":

View File

@@ -5,6 +5,7 @@ from . import views as lyric_views
urlpatterns = [ urlpatterns = [
path('send_login_email', lyric_views.send_login_email, name='send_login_email'), path('send_login_email', lyric_views.send_login_email, name='send_login_email'),
path('login', lyric_views.login, name='login'), path('login', lyric_views.login, name='login'),
path('logout', auth_views.LogoutView.as_view(next_page='/'), name='logout') path('logout', auth_views.LogoutView.as_view(next_page='/'), name='logout'),
path('dev-login/<str:session_key>/', lyric_views.dev_login, name='dev_login'),
] ]

View File

@@ -1,4 +1,6 @@
from django.conf import settings
from django.contrib import auth, messages from django.contrib import auth, messages
from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
@@ -27,3 +29,13 @@ def login(request):
else: else:
messages.error(request, "Invalid login link!—please request another") messages.error(request, "Invalid login link!—please request another")
return redirect("/") return redirect("/")
def dev_login(request, session_key):
"""DEBUG-only: set session cookie and redirect. Used by setup_sig_session command."""
if not settings.DEBUG:
raise Http404
next_url = request.GET.get("next", "/")
response = redirect(next_url)
response.set_cookie(settings.SESSION_COOKIE_NAME, session_key, httponly=True)
return response

View File

@@ -57,13 +57,13 @@ INSTALLED_APPS = [
# Board apps # Board apps
'apps.dashboard', 'apps.dashboard',
'apps.gameboard', 'apps.gameboard',
'apps.billboard',
# Gamer apps # Gamer apps
'apps.lyric', 'apps.lyric',
'apps.epic', 'apps.epic',
'apps.drama', 'apps.drama',
'apps.billboard',
'apps.ap',
# Custom apps # Custom apps
'apps.ap',
'apps.api', 'apps.api',
'apps.applets', 'apps.applets',
'functional_tests', 'functional_tests',

View File

@@ -195,10 +195,10 @@ class ChannelsFunctionalTest(ChannelsLiveServerTestCase):
) )
@wait @wait
def wait_for(self, fn): def wait_for(self, fn, browser=None):
return fn() return fn()
def wait_for_slow(self, fn, timeout=30): def wait_for_slow(self, fn, timeout=30, browser=None):
start_time = time.time() start_time = time.time()
while True: while True:
try: try:

View File

@@ -0,0 +1,128 @@
"""
Management command for manual multi-user sig-select testing.
Creates (or reuses) a room with all 6 gate slots filled, roles assigned,
and table_status=SIG_SELECT. Prints one pre-auth URL per gamer so you can
paste them into 6 Firefox Multi-Account Container tabs.
Usage:
python src/manage.py setup_sig_session
python src/manage.py setup_sig_session --base-url http://localhost:8000
python src/manage.py setup_sig_session --room <uuid> # reuse existing room
"""
from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY
from django.contrib.sessions.backends.db import SessionStore
from django.core.management.base import BaseCommand
from apps.epic.models import DeckVariant, GateSlot, Room, TableSeat, TarotCard
from apps.lyric.models import User
GAMERS = [
("founder@test.io", "discoman"),
("amigo@test.io", "amigo"),
("bud@test.io", "bud"),
("pal@test.io", "pal"),
("dude@test.io", "dude"),
("bro@test.io", "bro"),
]
ROLES = ["PC", "NC", "EC", "SC", "AC", "BC"]
def _ensure_earthman():
"""Return (or create) the Earthman DeckVariant with enough sig-deck cards seeded."""
earthman, _ = DeckVariant.objects.get_or_create(
slug="earthman",
defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True},
)
_NAME = {11: "Maid", 12: "Jack", 13: "Queen", 14: "King"}
for suit in ("WANDS", "PENTACLES", "SWORDS", "CUPS"):
for number in (11, 12, 13, 14):
TarotCard.objects.get_or_create(
deck_variant=earthman,
slug=f"{_NAME[number].lower()}-of-{suit.lower()}-em",
defaults={
"arcana": "MINOR",
"suit": suit,
"number": number,
"name": f"{_NAME[number]} of {suit.capitalize()}",
},
)
return earthman
def _make_session(user):
session = SessionStore()
session[SESSION_KEY] = str(user.pk)
session[BACKEND_SESSION_KEY] = "apps.lyric.authentication.PasswordlessAuthenticationBackend"
session[HASH_SESSION_KEY] = user.get_session_auth_hash()
session.save()
return session.session_key
class Command(BaseCommand):
help = "Set up a SIG_SELECT room and print pre-auth URLs for all six gamers"
def add_arguments(self, parser):
parser.add_argument("--base-url", default="http://localhost:8000")
parser.add_argument("--room", default=None, help="UUID of an existing room to reuse")
def handle(self, *args, **options):
base_url = options["base_url"].rstrip("/")
earthman = _ensure_earthman()
# ── Users ────────────────────────────────────────────────────────────
users = []
for email, _ in GAMERS:
user, _ = User.objects.get_or_create(email=email)
user.is_staff = True
user.is_superuser = True
if not user.equipped_deck:
user.equipped_deck = earthman
user.save()
users.append(user)
# ── Room ─────────────────────────────────────────────────────────────
if options["room"]:
room = Room.objects.get(pk=options["room"])
else:
room = Room.objects.create(
name="Sig Select Test Room",
owner=users[0],
visibility=Room.PUBLIC,
)
# ── Gate slots ───────────────────────────────────────────────────────
for i, user in enumerate(users, start=1):
slot = room.gate_slots.get(slot_number=i)
slot.gamer = user
slot.status = GateSlot.FILLED
slot.save()
room.gate_status = Room.OPEN
room.save()
# ── Table seats + roles ──────────────────────────────────────────────
for i, (user, role) in enumerate(zip(users, ROLES), start=1):
TableSeat.objects.update_or_create(
room=room, slot_number=i,
defaults={"gamer": user, "role": role, "role_revealed": True},
)
room.table_status = Room.SIG_SELECT
room.save()
# ── Print URLs ───────────────────────────────────────────────────────
room_path = f"/gameboard/room/{room.pk}/"
self.stdout.write(f"\nRoom: {base_url}{room_path}\n")
self.stdout.write(f"{'Container':<12} {'Email':<22} {'Role':<6} URL")
self.stdout.write("" * 100)
for (email, container), user, role in zip(GAMERS, users, ROLES):
session_key = _make_session(user)
url = f"{base_url}/lyric/dev-login/{session_key}/?next={room_path}"
self.stdout.write(f"{container:<12} {email:<22} {role:<6} {url}")
self.stdout.write("")

View File

@@ -101,7 +101,7 @@ class BillboardScrollTest(FunctionalTest):
self.assertIn("deposits a Free Token for slot 2 (expires in 7 days).", scroll.text) self.assertIn("deposits a Free Token for slot 2 (expires in 7 days).", scroll.text)
# Role selection event is rendered as prose # Role selection event is rendered as prose
self.assertIn("elects to start as the Player", scroll.text) self.assertIn("assumes 1st Chair", scroll.text)
# ------------------------------------------------------------------ # # ------------------------------------------------------------------ #
# Test 3 — current user's events are right-aligned; others' are left # # Test 3 — current user's events are right-aligned; others' are left #
@@ -354,3 +354,149 @@ class BillscrollEntryLayoutTest(FunctionalTest):
# events[0] is the backdated record (oldest first, ascending order) # events[0] is the backdated record (oldest first, ascending order)
old_ts = events[0].find_element(By.CSS_SELECTOR, ".drama-event-time") old_ts = events[0].find_element(By.CSS_SELECTOR, ".drama-event-time")
self.assertRegex(old_ts.text, r"\d{2}\s+[A-Z][a-z]{2}\s+\d{4}") self.assertRegex(old_ts.text, r"\d{2}\s+[A-Z][a-z]{2}\s+\d{4}")
class BillscrollGearMenuTest(FunctionalTest):
"""
FT: the billscroll page has a gear menu that filters events by label.
Frame = all regular (non-struck) drama entries.
Redact = struck-through (retracted) entries, e.g. a WAIT NVM after TAKE SIG.
Scenario (one gamer, Role + Sig events):
1. Both labels checked by default — all events visible.
2. Uncheck Redact → OK: struck entries disappear.
3. Recheck Redact + uncheck Frame → OK: regular entries gone; struck
entries visible (but still render struck-through — they remain "redacted"
in the narrative sense).
4. Recheck Frame → OK: all entries return.
"""
def setUp(self):
super().setUp()
self.founder = User.objects.create(email="founder@geartest.io")
self.room = Room.objects.create(name="Gear Filter Room", owner=self.founder)
# Two Frame events — ROLE_SELECTED, non-struck
record(self.room, GameEvent.ROLE_SELECTED, actor=self.founder,
role="PC", slot_number=1, role_display="Player")
record(self.room, GameEvent.ROLE_SELECTED, actor=self.founder,
role="NC", slot_number=2, role_display="Narrator")
# Two Redact events — SIG_READY with retracted=True → event.struck is True
sig1 = record(self.room, GameEvent.SIG_READY, actor=self.founder,
card_name="The Wanderer", corner_rank="0", suit_icon="")
sig1.data["retracted"] = True
sig1.save(update_fields=["data"])
sig2 = record(self.room, GameEvent.SIG_READY, actor=self.founder,
card_name="Maid of Brands", corner_rank="M",
suit_icon="fa-wand-sparkles")
sig2.data["retracted"] = True
sig2.save(update_fields=["data"])
def _go_to_scroll(self):
self.create_pre_authenticated_session("founder@geartest.io")
self.browser.get(
self.live_server_url + f"/billboard/room/{self.room.id}/scroll/"
)
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_drama_scroll")
)
def _open_gear(self):
"""Click the gear btn and return the now-visible menu element."""
gear = self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR,
".gear-btn[data-menu-target='id_billscroll_menu']"
)
)
self.browser.execute_script("arguments[0].click()", gear)
return self.wait_for(
lambda: self.browser.find_element(By.ID, "id_billscroll_menu")
)
def _visible_events(self, label):
"""Count displayed .drama-event elements with the given data-label."""
els = self.browser.find_elements(
By.CSS_SELECTOR, f".drama-event[data-label='{label}']"
)
return sum(1 for e in els if e.is_displayed())
# ------------------------------------------------------------------ #
# Step 1 — gear menu opens; both labels present and pre-checked #
# ------------------------------------------------------------------ #
def test_gear_menu_shows_frame_and_redact_checkboxes(self):
self._go_to_scroll()
menu = self._open_gear()
self.assertTrue(menu.is_displayed())
frame_cb = menu.find_element(By.CSS_SELECTOR, "input[value='frame']")
redact_cb = menu.find_element(By.CSS_SELECTOR, "input[value='redact']")
self.assertTrue(frame_cb.is_selected())
self.assertTrue(redact_cb.is_selected())
self.assertIn("Frame", menu.text)
self.assertIn("Redact", menu.text)
# ------------------------------------------------------------------ #
# Steps 2 4 — filter flow #
# ------------------------------------------------------------------ #
def test_gear_menu_filter_flow(self):
self._go_to_scroll()
# Step 1: all 4 events visible (2 frame + 2 redact)
self.assertEqual(self._visible_events("frame"), 2)
self.assertEqual(self._visible_events("redact"), 2)
# Step 2: uncheck Redact → OK → struck entries disappear
menu = self._open_gear()
menu.find_element(By.CSS_SELECTOR, "input[value='redact']").click()
menu.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
self.wait_for(lambda: self.assertEqual(self._visible_events("redact"), 0))
self.assertEqual(self._visible_events("frame"), 2)
# Step 3: recheck Redact + uncheck Frame → OK
# Redact events re-appear (still struck-through); Frame events gone.
menu = self._open_gear()
menu.find_element(By.CSS_SELECTOR, "input[value='redact']").click() # recheck
menu.find_element(By.CSS_SELECTOR, "input[value='frame']").click() # uncheck
menu.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
self.wait_for(lambda: self.assertEqual(self._visible_events("frame"), 0))
self.assertEqual(self._visible_events("redact"), 2)
# Struck-through entries still carry the .struck class (visually "gone" in narrative)
redact_bodies = self.browser.find_elements(
By.CSS_SELECTOR, ".drama-event[data-label='redact'] .drama-event-body"
)
self.assertTrue(all("struck" in b.get_attribute("class") for b in redact_bodies))
# Step 4: recheck Frame → OK → all events return
menu = self._open_gear()
menu.find_element(By.CSS_SELECTOR, "input[value='frame']").click() # recheck
menu.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
self.wait_for(lambda: self.assertEqual(self._visible_events("frame"), 2))
self.assertEqual(self._visible_events("redact"), 2)
# ------------------------------------------------------------------ #
# Persistence — filter survives a full page reload #
# ------------------------------------------------------------------ #
def test_filter_selection_persists_across_refresh(self):
self._go_to_scroll()
# Uncheck Redact → OK: struck entries disappear
menu = self._open_gear()
menu.find_element(By.CSS_SELECTOR, "input[value='redact']").click()
menu.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
self.wait_for(lambda: self.assertEqual(self._visible_events("redact"), 0))
# Hard reload — same URL, same session cookie
self.browser.refresh()
self.wait_for(lambda: self.browser.find_element(By.ID, "id_drama_scroll"))
# Struck entries still absent after reload
self.wait_for(lambda: self.assertEqual(self._visible_events("redact"), 0))
self.assertEqual(self._visible_events("frame"), 2)
# Gear menu still shows Redact unchecked
menu = self._open_gear()
redact_cb = menu.find_element(By.CSS_SELECTOR, "input[value='redact']")
self.assertFalse(redact_cb.is_selected())

View File

@@ -119,6 +119,9 @@ class DashboardMaintenanceTest(FunctionalTest):
class AppletMenuDismissTest(FunctionalTest): class AppletMenuDismissTest(FunctionalTest):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
# Portrait viewport: sidebars don't activate, h2 sits safely above
# #id_dash_content and can't be obscured by it regardless of font metrics.
self.browser.set_window_size(800, 1200)
Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) Applet.objects.get_or_create(slug="username", defaults={"name": "Username"})
Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"}) Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"})
self.create_pre_authenticated_session("discoman@example.com") self.create_pre_authenticated_session("discoman@example.com")

View File

@@ -34,14 +34,15 @@ def _assign_all_roles(room, role_order=None):
slug="earthman", slug="earthman",
defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True}, defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True},
) )
# Seed the 18 sig deck cards (migration data is flushed in TransactionTestCase FTs) # Seed the 18 sig deck cards (migration data is flushed in TransactionTestCase FTs).
# _sig_unique_cards() filters arcana=MIDDLE, suits BRANDS/CROWNS/BLADES/GRAILS (Earthman).
_NAME = {11: "Maid", 12: "Jack", 13: "Queen", 14: "King"} _NAME = {11: "Maid", 12: "Jack", 13: "Queen", 14: "King"}
for suit in ("WANDS", "PENTACLES", "SWORDS", "CUPS"): for suit in ("BRANDS", "CROWNS", "BLADES", "GRAILS"):
for number in (11, 12, 13, 14): for number in (11, 12, 13, 14):
TarotCard.objects.get_or_create( TarotCard.objects.get_or_create(
deck_variant=earthman, deck_variant=earthman,
slug=f"{_NAME[number].lower()}-of-{suit.lower()}-em", slug=f"{_NAME[number].lower()}-of-{suit.lower()}-em",
defaults={"arcana": "MINOR", "suit": suit, "number": number, defaults={"arcana": "MIDDLE", "suit": suit, "number": number,
"name": f"{_NAME[number]} of {suit.capitalize()}"}, "name": f"{_NAME[number]} of {suit.capitalize()}"},
) )
for number, name, slug in [ for number, name, slug in [
@@ -138,3 +139,652 @@ class SigSelectChannelsTest(ChannelsFunctionalTest):
)) ))
return b return b
def _setup_sig_select_room(self):
"""Create a full SIG_SELECT room; return (room, [user_pc, user_nc, ...])."""
emails = [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
]
founder, _ = User.objects.get_or_create(email=emails[0])
room = Room.objects.create(name="Cursor Colour Test", owner=founder)
gamers = _fill_room_via_orm(room, emails)
_assign_all_roles(room)
return room, gamers
# ── SC1: NC hover → PC sees mid cursor active, coloured --priYl ────────── #
@tag('channels')
def test_nc_hover_activates_mid_cursor_in_pc_browser(self):
"""
When NC (levity mid) hovers a card, PC (levity left) must see the
--mid cursor become active, coloured --priYl (rgb 255 207 52).
Verifies: WS broadcast pipeline + JS applyHover + CSS role colouring.
"""
room, gamers = self._setup_sig_select_room()
room_url = self.live_server_url + f"/gameboard/room/{room.pk}/"
# ── Browser 1: PC (founder) ───────────────────────────────────────────
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
# ── Browser 2: NC (amigo) ─────────────────────────────────────────────
browser2 = self._make_browser2("amigo@test.io")
try:
browser2.get(room_url)
self.wait_for(lambda: browser2.find_element(By.CSS_SELECTOR, ".sig-overlay"))
# Grab the first card ID visible in browser2's deck
first_card = browser2.find_element(By.CSS_SELECTOR, ".sig-card")
card_id = first_card.get_attribute("data-card-id")
# Hover over it — triggers sendHover() → WS broadcast
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(browser2).move_to_element(first_card).perform()
# ── Browser 1 should see --mid cursor go active (anchor carries class) ─
mid_cursor_sel = f'.sig-card[data-card-id="{card_id}"] .sig-cursor--mid'
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, mid_cursor_sel + ".active"
)
)
# CSS colour check: portal float has data-role="NC" → --priYl = 255, 207, 52
portal_sel = '.sig-cursor-float[data-role="NC"]'
portal_cursor = self.browser.find_element(By.CSS_SELECTOR, portal_sel)
color = self.browser.execute_script(
"return window.getComputedStyle(arguments[0]).color",
portal_cursor,
)
self.assertEqual(color, "rgb(255, 207, 52)", f"Expected --priYl colour for NC cursor, got {color}")
# ── Mouse-off: anchor class removed, portal float gone ────────────
ActionChains(browser2).move_to_element(
browser2.find_element(By.CSS_SELECTOR, ".sig-stage")
).perform()
self.wait_for(
lambda: not self.browser.find_elements(
By.CSS_SELECTOR, mid_cursor_sel + ".active"
)
)
finally:
browser2.quit()
# ── SC2: NC reserves → PC sees card border coloured --priYl ──────────── #
@tag('channels')
def test_nc_reservation_glows_priYl_in_pc_browser(self):
"""
When NC (levity mid) clicks OK on a card, PC must see that card's border
coloured --priYl (rgb 255 207 52) via the data-reserved-by CSS selector.
Verifies: sig_reserve view → WS broadcast → applyReservation → CSS glow.
"""
room, gamers = self._setup_sig_select_room()
room_url = self.live_server_url + f"/gameboard/room/{room.pk}/"
# ── Browser 1: PC (founder) ───────────────────────────────────────────
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
# ── Browser 2: NC (amigo) ─────────────────────────────────────────────
browser2 = self._make_browser2("amigo@test.io")
try:
browser2.get(room_url)
self.wait_for(lambda: browser2.find_element(By.CSS_SELECTOR, ".sig-overlay"))
# Get first card in B2's deck
first_card = browser2.find_element(By.CSS_SELECTOR, ".sig-card")
card_id = first_card.get_attribute("data-card-id")
# Click card body → .sig-focused → OK button appears
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(browser2).move_to_element(first_card).perform()
first_card.click()
ok_btn = self.wait_for(
lambda: browser2.find_element(By.CSS_SELECTOR, ".sig-focused .sig-ok-btn")
)
ok_btn.click()
# ── B1 should see the card's border turn --priYl ──────────────────
reserved_card_sel = f'.sig-card[data-card-id="{card_id}"]'
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, reserved_card_sel + '[data-reserved-by="NC"]'
)
)
reserved_card = self.browser.find_element(By.CSS_SELECTOR, reserved_card_sel)
box_shadow = self.browser.execute_script(
"return window.getComputedStyle(arguments[0]).boxShadow",
reserved_card,
)
self.assertIn(
"255, 207, 52", box_shadow,
f"Expected --priYl (255,207,52) in box-shadow for NC reservation, got {box_shadow}",
)
finally:
browser2.quit()
# ── Polarity theming: qualifier text + no correspondence ─────────────────────
class SigSelectThemeTest(FunctionalTest):
"""Polarity-qualifier display (Graven/Leavened) and correspondence suppression.
No WebSocket needed — stage updates are local; uses plain FunctionalTest."""
EMAILS = [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
]
def setUp(self):
super().setUp()
self.browser.set_window_size(800, 1200)
Applet.objects.get_or_create(slug="new-game", defaults={"name": "New Game", "context": "gameboard"})
Applet.objects.get_or_create(slug="my-games", defaults={"name": "My Games", "context": "gameboard"})
def _setup_sig_room(self):
founder, _ = User.objects.get_or_create(email=self.EMAILS[0])
room = Room.objects.create(name="Theme Test", owner=founder)
_fill_room_via_orm(room, self.EMAILS)
_assign_all_roles(room)
return room
def _hover_card(self, css):
from selenium.webdriver.common.action_chains import ActionChains
card = self.browser.find_element(By.CSS_SELECTOR, css)
ActionChains(self.browser).move_to_element(card).perform()
return card
# ── ST1: Levity (Leavened) qualifier ──────────────────────────────────── #
def test_levity_non_major_card_shows_leavened_above(self):
"""Hovering a non-major card in the levity overlay shows 'Leavened' in
qualifier-above and nothing in qualifier-below."""
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io") # PC = levity
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._hover_card('.sig-card[data-arcana="Middle Arcana"]')
above = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-qualifier-above")
)
self.assertEqual(above.text, "Leavened")
below = self.browser.find_element(By.CSS_SELECTOR, ".sig-qualifier-below")
self.assertEqual(below.text, "")
def test_levity_major_card_shows_leavened_below(self):
"""Hovering a major arcana card in the levity overlay shows 'Leavened' in
qualifier-below and nothing in qualifier-above."""
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io") # PC = levity
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._hover_card('.sig-card[data-arcana="Major Arcana"]')
below = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-qualifier-below")
)
self.assertEqual(below.text, "Leavened")
above = self.browser.find_element(By.CSS_SELECTOR, ".sig-qualifier-above")
self.assertEqual(above.text, "")
# ── ST2: Gravity (Graven) qualifier ───────────────────────────────────── #
def test_gravity_non_major_card_shows_graven_above(self):
"""EC (bud) sees the gravity overlay; hovering a non-major card shows 'Graven'."""
room = self._setup_sig_room()
self.create_pre_authenticated_session("bud@test.io") # EC = gravity
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._hover_card('.sig-card[data-arcana="Middle Arcana"]')
above = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-qualifier-above")
)
self.assertEqual(above.text, "Graven")
# ── ST3: Correspondence not shown ─────────────────────────────────────── #
def test_correspondence_not_shown_in_sig_select(self):
"""The Minchiate-equivalence field must always be blank on the stage card."""
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
# Hover any card — correspondence should remain empty regardless
self._hover_card(".sig-card")
self.wait_for(lambda: self.browser.find_element(
By.CSS_SELECTOR, ".sig-stage-card"
))
corr = self.browser.find_element(By.CSS_SELECTOR, ".fan-card-correspondence")
self.assertEqual(corr.text, "")
# ── TAKE SIG / WAIT NVM — ready gate ──────────────────────────────────────────
#
# TAKE SIG (.btn.btn-primary) appears at the bottom-left corner of the card
# stage preview once a gamer has clicked OK on a card (SigReservation exists).
# Clicking it sets the gamer's status to ready and changes the btn to WAIT NVM.
# WAIT NVM cancels the ready status and reverts back to TAKE SIG.
#
# When all three gamers in a polarity WS room are ready, a 12-second countdown
# starts. Any WAIT NVM during the countdown cancels it; the saved remaining time
# is resumed when all three are ready again. When the countdown completes
# (client POSTs sig_confirm) the polarity group returns to the table hex.
# When both polarity groups have confirmed, PICK SKY btn appears in the hex
# center for all six gamers.
#
# ─────────────────────────────────────────────────────────────────────────────
class SigReadyGateTest(FunctionalTest):
"""Single-browser tests for TAKE SIG / WAIT NVM btn."""
def setUp(self):
super().setUp()
self.browser.set_window_size(800, 1200)
Applet.objects.get_or_create(
slug="new-game", defaults={"name": "New Game", "context": "gameboard"}
)
Applet.objects.get_or_create(
slug="my-games", defaults={"name": "My Games", "context": "gameboard"}
)
def _setup_sig_room(self):
emails = [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
]
founder, _ = User.objects.get_or_create(email=emails[0])
room = Room.objects.create(name="Ready Gate Test", owner=founder)
_fill_room_via_orm(room, emails)
_assign_all_roles(room)
return room
def _click_ok_on_any_card(self):
"""Click the first sig card to stage it, then click OK."""
card = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-card")
)
card.click()
ok_btn = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-ok-btn")
)
ok_btn.click()
# ── SRG1: TAKE SIG btn not visible before OK ──────────────────────── #
def test_take_sig_btn_not_visible_before_ok_click(self):
"""TAKE SIG must be absent until the gamer has OK'd a card."""
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
take_sig_btns = self.browser.find_elements(By.ID, "id_take_sig_btn")
self.assertEqual(len(take_sig_btns), 0)
# ── SRG2: TAKE SIG btn appears after OK ──────────────────────────── #
def test_take_sig_btn_appears_after_ok_click(self):
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._click_ok_on_any_card()
take_sig_btn = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_take_sig_btn")
)
self.assertIn("TAKE SIG", take_sig_btn.text.upper())
# ── SRG3: TAKE SIG → WAIT NVM ─────────────────────────────────────── #
def test_take_sig_btn_becomes_wait_no_after_click(self):
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._click_ok_on_any_card()
take_sig_btn = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_take_sig_btn")
)
take_sig_btn.click()
wait_no_btn = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_take_sig_btn")
)
self.assertIn("WAIT NVM", wait_no_btn.text.upper())
# WAIT NVM pulses a --terOr glow: btn-cancel class appears within one tick
self.wait_for(
lambda: "btn-cancel" in self.browser.find_element(
By.ID, "id_take_sig_btn"
).get_attribute("class")
)
# ── SRG4: WAIT NVM → TAKE SIG ─────────────────────────────────────── #
def test_wait_no_reverts_to_take_sig(self):
room = self._setup_sig_room()
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".sig-overlay"))
self._click_ok_on_any_card()
btn = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_take_sig_btn")
)
btn.click() # → WAIT NVM
self.wait_for(lambda: "WAIT NVM" in self.browser.find_element(
By.ID, "id_take_sig_btn").text.upper()
)
btn = self.browser.find_element(By.ID, "id_take_sig_btn")
btn.click() # → TAKE SIG again
self.wait_for(
lambda: self.assertIn(
"TAKE SIG",
self.browser.find_element(By.ID, "id_take_sig_btn").text.upper(),
)
)
@tag("channels")
class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
"""Multi-browser WebSocket tests for the polarity-room countdown and PICK SKY."""
def setUp(self):
super().setUp()
self.browser.set_window_size(800, 1200)
Applet.objects.get_or_create(
slug="new-game", defaults={"name": "New Game", "context": "gameboard"}
)
Applet.objects.get_or_create(
slug="my-games", defaults={"name": "My Games", "context": "gameboard"}
)
def _make_browser_for(self, email):
session_key = create_pre_authenticated_session(email)
options = webdriver.FirefoxOptions()
if os.environ.get("HEADLESS"):
options.add_argument("--headless")
b = webdriver.Firefox(options=options)
b.set_window_size(800, 1200)
b.get(self.live_server_url + "/404_no_such_url/")
b.add_cookie(dict(
name=django_settings.SESSION_COOKIE_NAME,
value=session_key,
path="/",
))
return b
def _ok_card_in_browser(self, b):
"""Reserve any available sig-card, then JS-click its OK button to trigger
the page's applyReservation() and reveal #id_take_sig_btn.
Iterates through cards until one succeeds — multiple browsers in the same
polarity group would otherwise all try to reserve the same first card and
get 409 conflicts."""
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
result = b.execute_async_script("""
var cb = arguments[arguments.length - 1];
var overlay = document.querySelector('.sig-overlay');
var cards = document.querySelectorAll('.sig-card');
if (!overlay || !cards.length) { cb({error: 'no overlay or cards'}); return; }
var reserveUrl = overlay.dataset.reserveUrl;
var csrfM = document.cookie.match(/csrftoken=([^;]+)/);
var csrf = csrfM ? csrfM[1] : '';
function tryCard(idx) {
if (idx >= cards.length) { cb({error: 'all cards taken'}); return; }
var cardId = cards[idx].dataset.cardId;
fetch(reserveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': csrf,
},
body: 'action=reserve&card_id=' + encodeURIComponent(cardId),
}).then(function (res) {
if (res.status === 409) { tryCard(idx + 1); return; }
cb({status: res.status, ok: res.ok, cardId: cardId});
}).catch(function (e) { cb({error: e.message}); });
}
tryCard(0);
""")
if not (result and result.get('ok')):
raise AssertionError(f"sig_reserve fetch failed: {result}")
# Fetch confirmed 200 — JS-click the *correct* card's OK button so
# applyReservation() runs in page context and reveals #id_take_sig_btn.
# (Idempotent re-reserve of the same card → 200, safe.)
card_id = result['cardId']
ok_btn = b.find_element(
By.CSS_SELECTOR, f'.sig-card[data-card-id="{card_id}"] .sig-ok-btn'
)
b.execute_script("arguments[0].click()", ok_btn)
def _setup_sig_select_room(self):
emails = [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
]
founder, _ = User.objects.get_or_create(email=emails[0])
room = Room.objects.create(name="Countdown Test", owner=founder)
_fill_room_via_orm(room, emails)
_assign_all_roles(room)
return room, emails
# ── SRG5: countdown appears when all three polarity ready ─────────── #
@tag("channels")
def test_countdown_element_appears_when_all_three_levity_gamers_ready(self):
"""When PC, NC, and SC each click TAKE SIG the countdown becomes visible."""
room, emails = self._setup_sig_select_room()
levity_emails = [emails[0], emails[1], emails[3]] # PC, NC, SC
browsers = []
try:
for email in levity_emails:
b = self._make_browser_for(email)
b.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
browsers.append(b)
# Each levity gamer OK's a card then clicks TAKE SIG
for b in browsers:
self._ok_card_in_browser(b)
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
)
b.find_element(By.ID, "id_take_sig_btn").click()
# All three browsers should now see the countdown button (numeral text)
for b in browsers:
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn").text.isdigit(),
browser=b,
)
finally:
for b in browsers:
b.quit()
# ── SRG6: countdown disappears when WAIT NVM clicked ──────────────── #
@tag("channels")
def test_countdown_disappears_when_any_levity_gamer_clicks_wait_no(self):
"""Any WAIT NVM during the countdown cancels it for all three browsers."""
room, emails = self._setup_sig_select_room()
levity_emails = [emails[0], emails[1], emails[3]]
browsers = []
try:
for email in levity_emails:
b = self._make_browser_for(email)
b.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
browsers.append(b)
# All go ready
for b in browsers:
self._ok_card_in_browser(b)
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
)
b.find_element(By.ID, "id_take_sig_btn").click()
# Confirm countdown started for all (button text is a numeral)
for b in browsers:
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn").text.isdigit(),
browser=b,
)
# PC clicks the countdown button to cancel
browsers[0].find_element(By.ID, "id_take_sig_btn").click()
# Countdown should cancel for all three (button back to WAIT NVM)
for b in browsers:
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn").text == "WAIT NVM",
browser=b,
)
finally:
for b in browsers:
b.quit()
# ── SRG7: PICK SKY btn appears after both polarity groups confirm ─── #
@tag("channels")
def test_pick_sky_btn_appears_in_hex_after_both_groups_confirm(self):
"""Once both levity and gravity countdowns complete, all six browsers
see the PICK SKY btn in the table hex center."""
# This test drives the full flow end-to-end but uses ORM shortcuts
# to set all-ready state for one polarity, letting the other complete
# via the UI, to keep execution time manageable.
room, emails = self._setup_sig_select_room()
# Pre-confirm gravity via ORM: set significators on EC/AC/BC seats
from apps.epic.models import TarotCard, DeckVariant
earthman = DeckVariant.objects.get(slug="earthman")
grav_roles = ["EC", "AC", "BC"]
grav_suits = ["GRAILS", "BLADES", "CROWNS"]
for role, suit in zip(grav_roles, grav_suits):
card = TarotCard.objects.get(
deck_variant=earthman, arcana="MIDDLE", suit=suit, number=11
)
seat = room.table_seats.get(role=role)
seat.significator = card
seat.save()
levity_emails = [emails[0], emails[1], emails[3]]
browsers = []
try:
for email in levity_emails:
b = self._make_browser_for(email)
b.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
browsers.append(b)
# All levity gamers OK and TAKE SIG
for b in browsers:
self._ok_card_in_browser(b)
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
)
b.find_element(By.ID, "id_take_sig_btn").click()
# Wait for countdown to expire or be confirmed; PICK SKY appears in hex
# countdown is 12 s so use wait_for_slow (MAX_WAIT=10 is not enough)
for b in browsers:
self.wait_for_slow(
lambda: b.find_element(By.ID, "id_pick_sky_btn"), browser=b
)
finally:
for b in browsers:
b.quit()
# ── SRG8: first-done group sees waiting message ───────────────────── #
@tag("channels")
def test_first_done_polarity_sees_other_group_settling_message(self):
"""After levity confirms but gravity hasn't yet, levity gamers see
'Gravity settling . . .' on the dormant hex."""
room, emails = self._setup_sig_select_room()
levity_emails = [emails[0], emails[1], emails[3]]
browsers = []
try:
for email in levity_emails:
b = self._make_browser_for(email)
b.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
browsers.append(b)
for b in browsers:
self._ok_card_in_browser(b)
self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
)
b.find_element(By.ID, "id_take_sig_btn").click()
# Wait for levity confirm → hex revealed, waiting message visible
# (countdown is 12 s, so wait_for's 10 s MAX_WAIT is not enough)
for b in browsers:
self.wait_for_slow(
lambda: "settling" in b.find_element(
By.ID, "id_hex_waiting_msg"
).text.lower(),
browser=b,
)
finally:
for b in browsers:
b.quit()
# ── SKY OVERLAY (natal wheel) — DEFERRED / PENDING PYSWISS ──────────────────
#
# These FTs outline the sky overlay behavior but are left as stubs.
# The sky overlay will be built after the PySwiss microservice (step 18)
# and the D3 natal wheel implementation. A prototype already exists and
# will be reviewed before these tests are filled in.
#
# class PickSkyTrayFlowTest(FunctionalTest):
#
# def test_pick_sky_btn_opens_tray_with_sig_card_in_slot_2(self):
# """Clicking PICK SKY opens #id_tray; tray cell 2 shows the gamer's
# sig card icon (Blank.svg placeholder until card-specific icons land)."""
# ...
#
# def test_tray_close_dismisses_sig_overlay_and_reveals_hex(self):
# """After tray closes the sig select modal is gone and the table hex
# is visible again."""
# ...
#
# def test_sky_overlay_appears_over_hex_after_tray_closes(self):
# """The sky overlay (#id_sky_overlay) appears over the hex once the
# tray animation completes."""
# ...
#
# def test_sky_overlay_prompts_for_input_date(self):
# """The sky overlay contains a date input field for natal wheel
# calculation via the PySwiss microservice API."""
# ...
#
# def test_sky_overlay_renders_natal_wheel_for_given_date(self):
# """Submitting a date triggers a D3-drawn natal wheel (pyswisseph
# data). Each house/planet is individually navigable."""
# ...
#
# def test_sky_overlay_accessible_during_play_for_timeframe_changes(self):
# """During IN_GAME phase a gamer can reopen the sky overlay to change
# the timeframe or check aspects presiding over the current scene."""
# ...
#
# ─────────────────────────────────────────────────────────────────────────────

View File

@@ -1,13 +1,15 @@
describe("SigSelect", () => { describe("SigSelect", () => {
let testDiv, stageCard, card, statBlock; let testDiv, stageCard, card, statBlock;
function makeFixture({ reservations = '{}', cardCautions = '[]' } = {}) { function makeFixture({ reservations = '{}', cardCautions = '[]', polarity = 'levity', userRole = 'PC' } = {}) {
testDiv = document.createElement("div"); testDiv = document.createElement("div");
testDiv.innerHTML = ` testDiv.innerHTML = `
<div class="sig-overlay" <div class="sig-overlay"
data-polarity="levity" data-polarity="${polarity}"
data-user-role="PC" data-user-role="${userRole}"
data-reserve-url="/epic/room/test/sig-reserve" data-reserve-url="/epic/room/test/sig-reserve"
data-ready-url="/epic/room/test/sig-ready"
data-reservations="${reservations.replace(/"/g, '&quot;')}"> data-reservations="${reservations.replace(/"/g, '&quot;')}">
<div class="sig-modal"> <div class="sig-modal">
<div class="sig-stage"> <div class="sig-stage">
@@ -15,7 +17,9 @@ describe("SigSelect", () => {
<span class="fan-corner-rank"></span> <span class="fan-corner-rank"></span>
<i class="stage-suit-icon"></i> <i class="stage-suit-icon"></i>
<p class="fan-card-name-group"></p> <p class="fan-card-name-group"></p>
<p class="sig-qualifier-above"></p>
<h3 class="fan-card-name"></h3> <h3 class="fan-card-name"></h3>
<p class="sig-qualifier-below"></p>
<p class="fan-card-arcana"></p> <p class="fan-card-arcana"></p>
<p class="fan-card-correspondence"></p> <p class="fan-card-correspondence"></p>
</div> </div>
@@ -409,4 +413,273 @@ describe("SigSelect", () => {
expect(statBlock.querySelectorAll("#id_stat_keywords_reversed li").length).toBe(0); expect(statBlock.querySelectorAll("#id_stat_keywords_reversed li").length).toBe(0);
}); });
}); });
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
//
// Fixture polarity = levity, userRole = PC.
// POLARITY_ROLES: levity → [PC, NC, SC] = [left, mid, right]
//
// Only tests the JS position mapping — colour is CSS-only.
describe("WS cursor hover", () => {
beforeEach(() => makeFixture());
it("NC hover activates the --mid cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
expect(card.querySelector(".sig-cursor--mid").classList.contains("active")).toBe(true);
});
it("SC hover activates the --right cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "SC", active: true },
}));
expect(card.querySelector(".sig-cursor--right").classList.contains("active")).toBe(true);
});
it("own role (PC) hover event is ignored — no cursor activates", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "PC", active: true },
}));
expect(card.querySelectorAll(".sig-cursor.active").length).toBe(0);
});
it("hover-off removes .active from the cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: false },
}));
expect(card.querySelector(".sig-cursor--mid").classList.contains("active")).toBe(false);
});
it("hover on unknown card_id is a no-op", () => {
expect(() => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 9999, role: "NC", active: true },
}));
}).not.toThrow();
});
});
// ── WS reservation — data-reserved-by attribute ───────────────────────── //
//
// applyReservation() sets data-reserved-by so the CSS can glow the card in
// the reserving gamer's role colour. These tests assert the attribute, not
// the colour (CSS variables aren't resolvable in the SpecRunner context).
describe("WS reservation sets data-reserved-by", () => {
beforeEach(() => makeFixture());
it("peer reservation sets data-reserved-by to the reserving role", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(card.dataset.reservedBy).toBe("NC");
});
it("peer reservation also adds .sig-reserved class", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(card.classList.contains("sig-reserved")).toBe(true);
});
it("release removes data-reserved-by", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: false },
}));
expect(card.dataset.reservedBy).toBeUndefined();
});
it("own reservation (PC) sets data-reserved-by AND .sig-reserved--own", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "PC", reserved: true },
}));
expect(card.dataset.reservedBy).toBe("PC");
expect(card.classList.contains("sig-reserved--own")).toBe(true);
});
it("peer reservation places a thumbs-up float and removes any hand-pointer float", () => {
// First, a hover float exists for NC (mid cursor)
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
expect(document.querySelector('.sig-cursor-float[data-role="NC"]')).not.toBeNull();
expect(document.querySelector('.fa-hand-pointer[data-role="NC"]')).not.toBeNull();
// NC then clicks OK — reservation arrives
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
// Thumbs-up replaces hand-pointer
const floatEl = document.querySelector('.sig-cursor-float[data-role="NC"]');
expect(floatEl).not.toBeNull();
expect(floatEl.classList.contains("fa-thumbs-up")).toBe(true);
expect(floatEl.classList.contains("fa-hand-pointer")).toBe(false);
});
it("peer release removes the thumbs-up float", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(document.querySelector('.sig-cursor-float--reserved[data-role="NC"]')).not.toBeNull();
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: false },
}));
expect(document.querySelector('.sig-cursor-float--reserved[data-role="NC"]')).toBeNull();
});
});
// ── Polarity theming — stage qualifier text ────────────────────────────── //
//
// On mouseenter, updateStage() injects "Leavened" or "Graven" into the
// sig-qualifier-above (non-major) or sig-qualifier-below (major arcana) slot.
// Correspondence field is never populated in sig-select context.
describe("polarity theming — stage qualifier", () => {
it("levity non-major card puts 'Leavened' in qualifier-above, qualifier-below empty", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
// data-arcana defaults to "Minor Arcana" in fixture → non-major
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("Leavened");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("");
});
it("levity major arcana card puts 'Leavened' in qualifier-below, qualifier-above empty", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Leavened");
});
it("major arcana title gets a trailing comma (qualifier reads as subtitle)", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.arcana = "Major Arcana";
card.dataset.nameTitle = "The Schizo";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-name").textContent).toBe("The Schizo,");
});
it("non-major arcana title has no trailing comma", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
// fixture default: Minor Arcana, "King of Pentacles"
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-name").textContent).toBe("King of Pentacles");
});
it("gravity non-major card puts 'Graven' in qualifier-above", () => {
makeFixture({ polarity: 'gravity', userRole: 'BC' });
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("Graven");
});
it("gravity major arcana card puts 'Graven' in qualifier-below", () => {
makeFixture({ polarity: 'gravity', userRole: 'BC' });
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Graven");
});
it("hovering clears qualifier slots from the previous card", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
// Now major — above should be empty, below filled
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Leavened");
});
it("correspondence field is never populated", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.correspondence = "Il Bagatto (Minchiate)";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-correspondence").textContent).toBe("");
});
});
// ── WAIT NVM glow pulse ────────────────────────────────────────────────────── //
//
// After clicking TAKE SIG (POST ok → isReady=true) a setInterval pulses the
// button at 600ms: odd ticks add .btn-cancel + a --terOr outer box-shadow;
// even ticks remove both. Uses jasmine.clock() to advance the fake timer.
describe("WAIT NVM glow pulse", () => {
let takeSigBtn;
beforeEach(() => {
jasmine.clock().install();
// Pre-reserve card 42 as PC so _showTakeSigBtn() fires during init
makeFixture({ reservations: '{"42":"PC"}' });
takeSigBtn = document.getElementById("id_take_sig_btn");
});
afterEach(() => {
jasmine.clock().uninstall();
});
async function clickTakeSig() {
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Flush the fetch .then() so _startWaitNoGlow() is called
await Promise.resolve();
}
it("adds .btn-cancel after the first pulse tick (600 ms)", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
});
it("sets a non-empty box-shadow after the first pulse tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.style.boxShadow).not.toBe("");
});
it("removes .btn-cancel on the second tick (even / trough)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
jasmine.clock().tick(600); // trough
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
it("clears box-shadow on the trough tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
jasmine.clock().tick(600);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("stops glow and removes .btn-cancel when WAIT NVM is clicked (unready)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // glow is on
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
// Click again → WAIT NVM → fetch unready → _stopWaitNoGlow()
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("glow does not advance after being stopped", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve(); // stop
jasmine.clock().tick(600); // would be another tick if running
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
});
}); });

View File

@@ -83,13 +83,15 @@
#id_wallet_applet_menu { @extend %applet-menu; } #id_wallet_applet_menu { @extend %applet-menu; }
#id_room_menu { @extend %applet-menu; } #id_room_menu { @extend %applet-menu; }
#id_billboard_applet_menu { @extend %applet-menu; } #id_billboard_applet_menu { @extend %applet-menu; }
#id_billscroll_menu { @extend %applet-menu; }
// Page-level gear buttons — fixed to viewport bottom-right // Page-level gear buttons — fixed to viewport bottom-right
.gameboard-page, .gameboard-page,
.dashboard-page, .dashboard-page,
.wallet-page, .wallet-page,
.room-page, .room-page,
.billboard-page { .billboard-page,
.billscroll-page {
> .gear-btn { > .gear-btn {
position: fixed; position: fixed;
bottom: 4.2rem; bottom: 4.2rem;
@@ -102,7 +104,8 @@
#id_game_applet_menu, #id_game_applet_menu,
#id_game_kit_menu, #id_game_kit_menu,
#id_wallet_applet_menu, #id_wallet_applet_menu,
#id_billboard_applet_menu { #id_billboard_applet_menu,
#id_billscroll_menu {
position: fixed; position: fixed;
bottom: 6.6rem; bottom: 6.6rem;
right: 1rem; right: 1rem;
@@ -118,7 +121,8 @@
.dashboard-page, .dashboard-page,
.wallet-page, .wallet-page,
.room-page, .room-page,
.billboard-page { .billboard-page,
.billscroll-page {
> .gear-btn { > .gear-btn {
right: 1rem; right: 1rem;
bottom: 3.95rem; // same gap above kit btn as portrait; no page-specific overrides needed bottom: 3.95rem; // same gap above kit btn as portrait; no page-specific overrides needed
@@ -131,7 +135,8 @@
#id_game_kit_menu, #id_game_kit_menu,
#id_wallet_applet_menu, #id_wallet_applet_menu,
#id_room_menu, #id_room_menu,
#id_billboard_applet_menu { #id_billboard_applet_menu,
#id_billscroll_menu {
right: 1rem; right: 1rem;
bottom: 6.6rem; bottom: 6.6rem;
top: auto; top: auto;
@@ -144,7 +149,8 @@
.dashboard-page, .dashboard-page,
.wallet-page, .wallet-page,
.room-page, .room-page,
.billboard-page { .billboard-page,
.billscroll-page {
> .gear-btn { right: 2.5rem; } > .gear-btn { right: 2.5rem; }
} }
@@ -153,7 +159,8 @@
#id_game_kit_menu, #id_game_kit_menu,
#id_wallet_applet_menu, #id_wallet_applet_menu,
#id_room_menu, #id_room_menu,
#id_billboard_applet_menu { right: 2.5rem; } #id_billboard_applet_menu,
#id_billscroll_menu { right: 2.5rem; }
} }
// ── Applet box visual shell (reusable outside the grid) ──── // ── Applet box visual shell (reusable outside the grid) ────

View File

@@ -139,6 +139,11 @@ body.page-billscroll {
.drama-event-body { .drama-event-body {
flex: 0 0 80%; flex: 0 0 80%;
&.struck {
text-decoration: line-through;
opacity: 0.5;
}
} }
.drama-event-time { .drama-event-time {

View File

@@ -288,6 +288,7 @@
&.btn-disabled { &.btn-disabled {
cursor: default !important; cursor: default !important;
pointer-events: none;
font-size: 1.2rem; font-size: 1.2rem;
padding-bottom: 0.1rem; padding-bottom: 0.1rem;
color: rgba(var(--secUser), 0.25) !important; color: rgba(var(--secUser), 0.25) !important;

View File

@@ -0,0 +1,672 @@
// ─── Card deck primitives — fan cards + sig-select overlay ─────────────────────
//
// Shared card display classes (.fan-card, .fan-card-corner, .fan-card-face, .fan-nav)
// extracted from _game-kit.scss; sig-select overlay extracted from _room.scss.
// ── Tarot fan modal ──────────────────────────────────────────────────────────
#id_tarot_fan_dialog {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
max-width: none;
max-height: none;
margin: 0;
padding: 0;
border: none;
background: rgba(0, 0, 0, 0.88);
overflow: hidden;
&::backdrop { display: none; } // Dialog IS the backdrop
}
.tarot-fan-wrap {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
perspective: 900px;
button {
box-shadow: none;
&:hover, &.active {
box-shadow: none;
}
}
}
.tarot-fan {
position: relative;
width: 220px;
height: 340px;
}
.fan-card {
position: absolute;
inset: 0;
width: 220px;
height: 340px;
border-radius: 0.75rem;
background: rgba(var(--priUser), 1);
border: 0.1rem solid rgba(var(--secUser), 0.4);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.25s ease, opacity 0.25s ease;
transform-style: preserve-3d;
&--active {
border-color: rgba(var(--secUser), 1);
box-shadow: 0 0 2rem rgba(var(--secUser), 0.3);
}
}
.fan-card-corner {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
line-height: 1;
color: rgba(var(--secUser), 0.75);
&--tl { top: 0.4rem; left: 0.4rem; }
&--br { bottom: 0.4rem; right: 0.4rem; transform: rotate(180deg); }
.fan-corner-rank {
font-size: 1.5rem;
font-weight: bold;
padding: 0.18rem 0;
}
i { font-size: 1.5rem; }
}
.fan-card-face {
padding: 1.25rem;
text-align: center;
display: flex;
flex-direction: column;
gap: 0.5rem;
.fan-card-number { font-size: 0.65rem; }
.fan-card-name-group { font-size: 0.65rem; margin: 0; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(var(--secUser), 1); }
.fan-card-name { font-size: 0.95rem; font-weight: bold; margin: 0; color: rgba(var(--terUser), 1); }
.fan-card-arcana { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.1em; color: rgba(var(--secUser), 1); }
.fan-card-correspondence { font-size: 0.6rem; font-style: italic; color: rgba(var(--secUser), 0.5); }
}
.fan-nav {
position: absolute;
z-index: 20;
font-size: 3rem;
line-height: 1;
background: none;
border: none;
color: rgba(var(--secUser), 0.6);
cursor: pointer;
padding: 1rem;
transition: color 0.15s;
pointer-events: auto;
&:hover { color: rgba(var(--secUser), 1); }
// Suppress browser focus ring on mouse/touch clicks; retain it for keyboard nav
&:focus:not(:focus-visible) { outline: none; box-shadow: none; }
&--prev { left: 1rem; }
&--next { right: 1rem; }
}
// ─── Sig Select overlay (SIG_SELECT phase) ────────────────────────────────────
//
// Two overlays (levity / gravity) run in parallel, one per polarity group.
// Layout mirrors the gatekeeper: dark Gaussian backdrop + centred modal.
// Inside the modal: upper stage (card preview) + lower mini card grid (no scroll).
html:has(.sig-backdrop) {
overflow: hidden;
}
.sig-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(5px);
z-index: 100;
pointer-events: none;
}
.sig-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: stretch;
justify-content: center;
z-index: 120;
pointer-events: none;
}
.sig-modal {
pointer-events: auto;
display: flex;
flex-direction: column;
width: 100%; // respects overlay padding-right set by JS
max-width: 420px;
max-height: 100%; // respects overlay padding-bottom set by JS
}
// ─── Stage ────────────────────────────────────────────────────────────────────
// flex: 1 — fills all space above the card grid; no background (backdrop blur).
// Row layout: preview card bottom-left, stat block fills the right.
// Card width is set by sizeSigCard() in room.js (smaller of 40% stage width or
// 80% stage height × 5/8) via --sig-card-w CSS variable — libsass can't handle
// container query units inside min().
.sig-stage {
flex: 1;
min-height: 0;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-end;
padding-left: 1.5rem;
gap: 0.75rem;
// Preview card — width driven by JS via --sig-card-w; aspect-ratio derives height.
.sig-stage-card {
flex-shrink: 0;
width: var(--sig-card-w, 120px);
height: auto;
aspect-ratio: 5 / 8;
border-radius: 0.5rem;
background: rgba(var(--priUser), 1);
border: 0.15rem solid rgba(var(--secUser), 0.6);
display: flex;
flex-direction: column;
position: relative;
padding: 0.25rem;
overflow: hidden;
// game-kit sets .fan-card-corner { position: absolute; top/left offsets }
// so these just need display/font overrides; the corners land at the card edges.
// All font-sizes scale with --sig-card-w (ratio = original-rem × 16 / 120).
.fan-card-corner--tl {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.1;
gap: 0.1rem;
.fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.133); font-weight: 700; }
i { font-size: calc(var(--sig-card-w, 120px) * 0.1); }
}
.fan-card-corner--br {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.1;
gap: 0.1rem;
.fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.12); font-weight: 700; }
i { font-size: calc(var(--sig-card-w, 120px) * 0.1); }
}
.fan-card-face {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0.25rem 0.15rem;
gap: 0.2rem;
.fan-card-name-group { font-size: calc(var(--sig-card-w, 120px) * 0.073); opacity: 0.6; }
.sig-qualifier-above,
.sig-qualifier-below { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; }
.fan-card-name { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; }
.fan-card-arcana { font-size: calc(var(--sig-card-w, 120px) * 0.067); text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; }
.fan-card-correspondence{ display: none; } // Minchiate equivalence shown in game-kit only
}
}
// Stat block — same dimensions as the preview card (width × 5:8 aspect).
// flex: 0 0 auto so it doesn't stretch to fill the stage; the rest of the
// stage row is simply empty, giving the card room to breathe.
.sig-stat-block {
flex: 0 0 auto;
width: var(--sig-card-w, 120px);
height: calc(var(--sig-card-w, 120px) * 8 / 5);
align-self: flex-end;
background: rgba(var(--priUser), 0.5);
border-radius: 0.4rem;
border: 0.1rem solid rgba(var(--terUser), 0.15);
display: none;
position: relative;
.sig-flip-btn {
position: absolute;
top: -1rem;
right: -1rem;
margin: 0;
z-index: 50;
}
.sig-caution-btn {
position: absolute;
top: 1.25rem;
right: -1rem;
margin: 0;
z-index: 50;
}
// Caution tooltip — covers the entire stat block (inset: 0), z-index above buttons.
.sig-caution-tooltip {
display: none;
position: absolute;
inset: 0;
z-index: 60;
background-color: rgba(var(--tooltip-bg), 0.6);
backdrop-filter: blur(6px);
border-radius: 0.4rem;
border: 0.1rem solid rgba(var(--priYl), 0.35);
padding: 0.75rem;
flex-direction: column;
gap: 0.4rem;
overflow-y: auto;
}
.sig-caution-header {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.sig-caution-title {
font-size: calc(var(--sig-card-w, 120px) * 0.093);
font-weight: 700;
margin: 0;
color: rgba(var(--priYl), 1);
}
.sig-caution-type {
font-size: calc(var(--sig-card-w, 120px) * 0.058);
opacity: 0.7;
text-transform: uppercase;
letter-spacing: 0.05em;
flex-shrink: 0;
}
.sig-caution-shoptalk {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
opacity: 0.55;
margin: 0;
font-style: italic;
}
.sig-caution-effect {
flex: 1;
font-size: calc(var(--sig-card-w, 120px) * 0.075);
margin: 0;
line-height: 1.55;
.card-ref {
color: rgba(var(--terUser), 1);
font-weight: 600;
}
}
.sig-caution-index {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
opacity: 0.55;
}
// Nav arrows portaled out of tooltip — sit at bottom corners above tooltip (z-70)
.sig-caution-prev,
.sig-caution-next {
display: none;
position: absolute;
bottom: -1rem;
margin: 0;
z-index: 70;
}
.sig-caution-prev { left: -1rem; }
.sig-caution-next { right: -1rem; }
.stat-face {
display: none;
padding: calc(var(--sig-card-w, 120px) * 0.37) calc(var(--sig-card-w, 120px) * 0.1) calc(var(--sig-card-w, 120px) * 0.08);
&--upright { display: block; }
}
&.is-reversed {
.stat-face--upright { display: none; }
.stat-face--reversed { display: block; }
}
.stat-face-label {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
text-transform: uppercase;
letter-spacing: 0.09em;
opacity: 0.4;
margin: 0 0 calc(var(--sig-card-w, 120px) * 0.07);
}
.stat-keywords {
list-style: none;
padding: 0;
margin: 0;
li {
font-size: calc(var(--sig-card-w, 120px) * 0.083);
padding: calc(var(--sig-card-w, 120px) * 0.042) 0;
opacity: 0.85;
border-bottom: 0.05rem solid rgba(var(--terUser), 0.12);
&:last-child { border-bottom: none; }
}
}
}
&.sig-stage--frozen .sig-stat-block { display: block; }
&.sig-caution-open .sig-stat-block {
.sig-caution-tooltip { display: flex; }
.sig-caution-prev, .sig-caution-next { display: inline-flex; }
}
}
// ─── Mini card grid ───────────────────────────────────────────────────────────
// flex: 0 0 auto — shrinks to card content; no background (backdrop blur).
// align-content: start prevents CSS grid from distributing extra height between rows.
.sig-deck-grid {
flex: 0 0 auto;
display: grid;
grid-template-columns: repeat(6, 1fr);
align-content: start;
gap: 2px;
padding: 4px;
overflow: hidden;
margin: 0 1rem 5rem 4rem;
}
.sig-card {
aspect-ratio: 5 / 8;
border-radius: 0.4rem;
background: rgba(var(--priUser), 0.97);
border: 1px solid rgba(var(--secUser), 0.3);
position: relative;
cursor: grab;
transition: border-color 0.15s, box-shadow 0.15s;
overflow: hidden;
// game-kit sets .fan-card-corner { position:absolute; top:0.4rem; left:0.4rem }
// Override: center the element within the card instead.
.fan-card-corner--tl {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
gap: 0; // game-kit has gap:0.15rem — too large at 0.5rem font-size
.fan-corner-rank { font-size: 1rem; font-weight: 700; }
i { font-size: 0.75rem; }
}
// OK / NVM overlay — appears on click (focused) or own reservation
.sig-card-actions {
position: absolute;
inset: 0;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
background: rgba(var(--priUser), 0.92);
border-radius: inherit;
.sig-nvm-btn { display: none; }
}
&.sig-focused .sig-card-actions { display: flex; }
&.sig-reserved--own .sig-card-actions {
display: flex;
.sig-ok-btn { display: none; }
.sig-nvm-btn { display: flex; }
}
// Cursor strip — hangs below the card bottom edge; overflow: visible allows this.
.sig-card-cursors {
position: absolute;
bottom: -0.6rem;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
padding: 0 2px;
}
// Rise above DOM-order siblings when a peer's cursor is active on this card.
// Without this, later cards in the grid paint over the overflowing cursor icons.
&:has(.sig-cursor.active) { z-index: 5; }
&:hover:not([data-reserved-by]) {
border-color: rgba(var(--secUser), 0.8);
box-shadow: 0 0 4px rgba(var(--secUser), 0.25);
}
&.sig-reserved {
cursor: not-allowed;
}
// Role-coloured reservation glow — border/shadow matches the reserving gamer's role.
// data-reserved-by is set by applyReservation() in sig-select.js.
// Own reservation also shows role colour (same as peers see), not a separate style.
&.sig-reserved {
&[data-reserved-by="PC"] { border-color: rgba(var(--priRd), 1); box-shadow: 0 0 0 2px rgba(var(--priRd), 1); }
&[data-reserved-by="NC"] { border-color: rgba(var(--priYl), 1); box-shadow: 0 0 0 2px rgba(var(--priYl), 1); }
&[data-reserved-by="EC"] { border-color: rgba(var(--priGn), 1); box-shadow: 0 0 0 2px rgba(var(--priGn), 1); }
&[data-reserved-by="SC"] { border-color: rgba(var(--priCy), 1); box-shadow: 0 0 0 2px rgba(var(--priCy), 1); }
&[data-reserved-by="AC"] { border-color: rgba(var(--priId), 1); box-shadow: 0 0 0 2px rgba(var(--priId), 1); }
&[data-reserved-by="BC"] { border-color: rgba(var(--priFs), 1); box-shadow: 0 0 0 2px rgba(var(--priFs), 1); }
}
&.sig-reserved--own {
cursor: grabbing;
}
}
// ─── Cursor anchors ───────────────────────────────────────────────────────────
//
// Three tiny dots along the bottom of each mini card, one per role in the group.
// Inactive: invisible. Active (another gamer is hovering): role-coloured dot.
// Position order is fixed per polarity (POLARITY_ROLES in sig-select.js):
// levity (PC / NC / SC) → left / mid / right
// gravity (BC / EC / AC) → left / mid / right
// In-card cursor elements — invisible anchors only.
// Visible icons are portaled to document root by applyHover() in sig-select.js.
.sig-cursor {
display: block;
font-size: 0; // zero-size: no layout impact, just carries .active class
color: transparent;
pointer-events: none;
}
// ─── Floating cursor portal ───────────────────────────────────────────────────
//
// sig-select.js creates these <i> elements inside #id_sig_cursor_portal, a
// position:fixed root-level container, so they escape all overflow/clip contexts.
// Positioned via getBoundingClientRect() on the card element.
#id_sig_cursor_portal {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 200; // above sig-overlay (120), below tray (310)
overflow: visible;
}
.sig-cursor-float {
position: absolute;
font-size: 1.5rem;
line-height: 1;
transform: translateX(-50%); // centre on the x coordinate from JS
pointer-events: none;
}
// Role-specific colour + outline shadow + ninUser glow
.sig-cursor-float[data-role="PC"] {
color: rgba(var(--priRd), 1);
text-shadow: 2px 0 0 rgba(var(--priOr),1), -2px 0 0 rgba(var(--priOr),1),
0 2px 0 rgba(var(--priOr),1), 0 -2px 0 rgba(var(--priOr),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="NC"] {
color: rgba(var(--priYl), 1);
text-shadow: 2px 0 0 rgba(var(--priLm),1), -2px 0 0 rgba(var(--priLm),1),
0 2px 0 rgba(var(--priLm),1), 0 -2px 0 rgba(var(--priLm),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="EC"] {
color: rgba(var(--priGn), 1);
text-shadow: 2px 0 0 rgba(var(--priTk),1), -2px 0 0 rgba(var(--priTk),1),
0 2px 0 rgba(var(--priTk),1), 0 -2px 0 rgba(var(--priTk),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="SC"] {
color: rgba(var(--priCy), 1);
text-shadow: 2px 0 0 rgba(var(--priBl),1), -2px 0 0 rgba(var(--priBl),1),
0 2px 0 rgba(var(--priBl),1), 0 -2px 0 rgba(var(--priBl),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="AC"] {
color: rgba(var(--priId), 1);
text-shadow: 2px 0 0 rgba(var(--priVt),1), -2px 0 0 rgba(var(--priVt),1),
0 2px 0 rgba(var(--priVt),1), 0 -2px 0 rgba(var(--priVt),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="BC"] {
color: rgba(var(--priFs), 1);
text-shadow: 2px 0 0 rgba(var(--priMe),1), -2px 0 0 rgba(var(--priMe),1),
0 2px 0 rgba(var(--priMe),1), 0 -2px 0 rgba(var(--priMe),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
// ─── Polarity theming — card colour inversion ────────────────────────────────
//
// Gravity (Graven): --priUser bg / --secUser text — standard dark palette.
// Levity (Leavened): --secUser bg / --priUser text — inverted, lighter feel.
// Both mini-cards and the stage preview card follow the same rule.
.sig-overlay[data-polarity="levity"] {
// Mini card: inverted palette. game-kit sets explicit colours on .fan-card-name
// and .fan-card-corner that out-specifc the parent color, so re-target them here.
.sig-card {
background: rgba(var(--secUser), 0.97);
border-color: rgba(var(--priUser), 0.3);
color: rgba(var(--priUser), 1);
.fan-card-corner { color: rgba(var(--priUser), 0.75); }
.fan-card-name { color: rgba(var(--quiUser), 1); }
// OK / NVM overlay — must match the inverted card background
.sig-card-actions { background: rgba(var(--secUser), 0.92); }
}
// Stage preview card: same inversion + title colour.
// .fan-card-name-group and .fan-card-arcana have explicit color in the base
// .fan-card-face rule (specificity 0,2,0) — must re-target them here (0,3,0).
// Opacity dim is still applied by the nested sig-stage-card rule.
.sig-stage-card {
background: rgba(var(--secUser), 1);
border-color: rgba(var(--priUser), 0.6);
color: rgba(var(--priUser), 1);
.fan-card-corner { color: rgba(var(--priUser), 0.75); }
.fan-card-name-group{ color: rgba(var(--priUser), 1); }
.fan-card-name { color: rgba(var(--quiUser), 1); }
.fan-card-arcana { color: rgba(var(--priUser), 1); }
}
// Polarity qualifier: same colour as the card title in this context
.sig-qualifier-above,
.sig-qualifier-below { color: rgba(var(--quiUser), 1); }
// card-ref spans inside the caution tooltip — must match the base rule's
// .sig-stat-block .sig-caution-effect .card-ref specificity (0,3,0) to win.
.sig-caution-effect .card-ref { color: rgba(var(--quiUser), 1); }
// Cursor colours live in .sig-cursor-float[data-role] rules (portal elements)
}
.sig-overlay[data-polarity="gravity"] {
// Stat block: invert priUser/secUser so gravity gets the same stark contrast as leavened cards
.sig-stat-block {
background: rgba(var(--secUser), 0.75);
color: rgba(var(--priUser), 1);
border-color: rgba(var(--priUser), 0.15);
}
// Caution tooltip: --tooltip-bg is black so priUser text (dark) would be invisible —
// override to secUser (light) so body text reads against the dark backdrop.
.sig-caution-tooltip { color: rgba(var(--secUser), 1); }
// Polarity qualifier: terUser for gravity (quiUser is levity's equivalent)
.sig-qualifier-above,
.sig-qualifier-below { color: rgba(var(--terUser), 1); }
// Cursor colours live in .sig-cursor-float[data-role] rules (portal elements)
}
// ─── Sig select: landscape overrides ─────────────────────────────────────────
// Landscape base: 9×2 grid of 3rem cards. At ≥992px (wide enough for 18 cards
// at 3rem + 4rem left + ~4rem right): collapse to a single 18×1 row so the
// stage preview gets maximum vertical real-estate.
// padding-left clears the fixed left navbar (JS sets right/bottom but not left).
// Grid margins reset to 0 — overlay padding handles all edge clearance.
@media (orientation: landscape) {
.sig-modal {
max-width: none;
flex-direction: row; // grid to the right, stage + card preview to the left
margin-left: 4rem;
margin-right: 3rem;
}
.sig-stage {
min-width: 0; // allow shrinking in row layout; align-items:flex-end already set
}
.sig-deck-grid {
grid-template-columns: repeat(6, 2.5rem);
margin: 0;
align-self: flex-end; // sit at the bottom of the modal row
}
}
@media (orientation: landscape) and (min-width: 900px) {
// Wide landscape: revert to stacked layout (stage top, 18-card row grid bottom).
.sig-modal {
flex-direction: column;
align-items: stretch;
}
.sig-stage {
min-width: auto;
align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth
margin-left: 3rem;
}
.sig-deck-grid {
grid-template-columns: repeat(18, 3rem);
align-self: center;
}
}
@media (orientation: landscape) and (min-width: 1800px) {
// Sig overlay: clear doubled sidebars (8rem each instead of 4rem/6rem)
.sig-overlay { padding-left: 8rem; padding-right: 8rem; }
.sig-stage {
align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth
margin-left: 3rem;
}
.sig-deck-grid {
grid-template-columns: repeat(18, 5rem);
align-self: center;
}
// Room menu: base right: 0.5rem (same-specificity ID rule) overrides _applets.scss
// XL block because _card-deck.scss is imported after _applets.scss. Re-declare here to win the cascade.
#id_room_menu { right: 2.5rem; }
}

View File

@@ -212,118 +212,3 @@
opacity: 0.45; opacity: 0.45;
} }
// ── Tarot fan modal ──────────────────────────────────────────────────────────
#id_tarot_fan_dialog {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
max-width: none;
max-height: none;
margin: 0;
padding: 0;
border: none;
background: rgba(0, 0, 0, 0.88);
overflow: hidden;
&::backdrop { display: none; } // Dialog IS the backdrop
}
.tarot-fan-wrap {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
perspective: 900px;
button {
box-shadow: none;
&:hover, &.active {
box-shadow: none;
}
}
}
.tarot-fan {
position: relative;
width: 220px;
height: 340px;
}
.fan-card {
position: absolute;
inset: 0;
width: 220px;
height: 340px;
border-radius: 0.75rem;
background: rgba(var(--priUser), 1);
border: 0.1rem solid rgba(var(--secUser), 0.4);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.25s ease, opacity 0.25s ease;
transform-style: preserve-3d;
&--active {
border-color: rgba(var(--secUser), 1);
box-shadow: 0 0 2rem rgba(var(--secUser), 0.3);
}
}
.fan-card-corner {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
line-height: 1;
color: rgba(var(--secUser), 0.75);
&--tl { top: 0.4rem; left: 0.4rem; }
&--br { bottom: 0.4rem; right: 0.4rem; transform: rotate(180deg); }
.fan-corner-rank {
font-size: 1.5rem;
font-weight: bold;
padding: 0.18rem 0;
}
i { font-size: 1.5rem; }
}
.fan-card-face {
padding: 1.25rem;
text-align: center;
display: flex;
flex-direction: column;
gap: 0.5rem;
.fan-card-number { font-size: 0.65rem; }
.fan-card-name-group { font-size: 0.65rem; margin: 0; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(var(--secUser), 1); }
.fan-card-name { font-size: 0.95rem; font-weight: bold; margin: 0; color: rgba(var(--terUser), 1); }
.fan-card-arcana { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.1em; color: rgba(var(--secUser), 1); }
.fan-card-correspondence { font-size: 0.6rem; font-style: italic; color: rgba(var(--secUser), 0.5); }
}
.fan-nav {
position: absolute;
z-index: 20;
font-size: 3rem;
line-height: 1;
background: none;
border: none;
color: rgba(var(--secUser), 0.6);
cursor: pointer;
padding: 1rem;
transition: color 0.15s;
pointer-events: auto;
&:hover { color: rgba(var(--secUser), 1); }
// Suppress browser focus ring on mouse/touch clicks; retain it for keyboard nav
&:focus:not(:focus-visible) { outline: none; box-shadow: none; }
&--prev { left: 1rem; }
&--next { right: 1rem; }
}

View File

@@ -546,10 +546,28 @@ html:has(.gate-backdrop) .position-strip .gate-slot button { pointer-events: aut
.table-center { .table-center {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
// "Gravity settling . . ." / "Levity appraising . . ." shown after a polarity
// group confirms their sigs while the other group is still selecting.
// Pulsing opacity signals active waiting without being jarring.
#id_hex_waiting_msg {
font-size: 0.7rem;
letter-spacing: 0.06em;
color: rgba(var(--terUser), 0.8);
text-align: center;
margin: 0.4rem 0 0;
animation: hex-wait-pulse 2.4s ease-in-out infinite;
}
@keyframes hex-wait-pulse {
0%, 100% { opacity: 0.75; }
50% { opacity: 0.3; }
}
.table-seat { .table-seat {
position: absolute; position: absolute;
display: grid; display: grid;
@@ -801,11 +819,18 @@ $card-h: 60px;
// Landscape mobile — aggressively scale down to fit short viewport // Landscape mobile — aggressively scale down to fit short viewport
@media (orientation: landscape) { @media (orientation: landscape) {
// Sink navbar below gate/role-select overlays when a modal is open. // Sink navbar + footer sidebar below any modal backdrop when open.
// Landscape navbar z-index is 100 (_base.scss); gate-backdrop/overlay are // Landscape navbar and footer sidebar are both z-index:100 (_base.scss).
// 100/120 — same level causes paint-order ties so we drop it to 50. // Gate/role-select/sig backdrops are also z-index:100 — DOM paint-order ties
// let the footer (later in DOM) bleed through. Drop both to 50.
html:has(.gate-backdrop) body .container .navbar, html:has(.gate-backdrop) body .container .navbar,
html:has(.role-select-backdrop) body .container .navbar { html:has(.role-select-backdrop) body .container .navbar,
html:has(.sig-backdrop) body .container .navbar {
z-index: 50;
}
html:has(.gate-backdrop) body #id_footer,
html:has(.role-select-backdrop) body #id_footer,
html:has(.sig-backdrop) body #id_footer {
z-index: 50; z-index: 50;
} }
@@ -832,431 +857,4 @@ $card-h: 60px;
} }
// ─── Sig Select overlay (SIG_SELECT phase) ────────────────────────────────────
//
// Two overlays (levity / gravity) run in parallel, one per polarity group.
// Layout mirrors the gatekeeper: dark Gaussian backdrop + centred modal.
// Inside the modal: upper stage (card preview) + lower mini card grid (no scroll).
html:has(.sig-backdrop) {
overflow: hidden;
}
.sig-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(5px);
z-index: 100;
pointer-events: none;
}
.sig-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: stretch;
justify-content: center;
z-index: 120;
pointer-events: none;
}
.sig-modal {
pointer-events: auto;
display: flex;
flex-direction: column;
width: 100%; // respects overlay padding-right set by JS
max-width: 420px;
max-height: 100%; // respects overlay padding-bottom set by JS
}
// ─── Stage ────────────────────────────────────────────────────────────────────
// flex: 1 — fills all space above the card grid; no background (backdrop blur).
// Row layout: preview card bottom-left, stat block fills the right.
// Card width is set by sizeSigCard() in room.js (smaller of 40% stage width or
// 80% stage height × 5/8) via --sig-card-w CSS variable — libsass can't handle
// container query units inside min().
.sig-stage {
flex: 1;
min-height: 0;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-end;
padding-left: 1.5rem;
gap: 0.75rem;
// Preview card — width driven by JS via --sig-card-w; aspect-ratio derives height.
.sig-stage-card {
flex-shrink: 0;
width: var(--sig-card-w, 120px);
height: auto;
aspect-ratio: 5 / 8;
border-radius: 0.5rem;
background: rgba(var(--priUser), 1);
border: 0.15rem solid rgba(var(--secUser), 0.6);
display: flex;
flex-direction: column;
position: relative;
padding: 0.25rem;
overflow: hidden;
// game-kit sets .fan-card-corner { position: absolute; top/left offsets }
// so these just need display/font overrides; the corners land at the card edges.
// All font-sizes scale with --sig-card-w (ratio = original-rem × 16 / 120).
.fan-card-corner--tl {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.1;
gap: 0.1rem;
.fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.133); font-weight: 700; }
i { font-size: calc(var(--sig-card-w, 120px) * 0.1); }
}
.fan-card-corner--br {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.1;
gap: 0.1rem;
.fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.12); font-weight: 700; }
i { font-size: calc(var(--sig-card-w, 120px) * 0.1); }
}
.fan-card-face {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0.25rem 0.15rem;
gap: 0.2rem;
.fan-card-name-group { font-size: calc(var(--sig-card-w, 120px) * 0.073); opacity: 0.6; }
.fan-card-name { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; }
.fan-card-arcana { font-size: calc(var(--sig-card-w, 120px) * 0.067); text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; }
.fan-card-correspondence{ font-size: calc(var(--sig-card-w, 120px) * 0.067); opacity: 0.5; }
}
}
// Stat block — same dimensions as the preview card (width × 5:8 aspect).
// flex: 0 0 auto so it doesn't stretch to fill the stage; the rest of the
// stage row is simply empty, giving the card room to breathe.
.sig-stat-block {
flex: 0 0 auto;
width: var(--sig-card-w, 120px);
height: calc(var(--sig-card-w, 120px) * 8 / 5);
align-self: flex-end;
background: rgba(var(--priUser), 0.5);
border-radius: 0.4rem;
border: 0.1rem solid rgba(var(--terUser), 0.15);
display: none;
position: relative;
.sig-flip-btn {
position: absolute;
top: -1rem;
right: -1rem;
margin: 0;
z-index: 50;
}
.sig-caution-btn {
position: absolute;
top: 1.25rem;
right: -1rem;
margin: 0;
z-index: 50;
}
// Caution tooltip — covers the entire stat block (inset: 0), z-index above buttons.
.sig-caution-tooltip {
display: none;
position: absolute;
inset: 0;
z-index: 60;
background-color: rgba(var(--tooltip-bg), 0.6);
backdrop-filter: blur(6px);
border-radius: 0.4rem;
border: 0.1rem solid rgba(var(--priYl), 0.35);
padding: 0.75rem;
flex-direction: column;
gap: 0.4rem;
overflow-y: auto;
}
.sig-caution-header {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.sig-caution-title {
font-size: calc(var(--sig-card-w, 120px) * 0.093);
font-weight: 700;
margin: 0;
color: rgba(var(--priYl), 1);
}
.sig-caution-type {
font-size: calc(var(--sig-card-w, 120px) * 0.058);
opacity: 0.7;
text-transform: uppercase;
letter-spacing: 0.05em;
flex-shrink: 0;
}
.sig-caution-shoptalk {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
opacity: 0.55;
margin: 0;
font-style: italic;
}
.sig-caution-effect {
flex: 1;
font-size: calc(var(--sig-card-w, 120px) * 0.075);
margin: 0;
line-height: 1.55;
.card-ref {
color: rgba(var(--terUser), 1);
font-weight: 600;
}
}
.sig-caution-index {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
opacity: 0.55;
}
// Nav arrows portaled out of tooltip — sit at bottom corners above tooltip (z-70)
.sig-caution-prev,
.sig-caution-next {
display: none;
position: absolute;
bottom: -1rem;
margin: 0;
z-index: 70;
}
.sig-caution-prev { left: -1rem; }
.sig-caution-next { right: -1rem; }
.stat-face {
display: none;
padding: calc(var(--sig-card-w, 120px) * 0.37) calc(var(--sig-card-w, 120px) * 0.1) calc(var(--sig-card-w, 120px) * 0.08);
&--upright { display: block; }
}
&.is-reversed {
.stat-face--upright { display: none; }
.stat-face--reversed { display: block; }
}
.stat-face-label {
font-size: calc(var(--sig-card-w, 120px) * 0.063);
text-transform: uppercase;
letter-spacing: 0.09em;
opacity: 0.4;
margin: 0 0 calc(var(--sig-card-w, 120px) * 0.07);
}
.stat-keywords {
list-style: none;
padding: 0;
margin: 0;
li {
font-size: calc(var(--sig-card-w, 120px) * 0.083);
padding: calc(var(--sig-card-w, 120px) * 0.042) 0;
opacity: 0.85;
border-bottom: 0.05rem solid rgba(var(--terUser), 0.12);
&:last-child { border-bottom: none; }
}
}
}
&.sig-stage--frozen .sig-stat-block { display: block; }
&.sig-caution-open .sig-stat-block {
.sig-caution-tooltip { display: flex; }
.sig-caution-prev, .sig-caution-next { display: inline-flex; }
}
}
// ─── Mini card grid ───────────────────────────────────────────────────────────
// flex: 0 0 auto — shrinks to card content; no background (backdrop blur).
// align-content: start prevents CSS grid from distributing extra height between rows.
.sig-deck-grid {
flex: 0 0 auto;
display: grid;
grid-template-columns: repeat(6, 1fr);
align-content: start;
gap: 2px;
padding: 4px;
overflow: hidden;
margin: 0 1rem 5rem 4rem;
}
.sig-card {
aspect-ratio: 5 / 8;
border-radius: 0.4rem;
background: rgba(var(--priUser), 0.97);
border: 1px solid rgba(var(--secUser), 0.3);
position: relative;
cursor: grab;
transition: border-color 0.15s, box-shadow 0.15s;
overflow: hidden;
// game-kit sets .fan-card-corner { position:absolute; top:0.4rem; left:0.4rem }
// Override: center the element within the card instead.
.fan-card-corner--tl {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
gap: 0; // game-kit has gap:0.15rem — too large at 0.5rem font-size
.fan-corner-rank { font-size: 1rem; font-weight: 700; }
i { font-size: 0.75rem; }
}
// OK / NVM overlay — appears on click (focused) or own reservation
.sig-card-actions {
position: absolute;
inset: 0;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
background: rgba(var(--priUser), 0.92);
border-radius: inherit;
.sig-nvm-btn { display: none; }
}
&.sig-focused .sig-card-actions { display: flex; }
&.sig-reserved--own .sig-card-actions {
display: flex;
.sig-ok-btn { display: none; }
.sig-nvm-btn { display: flex; }
}
// Cursor anchors strip — bottom of card
.sig-card-cursors {
position: absolute;
bottom: 2px;
left: 2px;
right: 2px;
display: flex;
justify-content: space-between;
}
&:hover:not([data-reserved-by]) {
border-color: rgba(var(--secUser), 0.8);
box-shadow: 0 0 4px rgba(var(--secUser), 0.25);
}
&.sig-reserved {
border-color: rgba(var(--terUser), 1);
box-shadow:
0 0 0.4rem rgba(var(--terUser), 0.7),
0 0 1rem rgba(var(--ninUser), 0.4);
cursor: not-allowed;
}
&.sig-reserved--own {
border-color: rgba(var(--secUser), 1);
box-shadow:
0 0 0.4rem rgba(var(--secUser), 0.7),
0 0 1rem rgba(var(--ninUser), 0.5);
cursor: grabbing;
}
}
// ─── Cursor anchors ───────────────────────────────────────────────────────────
//
// Three tiny dots along the bottom of each mini card, one per role in the group.
// Inactive: invisible. Active (another gamer is hovering): coloured dot.
.sig-cursor {
display: block;
width: 5px;
height: 5px;
border-radius: 50%;
background: transparent;
transition: background 0.1s;
&.active {
background: rgba(var(--terUser), 1);
box-shadow: 0 0 3px rgba(var(--ninUser), 0.8);
}
}
// ─── Sig select: landscape overrides ─────────────────────────────────────────
// Landscape base: 9×2 grid of 3rem cards. At ≥992px (wide enough for 18 cards
// at 3rem + 4rem left + ~4rem right): collapse to a single 18×1 row so the
// stage preview gets maximum vertical real-estate.
// padding-left clears the fixed left navbar (JS sets right/bottom but not left).
// Grid margins reset to 0 — overlay padding handles all edge clearance.
@media (orientation: landscape) {
.sig-modal {
max-width: none;
flex-direction: row; // grid to the right, stage + card preview to the left
margin-left: 4rem;
margin-right: 3rem;
}
.sig-stage {
min-width: 0; // allow shrinking in row layout; align-items:flex-end already set
}
.sig-deck-grid {
grid-template-columns: repeat(6, 2.5rem);
margin: 0;
align-self: flex-end; // sit at the bottom of the modal row
}
}
@media (orientation: landscape) and (min-width: 900px) {
// Wide landscape: revert to stacked layout (stage top, 18-card row grid bottom).
.sig-modal {
flex-direction: column;
align-items: stretch;
}
.sig-stage {
min-width: auto;
align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth
margin-left: 3rem;
}
.sig-deck-grid {
grid-template-columns: repeat(18, 3rem);
align-self: center;
}
}
@media (orientation: landscape) and (min-width: 1800px) {
// Sig overlay: clear doubled sidebars (8rem each instead of 4rem/6rem)
.sig-overlay { padding-left: 8rem; padding-right: 8rem; }
.sig-stage {
align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth
margin-left: 3rem;
}
.sig-deck-grid {
grid-template-columns: repeat(18, 5rem);
align-self: center;
}
// Room menu: base right: 0.5rem (same-specificity ID rule) overrides _applets.scss
// XL block because _room.scss is imported later. Re-declare here to win the cascade.
#id_room_menu { right: 2.5rem; }
}
// ─── Seat tray — see _tray.scss ───────────────────────────────────────────── // ─── Seat tray — see _tray.scss ─────────────────────────────────────────────

View File

@@ -6,6 +6,7 @@
@import 'gameboard'; @import 'gameboard';
@import 'palette-picker'; @import 'palette-picker';
@import 'room'; @import 'room';
@import 'card-deck';
@import 'tray'; @import 'tray';
@import 'billboard'; @import 'billboard';
@import 'game-kit'; @import 'game-kit';

View File

@@ -1,13 +1,15 @@
describe("SigSelect", () => { describe("SigSelect", () => {
let testDiv, stageCard, card, statBlock; let testDiv, stageCard, card, statBlock;
function makeFixture({ reservations = '{}', cardCautions = '[]' } = {}) { function makeFixture({ reservations = '{}', cardCautions = '[]', polarity = 'levity', userRole = 'PC' } = {}) {
testDiv = document.createElement("div"); testDiv = document.createElement("div");
testDiv.innerHTML = ` testDiv.innerHTML = `
<div class="sig-overlay" <div class="sig-overlay"
data-polarity="levity" data-polarity="${polarity}"
data-user-role="PC" data-user-role="${userRole}"
data-reserve-url="/epic/room/test/sig-reserve" data-reserve-url="/epic/room/test/sig-reserve"
data-ready-url="/epic/room/test/sig-ready"
data-reservations="${reservations.replace(/"/g, '&quot;')}"> data-reservations="${reservations.replace(/"/g, '&quot;')}">
<div class="sig-modal"> <div class="sig-modal">
<div class="sig-stage"> <div class="sig-stage">
@@ -15,7 +17,9 @@ describe("SigSelect", () => {
<span class="fan-corner-rank"></span> <span class="fan-corner-rank"></span>
<i class="stage-suit-icon"></i> <i class="stage-suit-icon"></i>
<p class="fan-card-name-group"></p> <p class="fan-card-name-group"></p>
<p class="sig-qualifier-above"></p>
<h3 class="fan-card-name"></h3> <h3 class="fan-card-name"></h3>
<p class="sig-qualifier-below"></p>
<p class="fan-card-arcana"></p> <p class="fan-card-arcana"></p>
<p class="fan-card-correspondence"></p> <p class="fan-card-correspondence"></p>
</div> </div>
@@ -409,4 +413,273 @@ describe("SigSelect", () => {
expect(statBlock.querySelectorAll("#id_stat_keywords_reversed li").length).toBe(0); expect(statBlock.querySelectorAll("#id_stat_keywords_reversed li").length).toBe(0);
}); });
}); });
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
//
// Fixture polarity = levity, userRole = PC.
// POLARITY_ROLES: levity → [PC, NC, SC] = [left, mid, right]
//
// Only tests the JS position mapping — colour is CSS-only.
describe("WS cursor hover", () => {
beforeEach(() => makeFixture());
it("NC hover activates the --mid cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
expect(card.querySelector(".sig-cursor--mid").classList.contains("active")).toBe(true);
});
it("SC hover activates the --right cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "SC", active: true },
}));
expect(card.querySelector(".sig-cursor--right").classList.contains("active")).toBe(true);
});
it("own role (PC) hover event is ignored — no cursor activates", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "PC", active: true },
}));
expect(card.querySelectorAll(".sig-cursor.active").length).toBe(0);
});
it("hover-off removes .active from the cursor", () => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: false },
}));
expect(card.querySelector(".sig-cursor--mid").classList.contains("active")).toBe(false);
});
it("hover on unknown card_id is a no-op", () => {
expect(() => {
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 9999, role: "NC", active: true },
}));
}).not.toThrow();
});
});
// ── WS reservation — data-reserved-by attribute ───────────────────────── //
//
// applyReservation() sets data-reserved-by so the CSS can glow the card in
// the reserving gamer's role colour. These tests assert the attribute, not
// the colour (CSS variables aren't resolvable in the SpecRunner context).
describe("WS reservation sets data-reserved-by", () => {
beforeEach(() => makeFixture());
it("peer reservation sets data-reserved-by to the reserving role", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(card.dataset.reservedBy).toBe("NC");
});
it("peer reservation also adds .sig-reserved class", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(card.classList.contains("sig-reserved")).toBe(true);
});
it("release removes data-reserved-by", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: false },
}));
expect(card.dataset.reservedBy).toBeUndefined();
});
it("own reservation (PC) sets data-reserved-by AND .sig-reserved--own", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "PC", reserved: true },
}));
expect(card.dataset.reservedBy).toBe("PC");
expect(card.classList.contains("sig-reserved--own")).toBe(true);
});
it("peer reservation places a thumbs-up float and removes any hand-pointer float", () => {
// First, a hover float exists for NC (mid cursor)
window.dispatchEvent(new CustomEvent("room:sig_hover", {
detail: { card_id: 42, role: "NC", active: true },
}));
expect(document.querySelector('.sig-cursor-float[data-role="NC"]')).not.toBeNull();
expect(document.querySelector('.fa-hand-pointer[data-role="NC"]')).not.toBeNull();
// NC then clicks OK — reservation arrives
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
// Thumbs-up replaces hand-pointer
const floatEl = document.querySelector('.sig-cursor-float[data-role="NC"]');
expect(floatEl).not.toBeNull();
expect(floatEl.classList.contains("fa-thumbs-up")).toBe(true);
expect(floatEl.classList.contains("fa-hand-pointer")).toBe(false);
});
it("peer release removes the thumbs-up float", () => {
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: true },
}));
expect(document.querySelector('.sig-cursor-float--reserved[data-role="NC"]')).not.toBeNull();
window.dispatchEvent(new CustomEvent("room:sig_reserved", {
detail: { card_id: 42, role: "NC", reserved: false },
}));
expect(document.querySelector('.sig-cursor-float--reserved[data-role="NC"]')).toBeNull();
});
});
// ── Polarity theming — stage qualifier text ────────────────────────────── //
//
// On mouseenter, updateStage() injects "Leavened" or "Graven" into the
// sig-qualifier-above (non-major) or sig-qualifier-below (major arcana) slot.
// Correspondence field is never populated in sig-select context.
describe("polarity theming — stage qualifier", () => {
it("levity non-major card puts 'Leavened' in qualifier-above, qualifier-below empty", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
// data-arcana defaults to "Minor Arcana" in fixture → non-major
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("Leavened");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("");
});
it("levity major arcana card puts 'Leavened' in qualifier-below, qualifier-above empty", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Leavened");
});
it("major arcana title gets a trailing comma (qualifier reads as subtitle)", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.arcana = "Major Arcana";
card.dataset.nameTitle = "The Schizo";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-name").textContent).toBe("The Schizo,");
});
it("non-major arcana title has no trailing comma", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
// fixture default: Minor Arcana, "King of Pentacles"
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-name").textContent).toBe("King of Pentacles");
});
it("gravity non-major card puts 'Graven' in qualifier-above", () => {
makeFixture({ polarity: 'gravity', userRole: 'BC' });
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("Graven");
});
it("gravity major arcana card puts 'Graven' in qualifier-below", () => {
makeFixture({ polarity: 'gravity', userRole: 'BC' });
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Graven");
});
it("hovering clears qualifier slots from the previous card", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
card.dataset.arcana = "Major Arcana";
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
// Now major — above should be empty, below filled
expect(testDiv.querySelector(".sig-qualifier-above").textContent).toBe("");
expect(testDiv.querySelector(".sig-qualifier-below").textContent).toBe("Leavened");
});
it("correspondence field is never populated", () => {
makeFixture({ polarity: 'levity', userRole: 'PC' });
card.dataset.correspondence = "Il Bagatto (Minchiate)";
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
expect(testDiv.querySelector(".fan-card-correspondence").textContent).toBe("");
});
});
// ── WAIT NVM glow pulse ────────────────────────────────────────────────────── //
//
// After clicking TAKE SIG (POST ok → isReady=true) a setInterval pulses the
// button at 600ms: odd ticks add .btn-cancel + a --terOr outer box-shadow;
// even ticks remove both. Uses jasmine.clock() to advance the fake timer.
describe("WAIT NVM glow pulse", () => {
let takeSigBtn;
beforeEach(() => {
jasmine.clock().install();
// Pre-reserve card 42 as PC so _showTakeSigBtn() fires during init
makeFixture({ reservations: '{"42":"PC"}' });
takeSigBtn = document.getElementById("id_take_sig_btn");
});
afterEach(() => {
jasmine.clock().uninstall();
});
async function clickTakeSig() {
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Flush the fetch .then() so _startWaitNoGlow() is called
await Promise.resolve();
}
it("adds .btn-cancel after the first pulse tick (600 ms)", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
});
it("sets a non-empty box-shadow after the first pulse tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.style.boxShadow).not.toBe("");
});
it("removes .btn-cancel on the second tick (even / trough)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
jasmine.clock().tick(600); // trough
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
it("clears box-shadow on the trough tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
jasmine.clock().tick(600);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("stops glow and removes .btn-cancel when WAIT NVM is clicked (unready)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // glow is on
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
// Click again → WAIT NVM → fetch unready → _stopWaitNoGlow()
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("glow does not advance after being stopped", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve(); // stop
jasmine.clock().tick(600); // would be another tick if running
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
});
}); });

View File

@@ -10,9 +10,9 @@
<a href="{% url 'billboard:scroll' recent_room.id %}" class="most-recent-load-more">Load more….</a> <a href="{% url 'billboard:scroll' recent_room.id %}" class="most-recent-load-more">Load more….</a>
{% for event in recent_events %} {% for event in recent_events %}
<div class="drama-event {% if event.actor == viewer %}mine{% else %}theirs{% endif %}"> <div class="drama-event {% if event.actor == viewer %}mine{% else %}theirs{% endif %}">
<span class="drama-event-body"> <span class="drama-event-body{% if event.struck %} struck{% endif %}">
<strong>{{ event.actor|display_name }}</strong> <strong>{{ event.actor|display_name }}</strong>
{{ event.to_prose }} {{ event.to_prose|safe }}
</span> </span>
<time class="drama-event-time" datetime="{{ event.timestamp|date:'c' }}"> <time class="drama-event-time" datetime="{{ event.timestamp|date:'c' }}">
{{ event.timestamp|relative_ts }} {{ event.timestamp|relative_ts }}

View File

@@ -6,6 +6,76 @@
{% block content %} {% block content %}
{% csrf_token %} {% csrf_token %}
<div class="billscroll-page"> <div class="billscroll-page">
{% include "apps/applets/_partials/_gear.html" with menu_id="id_billscroll_menu" %}
<div id="id_billscroll_menu" style="display:none;">
<form id="id_scroll_filter_form">
<label><input type="checkbox" name="labels" value="frame" checked> Frame</label>
<label><input type="checkbox" name="labels" value="redact" checked> Redact</label>
<div class="menu-btns">
<button type="submit" class="btn btn-confirm">OK</button>
<button type="button" class="btn btn-cancel applet-menu-cancel">NVM</button>
</div>
</form>
</div>
{% include "apps/billboard/_partials/_applet-billboard-scroll.html" %} {% include "apps/billboard/_partials/_applet-billboard-scroll.html" %}
</div> </div>
<script>
(function () {
var STORAGE_KEY = 'billscroll-labels-{{ room.id }}';
var ALL_LABELS = ['frame', 'redact'];
function applyFilter(checked) {
document.querySelectorAll(
'#id_drama_scroll .drama-event[data-label]'
).forEach(function (el) {
el.style.display = checked.indexOf(el.dataset.label) !== -1 ? '' : 'none';
});
}
function saveFilter(checked) {
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(checked)); } catch (_) {}
}
function loadFilter() {
try {
var raw = localStorage.getItem(STORAGE_KEY);
return raw ? JSON.parse(raw) : null;
} catch (_) { return null; }
}
function syncCheckboxes(checked) {
var form = document.getElementById('id_scroll_filter_form');
if (!form) return;
form.querySelectorAll('input[name="labels"]').forEach(function (cb) {
cb.checked = checked.indexOf(cb.value) !== -1;
});
}
// Restore saved filter on page load
var saved = loadFilter();
if (saved) {
applyFilter(saved);
syncCheckboxes(saved);
}
// Apply + save on OK
var form = document.getElementById('id_scroll_filter_form');
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
var checked = Array.from(
form.querySelectorAll('input[name="labels"]:checked')
).map(function (cb) { return cb.value; });
applyFilter(checked);
saveFilter(checked);
// Close the menu (mirror applets.js close logic)
var menu = document.getElementById('id_billscroll_menu');
if (menu) menu.style.display = 'none';
var gear = document.querySelector(
'.gear-btn[data-menu-target="id_billscroll_menu"]'
);
if (gear) gear.classList.remove('active');
});
}());
</script>
{% endblock %} {% endblock %}

View File

@@ -8,6 +8,9 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
data-polarity="{{ user_polarity }}" data-polarity="{{ user_polarity }}"
data-user-role="{{ user_seat.role }}" data-user-role="{{ user_seat.role }}"
data-reserve-url="{{ sig_reserve_url }}" data-reserve-url="{{ sig_reserve_url }}"
data-ready-url="{% url 'epic:sig_ready' room.id %}"
data-ready="{{ user_ready|yesno:'true,false' }}"
data-reservations="{{ sig_reservations_json }}"> data-reservations="{{ sig_reservations_json }}">
<div class="sig-modal"> <div class="sig-modal">
@@ -21,9 +24,11 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
</div> </div>
<div class="fan-card-face"> <div class="fan-card-face">
<p class="fan-card-name-group"></p> <p class="fan-card-name-group"></p>
<p class="sig-qualifier-above"></p>
<h3 class="fan-card-name"></h3> <h3 class="fan-card-name"></h3>
<p class="sig-qualifier-below"></p>
<p class="fan-card-arcana"></p> <p class="fan-card-arcana"></p>
<p class="fan-card-correspondence"></p> <p class="fan-card-correspondence"></p>{# not shown in sig-select — game-kit only #}
</div> </div>
<div class="fan-card-corner fan-card-corner--br"> <div class="fan-card-corner fan-card-corner--br">
<span class="fan-corner-rank"></span> <span class="fan-corner-rank"></span>
@@ -77,9 +82,9 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
<button class="sig-nvm-btn btn btn-cancel" type="button">NVM</button> <button class="sig-nvm-btn btn btn-cancel" type="button">NVM</button>
</div> </div>
<div class="sig-card-cursors"> <div class="sig-card-cursors">
<span class="sig-cursor sig-cursor--left"></span> <i class="fa-solid fa-hand-pointer sig-cursor sig-cursor--left"></i>
<span class="sig-cursor sig-cursor--mid"></span> <i class="fa-solid fa-hand-pointer sig-cursor sig-cursor--mid"></i>
<span class="sig-cursor sig-cursor--right"></span> <i class="fa-solid fa-hand-pointer sig-cursor sig-cursor--right"></i>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@@ -34,6 +34,14 @@
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if room.table_status == "SKY_SELECT" %}
<button id="id_pick_sky_btn" class="btn btn-primary">PICK<br>SKY</button>
{% elif room.table_status == "SIG_SELECT" %}
<button id="id_pick_sky_btn" class="btn btn-primary" style="display:none">PICK<br>SKY</button>
{% if polarity_done %}
<p id="id_hex_waiting_msg">{% if user_polarity == "levity" %}Gravity settling . . .{% else %}Levity appraising . . .{% endif %}</p>
{% endif %}
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@@ -54,8 +62,8 @@
</div> </div>
</div> </div>
{# Sig Select overlay — only shown to seated gamers in this polarity #} {# Sig Select overlay — suppressed once this gamer's polarity sigs are assigned #}
{% if room.table_status == "SIG_SELECT" and user_polarity %} {% if room.table_status == "SIG_SELECT" and user_polarity and not polarity_done %}
{% include "apps/gameboard/_partials/_sig_select_overlay.html" %} {% include "apps/gameboard/_partials/_sig_select_overlay.html" %}
{% endif %} {% endif %}
@@ -73,7 +81,7 @@
<i class="fa-solid fa-dice-d20"></i> <i class="fa-solid fa-dice-d20"></i>
</button> </button>
</div> </div>
<div id="id_tray" style="display:none"><div id="id_tray_grid" data-role-icons-url="{% static 'apps/epic/icons/cards-roles/' %}">{% if my_tray_role %}<div class="tray-cell tray-role-card" data-role="{{ my_tray_role }}"><img src="{% static my_tray_scrawl_static_path %}" alt="{{ my_tray_role }}"></div>{% else %}<div class="tray-cell"></div>{% endif %}{% for i in "2345678" %}<div class="tray-cell"></div>{% endfor %}</div></div> <div id="id_tray" style="display:none"><div id="id_tray_grid" data-role-icons-url="{% static 'apps/epic/icons/cards-roles/' %}">{% if my_tray_role %}<div class="tray-cell tray-role-card" data-role="{{ my_tray_role }}"><img src="{% static my_tray_scrawl_static_path %}" alt="{{ my_tray_role }}"></div>{% else %}<div class="tray-cell"></div>{% endif %}{% if my_tray_sig %}<div class="tray-cell tray-sig-card"><img src="{% static 'apps/epic/icons/cards-sigs/Blank.svg' %}" alt="{{ my_tray_sig.name }}"></div>{% else %}<div class="tray-cell"></div>{% endif %}{% for i in "345678" %}<div class="tray-cell"></div>{% endfor %}</div></div>
</div> </div>
{% endif %} {% endif %}
{% include "apps/gameboard/_partials/_room_gear.html" %} {% include "apps/gameboard/_partials/_room_gear.html" %}

View File

@@ -1,10 +1,10 @@
{% load lyric_extras %} {% load lyric_extras %}
<section id="id_drama_scroll" class="drama-scroll" data-scroll-position="{{ scroll_position|default:0 }}"> <section id="id_drama_scroll" class="drama-scroll" data-scroll-position="{{ scroll_position|default:0 }}">
{% for event in events %} {% for event in events %}
<div class="drama-event {% if event.actor == viewer %}mine{% else %}theirs{% endif %}"> <div class="drama-event {% if event.actor == viewer %}mine{% else %}theirs{% endif %}" data-label="{% if event.struck %}redact{% else %}frame{% endif %}">
<span class="drama-event-body"> <span class="drama-event-body{% if event.struck %} struck{% endif %}">
<strong>{{ event.actor|display_name }}</strong> <strong>{{ event.actor|display_name }}</strong>
{{ event.to_prose }} {{ event.to_prose|safe }}
</span> </span>
<time class="drama-event-time" datetime="{{ event.timestamp|date:'c' }}"> <time class="drama-event-time" datetime="{{ event.timestamp|date:'c' }}">
{{ event.timestamp|relative_ts }} {{ event.timestamp|relative_ts }}