anvilsign in

collin/mahjong · 82287db9

Two online modes over antics: pure online, and party with phone hands

Collin Richards · 2026-08-20 08:50 UTC · 82287db9c0f56beec27f127645ba58fc3e20c3bc · parent 323803dc · browse files

modifiedTODO.md+2 −0
⋯ 25 unchanged lines
2626 - [ ] computer shouldn't be able to take the pung before the throw animation completes
2727 - [ ] weirdness around throwing the tiles in the center, it's like attracted to the center, even gets stopped by wall it keeps going
2828 - [ ] put the tumble back on a thrown tile — `TUMBLE` in `src/table/pool.ts`
29+- [ ] fake 3d for the tiles (centered around controllign players pov)
30+- [ ] consider switching physics engine to real 3d engine
modifiedpackage-lock.json+24 −1
⋯ 7 unchanged lines
88 "name": "mahjong",
99 "version": "0.0.0",
1010 "dependencies": {
11+ "antics-sdk": "^0.3.0",
1112 "react": "^19.2.8",
12- "react-dom": "^19.2.8"
13+ "react-dom": "^19.2.8",
14+ "uqr": "^0.1.3"
1315 },
1416 "devDependencies": {
1517 "@types/node": "^24.13.3",
⋯ 834 unchanged lines
850852 "url": "https://opencollective.com/vitest"
851853 }
852854 },
855+ "node_modules/antics-protocol": {
856+ "version": "0.2.0",
857+ "resolved": "https://registry.npmjs.org/antics-protocol/-/antics-protocol-0.2.0.tgz",
858+ "integrity": "sha512-9kFogFzYF99ajcxCB9hWv4IRw3sgO1dF1jbw6cK7sx9EVc+rUuUh2xGhqDzdWoajZz3AmmeBhjmhfTfg2Hlq/Q==",
859+ "license": "SEE LICENSE IN LICENSE"
860+ },
861+ "node_modules/antics-sdk": {
862+ "version": "0.3.0",
863+ "resolved": "https://registry.npmjs.org/antics-sdk/-/antics-sdk-0.3.0.tgz",
864+ "integrity": "sha512-GhgJP2HiJaZTH89MsUZyJsNiJCkslLy965BAAudCYpFGQStJMUu6Yrg4VB1famvccZYK4ghS2eKwFnCvRgWeXg==",
865+ "license": "SEE LICENSE IN LICENSE",
866+ "dependencies": {
867+ "antics-protocol": "^0.1.0 || ^0.2.0"
868+ }
869+ },
853870 "node_modules/assertion-error": {
854871 "version": "2.0.1",
855872 "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
⋯ 675 unchanged lines
15311548 "dev": true,
15321549 "license": "MIT"
15331550 },
1551+ "node_modules/uqr": {
1552+ "version": "0.1.3",
1553+ "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz",
1554+ "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==",
1555+ "license": "MIT"
1556+ },
15341557 "node_modules/vite": {
15351558 "version": "8.2.0",
15361559 "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz",
⋯ 184 unchanged lines
modifiedpackage.json+3 −1
⋯ 10 unchanged lines
1111 "test": "vitest run"
1212 },
1313 "dependencies": {
14+ "antics-sdk": "^0.3.0",
1415 "react": "^19.2.8",
15- "react-dom": "^19.2.8"
16+ "react-dom": "^19.2.8",
17+ "uqr": "^0.1.3"
1618 },
1719 "devDependencies": {
1820 "@types/node": "^24.13.3",
⋯ 9 unchanged lines
deletedpublic/tiles/back.pngbinary
Binary file
deletedpublic/tiles/tiles.svg+0 −563
1-<?xml version="1.0" encoding="utf-8"?>
2-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5504" height="352" viewBox="0 0 2752 176" version="1.1">
4- <defs>
5- <g id="bt1">
6- <path d="M9,0C8.47,0,7.96,0.21,7.59,0.59l-7,7C0.21,7.96,0,8.47,0,9v77c0,1.1,0.9,2,2,2h53c0.53,0,1.04-0.21,1.41-0.59l7-7C63.79,80.04,64,79.53,64,79V2c0-1.1-0.9-2-2-2H9z"/>
7- <path fill="#9F9F9F" d="M11.75,78.25l-9,9h9H55c0.33,0,0.65-0.13,0.88-0.37l7-7c0.23-0.23,0.37-1.3,0.37-1.63H11.75z"/>
8- <path fill="#D7D7D7" d="M8.12,1.12l-7,7C0.88,8.35,0.75,8.67,0.75,9v66.25v1v9l2,2l7-7v-4v-1V0.75C9.42,0.75,8.35,0.88,8.12,1.12z"/>
9- <path fill="#BFBFBF" d="M1.61,86.89l10.14-10.14v-0.5L1.34,86.66C1.42,86.75,1.51,86.82,1.61,86.89z"/>
10- <path fill="#B7B7B7" d="M1.93,87.07l9.82-9.82v-0.5L1.61,86.89C1.71,86.96,1.82,87.02,1.93,87.07z"/>
11- <path fill="#AFAFAF" d="M2.3,87.2l9.45-9.45v-0.5l-9.82,9.82C2.05,87.12,2.17,87.17,2.3,87.2z"/>
12- <path fill="#A7A7A7" d="M11.75,77.75L2.3,87.2c0.14,0.03,0.29,0.05,0.45,0.05l9-9V77.75z"/>
13- <path fill="#C7C7C7" d="M1.34,86.66l10.41-10.41h-0.5L1.11,86.39C1.18,86.49,1.25,86.58,1.34,86.66z"/>
14- <path fill="#CDCDCD" d="M1.11,86.39l10.14-10.14h-0.5l-9.82,9.82C0.98,86.18,1.04,86.29,1.11,86.39z"/>
15- <path fill="#D2D2D2" d="M0.93,86.07l9.82-9.82h-0.5L0.8,85.7C0.83,85.83,0.88,85.95,0.93,86.07z"/>
16- <path fill="#D7D7D7" d="M10.25,76.25h-0.5l-9,9c0,0.15,0.02,0.3,0.05,0.45L10.25,76.25z"/>
17- <path fill="#F6F6F6" d="M63.25,78.25V2.75c0-1.1-0.9-2-2-2h-50.5c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h50.5C62.35,80.25,63.25,79.35,63.25,78.25z"/>
18- <path fill="#FFFFFF" d="M8.75,78.25V2.75c0-1.1,0.9-2,2-2h-1c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h1C9.65,80.25,8.75,79.35,8.75,78.25z"/>
19- </g>
20- <g id="bt2">
21- <path fill="#0000FF" d="M9,0C8.47,0,7.96,0.21,7.59,0.59l-7,7C0.21,7.96,0,8.47,0,9v77c0,1.1,0.9,2,2,2h53c0.53,0,1.04-0.21,1.41-0.59l7-7C63.79,80.04,64,79.53,64,79V2c0-1.1-0.9-2-2-2H9z"/>
22- <path fill="#8F8FFF" d="M11.75,78.25l-9,9h9H55c0.33,0,0.65-0.13,0.88-0.37l7-7c0.23-0.23,0.37-1.3,0.37-1.63H11.75z"/>
23- <path fill="#C7C7FF" d="M8.12,1.12l-7,7C0.88,8.35,0.75,8.67,0.75,9v66.25v1v9l2,2l7-7v-4v-1V0.75C9.42,0.75,8.35,0.88,8.12,1.12z"/>
24- <path fill="#AFAFFF" d="M1.61,86.89l10.14-10.14v-0.5L1.34,86.66C1.42,86.75,1.51,86.82,1.61,86.89z"/>
25- <path fill="#A7A7FF" d="M1.93,87.07l9.82-9.82v-0.5L1.61,86.89C1.71,86.96,1.82,87.02,1.93,87.07z"/>
26- <path fill="#9F9FFF" d="M2.3,87.2l9.45-9.45v-0.5l-9.82,9.82C2.05,87.12,2.17,87.17,2.3,87.2z"/>
27- <path fill="#9797FF" d="M11.75,77.75L2.3,87.2c0.14,0.03,0.29,0.05,0.45,0.05l9-9V77.75z"/>
28- <path fill="#B7B7FF" d="M1.34,86.66l10.41-10.41h-0.5L1.11,86.39C1.18,86.49,1.25,86.58,1.34,86.66z"/>
29- <path fill="#BDBDFF" d="M1.11,86.39l10.14-10.14h-0.5l-9.82,9.82C0.98,86.18,1.04,86.29,1.11,86.39z"/>
30- <path fill="#C2C2FF" d="M0.93,86.07l9.82-9.82h-0.5L0.8,85.7C0.83,85.83,0.88,85.95,0.93,86.07z"/>
31- <path fill="#C7C7FF" d="M10.25,76.25h-0.5l-9,9c0,0.15,0.02,0.3,0.05,0.45L10.25,76.25z"/>
32- <path fill="#E3E3FF" d="M61.25,0.75H9.75c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h51.5c1.1,0,2-0.9,2-2V2.75C63.25,1.65,62.35,0.75,61.25,0.75z"/>
33- <path fill="#FFFFFF" stroke="#0000FF" stroke-width="3" d="M60,74.19c0,1.55-1.22,2.81-2.72,2.81H13.72c-1.5,0-2.72-1.26-2.72-2.81V6.81C11,5.26,12.22,4,13.72,4h43.56C58.78,4,60,5.26,60,6.81V74.19z"/>
34- </g>
35- <path id="bams" fill="#006C00" d="M11,13.5c0-0.92-1.25-1.71-3.06-2.09V4.44C9.75,4.06,11,3.27,11,2.35C11,1.05,8.54,0,5.5,0S0,1.05,0,2.35c0,0.92,1.25,1.71,3.06,2.09v6.97C1.25,11.79,0,12.58,0,13.5c0,0.92,1.25,1.71,3.06,2.09v6.96C1.25,22.94,0,23.73,0,24.65C0,25.95,2.46,27,5.5,27s5.5-1.05,5.5-2.35c0-0.92-1.25-1.71-3.06-2.09v-6.96C9.75,15.21,11,14.42,11,13.5z M5.5,1.47c1.35,0,2.44,0.39,2.44,0.88c0,0.41-0.78,0.75-1.83,0.85v5.94c0,0.46-0.28,0.84-0.61,0.84S4.89,9.6,4.89,9.14V3.2C3.84,3.1,3.06,2.76,3.06,2.35C3.06,1.86,4.15,1.47,5.5,1.47z M5.5,25.53c-1.35,0-2.44-0.39-2.44-0.88c0-0.41,0.78-0.75,1.83-0.85v-5.94c0-0.46,0.28-0.84,0.61-0.84s0.61,0.38,0.61,0.84v5.94c1.05,0.1,1.83,0.44,1.83,0.85C7.94,25.14,6.85,25.53,5.5,25.53z M5.5,14.38c-1.86,0-3.36-0.39-3.36-0.88c0-0.49,1.5-0.88,3.36-0.88s3.36,0.39,3.36,0.88C8.86,13.99,7.36,14.38,5.5,14.38z"/>
36- <path id="bams2" fill="#9C0000" d="M11,13.5c0-0.92-1.25-1.71-3.06-2.09V4.44C9.75,4.06,11,3.27,11,2.35C11,1.05,8.54,0,5.5,0S0,1.05,0,2.35c0,0.92,1.25,1.71,3.06,2.09v6.97C1.25,11.79,0,12.58,0,13.5c0,0.92,1.25,1.71,3.06,2.09v6.96C1.25,22.94,0,23.73,0,24.65C0,25.95,2.46,27,5.5,27s5.5-1.05,5.5-2.35c0-0.92-1.25-1.71-3.06-2.09v-6.96C9.75,15.21,11,14.42,11,13.5z M5.5,1.47c1.35,0,2.44,0.39,2.44,0.88c0,0.41-0.78,0.75-1.83,0.85v5.94c0,0.46-0.28,0.84-0.61,0.84S4.89,9.6,4.89,9.14V3.2C3.84,3.1,3.06,2.76,3.06,2.35C3.06,1.86,4.15,1.47,5.5,1.47z M5.5,25.53c-1.35,0-2.44-0.39-2.44-0.88c0-0.41,0.78-0.75,1.83-0.85v-5.94c0-0.46,0.28-0.84,0.61-0.84s0.61,0.38,0.61,0.84v5.94c1.05,0.1,1.83,0.44,1.83,0.85C7.94,25.14,6.85,25.53,5.5,25.53z M5.5,14.38c-1.86,0-3.36-0.39-3.36-0.88c0-0.49,1.5-0.88,3.36-0.88s3.36,0.39,3.36,0.88C8.86,13.99,7.36,14.38,5.5,14.38z"/>
37- <g id="dots">
38- <path fill="#10106C" d="M9,1.42c-4.18,0-7.58,3.4-7.58,7.58s3.4,7.58,7.58,7.58c4.18,0,7.58-3.4,7.58-7.58S13.18,1.42,9,1.42zM9,15.11c-1.36,0-2.49-0.97-2.75-2.25c0.78,0.56,1.72,0.89,2.75,0.89c1.03,0,1.97-0.34,2.75-0.89C11.49,14.14,10.36,15.11,9,15.11z M9,2.89c1.36,0,2.49,0.97,2.75,2.25C10.97,4.59,10.03,4.25,9,4.25c-1.03,0-1.97,0.34-2.75,0.89C6.51,3.86,7.64,2.89,9,2.89z M12.75,9c0,0.57-0.14,1.11-0.37,1.59C12.04,9.92,11.51,9.37,10.85,9c0.66-0.37,1.19-0.92,1.53-1.59C12.61,7.89,12.75,8.43,12.75,9z M5.25,9c0-0.57,0.14-1.11,0.37-1.59C5.96,8.08,6.49,8.63,7.15,9c-0.66,0.37-1.19,0.92-1.53,1.59C5.39,10.11,5.25,9.57,5.25,9z M11.59,11.7c-1.02-0.27-1.82-1.1-2.03-2.14c1.04,0.21,1.87,1.01,2.14,2.03C11.66,11.63,11.63,11.66,11.59,11.7z M6.3,11.59c0.27-1.02,1.1-1.82,2.15-2.03c-0.21,1.04-1.01,1.87-2.03,2.14C6.37,11.66,6.34,11.63,6.3,11.59z M6.41,6.3c1.02,0.27,1.82,1.1,2.03,2.15C7.4,8.23,6.57,7.43,6.3,6.41C6.34,6.37,6.37,6.34,6.41,6.3z M11.7,6.41c-0.27,1.02-1.1,1.82-2.14,2.03c0.21-1.05,1.01-1.87,2.03-2.15C11.63,6.34,11.66,6.37,11.7,6.41z M9,7.15C8.63,6.49,8.08,5.96,7.41,5.62C7.89,5.39,8.43,5.25,9,5.25s1.11,0.14,1.59,0.37C9.92,5.96,9.37,6.49,9,7.15z M9,10.85c0.37,0.66,0.92,1.19,1.59,1.53c-0.49,0.23-1.02,0.37-1.59,0.37s-1.11-0.14-1.59-0.37C8.08,12.04,8.63,11.51,9,10.85z M5.14,11.75C3.86,11.49,2.89,10.36,2.89,9s0.97-2.49,2.25-2.75C4.59,7.03,4.25,7.97,4.25,9C4.25,10.03,4.59,10.97,5.14,11.75z M12.86,6.25c1.28,0.26,2.25,1.39,2.25,2.75s-0.97,2.49-2.25,2.75c0.56-0.78,0.89-1.72,0.89-2.75C13.75,7.97,13.41,7.03,12.86,6.25z M15.01,6.33c-0.59-0.6-1.38-0.99-2.25-1.09c-0.11-0.88-0.49-1.66-1.09-2.25C13.15,3.65,14.35,4.85,15.01,6.33z M6.33,2.99c-0.6,0.59-0.98,1.38-1.09,2.25C4.36,5.35,3.58,5.73,2.99,6.33C3.65,4.85,4.85,3.65,6.33,2.99z M2.99,11.67c0.59,0.6,1.38,0.99,2.25,1.09c0.11,0.88,0.49,1.66,1.09,2.25C4.85,14.35,3.65,13.15,2.99,11.67z M11.67,15.01c0.6-0.59,0.99-1.38,1.09-2.25c0.88-0.11,1.66-0.49,2.25-1.09C14.35,13.15,13.15,14.35,11.67,15.01z"/>
39- <path fill="#10106C" d="M9,0C4.04,0,0,4.04,0,9c0,4.96,4.04,9,9,9c4.96,0,9-4.04,9-9C18,4.04,13.96,0,9,0z M9,17c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S13.41,17,9,17z"/>
40- </g>
41- <g id="dots2">
42- <path fill="#9C0000" d="M9,1.42c-4.18,0-7.58,3.4-7.58,7.58s3.4,7.58,7.58,7.58c4.18,0,7.58-3.4,7.58-7.58S13.18,1.42,9,1.42zM9,15.11c-1.36,0-2.49-0.97-2.75-2.25c0.78,0.56,1.72,0.89,2.75,0.89c1.03,0,1.97-0.34,2.75-0.89C11.49,14.14,10.36,15.11,9,15.11z M9,2.89c1.36,0,2.49,0.97,2.75,2.25C10.97,4.59,10.03,4.25,9,4.25c-1.03,0-1.97,0.34-2.75,0.89C6.51,3.86,7.64,2.89,9,2.89z M12.75,9c0,0.57-0.14,1.11-0.37,1.59C12.04,9.92,11.51,9.37,10.85,9c0.66-0.37,1.19-0.92,1.53-1.59C12.61,7.89,12.75,8.43,12.75,9z M5.25,9c0-0.57,0.14-1.11,0.37-1.59C5.96,8.08,6.49,8.63,7.15,9c-0.66,0.37-1.19,0.92-1.53,1.59C5.39,10.11,5.25,9.57,5.25,9z M11.59,11.7c-1.02-0.27-1.82-1.1-2.03-2.14c1.04,0.21,1.87,1.01,2.14,2.03C11.66,11.63,11.63,11.66,11.59,11.7z M6.3,11.59c0.27-1.02,1.1-1.82,2.15-2.03c-0.21,1.04-1.01,1.87-2.03,2.14C6.37,11.66,6.34,11.63,6.3,11.59z M6.41,6.3c1.02,0.27,1.82,1.1,2.03,2.15C7.4,8.23,6.57,7.43,6.3,6.41C6.34,6.37,6.37,6.34,6.41,6.3z M11.7,6.41c-0.27,1.02-1.1,1.82-2.14,2.03c0.21-1.05,1.01-1.87,2.03-2.15C11.63,6.34,11.66,6.37,11.7,6.41z M9,7.15C8.63,6.49,8.08,5.96,7.41,5.62C7.89,5.39,8.43,5.25,9,5.25s1.11,0.14,1.59,0.37C9.92,5.96,9.37,6.49,9,7.15z M9,10.85c0.37,0.66,0.92,1.19,1.59,1.53c-0.49,0.23-1.02,0.37-1.59,0.37s-1.11-0.14-1.59-0.37C8.08,12.04,8.63,11.51,9,10.85z M5.14,11.75C3.86,11.49,2.89,10.36,2.89,9s0.97-2.49,2.25-2.75C4.59,7.03,4.25,7.97,4.25,9C4.25,10.03,4.59,10.97,5.14,11.75z M12.86,6.25c1.28,0.26,2.25,1.39,2.25,2.75s-0.97,2.49-2.25,2.75c0.56-0.78,0.89-1.72,0.89-2.75C13.75,7.97,13.41,7.03,12.86,6.25z M15.01,6.33c-0.59-0.6-1.38-0.99-2.25-1.09c-0.11-0.88-0.49-1.66-1.09-2.25C13.15,3.65,14.35,4.85,15.01,6.33z M6.33,2.99c-0.6,0.59-0.98,1.38-1.09,2.25C4.36,5.35,3.58,5.73,2.99,6.33C3.65,4.85,4.85,3.65,6.33,2.99z M2.99,11.67c0.59,0.6,1.38,0.99,2.25,1.09c0.11,0.88,0.49,1.66,1.09,2.25C4.85,14.35,3.65,13.15,2.99,11.67z M11.67,15.01c0.6-0.59,0.99-1.38,1.09-2.25c0.88-0.11,1.66-0.49,2.25-1.09C14.35,13.15,13.15,14.35,11.67,15.01z"/>
43- <path fill="#9C0000" d="M9,0C4.04,0,0,4.04,0,9c0,4.96,4.04,9,9,9c4.96,0,9-4.04,9-9C18,4.04,13.96,0,9,0z M9,17c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S13.41,17,9,17z"/>
44- </g>
45- <g id="nums">
46- <path fill="#BA0000" d="M18.01,0.14c0,0-4.39,3.33-4.78,3.63c-0.32-0.33-3.2-3.27-3.2-3.27l2.22,8.92l0.6-0.03c0.75-4.76,4.35-6.63,5.33-7.05c0.14,0.2,0.26,0.42,0.26,0.72c0,0.09-0.01,0.18-0.02,0.28c-0.21,1.67-2.39,4.64-6.68,7.21l-0.31,0.18l0.81,1.01l0.2-0.06c0.06-0.02,6.59-2.06,10.36-1.88l1.14,0.05l-0.96-0.63c-0.08-0.05-1.95-1.25-3.93-1.03c0.28-0.63,2.26-2.56,4.15-4.16l0.31-0.26L18.19,0L18.01,0.14z"/>
47- <path fill="#BA0000" d="M33.78,24.64c-0.17-0.17-4.21-4.28-5.63-5.01c-0.53-0.27-1.71-0.18-5.75,0.16c0,0-0.17,0.01-0.43,0.04c1.19-2.79,2.72-5.86,3.57-6.25l0.55-0.25l-2.13-2.09l-0.21,0.03c-5.11,0.87-13.08,2.09-14.11,1.9l-0.35-0.07l-0.12,0.33c-0.2,0.56,0.32,4.93,0.86,7.37C5.45,21.2,0,21.66,0,21.66l0.3,0.56l0.68,1.27l0.32-0.09c1.72-0.51,2.52-0.57,4.6-0.54c0.19,0.49,2.6,6.77,2.6,6.77l0.79-0.2c0,0-0.69-5.69-0.8-6.52c0.78,0.02,7.99,0.22,9.67,0.26c-1.09,0.92-3.56,3.02-3.56,3.02l0.4,0.71c0,0,5.15-1.76,5.41-1.85c0.25,0.1,4.3,1.65,4.3,1.65s-2.13-2.57-2.81-3.38c3.78,0.2,4.75,0.42,5.01,0.74c0.33,0.39,0.5,0.96,0.5,1.6c0,1.16-0.58,2.57-1.8,3.73c-1.82-0.46-5.17-2.34-5.21-2.36l-0.52,0.62L24.96,34l0.24-0.05c3.82-0.78,8.44-8.48,8.63-8.81L34,24.87L33.78,24.64z M21.02,16.65c-1.22,0.16-2.77,0.37-3.21,0.43c0-0.33,0-1.17,0-2c1.62-0.13,3.11-0.25,3.49-0.29C21.25,15.06,21.15,15.77,21.02,16.65z M16.09,15.22c0.02,0.84,0.04,1.75,0.05,2.06c-1.41,0.16-2.6,0.26-3.55,0.3c0.1-0.62,0.19-1.31,0.3-2.1C13.13,15.46,14.51,15.35,16.09,15.22z M12.03,20.65c0.08-0.35,0.17-0.76,0.26-1.24c1.3-0.24,2.61-0.46,3.89-0.63c0.01,0.48,0.03,1.1,0.04,1.52C15.97,20.31,14.26,20.46,12.03,20.65zM17.8,20.17c0-0.48,0-1.18,0-1.6c0.98-0.1,1.36-0.08,1.96-0.02l0.96,0.06c-0.07,0.46-0.14,0.92-0.2,1.35C19.43,20.04,18.19,20.14,17.8,20.17z"/>
48- </g>
49- <g id="flower">
50- <rect x="1" y="1" fill="#F8F6D8" stroke="#F8F6D8" width="43" height="67"/>
51- <path fill="#D3BB54" d="M11.27,58.16l-0.71-8.35c1.76-0.67,2.92-1.76,2.83-2.86c-0.09-1.1-1.42-1.91-3.26-2.16l-0.71-8.36c1.76-0.67,2.92-1.76,2.83-2.86c-0.13-1.56-2.69-2.54-5.71-2.19c-3.02,0.35-5.37,1.89-5.23,3.44c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.13,1.55,2.69,2.53,5.71,2.19c3.02-0.35,5.37-1.89,5.23-3.44C14.43,59.22,13.11,58.41,11.27,58.16z M4.34,34.47c-0.05-0.58,1-1.18,2.34-1.34c1.34-0.15,2.47,0.2,2.52,0.78c0.04,0.49-0.7,0.99-1.74,1.23l0.61,7.13c0.05,0.56-0.19,1.04-0.52,1.08c-0.34,0.04-0.65-0.38-0.7-0.94l-0.61-7.13C5.19,35.28,4.38,34.96,4.34,34.47z M4.57,47.95c-0.05-0.58,1.41-1.23,3.25-1.44c1.85-0.21,3.39,0.09,3.43,0.67c0.05,0.58-1.41,1.23-3.25,1.44C6.15,48.84,4.62,48.54,4.57,47.95z M9.15,62c-1.34,0.15-2.47-0.19-2.52-0.78c-0.04-0.49,0.7-0.99,1.74-1.23l-0.61-7.13c-0.05-0.56,0.19-1.04,0.52-1.08c0.33-0.04,0.65,0.38,0.69,0.94l0.61,7.13c1.06,0,1.87,0.32,1.91,0.81C11.54,61.25,10.49,61.85,9.15,62z"/>
52- <path fill="none" stroke="#7C5A2D" stroke-width="0.25" d="M11.27,58.16l-0.71-8.35c1.76-0.67,2.92-1.76,2.83-2.86c-0.09-1.1-1.42-1.91-3.26-2.16l-0.71-8.36c1.76-0.67,2.92-1.76,2.83-2.86c-0.13-1.56-2.69-2.54-5.71-2.19c-3.02,0.35-5.37,1.89-5.23,3.44c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.13,1.55,2.69,2.53,5.71,2.19c3.02-0.35,5.37-1.89,5.23-3.44C14.43,59.22,13.11,58.41,11.27,58.16z M4.34,34.47c-0.05-0.58,1-1.18,2.34-1.34c1.34-0.15,2.47,0.2,2.52,0.78c0.04,0.49-0.7,0.99-1.74,1.23l0.61,7.13c0.05,0.56-0.19,1.04-0.52,1.08c-0.34,0.04-0.65-0.38-0.7-0.94l-0.61-7.13C5.19,35.28,4.38,34.96,4.34,34.47z M4.57,47.95c-0.05-0.58,1.41-1.23,3.25-1.44c1.85-0.21,3.39,0.09,3.43,0.67c0.05,0.58-1.41,1.23-3.25,1.44C6.15,48.84,4.62,48.54,4.57,47.95z M9.15,62c-1.34,0.15-2.47-0.19-2.52-0.78c-0.04-0.49,0.7-0.99,1.74-1.23l-0.61-7.13c-0.05-0.56,0.19-1.04,0.52-1.08c0.33-0.04,0.65,0.38,0.69,0.94l0.61,7.13c1.06,0,1.87,0.32,1.91,0.81C11.54,61.25,10.49,61.85,9.15,62z"/>
53- <path fill="#EADDA7" stroke="#7C5A2D" stroke-width="0.25" d="M5.23,67.99c2.49-8.18-3.33-18.78-3.33-18.78s10.4,10.59,7.48,18.78H5.23z"/>
54- <path fill="#DBC879" d="M19.02,36.17c-2.92-0.98-5.59-0.56-5.97,0.94c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.85-0.14-3.28,0.37-3.54,1.43c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.84-0.14-3.27,0.37-3.54,1.43c-0.38,1.5,1.68,3.51,4.6,4.5c2.92,0.98,5.59,0.56,5.96-0.94c0.27-1.06-0.7-2.38-2.32-3.41l2.03-8.06c1.85,0.14,3.28-0.37,3.55-1.43c0.27-1.06-0.71-2.38-2.33-3.41l2.03-8.06c1.84,0.14,3.28-0.37,3.54-1.43C23.99,39.16,21.94,37.15,19.02,36.17z M14.41,57.04l-1.73,6.88c0.98,0.45,1.63,1.1,1.51,1.58c-0.14,0.56-1.31,0.66-2.6,0.23c-1.3-0.44-2.23-1.25-2.09-1.81c0.12-0.47,0.97-0.62,2.01-0.39l1.73-6.88c0.13-0.54,0.51-0.88,0.83-0.77C14.39,55.98,14.54,56.5,14.41,57.04z M18.32,52.88c-0.14,0.56-1.7,0.53-3.48-0.07c-1.78-0.6-3.11-1.54-2.97-2.11c0.14-0.56,1.7-0.53,3.48,0.07C17.13,51.37,18.46,52.32,18.32,52.88z M20.68,39.67c-0.12,0.47-0.97,0.62-2.01,0.39l-1.73,6.88c-0.13,0.54-0.51,0.88-0.83,0.77c-0.32-0.11-0.48-0.63-0.34-1.17l1.73-6.88c-0.98-0.45-1.63-1.1-1.51-1.58c0.14-0.56,1.31-0.67,2.6-0.23C19.89,38.3,20.82,39.11,20.68,39.67z"/>
55- <path fill="none" stroke="#7C5A2D" stroke-width="0.25" d="M19.02,36.17c-2.92-0.98-5.59-0.56-5.97,0.94c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.85-0.14-3.28,0.37-3.54,1.43c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.84-0.14-3.27,0.37-3.54,1.43c-0.38,1.5,1.68,3.51,4.6,4.5c2.92,0.98,5.59,0.56,5.96-0.94c0.27-1.06-0.7-2.38-2.32-3.41l2.03-8.06c1.85,0.14,3.28-0.37,3.55-1.43c0.27-1.06-0.71-2.38-2.33-3.41l2.03-8.06c1.84,0.14,3.28-0.37,3.54-1.43C23.99,39.16,21.94,37.15,19.02,36.17z M14.41,57.04l-1.73,6.88c0.98,0.45,1.63,1.1,1.51,1.58c-0.14,0.56-1.31,0.66-2.6,0.23c-1.3-0.44-2.23-1.25-2.09-1.81c0.12-0.47,0.97-0.62,2.01-0.39l1.73-6.88c0.13-0.54,0.51-0.88,0.83-0.77C14.39,55.98,14.54,56.5,14.41,57.04z M18.32,52.88c-0.14,0.56-1.7,0.53-3.48-0.07c-1.78-0.6-3.11-1.54-2.97-2.11c0.14-0.56,1.7-0.53,3.48,0.07C17.13,51.37,18.46,52.32,18.32,52.88z M20.68,39.67c-0.12,0.47-0.97,0.62-2.01,0.39l-1.73,6.88c-0.13,0.54-0.51,0.88-0.83,0.77c-0.32-0.11-0.48-0.63-0.34-1.17l1.73-6.88c-0.98-0.45-1.63-1.1-1.51-1.58c0.14-0.56,1.31-0.67,2.6-0.23C19.89,38.3,20.82,39.11,20.68,39.67z"/>
56- <path fill="#EADA9B" stroke="#7C5A2D" stroke-width="0.25" d="M19.85,67.7c-1.77-5.79,2.35-13.28,2.35-13.28s-7.35,7.49-5.29,13.28H19.85z"/>
57- <path fill="#BFAA52" stroke="#7C5A2D" d="M30.33,52.2c-10.89,0-12.13-1.84-15.85-5.97c4.7-3.44,10.9-3.9,16.34,4.82L30.33,52.2z"/>
58- <path fill="#DBC775" d="M27.52,50.68c-6.89,0-7.68-1.16-10.03-3.78c2.98-2.18,6.9-2.47,10.34,3.05L27.52,50.68z"/>
59- <path fill="#BFAA52" stroke="#7C5A2D" d="M42.27,47.51c-0.36,2.52-6.85,4.67-9.62,5.14c-2.77,0.47,1.52-5.63,6.18-9.5C41.78,42.64,42.5,45.94,42.27,47.51z"/>
60- <path fill="#DBC775" d="M37.59,49.31c-0.15,1.08-2.95,2.01-4.14,2.21c-1.19,0.2,0.66-2.42,2.66-4.09C37.38,47.21,37.69,48.63,37.59,49.31z"/>
61- <path fill="#E5D389" stroke="#7C5A2D" stroke-width="0.75" d="M38.25,67.92c-7.82-3.7-5.44-11.92-5.44-18.72l-2.23-0.29c0,0-2.8,17.48,4.83,18.76L38.25,67.92z"/>
62- <path fill="#BFAA52" stroke="#7C5A2D" d="M43.33,52.91v7.75c0,0-2.47,2.07-4.21,2.07c-1.73,0-6.93-9.55-6.44-10.59C33.19,51.1,42.84,50.85,43.33,52.91z"/>
63- <path fill="#DBC775" d="M40.06,53.5c1.02,0.72,0,3.43,0,3.43s-1.33,0.91-2.27,0.91c-0.93,0-3.73-4.22-3.47-4.68S39.04,52.79,40.06,53.5z"/>
64- <path fill="#BFAA52" stroke="#7C5A2D" d="M31.45,52.39c-12.87-1.29-10.89,4.39-14.11,10.07C27.49,65.3,28.23,57.04,31.45,52.39z"/>
65- <path fill="#E2CF7F" d="M30.26,53.1c-8.21-0.82-6.95,2.8-9,6.42C27.74,61.34,28.21,56.07,30.26,53.1z"/>
66- <path fill="#BFAA52" stroke="#7C5A2D" d="M32.93,50.42c0,0,3.69-9.06-0.25-11.11c-4.04-2.11-6.64-1.29-7.13,0c-0.5,1.29,3.91,12.41,5.65,12.66C32.93,52.23,32.93,50.42,32.93,50.42z"/>
67- <path fill="#DBC775" d="M32.14,50.27c0,0,2.05-5.67-0.14-6.95c-2.24-1.32-3.68-0.81-3.96,0c-0.28,0.81,2.17,7.76,3.13,7.92C32.14,51.4,32.14,50.27,32.14,50.27z"/>
68- <path fill="#EADDA7" stroke="#7C5A2D" stroke-width="0.25" d="M31.45,51.12c0,0-1.48-2.54-1.23-4.87"/>
69- <path fill="#BFAA52" stroke="#7C5A2D" d="M30.33,52.6c0,0-4.13,5.57-2.01,5.48c2.41-0.11,2.6,0,5.1,0.46c2.57,0.47,0.2-5.55-1.6-6.32L30.33,52.6z"/>
70- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M27.27,48.13c0,0,2.02,2.93,3.35,2.93c1.33,0,0.61,2.18,0,1.56C30,51.99,28.15,50.19,27.05,50C26.23,50,26.04,47.66,27.27,48.13z"/>
71- <path fill="#D3BB54" stroke="#AF9A41" d="M44,12.51c-4.46,0-8.54,2.85-8.54,6.37c0,3.52,4.08,6.37,8.54,6.37V12.51z"/>
72- <path fill="#D3BB54" stroke="#AF9A41" d="M33.33,7.64c0-3.52-3.62-6.23-8.08-6.23c-4.46,0-8.08,2.71-8.08,6.23s3.62,6.37,8.08,6.37C29.71,14.01,33.33,11.16,33.33,7.64z"/>
73- <ellipse fill="#D3BB54" stroke="#AF9A41" cx="29.56" cy="17.54" rx="8.08" ry="6.37"/>
74- <path fill="#DBC879" stroke="#AF9A41" d="M44,16.35V1.42H30.9c-2.93,1.56-3.79,4.49-3.79,7.49c0,4.79,4.93,8.68,11.02,8.68C40.21,17.58,42.34,17.13,44,16.35z"/>
75- <path fill="#D3BB54" stroke="#AF9A41" d="M31.94,15.47c0,0-0.72,1.82-3.03,1.82"/>
76- <path fill="#D3BB54" stroke="#AF9A41" d="M27.19,6.62c0,0-1.73,0.57-3.17-1.02"/>
77- <path fill="#663333" d="M44,0H0v69h45V0H44z M43,2c0,1.91,0,63.09,0,65c-1.86,0-39.14,0-41,0C2,65.09,2,3.91,2,2C3.86,2,41.14,2,43,2z"/>
78- <path fill="#E2CF7F" d="M31.13,52.9c0,0-1.71,2.31-0.83,2.27c1-0.04,1.08,0,2.11,0.19c1.06,0.2,0.08-2.3-0.66-2.62L31.13,52.9z"/>
79- </g>
80- <g id="season">
81- <rect x="1" y="1" fill="#E3E4FF" width="43" height="67"/>
82- <polygon fill="#CAE0FF" stroke="#99B9FF" points="4.69,42.38 9.02,39.76 10.2,44.33 6.26,48.26 8.62,49.89 10.2,48.26 10.98,51.52 14.91,51.19 14.52,47.93 17.27,49.24 18.85,47.28 14.13,44.01 13.34,39.44 19.24,41.4 20.81,45.97 23.56,44.99 22.77,42.7 26.71,44.33 28.28,41.07 23.96,39.44 27.11,38.45 25.92,36.17 20.42,37.8 14.91,35.84 20.03,32.9 25.92,33.88 26.31,31.27 23.17,30.94 26.31,28.98 23.96,26.37 20.81,28 20.81,26.04 18.06,25.71 17.27,30.61 12.55,33.23 11.77,28.66 15.7,24.41 12.95,23.1 11.38,25.06 10.58,21.47 7.05,22.12 7.45,25.39 4.69,24.08 3.12,25.71 7.45,28.66 8.23,33.55 3.12,31.92 1,27.81 1,34.92 1.16,34.86 6.26,36.82 1.55,39.76 1,39.66 1,45.06 1.16,44.99 1,45.91 1,47.31 3.51,47.6"/>
83- <path fill="#99B9FF" stroke="#678ED3" d="M40.63,26.67c1.1,0,2.17-0.13,3.21-0.35V1.5h-14.3c-2.44,2.67-3.95,6.22-3.95,10.13C25.59,19.94,32.32,26.67,40.63,26.67z"/>
84- <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="22.61,14.9 22.3,14.59 20.96,16.9 23.27,18.23 23.39,17.81"/>
85- <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="34.45,28.87 34.03,28.98 35.36,31.29 37.67,29.96 37.35,29.65"/>
86- <polygon fill="#798CD8" points="43.83,30.1 43.83,29.71 43.59,29.96"/>
87- <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="23.39,5.45 23.27,5.03 20.96,6.36 22.3,8.67 22.61,8.36"/>
88- <polygon fill="#99B9FF" stroke="#678ED3" points="23.31,15.6 23.31,7.66 22.61,8.36 22.3,8.67 19.34,11.63 22.3,14.59 22.61,14.9"/>
89- <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="26.66,23.47 26.23,23.36 26.23,26.02 28.9,26.02 28.78,25.6"/>
90- <polygon fill="#99B9FF" stroke="#678ED3" points="37.35,29.65 37.67,29.96 40.63,32.92 43.59,29.96 43.83,29.71 43.83,28.95 36.66,28.95"/>
91- <polygon fill="#99B9FF" stroke="#678ED3" points="35.41,28.61 28.53,24.64 28.78,25.6 28.9,26.02 29.98,30.07 34.03,28.98 34.45,28.87"/>
92- <polygon fill="#99B9FF" stroke="#678ED3" points="27.61,23.73 23.64,16.85 23.39,17.81 23.27,18.23 22.19,22.27 26.23,23.36 26.66,23.47"/>
93- <polygon fill="#99B9FF" stroke="#678ED3" points="23.39,5.45 23.64,6.41 26.48,1.5 22.33,1.5 23.27,5.03 "/>
94- <path fill="#99B9FF" stroke="#678ED3" d="M30.67,46.67c0,0,2.32-3,7-3c5.67,0,5.67,9.33,5.67,9.33H19.67c0,0-0.33-6.67,7.67-6.67c3.67,0,6,4,6,4"/>
95- <line fill="#99B9FF" stroke="#678ED3" x1="29.5" y1="57.27" x2="24.83" y2="65.36"/>
96- <line fill="#99B9FF" stroke="#678ED3" x1="34.22" y1="55.68" x2="28.68" y2="65.95"/>
97- <line fill="#99B9FF" stroke="#678ED3" x1="23.33" y1="61" x2="20.5" y2="65.91"/>
98- <line fill="#99B9FF" stroke="#678ED3" x1="37.46" y1="54.63" x2="33.25" y2="62.59"/>
99- <line fill="#99B9FF" stroke="#678ED3" x1="40.46" y1="62.17" x2="38.13" y2="66.21"/>
100- <line fill="#99B9FF" stroke="#678ED3" x1="27.11" y1="54.52" x2="24.44" y2="59.14"/>
101- <line fill="#99B9FF" stroke="#678ED3" x1="31.67" y1="54" x2="30.67" y2="55.73"/>
102- <line fill="#99B9FF" stroke="#678ED3" x1="40.98" y1="55.6" x2="37.81" y2="61.09"/>
103- <line fill="#99B9FF" stroke="#678ED3" x1="23.84" y1="55.04" x2="22.18" y2="57.92"/>
104- <line fill="#99B9FF" stroke="#678ED3" x1="21.13" y1="55.63" x2="16.91" y2="63.59"/>
105- <line fill="#99B9FF" stroke="#678ED3" x1="36" y1="63.6" x2="35" y2="65.33"/>
106- <path fill="#678ED3" d="M44,0H0v69h45V0H44z M43,2c0,1.91,0,63.09,0,65c-1.86,0-39.14,0-41,0C2,65.09,2,3.91,2,2C3.86,2,41.14,2,43,2z"/>
107- </g>
108- <!-- 一索 is a bird, not a stick: a sparrow on a perch, drawn line-art in
109- the sheet's own green, red and blue. Origin is the glyph's top-left; it
110- occupies a 44x56 box that centers in the tile face. -->
111- <g id="yitiao" fill="none" stroke-linecap="round" stroke-linejoin="round">
112- <!-- perch, with a knob at each end -->
113- <g stroke="#006C00" fill="#006C00">
114- <path stroke-width="1.4" d="M4.5,35H39.5"/>
115- <circle cx="3.6" cy="35" r="1.9"/>
116- <circle cx="40.4" cy="35" r="1.9"/>
117- </g>
118- <!-- tail plumes -->
119- <g stroke="#006C00" stroke-width="0.9">
120- <path d="M19.6,30.5C16,37,14.2,44,14,51.5"/>
121- <path d="M20.8,31C18,38,17,45.5,17.5,53.5"/>
122- <path d="M22,31.4C20.2,38.5,20.2,46.5,21.5,55"/>
123- <path d="M23.2,31.4C22.6,38.5,23.6,46.5,25.8,55.5"/>
124- <path d="M24.3,31C24.8,38,26.4,45.5,29,54"/>
125- <path d="M25.2,30.3C26.4,36.5,28.6,43.5,31.6,51"/>
126- </g>
127- <!-- far wing -->
128- <path stroke="#294D8E" stroke-width="1.2" d="M25.5,19.5C30,20.5,34.8,24.5,38.8,31C34.5,29.2,30,28.4,27,28.7"/>
129- <!-- long flight feather, sweeping past the perch -->
130- <path stroke="#294D8E" stroke-width="1.2" d="M23,23.5C17.5,27.5,10.8,34.5,5,44.5C11.5,38.5,17.8,32.5,22,29"/>
131- <!-- near wing, folded over the breast -->
132- <path stroke="#294D8E" stroke-width="1.2" d="M24,17.8C17.4,18.6,10.6,23.4,6.2,31.2C12,27.4,17.6,25.6,21.4,26.2C22.8,23.4,23.6,20.6,24,17.8Z"/>
133- <g stroke="#294D8E" stroke-width="0.6">
134- <path d="M19,19.6C18.2,21.8,17.4,24.2,16.6,26.4"/>
135- <path d="M16.6,21C15.8,23.2,14.8,25.4,14,27.4"/>
136- <path d="M14.2,22.6C13.4,24.6,12.4,26.6,11.6,28.6"/>
137- <path d="M11.8,24.6C11,26.4,10.2,28.2,9.4,29.8"/>
138- </g>
139- <!-- head, beak, back and breast -->
140- <g stroke="#9C0000" stroke-width="1.3">
141- <path d="M20,8.6C17.2,7.8,14.4,9.4,13.2,11.8L7.4,13.9L13.2,15.9C12.6,17.6,12.4,19.4,12.7,21"/>
142- <path d="M20.8,9.2C24.4,12,26.4,16.2,26.8,21.4C27.2,26.4,26.2,30,24.4,32"/>
143- <path d="M13.4,17.2C14.2,22.4,16.4,27.4,19.8,31.2"/>
144- </g>
145- <g stroke="#9C0000" stroke-width="0.7">
146- <path d="M15.4,18.4C16,22,17.2,25.4,19,28.6"/>
147- <path d="M18.4,19.2C18.8,22.8,19.8,26.2,21.4,29.6"/>
148- <path d="M21.6,20C21.8,23.6,22.6,26.8,24,30.2"/>
149- </g>
150- <!-- swept-back crest -->
151- <g stroke="#006C00" stroke-width="1.2">
152- <path d="M13.8,10.6C13.2,5.6,16.2,2.4,20.2,2.8C23.4,3.2,24.4,5.4,22.4,6.8"/>
153- <path d="M16.6,8.8C17.8,7.2,20,6.2,22.4,6.8"/>
154- </g>
155- <path stroke="#9C0000" stroke-width="0.7" d="M8.8,14.6L13,14.2"/>
156- <circle fill="#9C0000" stroke="none" cx="16.8" cy="11.9" r="1.4"/>
157- </g>
158- </defs>
159- <use x="0" y="0" xlink:href="#bt1"/>
160- <use x="64" y="0" xlink:href="#bt1"/>
161- <use x="128" y="0" xlink:href="#bt1"/>
162- <use x="192" y="0" xlink:href="#bt1"/>
163- <use x="256" y="0" xlink:href="#bt1"/>
164- <use x="320" y="0" xlink:href="#bt1"/>
165- <use x="384" y="0" xlink:href="#bt1"/>
166- <use x="448" y="0" xlink:href="#bt1"/>
167- <use x="512" y="0" xlink:href="#bt1"/>
168- <use x="576" y="0" xlink:href="#bt1"/>
169- <use x="640" y="0" xlink:href="#bt1"/>
170- <use x="704" y="0" xlink:href="#bt1"/>
171- <use x="768" y="0" xlink:href="#bt1"/>
172- <use x="832" y="0" xlink:href="#bt1"/>
173- <use x="896" y="0" xlink:href="#bt1"/>
174- <use x="960" y="0" xlink:href="#bt1"/>
175- <use x="1024" y="0" xlink:href="#bt1"/>
176- <use x="1088" y="0" xlink:href="#bt1"/>
177- <use x="1152" y="0" xlink:href="#bt1"/>
178- <use x="1216" y="0" xlink:href="#bt1"/>
179- <use x="1280" y="0" xlink:href="#bt1"/>
180- <use x="1344" y="0" xlink:href="#bt1"/>
181- <use x="1408" y="0" xlink:href="#bt1"/>
182- <use x="1472" y="0" xlink:href="#bt1"/>
183- <use x="1536" y="0" xlink:href="#bt1"/>
184- <use x="1600" y="0" xlink:href="#bt1"/>
185- <use x="1664" y="0" xlink:href="#bt1"/>
186- <use x="1728" y="0" xlink:href="#bt1"/>
187- <use x="1792" y="0" xlink:href="#bt1"/>
188- <use x="1856" y="0" xlink:href="#bt1"/>
189- <use x="1920" y="0" xlink:href="#bt1"/>
190- <use x="1984" y="0" xlink:href="#bt1"/>
191- <use x="2048" y="0" xlink:href="#bt1"/>
192- <use x="2112" y="0" xlink:href="#bt1"/>
193- <use x="2176" y="0" xlink:href="#bt1"/>
194- <use x="2240" y="0" xlink:href="#bt1"/>
195- <use x="2304" y="0" xlink:href="#bt1"/>
196- <use x="2368" y="0" xlink:href="#bt1"/>
197- <use x="2432" y="0" xlink:href="#bt1"/>
198- <use x="2496" y="0" xlink:href="#bt1"/>
199- <use x="2560" y="0" xlink:href="#bt1"/>
200- <use x="2624" y="0" xlink:href="#bt1"/>
201- <use x="2688" y="0" xlink:href="#bt1"/>
202- <use x="0" y="88" xlink:href="#bt2"/>
203- <use x="64" y="88" xlink:href="#bt2"/>
204- <use x="128" y="88" xlink:href="#bt2"/>
205- <use x="192" y="88" xlink:href="#bt2"/>
206- <use x="256" y="88" xlink:href="#bt2"/>
207- <use x="320" y="88" xlink:href="#bt2"/>
208- <use x="384" y="88" xlink:href="#bt2"/>
209- <use x="448" y="88" xlink:href="#bt2"/>
210- <use x="512" y="88" xlink:href="#bt2"/>
211- <use x="576" y="88" xlink:href="#bt2"/>
212- <use x="640" y="88" xlink:href="#bt2"/>
213- <use x="704" y="88" xlink:href="#bt2"/>
214- <use x="768" y="88" xlink:href="#bt2"/>
215- <use x="832" y="88" xlink:href="#bt2"/>
216- <use x="896" y="88" xlink:href="#bt2"/>
217- <use x="960" y="88" xlink:href="#bt2"/>
218- <use x="1024" y="88" xlink:href="#bt2"/>
219- <use x="1088" y="88" xlink:href="#bt2"/>
220- <use x="1152" y="88" xlink:href="#bt2"/>
221- <use x="1216" y="88" xlink:href="#bt2"/>
222- <use x="1280" y="88" xlink:href="#bt2"/>
223- <use x="1344" y="88" xlink:href="#bt2"/>
224- <use x="1408" y="88" xlink:href="#bt2"/>
225- <use x="1472" y="88" xlink:href="#bt2"/>
226- <use x="1536" y="88" xlink:href="#bt2"/>
227- <use x="1600" y="88" xlink:href="#bt2"/>
228- <use x="1664" y="88" xlink:href="#bt2"/>
229- <use x="1728" y="88" xlink:href="#bt2"/>
230- <use x="1792" y="88" xlink:href="#bt2"/>
231- <use x="1856" y="88" xlink:href="#bt2"/>
232- <use x="1920" y="88" xlink:href="#bt2"/>
233- <use x="1984" y="88" xlink:href="#bt2"/>
234- <use x="2048" y="88" xlink:href="#bt2"/>
235- <use x="2112" y="88" xlink:href="#bt2"/>
236- <use x="2176" y="88" xlink:href="#bt2"/>
237- <use x="2240" y="88" xlink:href="#bt2"/>
238- <use x="2304" y="88" xlink:href="#bt2"/>
239- <use x="2368" y="88" xlink:href="#bt2"/>
240- <use x="2432" y="88" xlink:href="#bt2"/>
241- <use x="2496" y="88" xlink:href="#bt2"/>
242- <use x="2560" y="88" xlink:href="#bt2"/>
243- <use x="2624" y="88" xlink:href="#bt2"/>
244- <use x="2688" y="88" xlink:href="#bt2"/>
245- <use x="979" y="41" xlink:href="#nums"/>
246- <use x="1043" y="41" xlink:href="#nums"/>
247- <use x="1107" y="41" xlink:href="#nums"/>
248- <use x="1171" y="41" xlink:href="#nums"/>
249- <use x="1235" y="41" xlink:href="#nums"/>
250- <use x="1299" y="41" xlink:href="#nums"/>
251- <use x="1363" y="41" xlink:href="#nums"/>
252- <use x="1427" y="41" xlink:href="#nums"/>
253- <use x="1491" y="41" xlink:href="#nums"/>
254- <use x="979" y="129" xlink:href="#nums"/>
255- <use x="1043" y="129" xlink:href="#nums"/>
256- <use x="1107" y="129" xlink:href="#nums"/>
257- <use x="1171" y="129" xlink:href="#nums"/>
258- <use x="1235" y="129" xlink:href="#nums"/>
259- <use x="1299" y="129" xlink:href="#nums"/>
260- <use x="1363" y="129" xlink:href="#nums"/>
261- <use x="1427" y="129" xlink:href="#nums"/>
262- <use x="1491" y="129" xlink:href="#nums"/>
263- <use x="2125" y="6" xlink:href="#season"/>
264- <use x="2189" y="6" xlink:href="#season"/>
265- <use x="2253" y="6" xlink:href="#season"/>
266- <use x="2317" y="6" xlink:href="#season"/>
267- <use x="2125" y="94" xlink:href="#season"/>
268- <use x="2189" y="94" xlink:href="#season"/>
269- <use x="2253" y="94" xlink:href="#season"/>
270- <use x="2317" y="94" xlink:href="#season"/>
271- <use x="2445" y="6" xlink:href="#flower"/>
272- <use x="2509" y="6" xlink:href="#flower"/>
273- <use x="2573" y="6" xlink:href="#flower"/>
274- <use x="2637" y="6" xlink:href="#flower"/>
275- <use x="2445" y="94" xlink:href="#flower"/>
276- <use x="2509" y="94" xlink:href="#flower"/>
277- <use x="2573" y="94" xlink:href="#flower"/>
278- <use x="2637" y="94" xlink:href="#flower"/>
279- <use transform="translate(88.5,16.5) scale(1.22)" xlink:href="#dots"/>
280- <use transform="translate(88.5,42.5) scale(1.22)" xlink:href="#dots"/>
281- <use transform="translate(88.5,104.5) scale(1.22)" xlink:href="#dots"/>
282- <use transform="translate(88.5,130.5) scale(1.22)" xlink:href="#dots"/>
283- <use transform="translate(144.5,12.5)" xlink:href="#dots"/>
284- <use transform="translate(144.5,100.5)" xlink:href="#dots"/>
285- <use transform="translate(154.5,31.5)" xlink:href="#dots2"/>
286- <use transform="translate(154.5,119.5)" xlink:href="#dots2"/>
287- <use transform="translate(164.5,50.5)" xlink:href="#dots"/>
288- <use transform="translate(164.5,138.5)" xlink:href="#dots"/>
289- <use transform="translate(209,15) scale(.94)" xlink:href="#dots"/>
290- <use transform="translate(209,49) scale(.94)" xlink:href="#dots"/>
291- <use transform="translate(209,103) scale(.94)" xlink:href="#dots"/>
292- <use transform="translate(209,137) scale(.94)" xlink:href="#dots"/>
293- <use transform="translate(229,15) scale(.94)" xlink:href="#dots"/>
294- <use transform="translate(229,49) scale(.94)" xlink:href="#dots"/>
295- <use transform="translate(229,103) scale(.94)" xlink:href="#dots"/>
296- <use transform="translate(229,137) scale(.94)" xlink:href="#dots"/>
297- <use transform="translate(273.5,13.5) scale(.88)" xlink:href="#dots"/>
298- <use transform="translate(273.5,51.5) scale(.88)" xlink:href="#dots"/>
299- <use transform="translate(273.5,101.5) scale(.88)" xlink:href="#dots"/>
300- <use transform="translate(273.5,139.5) scale(.88)" xlink:href="#dots"/>
301- <use transform="translate(283.5,32.5) scale(.88)" xlink:href="#dots2"/>
302- <use transform="translate(283.5,120.5) scale(.88)" xlink:href="#dots2"/>
303- <use transform="translate(293.5,13.5) scale(.88)" xlink:href="#dots"/>
304- <use transform="translate(293.5,51.5) scale(.88)" xlink:href="#dots"/>
305- <use transform="translate(293.5,101.5) scale(.88)" xlink:href="#dots"/>
306- <use transform="translate(293.5,139.5) scale(.88)" xlink:href="#dots"/>
307- <use transform="translate(340,14) scale(.72)" xlink:href="#dots"/>
308- <use transform="translate(340,37) scale(.72)" xlink:href="#dots2"/>
309- <use transform="translate(340,54) scale(.72)" xlink:href="#dots2"/>
310- <use transform="translate(340,102) scale(.72)" xlink:href="#dots"/>
311- <use transform="translate(340,125) scale(.72)" xlink:href="#dots2"/>
312- <use transform="translate(340,142) scale(.72)" xlink:href="#dots2"/>
313- <use transform="translate(358,14) scale(.72)" xlink:href="#dots"/>
314- <use transform="translate(358,37) scale(.72)" xlink:href="#dots2"/>
315- <use transform="translate(358,54) scale(.72)" xlink:href="#dots2"/>
316- <use transform="translate(358,102) scale(.72)" xlink:href="#dots"/>
317- <use transform="translate(358,125) scale(.72)" xlink:href="#dots2"/>
318- <use transform="translate(358,142) scale(.72)" xlink:href="#dots2"/>
319- <use transform="translate(399,10) scale(.72)" xlink:href="#dots"/>
320- <use transform="translate(404,41) scale(.72)" xlink:href="#dots2"/>
321- <use transform="translate(404,58) scale(.72)" xlink:href="#dots2"/>
322- <use transform="translate(413,17) scale(.72)" xlink:href="#dots"/>
323- <use transform="translate(427,24) scale(.72)" xlink:href="#dots"/>
324- <use transform="translate(404,129) scale(.72)" xlink:href="#dots2"/>
325- <use transform="translate(404,146) scale(.72)" xlink:href="#dots2"/>
326- <use transform="translate(399,98) scale(.72)" xlink:href="#dots"/>
327- <use transform="translate(422,41) scale(.72)" xlink:href="#dots2"/>
328- <use transform="translate(422,58) scale(.72)" xlink:href="#dots2"/>
329- <use transform="translate(413,105) scale(.72)" xlink:href="#dots"/>
330- <use transform="translate(427,112) scale(.72)" xlink:href="#dots"/>
331- <use transform="translate(422,129) scale(.72)" xlink:href="#dots2"/>
332- <use transform="translate(422,146) scale(.72)" xlink:href="#dots2"/>
333- <use transform="translate(469,10) scale(.72)" xlink:href="#dots"/>
334- <use transform="translate(469,26) scale(.72)" xlink:href="#dots"/>
335- <use transform="translate(469,42) scale(.72)" xlink:href="#dots"/>
336- <use transform="translate(469,58) scale(.72)" xlink:href="#dots"/>
337- <use transform="translate(469,98) scale(.72)" xlink:href="#dots"/>
338- <use transform="translate(469,114) scale(.72)" xlink:href="#dots"/>
339- <use transform="translate(469,130) scale(.72)" xlink:href="#dots"/>
340- <use transform="translate(469,146) scale(.72)" xlink:href="#dots"/>
341- <use transform="translate(485,10) scale(.72)" xlink:href="#dots"/>
342- <use transform="translate(485,26) scale(.72)" xlink:href="#dots"/>
343- <use transform="translate(485,42) scale(.72)" xlink:href="#dots"/>
344- <use transform="translate(485,58) scale(.72)" xlink:href="#dots"/>
345- <use transform="translate(485,98) scale(.72)" xlink:href="#dots"/>
346- <use transform="translate(485,114) scale(.72)" xlink:href="#dots"/>
347- <use transform="translate(485,130) scale(.72)" xlink:href="#dots"/>
348- <use transform="translate(485,146) scale(.72)" xlink:href="#dots"/>
349- <use transform="translate(526,14) scale(.72)" xlink:href="#dots"/>
350- <use transform="translate(526,34) scale(.72)" xlink:href="#dots2"/>
351- <use transform="translate(526,54) scale(.72)" xlink:href="#dots"/>
352- <use transform="translate(526,102) scale(.72)" xlink:href="#dots"/>
353- <use transform="translate(526,122) scale(.72)" xlink:href="#dots2"/>
354- <use transform="translate(526,142) scale(.72)" xlink:href="#dots"/>
355- <use transform="translate(541,14) scale(.72)" xlink:href="#dots"/>
356- <use transform="translate(541,34) scale(.72)" xlink:href="#dots2"/>
357- <use transform="translate(541,54) scale(.72)" xlink:href="#dots"/>
358- <use transform="translate(541,102) scale(.72)" xlink:href="#dots"/>
359- <use transform="translate(541,122) scale(.72)" xlink:href="#dots2"/>
360- <use transform="translate(541,142) scale(.72)" xlink:href="#dots"/>
361- <use transform="translate(556,14) scale(.72)" xlink:href="#dots"/>
362- <use transform="translate(556,34) scale(.72)" xlink:href="#dots2"/>
363- <use transform="translate(556,54) scale(.72)" xlink:href="#dots"/>
364- <use transform="translate(556,102) scale(.72)" xlink:href="#dots"/>
365- <use transform="translate(556,122) scale(.72)" xlink:href="#dots2"/>
366- <use transform="translate(556,142) scale(.72)" xlink:href="#dots"/>
367- <use transform="translate(1550.5,11.9)" xlink:href="#yitiao"/>
368- <use transform="translate(1550.5,99.9)" xlink:href="#yitiao"/>
369- <use transform="translate(1630,9.5)" xlink:href="#bams"/>
370- <use transform="translate(1630,44.5)" xlink:href="#bams"/>
371- <use transform="translate(1630,97.5)" xlink:href="#bams"/>
372- <use transform="translate(1630,132.5)" xlink:href="#bams"/>
373- <use transform="translate(1682,44.5)" xlink:href="#bams"/>
374- <use transform="translate(1682,132.5)" xlink:href="#bams"/>
375- <use transform="translate(1694,9.5)" xlink:href="#bams"/>
376- <use transform="translate(1694,97.5)" xlink:href="#bams"/>
377- <use transform="translate(1705,44.5)" xlink:href="#bams"/>
378- <use transform="translate(1705,132.5)" xlink:href="#bams"/>
379- <use transform="translate(1746,9.5)" xlink:href="#bams"/>
380- <use transform="translate(1746,44.5)" xlink:href="#bams"/>
381- <use transform="translate(1746,97.5)" xlink:href="#bams"/>
382- <use transform="translate(1746,132.5)" xlink:href="#bams"/>
383- <use transform="translate(1770,9.5)" xlink:href="#bams"/>
384- <use transform="translate(1770,44.5)" xlink:href="#bams"/>
385- <use transform="translate(1770,97.5)" xlink:href="#bams"/>
386- <use transform="translate(1770,132.5)" xlink:href="#bams"/>
387- <use transform="translate(1810,10.5) scale(.91,.925)" xlink:href="#bams"/>
388- <use transform="translate(1810,45.5) scale(.91,.925)" xlink:href="#bams"/>
389- <use transform="translate(1810,98.5) scale(.91,.925)" xlink:href="#bams"/>
390- <use transform="translate(1810,133.5) scale(.91,.925)" xlink:href="#bams"/>
391- <use transform="translate(1822.5,116) scale(.91,.925)" xlink:href="#bams2"/>
392- <use transform="translate(1822.5,28) scale(.91,.925)" xlink:href="#bams2"/>
393- <use transform="translate(1835,10.5) scale(.91,.925)" xlink:href="#bams"/>
394- <use transform="translate(1835,45.5) scale(.91,.925)" xlink:href="#bams"/>
395- <use transform="translate(1835,98.5) scale(.91,.925)" xlink:href="#bams"/>
396- <use transform="translate(1835,133.5) scale(.91,.925)" xlink:href="#bams"/>
397- <use transform="translate(1875,12.5) scale(.82,.85)" xlink:href="#bams"/>
398- <use transform="translate(1875,45.5) scale(.82,.85)" xlink:href="#bams"/>
399- <use transform="translate(1875,100.5) scale(.82,.85)" xlink:href="#bams"/>
400- <use transform="translate(1875,133.5) scale(.82,.85)" xlink:href="#bams"/>
401- <use transform="translate(1887,12.5) scale(.82,.85)" xlink:href="#bams"/>
402- <use transform="translate(1887,45.5) scale(.82,.85)" xlink:href="#bams"/>
403- <use transform="translate(1887,100.5) scale(.82,.85)" xlink:href="#bams"/>
404- <use transform="translate(1887,133.5) scale(.82,.85)" xlink:href="#bams"/>
405- <use transform="translate(1899,12.5) scale(.82,.85)" xlink:href="#bams"/>
406- <use transform="translate(1899,45.5) scale(.82,.85)" xlink:href="#bams"/>
407- <use transform="translate(1899,100.5) scale(.82,.85)" xlink:href="#bams"/>
408- <use transform="translate(1899,133.5) scale(.82,.85)" xlink:href="#bams"/>
409- <use transform="translate(1939.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
410- <use transform="translate(1951.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
411- <use transform="translate(1963.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
412- <use transform="translate(1939.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
413- <use transform="translate(1951.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
414- <use transform="translate(1963.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
415- <use transform="translate(1939.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
416- <use transform="translate(1951.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
417- <use transform="translate(1963.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
418- <use transform="translate(1939.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
419- <use transform="translate(1951.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
420- <use transform="translate(1963.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
421- <use transform="translate(1951.5,12.5) scale(.73,.66)" xlink:href="#bams2"/>
422- <use transform="translate(1951.5,100.5) scale(.73,.66)" xlink:href="#bams2"/>
423- <use transform="translate(2003.5,13.5) scale(.73,.77)" xlink:href="#bams"/>
424- <use transform="translate(2003.5,46.5) scale(.73,.77)" xlink:href="#bams"/>
425- <use transform="translate(2003.5,101.5) scale(.73,.77)" xlink:href="#bams"/>
426- <use transform="translate(2003.5,134.5) scale(.73,.77)" xlink:href="#bams"/>
427- <use transform="translate(2027.5,13.5) scale(.73,.77)" xlink:href="#bams"/>
428- <use transform="translate(2027.5,46.5) scale(.73,.77)" xlink:href="#bams"/>
429- <use transform="translate(2027.5,101.5) scale(.73,.77)" xlink:href="#bams"/>
430- <use transform="translate(2027.5,134.5) scale(.73,.77)" xlink:href="#bams"/>
431- <use transform="translate(2067.5,12.5) scale(.73,.66)" xlink:href="#bams"/>
432- <use transform="translate(2067.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
433- <use transform="translate(2067.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
434- <use transform="translate(2067.5,100.5) scale(.73,.66)" xlink:href="#bams"/>
435- <use transform="translate(2067.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
436- <use transform="translate(2067.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
437- <use transform="translate(2079.5,12.5) scale(.73,.66)" xlink:href="#bams2"/>
438- <use transform="translate(2079.5,31.5) scale(.73,.66)" xlink:href="#bams2"/>
439- <use transform="translate(2079.5,50.5) scale(.73,.66)" xlink:href="#bams2"/>
440- <use transform="translate(2079.5,100.5) scale(.73,.66)" xlink:href="#bams2"/>
441- <use transform="translate(2079.5,119.5) scale(.73,.66)" xlink:href="#bams2"/>
442- <use transform="translate(2079.5,138.5) scale(.73,.66)" xlink:href="#bams2"/>
443- <use transform="translate(2091.5,12.5) scale(.73,.66)" xlink:href="#bams"/>
444- <use transform="translate(2091.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
445- <use transform="translate(2091.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
446- <use transform="translate(2091.5,100.5) scale(.73,.66)" xlink:href="#bams"/>
447- <use transform="translate(2091.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
448- <use transform="translate(2091.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
449- <path fill="#006C00" d="M2031.75,29.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.58-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8s-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8S2032.33,30.64,2031.75,29.81z M2021.19,22.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58c0.8-0.56,1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.3,0.21,0.65,0.01,0.79C2021.73,22.9,2021.4,22.77,2021.19,22.48z M2021.5,26.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2022.79,26.33,2021.71,26.71,2021.5,26.4z M2028.87,32.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.29-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2030.15,31.39,2029.67,32.1,2028.87,32.67z"/>
450- <path fill="#006C00" d="M2021.52,16.4c-1.81-1.27-3.75-1.62-4.32-0.8c-0.41,0.58-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.35-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.63,4.32,0.8c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2024.33,19.36,2023.33,17.67,2021.52,16.4z M2013.92,28.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.31c-0.22,0.31-1.04,0.11-1.85-0.46s-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2014.13,27.38,2014.13,27.74,2013.92,28.03z M2017.5,26.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2017,25.21,2017.72,26.09,2017.5,26.4z M2021.93,18.91c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.3-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46S2022.15,18.6,2021.93,18.91z"/>
451- <path fill="#006C00" d="M2031.75,117.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.58-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8c-1.81,1.27-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2031.33,120.33,2032.33,118.64,2031.75,117.81z M2021.19,110.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.3,0.21,0.65,0.01,0.79C2021.73,110.9,2021.4,110.77,2021.19,110.48z M2021.5,114.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2022.79,114.34,2021.71,114.71,2021.5,114.4z M2028.87,120.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.31l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2030.15,119.39,2029.67,120.1,2028.87,120.67z"/>
452- <path fill="#006C00" d="M2021.52,104.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.35-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2024.33,107.36,2023.33,105.67,2021.52,104.4z M2013.92,116.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.31c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.29,0.54-0.42,0.74-0.28C2014.13,115.38,2014.13,115.74,2013.92,116.03z M2017.5,114.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2017,113.21,2017.72,114.09,2017.5,114.4z M2021.93,106.91c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.3-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2021.67,105.9,2022.15,106.61,2021.93,106.91z"/>
453- <path fill="#006C00" d="M2023.75,148.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8s-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2023.33,151.33,2024.33,149.64,2023.75,148.81z M2013.19,141.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.29,0.21,0.65,0.01,0.79C2013.73,141.9,2013.4,141.77,2013.19,141.48z M2013.5,145.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96S2013.71,145.71,2013.5,145.4zM2020.87,151.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2022.15,150.4,2021.67,151.1,2020.87,151.67z"/>
454- <path fill="#006C00" d="M2029.52,135.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.35,0.53,2.75-0.06c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2032.33,138.36,2031.33,136.67,2029.52,135.4z M2021.92,147.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.3c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2022.13,146.39,2022.13,146.74,2021.92,147.03z M2025.5,145.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2025,144.21,2025.72,145.09,2025.5,145.4z M2029.93,137.92c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.29-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2029.67,136.9,2030.15,137.61,2029.93,137.92z"/>
455- <path fill="#006C00" d="M2023.75,60.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.59-1.51-0.56-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8c-1.81,1.27-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2023.33,63.33,2024.33,61.64,2023.75,60.81z M2013.19,53.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22 c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.29,0.21,0.65,0.01,0.79C2013.73,53.9,2013.4,53.77,2013.19,53.48z M2013.5,57.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2014.79,57.34,2013.71,57.71,2013.5,57.4z M2020.87,63.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2022.15,62.4,2021.67,63.1,2020.87,63.67z"/>
456- <path fill="#006C00" d="M2029.52,47.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.35,0.53,2.75-0.06c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2032.33,50.36,2031.33,48.67,2029.52,47.4z M2021.92,59.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.3c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2022.13,58.39,2022.13,58.74,2021.92,59.03z M2025.5,57.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2025,56.21,2025.72,57.09,2025.5,57.4z M2029.93,49.92c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.29-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2029.67,48.9,2030.15,49.61,2029.93,49.92z"/>
457- <path fill="#20208C" d="M1171.2,12.1l6.8,19.4l0.3-0.1c3.6-1.2,13.3-4.1,15.3-2.6c0.2,0.2,0.3,0.4,0.3,0.7l-0.1,0.8l0.6-0.5c6.8-5.3,9.4-14,9.4-14.1l0-0.2l-0.1-0.1c-4.9-7.3-24.9-1.3-25.7-1l-0.2,0.1l0,0.2c0,0-0.2,1.7-0.2,2.4c-0.4-1.5-1.1-4.1-1.1-4.1l-5.6-1.3L1171.2,12.1z M1178.1,17.8c1.2-0.3,2.6-0.7,4-1c0,0,0,0,0,0s1.9,9.3,2,9.9c-0.5,0.1-3,0.8-3.6,1c-0.1-0.4-1.4-5.6-2.5-9.9C1178,17.9,1178.1,17.8,1178.1,17.8z M1189.8,15.5c0,0.1-0.4,9.6-0.4,10.2c-0.4,0.1-2.1,0.4-2.6,0.5c-0.1-0.6-1.8-9.6-1.8-9.9C1186.7,15.9,1188.3,15.7,1189.8,15.5z M1197.7,16.4c-0.2,2.3-1.1,8.1-2.6,9.9c-0.1-0.1,0-0.2-0.1-0.3c-0.5-0.5-1.6-0.7-3.3-0.6c0.3-4.5,1-7.5,1.9-7.9l0.7-0.3c0,0-3.5-1.7-3.8-1.8C1194.3,15,1197.3,15.1,1197.7,16.4z"/>
458- <path fill="#20208C" d="M1003.8,18.1c-0.3,0.2-0.4,0.5-0.4,0.8c0,0.3,0.1,0.5,0.1,0.7c-2.6,0.2-19.7,1.8-23.9,1.4l-0.4,0l1.7,6l0.3-0.1c3.3-1.1,24.1-2.4,29-1.4c0.8,0.1,1.2,0,1.4-0.4c0.5-1-1.9-3.5-3.4-4.8C1007.3,19.4,1004.8,17.5,1003.8,18.1z"/>
459- <path fill="#20208C" d="M1067.9,23.9c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.2,0,0.5,0.1,0.7c-2.8,0.3-19.9,1.9-24.1,1.4l-0.5-0.1l1.7,6.2l0.4-0.1c3.3-1.2,24.3-2.4,29.3-1.4c0.8,0.2,1.3,0,1.5-0.4c0.7-1.3-3-4.6-3.4-4.9C1071.5,25.2,1069,23.2,1067.9,23.9z"/>
460- <path fill="#20208C" d="M1060.1,12.9c-2.6,0.9-6.2,2.2-12.3,2.2l-0.7,0l2.9,4.2l16.7-2.2l0.5-3.1l-0.1-0.1C1064.7,11.2,1063,11.8,1060.1,12.9z"/>
461- <path fill="#20208C" d="M1131.7,26.3c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.3,0,0.5,0.1,0.7c-2.7,0.3-19.3,1.9-23.4,1.4l-0.5-0.1l1.7,6.2l0.3-0.1c3.2-1.2,23.6-2.4,28.4-1.4c0.8,0.2,1.3,0,1.5-0.4c0.6-1.3-2.9-4.6-3.3-5C1135.1,27.7,1132.7,25.6,1131.7,26.3z"/>
462- <polygon fill="#20208C" points="1125.3,19 1114,20.4 1115.6,24.7 1127.6,23.3 1125.6,19 "/>
463- <path fill="#20208C" d="M1122.9,8.6c-3,1.1-7,2.6-14,2.6l-0.6,0l3.2,4.9l18.9-2.6l0.6-3.6l-0.1-0.1C1128.2,6.6,1126.3,7.3,1122.9,8.6z"/>
464- <path fill="#20208C" d="M1245.8,10.3l-10.3,10.5l0.4,0.5c0,0,5.6-3.2,7-4c-0.6,1.5-4.3,11.6-4.3,11.6l3.3,3.5c0,0,2.4-15.6,2.4-15.9c0.3-0.2,5.1-2.9,5.1-2.9l-3.4-3.7L1245.8,10.3z"/>
465- <path fill="#20208C" d="M1258.6,10.2h-1.9c0,0-0.6,1.3-0.9,1.9c0,0-0.1-0.1-0.1-0.1l-5.9,3.5l0.2,0.7c0,0,3-0.2,3.9-0.2c-0.4,0.8-2.2,4.5-2.3,4.8c-0.4,0-6.3,0.5-6.3,0.5l-0.1,0.7l4,1.6c0,0,0.7-0.3,1.3-0.5c-0.6,1.2-2.5,5.2-2.7,5.5c-1.3,0.1-2.3,0.1-2.9,0l-0.4,0l0.5,3.7l0.3-0.1c1.4-0.2,12.2-1.6,14.1-1.7c1.8-0.1,7.3,2.1,7.4,2.1l0.7,0.3l-0.2-0.7c-0.1-0.4-1.3-3.9-2.2-4.8l-0.1-0.1l-0.1,0c-0.8-0.2-3.6,0-7.5,0.4c0.9-1,4.3-4.6,4.3-4.6l-3.2-3.4c0,0-2.9,7.8-3,8.2c-0.3,0-2.7,0.3-3.5,0.3c0.4-0.9,2.5-6.5,2.6-6.7c0.3-0.1,3.2-1.2,3.2-1.2l-0.2-0.7c0,0-1.7,0.3-2.5,0.4c0.3-0.9,1.6-4.1,1.7-4.4c0.2,0,1.2-0.1,1.2-0.1s-0.6-0.9-0.7-1.1c0.1-0.3,1.7-4.4,1.7-4.4H1258.6z"/>
466- <path fill="#20208C" d="M1313.2,9.8c0,0.3,0,0.6,0,1c0,1.6-0.2,3.7-0.7,6.2c-5.9,0.7-11.1,1.5-11.2,1.5l-0.3,0V21c0,0,9.9-0.4,10.7-0.5c-0.2,0.7-1,4-1,4l0.6,0.3c0,0,2.9-4.1,3.1-4.4c11.7-0.3,14.4,0.2,15,0.5l0.6,0.4l-0.6-3.5c-0.3-1-2-2.1-12.2-1.3c0.6-0.9,3.2-4.6,3.2-4.6l-7.2-2.7L1313.2,9.8z"/>
467- <path fill="#20208C" d="M1307.6,24.8c0,0,0.8,4.7-1.6,8.5l0.5,0.5c4.4-2.9,5.9-6.6,5.9-6.7l0.1-0.3l-5-2.6L1307.6,24.8z"/>
468- <path fill="#20208C" d="M1317.1,23.7c0.2,0.3,5.9,7.7,7.9,9.6l0.1,0.1l0.2,0c0.4,0,0.8-0.3,1.1-0.7c0.8-1.1,1.3-3.2,1-4.3c-0.4-1.3-7.6-4.3-9.8-5.2l-1.2-0.5L1317.1,23.7z"/>
469- <path fill="#20208C" d="M1387.2,11.7L1387.2,11.7c-0.2,0.4-1.5,1.9-8.3,7.6c-0.1-0.7-0.2-1.6-0.2-1.6c-0.2-1.9-0.4-4.1-0.8-6.5l-0.1-0.3l-3.2,0.3l0,0.3c0,0.6,0,1.1,0,1.7c0,3.3,0.3,6.2,0.9,8.9c-0.3,0.2-10,8.2-10,8.2l0.3,0.3l1.9,1.8c0,0,7.9-6.9,8.6-7.6c0.7,2.2,1.6,4,2.7,5c2,2,4.2,1.7,7,1.3c2-0.3,4.3-0.6,7-0.2l0.5,0.1l-0.6-2.8c0-0.6-0.3-1.1-0.7-1.5c-1.3-0.9-3.8-0.4-6.5,0.1c-1.3,0.3-2.7,0.5-3.9,0.6c-1.3-1.2-2.1-2.8-2.6-5.2c7.5-6.2,10.3-7.8,11.3-8.1l0.2-0.1l0-0.3c0-0.2,0-0.3,0-0.4c0-1.8-0.9-2.8-1.7-3C1388.5,10.4,1387.7,10.6,1387.2,11.7z"/>
470- <path fill="#20208C" d="M1432.4,19c0,3.7-4.5,9.2-4.7,9.4l-0.7,0.9l1.1-0.3c5.2-1.4,8.2-5,8.3-5.1l0.2-0.2l-4.2-5.7V19z"/>
471- <path fill="#20208C" d="M1437.9,11.3l-2,0.3l0.3,0.5c0.1,0.2,9.3,15.7,10.6,19.3l0.1,0.3l0.3-0.1c2.2-0.4,4-0.2,5.8,0c2.2,0.2,4.3,0.4,6.7-0.4l0.4-0.1l-0.2-0.4c-1.2-2-2.5-2.4-4.2-2.9c-3.2-0.9-8-2.3-17.3-16.3l-0.1-0.2L1437.9,11.3z"/>
472- <path fill="#20208C" d="M1520.6,23.6c0,0-2.9,3.2-3.1,3.4c-3.2,0.7-8.4,1.2-8.8-0.4c-0.5-1.9,3.4-10.4,4.9-13.6l0.2-0.3l-5.6-2.8l-0.1,0.4c-0.1,0.2-0.4,1.3-0.9,3c-2,0-4.2,0-4.5,0c0-0.4-0.1-1.3-0.1-1.3l0,0c-1.4-2.3-7.4-0.7-8.1-0.6l-0.4,0.1l0.2,0.4c0.2,0.4,0.4,0.9,0.5,1.4c-0.8,0-3.8,0-3.8,0l1.9,3.6c0,0,1.8-0.3,2.4-0.4c0,0.2,0,0.5,0,0.7c0,6.2-2.7,14.1-2.7,14.2l0.6,0.3c0.3-0.4,7.9-9.5,9.3-16.4c0.2,0,2.4-0.4,4.4-0.7c-1.2,4.7-2.6,12.1-0.3,14.8c3.4,4,16.6,3.7,17.1,3.5l0.3-0.1L1520.6,23.6z"/>
473- <path fill="#20208C" d="M1171.2,100.1l6.8,19.4l0.3-0.1c3.6-1.2,13.3-4.1,15.3-2.6c0.2,0.2,0.3,0.4,0.3,0.7l-0.1,0.8l0.6-0.5c6.8-5.3,9.4-14,9.4-14.1l0-0.2l-0.1-0.1c-4.9-7.3-24.9-1.3-25.7-1l-0.2,0.1l0,0.2c0,0-0.2,1.7-0.2,2.4c-0.4-1.5-1.1-4.1-1.1-4.1l-5.6-1.3L1171.2,100.1z M1178.1,105.8c1.2-0.3,2.6-0.7,4-1c0,0,0,0,0,0s1.9,9.3,2,9.9c-0.5,0.1-3,0.8-3.6,1c-0.1-0.4-1.4-5.6-2.5-9.9C1178,105.9,1178.1,105.8,1178.1,105.8z M1189.8,103.5c0,0.1-0.4,9.6-0.4,10.2c-0.4,0.1-2.1,0.4-2.6,0.5c-0.1-0.6-1.8-9.6-1.8-9.9C1186.7,103.9,1188.3,103.7,1189.8,103.5z M1197.7,104.4c-0.2,2.3-1.1,8.1-2.6,9.9c-0.1-0.1,0-0.2-0.1-0.3c-0.5-0.5-1.6-0.7-3.3-0.6c0.3-4.5,1-7.5,1.9-7.9l0.7-0.3c0,0-3.5-1.7-3.8-1.8C1194.3,103,1197.3,103.1,1197.7,104.4z"/>
474- <path fill="#20208C" d="M1003.8,106.1c-0.3,0.2-0.4,0.5-0.4,0.8c0,0.3,0.1,0.5,0.1,0.7c-2.6,0.2-19.7,1.8-23.9,1.4l-0.4,0l1.7,6l0.3-0.1c3.3-1.1,24.1-2.4,29-1.4c0.8,0.1,1.2,0,1.4-0.4c0.5-1-1.9-3.5-3.4-4.8C1007.3,107.4,1004.8,105.5,1003.8,106.1z"/>
475- <path fill="#20208C" d="M1067.9,111.9c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.2,0,0.5,0.1,0.7c-2.8,0.3-19.9,1.9-24.1,1.4l-0.5-0.1l1.7,6.2l0.4-0.1c3.3-1.2,24.3-2.4,29.3-1.4c0.8,0.2,1.3,0,1.5-0.4c0.7-1.3-3-4.6-3.4-4.9C1071.5,113.2,1069,111.2,1067.9,111.9z"/>
476- <path fill="#20208C" d="M1060.1,100.9c-2.6,0.9-6.2,2.2-12.3,2.2l-0.7,0l2.9,4.2l16.7-2.2l0.5-3.1l-0.1-0.1C1064.7,99.2,1063,99.8,1060.1,100.9z"/>
477- <path fill="#20208C" d="M1131.7,114.3c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.3,0,0.5,0.1,0.7c-2.7,0.3-19.3,1.9-23.4,1.4l-0.5-0.1l1.7,6.2l0.3-0.1c3.2-1.2,23.6-2.4,28.4-1.4c0.8,0.2,1.3,0,1.5-0.4c0.6-1.3-2.9-4.6-3.3-5C1135.1,115.7,1132.7,113.6,1131.7,114.3z"/>
478- <polygon fill="#20208C" points="1125.3,107 1114,108.4 1115.6,112.7 1127.6,111.3 1125.6,107 "/>
479- <path fill="#20208C" d="M1122.9,96.6c-3,1.1-7,2.6-14,2.6l-0.6,0l3.2,4.9l18.9-2.6l0.6-3.6l-0.1-0.1C1128.2,94.6,1126.3,95.3,1122.9,96.6z"/>
480- <path fill="#20208C" d="M1245.8,98.3l-10.3,10.5l0.4,0.5c0,0,5.6-3.2,7-4c-0.6,1.5-4.3,11.6-4.3,11.6l3.3,3.5c0,0,2.4-15.6,2.4-15.9c0.3-0.2,5.1-2.9,5.1-2.9l-3.4-3.7L1245.8,98.3z"/>
481- <path fill="#20208C" d="M1258.6,98.2h-1.9c0,0-0.6,1.3-0.9,1.9c0,0-0.1-0.1-0.1-0.1l-5.9,3.5l0.2,0.7c0,0,3-0.2,3.9-0.2c-0.4,0.8-2.2,4.5-2.3,4.8c-0.4,0-6.3,0.5-6.3,0.5l-0.1,0.7l4,1.6c0,0,0.7-0.3,1.3-0.5c-0.6,1.2-2.5,5.2-2.7,5.5c-1.3,0.1-2.3,0.1-2.9,0l-0.4,0l0.5,3.7l0.3-0.1c1.4-0.2,12.2-1.6,14.1-1.7c1.8-0.1,7.3,2.1,7.4,2.1l0.7,0.3l-0.2-0.7c-0.1-0.4-1.3-3.9-2.2-4.8l-0.1-0.1l-0.1,0c-0.8-0.2-3.6,0-7.5,0.4c0.9-1,4.3-4.6,4.3-4.6l-3.2-3.4c0,0-2.9,7.8-3,8.2c-0.3,0-2.7,0.3-3.5,0.3c0.4-0.9,2.5-6.5,2.6-6.7c0.3-0.1,3.2-1.2,3.2-1.2l-0.2-0.7c0,0-1.7,0.3-2.5,0.4c0.3-0.9,1.6-4.1,1.7-4.4c0.2,0,1.2-0.1,1.2-0.1s-0.6-0.9-0.7-1.1c0.1-0.3,1.7-4.4,1.7-4.4H1258.6z"/>
482- <path fill="#20208C" d="M1313.2,97.8c0,0.3,0,0.6,0,1c0,1.6-0.2,3.7-0.7,6.2c-5.9,0.7-11.1,1.5-11.2,1.5l-0.3,0v2.5c0,0,9.9-0.4,10.7-0.5c-0.2,0.7-1,4-1,4l0.6,0.3c0,0,2.9-4.1,3.1-4.4c11.7-0.3,14.4,0.2,15,0.5l0.6,0.4l-0.6-3.5c-0.3-1-2-2.1-12.2-1.3c0.6-0.9,3.2-4.6,3.2-4.6l-7.2-2.7L1313.2,97.8z"/>
483- <path fill="#20208C" d="M1307.6,112.8c0,0,0.8,4.7-1.6,8.5l0.5,0.5c4.4-2.9,5.9-6.6,5.9-6.7l0.1-0.3l-5-2.6L1307.6,112.8z"/>
484- <path fill="#20208C" d="M1317.1,111.7c0.2,0.3,5.9,7.7,7.9,9.6l0.1,0.1l0.2,0c0.4,0,0.8-0.3,1.1-0.7c0.8-1.1,1.3-3.2,1-4.3c-0.4-1.3-7.6-4.3-9.8-5.2l-1.2-0.5L1317.1,111.7z"/>
485- <path fill="#20208C" d="M1387.2,99.7L1387.2,99.7c-0.2,0.4-1.5,1.9-8.3,7.6c-0.1-0.7-0.2-1.6-0.2-1.6c-0.2-1.9-0.4-4.1-0.8-6.5l-0.1-0.3l-3.2,0.3l0,0.3c0,0.6,0,1.1,0,1.7c0,3.3,0.3,6.2,0.9,8.9c-0.3,0.2-10,8.2-10,8.2l0.3,0.3l1.9,1.8c0,0,7.9-6.9,8.6-7.6c0.7,2.2,1.6,4,2.7,5c2,2,4.2,1.7,7,1.3c2-0.3,4.3-0.6,7-0.2l0.5,0.1l-0.6-2.8c0-0.6-0.3-1.1-0.7-1.5c-1.3-0.9-3.8-0.4-6.5,0.1c-1.3,0.3-2.7,0.5-3.9,0.6c-1.3-1.2-2.1-2.8-2.6-5.2c7.5-6.2,10.3-7.8,11.3-8.1l0.2-0.1l0-0.3c0-0.2,0-0.3,0-0.4c0-1.8-0.9-2.8-1.7-3C1388.5,98.4,1387.7,98.6,1387.2,99.7z"/>
486- <path fill="#20208C" d="M1432.4,107c0,3.7-4.5,9.2-4.7,9.4l-0.7,0.9l1.1-0.3c5.2-1.4,8.2-5,8.3-5.1l0.2-0.2l-4.2-5.7V107z"/>
487- <path fill="#20208C" d="M1437.9,99.3l-2,0.3l0.3,0.5c0.1,0.2,9.3,15.7,10.6,19.3l0.1,0.3l0.3-0.1c2.2-0.4,4-0.2,5.8,0c2.2,0.2,4.3,0.4,6.7-0.4l0.4-0.1l-0.2-0.4c-1.2-2-2.5-2.4-4.2-2.9c-3.2-0.9-8-2.3-17.3-16.3l-0.1-0.2L1437.9,99.3z"/>
488- <path fill="#20208C" d="M1520.6,111.6c0,0-2.9,3.2-3.1,3.4c-3.2,0.7-8.4,1.2-8.8-0.4c-0.5-1.9,3.4-10.4,4.9-13.6l0.2-0.3l-5.6-2.8l-0.1,0.4c-0.1,0.2-0.4,1.3-0.9,3c-2,0-4.2,0-4.5,0c0-0.4-0.1-1.3-0.1-1.3l0,0c-1.4-2.3-7.4-0.7-8.1-0.6l-0.4,0.1l0.2,0.4c0.2,0.4,0.4,0.9,0.5,1.4c-0.8,0-3.8,0-3.8,0l1.9,3.6c0,0,1.8-0.3,2.4-0.4c0,0.2,0,0.5,0,0.7c0,6.2-2.7,14.1-2.7,14.2l0.6,0.3c0.3-0.4,7.9-9.5,9.3-16.4c0.2,0,2.4-0.4,4.4-0.7c-1.2,4.7-2.6,12.1-0.3,14.8c3.4,4,16.6,3.7,17.1,3.5l0.3-0.1L1520.6,111.6z"/>
489- <path fill="#101040" d="M632.3,142.7c-2-0.9-11.8,3.3-11.8,3.3l0.5-20.7c0,0,5.3-4.3,9.6-1.7l-1.2-5c-1.4-0.2-8.4,5.5-8.4,5.5s-0.6-18.8,4.8-19.9c0,0-8.9-2.6-9.7-2.2c-0.4,0.2-0.4,13.8-0.2,27.1c-3.2,3.1-5.9,6.2-7.1,7.3l0.4-28.5c0,0-4.7-2.6-5.8-1.4l0.2,14.8c-0.3-0.5-0.7-0.9-1-1.3c-1-1.3-6.1-4.2-6.1-4.2s1.7,5.6,2.5,6.6c0.7,0.9,3,2.9,4.6,2.2l0.2,16.5c-3.4,3-7.9,6.8-8.5,5.4c-1.5-3.2-4.7-3-5.2-1.5c-0.5,1.6,4.2,10.2,5.8,10.1c0.8-0.1,4.4-4.9,8-10l0.1,5.3c1.6,2.1,3.8,0.5,4.6-0.7l0.3-11c1.1-1.6,4-4.5,7.1-7.9c0.2,12.1,0.3,22.8,0.3,22.8c4.9-5.2,16.7-4,16.7-4L632.3,142.7z"/>
490- <path fill="#101040" d="M688.4,14.3c-8.5,5.7-21.5,6.9-28.8,5.9l-0.9,2c11.7,3.8,26.4,1,31.2-3.2L688.4,14.3z"/>
491- <path fill="#101040" d="M692.5,39.2c-3.6-0.9-7.3-1.4-10.8-1.7c0.8-2.9,1.2-6,1-9.2l-7.8,2c0.6,1.7,0.6,4.3,0.2,7c-1.6,0-3.1,0.1-4.5,0.3c0.2-2.7,0.4-3.3-0.5-6l-4.7,1.3c1,1.8,1.5,2.3,1.8,5.1c-3.4,1-5.7,1.8-6.9,3.2c-5.1,6.1,0,23.2,0,23.2s-0.2-15.4,2-17c2.2-1.6,4.7-1.8,4.7-1.8c0.1,1.5-1.6,6.7-1.6,6.7c2.2-2.7,3.2-3.6,4.3-7.2c1.6-0.5,2.5-0.8,4.1-1c-1.6,6.1-4.5,10.9-4.5,10.9l7.4,2c-4.1-0.1-13.3,5.1-13.3,5.1s16.2-4,20.8-1.1l1.2-2.9c-3.4-2.7-10.5-4.9-10.5-4.9s2.2-4.1,4.6-9.4c2.3,0.1,5.6,0.3,7.7,0.9c9.2,4.1-1.7,22.4-1.7,22.4S705.7,45.9,692.5,39.2z"/>
492- <path fill="#101040" d="M735.3,14.2c0.4,0.5,0.9,2.3,0.9,8.6c0,0.9,0,1.8,0,2.8c-3.4,0.5-6.3,1-6.8,1.2l-0.6,0.2l2.8,2.7l0.2-0.1c1.1-0.3,2.6-0.5,4.2-0.7c0,0.7-0.4,7.3-0.4,7.9c-4.9,0.4-9.2,1.1-12.7,1.9c-3,0.8-4.8,2.6-5.2,5.4c-0.1,0.4-0.1,0.9-0.1,1.4c0,7.1,6.9,16.9,7,16.9l0.8,0.5l-0.3-0.9c0-0.1-2.8-9.4-2.8-15.6c0-1.8,0.2-3.3,0.8-4.3c0.4-0.6,0.8-0.9,1.4-1c1.3-0.2,2.7-0.4,4-0.6c0,0,3.1,4,3.7,4.7c-0.5,0.1-1.4,0.3-1.4,0.3l0,0.3l0.2,2.2l0.4-0.1c0.6-0.2,1.8-0.5,3.2-0.7c0,0.6,0.1,2.4,0.2,2.9c-1.7,0.5-3.2,1.1-3.8,1.4l-0.4,0.2l1.5,1.6l0.2-0.1c0.7-0.3,1.7-0.6,2.7-0.9c0,0.7,0.3,5.6,0.3,5.6l0.7,0c0,0,1.1-5.5,1.2-6c1.8-0.2,3.5-0.1,4.8,0.2l0.4,0.1v-3l-0.2-0.1c-0.8-0.4-2.3-0.4-4.5,0.1c0.1-0.7,0.4-2.1,0.5-2.5c1.6-0.1,2.9-0.2,3.8-0.1l0.5,0.1l-0.7-2.6c0,0-0.9,0-1.4,0.1c0.8-0.9,3.5-4.4,3.7-4.6c0.3,0,3.9,0.1,3.9,0.1c1.6,0.1,3,0.7,4.1,2c1.3,1.5,2,3.9,2,6.3c0,0.5,0,1.1-0.1,1.6l-0.1,0.7c-0.7,5.1-1.3,8.8-3.1,9.6c-1.2,0.5-3.1-0.3-5.9-2.4l-0.5,0.5l5.5,9.6l0.3-0.1c6.7-2.4,7.7-3.8,10.4-15.4c1.2-5.1,0.8-9.1-1.3-11.8c-2.5-3.3-6.3-3.7-6.5-3.7c-0.1,0-6.7-0.3-14.6,0.2c0.2-0.8,2-7.5,2.1-7.9c2.5,0,4.7,0.1,5.7,0.5l0.4,0.1l0.4-4.7l-0.4,0c-1.1,0-2.8,0.1-5.1,0.3c1.3-4.4,1.9-5.8,2.1-6.2l0.2-0.3l-8.1-4.7L735.3,14.2z M739.7,39.5c-0.1,0.7-0.7,4.3-0.7,4.7c0,0-0.1,0-0.1,0c0-0.1,0.1-0.4,0.1-0.4l-4.4,0.4c0,0,0,0.5,0,0.6c-0.4,0.1-1,0.2-1.4,0.2c0-0.7,0-5,0-5.1C735.4,39.8,737.5,39.6,739.7,39.5z"/>
493- <path fill="#101040" d="M804.5,41.3l0.1-6.4l4.4-0.3l-0.3-2.4l-4.1,1.2l0-5.7c2.4,0.3,5,0.8,7.1,0.9c4.3,0.3-1.2,14.1-1.2,14.1s16.3-14.4,2.6-18.1c-1.4,0-4.9,0.2-8.4,0.4l0-3.9l7.5-1.9l-7.5-0.1l0-4l-4.6-3.4l0.5,8.7l-4,2.7l4.1-1l0.2,3.2c-1.8,0.2-3.4,0.3-4.3,0.5c-11.5,2.2,1,17.2,1,17.2s-4-14,0.5-15.3c0.9-0.3,1.8-0.3,2.9-0.3l0.4,7l-3,0.9l3-0.2l0.2,4.1c-9.2,12.1-19.7,16.6-19.7,16.6s10.2-1.1,20-11.9l1,18.7c-3.7-1.5-7-3.6-7-3.6s2.4,7.1,7.5,10.1l4.3-5.1c-1-0.2-2.1-0.5-3.2-0.9l0.2-18.9c2.7,3.2,9,10.5,11.7,11l9-3C821.9,51.6,807.7,43.3,804.5,41.3z"/>
494- <path fill="#101040" d="M688.4,102.3c-8.5,5.7-21.5,6.9-28.8,5.9l-0.9,2c11.7,3.8,26.4,1,31.2-3.2L688.4,102.3z"/>
495- <path fill="#101040" d="M692.5,127.2c-3.6-0.9-7.3-1.4-10.8-1.7c0.8-2.9,1.2-6,1-9.2l-7.8,2c0.6,1.7,0.6,4.3,0.2,7c-1.6,0-3.1,0.1-4.5,0.3c0.2-2.7,0.4-3.3-0.5-6l-4.7,1.3c1,1.8,1.5,2.3,1.8,5.1c-3.4,1-5.7,1.8-6.9,3.2c-5.1,6.1,0,23.2,0,23.2s-0.2-15.4,2-17c2.2-1.6,4.7-1.8,4.7-1.8c0.1,1.5-1.6,6.7-1.6,6.7c2.2-2.7,3.2-3.6,4.3-7.2c1.6-0.5,2.5-0.8,4.1-1c-1.6,6.1-4.5,10.9-4.5,10.9l7.4,2c-4.1-0.1-13.3,5.1-13.3,5.1s16.2-4,20.8-1.1l1.2-2.9c-3.4-2.7-10.5-4.9-10.5-4.9s2.2-4.1,4.6-9.4c2.3,0.1,5.6,0.3,7.7,0.9c9.2,4.1-1.7,22.4-1.7,22.4S705.7,133.9,692.5,127.2z"/>
496- <path fill="#101040" d="M735.3,102.2c0.4,0.5,0.9,2.3,0.9,8.6c0,0.8,0,1.8,0,2.8c-3.4,0.5-6.3,1-6.8,1.2l-0.6,0.2l2.8,2.7l0.2-0.1c1.1-0.3,2.6-0.5,4.2-0.7c0,0.7-0.4,7.3-0.4,7.9c-4.9,0.4-9.2,1.1-12.7,1.9c-3,0.8-4.8,2.6-5.2,5.4c-0.1,0.4-0.1,0.9-0.1,1.4c0,7.1,6.9,16.9,7,16.9l0.8,0.5l-0.3-0.9c0-0.1-2.8-9.4-2.8-15.6c0-1.8,0.2-3.3,0.8-4.3c0.4-0.6,0.8-0.9,1.4-1c1.3-0.2,2.7-0.4,4-0.6c0,0,3.1,4,3.7,4.7c-0.5,0.1-1.4,0.3-1.4,0.3l0,0.3l0.2,2.2l0.4-0.1c0.6-0.2,1.8-0.5,3.2-0.7c0,0.6,0.1,2.4,0.2,2.9c-1.7,0.5-3.2,1.1-3.8,1.4l-0.4,0.2l1.5,1.6l0.2-0.1c0.7-0.3,1.7-0.6,2.7-0.9c0,0.7,0.3,5.6,0.3,5.6l0.7,0c0,0,1.1-5.5,1.2-6c1.8-0.2,3.5-0.1,4.8,0.2l0.4,0.1v-3l-0.2-0.1c-0.8-0.4-2.3-0.4-4.5,0.1c0.1-0.7,0.4-2.1,0.5-2.5c1.6-0.1,2.9-0.2,3.8-0.1l0.5,0.1l-0.7-2.6c0,0-0.9,0-1.4,0.1c0.8-0.9,3.5-4.4,3.7-4.6c0.3,0,3.9,0.1,3.9,0.1c1.6,0.1,3,0.7,4.1,2c1.3,1.5,2,3.9,2,6.3c0,0.5,0,1.1-0.1,1.6l-0.1,0.7c-0.7,5.1-1.3,8.8-3.1,9.6c-1.2,0.5-3.1-0.3-5.9-2.4l-0.5,0.5l5.5,9.6l0.3-0.1c6.7-2.4,7.7-3.8,10.4-15.4c1.2-5.1,0.8-9.1-1.3-11.8c-2.5-3.3-6.3-3.7-6.5-3.7c-0.1,0-6.7-0.3-14.6,0.2c0.2-0.8,2-7.5,2.1-7.9c2.5,0,4.7,0.1,5.7,0.5l0.4,0.1l0.4-4.7l-0.4,0c-1.1,0-2.8,0.1-5.1,0.3c1.3-4.4,1.9-5.8,2.1-6.2l0.2-0.3l-8.1-4.7L735.3,102.2z M739.7,127.5c-0.1,0.7-0.7,4.3-0.7,4.7c0,0-0.1,0-0.1,0c0-0.1,0.1-0.4,0.1-0.4l-4.4,0.4c0,0,0,0.5,0,0.6c-0.4,0.1-1,0.2-1.4,0.2c0-0.7,0-5,0-5.1C735.4,127.8,737.5,127.6,739.7,127.5z"/>
497- <path fill="#101040" d="M804.5,129.3l0.1-6.4l4.4-0.3l-0.3-2.4l-4.1,1.2l0-5.7c2.4,0.3,5,0.8,7.1,0.9c4.3,0.3-1.2,14.1-1.2,14.1s16.3-14.4,2.6-18.1c-1.4,0-4.9,0.2-8.4,0.4l0-3.9l7.5-1.9l-7.5-0.1l0-4l-4.6-3.4l0.5,8.7l-4,2.7l4.1-1l0.2,3.2c-1.8,0.2-3.4,0.3-4.3,0.5c-11.5,2.2,1,17.2,1,17.2s-4-14,0.5-15.3c0.9-0.3,1.8-0.3,2.9-0.3l0.4,7l-3,0.9l3-0.2l0.2,4.1c-9.2,12.1-19.7,16.6-19.7,16.6s10.2-1.1,20-11.9l1,18.7c-3.7-1.5-7-3.6-7-3.6s2.4,7.1,7.5,10.1l4.3-5.1c-1-0.2-2.1-0.5-3.2-0.9l0.2-18.9c2.7,3.2,9,10.5,11.7,11l9-3C821.9,139.6,807.7,131.3,804.5,129.3z"/>
498- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M49.4,40.5c0,7.7-6.2,13.9-13.9,13.9c-7.7,0-13.9-6.2-13.9-13.9c0-7.7,6.2-13.9,13.9-13.9C43.2,26.6,49.4,32.8,49.4,40.5z"/>
499- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="42" cy="40.5" r="6.5"/>
500- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="29" cy="40.5" r="6.5"/>
501- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="34" r="6.5"/>
502- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="47" r="6.5"/>
503- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M44.8,40.5c0,5.1-4.2,9.3-9.3,9.3s-9.3-4.2-9.3-9.3c0-5.1,4.2-9.3,9.3-9.3S44.8,35.4,44.8,40.5z"/>
504- <path fill="none" stroke="#BA0000" stroke-width="2" d="M52.2,40.5c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7c0-9.2,7.5-16.7,16.7-16.7C44.7,23.8,52.2,31.3,52.2,40.5z"/>
505- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M55,40.5C55,51.3,46.3,60,35.5,60C24.7,60,16,51.3,16,40.5C16,29.7,24.7,21,35.5,21C46.3,21,55,29.7,55,40.5z"/>
506- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M49.4,128.5c0,7.7-6.2,13.9-13.9,13.9c-7.7,0-13.9-6.2-13.9-13.9s6.2-13.9,13.9-13.9C43.2,114.6,49.4,120.8,49.4,128.5z"/>
507- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="42" cy="128.5" r="6.5"/>
508- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="29" cy="128.5" r="6.5"/>
509- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="122" r="6.5"/>
510- <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="135" r="6.5"/>
511- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M44.8,128.5c0,5.1-4.2,9.3-9.3,9.3s-9.3-4.2-9.3-9.3c0-5.1,4.2-9.3,9.3-9.3S44.8,123.4,44.8,128.5z"/>
512- <path fill="none" stroke="#BA0000" stroke-width="2" d="M52.2,128.5c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7c0-9.2,7.5-16.7,16.7-16.7C44.7,111.8,52.2,119.3,52.2,128.5z"/>
513- <path fill="none" stroke="#10106C" stroke-width="0.75" d="M55,128.5c0,10.8-8.7,19.5-19.5,19.5c-10.8,0-19.5-8.7-19.5-19.5c0-10.8,8.7-19.5,19.5-19.5C46.3,109,55,117.7,55,128.5z"/>
514- <path fill="#BA0000" d="M861.8,10.2c-0.4,0.4-0.5,1-0.5,1.7c0,1.3,0.4,2.9,0.6,3.5c0,0,0.3,7,0.4,7.8c-0.9,0.1-2.5,0.2-3.1,0.4l-0.8,0c-1.7,0.1-3.3,0.2-4,0.9l-0.3,0.3l0.3,0.4c0.5,0.6,0.7,2.3,0.9,4.1c0.5,3.7,1.1,8.3,4.1,10.1l0.6,0.3L860,39c0,0,0.1-0.3,0.1-0.7c0-0.3-0.1-0.8-0.2-1.5c0.8,0.1,2.5,0.3,3.1,0.3c0.1,0.8,2.7,33.4,2.7,33.4l1,0c0,0,1.1-32.2,1.1-33.2c0.7,0,3.8,0.1,4.7,0.2c-0.2,0.6-1,3.1-1,3.1l1.4-1.3c0.4-0.3,9.1-8.2,7.9-13.4c-0.4-1.7-1.8-2.9-4.1-3.5l-0.1,0h-0.1c-1.3,0-5.3,0.1-8.3,0.3c0.1-2.6,0.1-4,0.1-4.7c0-0.6,0-0.7,0-0.8c0,0,0-0.1,0-0.1c0-0.5,0.5-1.2,0.9-1.7c0.4-0.6,0.9-1.1,0.9-1.7c0-0.2-0.1-0.4-0.2-0.6c-0.6-0.9-5-3.2-6.9-3.4C862.4,9.8,862,9.9,861.8,10.2z M861.9,26.6c0,0,0.2,0,0.5,0c0,0.9,0.3,7.2,0.4,8.1c-0.8-0.1-2.7-0.3-3.3-0.3c-0.3-1.3-0.4-2.7-0.5-4.1c-0.1-1.2-0.2-2.4-0.4-3.5C859.5,26.8,861.9,26.6,861.9,26.6z M868,29.7c0,0,0.1-2.4,0.1-3.2c1.3,0.2,3,0.3,4.6,0.3c0,0,1.2,0,1.8,0c0,0.4-0.1,0.9-0.1,0.9c-0.2,2.2-0.5,4.8-1.4,7.4c-0.6,0-4.3-0.2-5.1-0.3C867.9,34.1,868,29.7,868,29.7z"/>
515- <path fill="#BA0000" d="M861.8,98.2c-0.4,0.4-0.5,1-0.5,1.7c0,1.3,0.4,2.9,0.6,3.5c0,0,0.3,7,0.4,7.8c-0.9,0.1-2.5,0.2-3.1,0.4l-0.8,0c-1.7,0.1-3.3,0.2-4,0.9l-0.3,0.3l0.3,0.4c0.5,0.6,0.7,2.3,0.9,4.1c0.5,3.7,1.1,8.3,4.1,10.1l0.6,0.3l0.2-0.6c0,0,0.1-0.3,0.1-0.7c0-0.3-0.1-0.8-0.2-1.5c0.8,0.1,2.5,0.3,3.1,0.3c0.1,0.8,2.7,33.4,2.7,33.4l1,0c0,0,1.1-32.2,1.1-33.2c0.7,0,3.8,0.1,4.7,0.2c-0.2,0.6-1,3.1-1,3.1l1.4-1.3c0.4-0.3,9.1-8.2,7.9-13.4c-0.4-1.7-1.8-2.9-4.1-3.5l-0.1,0h-0.1c-1.3,0-5.3,0.1-8.3,0.3c0.1-2.6,0.1-4,0.1-4.7c0-0.6,0-0.7,0-0.8c0,0,0-0.1,0-0.1c0-0.5,0.5-1.2,0.9-1.7c0.4-0.6,0.9-1.1,0.9-1.7c0-0.2-0.1-0.4-0.2-0.6c-0.6-0.9-5-3.2-6.9-3.4C862.4,97.8,862,97.9,861.8,98.2z M861.9,114.6c0,0,0.2,0,0.5,0c0,0.9,0.3,7.2,0.4,8.1c-0.8-0.1-2.7-0.3-3.3-0.3c-0.3-1.3-0.4-2.7-0.5-4.1c-0.1-1.2-0.2-2.4-0.4-3.5C859.5,114.8,861.9,114.6,861.9,114.6z M868,117.7c0,0,0.1-2.4,0.1-3.2c1.3,0.2,3,0.3,4.6,0.3c0,0,1.2,0,1.8,0c0,0.4-0.1,0.9-0.1,0.9c-0.2,2.2-0.5,4.8-1.4,7.4c-0.6,0-4.3-0.2-5.1-0.3C867.9,122.1,868,117.7,868,117.7z"/>
516- <path fill="#004C00" d="M925,47.8c0,0.1-1.4,5.1-5.1,7.1c-0.5-0.6-3.1-4.2-3.1-4.2l-0.9,0.5l4.4,11.4l1-0.2c0-2.2,1.1-3.5,2.4-4.9c1.8-2.1,3.8-4.5,2.3-9.7l-0.5-1.7L925,47.8z"/>
517- <path fill="#004C00" d="M929.5,37c0,0-0.5,4.9-1.9,7.8l-0.3,0.7l0.7,0.1c1.8,0.1,3.5-3.7,4.1-5.4c0.4,0,1.4,0.1,2,0.1c-0.3,1.2-0.7,2.8-0.7,3.8c0,0.4,0,0.7,0.2,0.9l0.5,0.3c0.8,0.1,5.2-0.2,6.1-0.3l0-1c-1.5-0.2-3.3-0.7-3.5-1.2c-0.2-0.6,0.6-3,1.5-5l0.3-0.7l-9-0.6L929.5,37z"/>
518- <path fill="#004C00" d="M942,46.6L927.3,47c0,0,4.9,6.3,5.4,6.9c-2.6,2.4-4.9,4.2-7,5.4l-0.6,0.4l0.6,0.5c1.4,1.1,4.2-0.2,8.6-4.2c2.3,3.2,3.8,5.5,4.3,6.9l0.2,0.7l2.7-2.2l0-0.2l0-0.3c0-0.5,0-2.1-4.7-7.3c3-3.1,5.6-6,5.6-6.1l0.7-0.9L942,46.6z M937.3,49.4c-0.8,0.9-1.7,1.8-2.1,2.3c-0.4-0.4-1.2-1.2-1.9-1.9C934.4,49.6,936,49.5,937.3,49.4z"/>
519- <path fill="#004C00" d="M931.1,25.2l-1.7,1.7l-4.5-5.2l-0.2,0c-0.7,0-4.3,0.1-5.2,1.4c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.1,0,0.2c0.1,0.5,0.7,0.7,1.8,1.1c0.5,0.2,1.2,0.5,1.5,0.7c-1.1,2.3-2.3,4.4-3.7,6.4c-0.7-0.4-4.1-2.5-4.1-2.5l-0.6,0.8c0,0,2.8,3.1,3.3,3.6c-2.8,3.5-6.2,6.6-10.4,9.5l0.4,0.9c12.8-3.3,21.9-16.2,22.1-16.5l2.3,2.2c-3.2,2-6.2,3.8-6.2,3.8l0.5,0.9c2-0.8,4.8-1.9,7.6-3L945,42.4l10.8-3.8l-0.1-1c-5.1-0.2-12-4.4-17.1-7.9c2.9-1,5.3-1.7,6.4-1.6c0.7,0,0.9-0.3,1-0.5c0.4-1.1-3.5-4.4-11.4-9.7l-0.5-0.3l-0.2,0.6 M934.9,27c0.7-1.2,1.6-2.9,1.9-3.3c0.5,0.3,1.8,1.1,2.5,1.6c-0.4,0.2-1.9,1.1-3.7,2.3C935.4,27.4,935.2,27.2,934.9,27z"/>
520- <path fill="#004C00" d="M926.9,35.4l-6.2,3.5l0.3,0.9c0,0,1.1-0.1,1.8-0.1c-0.2,0.7-0.6,1.8-1.1,3.2c-0.6,0-5-0.2-5-0.2l0.8,0.9c0.3,0.3,1.6,1.7,2.7,1.7c0,0,0.3-0.1,0.6-0.1c-0.8,2-1.7,3.7-2.6,5l0.7,0.7c1.5-1.2,3.2-3.4,4.8-6.4c0.3-0.1,1.2-0.4,1.2-0.4l-0.1-1c0,0-0.2,0-0.4,0c1.8-3.6,3.1-7.1,3.1-7.1l0.5-1.3L926.9,35.4z"/>
521- <path fill="#004C00" d="M925,135.8c0,0.1-1.4,5.1-5.1,7.1c-0.5-0.6-3.1-4.2-3.1-4.2l-0.9,0.5l4.4,11.4l1-0.2c0-2.2,1.1-3.5,2.4-4.9c1.8-2.1,3.8-4.5,2.3-9.7l-0.5-1.7L925,135.8z"/>
522- <path fill="#004C00" d="M929.5,125c0,0-0.5,4.9-1.9,7.8l-0.3,0.7l0.7,0.1c1.8,0.1,3.5-3.7,4.1-5.4c0.4,0,1.4,0.1,2,0.1c-0.3,1.2-0.7,2.8-0.7,3.8c0,0.4,0,0.7,0.2,0.9l0.5,0.3c0.8,0.1,5.2-0.2,6.1-0.3l0-1c-1.5-0.2-3.3-0.7-3.5-1.2c-0.2-0.6,0.6-3,1.5-5l0.3-0.7l-9-0.6L929.5,125z"/>
523- <path fill="#004C00" d="M942,134.6l-14.7,0.4c0,0,4.9,6.3,5.4,6.9c-2.6,2.4-4.9,4.2-7,5.4l-0.6,0.4l0.6,0.5c1.4,1.1,4.2-0.2,8.6-4.2c2.3,3.2,3.8,5.5,4.3,6.9l0.2,0.7l2.7-2.3l0-0.2l0-0.3c0-0.5,0-2.1-4.7-7.3c3-3.1,5.6-6,5.6-6.1l0.7-0.9L942,134.6z M937.3,137.4c-0.8,0.9-1.7,1.8-2.1,2.3c-0.4-0.4-1.2-1.2-1.9-1.9C934.4,137.6,936,137.5,937.3,137.4z"/>
524- <path fill="#004C00" d="M931.1,113.2l-1.7,1.7l-4.5-5.2l-0.2,0c-0.7,0-4.3,0.1-5.2,1.4c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.1,0,0.2c0.1,0.5,0.7,0.7,1.8,1.1c0.5,0.2,1.2,0.5,1.5,0.7c-1.1,2.3-2.3,4.4-3.7,6.4c-0.7-0.4-4.1-2.5-4.1-2.5l-0.6,0.8c0,0,2.8,3.1,3.3,3.6c-2.8,3.5-6.2,6.6-10.4,9.5l0.4,0.9c12.8-3.3,21.9-16.2,22.1-16.5l2.3,2.2c-3.2,2-6.2,3.8-6.2,3.8l0.5,0.9c2-0.8,4.8-1.9,7.6-3l11.4,10.9l10.8-3.8l-0.1-1c-5.1-0.2-12-4.4-17.1-7.9c2.9-1,5.3-1.7,6.4-1.6c0.7,0,0.9-0.3,1-0.5c0.4-1.1-3.5-4.4-11.4-9.7l-0.5-0.3l-0.2,0.6 M934.9,115c0.7-1.2,1.6-2.9,1.9-3.3c0.5,0.3,1.8,1.1,2.5,1.6c-0.4,0.2-1.9,1.1-3.7,2.3C935.4,115.4,935.2,115.2,934.9,115z"/>
525- <path fill="#004C00" d="M926.9,123.4l-6.2,3.5l0.3,0.9c0,0,1.1-0.1,1.8-0.1c-0.2,0.7-0.6,1.8-1.1,3.2c-0.6,0-5-0.2-5-0.2l0.8,0.9c0.3,0.3,1.6,1.7,2.7,1.7c0,0,0.3-0.1,0.6-0.1c-0.8,2-1.7,3.7-2.6,5l0.7,0.7c1.5-1.2,3.2-3.4,4.8-6.4c0.3-0.1,1.2-0.4,1.2-0.4l-0.1-1c0,0-0.2,0-0.4,0c1.8-3.6,3.1-7.1,3.1-7.1l0.5-1.3L926.9,123.4z"/>
526- <rect x="2386" y="12" rx="5" fill="#294D8E" width="35" height="57"/>
527- <rect x="2387" y="13" rx="4" fill="#F4F4F4" width="33" height="55"/>
528- <rect x="2390" y="16" rx="3" fill="#294D8E" width="27" height="49"/>
529- <rect x="2393" y="19" rx="7" fill="#F4F4F4" width="21" height="43"/>
530- <rect x="2386" y="100" rx="5" fill="#294D8E" width="35" height="57"/>
531- <rect x="2387" y="101" rx="4" fill="#FFFFFF" width="33" height="55"/>
532- <rect x="2390" y="104" rx="3" fill="#294D8E" width="27" height="49"/>
533- <rect x="2393" y="107" rx="7" fill="#FFFFFF" width="21" height="43"/>
534- <path fill="#99B9FF" stroke="#678ED3" d="M2323.8,13.9l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2333.4,11,2323.8,13.9,2323.8,13.9z"/>
535- <path fill="#99B9FF" stroke="#678ED3" d="M2267.6,19.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2259.5,20.7,2267.6,19.9,2267.6,19.9z"/>
536- <path fill="#99B9FF" stroke="#678ED3" d="M2139.6,16.1c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2131.5,16.9,2139.6,16.1,2139.6,16.1z"/>
537- <polygon fill="#99B9FF" stroke="#678ED3" points="2264.9,16.3 2265.7,17.8 2260.9,18.3 2260.4,16.8"/>
538- <path fill="#99B9FF" stroke="#678ED3" d="M2267.1,12.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2267.1,12.5z"/>
539- <path fill="#99B9FF" stroke="#678ED3" d="M2203.6,18.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2195.5,19.7,2203.6,18.9,2203.6,18.9z"/>
540- <path fill="#99B9FF" stroke="#678ED3" d="M2203.1,14.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2203.1,14.5z"/>
541- <path fill="#99B9FF" stroke="#678ED3" d="M2323.8,101.9l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2333.4,99,2323.8,101.9,2323.8,101.9z"/>
542- <path fill="#99B9FF" stroke="#678ED3" d="M2267.6,107.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2259.5,108.7,2267.6,107.9,2267.6,107.9z"/>
543- <path fill="#99B9FF" stroke="#678ED3" d="M2139.6,104.1c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2131.5,104.9,2139.6,104.1,2139.6,104.1z"/>
544- <polygon fill="#99B9FF" stroke="#678ED3" points="2264.9,104.3 2265.7,105.8 2260.9,106.3 2260.4,104.8 "/>
545- <path fill="#99B9FF" stroke="#678ED3" d="M2267.1,100.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2267.1,100.5z"/>
546- <path fill="#99B9FF" stroke="#678ED3" d="M2203.6,106.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2195.5,107.7,2203.6,106.9,2203.6,106.9z"/>
547- <path fill="#99B9FF" stroke="#678ED3" d="M2203.1,102.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2203.1,102.5z"/>
548- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2643.1,20.3l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7L2640,19l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2652.7,17.4,2643.1,20.3,2643.1,20.3z"/>
549- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.9,26.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2578.9,27.1,2586.9,26.3,2586.9,26.3z"/>
550- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2458.9,22.5c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2450.9,23.3,2458.9,22.5,2458.9,22.5z"/>
551- <polygon fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" points="2584.3,22.6 2585,24.1 2580.3,24.7 2579.7,23.2 "/>
552- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.5,18.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2586.5,18.8z"/>
553- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.9,25.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2514.9,26.1,2522.9,25.3,2522.9,25.3z"/>
554- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.5,20.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2522.5,20.8z"/>
555- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2643.1,108.3l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2652.7,105.4,2643.1,108.3,2643.1,108.3z"/>
556- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.9,114.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2578.9,115.1,2586.9,114.3,2586.9,114.3z"/>
557- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2458.9,110.5c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2450.9,111.3,2458.9,110.5,2458.9,110.5z"/>
558- <polygon fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" points="2584.3,110.6 2585,112.1 2580.3,112.7 2579.7,111.2 "/>
559- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.5,106.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2586.5,106.8z"/>
560- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.9,113.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2514.9,114.1,2522.9,113.3,2522.9,113.3z"/>
561- <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.5,108.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2522.5,108.8z"/>
562- <path fill="#101040" d="M632.3,54.7c-2-0.9-11.8,3.3-11.8,3.3l0.5-20.7c0,0,5.3-4.3,9.6-1.7l-1.2-5c-1.4-0.2-8.4,5.5-8.4,5.5s-0.6-18.8,4.8-19.9c0,0-8.9-2.6-9.7-2.2c-0.4,0.2-0.4,13.8-0.2,27.1c-3.2,3.1-5.9,6.2-7.1,7.3l0.4-28.5c0,0-4.7-2.6-5.8-1.4l0.2,14.8c-0.3-0.5-0.7-0.9-1-1.3c-1-1.3-6.1-4.2-6.1-4.2s1.7,5.6,2.5,6.6c0.7,0.9,3,2.9,4.6,2.2l0.2,16.5c-3.4,3-7.9,6.8-8.5,5.4c-1.5-3.2-4.7-3-5.2-1.5c-0.5,1.6,4.2,10.2,5.8,10.1c0.8-0.1,4.4-4.9,8-10l0.1,5.3c1.6,2.1,3.8,0.5,4.6-0.7l0.3-11c1.1-1.6,4-4.5,7.1-7.9c0.2,12.1,0.3,22.8,0.3,22.8c4.9-5.2,16.7-4,16.7-4L632.3,54.7z"/>
563-</svg>
addedscripts/antics-deploy.mjs+92 −0
1+#!/usr/bin/env node
2+// Deploy the built game to antics.gg as a multi-file bundle.
3+//
4+// npm run build && node scripts/antics-deploy.mjs # keyless: a fresh link each time
5+// node scripts/antics-deploy.mjs --project <proj_id> # under a project: stable /p/ share link
6+//
7+// A keyless deploy needs no account at all. Deploying under a project needs a
8+// token from `npx antics-cli login`, read from ANTICS_TOKEN or ~/.antics/token.
9+
10+import { readdir, readFile } from 'node:fs/promises';
11+import { homedir } from 'node:os';
12+import { join, relative, extname } from 'node:path';
13+
14+const HOST = process.env.ANTICS_HOST || 'https://antics.gg';
15+const DIST = new URL('../dist', import.meta.url).pathname;
16+
17+// Anything not in this list rides along as a data: URI.
18+const TEXT = new Set(['.html', '.js', '.css', '.svg', '.json', '.webmanifest', '.txt', '.map']);
19+const MIME = {
20+ '.png': 'image/png',
21+ '.jpg': 'image/jpeg',
22+ '.webp': 'image/webp',
23+ '.mp3': 'audio/mpeg',
24+ '.woff2': 'font/woff2',
25+ '.ico': 'image/x-icon',
26+};
27+
28+async function* walk(dir) {
29+ for (const e of await readdir(dir, { withFileTypes: true })) {
30+ const p = join(dir, e.name);
31+ if (e.isDirectory()) yield* walk(p);
32+ else yield p;
33+ }
34+}
35+
36+async function collect() {
37+ const files = {};
38+ let bytes = 0;
39+ for await (const p of walk(DIST)) {
40+ const rel = relative(DIST, p);
41+ const ext = extname(rel);
42+ const buf = await readFile(p);
43+ bytes += buf.length;
44+ files[rel] = TEXT.has(ext)
45+ ? buf.toString('utf8')
46+ : `data:${MIME[ext] ?? 'application/octet-stream'};base64,${buf.toString('base64')}`;
47+ }
48+ return { files, bytes };
49+}
50+
51+async function token() {
52+ if (process.env.ANTICS_TOKEN) return process.env.ANTICS_TOKEN;
53+ try {
54+ return (await readFile(join(homedir(), '.antics', 'token'), 'utf8')).trim();
55+ } catch {
56+ return null;
57+ }
58+}
59+
60+const args = process.argv.slice(2);
61+const projectAt = args.indexOf('--project');
62+const project = projectAt >= 0 ? args[projectAt + 1] : null;
63+
64+const { files, bytes } = await collect();
65+console.log(`deploying ${Object.keys(files).length} files, ${(bytes / 1024 / 1024).toFixed(1)} MB from dist/`);
66+
67+const body = { files };
68+const headers = { 'content-type': 'application/json' };
69+if (project) {
70+ body.project = project;
71+ const t = await token();
72+ if (!t) {
73+ console.error('a --project deploy needs a token: run `npx antics-cli login`, or set ANTICS_TOKEN');
74+ process.exit(1);
75+ }
76+ headers.authorization = `Bearer ${t}`;
77+}
78+
79+const res = await fetch(`${HOST}/api/deploy`, {
80+ method: 'POST',
81+ headers,
82+ body: JSON.stringify(body),
83+});
84+const out = await res.json().catch(() => ({}));
85+if (!res.ok) {
86+ console.error(`deploy failed: ${res.status}`, out);
87+ process.exit(1);
88+}
89+console.log('deployed.');
90+if (out.playUrl) console.log(` play (this build): ${out.playUrl}`);
91+if (out.shareUrl) console.log(` share (follows updates): ${out.shareUrl}`);
92+if (out.hash) console.log(` hash: ${out.hash}`);
modifiedsrc/App.tsx+187 −24
11 import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
22 import { AutoPlay } from './game/autoplay';
3+import type { TableCtl } from './game/ctl';
34 import { Game } from './game/engine';
45 import { loadPrefs, savePrefs, type Prefs } from './game/prefs';
56 import { loadSave } from './game/save';
67 import { play, setSoundEnabled, setVoiceEnabled, setVolume } from './game/sound';
78 import { WIND_EN, WIND_NAMES } from './game/tiles';
89 import type { SeatId } from './game/types';
10+import { roomFromUrl, seatLink } from './net/protocol';
11+import { NetSession } from './net/session';
912 import { Center } from './ui/Center';
1013 import { useCompact } from './ui/compact';
14+import { Controller } from './ui/Controller';
1115 import { FullscreenToggle } from './ui/FullscreenToggle';
1216 import { enterFullscreen, useFullscreen } from './ui/fullscreen';
1317 import { Help } from './ui/Help';
18+import { OnlineLobby } from './ui/OnlineLobby';
1419 import { TablePool } from './table/pool';
1520 import { Pool } from './ui/Pool';
16-import { Seat } from './ui/Seat';
21+import { Seat, type RemoteSeat } from './ui/Seat';
1722 import { Settings } from './ui/Settings';
1823 import './styles.css';
1924
⋯ 2 unchanged lines
2227 const SOLO_SEATS = [false, true, true, true];
2328 const HOTSEAT_SEATS = [false, false, false, false];
2429
30+// One join per page, whoever asks first — the lobby button, or the ?room= the
31+// antics page hands us. A promise rather than a flag, so a second caller waits
32+// on the same join instead of starting another.
33+let joining: Promise<NetSession> | null = null;
34+const joinOnce = () => (joining ??= NetSession.join());
35+
36+const subNothing = () => () => {};
37+const zero = () => 0;
38+
2539 export default function App() {
2640 const [prefs, setPrefs] = useState<Prefs>(loadPrefs);
2741 // Phone-sized: everything reads upright for the one person holding it.
⋯ 12 unchanged lines
4054 const [settingsOpen, setSettingsOpen] = useState(false);
4155 // Read once at startup; after that the live game is the source of truth.
4256 const [saved] = useState(() => loadSave());
43- const phase = game.state.phase;
4457
58+ // ---- the room, if this page is in one ----------------------------------
59+ const [session, setSession] = useState<NetSession | null>(null);
60+ const [netErr, setNetErr] = useState<string | null>(null);
61+ const [connecting, setConnecting] = useState(false);
62+ useSyncExternalStore(session?.subscribe ?? subNothing, session?.getSnapshot ?? zero);
63+ useSyncExternalStore(session?.table.subscribe ?? subNothing, session?.table.getSnapshot ?? zero);
64+
65+ const connect = async (): Promise<NetSession | null> => {
66+ setConnecting(true);
67+ try {
68+ const s = await joinOnce();
69+ setSession(s);
70+ setNetErr(null);
71+ return s;
72+ } catch (e) {
73+ setNetErr(e instanceof Error ? e.message : String(e));
74+ joining = null;
75+ return null;
76+ } finally {
77+ setConnecting(false);
78+ }
79+ };
80+
81+ // Opened from a room link — the antics page passes ?room= down to us — so
82+ // everyone on that link shares one table. Join it before anyone taps
83+ // anything: presence is the first thing a shared room owes its visitors.
84+ const auto = useRef(false);
85+ useEffect(() => {
86+ if (auto.current || !roomFromUrl()) return;
87+ auto.current = true;
88+ void connect();
89+ // eslint-disable-next-line react-hooks/exhaustive-deps
90+ }, []);
91+
92+ // The full-size tables hand phone throws to the pool, and hold the computer
93+ // players while something is still skidding across it.
94+ useEffect(() => {
95+ if (!session) return;
96+ session.onThrowFx = (seat, t) => {
97+ pool.throwFromNet(seat, t);
98+ pool.wake();
99+ };
100+ session.setBusy(() => pool.busy);
101+ return () => {
102+ session.onThrowFx = () => {};
103+ session.setBusy(() => false);
104+ };
105+ }, [session, pool]);
106+ useEffect(() => session?.table.onSound(play), [session]);
107+
108+ // Walking back into a running online game unseated — a reload, or a friend
109+ // arriving late — takes the first empty chair, deserted or computer-run.
110+ const reclaimed = useRef(false);
111+ useEffect(() => {
112+ if (!session || session.mode !== 'online' || !session.started) return;
113+ if (reclaimed.current || session.mySeat !== null) return;
114+ if (session.seats.some((s) => s.id === null)) {
115+ reclaimed.current = true;
116+ session.claimSeat(null);
117+ }
118+ });
119+
120+ const netMode = session?.mode ?? null;
121+ const mySeat = session?.mySeat ?? null;
122+ // The party table's common screen. Usually the device that started the game;
123+ // after a reload that flag is gone, so anything full-sized and unseated in a
124+ // party room shows the table rather than a phone's controller.
125+ const isScreen = session?.role === 'screen' || (netMode === 'party' && !compact && mySeat === null);
126+ const netActive = !!session && (netMode === 'party' ? true : netMode === 'online' && session.started);
127+
128+ // Which engine this screen draws and drives.
129+ const ctl: TableCtl = netActive ? session!.table : game;
130+ const phase = ctl.state.phase;
131+ // Online the table is turned so your own seat is at the bottom edge.
132+ const viewSeat: SeatId = netMode === 'online' ? mySeat ?? 0 : 0;
133+ const posOf = (seat: SeatId): SeatId => (((seat - viewSeat + 4) % 4) as SeatId);
134+
45135 // Names and stakes live in the game state as well as in the preferences —
46136 // saved games keep the settings they were played under.
47137 const commitSettings = (p: Prefs) => {
48138 setPrefs(p);
49- game.applySettings(p.names, p.rules);
139+ ctl.applySettings(p.names, p.rules);
50140 setSettingsOpen(false);
51141 };
52142 // The panel auditions sound changes as you make them, so cancelling has to
⋯ 10 unchanged lines
63153 onSave={commitSettings}
64154 onClose={cancelSettings}
65155 stakesLocked={phase !== 'lobby' && phase !== 'gameEnd'}
66- bots={game.state.bots}
156+ bots={ctl.state.bots}
67157 />
68158 );
69159
⋯ 29 unchanged lines
99189 game.restore(saved!.state);
100190 };
101191
192+ // The two networked ways to start. Whoever creates the room is its host and
193+ // may open a mode; a visitor who follows the link into a room where nothing
194+ // has started yet waits for the host's choice.
195+ const openOnline = async () => {
196+ const s = await connect();
197+ if (s?.isHost && !s.mode) s.openOnlineLobby();
198+ };
199+ const openParty = async () => {
200+ const s = await connect();
201+ if (s?.isHost && !s.mode) s.startParty(prefs.names, prefs.rules);
202+ };
203+
102204 // Preferences are written straight through, so a mute survives a refresh.
103205 useEffect(() => {
104206 savePrefs(prefs);
⋯ 3 unchanged lines
108210 }, [prefs]);
109211 useEffect(() => game.onSound(play), [game]);
110212 // Watches the table for the whole session and only stirs when a seat the
111- // computer holds is the one being waited on.
213+ // computer holds is the one being waited on. Idle in a networked game, where
214+ // whichever device is the room's host runs its own AutoPlay.
112215 // The computer waits for a thrown tile to land — and for whoever it landed on
113216 // to finish saying so — before it reaches for it.
114217 useEffect(() => new AutoPlay(game, () => pool.busy).start(), [game, pool]);
115218
116- if (phase === 'lobby') {
219+ // ---- routing ------------------------------------------------------------
220+ // A phone at the party table holds a hand, not the table.
221+ if (session && netMode === 'party' && !isScreen) {
222+ return <Controller session={session} />;
223+ }
224+ if (session && netMode === 'online' && !session.started) {
225+ return <OnlineLobby session={session} prefs={prefs} />;
226+ }
227+
228+ if (!netActive && phase === 'lobby') {
117229 const round = saved ? Math.floor(saved.state.rotations / 4) % 4 : 0;
118230 const savedSolo = saved?.state.bots.some(Boolean);
231+ const netIdle = !session?.mode;
119232 return (
120233 <div className="lobby">
121234 <h1>台灣麻將</h1>
122- <p className="sub">Taiwanese Mahjong · 16 tiles · four players, one screen</p>
123- <p className="hint">
124- 把擋板放在螢幕四邊,各自遮住自己的牌。
125- <br />
126- Stand a card over your edge of the screen to hide your hand.
127- </p>
235+ <p className="sub">Taiwanese Mahjong · 16 tiles · four players</p>
128236
129237 {saved && (
130238 <div className="resume">
⋯ 29 unchanged lines
160268 四人同桌 Hotseat
161269 <em>四個人共用一個螢幕 · four people, one screen</em>
162270 </button>
271+ {(!session || (session.isHost && netIdle)) && (
272+ <>
273+ <button className="btn big-btn ghost" onClick={openOnline} disabled={connecting}>
274+ 線上對戰 Online game
275+ <em>各自用自己的裝置 · everyone joins from their own device</em>
276+ </button>
277+ <button className="btn big-btn ghost" onClick={openParty} disabled={connecting}>
278+ 手機派對 Party mode
279+ <em>這螢幕當桌,掃 QR 用手機拿牌 · this screen is the table, phones hold the hands</em>
280+ </button>
281+ </>
282+ )}
283+ {session && !session.isHost && netIdle && (
284+ <p className="hint">
285+ 已連線 {session.room.code} · {session.playerCount} 人在房,等房主選模式
286+ <br />
287+ connected — waiting for the host to pick a mode
288+ </p>
289+ )}
163290 </div>
164291
292+ {(netErr || session?.err) && <p className="hint">{netErr ?? session?.err}</p>}
293+
165294 <div className="lobby-extras">
166295 <button className="btn ghost pill" onClick={() => setSettingsOpen(true)}>
167296 ⚙ 設定 Settings
⋯ 7 unchanged lines
175304 );
176305 }
177306
307+ // What each seat is to this particular screen: whose buttons it shows, whose
308+ // tiles it may turn over, and what stands in for the seats that live on
309+ // other devices.
310+ const seatProps = (seat: SeatId): { local: boolean; remote?: RemoteSeat } => {
311+ if (!netActive) return { local: true };
312+ const slot = session!.seats[seat];
313+ if (netMode === 'party') {
314+ return {
315+ // A seat nobody has scanned is played right here, hotseat-style.
316+ local: slot.id === null,
317+ remote: {
318+ qrUrl: seatLink(session!.room.link, seat),
319+ holder: slot.id !== null ? slot.name || '手機 phone' : null,
320+ },
321+ };
322+ }
323+ return {
324+ local: seat === mySeat,
325+ // A deserted human seat: the host's screen offers it to the computer.
326+ remote:
327+ slot.id === null && !ctl.state.bots[seat] && session!.isHost
328+ ? { holder: null, onHandToBot: () => session!.handToBot(seat) }
329+ : undefined,
330+ };
331+ };
332+
178333 return (
179334 <div className="table">
180- {SEATS.map((s) => (
181- <Seat
182- key={s}
183- game={game}
184- seat={s}
185- compact={compact}
186- pool={compact ? undefined : pool}
187- onHelp={() => setHelpSeat(s)}
188- />
189- ))}
335+ {SEATS.map((s) => {
336+ const sp = seatProps(s);
337+ return (
338+ <Seat
339+ key={s}
340+ game={ctl}
341+ seat={s}
342+ pos={posOf(s)}
343+ local={sp.local}
344+ remote={sp.remote}
345+ compact={compact}
346+ pool={compact ? undefined : pool}
347+ onHelp={() => setHelpSeat(posOf(s))}
348+ />
349+ );
350+ })}
190351 {/* The tiles in the middle. Not on a phone: the compact layout has no
191352 centre square for them to be thrown into. */}
192- {!compact && <Pool game={game} pool={pool} />}
353+ {!compact && <Pool game={ctl} pool={pool} viewSeat={viewSeat} />}
193354 <Center
194- game={game}
355+ game={ctl}
195356 prefs={prefs}
196357 compact={compact}
358+ viewSeat={viewSeat}
359+ preferSeat={netMode === 'online' ? mySeat : null}
197360 onPrefs={setPrefs}
198361 onSettings={() => setSettingsOpen(true)}
199362 />
⋯ 15 unchanged lines
addedsrc/assets/back.pngbinary
Binary file
addedsrc/assets/tiles.svg+563 −0
1+<?xml version="1.0" encoding="utf-8"?>
2+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5504" height="352" viewBox="0 0 2752 176" version="1.1">
4+ <defs>
5+ <g id="bt1">
6+ <path d="M9,0C8.47,0,7.96,0.21,7.59,0.59l-7,7C0.21,7.96,0,8.47,0,9v77c0,1.1,0.9,2,2,2h53c0.53,0,1.04-0.21,1.41-0.59l7-7C63.79,80.04,64,79.53,64,79V2c0-1.1-0.9-2-2-2H9z"/>
7+ <path fill="#9F9F9F" d="M11.75,78.25l-9,9h9H55c0.33,0,0.65-0.13,0.88-0.37l7-7c0.23-0.23,0.37-1.3,0.37-1.63H11.75z"/>
8+ <path fill="#D7D7D7" d="M8.12,1.12l-7,7C0.88,8.35,0.75,8.67,0.75,9v66.25v1v9l2,2l7-7v-4v-1V0.75C9.42,0.75,8.35,0.88,8.12,1.12z"/>
9+ <path fill="#BFBFBF" d="M1.61,86.89l10.14-10.14v-0.5L1.34,86.66C1.42,86.75,1.51,86.82,1.61,86.89z"/>
10+ <path fill="#B7B7B7" d="M1.93,87.07l9.82-9.82v-0.5L1.61,86.89C1.71,86.96,1.82,87.02,1.93,87.07z"/>
11+ <path fill="#AFAFAF" d="M2.3,87.2l9.45-9.45v-0.5l-9.82,9.82C2.05,87.12,2.17,87.17,2.3,87.2z"/>
12+ <path fill="#A7A7A7" d="M11.75,77.75L2.3,87.2c0.14,0.03,0.29,0.05,0.45,0.05l9-9V77.75z"/>
13+ <path fill="#C7C7C7" d="M1.34,86.66l10.41-10.41h-0.5L1.11,86.39C1.18,86.49,1.25,86.58,1.34,86.66z"/>
14+ <path fill="#CDCDCD" d="M1.11,86.39l10.14-10.14h-0.5l-9.82,9.82C0.98,86.18,1.04,86.29,1.11,86.39z"/>
15+ <path fill="#D2D2D2" d="M0.93,86.07l9.82-9.82h-0.5L0.8,85.7C0.83,85.83,0.88,85.95,0.93,86.07z"/>
16+ <path fill="#D7D7D7" d="M10.25,76.25h-0.5l-9,9c0,0.15,0.02,0.3,0.05,0.45L10.25,76.25z"/>
17+ <path fill="#F6F6F6" d="M63.25,78.25V2.75c0-1.1-0.9-2-2-2h-50.5c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h50.5C62.35,80.25,63.25,79.35,63.25,78.25z"/>
18+ <path fill="#FFFFFF" d="M8.75,78.25V2.75c0-1.1,0.9-2,2-2h-1c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h1C9.65,80.25,8.75,79.35,8.75,78.25z"/>
19+ </g>
20+ <g id="bt2">
21+ <path fill="#0000FF" d="M9,0C8.47,0,7.96,0.21,7.59,0.59l-7,7C0.21,7.96,0,8.47,0,9v77c0,1.1,0.9,2,2,2h53c0.53,0,1.04-0.21,1.41-0.59l7-7C63.79,80.04,64,79.53,64,79V2c0-1.1-0.9-2-2-2H9z"/>
22+ <path fill="#8F8FFF" d="M11.75,78.25l-9,9h9H55c0.33,0,0.65-0.13,0.88-0.37l7-7c0.23-0.23,0.37-1.3,0.37-1.63H11.75z"/>
23+ <path fill="#C7C7FF" d="M8.12,1.12l-7,7C0.88,8.35,0.75,8.67,0.75,9v66.25v1v9l2,2l7-7v-4v-1V0.75C9.42,0.75,8.35,0.88,8.12,1.12z"/>
24+ <path fill="#AFAFFF" d="M1.61,86.89l10.14-10.14v-0.5L1.34,86.66C1.42,86.75,1.51,86.82,1.61,86.89z"/>
25+ <path fill="#A7A7FF" d="M1.93,87.07l9.82-9.82v-0.5L1.61,86.89C1.71,86.96,1.82,87.02,1.93,87.07z"/>
26+ <path fill="#9F9FFF" d="M2.3,87.2l9.45-9.45v-0.5l-9.82,9.82C2.05,87.12,2.17,87.17,2.3,87.2z"/>
27+ <path fill="#9797FF" d="M11.75,77.75L2.3,87.2c0.14,0.03,0.29,0.05,0.45,0.05l9-9V77.75z"/>
28+ <path fill="#B7B7FF" d="M1.34,86.66l10.41-10.41h-0.5L1.11,86.39C1.18,86.49,1.25,86.58,1.34,86.66z"/>
29+ <path fill="#BDBDFF" d="M1.11,86.39l10.14-10.14h-0.5l-9.82,9.82C0.98,86.18,1.04,86.29,1.11,86.39z"/>
30+ <path fill="#C2C2FF" d="M0.93,86.07l9.82-9.82h-0.5L0.8,85.7C0.83,85.83,0.88,85.95,0.93,86.07z"/>
31+ <path fill="#C7C7FF" d="M10.25,76.25h-0.5l-9,9c0,0.15,0.02,0.3,0.05,0.45L10.25,76.25z"/>
32+ <path fill="#E3E3FF" d="M61.25,0.75H9.75c-1.1,0-2,0.9-2,2v75.5c0,1.1,0.9,2,2,2h51.5c1.1,0,2-0.9,2-2V2.75C63.25,1.65,62.35,0.75,61.25,0.75z"/>
33+ <path fill="#FFFFFF" stroke="#0000FF" stroke-width="3" d="M60,74.19c0,1.55-1.22,2.81-2.72,2.81H13.72c-1.5,0-2.72-1.26-2.72-2.81V6.81C11,5.26,12.22,4,13.72,4h43.56C58.78,4,60,5.26,60,6.81V74.19z"/>
34+ </g>
35+ <path id="bams" fill="#006C00" d="M11,13.5c0-0.92-1.25-1.71-3.06-2.09V4.44C9.75,4.06,11,3.27,11,2.35C11,1.05,8.54,0,5.5,0S0,1.05,0,2.35c0,0.92,1.25,1.71,3.06,2.09v6.97C1.25,11.79,0,12.58,0,13.5c0,0.92,1.25,1.71,3.06,2.09v6.96C1.25,22.94,0,23.73,0,24.65C0,25.95,2.46,27,5.5,27s5.5-1.05,5.5-2.35c0-0.92-1.25-1.71-3.06-2.09v-6.96C9.75,15.21,11,14.42,11,13.5z M5.5,1.47c1.35,0,2.44,0.39,2.44,0.88c0,0.41-0.78,0.75-1.83,0.85v5.94c0,0.46-0.28,0.84-0.61,0.84S4.89,9.6,4.89,9.14V3.2C3.84,3.1,3.06,2.76,3.06,2.35C3.06,1.86,4.15,1.47,5.5,1.47z M5.5,25.53c-1.35,0-2.44-0.39-2.44-0.88c0-0.41,0.78-0.75,1.83-0.85v-5.94c0-0.46,0.28-0.84,0.61-0.84s0.61,0.38,0.61,0.84v5.94c1.05,0.1,1.83,0.44,1.83,0.85C7.94,25.14,6.85,25.53,5.5,25.53z M5.5,14.38c-1.86,0-3.36-0.39-3.36-0.88c0-0.49,1.5-0.88,3.36-0.88s3.36,0.39,3.36,0.88C8.86,13.99,7.36,14.38,5.5,14.38z"/>
36+ <path id="bams2" fill="#9C0000" d="M11,13.5c0-0.92-1.25-1.71-3.06-2.09V4.44C9.75,4.06,11,3.27,11,2.35C11,1.05,8.54,0,5.5,0S0,1.05,0,2.35c0,0.92,1.25,1.71,3.06,2.09v6.97C1.25,11.79,0,12.58,0,13.5c0,0.92,1.25,1.71,3.06,2.09v6.96C1.25,22.94,0,23.73,0,24.65C0,25.95,2.46,27,5.5,27s5.5-1.05,5.5-2.35c0-0.92-1.25-1.71-3.06-2.09v-6.96C9.75,15.21,11,14.42,11,13.5z M5.5,1.47c1.35,0,2.44,0.39,2.44,0.88c0,0.41-0.78,0.75-1.83,0.85v5.94c0,0.46-0.28,0.84-0.61,0.84S4.89,9.6,4.89,9.14V3.2C3.84,3.1,3.06,2.76,3.06,2.35C3.06,1.86,4.15,1.47,5.5,1.47z M5.5,25.53c-1.35,0-2.44-0.39-2.44-0.88c0-0.41,0.78-0.75,1.83-0.85v-5.94c0-0.46,0.28-0.84,0.61-0.84s0.61,0.38,0.61,0.84v5.94c1.05,0.1,1.83,0.44,1.83,0.85C7.94,25.14,6.85,25.53,5.5,25.53z M5.5,14.38c-1.86,0-3.36-0.39-3.36-0.88c0-0.49,1.5-0.88,3.36-0.88s3.36,0.39,3.36,0.88C8.86,13.99,7.36,14.38,5.5,14.38z"/>
37+ <g id="dots">
38+ <path fill="#10106C" d="M9,1.42c-4.18,0-7.58,3.4-7.58,7.58s3.4,7.58,7.58,7.58c4.18,0,7.58-3.4,7.58-7.58S13.18,1.42,9,1.42zM9,15.11c-1.36,0-2.49-0.97-2.75-2.25c0.78,0.56,1.72,0.89,2.75,0.89c1.03,0,1.97-0.34,2.75-0.89C11.49,14.14,10.36,15.11,9,15.11z M9,2.89c1.36,0,2.49,0.97,2.75,2.25C10.97,4.59,10.03,4.25,9,4.25c-1.03,0-1.97,0.34-2.75,0.89C6.51,3.86,7.64,2.89,9,2.89z M12.75,9c0,0.57-0.14,1.11-0.37,1.59C12.04,9.92,11.51,9.37,10.85,9c0.66-0.37,1.19-0.92,1.53-1.59C12.61,7.89,12.75,8.43,12.75,9z M5.25,9c0-0.57,0.14-1.11,0.37-1.59C5.96,8.08,6.49,8.63,7.15,9c-0.66,0.37-1.19,0.92-1.53,1.59C5.39,10.11,5.25,9.57,5.25,9z M11.59,11.7c-1.02-0.27-1.82-1.1-2.03-2.14c1.04,0.21,1.87,1.01,2.14,2.03C11.66,11.63,11.63,11.66,11.59,11.7z M6.3,11.59c0.27-1.02,1.1-1.82,2.15-2.03c-0.21,1.04-1.01,1.87-2.03,2.14C6.37,11.66,6.34,11.63,6.3,11.59z M6.41,6.3c1.02,0.27,1.82,1.1,2.03,2.15C7.4,8.23,6.57,7.43,6.3,6.41C6.34,6.37,6.37,6.34,6.41,6.3z M11.7,6.41c-0.27,1.02-1.1,1.82-2.14,2.03c0.21-1.05,1.01-1.87,2.03-2.15C11.63,6.34,11.66,6.37,11.7,6.41z M9,7.15C8.63,6.49,8.08,5.96,7.41,5.62C7.89,5.39,8.43,5.25,9,5.25s1.11,0.14,1.59,0.37C9.92,5.96,9.37,6.49,9,7.15z M9,10.85c0.37,0.66,0.92,1.19,1.59,1.53c-0.49,0.23-1.02,0.37-1.59,0.37s-1.11-0.14-1.59-0.37C8.08,12.04,8.63,11.51,9,10.85z M5.14,11.75C3.86,11.49,2.89,10.36,2.89,9s0.97-2.49,2.25-2.75C4.59,7.03,4.25,7.97,4.25,9C4.25,10.03,4.59,10.97,5.14,11.75z M12.86,6.25c1.28,0.26,2.25,1.39,2.25,2.75s-0.97,2.49-2.25,2.75c0.56-0.78,0.89-1.72,0.89-2.75C13.75,7.97,13.41,7.03,12.86,6.25z M15.01,6.33c-0.59-0.6-1.38-0.99-2.25-1.09c-0.11-0.88-0.49-1.66-1.09-2.25C13.15,3.65,14.35,4.85,15.01,6.33z M6.33,2.99c-0.6,0.59-0.98,1.38-1.09,2.25C4.36,5.35,3.58,5.73,2.99,6.33C3.65,4.85,4.85,3.65,6.33,2.99z M2.99,11.67c0.59,0.6,1.38,0.99,2.25,1.09c0.11,0.88,0.49,1.66,1.09,2.25C4.85,14.35,3.65,13.15,2.99,11.67z M11.67,15.01c0.6-0.59,0.99-1.38,1.09-2.25c0.88-0.11,1.66-0.49,2.25-1.09C14.35,13.15,13.15,14.35,11.67,15.01z"/>
39+ <path fill="#10106C" d="M9,0C4.04,0,0,4.04,0,9c0,4.96,4.04,9,9,9c4.96,0,9-4.04,9-9C18,4.04,13.96,0,9,0z M9,17c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S13.41,17,9,17z"/>
40+ </g>
41+ <g id="dots2">
42+ <path fill="#9C0000" d="M9,1.42c-4.18,0-7.58,3.4-7.58,7.58s3.4,7.58,7.58,7.58c4.18,0,7.58-3.4,7.58-7.58S13.18,1.42,9,1.42zM9,15.11c-1.36,0-2.49-0.97-2.75-2.25c0.78,0.56,1.72,0.89,2.75,0.89c1.03,0,1.97-0.34,2.75-0.89C11.49,14.14,10.36,15.11,9,15.11z M9,2.89c1.36,0,2.49,0.97,2.75,2.25C10.97,4.59,10.03,4.25,9,4.25c-1.03,0-1.97,0.34-2.75,0.89C6.51,3.86,7.64,2.89,9,2.89z M12.75,9c0,0.57-0.14,1.11-0.37,1.59C12.04,9.92,11.51,9.37,10.85,9c0.66-0.37,1.19-0.92,1.53-1.59C12.61,7.89,12.75,8.43,12.75,9z M5.25,9c0-0.57,0.14-1.11,0.37-1.59C5.96,8.08,6.49,8.63,7.15,9c-0.66,0.37-1.19,0.92-1.53,1.59C5.39,10.11,5.25,9.57,5.25,9z M11.59,11.7c-1.02-0.27-1.82-1.1-2.03-2.14c1.04,0.21,1.87,1.01,2.14,2.03C11.66,11.63,11.63,11.66,11.59,11.7z M6.3,11.59c0.27-1.02,1.1-1.82,2.15-2.03c-0.21,1.04-1.01,1.87-2.03,2.14C6.37,11.66,6.34,11.63,6.3,11.59z M6.41,6.3c1.02,0.27,1.82,1.1,2.03,2.15C7.4,8.23,6.57,7.43,6.3,6.41C6.34,6.37,6.37,6.34,6.41,6.3z M11.7,6.41c-0.27,1.02-1.1,1.82-2.14,2.03c0.21-1.05,1.01-1.87,2.03-2.15C11.63,6.34,11.66,6.37,11.7,6.41z M9,7.15C8.63,6.49,8.08,5.96,7.41,5.62C7.89,5.39,8.43,5.25,9,5.25s1.11,0.14,1.59,0.37C9.92,5.96,9.37,6.49,9,7.15z M9,10.85c0.37,0.66,0.92,1.19,1.59,1.53c-0.49,0.23-1.02,0.37-1.59,0.37s-1.11-0.14-1.59-0.37C8.08,12.04,8.63,11.51,9,10.85z M5.14,11.75C3.86,11.49,2.89,10.36,2.89,9s0.97-2.49,2.25-2.75C4.59,7.03,4.25,7.97,4.25,9C4.25,10.03,4.59,10.97,5.14,11.75z M12.86,6.25c1.28,0.26,2.25,1.39,2.25,2.75s-0.97,2.49-2.25,2.75c0.56-0.78,0.89-1.72,0.89-2.75C13.75,7.97,13.41,7.03,12.86,6.25z M15.01,6.33c-0.59-0.6-1.38-0.99-2.25-1.09c-0.11-0.88-0.49-1.66-1.09-2.25C13.15,3.65,14.35,4.85,15.01,6.33z M6.33,2.99c-0.6,0.59-0.98,1.38-1.09,2.25C4.36,5.35,3.58,5.73,2.99,6.33C3.65,4.85,4.85,3.65,6.33,2.99z M2.99,11.67c0.59,0.6,1.38,0.99,2.25,1.09c0.11,0.88,0.49,1.66,1.09,2.25C4.85,14.35,3.65,13.15,2.99,11.67z M11.67,15.01c0.6-0.59,0.99-1.38,1.09-2.25c0.88-0.11,1.66-0.49,2.25-1.09C14.35,13.15,13.15,14.35,11.67,15.01z"/>
43+ <path fill="#9C0000" d="M9,0C4.04,0,0,4.04,0,9c0,4.96,4.04,9,9,9c4.96,0,9-4.04,9-9C18,4.04,13.96,0,9,0z M9,17c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S13.41,17,9,17z"/>
44+ </g>
45+ <g id="nums">
46+ <path fill="#BA0000" d="M18.01,0.14c0,0-4.39,3.33-4.78,3.63c-0.32-0.33-3.2-3.27-3.2-3.27l2.22,8.92l0.6-0.03c0.75-4.76,4.35-6.63,5.33-7.05c0.14,0.2,0.26,0.42,0.26,0.72c0,0.09-0.01,0.18-0.02,0.28c-0.21,1.67-2.39,4.64-6.68,7.21l-0.31,0.18l0.81,1.01l0.2-0.06c0.06-0.02,6.59-2.06,10.36-1.88l1.14,0.05l-0.96-0.63c-0.08-0.05-1.95-1.25-3.93-1.03c0.28-0.63,2.26-2.56,4.15-4.16l0.31-0.26L18.19,0L18.01,0.14z"/>
47+ <path fill="#BA0000" d="M33.78,24.64c-0.17-0.17-4.21-4.28-5.63-5.01c-0.53-0.27-1.71-0.18-5.75,0.16c0,0-0.17,0.01-0.43,0.04c1.19-2.79,2.72-5.86,3.57-6.25l0.55-0.25l-2.13-2.09l-0.21,0.03c-5.11,0.87-13.08,2.09-14.11,1.9l-0.35-0.07l-0.12,0.33c-0.2,0.56,0.32,4.93,0.86,7.37C5.45,21.2,0,21.66,0,21.66l0.3,0.56l0.68,1.27l0.32-0.09c1.72-0.51,2.52-0.57,4.6-0.54c0.19,0.49,2.6,6.77,2.6,6.77l0.79-0.2c0,0-0.69-5.69-0.8-6.52c0.78,0.02,7.99,0.22,9.67,0.26c-1.09,0.92-3.56,3.02-3.56,3.02l0.4,0.71c0,0,5.15-1.76,5.41-1.85c0.25,0.1,4.3,1.65,4.3,1.65s-2.13-2.57-2.81-3.38c3.78,0.2,4.75,0.42,5.01,0.74c0.33,0.39,0.5,0.96,0.5,1.6c0,1.16-0.58,2.57-1.8,3.73c-1.82-0.46-5.17-2.34-5.21-2.36l-0.52,0.62L24.96,34l0.24-0.05c3.82-0.78,8.44-8.48,8.63-8.81L34,24.87L33.78,24.64z M21.02,16.65c-1.22,0.16-2.77,0.37-3.21,0.43c0-0.33,0-1.17,0-2c1.62-0.13,3.11-0.25,3.49-0.29C21.25,15.06,21.15,15.77,21.02,16.65z M16.09,15.22c0.02,0.84,0.04,1.75,0.05,2.06c-1.41,0.16-2.6,0.26-3.55,0.3c0.1-0.62,0.19-1.31,0.3-2.1C13.13,15.46,14.51,15.35,16.09,15.22z M12.03,20.65c0.08-0.35,0.17-0.76,0.26-1.24c1.3-0.24,2.61-0.46,3.89-0.63c0.01,0.48,0.03,1.1,0.04,1.52C15.97,20.31,14.26,20.46,12.03,20.65zM17.8,20.17c0-0.48,0-1.18,0-1.6c0.98-0.1,1.36-0.08,1.96-0.02l0.96,0.06c-0.07,0.46-0.14,0.92-0.2,1.35C19.43,20.04,18.19,20.14,17.8,20.17z"/>
48+ </g>
49+ <g id="flower">
50+ <rect x="1" y="1" fill="#F8F6D8" stroke="#F8F6D8" width="43" height="67"/>
51+ <path fill="#D3BB54" d="M11.27,58.16l-0.71-8.35c1.76-0.67,2.92-1.76,2.83-2.86c-0.09-1.1-1.42-1.91-3.26-2.16l-0.71-8.36c1.76-0.67,2.92-1.76,2.83-2.86c-0.13-1.56-2.69-2.54-5.71-2.19c-3.02,0.35-5.37,1.89-5.23,3.44c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.13,1.55,2.69,2.53,5.71,2.19c3.02-0.35,5.37-1.89,5.23-3.44C14.43,59.22,13.11,58.41,11.27,58.16z M4.34,34.47c-0.05-0.58,1-1.18,2.34-1.34c1.34-0.15,2.47,0.2,2.52,0.78c0.04,0.49-0.7,0.99-1.74,1.23l0.61,7.13c0.05,0.56-0.19,1.04-0.52,1.08c-0.34,0.04-0.65-0.38-0.7-0.94l-0.61-7.13C5.19,35.28,4.38,34.96,4.34,34.47z M4.57,47.95c-0.05-0.58,1.41-1.23,3.25-1.44c1.85-0.21,3.39,0.09,3.43,0.67c0.05,0.58-1.41,1.23-3.25,1.44C6.15,48.84,4.62,48.54,4.57,47.95z M9.15,62c-1.34,0.15-2.47-0.19-2.52-0.78c-0.04-0.49,0.7-0.99,1.74-1.23l-0.61-7.13c-0.05-0.56,0.19-1.04,0.52-1.08c0.33-0.04,0.65,0.38,0.69,0.94l0.61,7.13c1.06,0,1.87,0.32,1.91,0.81C11.54,61.25,10.49,61.85,9.15,62z"/>
52+ <path fill="none" stroke="#7C5A2D" stroke-width="0.25" d="M11.27,58.16l-0.71-8.35c1.76-0.67,2.92-1.76,2.83-2.86c-0.09-1.1-1.42-1.91-3.26-2.16l-0.71-8.36c1.76-0.67,2.92-1.76,2.83-2.86c-0.13-1.56-2.69-2.54-5.71-2.19c-3.02,0.35-5.37,1.89-5.23,3.44c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.09,1.1,1.42,1.91,3.25,2.16l0.71,8.36c-1.76,0.67-2.92,1.76-2.83,2.86c0.13,1.55,2.69,2.53,5.71,2.19c3.02-0.35,5.37-1.89,5.23-3.44C14.43,59.22,13.11,58.41,11.27,58.16z M4.34,34.47c-0.05-0.58,1-1.18,2.34-1.34c1.34-0.15,2.47,0.2,2.52,0.78c0.04,0.49-0.7,0.99-1.74,1.23l0.61,7.13c0.05,0.56-0.19,1.04-0.52,1.08c-0.34,0.04-0.65-0.38-0.7-0.94l-0.61-7.13C5.19,35.28,4.38,34.96,4.34,34.47z M4.57,47.95c-0.05-0.58,1.41-1.23,3.25-1.44c1.85-0.21,3.39,0.09,3.43,0.67c0.05,0.58-1.41,1.23-3.25,1.44C6.15,48.84,4.62,48.54,4.57,47.95z M9.15,62c-1.34,0.15-2.47-0.19-2.52-0.78c-0.04-0.49,0.7-0.99,1.74-1.23l-0.61-7.13c-0.05-0.56,0.19-1.04,0.52-1.08c0.33-0.04,0.65,0.38,0.69,0.94l0.61,7.13c1.06,0,1.87,0.32,1.91,0.81C11.54,61.25,10.49,61.85,9.15,62z"/>
53+ <path fill="#EADDA7" stroke="#7C5A2D" stroke-width="0.25" d="M5.23,67.99c2.49-8.18-3.33-18.78-3.33-18.78s10.4,10.59,7.48,18.78H5.23z"/>
54+ <path fill="#DBC879" d="M19.02,36.17c-2.92-0.98-5.59-0.56-5.97,0.94c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.85-0.14-3.28,0.37-3.54,1.43c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.84-0.14-3.27,0.37-3.54,1.43c-0.38,1.5,1.68,3.51,4.6,4.5c2.92,0.98,5.59,0.56,5.96-0.94c0.27-1.06-0.7-2.38-2.32-3.41l2.03-8.06c1.85,0.14,3.28-0.37,3.55-1.43c0.27-1.06-0.71-2.38-2.33-3.41l2.03-8.06c1.84,0.14,3.28-0.37,3.54-1.43C23.99,39.16,21.94,37.15,19.02,36.17z M14.41,57.04l-1.73,6.88c0.98,0.45,1.63,1.1,1.51,1.58c-0.14,0.56-1.31,0.66-2.6,0.23c-1.3-0.44-2.23-1.25-2.09-1.81c0.12-0.47,0.97-0.62,2.01-0.39l1.73-6.88c0.13-0.54,0.51-0.88,0.83-0.77C14.39,55.98,14.54,56.5,14.41,57.04z M18.32,52.88c-0.14,0.56-1.7,0.53-3.48-0.07c-1.78-0.6-3.11-1.54-2.97-2.11c0.14-0.56,1.7-0.53,3.48,0.07C17.13,51.37,18.46,52.32,18.32,52.88z M20.68,39.67c-0.12,0.47-0.97,0.62-2.01,0.39l-1.73,6.88c-0.13,0.54-0.51,0.88-0.83,0.77c-0.32-0.11-0.48-0.63-0.34-1.17l1.73-6.88c-0.98-0.45-1.63-1.1-1.51-1.58c0.14-0.56,1.31-0.67,2.6-0.23C19.89,38.3,20.82,39.11,20.68,39.67z"/>
55+ <path fill="none" stroke="#7C5A2D" stroke-width="0.25" d="M19.02,36.17c-2.92-0.98-5.59-0.56-5.97,0.94c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.85-0.14-3.28,0.37-3.54,1.43c-0.27,1.06,0.7,2.38,2.33,3.41l-2.03,8.06c-1.84-0.14-3.27,0.37-3.54,1.43c-0.38,1.5,1.68,3.51,4.6,4.5c2.92,0.98,5.59,0.56,5.96-0.94c0.27-1.06-0.7-2.38-2.32-3.41l2.03-8.06c1.85,0.14,3.28-0.37,3.55-1.43c0.27-1.06-0.71-2.38-2.33-3.41l2.03-8.06c1.84,0.14,3.28-0.37,3.54-1.43C23.99,39.16,21.94,37.15,19.02,36.17z M14.41,57.04l-1.73,6.88c0.98,0.45,1.63,1.1,1.51,1.58c-0.14,0.56-1.31,0.66-2.6,0.23c-1.3-0.44-2.23-1.25-2.09-1.81c0.12-0.47,0.97-0.62,2.01-0.39l1.73-6.88c0.13-0.54,0.51-0.88,0.83-0.77C14.39,55.98,14.54,56.5,14.41,57.04z M18.32,52.88c-0.14,0.56-1.7,0.53-3.48-0.07c-1.78-0.6-3.11-1.54-2.97-2.11c0.14-0.56,1.7-0.53,3.48,0.07C17.13,51.37,18.46,52.32,18.32,52.88z M20.68,39.67c-0.12,0.47-0.97,0.62-2.01,0.39l-1.73,6.88c-0.13,0.54-0.51,0.88-0.83,0.77c-0.32-0.11-0.48-0.63-0.34-1.17l1.73-6.88c-0.98-0.45-1.63-1.1-1.51-1.58c0.14-0.56,1.31-0.67,2.6-0.23C19.89,38.3,20.82,39.11,20.68,39.67z"/>
56+ <path fill="#EADA9B" stroke="#7C5A2D" stroke-width="0.25" d="M19.85,67.7c-1.77-5.79,2.35-13.28,2.35-13.28s-7.35,7.49-5.29,13.28H19.85z"/>
57+ <path fill="#BFAA52" stroke="#7C5A2D" d="M30.33,52.2c-10.89,0-12.13-1.84-15.85-5.97c4.7-3.44,10.9-3.9,16.34,4.82L30.33,52.2z"/>
58+ <path fill="#DBC775" d="M27.52,50.68c-6.89,0-7.68-1.16-10.03-3.78c2.98-2.18,6.9-2.47,10.34,3.05L27.52,50.68z"/>
59+ <path fill="#BFAA52" stroke="#7C5A2D" d="M42.27,47.51c-0.36,2.52-6.85,4.67-9.62,5.14c-2.77,0.47,1.52-5.63,6.18-9.5C41.78,42.64,42.5,45.94,42.27,47.51z"/>
60+ <path fill="#DBC775" d="M37.59,49.31c-0.15,1.08-2.95,2.01-4.14,2.21c-1.19,0.2,0.66-2.42,2.66-4.09C37.38,47.21,37.69,48.63,37.59,49.31z"/>
61+ <path fill="#E5D389" stroke="#7C5A2D" stroke-width="0.75" d="M38.25,67.92c-7.82-3.7-5.44-11.92-5.44-18.72l-2.23-0.29c0,0-2.8,17.48,4.83,18.76L38.25,67.92z"/>
62+ <path fill="#BFAA52" stroke="#7C5A2D" d="M43.33,52.91v7.75c0,0-2.47,2.07-4.21,2.07c-1.73,0-6.93-9.55-6.44-10.59C33.19,51.1,42.84,50.85,43.33,52.91z"/>
63+ <path fill="#DBC775" d="M40.06,53.5c1.02,0.72,0,3.43,0,3.43s-1.33,0.91-2.27,0.91c-0.93,0-3.73-4.22-3.47-4.68S39.04,52.79,40.06,53.5z"/>
64+ <path fill="#BFAA52" stroke="#7C5A2D" d="M31.45,52.39c-12.87-1.29-10.89,4.39-14.11,10.07C27.49,65.3,28.23,57.04,31.45,52.39z"/>
65+ <path fill="#E2CF7F" d="M30.26,53.1c-8.21-0.82-6.95,2.8-9,6.42C27.74,61.34,28.21,56.07,30.26,53.1z"/>
66+ <path fill="#BFAA52" stroke="#7C5A2D" d="M32.93,50.42c0,0,3.69-9.06-0.25-11.11c-4.04-2.11-6.64-1.29-7.13,0c-0.5,1.29,3.91,12.41,5.65,12.66C32.93,52.23,32.93,50.42,32.93,50.42z"/>
67+ <path fill="#DBC775" d="M32.14,50.27c0,0,2.05-5.67-0.14-6.95c-2.24-1.32-3.68-0.81-3.96,0c-0.28,0.81,2.17,7.76,3.13,7.92C32.14,51.4,32.14,50.27,32.14,50.27z"/>
68+ <path fill="#EADDA7" stroke="#7C5A2D" stroke-width="0.25" d="M31.45,51.12c0,0-1.48-2.54-1.23-4.87"/>
69+ <path fill="#BFAA52" stroke="#7C5A2D" d="M30.33,52.6c0,0-4.13,5.57-2.01,5.48c2.41-0.11,2.6,0,5.1,0.46c2.57,0.47,0.2-5.55-1.6-6.32L30.33,52.6z"/>
70+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M27.27,48.13c0,0,2.02,2.93,3.35,2.93c1.33,0,0.61,2.18,0,1.56C30,51.99,28.15,50.19,27.05,50C26.23,50,26.04,47.66,27.27,48.13z"/>
71+ <path fill="#D3BB54" stroke="#AF9A41" d="M44,12.51c-4.46,0-8.54,2.85-8.54,6.37c0,3.52,4.08,6.37,8.54,6.37V12.51z"/>
72+ <path fill="#D3BB54" stroke="#AF9A41" d="M33.33,7.64c0-3.52-3.62-6.23-8.08-6.23c-4.46,0-8.08,2.71-8.08,6.23s3.62,6.37,8.08,6.37C29.71,14.01,33.33,11.16,33.33,7.64z"/>
73+ <ellipse fill="#D3BB54" stroke="#AF9A41" cx="29.56" cy="17.54" rx="8.08" ry="6.37"/>
74+ <path fill="#DBC879" stroke="#AF9A41" d="M44,16.35V1.42H30.9c-2.93,1.56-3.79,4.49-3.79,7.49c0,4.79,4.93,8.68,11.02,8.68C40.21,17.58,42.34,17.13,44,16.35z"/>
75+ <path fill="#D3BB54" stroke="#AF9A41" d="M31.94,15.47c0,0-0.72,1.82-3.03,1.82"/>
76+ <path fill="#D3BB54" stroke="#AF9A41" d="M27.19,6.62c0,0-1.73,0.57-3.17-1.02"/>
77+ <path fill="#663333" d="M44,0H0v69h45V0H44z M43,2c0,1.91,0,63.09,0,65c-1.86,0-39.14,0-41,0C2,65.09,2,3.91,2,2C3.86,2,41.14,2,43,2z"/>
78+ <path fill="#E2CF7F" d="M31.13,52.9c0,0-1.71,2.31-0.83,2.27c1-0.04,1.08,0,2.11,0.19c1.06,0.2,0.08-2.3-0.66-2.62L31.13,52.9z"/>
79+ </g>
80+ <g id="season">
81+ <rect x="1" y="1" fill="#E3E4FF" width="43" height="67"/>
82+ <polygon fill="#CAE0FF" stroke="#99B9FF" points="4.69,42.38 9.02,39.76 10.2,44.33 6.26,48.26 8.62,49.89 10.2,48.26 10.98,51.52 14.91,51.19 14.52,47.93 17.27,49.24 18.85,47.28 14.13,44.01 13.34,39.44 19.24,41.4 20.81,45.97 23.56,44.99 22.77,42.7 26.71,44.33 28.28,41.07 23.96,39.44 27.11,38.45 25.92,36.17 20.42,37.8 14.91,35.84 20.03,32.9 25.92,33.88 26.31,31.27 23.17,30.94 26.31,28.98 23.96,26.37 20.81,28 20.81,26.04 18.06,25.71 17.27,30.61 12.55,33.23 11.77,28.66 15.7,24.41 12.95,23.1 11.38,25.06 10.58,21.47 7.05,22.12 7.45,25.39 4.69,24.08 3.12,25.71 7.45,28.66 8.23,33.55 3.12,31.92 1,27.81 1,34.92 1.16,34.86 6.26,36.82 1.55,39.76 1,39.66 1,45.06 1.16,44.99 1,45.91 1,47.31 3.51,47.6"/>
83+ <path fill="#99B9FF" stroke="#678ED3" d="M40.63,26.67c1.1,0,2.17-0.13,3.21-0.35V1.5h-14.3c-2.44,2.67-3.95,6.22-3.95,10.13C25.59,19.94,32.32,26.67,40.63,26.67z"/>
84+ <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="22.61,14.9 22.3,14.59 20.96,16.9 23.27,18.23 23.39,17.81"/>
85+ <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="34.45,28.87 34.03,28.98 35.36,31.29 37.67,29.96 37.35,29.65"/>
86+ <polygon fill="#798CD8" points="43.83,30.1 43.83,29.71 43.59,29.96"/>
87+ <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="23.39,5.45 23.27,5.03 20.96,6.36 22.3,8.67 22.61,8.36"/>
88+ <polygon fill="#99B9FF" stroke="#678ED3" points="23.31,15.6 23.31,7.66 22.61,8.36 22.3,8.67 19.34,11.63 22.3,14.59 22.61,14.9"/>
89+ <polygon fill="#C9D3F2" stroke="#9EB3E8" stroke-width="0.5" points="26.66,23.47 26.23,23.36 26.23,26.02 28.9,26.02 28.78,25.6"/>
90+ <polygon fill="#99B9FF" stroke="#678ED3" points="37.35,29.65 37.67,29.96 40.63,32.92 43.59,29.96 43.83,29.71 43.83,28.95 36.66,28.95"/>
91+ <polygon fill="#99B9FF" stroke="#678ED3" points="35.41,28.61 28.53,24.64 28.78,25.6 28.9,26.02 29.98,30.07 34.03,28.98 34.45,28.87"/>
92+ <polygon fill="#99B9FF" stroke="#678ED3" points="27.61,23.73 23.64,16.85 23.39,17.81 23.27,18.23 22.19,22.27 26.23,23.36 26.66,23.47"/>
93+ <polygon fill="#99B9FF" stroke="#678ED3" points="23.39,5.45 23.64,6.41 26.48,1.5 22.33,1.5 23.27,5.03 "/>
94+ <path fill="#99B9FF" stroke="#678ED3" d="M30.67,46.67c0,0,2.32-3,7-3c5.67,0,5.67,9.33,5.67,9.33H19.67c0,0-0.33-6.67,7.67-6.67c3.67,0,6,4,6,4"/>
95+ <line fill="#99B9FF" stroke="#678ED3" x1="29.5" y1="57.27" x2="24.83" y2="65.36"/>
96+ <line fill="#99B9FF" stroke="#678ED3" x1="34.22" y1="55.68" x2="28.68" y2="65.95"/>
97+ <line fill="#99B9FF" stroke="#678ED3" x1="23.33" y1="61" x2="20.5" y2="65.91"/>
98+ <line fill="#99B9FF" stroke="#678ED3" x1="37.46" y1="54.63" x2="33.25" y2="62.59"/>
99+ <line fill="#99B9FF" stroke="#678ED3" x1="40.46" y1="62.17" x2="38.13" y2="66.21"/>
100+ <line fill="#99B9FF" stroke="#678ED3" x1="27.11" y1="54.52" x2="24.44" y2="59.14"/>
101+ <line fill="#99B9FF" stroke="#678ED3" x1="31.67" y1="54" x2="30.67" y2="55.73"/>
102+ <line fill="#99B9FF" stroke="#678ED3" x1="40.98" y1="55.6" x2="37.81" y2="61.09"/>
103+ <line fill="#99B9FF" stroke="#678ED3" x1="23.84" y1="55.04" x2="22.18" y2="57.92"/>
104+ <line fill="#99B9FF" stroke="#678ED3" x1="21.13" y1="55.63" x2="16.91" y2="63.59"/>
105+ <line fill="#99B9FF" stroke="#678ED3" x1="36" y1="63.6" x2="35" y2="65.33"/>
106+ <path fill="#678ED3" d="M44,0H0v69h45V0H44z M43,2c0,1.91,0,63.09,0,65c-1.86,0-39.14,0-41,0C2,65.09,2,3.91,2,2C3.86,2,41.14,2,43,2z"/>
107+ </g>
108+ <!-- 一索 is a bird, not a stick: a sparrow on a perch, drawn line-art in
109+ the sheet's own green, red and blue. Origin is the glyph's top-left; it
110+ occupies a 44x56 box that centers in the tile face. -->
111+ <g id="yitiao" fill="none" stroke-linecap="round" stroke-linejoin="round">
112+ <!-- perch, with a knob at each end -->
113+ <g stroke="#006C00" fill="#006C00">
114+ <path stroke-width="1.4" d="M4.5,35H39.5"/>
115+ <circle cx="3.6" cy="35" r="1.9"/>
116+ <circle cx="40.4" cy="35" r="1.9"/>
117+ </g>
118+ <!-- tail plumes -->
119+ <g stroke="#006C00" stroke-width="0.9">
120+ <path d="M19.6,30.5C16,37,14.2,44,14,51.5"/>
121+ <path d="M20.8,31C18,38,17,45.5,17.5,53.5"/>
122+ <path d="M22,31.4C20.2,38.5,20.2,46.5,21.5,55"/>
123+ <path d="M23.2,31.4C22.6,38.5,23.6,46.5,25.8,55.5"/>
124+ <path d="M24.3,31C24.8,38,26.4,45.5,29,54"/>
125+ <path d="M25.2,30.3C26.4,36.5,28.6,43.5,31.6,51"/>
126+ </g>
127+ <!-- far wing -->
128+ <path stroke="#294D8E" stroke-width="1.2" d="M25.5,19.5C30,20.5,34.8,24.5,38.8,31C34.5,29.2,30,28.4,27,28.7"/>
129+ <!-- long flight feather, sweeping past the perch -->
130+ <path stroke="#294D8E" stroke-width="1.2" d="M23,23.5C17.5,27.5,10.8,34.5,5,44.5C11.5,38.5,17.8,32.5,22,29"/>
131+ <!-- near wing, folded over the breast -->
132+ <path stroke="#294D8E" stroke-width="1.2" d="M24,17.8C17.4,18.6,10.6,23.4,6.2,31.2C12,27.4,17.6,25.6,21.4,26.2C22.8,23.4,23.6,20.6,24,17.8Z"/>
133+ <g stroke="#294D8E" stroke-width="0.6">
134+ <path d="M19,19.6C18.2,21.8,17.4,24.2,16.6,26.4"/>
135+ <path d="M16.6,21C15.8,23.2,14.8,25.4,14,27.4"/>
136+ <path d="M14.2,22.6C13.4,24.6,12.4,26.6,11.6,28.6"/>
137+ <path d="M11.8,24.6C11,26.4,10.2,28.2,9.4,29.8"/>
138+ </g>
139+ <!-- head, beak, back and breast -->
140+ <g stroke="#9C0000" stroke-width="1.3">
141+ <path d="M20,8.6C17.2,7.8,14.4,9.4,13.2,11.8L7.4,13.9L13.2,15.9C12.6,17.6,12.4,19.4,12.7,21"/>
142+ <path d="M20.8,9.2C24.4,12,26.4,16.2,26.8,21.4C27.2,26.4,26.2,30,24.4,32"/>
143+ <path d="M13.4,17.2C14.2,22.4,16.4,27.4,19.8,31.2"/>
144+ </g>
145+ <g stroke="#9C0000" stroke-width="0.7">
146+ <path d="M15.4,18.4C16,22,17.2,25.4,19,28.6"/>
147+ <path d="M18.4,19.2C18.8,22.8,19.8,26.2,21.4,29.6"/>
148+ <path d="M21.6,20C21.8,23.6,22.6,26.8,24,30.2"/>
149+ </g>
150+ <!-- swept-back crest -->
151+ <g stroke="#006C00" stroke-width="1.2">
152+ <path d="M13.8,10.6C13.2,5.6,16.2,2.4,20.2,2.8C23.4,3.2,24.4,5.4,22.4,6.8"/>
153+ <path d="M16.6,8.8C17.8,7.2,20,6.2,22.4,6.8"/>
154+ </g>
155+ <path stroke="#9C0000" stroke-width="0.7" d="M8.8,14.6L13,14.2"/>
156+ <circle fill="#9C0000" stroke="none" cx="16.8" cy="11.9" r="1.4"/>
157+ </g>
158+ </defs>
159+ <use x="0" y="0" xlink:href="#bt1"/>
160+ <use x="64" y="0" xlink:href="#bt1"/>
161+ <use x="128" y="0" xlink:href="#bt1"/>
162+ <use x="192" y="0" xlink:href="#bt1"/>
163+ <use x="256" y="0" xlink:href="#bt1"/>
164+ <use x="320" y="0" xlink:href="#bt1"/>
165+ <use x="384" y="0" xlink:href="#bt1"/>
166+ <use x="448" y="0" xlink:href="#bt1"/>
167+ <use x="512" y="0" xlink:href="#bt1"/>
168+ <use x="576" y="0" xlink:href="#bt1"/>
169+ <use x="640" y="0" xlink:href="#bt1"/>
170+ <use x="704" y="0" xlink:href="#bt1"/>
171+ <use x="768" y="0" xlink:href="#bt1"/>
172+ <use x="832" y="0" xlink:href="#bt1"/>
173+ <use x="896" y="0" xlink:href="#bt1"/>
174+ <use x="960" y="0" xlink:href="#bt1"/>
175+ <use x="1024" y="0" xlink:href="#bt1"/>
176+ <use x="1088" y="0" xlink:href="#bt1"/>
177+ <use x="1152" y="0" xlink:href="#bt1"/>
178+ <use x="1216" y="0" xlink:href="#bt1"/>
179+ <use x="1280" y="0" xlink:href="#bt1"/>
180+ <use x="1344" y="0" xlink:href="#bt1"/>
181+ <use x="1408" y="0" xlink:href="#bt1"/>
182+ <use x="1472" y="0" xlink:href="#bt1"/>
183+ <use x="1536" y="0" xlink:href="#bt1"/>
184+ <use x="1600" y="0" xlink:href="#bt1"/>
185+ <use x="1664" y="0" xlink:href="#bt1"/>
186+ <use x="1728" y="0" xlink:href="#bt1"/>
187+ <use x="1792" y="0" xlink:href="#bt1"/>
188+ <use x="1856" y="0" xlink:href="#bt1"/>
189+ <use x="1920" y="0" xlink:href="#bt1"/>
190+ <use x="1984" y="0" xlink:href="#bt1"/>
191+ <use x="2048" y="0" xlink:href="#bt1"/>
192+ <use x="2112" y="0" xlink:href="#bt1"/>
193+ <use x="2176" y="0" xlink:href="#bt1"/>
194+ <use x="2240" y="0" xlink:href="#bt1"/>
195+ <use x="2304" y="0" xlink:href="#bt1"/>
196+ <use x="2368" y="0" xlink:href="#bt1"/>
197+ <use x="2432" y="0" xlink:href="#bt1"/>
198+ <use x="2496" y="0" xlink:href="#bt1"/>
199+ <use x="2560" y="0" xlink:href="#bt1"/>
200+ <use x="2624" y="0" xlink:href="#bt1"/>
201+ <use x="2688" y="0" xlink:href="#bt1"/>
202+ <use x="0" y="88" xlink:href="#bt2"/>
203+ <use x="64" y="88" xlink:href="#bt2"/>
204+ <use x="128" y="88" xlink:href="#bt2"/>
205+ <use x="192" y="88" xlink:href="#bt2"/>
206+ <use x="256" y="88" xlink:href="#bt2"/>
207+ <use x="320" y="88" xlink:href="#bt2"/>
208+ <use x="384" y="88" xlink:href="#bt2"/>
209+ <use x="448" y="88" xlink:href="#bt2"/>
210+ <use x="512" y="88" xlink:href="#bt2"/>
211+ <use x="576" y="88" xlink:href="#bt2"/>
212+ <use x="640" y="88" xlink:href="#bt2"/>
213+ <use x="704" y="88" xlink:href="#bt2"/>
214+ <use x="768" y="88" xlink:href="#bt2"/>
215+ <use x="832" y="88" xlink:href="#bt2"/>
216+ <use x="896" y="88" xlink:href="#bt2"/>
217+ <use x="960" y="88" xlink:href="#bt2"/>
218+ <use x="1024" y="88" xlink:href="#bt2"/>
219+ <use x="1088" y="88" xlink:href="#bt2"/>
220+ <use x="1152" y="88" xlink:href="#bt2"/>
221+ <use x="1216" y="88" xlink:href="#bt2"/>
222+ <use x="1280" y="88" xlink:href="#bt2"/>
223+ <use x="1344" y="88" xlink:href="#bt2"/>
224+ <use x="1408" y="88" xlink:href="#bt2"/>
225+ <use x="1472" y="88" xlink:href="#bt2"/>
226+ <use x="1536" y="88" xlink:href="#bt2"/>
227+ <use x="1600" y="88" xlink:href="#bt2"/>
228+ <use x="1664" y="88" xlink:href="#bt2"/>
229+ <use x="1728" y="88" xlink:href="#bt2"/>
230+ <use x="1792" y="88" xlink:href="#bt2"/>
231+ <use x="1856" y="88" xlink:href="#bt2"/>
232+ <use x="1920" y="88" xlink:href="#bt2"/>
233+ <use x="1984" y="88" xlink:href="#bt2"/>
234+ <use x="2048" y="88" xlink:href="#bt2"/>
235+ <use x="2112" y="88" xlink:href="#bt2"/>
236+ <use x="2176" y="88" xlink:href="#bt2"/>
237+ <use x="2240" y="88" xlink:href="#bt2"/>
238+ <use x="2304" y="88" xlink:href="#bt2"/>
239+ <use x="2368" y="88" xlink:href="#bt2"/>
240+ <use x="2432" y="88" xlink:href="#bt2"/>
241+ <use x="2496" y="88" xlink:href="#bt2"/>
242+ <use x="2560" y="88" xlink:href="#bt2"/>
243+ <use x="2624" y="88" xlink:href="#bt2"/>
244+ <use x="2688" y="88" xlink:href="#bt2"/>
245+ <use x="979" y="41" xlink:href="#nums"/>
246+ <use x="1043" y="41" xlink:href="#nums"/>
247+ <use x="1107" y="41" xlink:href="#nums"/>
248+ <use x="1171" y="41" xlink:href="#nums"/>
249+ <use x="1235" y="41" xlink:href="#nums"/>
250+ <use x="1299" y="41" xlink:href="#nums"/>
251+ <use x="1363" y="41" xlink:href="#nums"/>
252+ <use x="1427" y="41" xlink:href="#nums"/>
253+ <use x="1491" y="41" xlink:href="#nums"/>
254+ <use x="979" y="129" xlink:href="#nums"/>
255+ <use x="1043" y="129" xlink:href="#nums"/>
256+ <use x="1107" y="129" xlink:href="#nums"/>
257+ <use x="1171" y="129" xlink:href="#nums"/>
258+ <use x="1235" y="129" xlink:href="#nums"/>
259+ <use x="1299" y="129" xlink:href="#nums"/>
260+ <use x="1363" y="129" xlink:href="#nums"/>
261+ <use x="1427" y="129" xlink:href="#nums"/>
262+ <use x="1491" y="129" xlink:href="#nums"/>
263+ <use x="2125" y="6" xlink:href="#season"/>
264+ <use x="2189" y="6" xlink:href="#season"/>
265+ <use x="2253" y="6" xlink:href="#season"/>
266+ <use x="2317" y="6" xlink:href="#season"/>
267+ <use x="2125" y="94" xlink:href="#season"/>
268+ <use x="2189" y="94" xlink:href="#season"/>
269+ <use x="2253" y="94" xlink:href="#season"/>
270+ <use x="2317" y="94" xlink:href="#season"/>
271+ <use x="2445" y="6" xlink:href="#flower"/>
272+ <use x="2509" y="6" xlink:href="#flower"/>
273+ <use x="2573" y="6" xlink:href="#flower"/>
274+ <use x="2637" y="6" xlink:href="#flower"/>
275+ <use x="2445" y="94" xlink:href="#flower"/>
276+ <use x="2509" y="94" xlink:href="#flower"/>
277+ <use x="2573" y="94" xlink:href="#flower"/>
278+ <use x="2637" y="94" xlink:href="#flower"/>
279+ <use transform="translate(88.5,16.5) scale(1.22)" xlink:href="#dots"/>
280+ <use transform="translate(88.5,42.5) scale(1.22)" xlink:href="#dots"/>
281+ <use transform="translate(88.5,104.5) scale(1.22)" xlink:href="#dots"/>
282+ <use transform="translate(88.5,130.5) scale(1.22)" xlink:href="#dots"/>
283+ <use transform="translate(144.5,12.5)" xlink:href="#dots"/>
284+ <use transform="translate(144.5,100.5)" xlink:href="#dots"/>
285+ <use transform="translate(154.5,31.5)" xlink:href="#dots2"/>
286+ <use transform="translate(154.5,119.5)" xlink:href="#dots2"/>
287+ <use transform="translate(164.5,50.5)" xlink:href="#dots"/>
288+ <use transform="translate(164.5,138.5)" xlink:href="#dots"/>
289+ <use transform="translate(209,15) scale(.94)" xlink:href="#dots"/>
290+ <use transform="translate(209,49) scale(.94)" xlink:href="#dots"/>
291+ <use transform="translate(209,103) scale(.94)" xlink:href="#dots"/>
292+ <use transform="translate(209,137) scale(.94)" xlink:href="#dots"/>
293+ <use transform="translate(229,15) scale(.94)" xlink:href="#dots"/>
294+ <use transform="translate(229,49) scale(.94)" xlink:href="#dots"/>
295+ <use transform="translate(229,103) scale(.94)" xlink:href="#dots"/>
296+ <use transform="translate(229,137) scale(.94)" xlink:href="#dots"/>
297+ <use transform="translate(273.5,13.5) scale(.88)" xlink:href="#dots"/>
298+ <use transform="translate(273.5,51.5) scale(.88)" xlink:href="#dots"/>
299+ <use transform="translate(273.5,101.5) scale(.88)" xlink:href="#dots"/>
300+ <use transform="translate(273.5,139.5) scale(.88)" xlink:href="#dots"/>
301+ <use transform="translate(283.5,32.5) scale(.88)" xlink:href="#dots2"/>
302+ <use transform="translate(283.5,120.5) scale(.88)" xlink:href="#dots2"/>
303+ <use transform="translate(293.5,13.5) scale(.88)" xlink:href="#dots"/>
304+ <use transform="translate(293.5,51.5) scale(.88)" xlink:href="#dots"/>
305+ <use transform="translate(293.5,101.5) scale(.88)" xlink:href="#dots"/>
306+ <use transform="translate(293.5,139.5) scale(.88)" xlink:href="#dots"/>
307+ <use transform="translate(340,14) scale(.72)" xlink:href="#dots"/>
308+ <use transform="translate(340,37) scale(.72)" xlink:href="#dots2"/>
309+ <use transform="translate(340,54) scale(.72)" xlink:href="#dots2"/>
310+ <use transform="translate(340,102) scale(.72)" xlink:href="#dots"/>
311+ <use transform="translate(340,125) scale(.72)" xlink:href="#dots2"/>
312+ <use transform="translate(340,142) scale(.72)" xlink:href="#dots2"/>
313+ <use transform="translate(358,14) scale(.72)" xlink:href="#dots"/>
314+ <use transform="translate(358,37) scale(.72)" xlink:href="#dots2"/>
315+ <use transform="translate(358,54) scale(.72)" xlink:href="#dots2"/>
316+ <use transform="translate(358,102) scale(.72)" xlink:href="#dots"/>
317+ <use transform="translate(358,125) scale(.72)" xlink:href="#dots2"/>
318+ <use transform="translate(358,142) scale(.72)" xlink:href="#dots2"/>
319+ <use transform="translate(399,10) scale(.72)" xlink:href="#dots"/>
320+ <use transform="translate(404,41) scale(.72)" xlink:href="#dots2"/>
321+ <use transform="translate(404,58) scale(.72)" xlink:href="#dots2"/>
322+ <use transform="translate(413,17) scale(.72)" xlink:href="#dots"/>
323+ <use transform="translate(427,24) scale(.72)" xlink:href="#dots"/>
324+ <use transform="translate(404,129) scale(.72)" xlink:href="#dots2"/>
325+ <use transform="translate(404,146) scale(.72)" xlink:href="#dots2"/>
326+ <use transform="translate(399,98) scale(.72)" xlink:href="#dots"/>
327+ <use transform="translate(422,41) scale(.72)" xlink:href="#dots2"/>
328+ <use transform="translate(422,58) scale(.72)" xlink:href="#dots2"/>
329+ <use transform="translate(413,105) scale(.72)" xlink:href="#dots"/>
330+ <use transform="translate(427,112) scale(.72)" xlink:href="#dots"/>
331+ <use transform="translate(422,129) scale(.72)" xlink:href="#dots2"/>
332+ <use transform="translate(422,146) scale(.72)" xlink:href="#dots2"/>
333+ <use transform="translate(469,10) scale(.72)" xlink:href="#dots"/>
334+ <use transform="translate(469,26) scale(.72)" xlink:href="#dots"/>
335+ <use transform="translate(469,42) scale(.72)" xlink:href="#dots"/>
336+ <use transform="translate(469,58) scale(.72)" xlink:href="#dots"/>
337+ <use transform="translate(469,98) scale(.72)" xlink:href="#dots"/>
338+ <use transform="translate(469,114) scale(.72)" xlink:href="#dots"/>
339+ <use transform="translate(469,130) scale(.72)" xlink:href="#dots"/>
340+ <use transform="translate(469,146) scale(.72)" xlink:href="#dots"/>
341+ <use transform="translate(485,10) scale(.72)" xlink:href="#dots"/>
342+ <use transform="translate(485,26) scale(.72)" xlink:href="#dots"/>
343+ <use transform="translate(485,42) scale(.72)" xlink:href="#dots"/>
344+ <use transform="translate(485,58) scale(.72)" xlink:href="#dots"/>
345+ <use transform="translate(485,98) scale(.72)" xlink:href="#dots"/>
346+ <use transform="translate(485,114) scale(.72)" xlink:href="#dots"/>
347+ <use transform="translate(485,130) scale(.72)" xlink:href="#dots"/>
348+ <use transform="translate(485,146) scale(.72)" xlink:href="#dots"/>
349+ <use transform="translate(526,14) scale(.72)" xlink:href="#dots"/>
350+ <use transform="translate(526,34) scale(.72)" xlink:href="#dots2"/>
351+ <use transform="translate(526,54) scale(.72)" xlink:href="#dots"/>
352+ <use transform="translate(526,102) scale(.72)" xlink:href="#dots"/>
353+ <use transform="translate(526,122) scale(.72)" xlink:href="#dots2"/>
354+ <use transform="translate(526,142) scale(.72)" xlink:href="#dots"/>
355+ <use transform="translate(541,14) scale(.72)" xlink:href="#dots"/>
356+ <use transform="translate(541,34) scale(.72)" xlink:href="#dots2"/>
357+ <use transform="translate(541,54) scale(.72)" xlink:href="#dots"/>
358+ <use transform="translate(541,102) scale(.72)" xlink:href="#dots"/>
359+ <use transform="translate(541,122) scale(.72)" xlink:href="#dots2"/>
360+ <use transform="translate(541,142) scale(.72)" xlink:href="#dots"/>
361+ <use transform="translate(556,14) scale(.72)" xlink:href="#dots"/>
362+ <use transform="translate(556,34) scale(.72)" xlink:href="#dots2"/>
363+ <use transform="translate(556,54) scale(.72)" xlink:href="#dots"/>
364+ <use transform="translate(556,102) scale(.72)" xlink:href="#dots"/>
365+ <use transform="translate(556,122) scale(.72)" xlink:href="#dots2"/>
366+ <use transform="translate(556,142) scale(.72)" xlink:href="#dots"/>
367+ <use transform="translate(1550.5,11.9)" xlink:href="#yitiao"/>
368+ <use transform="translate(1550.5,99.9)" xlink:href="#yitiao"/>
369+ <use transform="translate(1630,9.5)" xlink:href="#bams"/>
370+ <use transform="translate(1630,44.5)" xlink:href="#bams"/>
371+ <use transform="translate(1630,97.5)" xlink:href="#bams"/>
372+ <use transform="translate(1630,132.5)" xlink:href="#bams"/>
373+ <use transform="translate(1682,44.5)" xlink:href="#bams"/>
374+ <use transform="translate(1682,132.5)" xlink:href="#bams"/>
375+ <use transform="translate(1694,9.5)" xlink:href="#bams"/>
376+ <use transform="translate(1694,97.5)" xlink:href="#bams"/>
377+ <use transform="translate(1705,44.5)" xlink:href="#bams"/>
378+ <use transform="translate(1705,132.5)" xlink:href="#bams"/>
379+ <use transform="translate(1746,9.5)" xlink:href="#bams"/>
380+ <use transform="translate(1746,44.5)" xlink:href="#bams"/>
381+ <use transform="translate(1746,97.5)" xlink:href="#bams"/>
382+ <use transform="translate(1746,132.5)" xlink:href="#bams"/>
383+ <use transform="translate(1770,9.5)" xlink:href="#bams"/>
384+ <use transform="translate(1770,44.5)" xlink:href="#bams"/>
385+ <use transform="translate(1770,97.5)" xlink:href="#bams"/>
386+ <use transform="translate(1770,132.5)" xlink:href="#bams"/>
387+ <use transform="translate(1810,10.5) scale(.91,.925)" xlink:href="#bams"/>
388+ <use transform="translate(1810,45.5) scale(.91,.925)" xlink:href="#bams"/>
389+ <use transform="translate(1810,98.5) scale(.91,.925)" xlink:href="#bams"/>
390+ <use transform="translate(1810,133.5) scale(.91,.925)" xlink:href="#bams"/>
391+ <use transform="translate(1822.5,116) scale(.91,.925)" xlink:href="#bams2"/>
392+ <use transform="translate(1822.5,28) scale(.91,.925)" xlink:href="#bams2"/>
393+ <use transform="translate(1835,10.5) scale(.91,.925)" xlink:href="#bams"/>
394+ <use transform="translate(1835,45.5) scale(.91,.925)" xlink:href="#bams"/>
395+ <use transform="translate(1835,98.5) scale(.91,.925)" xlink:href="#bams"/>
396+ <use transform="translate(1835,133.5) scale(.91,.925)" xlink:href="#bams"/>
397+ <use transform="translate(1875,12.5) scale(.82,.85)" xlink:href="#bams"/>
398+ <use transform="translate(1875,45.5) scale(.82,.85)" xlink:href="#bams"/>
399+ <use transform="translate(1875,100.5) scale(.82,.85)" xlink:href="#bams"/>
400+ <use transform="translate(1875,133.5) scale(.82,.85)" xlink:href="#bams"/>
401+ <use transform="translate(1887,12.5) scale(.82,.85)" xlink:href="#bams"/>
402+ <use transform="translate(1887,45.5) scale(.82,.85)" xlink:href="#bams"/>
403+ <use transform="translate(1887,100.5) scale(.82,.85)" xlink:href="#bams"/>
404+ <use transform="translate(1887,133.5) scale(.82,.85)" xlink:href="#bams"/>
405+ <use transform="translate(1899,12.5) scale(.82,.85)" xlink:href="#bams"/>
406+ <use transform="translate(1899,45.5) scale(.82,.85)" xlink:href="#bams"/>
407+ <use transform="translate(1899,100.5) scale(.82,.85)" xlink:href="#bams"/>
408+ <use transform="translate(1899,133.5) scale(.82,.85)" xlink:href="#bams"/>
409+ <use transform="translate(1939.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
410+ <use transform="translate(1951.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
411+ <use transform="translate(1963.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
412+ <use transform="translate(1939.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
413+ <use transform="translate(1951.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
414+ <use transform="translate(1963.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
415+ <use transform="translate(1939.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
416+ <use transform="translate(1951.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
417+ <use transform="translate(1963.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
418+ <use transform="translate(1939.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
419+ <use transform="translate(1951.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
420+ <use transform="translate(1963.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
421+ <use transform="translate(1951.5,12.5) scale(.73,.66)" xlink:href="#bams2"/>
422+ <use transform="translate(1951.5,100.5) scale(.73,.66)" xlink:href="#bams2"/>
423+ <use transform="translate(2003.5,13.5) scale(.73,.77)" xlink:href="#bams"/>
424+ <use transform="translate(2003.5,46.5) scale(.73,.77)" xlink:href="#bams"/>
425+ <use transform="translate(2003.5,101.5) scale(.73,.77)" xlink:href="#bams"/>
426+ <use transform="translate(2003.5,134.5) scale(.73,.77)" xlink:href="#bams"/>
427+ <use transform="translate(2027.5,13.5) scale(.73,.77)" xlink:href="#bams"/>
428+ <use transform="translate(2027.5,46.5) scale(.73,.77)" xlink:href="#bams"/>
429+ <use transform="translate(2027.5,101.5) scale(.73,.77)" xlink:href="#bams"/>
430+ <use transform="translate(2027.5,134.5) scale(.73,.77)" xlink:href="#bams"/>
431+ <use transform="translate(2067.5,12.5) scale(.73,.66)" xlink:href="#bams"/>
432+ <use transform="translate(2067.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
433+ <use transform="translate(2067.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
434+ <use transform="translate(2067.5,100.5) scale(.73,.66)" xlink:href="#bams"/>
435+ <use transform="translate(2067.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
436+ <use transform="translate(2067.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
437+ <use transform="translate(2079.5,12.5) scale(.73,.66)" xlink:href="#bams2"/>
438+ <use transform="translate(2079.5,31.5) scale(.73,.66)" xlink:href="#bams2"/>
439+ <use transform="translate(2079.5,50.5) scale(.73,.66)" xlink:href="#bams2"/>
440+ <use transform="translate(2079.5,100.5) scale(.73,.66)" xlink:href="#bams2"/>
441+ <use transform="translate(2079.5,119.5) scale(.73,.66)" xlink:href="#bams2"/>
442+ <use transform="translate(2079.5,138.5) scale(.73,.66)" xlink:href="#bams2"/>
443+ <use transform="translate(2091.5,12.5) scale(.73,.66)" xlink:href="#bams"/>
444+ <use transform="translate(2091.5,31.5) scale(.73,.66)" xlink:href="#bams"/>
445+ <use transform="translate(2091.5,50.5) scale(.73,.66)" xlink:href="#bams"/>
446+ <use transform="translate(2091.5,100.5) scale(.73,.66)" xlink:href="#bams"/>
447+ <use transform="translate(2091.5,119.5) scale(.73,.66)" xlink:href="#bams"/>
448+ <use transform="translate(2091.5,138.5) scale(.73,.66)" xlink:href="#bams"/>
449+ <path fill="#006C00" d="M2031.75,29.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.58-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8s-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8S2032.33,30.64,2031.75,29.81z M2021.19,22.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58c0.8-0.56,1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.3,0.21,0.65,0.01,0.79C2021.73,22.9,2021.4,22.77,2021.19,22.48z M2021.5,26.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2022.79,26.33,2021.71,26.71,2021.5,26.4z M2028.87,32.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.29-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2030.15,31.39,2029.67,32.1,2028.87,32.67z"/>
450+ <path fill="#006C00" d="M2021.52,16.4c-1.81-1.27-3.75-1.62-4.32-0.8c-0.41,0.58-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.35-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.63,4.32,0.8c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2024.33,19.36,2023.33,17.67,2021.52,16.4z M2013.92,28.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.31c-0.22,0.31-1.04,0.11-1.85-0.46s-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2014.13,27.38,2014.13,27.74,2013.92,28.03z M2017.5,26.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2017,25.21,2017.72,26.09,2017.5,26.4z M2021.93,18.91c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.3-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46S2022.15,18.6,2021.93,18.91z"/>
451+ <path fill="#006C00" d="M2031.75,117.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.58-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8c-1.81,1.27-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2031.33,120.33,2032.33,118.64,2031.75,117.81z M2021.19,110.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.3,0.21,0.65,0.01,0.79C2021.73,110.9,2021.4,110.77,2021.19,110.48z M2021.5,114.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2022.79,114.34,2021.71,114.71,2021.5,114.4z M2028.87,120.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.31l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2030.15,119.39,2029.67,120.1,2028.87,120.67z"/>
452+ <path fill="#006C00" d="M2021.52,104.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.35-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2024.33,107.36,2023.33,105.67,2021.52,104.4z M2013.92,116.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.31c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.29,0.54-0.42,0.74-0.28C2014.13,115.38,2014.13,115.74,2013.92,116.03z M2017.5,114.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2017,113.21,2017.72,114.09,2017.5,114.4z M2021.93,106.91c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.3-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2021.67,105.9,2022.15,106.61,2021.93,106.91z"/>
453+ <path fill="#006C00" d="M2023.75,148.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8s-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2023.33,151.33,2024.33,149.64,2023.75,148.81z M2013.19,141.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.29,0.21,0.65,0.01,0.79C2013.73,141.9,2013.4,141.77,2013.19,141.48z M2013.5,145.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96S2013.71,145.71,2013.5,145.4zM2020.87,151.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2022.15,150.4,2021.67,151.1,2020.87,151.67z"/>
454+ <path fill="#006C00" d="M2029.52,135.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.35,0.53,2.75-0.06c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2032.33,138.36,2031.33,136.67,2029.52,135.4z M2021.92,147.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.3c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2022.13,146.39,2022.13,146.74,2021.92,147.03z M2025.5,145.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2025,144.21,2025.72,145.09,2025.5,145.4z M2029.93,137.92c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.29-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2029.67,136.9,2030.15,137.61,2029.93,137.92z"/>
455+ <path fill="#006C00" d="M2023.75,60.81c-0.41-0.59-1.51-0.57-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.41-0.59-1.51-0.56-2.75-0.06l-3.11-4.44c0.9-1,1.3-2.02,0.89-2.61c-0.58-0.83-2.51-0.47-4.32,0.8c-1.81,1.27-2.81,2.96-2.23,3.79c0.41,0.59,1.51,0.57,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.41,0.58,1.51,0.56,2.75,0.06l3.11,4.44c-0.9,1-1.3,2.02-0.89,2.61c0.58,0.83,2.51,0.47,4.32-0.8C2023.33,63.33,2024.33,61.64,2023.75,60.81z M2013.19,53.48l-2.65-3.79c-0.67,0.38-1.29,0.49-1.47,0.22 c-0.22-0.31,0.26-1.02,1.06-1.58s1.63-0.77,1.85-0.46c0.18,0.26-0.13,0.8-0.71,1.3l2.65,3.79c0.21,0.29,0.21,0.65,0.01,0.79C2013.73,53.9,2013.4,53.77,2013.19,53.48z M2013.5,57.4c-0.22-0.31,0.5-1.19,1.61-1.96c1.11-0.78,2.18-1.15,2.4-0.84c0.22,0.31-0.5,1.19-1.61,1.96C2014.79,57.34,2013.71,57.71,2013.5,57.4z M2020.87,63.67c-0.8,0.56-1.63,0.77-1.85,0.46c-0.18-0.26,0.13-0.8,0.71-1.3l-2.65-3.79c-0.21-0.3-0.21-0.65-0.01-0.79c0.2-0.14,0.53-0.02,0.74,0.28l2.65,3.79c0.67-0.38,1.29-0.49,1.47-0.22C2022.15,62.4,2021.67,63.1,2020.87,63.67z"/>
456+ <path fill="#006C00" d="M2029.52,47.4c-1.81-1.27-3.75-1.63-4.32-0.8c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.41,0.59-0.02,1.61,0.89,2.61l-3.11,4.44c-1.25-0.51-2.34-0.53-2.75,0.06c-0.58,0.83,0.42,2.52,2.23,3.79c1.81,1.27,3.75,1.62,4.32,0.8c0.41-0.58,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.35,0.53,2.75-0.06c0.41-0.59,0.02-1.61-0.89-2.61l3.11-4.44c1.25,0.51,2.34,0.53,2.75-0.06C2032.33,50.36,2031.33,48.67,2029.52,47.4z M2021.92,59.03l-2.65,3.79c0.58,0.5,0.9,1.04,0.71,1.3c-0.22,0.31-1.04,0.11-1.85-0.46c-0.8-0.56-1.28-1.27-1.06-1.58c0.18-0.26,0.8-0.15,1.47,0.22l2.65-3.79c0.21-0.3,0.54-0.42,0.74-0.28C2022.13,58.39,2022.13,58.74,2021.92,59.03z M2025.5,57.4c-0.22,0.31-1.29-0.07-2.4-0.84c-1.11-0.77-1.83-1.65-1.61-1.96c0.22-0.31,1.29,0.07,2.4,0.84C2025,56.21,2025.72,57.09,2025.5,57.4z M2029.93,49.92c-0.18,0.26-0.8,0.15-1.47-0.22l-2.65,3.79c-0.21,0.29-0.54,0.42-0.74,0.28c-0.2-0.14-0.2-0.49,0.01-0.79l2.65-3.79c-0.58-0.5-0.9-1.04-0.71-1.3c0.22-0.31,1.04-0.11,1.85,0.46C2029.67,48.9,2030.15,49.61,2029.93,49.92z"/>
457+ <path fill="#20208C" d="M1171.2,12.1l6.8,19.4l0.3-0.1c3.6-1.2,13.3-4.1,15.3-2.6c0.2,0.2,0.3,0.4,0.3,0.7l-0.1,0.8l0.6-0.5c6.8-5.3,9.4-14,9.4-14.1l0-0.2l-0.1-0.1c-4.9-7.3-24.9-1.3-25.7-1l-0.2,0.1l0,0.2c0,0-0.2,1.7-0.2,2.4c-0.4-1.5-1.1-4.1-1.1-4.1l-5.6-1.3L1171.2,12.1z M1178.1,17.8c1.2-0.3,2.6-0.7,4-1c0,0,0,0,0,0s1.9,9.3,2,9.9c-0.5,0.1-3,0.8-3.6,1c-0.1-0.4-1.4-5.6-2.5-9.9C1178,17.9,1178.1,17.8,1178.1,17.8z M1189.8,15.5c0,0.1-0.4,9.6-0.4,10.2c-0.4,0.1-2.1,0.4-2.6,0.5c-0.1-0.6-1.8-9.6-1.8-9.9C1186.7,15.9,1188.3,15.7,1189.8,15.5z M1197.7,16.4c-0.2,2.3-1.1,8.1-2.6,9.9c-0.1-0.1,0-0.2-0.1-0.3c-0.5-0.5-1.6-0.7-3.3-0.6c0.3-4.5,1-7.5,1.9-7.9l0.7-0.3c0,0-3.5-1.7-3.8-1.8C1194.3,15,1197.3,15.1,1197.7,16.4z"/>
458+ <path fill="#20208C" d="M1003.8,18.1c-0.3,0.2-0.4,0.5-0.4,0.8c0,0.3,0.1,0.5,0.1,0.7c-2.6,0.2-19.7,1.8-23.9,1.4l-0.4,0l1.7,6l0.3-0.1c3.3-1.1,24.1-2.4,29-1.4c0.8,0.1,1.2,0,1.4-0.4c0.5-1-1.9-3.5-3.4-4.8C1007.3,19.4,1004.8,17.5,1003.8,18.1z"/>
459+ <path fill="#20208C" d="M1067.9,23.9c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.2,0,0.5,0.1,0.7c-2.8,0.3-19.9,1.9-24.1,1.4l-0.5-0.1l1.7,6.2l0.4-0.1c3.3-1.2,24.3-2.4,29.3-1.4c0.8,0.2,1.3,0,1.5-0.4c0.7-1.3-3-4.6-3.4-4.9C1071.5,25.2,1069,23.2,1067.9,23.9z"/>
460+ <path fill="#20208C" d="M1060.1,12.9c-2.6,0.9-6.2,2.2-12.3,2.2l-0.7,0l2.9,4.2l16.7-2.2l0.5-3.1l-0.1-0.1C1064.7,11.2,1063,11.8,1060.1,12.9z"/>
461+ <path fill="#20208C" d="M1131.7,26.3c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.3,0,0.5,0.1,0.7c-2.7,0.3-19.3,1.9-23.4,1.4l-0.5-0.1l1.7,6.2l0.3-0.1c3.2-1.2,23.6-2.4,28.4-1.4c0.8,0.2,1.3,0,1.5-0.4c0.6-1.3-2.9-4.6-3.3-5C1135.1,27.7,1132.7,25.6,1131.7,26.3z"/>
462+ <polygon fill="#20208C" points="1125.3,19 1114,20.4 1115.6,24.7 1127.6,23.3 1125.6,19 "/>
463+ <path fill="#20208C" d="M1122.9,8.6c-3,1.1-7,2.6-14,2.6l-0.6,0l3.2,4.9l18.9-2.6l0.6-3.6l-0.1-0.1C1128.2,6.6,1126.3,7.3,1122.9,8.6z"/>
464+ <path fill="#20208C" d="M1245.8,10.3l-10.3,10.5l0.4,0.5c0,0,5.6-3.2,7-4c-0.6,1.5-4.3,11.6-4.3,11.6l3.3,3.5c0,0,2.4-15.6,2.4-15.9c0.3-0.2,5.1-2.9,5.1-2.9l-3.4-3.7L1245.8,10.3z"/>
465+ <path fill="#20208C" d="M1258.6,10.2h-1.9c0,0-0.6,1.3-0.9,1.9c0,0-0.1-0.1-0.1-0.1l-5.9,3.5l0.2,0.7c0,0,3-0.2,3.9-0.2c-0.4,0.8-2.2,4.5-2.3,4.8c-0.4,0-6.3,0.5-6.3,0.5l-0.1,0.7l4,1.6c0,0,0.7-0.3,1.3-0.5c-0.6,1.2-2.5,5.2-2.7,5.5c-1.3,0.1-2.3,0.1-2.9,0l-0.4,0l0.5,3.7l0.3-0.1c1.4-0.2,12.2-1.6,14.1-1.7c1.8-0.1,7.3,2.1,7.4,2.1l0.7,0.3l-0.2-0.7c-0.1-0.4-1.3-3.9-2.2-4.8l-0.1-0.1l-0.1,0c-0.8-0.2-3.6,0-7.5,0.4c0.9-1,4.3-4.6,4.3-4.6l-3.2-3.4c0,0-2.9,7.8-3,8.2c-0.3,0-2.7,0.3-3.5,0.3c0.4-0.9,2.5-6.5,2.6-6.7c0.3-0.1,3.2-1.2,3.2-1.2l-0.2-0.7c0,0-1.7,0.3-2.5,0.4c0.3-0.9,1.6-4.1,1.7-4.4c0.2,0,1.2-0.1,1.2-0.1s-0.6-0.9-0.7-1.1c0.1-0.3,1.7-4.4,1.7-4.4H1258.6z"/>
466+ <path fill="#20208C" d="M1313.2,9.8c0,0.3,0,0.6,0,1c0,1.6-0.2,3.7-0.7,6.2c-5.9,0.7-11.1,1.5-11.2,1.5l-0.3,0V21c0,0,9.9-0.4,10.7-0.5c-0.2,0.7-1,4-1,4l0.6,0.3c0,0,2.9-4.1,3.1-4.4c11.7-0.3,14.4,0.2,15,0.5l0.6,0.4l-0.6-3.5c-0.3-1-2-2.1-12.2-1.3c0.6-0.9,3.2-4.6,3.2-4.6l-7.2-2.7L1313.2,9.8z"/>
467+ <path fill="#20208C" d="M1307.6,24.8c0,0,0.8,4.7-1.6,8.5l0.5,0.5c4.4-2.9,5.9-6.6,5.9-6.7l0.1-0.3l-5-2.6L1307.6,24.8z"/>
468+ <path fill="#20208C" d="M1317.1,23.7c0.2,0.3,5.9,7.7,7.9,9.6l0.1,0.1l0.2,0c0.4,0,0.8-0.3,1.1-0.7c0.8-1.1,1.3-3.2,1-4.3c-0.4-1.3-7.6-4.3-9.8-5.2l-1.2-0.5L1317.1,23.7z"/>
469+ <path fill="#20208C" d="M1387.2,11.7L1387.2,11.7c-0.2,0.4-1.5,1.9-8.3,7.6c-0.1-0.7-0.2-1.6-0.2-1.6c-0.2-1.9-0.4-4.1-0.8-6.5l-0.1-0.3l-3.2,0.3l0,0.3c0,0.6,0,1.1,0,1.7c0,3.3,0.3,6.2,0.9,8.9c-0.3,0.2-10,8.2-10,8.2l0.3,0.3l1.9,1.8c0,0,7.9-6.9,8.6-7.6c0.7,2.2,1.6,4,2.7,5c2,2,4.2,1.7,7,1.3c2-0.3,4.3-0.6,7-0.2l0.5,0.1l-0.6-2.8c0-0.6-0.3-1.1-0.7-1.5c-1.3-0.9-3.8-0.4-6.5,0.1c-1.3,0.3-2.7,0.5-3.9,0.6c-1.3-1.2-2.1-2.8-2.6-5.2c7.5-6.2,10.3-7.8,11.3-8.1l0.2-0.1l0-0.3c0-0.2,0-0.3,0-0.4c0-1.8-0.9-2.8-1.7-3C1388.5,10.4,1387.7,10.6,1387.2,11.7z"/>
470+ <path fill="#20208C" d="M1432.4,19c0,3.7-4.5,9.2-4.7,9.4l-0.7,0.9l1.1-0.3c5.2-1.4,8.2-5,8.3-5.1l0.2-0.2l-4.2-5.7V19z"/>
471+ <path fill="#20208C" d="M1437.9,11.3l-2,0.3l0.3,0.5c0.1,0.2,9.3,15.7,10.6,19.3l0.1,0.3l0.3-0.1c2.2-0.4,4-0.2,5.8,0c2.2,0.2,4.3,0.4,6.7-0.4l0.4-0.1l-0.2-0.4c-1.2-2-2.5-2.4-4.2-2.9c-3.2-0.9-8-2.3-17.3-16.3l-0.1-0.2L1437.9,11.3z"/>
472+ <path fill="#20208C" d="M1520.6,23.6c0,0-2.9,3.2-3.1,3.4c-3.2,0.7-8.4,1.2-8.8-0.4c-0.5-1.9,3.4-10.4,4.9-13.6l0.2-0.3l-5.6-2.8l-0.1,0.4c-0.1,0.2-0.4,1.3-0.9,3c-2,0-4.2,0-4.5,0c0-0.4-0.1-1.3-0.1-1.3l0,0c-1.4-2.3-7.4-0.7-8.1-0.6l-0.4,0.1l0.2,0.4c0.2,0.4,0.4,0.9,0.5,1.4c-0.8,0-3.8,0-3.8,0l1.9,3.6c0,0,1.8-0.3,2.4-0.4c0,0.2,0,0.5,0,0.7c0,6.2-2.7,14.1-2.7,14.2l0.6,0.3c0.3-0.4,7.9-9.5,9.3-16.4c0.2,0,2.4-0.4,4.4-0.7c-1.2,4.7-2.6,12.1-0.3,14.8c3.4,4,16.6,3.7,17.1,3.5l0.3-0.1L1520.6,23.6z"/>
473+ <path fill="#20208C" d="M1171.2,100.1l6.8,19.4l0.3-0.1c3.6-1.2,13.3-4.1,15.3-2.6c0.2,0.2,0.3,0.4,0.3,0.7l-0.1,0.8l0.6-0.5c6.8-5.3,9.4-14,9.4-14.1l0-0.2l-0.1-0.1c-4.9-7.3-24.9-1.3-25.7-1l-0.2,0.1l0,0.2c0,0-0.2,1.7-0.2,2.4c-0.4-1.5-1.1-4.1-1.1-4.1l-5.6-1.3L1171.2,100.1z M1178.1,105.8c1.2-0.3,2.6-0.7,4-1c0,0,0,0,0,0s1.9,9.3,2,9.9c-0.5,0.1-3,0.8-3.6,1c-0.1-0.4-1.4-5.6-2.5-9.9C1178,105.9,1178.1,105.8,1178.1,105.8z M1189.8,103.5c0,0.1-0.4,9.6-0.4,10.2c-0.4,0.1-2.1,0.4-2.6,0.5c-0.1-0.6-1.8-9.6-1.8-9.9C1186.7,103.9,1188.3,103.7,1189.8,103.5z M1197.7,104.4c-0.2,2.3-1.1,8.1-2.6,9.9c-0.1-0.1,0-0.2-0.1-0.3c-0.5-0.5-1.6-0.7-3.3-0.6c0.3-4.5,1-7.5,1.9-7.9l0.7-0.3c0,0-3.5-1.7-3.8-1.8C1194.3,103,1197.3,103.1,1197.7,104.4z"/>
474+ <path fill="#20208C" d="M1003.8,106.1c-0.3,0.2-0.4,0.5-0.4,0.8c0,0.3,0.1,0.5,0.1,0.7c-2.6,0.2-19.7,1.8-23.9,1.4l-0.4,0l1.7,6l0.3-0.1c3.3-1.1,24.1-2.4,29-1.4c0.8,0.1,1.2,0,1.4-0.4c0.5-1-1.9-3.5-3.4-4.8C1007.3,107.4,1004.8,105.5,1003.8,106.1z"/>
475+ <path fill="#20208C" d="M1067.9,111.9c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.2,0,0.5,0.1,0.7c-2.8,0.3-19.9,1.9-24.1,1.4l-0.5-0.1l1.7,6.2l0.4-0.1c3.3-1.2,24.3-2.4,29.3-1.4c0.8,0.2,1.3,0,1.5-0.4c0.7-1.3-3-4.6-3.4-4.9C1071.5,113.2,1069,111.2,1067.9,111.9z"/>
476+ <path fill="#20208C" d="M1060.1,100.9c-2.6,0.9-6.2,2.2-12.3,2.2l-0.7,0l2.9,4.2l16.7-2.2l0.5-3.1l-0.1-0.1C1064.7,99.2,1063,99.8,1060.1,100.9z"/>
477+ <path fill="#20208C" d="M1131.7,114.3c-0.3,0.2-0.4,0.5-0.4,0.9c0,0.3,0,0.5,0.1,0.7c-2.7,0.3-19.3,1.9-23.4,1.4l-0.5-0.1l1.7,6.2l0.3-0.1c3.2-1.2,23.6-2.4,28.4-1.4c0.8,0.2,1.3,0,1.5-0.4c0.6-1.3-2.9-4.6-3.3-5C1135.1,115.7,1132.7,113.6,1131.7,114.3z"/>
478+ <polygon fill="#20208C" points="1125.3,107 1114,108.4 1115.6,112.7 1127.6,111.3 1125.6,107 "/>
479+ <path fill="#20208C" d="M1122.9,96.6c-3,1.1-7,2.6-14,2.6l-0.6,0l3.2,4.9l18.9-2.6l0.6-3.6l-0.1-0.1C1128.2,94.6,1126.3,95.3,1122.9,96.6z"/>
480+ <path fill="#20208C" d="M1245.8,98.3l-10.3,10.5l0.4,0.5c0,0,5.6-3.2,7-4c-0.6,1.5-4.3,11.6-4.3,11.6l3.3,3.5c0,0,2.4-15.6,2.4-15.9c0.3-0.2,5.1-2.9,5.1-2.9l-3.4-3.7L1245.8,98.3z"/>
481+ <path fill="#20208C" d="M1258.6,98.2h-1.9c0,0-0.6,1.3-0.9,1.9c0,0-0.1-0.1-0.1-0.1l-5.9,3.5l0.2,0.7c0,0,3-0.2,3.9-0.2c-0.4,0.8-2.2,4.5-2.3,4.8c-0.4,0-6.3,0.5-6.3,0.5l-0.1,0.7l4,1.6c0,0,0.7-0.3,1.3-0.5c-0.6,1.2-2.5,5.2-2.7,5.5c-1.3,0.1-2.3,0.1-2.9,0l-0.4,0l0.5,3.7l0.3-0.1c1.4-0.2,12.2-1.6,14.1-1.7c1.8-0.1,7.3,2.1,7.4,2.1l0.7,0.3l-0.2-0.7c-0.1-0.4-1.3-3.9-2.2-4.8l-0.1-0.1l-0.1,0c-0.8-0.2-3.6,0-7.5,0.4c0.9-1,4.3-4.6,4.3-4.6l-3.2-3.4c0,0-2.9,7.8-3,8.2c-0.3,0-2.7,0.3-3.5,0.3c0.4-0.9,2.5-6.5,2.6-6.7c0.3-0.1,3.2-1.2,3.2-1.2l-0.2-0.7c0,0-1.7,0.3-2.5,0.4c0.3-0.9,1.6-4.1,1.7-4.4c0.2,0,1.2-0.1,1.2-0.1s-0.6-0.9-0.7-1.1c0.1-0.3,1.7-4.4,1.7-4.4H1258.6z"/>
482+ <path fill="#20208C" d="M1313.2,97.8c0,0.3,0,0.6,0,1c0,1.6-0.2,3.7-0.7,6.2c-5.9,0.7-11.1,1.5-11.2,1.5l-0.3,0v2.5c0,0,9.9-0.4,10.7-0.5c-0.2,0.7-1,4-1,4l0.6,0.3c0,0,2.9-4.1,3.1-4.4c11.7-0.3,14.4,0.2,15,0.5l0.6,0.4l-0.6-3.5c-0.3-1-2-2.1-12.2-1.3c0.6-0.9,3.2-4.6,3.2-4.6l-7.2-2.7L1313.2,97.8z"/>
483+ <path fill="#20208C" d="M1307.6,112.8c0,0,0.8,4.7-1.6,8.5l0.5,0.5c4.4-2.9,5.9-6.6,5.9-6.7l0.1-0.3l-5-2.6L1307.6,112.8z"/>
484+ <path fill="#20208C" d="M1317.1,111.7c0.2,0.3,5.9,7.7,7.9,9.6l0.1,0.1l0.2,0c0.4,0,0.8-0.3,1.1-0.7c0.8-1.1,1.3-3.2,1-4.3c-0.4-1.3-7.6-4.3-9.8-5.2l-1.2-0.5L1317.1,111.7z"/>
485+ <path fill="#20208C" d="M1387.2,99.7L1387.2,99.7c-0.2,0.4-1.5,1.9-8.3,7.6c-0.1-0.7-0.2-1.6-0.2-1.6c-0.2-1.9-0.4-4.1-0.8-6.5l-0.1-0.3l-3.2,0.3l0,0.3c0,0.6,0,1.1,0,1.7c0,3.3,0.3,6.2,0.9,8.9c-0.3,0.2-10,8.2-10,8.2l0.3,0.3l1.9,1.8c0,0,7.9-6.9,8.6-7.6c0.7,2.2,1.6,4,2.7,5c2,2,4.2,1.7,7,1.3c2-0.3,4.3-0.6,7-0.2l0.5,0.1l-0.6-2.8c0-0.6-0.3-1.1-0.7-1.5c-1.3-0.9-3.8-0.4-6.5,0.1c-1.3,0.3-2.7,0.5-3.9,0.6c-1.3-1.2-2.1-2.8-2.6-5.2c7.5-6.2,10.3-7.8,11.3-8.1l0.2-0.1l0-0.3c0-0.2,0-0.3,0-0.4c0-1.8-0.9-2.8-1.7-3C1388.5,98.4,1387.7,98.6,1387.2,99.7z"/>
486+ <path fill="#20208C" d="M1432.4,107c0,3.7-4.5,9.2-4.7,9.4l-0.7,0.9l1.1-0.3c5.2-1.4,8.2-5,8.3-5.1l0.2-0.2l-4.2-5.7V107z"/>
487+ <path fill="#20208C" d="M1437.9,99.3l-2,0.3l0.3,0.5c0.1,0.2,9.3,15.7,10.6,19.3l0.1,0.3l0.3-0.1c2.2-0.4,4-0.2,5.8,0c2.2,0.2,4.3,0.4,6.7-0.4l0.4-0.1l-0.2-0.4c-1.2-2-2.5-2.4-4.2-2.9c-3.2-0.9-8-2.3-17.3-16.3l-0.1-0.2L1437.9,99.3z"/>
488+ <path fill="#20208C" d="M1520.6,111.6c0,0-2.9,3.2-3.1,3.4c-3.2,0.7-8.4,1.2-8.8-0.4c-0.5-1.9,3.4-10.4,4.9-13.6l0.2-0.3l-5.6-2.8l-0.1,0.4c-0.1,0.2-0.4,1.3-0.9,3c-2,0-4.2,0-4.5,0c0-0.4-0.1-1.3-0.1-1.3l0,0c-1.4-2.3-7.4-0.7-8.1-0.6l-0.4,0.1l0.2,0.4c0.2,0.4,0.4,0.9,0.5,1.4c-0.8,0-3.8,0-3.8,0l1.9,3.6c0,0,1.8-0.3,2.4-0.4c0,0.2,0,0.5,0,0.7c0,6.2-2.7,14.1-2.7,14.2l0.6,0.3c0.3-0.4,7.9-9.5,9.3-16.4c0.2,0,2.4-0.4,4.4-0.7c-1.2,4.7-2.6,12.1-0.3,14.8c3.4,4,16.6,3.7,17.1,3.5l0.3-0.1L1520.6,111.6z"/>
489+ <path fill="#101040" d="M632.3,142.7c-2-0.9-11.8,3.3-11.8,3.3l0.5-20.7c0,0,5.3-4.3,9.6-1.7l-1.2-5c-1.4-0.2-8.4,5.5-8.4,5.5s-0.6-18.8,4.8-19.9c0,0-8.9-2.6-9.7-2.2c-0.4,0.2-0.4,13.8-0.2,27.1c-3.2,3.1-5.9,6.2-7.1,7.3l0.4-28.5c0,0-4.7-2.6-5.8-1.4l0.2,14.8c-0.3-0.5-0.7-0.9-1-1.3c-1-1.3-6.1-4.2-6.1-4.2s1.7,5.6,2.5,6.6c0.7,0.9,3,2.9,4.6,2.2l0.2,16.5c-3.4,3-7.9,6.8-8.5,5.4c-1.5-3.2-4.7-3-5.2-1.5c-0.5,1.6,4.2,10.2,5.8,10.1c0.8-0.1,4.4-4.9,8-10l0.1,5.3c1.6,2.1,3.8,0.5,4.6-0.7l0.3-11c1.1-1.6,4-4.5,7.1-7.9c0.2,12.1,0.3,22.8,0.3,22.8c4.9-5.2,16.7-4,16.7-4L632.3,142.7z"/>
490+ <path fill="#101040" d="M688.4,14.3c-8.5,5.7-21.5,6.9-28.8,5.9l-0.9,2c11.7,3.8,26.4,1,31.2-3.2L688.4,14.3z"/>
491+ <path fill="#101040" d="M692.5,39.2c-3.6-0.9-7.3-1.4-10.8-1.7c0.8-2.9,1.2-6,1-9.2l-7.8,2c0.6,1.7,0.6,4.3,0.2,7c-1.6,0-3.1,0.1-4.5,0.3c0.2-2.7,0.4-3.3-0.5-6l-4.7,1.3c1,1.8,1.5,2.3,1.8,5.1c-3.4,1-5.7,1.8-6.9,3.2c-5.1,6.1,0,23.2,0,23.2s-0.2-15.4,2-17c2.2-1.6,4.7-1.8,4.7-1.8c0.1,1.5-1.6,6.7-1.6,6.7c2.2-2.7,3.2-3.6,4.3-7.2c1.6-0.5,2.5-0.8,4.1-1c-1.6,6.1-4.5,10.9-4.5,10.9l7.4,2c-4.1-0.1-13.3,5.1-13.3,5.1s16.2-4,20.8-1.1l1.2-2.9c-3.4-2.7-10.5-4.9-10.5-4.9s2.2-4.1,4.6-9.4c2.3,0.1,5.6,0.3,7.7,0.9c9.2,4.1-1.7,22.4-1.7,22.4S705.7,45.9,692.5,39.2z"/>
492+ <path fill="#101040" d="M735.3,14.2c0.4,0.5,0.9,2.3,0.9,8.6c0,0.9,0,1.8,0,2.8c-3.4,0.5-6.3,1-6.8,1.2l-0.6,0.2l2.8,2.7l0.2-0.1c1.1-0.3,2.6-0.5,4.2-0.7c0,0.7-0.4,7.3-0.4,7.9c-4.9,0.4-9.2,1.1-12.7,1.9c-3,0.8-4.8,2.6-5.2,5.4c-0.1,0.4-0.1,0.9-0.1,1.4c0,7.1,6.9,16.9,7,16.9l0.8,0.5l-0.3-0.9c0-0.1-2.8-9.4-2.8-15.6c0-1.8,0.2-3.3,0.8-4.3c0.4-0.6,0.8-0.9,1.4-1c1.3-0.2,2.7-0.4,4-0.6c0,0,3.1,4,3.7,4.7c-0.5,0.1-1.4,0.3-1.4,0.3l0,0.3l0.2,2.2l0.4-0.1c0.6-0.2,1.8-0.5,3.2-0.7c0,0.6,0.1,2.4,0.2,2.9c-1.7,0.5-3.2,1.1-3.8,1.4l-0.4,0.2l1.5,1.6l0.2-0.1c0.7-0.3,1.7-0.6,2.7-0.9c0,0.7,0.3,5.6,0.3,5.6l0.7,0c0,0,1.1-5.5,1.2-6c1.8-0.2,3.5-0.1,4.8,0.2l0.4,0.1v-3l-0.2-0.1c-0.8-0.4-2.3-0.4-4.5,0.1c0.1-0.7,0.4-2.1,0.5-2.5c1.6-0.1,2.9-0.2,3.8-0.1l0.5,0.1l-0.7-2.6c0,0-0.9,0-1.4,0.1c0.8-0.9,3.5-4.4,3.7-4.6c0.3,0,3.9,0.1,3.9,0.1c1.6,0.1,3,0.7,4.1,2c1.3,1.5,2,3.9,2,6.3c0,0.5,0,1.1-0.1,1.6l-0.1,0.7c-0.7,5.1-1.3,8.8-3.1,9.6c-1.2,0.5-3.1-0.3-5.9-2.4l-0.5,0.5l5.5,9.6l0.3-0.1c6.7-2.4,7.7-3.8,10.4-15.4c1.2-5.1,0.8-9.1-1.3-11.8c-2.5-3.3-6.3-3.7-6.5-3.7c-0.1,0-6.7-0.3-14.6,0.2c0.2-0.8,2-7.5,2.1-7.9c2.5,0,4.7,0.1,5.7,0.5l0.4,0.1l0.4-4.7l-0.4,0c-1.1,0-2.8,0.1-5.1,0.3c1.3-4.4,1.9-5.8,2.1-6.2l0.2-0.3l-8.1-4.7L735.3,14.2z M739.7,39.5c-0.1,0.7-0.7,4.3-0.7,4.7c0,0-0.1,0-0.1,0c0-0.1,0.1-0.4,0.1-0.4l-4.4,0.4c0,0,0,0.5,0,0.6c-0.4,0.1-1,0.2-1.4,0.2c0-0.7,0-5,0-5.1C735.4,39.8,737.5,39.6,739.7,39.5z"/>
493+ <path fill="#101040" d="M804.5,41.3l0.1-6.4l4.4-0.3l-0.3-2.4l-4.1,1.2l0-5.7c2.4,0.3,5,0.8,7.1,0.9c4.3,0.3-1.2,14.1-1.2,14.1s16.3-14.4,2.6-18.1c-1.4,0-4.9,0.2-8.4,0.4l0-3.9l7.5-1.9l-7.5-0.1l0-4l-4.6-3.4l0.5,8.7l-4,2.7l4.1-1l0.2,3.2c-1.8,0.2-3.4,0.3-4.3,0.5c-11.5,2.2,1,17.2,1,17.2s-4-14,0.5-15.3c0.9-0.3,1.8-0.3,2.9-0.3l0.4,7l-3,0.9l3-0.2l0.2,4.1c-9.2,12.1-19.7,16.6-19.7,16.6s10.2-1.1,20-11.9l1,18.7c-3.7-1.5-7-3.6-7-3.6s2.4,7.1,7.5,10.1l4.3-5.1c-1-0.2-2.1-0.5-3.2-0.9l0.2-18.9c2.7,3.2,9,10.5,11.7,11l9-3C821.9,51.6,807.7,43.3,804.5,41.3z"/>
494+ <path fill="#101040" d="M688.4,102.3c-8.5,5.7-21.5,6.9-28.8,5.9l-0.9,2c11.7,3.8,26.4,1,31.2-3.2L688.4,102.3z"/>
495+ <path fill="#101040" d="M692.5,127.2c-3.6-0.9-7.3-1.4-10.8-1.7c0.8-2.9,1.2-6,1-9.2l-7.8,2c0.6,1.7,0.6,4.3,0.2,7c-1.6,0-3.1,0.1-4.5,0.3c0.2-2.7,0.4-3.3-0.5-6l-4.7,1.3c1,1.8,1.5,2.3,1.8,5.1c-3.4,1-5.7,1.8-6.9,3.2c-5.1,6.1,0,23.2,0,23.2s-0.2-15.4,2-17c2.2-1.6,4.7-1.8,4.7-1.8c0.1,1.5-1.6,6.7-1.6,6.7c2.2-2.7,3.2-3.6,4.3-7.2c1.6-0.5,2.5-0.8,4.1-1c-1.6,6.1-4.5,10.9-4.5,10.9l7.4,2c-4.1-0.1-13.3,5.1-13.3,5.1s16.2-4,20.8-1.1l1.2-2.9c-3.4-2.7-10.5-4.9-10.5-4.9s2.2-4.1,4.6-9.4c2.3,0.1,5.6,0.3,7.7,0.9c9.2,4.1-1.7,22.4-1.7,22.4S705.7,133.9,692.5,127.2z"/>
496+ <path fill="#101040" d="M735.3,102.2c0.4,0.5,0.9,2.3,0.9,8.6c0,0.8,0,1.8,0,2.8c-3.4,0.5-6.3,1-6.8,1.2l-0.6,0.2l2.8,2.7l0.2-0.1c1.1-0.3,2.6-0.5,4.2-0.7c0,0.7-0.4,7.3-0.4,7.9c-4.9,0.4-9.2,1.1-12.7,1.9c-3,0.8-4.8,2.6-5.2,5.4c-0.1,0.4-0.1,0.9-0.1,1.4c0,7.1,6.9,16.9,7,16.9l0.8,0.5l-0.3-0.9c0-0.1-2.8-9.4-2.8-15.6c0-1.8,0.2-3.3,0.8-4.3c0.4-0.6,0.8-0.9,1.4-1c1.3-0.2,2.7-0.4,4-0.6c0,0,3.1,4,3.7,4.7c-0.5,0.1-1.4,0.3-1.4,0.3l0,0.3l0.2,2.2l0.4-0.1c0.6-0.2,1.8-0.5,3.2-0.7c0,0.6,0.1,2.4,0.2,2.9c-1.7,0.5-3.2,1.1-3.8,1.4l-0.4,0.2l1.5,1.6l0.2-0.1c0.7-0.3,1.7-0.6,2.7-0.9c0,0.7,0.3,5.6,0.3,5.6l0.7,0c0,0,1.1-5.5,1.2-6c1.8-0.2,3.5-0.1,4.8,0.2l0.4,0.1v-3l-0.2-0.1c-0.8-0.4-2.3-0.4-4.5,0.1c0.1-0.7,0.4-2.1,0.5-2.5c1.6-0.1,2.9-0.2,3.8-0.1l0.5,0.1l-0.7-2.6c0,0-0.9,0-1.4,0.1c0.8-0.9,3.5-4.4,3.7-4.6c0.3,0,3.9,0.1,3.9,0.1c1.6,0.1,3,0.7,4.1,2c1.3,1.5,2,3.9,2,6.3c0,0.5,0,1.1-0.1,1.6l-0.1,0.7c-0.7,5.1-1.3,8.8-3.1,9.6c-1.2,0.5-3.1-0.3-5.9-2.4l-0.5,0.5l5.5,9.6l0.3-0.1c6.7-2.4,7.7-3.8,10.4-15.4c1.2-5.1,0.8-9.1-1.3-11.8c-2.5-3.3-6.3-3.7-6.5-3.7c-0.1,0-6.7-0.3-14.6,0.2c0.2-0.8,2-7.5,2.1-7.9c2.5,0,4.7,0.1,5.7,0.5l0.4,0.1l0.4-4.7l-0.4,0c-1.1,0-2.8,0.1-5.1,0.3c1.3-4.4,1.9-5.8,2.1-6.2l0.2-0.3l-8.1-4.7L735.3,102.2z M739.7,127.5c-0.1,0.7-0.7,4.3-0.7,4.7c0,0-0.1,0-0.1,0c0-0.1,0.1-0.4,0.1-0.4l-4.4,0.4c0,0,0,0.5,0,0.6c-0.4,0.1-1,0.2-1.4,0.2c0-0.7,0-5,0-5.1C735.4,127.8,737.5,127.6,739.7,127.5z"/>
497+ <path fill="#101040" d="M804.5,129.3l0.1-6.4l4.4-0.3l-0.3-2.4l-4.1,1.2l0-5.7c2.4,0.3,5,0.8,7.1,0.9c4.3,0.3-1.2,14.1-1.2,14.1s16.3-14.4,2.6-18.1c-1.4,0-4.9,0.2-8.4,0.4l0-3.9l7.5-1.9l-7.5-0.1l0-4l-4.6-3.4l0.5,8.7l-4,2.7l4.1-1l0.2,3.2c-1.8,0.2-3.4,0.3-4.3,0.5c-11.5,2.2,1,17.2,1,17.2s-4-14,0.5-15.3c0.9-0.3,1.8-0.3,2.9-0.3l0.4,7l-3,0.9l3-0.2l0.2,4.1c-9.2,12.1-19.7,16.6-19.7,16.6s10.2-1.1,20-11.9l1,18.7c-3.7-1.5-7-3.6-7-3.6s2.4,7.1,7.5,10.1l4.3-5.1c-1-0.2-2.1-0.5-3.2-0.9l0.2-18.9c2.7,3.2,9,10.5,11.7,11l9-3C821.9,139.6,807.7,131.3,804.5,129.3z"/>
498+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M49.4,40.5c0,7.7-6.2,13.9-13.9,13.9c-7.7,0-13.9-6.2-13.9-13.9c0-7.7,6.2-13.9,13.9-13.9C43.2,26.6,49.4,32.8,49.4,40.5z"/>
499+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="42" cy="40.5" r="6.5"/>
500+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="29" cy="40.5" r="6.5"/>
501+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="34" r="6.5"/>
502+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="47" r="6.5"/>
503+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M44.8,40.5c0,5.1-4.2,9.3-9.3,9.3s-9.3-4.2-9.3-9.3c0-5.1,4.2-9.3,9.3-9.3S44.8,35.4,44.8,40.5z"/>
504+ <path fill="none" stroke="#BA0000" stroke-width="2" d="M52.2,40.5c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7c0-9.2,7.5-16.7,16.7-16.7C44.7,23.8,52.2,31.3,52.2,40.5z"/>
505+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M55,40.5C55,51.3,46.3,60,35.5,60C24.7,60,16,51.3,16,40.5C16,29.7,24.7,21,35.5,21C46.3,21,55,29.7,55,40.5z"/>
506+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M49.4,128.5c0,7.7-6.2,13.9-13.9,13.9c-7.7,0-13.9-6.2-13.9-13.9s6.2-13.9,13.9-13.9C43.2,114.6,49.4,120.8,49.4,128.5z"/>
507+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="42" cy="128.5" r="6.5"/>
508+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="29" cy="128.5" r="6.5"/>
509+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="122" r="6.5"/>
510+ <circle fill="none" stroke="#10106C" stroke-width="1.6" cx="35.5" cy="135" r="6.5"/>
511+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M44.8,128.5c0,5.1-4.2,9.3-9.3,9.3s-9.3-4.2-9.3-9.3c0-5.1,4.2-9.3,9.3-9.3S44.8,123.4,44.8,128.5z"/>
512+ <path fill="none" stroke="#BA0000" stroke-width="2" d="M52.2,128.5c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7c0-9.2,7.5-16.7,16.7-16.7C44.7,111.8,52.2,119.3,52.2,128.5z"/>
513+ <path fill="none" stroke="#10106C" stroke-width="0.75" d="M55,128.5c0,10.8-8.7,19.5-19.5,19.5c-10.8,0-19.5-8.7-19.5-19.5c0-10.8,8.7-19.5,19.5-19.5C46.3,109,55,117.7,55,128.5z"/>
514+ <path fill="#BA0000" d="M861.8,10.2c-0.4,0.4-0.5,1-0.5,1.7c0,1.3,0.4,2.9,0.6,3.5c0,0,0.3,7,0.4,7.8c-0.9,0.1-2.5,0.2-3.1,0.4l-0.8,0c-1.7,0.1-3.3,0.2-4,0.9l-0.3,0.3l0.3,0.4c0.5,0.6,0.7,2.3,0.9,4.1c0.5,3.7,1.1,8.3,4.1,10.1l0.6,0.3L860,39c0,0,0.1-0.3,0.1-0.7c0-0.3-0.1-0.8-0.2-1.5c0.8,0.1,2.5,0.3,3.1,0.3c0.1,0.8,2.7,33.4,2.7,33.4l1,0c0,0,1.1-32.2,1.1-33.2c0.7,0,3.8,0.1,4.7,0.2c-0.2,0.6-1,3.1-1,3.1l1.4-1.3c0.4-0.3,9.1-8.2,7.9-13.4c-0.4-1.7-1.8-2.9-4.1-3.5l-0.1,0h-0.1c-1.3,0-5.3,0.1-8.3,0.3c0.1-2.6,0.1-4,0.1-4.7c0-0.6,0-0.7,0-0.8c0,0,0-0.1,0-0.1c0-0.5,0.5-1.2,0.9-1.7c0.4-0.6,0.9-1.1,0.9-1.7c0-0.2-0.1-0.4-0.2-0.6c-0.6-0.9-5-3.2-6.9-3.4C862.4,9.8,862,9.9,861.8,10.2z M861.9,26.6c0,0,0.2,0,0.5,0c0,0.9,0.3,7.2,0.4,8.1c-0.8-0.1-2.7-0.3-3.3-0.3c-0.3-1.3-0.4-2.7-0.5-4.1c-0.1-1.2-0.2-2.4-0.4-3.5C859.5,26.8,861.9,26.6,861.9,26.6z M868,29.7c0,0,0.1-2.4,0.1-3.2c1.3,0.2,3,0.3,4.6,0.3c0,0,1.2,0,1.8,0c0,0.4-0.1,0.9-0.1,0.9c-0.2,2.2-0.5,4.8-1.4,7.4c-0.6,0-4.3-0.2-5.1-0.3C867.9,34.1,868,29.7,868,29.7z"/>
515+ <path fill="#BA0000" d="M861.8,98.2c-0.4,0.4-0.5,1-0.5,1.7c0,1.3,0.4,2.9,0.6,3.5c0,0,0.3,7,0.4,7.8c-0.9,0.1-2.5,0.2-3.1,0.4l-0.8,0c-1.7,0.1-3.3,0.2-4,0.9l-0.3,0.3l0.3,0.4c0.5,0.6,0.7,2.3,0.9,4.1c0.5,3.7,1.1,8.3,4.1,10.1l0.6,0.3l0.2-0.6c0,0,0.1-0.3,0.1-0.7c0-0.3-0.1-0.8-0.2-1.5c0.8,0.1,2.5,0.3,3.1,0.3c0.1,0.8,2.7,33.4,2.7,33.4l1,0c0,0,1.1-32.2,1.1-33.2c0.7,0,3.8,0.1,4.7,0.2c-0.2,0.6-1,3.1-1,3.1l1.4-1.3c0.4-0.3,9.1-8.2,7.9-13.4c-0.4-1.7-1.8-2.9-4.1-3.5l-0.1,0h-0.1c-1.3,0-5.3,0.1-8.3,0.3c0.1-2.6,0.1-4,0.1-4.7c0-0.6,0-0.7,0-0.8c0,0,0-0.1,0-0.1c0-0.5,0.5-1.2,0.9-1.7c0.4-0.6,0.9-1.1,0.9-1.7c0-0.2-0.1-0.4-0.2-0.6c-0.6-0.9-5-3.2-6.9-3.4C862.4,97.8,862,97.9,861.8,98.2z M861.9,114.6c0,0,0.2,0,0.5,0c0,0.9,0.3,7.2,0.4,8.1c-0.8-0.1-2.7-0.3-3.3-0.3c-0.3-1.3-0.4-2.7-0.5-4.1c-0.1-1.2-0.2-2.4-0.4-3.5C859.5,114.8,861.9,114.6,861.9,114.6z M868,117.7c0,0,0.1-2.4,0.1-3.2c1.3,0.2,3,0.3,4.6,0.3c0,0,1.2,0,1.8,0c0,0.4-0.1,0.9-0.1,0.9c-0.2,2.2-0.5,4.8-1.4,7.4c-0.6,0-4.3-0.2-5.1-0.3C867.9,122.1,868,117.7,868,117.7z"/>
516+ <path fill="#004C00" d="M925,47.8c0,0.1-1.4,5.1-5.1,7.1c-0.5-0.6-3.1-4.2-3.1-4.2l-0.9,0.5l4.4,11.4l1-0.2c0-2.2,1.1-3.5,2.4-4.9c1.8-2.1,3.8-4.5,2.3-9.7l-0.5-1.7L925,47.8z"/>
517+ <path fill="#004C00" d="M929.5,37c0,0-0.5,4.9-1.9,7.8l-0.3,0.7l0.7,0.1c1.8,0.1,3.5-3.7,4.1-5.4c0.4,0,1.4,0.1,2,0.1c-0.3,1.2-0.7,2.8-0.7,3.8c0,0.4,0,0.7,0.2,0.9l0.5,0.3c0.8,0.1,5.2-0.2,6.1-0.3l0-1c-1.5-0.2-3.3-0.7-3.5-1.2c-0.2-0.6,0.6-3,1.5-5l0.3-0.7l-9-0.6L929.5,37z"/>
518+ <path fill="#004C00" d="M942,46.6L927.3,47c0,0,4.9,6.3,5.4,6.9c-2.6,2.4-4.9,4.2-7,5.4l-0.6,0.4l0.6,0.5c1.4,1.1,4.2-0.2,8.6-4.2c2.3,3.2,3.8,5.5,4.3,6.9l0.2,0.7l2.7-2.2l0-0.2l0-0.3c0-0.5,0-2.1-4.7-7.3c3-3.1,5.6-6,5.6-6.1l0.7-0.9L942,46.6z M937.3,49.4c-0.8,0.9-1.7,1.8-2.1,2.3c-0.4-0.4-1.2-1.2-1.9-1.9C934.4,49.6,936,49.5,937.3,49.4z"/>
519+ <path fill="#004C00" d="M931.1,25.2l-1.7,1.7l-4.5-5.2l-0.2,0c-0.7,0-4.3,0.1-5.2,1.4c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.1,0,0.2c0.1,0.5,0.7,0.7,1.8,1.1c0.5,0.2,1.2,0.5,1.5,0.7c-1.1,2.3-2.3,4.4-3.7,6.4c-0.7-0.4-4.1-2.5-4.1-2.5l-0.6,0.8c0,0,2.8,3.1,3.3,3.6c-2.8,3.5-6.2,6.6-10.4,9.5l0.4,0.9c12.8-3.3,21.9-16.2,22.1-16.5l2.3,2.2c-3.2,2-6.2,3.8-6.2,3.8l0.5,0.9c2-0.8,4.8-1.9,7.6-3L945,42.4l10.8-3.8l-0.1-1c-5.1-0.2-12-4.4-17.1-7.9c2.9-1,5.3-1.7,6.4-1.6c0.7,0,0.9-0.3,1-0.5c0.4-1.1-3.5-4.4-11.4-9.7l-0.5-0.3l-0.2,0.6 M934.9,27c0.7-1.2,1.6-2.9,1.9-3.3c0.5,0.3,1.8,1.1,2.5,1.6c-0.4,0.2-1.9,1.1-3.7,2.3C935.4,27.4,935.2,27.2,934.9,27z"/>
520+ <path fill="#004C00" d="M926.9,35.4l-6.2,3.5l0.3,0.9c0,0,1.1-0.1,1.8-0.1c-0.2,0.7-0.6,1.8-1.1,3.2c-0.6,0-5-0.2-5-0.2l0.8,0.9c0.3,0.3,1.6,1.7,2.7,1.7c0,0,0.3-0.1,0.6-0.1c-0.8,2-1.7,3.7-2.6,5l0.7,0.7c1.5-1.2,3.2-3.4,4.8-6.4c0.3-0.1,1.2-0.4,1.2-0.4l-0.1-1c0,0-0.2,0-0.4,0c1.8-3.6,3.1-7.1,3.1-7.1l0.5-1.3L926.9,35.4z"/>
521+ <path fill="#004C00" d="M925,135.8c0,0.1-1.4,5.1-5.1,7.1c-0.5-0.6-3.1-4.2-3.1-4.2l-0.9,0.5l4.4,11.4l1-0.2c0-2.2,1.1-3.5,2.4-4.9c1.8-2.1,3.8-4.5,2.3-9.7l-0.5-1.7L925,135.8z"/>
522+ <path fill="#004C00" d="M929.5,125c0,0-0.5,4.9-1.9,7.8l-0.3,0.7l0.7,0.1c1.8,0.1,3.5-3.7,4.1-5.4c0.4,0,1.4,0.1,2,0.1c-0.3,1.2-0.7,2.8-0.7,3.8c0,0.4,0,0.7,0.2,0.9l0.5,0.3c0.8,0.1,5.2-0.2,6.1-0.3l0-1c-1.5-0.2-3.3-0.7-3.5-1.2c-0.2-0.6,0.6-3,1.5-5l0.3-0.7l-9-0.6L929.5,125z"/>
523+ <path fill="#004C00" d="M942,134.6l-14.7,0.4c0,0,4.9,6.3,5.4,6.9c-2.6,2.4-4.9,4.2-7,5.4l-0.6,0.4l0.6,0.5c1.4,1.1,4.2-0.2,8.6-4.2c2.3,3.2,3.8,5.5,4.3,6.9l0.2,0.7l2.7-2.3l0-0.2l0-0.3c0-0.5,0-2.1-4.7-7.3c3-3.1,5.6-6,5.6-6.1l0.7-0.9L942,134.6z M937.3,137.4c-0.8,0.9-1.7,1.8-2.1,2.3c-0.4-0.4-1.2-1.2-1.9-1.9C934.4,137.6,936,137.5,937.3,137.4z"/>
524+ <path fill="#004C00" d="M931.1,113.2l-1.7,1.7l-4.5-5.2l-0.2,0c-0.7,0-4.3,0.1-5.2,1.4c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.1,0,0.2c0.1,0.5,0.7,0.7,1.8,1.1c0.5,0.2,1.2,0.5,1.5,0.7c-1.1,2.3-2.3,4.4-3.7,6.4c-0.7-0.4-4.1-2.5-4.1-2.5l-0.6,0.8c0,0,2.8,3.1,3.3,3.6c-2.8,3.5-6.2,6.6-10.4,9.5l0.4,0.9c12.8-3.3,21.9-16.2,22.1-16.5l2.3,2.2c-3.2,2-6.2,3.8-6.2,3.8l0.5,0.9c2-0.8,4.8-1.9,7.6-3l11.4,10.9l10.8-3.8l-0.1-1c-5.1-0.2-12-4.4-17.1-7.9c2.9-1,5.3-1.7,6.4-1.6c0.7,0,0.9-0.3,1-0.5c0.4-1.1-3.5-4.4-11.4-9.7l-0.5-0.3l-0.2,0.6 M934.9,115c0.7-1.2,1.6-2.9,1.9-3.3c0.5,0.3,1.8,1.1,2.5,1.6c-0.4,0.2-1.9,1.1-3.7,2.3C935.4,115.4,935.2,115.2,934.9,115z"/>
525+ <path fill="#004C00" d="M926.9,123.4l-6.2,3.5l0.3,0.9c0,0,1.1-0.1,1.8-0.1c-0.2,0.7-0.6,1.8-1.1,3.2c-0.6,0-5-0.2-5-0.2l0.8,0.9c0.3,0.3,1.6,1.7,2.7,1.7c0,0,0.3-0.1,0.6-0.1c-0.8,2-1.7,3.7-2.6,5l0.7,0.7c1.5-1.2,3.2-3.4,4.8-6.4c0.3-0.1,1.2-0.4,1.2-0.4l-0.1-1c0,0-0.2,0-0.4,0c1.8-3.6,3.1-7.1,3.1-7.1l0.5-1.3L926.9,123.4z"/>
526+ <rect x="2386" y="12" rx="5" fill="#294D8E" width="35" height="57"/>
527+ <rect x="2387" y="13" rx="4" fill="#F4F4F4" width="33" height="55"/>
528+ <rect x="2390" y="16" rx="3" fill="#294D8E" width="27" height="49"/>
529+ <rect x="2393" y="19" rx="7" fill="#F4F4F4" width="21" height="43"/>
530+ <rect x="2386" y="100" rx="5" fill="#294D8E" width="35" height="57"/>
531+ <rect x="2387" y="101" rx="4" fill="#FFFFFF" width="33" height="55"/>
532+ <rect x="2390" y="104" rx="3" fill="#294D8E" width="27" height="49"/>
533+ <rect x="2393" y="107" rx="7" fill="#FFFFFF" width="21" height="43"/>
534+ <path fill="#99B9FF" stroke="#678ED3" d="M2323.8,13.9l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2333.4,11,2323.8,13.9,2323.8,13.9z"/>
535+ <path fill="#99B9FF" stroke="#678ED3" d="M2267.6,19.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2259.5,20.7,2267.6,19.9,2267.6,19.9z"/>
536+ <path fill="#99B9FF" stroke="#678ED3" d="M2139.6,16.1c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2131.5,16.9,2139.6,16.1,2139.6,16.1z"/>
537+ <polygon fill="#99B9FF" stroke="#678ED3" points="2264.9,16.3 2265.7,17.8 2260.9,18.3 2260.4,16.8"/>
538+ <path fill="#99B9FF" stroke="#678ED3" d="M2267.1,12.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2267.1,12.5z"/>
539+ <path fill="#99B9FF" stroke="#678ED3" d="M2203.6,18.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2195.5,19.7,2203.6,18.9,2203.6,18.9z"/>
540+ <path fill="#99B9FF" stroke="#678ED3" d="M2203.1,14.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2203.1,14.5z"/>
541+ <path fill="#99B9FF" stroke="#678ED3" d="M2323.8,101.9l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2333.4,99,2323.8,101.9,2323.8,101.9z"/>
542+ <path fill="#99B9FF" stroke="#678ED3" d="M2267.6,107.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2259.5,108.7,2267.6,107.9,2267.6,107.9z"/>
543+ <path fill="#99B9FF" stroke="#678ED3" d="M2139.6,104.1c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2131.5,104.9,2139.6,104.1,2139.6,104.1z"/>
544+ <polygon fill="#99B9FF" stroke="#678ED3" points="2264.9,104.3 2265.7,105.8 2260.9,106.3 2260.4,104.8 "/>
545+ <path fill="#99B9FF" stroke="#678ED3" d="M2267.1,100.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2267.1,100.5z"/>
546+ <path fill="#99B9FF" stroke="#678ED3" d="M2203.6,106.9c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2195.5,107.7,2203.6,106.9,2203.6,106.9z"/>
547+ <path fill="#99B9FF" stroke="#678ED3" d="M2203.1,102.5c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2203.1,102.5z"/>
548+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2643.1,20.3l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7L2640,19l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2652.7,17.4,2643.1,20.3,2643.1,20.3z"/>
549+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.9,26.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2578.9,27.1,2586.9,26.3,2586.9,26.3z"/>
550+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2458.9,22.5c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2450.9,23.3,2458.9,22.5,2458.9,22.5z"/>
551+ <polygon fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" points="2584.3,22.6 2585,24.1 2580.3,24.7 2579.7,23.2 "/>
552+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.5,18.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2586.5,18.8z"/>
553+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.9,25.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2514.9,26.1,2522.9,25.3,2522.9,25.3z"/>
554+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.5,20.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2522.5,20.8z"/>
555+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2643.1,108.3l-0.1,1.3c0,0,8.9-2.4,9.4-0.6c0,0-0.4,4.1-1.4,4.9c0-0.5-0.7-0.6-1.7-0.5c0.1-1.1,0.3-3.7,1-4l-1.6-0.7l-0.2,4.8c-0.5,0.1-1,0.2-1.5,0.3l-0.9-4.8l-1,0.4l0.9,4.7c-1.1,0.3-2,0.5-2,0.5l-1.8-7l-2.2-0.5l3.1,8.9c0,0,7.7-2.7,7.5-0.7c3.2-2.5,4.3-6.5,4.3-6.5C2652.7,105.4,2643.1,108.3,2643.1,108.3z"/>
556+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.9,114.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2578.9,115.1,2586.9,114.3,2586.9,114.3z"/>
557+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2458.9,110.5c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2450.9,111.3,2458.9,110.5,2458.9,110.5z"/>
558+ <polygon fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" points="2584.3,110.6 2585,112.1 2580.3,112.7 2579.7,111.2 "/>
559+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2586.5,106.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2586.5,106.8z"/>
560+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.9,113.3c-0.8-2.5,4.7,2.6,2.6,2.2c-2.1-0.4-10.6,0.1-11.9,0.6l-0.6-2.2C2514.9,114.1,2522.9,113.3,2522.9,113.3z"/>
561+ <path fill="#DBC879" stroke="#7C5A2D" stroke-width="0.5" d="M2522.5,108.8c-2-2.3-2.8,0.6-9,0.6l1.1,1.7l7.7-1L2522.5,108.8z"/>
562+ <path fill="#101040" d="M632.3,54.7c-2-0.9-11.8,3.3-11.8,3.3l0.5-20.7c0,0,5.3-4.3,9.6-1.7l-1.2-5c-1.4-0.2-8.4,5.5-8.4,5.5s-0.6-18.8,4.8-19.9c0,0-8.9-2.6-9.7-2.2c-0.4,0.2-0.4,13.8-0.2,27.1c-3.2,3.1-5.9,6.2-7.1,7.3l0.4-28.5c0,0-4.7-2.6-5.8-1.4l0.2,14.8c-0.3-0.5-0.7-0.9-1-1.3c-1-1.3-6.1-4.2-6.1-4.2s1.7,5.6,2.5,6.6c0.7,0.9,3,2.9,4.6,2.2l0.2,16.5c-3.4,3-7.9,6.8-8.5,5.4c-1.5-3.2-4.7-3-5.2-1.5c-0.5,1.6,4.2,10.2,5.8,10.1c0.8-0.1,4.4-4.9,8-10l0.1,5.3c1.6,2.1,3.8,0.5,4.6-0.7l0.3-11c1.1-1.6,4-4.5,7.1-7.9c0.2,12.1,0.3,22.8,0.3,22.8c4.9-5.2,16.7-4,16.7-4L632.3,54.7z"/>
563+</svg>
addedsrc/game/ctl.ts+71 −0
1+import type { Claim, ClaimOption, GameState, SoundCue } from './engine';
2+import type { Tile } from './tiles';
3+import type { Rules, SeatId } from './types';
4+
5+/**
6+ * How hard a tile left a phone, in the thrower's own frame: `u` is where along
7+ * their edge it was let go of (0 = their left, 1 = their right), `vx` is speed
8+ * along that edge, `vy` speed *into* the table, both px/s. The common screen
9+ * turns this into a throw from that player's side of the table.
10+ */
11+export interface NetThrow {
12+ u: number;
13+ vx: number;
14+ vy: number;
15+ spin: number;
16+}
17+
18+/**
19+ * What the table's UI needs from whatever is running the game.
20+ *
21+ * `Game` satisfies this directly — the hotseat and solo tables talk straight to
22+ * the engine, exactly as before. The online tables put `net/table.ts` here
23+ * instead: same surface, but a call either drives the authoritative engine (on
24+ * the host) or is sent to whoever has it. The UI cannot tell the difference,
25+ * which is the point — every screen is drawn from a `GameState` and every
26+ * button is one of these calls, wherever the state actually lives.
27+ */
28+export interface TableCtl {
29+ readonly state: GameState;
30+ subscribe(fn: () => void): () => void;
31+ getSnapshot(): number;
32+
33+ discard(seat: SeatId, tile: Tile, thrown?: NetThrow): void;
34+ respond(seat: SeatId, claim: Claim | 'pass'): void;
35+ resolveNow(): void;
36+ declareConcealedKong(seat: SeatId, tile: Tile): void;
37+ declareAddedKong(seat: SeatId, tile: Tile): void;
38+ declareSelfDraw(seat: SeatId): void;
39+ reorderHand(seat: SeatId, from: number, to: number): void;
40+ sortHand(seat: SeatId): void;
41+ nextHand(): void;
42+ newGame(bots?: boolean[], names?: string[]): void;
43+ applySettings(names: string[], rules: Rules): void;
44+
45+ undoable(): { zh: string; en: string } | null;
46+ undo(): void;
47+
48+ canSelfDrawWin(seat: SeatId): boolean;
49+ concealedKongs(seat: SeatId): Tile[];
50+ addedKongs(seat: SeatId): Tile[];
51+ tenpai(seat: SeatId): Tile[];
52+ sacredTiles(seat: SeatId): Tile[];
53+ nextDrawer(): SeatId | null;
54+
55+ readonly hasBots: boolean;
56+ isBot(seat: SeatId): boolean;
57+ readonly wallLeft: number;
58+ readonly roundWind: number;
59+
60+ onSound(fn: (cue: SoundCue) => void): () => void;
61+}
62+
63+/** Claim options a seat is still owed an answer on, or undefined. */
64+export const pendingOptions = (
65+ state: GameState,
66+ seat: SeatId,
67+): ClaimOption[] | undefined =>
68+ (state.phase === 'claims' || state.phase === 'robkong') &&
69+ state.responses[seat] === undefined
70+ ? state.options[seat]
71+ : undefined;
modifiedsrc/game/engine.ts+26 −1
⋯ 140 unchanged lines
141141 state: GameState;
142142 private listeners = new Set<() => void>();
143143 private version = 0;
144+ /**
145+ * Whether moves are written to localStorage. Off for the online tables: the
146+ * authoritative copy lives in the room, a mirror of somebody else's game must
147+ * not overwrite the local solo save — and the sandbox a deployed game runs in
148+ * has no storage to write to anyway.
149+ */
150+ persist = true;
144151
145152 constructor(names: string[] = DEFAULT_NAMES, rules: Rules = DEFAULT_RULES) {
146153 this.state = {
⋯ 37 unchanged lines
184191 getSnapshot = () => this.version;
185192 private emit() {
186193 this.version++;
187- saveGame(this.state);
194+ if (this.persist) saveGame(this.state);
188195 for (const l of this.listeners) l();
189196 }
190197
⋯ 92 unchanged lines
283290 this.state = state;
284291 this.emit();
285292 }
293+
294+ /** One nameplate, not the whole settings screen — a phone claimed a seat. */
295+ renameSeat(seat: SeatId, name: string) {
296+ if (!name || this.state.players[seat].name === name) return;
297+ this.state.players[seat].name = name;
298+ this.emit();
299+ }
300+
301+ /**
302+ * Hand a seat to the computer, or take it back. This is what happens online
303+ * when a player drops mid-hand: their seat cannot hold the table forever, and
304+ * it cannot simply be skipped either — somebody has to play it.
305+ */
306+ setBot(seat: SeatId, bot: boolean) {
307+ if (this.state.bots[seat] === bot) return;
308+ this.state.bots[seat] = bot;
309+ this.emit();
310+ }
286311 private say(msg: string) {
287312 this.state.log.unshift(msg);
288313 if (this.state.log.length > 40) this.state.log.pop();
⋯ 717 unchanged lines
addedsrc/net/handOrder.test.ts+29 −0
1+import { describe, expect, it } from 'vitest';
2+import { reconcileOrder } from './handOrder';
3+
4+describe('reconcileOrder', () => {
5+ it('keeps an arrangement while the tiles are all still there', () => {
6+ expect(reconcileOrder([5, 2, 9, 2], [2, 2, 5, 9])).toEqual([5, 2, 9, 2]);
7+ });
8+
9+ it('appends a new draw at the end, whatever the engine sorted it next to', () => {
10+ expect(reconcileOrder([5, 2, 9], [2, 3, 5, 9])).toEqual([5, 2, 9, 3]);
11+ });
12+
13+ it('drops a discard and keeps everything else where it was', () => {
14+ expect(reconcileOrder([5, 2, 9, 3], [2, 5, 9])).toEqual([5, 2, 9]);
15+ });
16+
17+ it('handles duplicates by count, not identity', () => {
18+ // Two of the 2s went (a pung claim), one 7 arrived.
19+ expect(reconcileOrder([2, 8, 2, 2], [2, 7, 8])).toEqual([2, 8, 7]);
20+ });
21+
22+ it('builds an order from nothing', () => {
23+ expect(reconcileOrder([], [3, 1, 2])).toEqual([3, 1, 2]);
24+ });
25+
26+ it('survives a whole new hand', () => {
27+ expect(reconcileOrder([1, 2, 3], [10, 11, 12])).toEqual([10, 11, 12]);
28+ });
29+});
addedsrc/net/handOrder.ts+37 −0
1+import type { Tile } from '../game/tiles';
2+
3+/**
4+ * A hand's arrangement is the player's own business, and online it stays on
5+ * their device: sending every drag over the wire and waiting for it to come
6+ * back would put a round-trip inside a gesture. So the shared state carries the
7+ * hand as a multiset in whatever order the engine keeps it, and each client
8+ * lays its own copy out however its player left it.
9+ *
10+ * This is the reconciliation: keep the previous arrangement wherever the tiles
11+ * are still there, drop what has gone, and append what is new at the end — the
12+ * engine appends the drawn tile last, so the tile that just arrived lands in
13+ * the drawn slot the UI already marks.
14+ */
15+export function reconcileOrder(prev: Tile[], hand: Tile[]): Tile[] {
16+ const counts = new Map<Tile, number>();
17+ for (const t of hand) counts.set(t, (counts.get(t) ?? 0) + 1);
18+
19+ const out: Tile[] = [];
20+ for (const t of prev) {
21+ const n = counts.get(t) ?? 0;
22+ if (n > 0) {
23+ counts.set(t, n - 1);
24+ out.push(t);
25+ }
26+ }
27+ // Whatever is left arrived since we last looked, in the engine's own order —
28+ // which puts the newest draw at the end, where the 摸 marker expects it.
29+ for (const t of hand) {
30+ const n = counts.get(t) ?? 0;
31+ if (n > 0) {
32+ counts.set(t, n - 1);
33+ out.push(t);
34+ }
35+ }
36+ return out;
37+}
addedsrc/net/protocol.ts+102 −0
1+import type { Claim, GameState } from '../game/engine';
2+import type { NetThrow } from '../game/ctl';
3+import type { Tile } from '../game/tiles';
4+import type { SeatId } from '../game/types';
5+
6+/**
7+ * What goes over the wire, in one place.
8+ *
9+ * The room is host-authoritative: exactly one client — whichever one antics
10+ * says is the host — runs the real engine, and everything it decides is
11+ * published as plain `GameState` JSON under one key of the shared room state.
12+ * Everyone else renders that and asks for things by sending intents, which the
13+ * host validates against the seat map before playing them through the same
14+ * engine methods a button would have called. The engine itself never knows any
15+ * of this is happening — the same trick `autoplay.ts` plays, stretched over a
16+ * network.
17+ */
18+
19+/** The two ways a room can be playing. */
20+export type NetMode = 'online' | 'party';
21+
22+/** Who holds a seat. The name outlives the connection — see the antics docs on
23+ * leavers: record it where it arrives, the match outlives the connection. */
24+export interface SeatSlot {
25+ /** antics player id, or null while nobody holds it. */
26+ id: string | null;
27+ name: string;
28+}
29+
30+/** The shared room state, as the host writes it. */
31+export interface SharedKeys {
32+ mode: NetMode;
33+ seats: SeatSlot[];
34+ /** The whole engine state. null while the room is still in its lobby. */
35+ game: GameState | null;
36+}
37+
38+/** Something a player asks the host to play for their seat. */
39+export type Act =
40+ | { kind: 'discard'; tile: Tile; thrown?: NetThrow }
41+ | { kind: 'respond'; claim: Claim | 'pass' }
42+ | { kind: 'resolveNow' }
43+ | { kind: 'ankong'; tile: Tile }
44+ | { kind: 'addkong'; tile: Tile }
45+ | { kind: 'selfDraw' }
46+ | { kind: 'nextHand' }
47+ | { kind: 'newGame' };
48+
49+export interface ActMsg {
50+ seat: SeatId;
51+ act: Act;
52+}
53+
54+/** Ask the host for a seat. null means "any free one". */
55+export interface SeatMsg {
56+ seat: SeatId | null;
57+}
58+
59+/**
60+ * Host to everyone else: things worth seeing or hearing that are not state — a
61+ * sound cue, or the throw a discard arrived with, so every table can show the
62+ * tile flying in from that player's edge.
63+ */
64+export type FxMsg =
65+ | { fx: 'sfx'; kind: string; tile?: Tile }
66+ | { fx: 'throw'; seat: SeatId; t: NetThrow };
67+
68+export const EV_ACT = 'act';
69+export const EV_SEAT = 'seat';
70+export const EV_FX = 'fx';
71+
72+export const EMPTY_SEATS: SeatSlot[] = [
73+ { id: null, name: '' },
74+ { id: null, name: '' },
75+ { id: null, name: '' },
76+ { id: null, name: '' },
77+];
78+
79+/** The seat in the QR's link, if the hosting page passed it through to us. */
80+export function seatFromUrl(): SeatId | null {
81+ try {
82+ const raw = new URLSearchParams(window.location.search).get('seat');
83+ if (raw === null) return null;
84+ const n = Number(raw);
85+ return n === 0 || n === 1 || n === 2 || n === 3 ? (n as SeatId) : null;
86+ } catch {
87+ return null;
88+ }
89+}
90+
91+/** Whether the page arrived with a room to join — i.e. we are on antics. */
92+export function roomFromUrl(): string | null {
93+ try {
94+ return new URLSearchParams(window.location.search).get('room');
95+ } catch {
96+ return null;
97+ }
98+}
99+
100+/** The room's invite link with the seat stitched in, for that seat's QR. */
101+export const seatLink = (link: string, seat: SeatId) =>
102+ `${link}${link.includes('?') ? '&' : '?'}seat=${seat}`;
addedsrc/net/session.ts+331 −0
1+import { joinRoom, type Room } from 'antics-sdk';
2+import { AutoPlay } from '../game/autoplay';
3+import type { NetThrow } from '../game/ctl';
4+import type { SoundEvent } from '../game/engine';
5+import type { Tile } from '../game/tiles';
6+import type { Rules, SeatId } from '../game/types';
7+import {
8+ EMPTY_SEATS,
9+ EV_ACT,
10+ EV_FX,
11+ EV_SEAT,
12+ type ActMsg,
13+ type FxMsg,
14+ type NetMode,
15+ type SeatMsg,
16+ type SeatSlot,
17+} from './protocol';
18+import { NetTable } from './table';
19+
20+const SEATS: SeatId[] = [0, 1, 2, 3];
21+
22+/**
23+ * One device's connection to a room, and everything that follows from it.
24+ *
25+ * antics elects a host, and the host runs the table: the real engine, the
26+ * computer players, the answering of everyone else's intents. Everybody else
27+ * mirrors the state the host publishes. If the host leaves, antics picks a new
28+ * one, whose mirror is already current — it starts its own engine from what it
29+ * was just watching and the game carries on.
30+ *
31+ * `role` is this device's, not the room's: the laptop that starts a party game
32+ * is the `screen` everyone throws at; every other device is a `player`.
33+ */
34+export type NetRole = 'player' | 'screen';
35+
36+export class NetSession {
37+ readonly room: Room;
38+ readonly table: NetTable;
39+ role: NetRole = 'player';
40+ /** Latest connection trouble worth showing, or null. */
41+ err: string | null = null;
42+ /**
43+ * A phone threw a tile: the full-size tables put it into their pool so it
44+ * flies in from that player's edge. Wired by whoever owns a pool.
45+ */
46+ onThrowFx: (seat: SeatId, t: NetThrow) => void = () => {};
47+
48+ private autoplay: AutoPlay | null = null;
49+ private stopAutoplay: (() => void) | null = null;
50+ private busy: () => boolean = () => false;
51+ private listeners = new Set<() => void>();
52+ private version = 0;
53+ private unsubs: (() => void)[] = [];
54+
55+ private constructor(room: Room) {
56+ this.room = room;
57+ this.table = new NetTable({
58+ isAuthority: () => this.isHost,
59+ mySeat: () => this.mySeat,
60+ sendAct: (seat, act) => this.room.send(EV_ACT, { seat, act } as never),
61+ });
62+
63+ const u = this.unsubs;
64+ u.push(
65+ room.onState(() => {
66+ this.table.adopt(this.sharedGame());
67+ this.syncAutoplay();
68+ this.bump();
69+ }),
70+ room.on(EV_ACT, (p, from) => this.handleAct(p as unknown as ActMsg, from)),
71+ room.on(EV_SEAT, (p, from) => this.handleSeat(p as unknown as SeatMsg, from)),
72+ room.on(EV_FX, (p) => this.handleFx(p as unknown as FxMsg)),
73+ room.onJoin(() => this.bump()),
74+ room.onLeave((p) => {
75+ if (this.isHost) this.vacate(p.id);
76+ this.bump();
77+ }),
78+ room.onHostChange(() => {
79+ // A new host's engine is its mirror — already current. It only has to
80+ // start acting like the host, which `syncAutoplay` and the guards on
81+ // every handler do by asking `isHost` fresh each time.
82+ this.syncAutoplay();
83+ this.bump();
84+ }),
85+ room.onError((e) => {
86+ this.err = `${e.message} — ${e.hint}`;
87+ this.bump();
88+ }),
89+ // The host's own engine is the source of truth: publish every change.
90+ this.table.engine.subscribe(() => {
91+ if (this.isHost && this.mode) this.publishGame();
92+ this.syncAutoplay();
93+ }),
94+ this.table.engine.onSound((cue) => {
95+ if (!this.isHost) return;
96+ this.table.emitSound(cue);
97+ this.room.send(EV_FX, { fx: 'sfx', kind: cue.kind, tile: cue.tile } as never);
98+ }),
99+ );
100+ // Mirror whatever the room already holds — a late joiner walks in on the
101+ // game as it stands, not on an empty table.
102+ this.table.adopt(this.sharedGame());
103+ }
104+
105+ /** Join the room in the page URL, or create a fresh one. */
106+ static async join(): Promise<NetSession> {
107+ const room = await joinRoom({});
108+ return new NetSession(room);
109+ }
110+
111+ leave() {
112+ for (const u of this.unsubs) u();
113+ this.unsubs = [];
114+ this.stopAutoplay?.();
115+ this.stopAutoplay = null;
116+ this.room.leave();
117+ }
118+
119+ // ---- store plumbing ----------------------------------------------------
120+ subscribe = (fn: () => void) => {
121+ this.listeners.add(fn);
122+ return () => {
123+ this.listeners.delete(fn);
124+ };
125+ };
126+ getSnapshot = () => this.version;
127+ private bump() {
128+ this.version++;
129+ for (const l of this.listeners) l();
130+ }
131+
132+ // ---- what the room says ------------------------------------------------
133+ get isHost() {
134+ return this.room.isHost;
135+ }
136+ get mode(): NetMode | null {
137+ const m = this.room.state.mode;
138+ return m === 'online' || m === 'party' ? m : null;
139+ }
140+ get seats(): SeatSlot[] {
141+ const s = this.room.state.seats;
142+ return Array.isArray(s) && s.length === 4 ? (s as unknown as SeatSlot[]) : EMPTY_SEATS;
143+ }
144+ private sharedGame(): unknown {
145+ return this.room.state.game ?? null;
146+ }
147+ get mySeat(): SeatId | null {
148+ const seats = this.seats;
149+ for (const i of SEATS) if (seats[i].id === this.room.me.id) return i;
150+ return null;
151+ }
152+ /** Whether a hand has actually been dealt in this room. */
153+ get started(): boolean {
154+ const g = this.table.engine.state;
155+ return this.sharedGame() !== null && g.phase !== 'lobby';
156+ }
157+ /** Everyone here, for the lobby's presence line. */
158+ get playerCount() {
159+ return this.room.players.length;
160+ }
161+
162+ // ---- lobby / starting --------------------------------------------------
163+ /**
164+ * Open an online game in this room: publish the mode and take a seat. Only
165+ * the host can (room state is the host's to write); the button that calls
166+ * this is only shown to the host.
167+ */
168+ openOnlineLobby() {
169+ if (!this.isHost) return;
170+ this.room.setState({ mode: 'online', seats: structuredClone(EMPTY_SEATS) as never });
171+ this.claimSeat(0);
172+ }
173+
174+ /** Deal the online game. Seats nobody claimed go to the computer. */
175+ startOnline(rules: Rules) {
176+ if (!this.isHost) return;
177+ const seats = this.seats;
178+ const bots = seats.map((s) => s.id === null);
179+ if (bots.every(Boolean)) return; // nobody seated at all
180+ const names = seats.map((s, i) => s.name || `玩家 ${i + 1}`);
181+ const eng = this.table.engine;
182+ eng.applySettings(names, rules);
183+ eng.newGame(bots, names);
184+ }
185+
186+ /**
187+ * The party table: this device becomes the common screen and deals at once.
188+ * All four seats start as people at this screen, exactly like hotseat — a
189+ * phone that scans a seat's QR takes that seat over, and gives it back if it
190+ * leaves.
191+ */
192+ startParty(names: string[], rules: Rules) {
193+ if (!this.isHost) return;
194+ this.role = 'screen';
195+ const seats = EMPTY_SEATS.map((_, i) => ({ id: null, name: names[i] ?? '' }));
196+ this.room.setState({ mode: 'party', seats: seats as never });
197+ const eng = this.table.engine;
198+ eng.applySettings(names, rules);
199+ eng.newGame([false, false, false, false], names);
200+ }
201+
202+ /** Ask for a seat — a numbered one, or any free one. */
203+ claimSeat(seat: SeatId | null) {
204+ if (this.isHost) this.handleSeat({ seat }, this.room.me.id);
205+ else this.room.send(EV_SEAT, { seat } as never);
206+ }
207+
208+ /** Hand a deserted seat to the computer (host's own button). */
209+ handToBot(seat: SeatId) {
210+ if (!this.isHost) return;
211+ if (this.seats[seat].id !== null) return;
212+ this.table.engine.setBot(seat, true);
213+ }
214+
215+ /** The pool animations to wait on before a computer player moves. */
216+ setBusy(busy: () => boolean) {
217+ this.busy = busy;
218+ }
219+
220+ // ---- host duties -------------------------------------------------------
221+ private handleSeat(msg: SeatMsg, from: string) {
222+ if (!this.isHost) return;
223+ const name = this.room.player(from)?.name ?? '';
224+ const seats = structuredClone(this.seats);
225+ let seat = msg.seat;
226+ if (seat === null) {
227+ seat = SEATS.find((i) => seats[i].id === null) ?? null;
228+ if (seat === null) return;
229+ }
230+ if (seats[seat].id !== null && seats[seat].id !== from) return; // taken
231+ // One seat per player: claiming a new one lets go of the old.
232+ for (const i of SEATS) if (seats[i].id === from) seats[i] = { id: null, name: seats[i].name };
233+ seats[seat] = { id: from, name: name || seats[seat].name };
234+ this.room.setState({ seats: seats as never });
235+
236+ // Mid-game, the seat comes back from the computer (or the common screen)
237+ // to the person who claimed it.
238+ if (this.sharedGame() !== null) {
239+ this.table.engine.setBot(seat, false);
240+ if (name) this.table.engine.renameSeat(seat, name);
241+ }
242+ this.bump();
243+ }
244+
245+ private vacate(id: string) {
246+ const seats = structuredClone(this.seats);
247+ let changed = false;
248+ for (const i of SEATS) {
249+ if (seats[i].id === id) {
250+ seats[i] = { id: null, name: seats[i].name };
251+ changed = true;
252+ }
253+ }
254+ if (changed) this.room.setState({ seats: seats as never });
255+ // The seat stays human: at a party table the common screen simply plays it
256+ // again, and online the host is offered the 電腦代打 button instead of the
257+ // table deciding on its own that a dropped friend is gone for good.
258+ }
259+
260+ private handleAct(msg: ActMsg, from: string) {
261+ if (!this.isHost || !msg || typeof msg.seat !== 'number') return;
262+ const seat = msg.seat;
263+ // Their own seat — or one nobody holds, which is how the party screen
264+ // plays its unclaimed seats when it is not the one running the engine
265+ // (after a reload, say, when a phone has inherited the host's chair).
266+ const holder = this.seats[seat]?.id;
267+ if (holder !== from && holder !== null) return;
268+ const eng = this.table.engine;
269+ const act = msg.act;
270+ switch (act.kind) {
271+ case 'discard':
272+ if (act.thrown) {
273+ // Show the throw before the state lands: the pool holds the release
274+ // until the discard it belongs to arrives.
275+ this.onThrowFx(seat, act.thrown);
276+ this.room.send(EV_FX, { fx: 'throw', seat, t: act.thrown } as never);
277+ }
278+ eng.discard(seat, act.tile as Tile);
279+ break;
280+ case 'respond':
281+ eng.respond(seat, act.claim);
282+ break;
283+ case 'resolveNow':
284+ if (eng.nextDrawer() === seat) eng.resolveNow();
285+ break;
286+ case 'ankong':
287+ eng.declareConcealedKong(seat, act.tile as Tile);
288+ break;
289+ case 'addkong':
290+ eng.declareAddedKong(seat, act.tile as Tile);
291+ break;
292+ case 'selfDraw':
293+ eng.declareSelfDraw(seat);
294+ break;
295+ case 'nextHand':
296+ eng.nextHand();
297+ break;
298+ case 'newGame':
299+ eng.newGame(eng.state.bots);
300+ break;
301+ }
302+ }
303+
304+ private handleFx(msg: FxMsg) {
305+ if (this.isHost || !msg) return;
306+ if (msg.fx === 'sfx') {
307+ this.table.emitSound({ kind: msg.kind as SoundEvent, tile: msg.tile });
308+ } else if (msg.fx === 'throw') {
309+ this.onThrowFx(msg.seat, msg.t);
310+ }
311+ }
312+
313+ private publishGame() {
314+ // The engine mutates its state in place; the room keeps what it is handed,
315+ // so it gets a copy that will hold still.
316+ this.room.setState({ game: structuredClone(this.table.engine.state) as never });
317+ }
318+
319+ /** The computer plays exactly when this device is the host of a live game. */
320+ private syncAutoplay() {
321+ const should = this.isHost && this.mode !== null && this.sharedGame() !== null;
322+ if (should && !this.autoplay) {
323+ this.autoplay = new AutoPlay(this.table.engine, () => this.busy());
324+ this.stopAutoplay = this.autoplay.start();
325+ } else if (!should && this.autoplay) {
326+ this.stopAutoplay?.();
327+ this.autoplay = null;
328+ this.stopAutoplay = null;
329+ }
330+ }
331+}
addedsrc/net/table.ts+264 −0
1+import type { NetThrow, TableCtl } from '../game/ctl';
2+import { Game, type Claim, type GameState, type SoundCue } from '../game/engine';
3+import { sortTiles, type Tile } from '../game/tiles';
4+import type { Rules, SeatId } from '../game/types';
5+import { reconcileOrder } from './handOrder';
6+import type { Act } from './protocol';
7+
8+/**
9+ * How the network session talks to the table. Kept this small so the table can
10+ * be reasoned about (and tested) without a room behind it.
11+ */
12+export interface TableWire {
13+ /** Whether this device is running the authoritative engine right now. */
14+ isAuthority(): boolean;
15+ /** The seat this device's player holds, if any. */
16+ mySeat(): SeatId | null;
17+ sendAct(seat: SeatId, act: Act): void;
18+}
19+
20+/** How long an unanswered discard hides its tile before we admit it was lost. */
21+const PENDING_TTL = 3000;
22+
23+/**
24+ * The online table: the same surface the engine offers (`TableCtl`), backed by
25+ * a `Game` that is either the real one or a mirror of the host's.
26+ *
27+ * On the host the inner engine is authoritative and calls go straight into it.
28+ * Everywhere else the inner engine is a mirror kept current by `adopt`, and a
29+ * call becomes an intent sent to the host — with two exceptions, both about
30+ * your own hand, both because a round-trip inside a gesture is lag you can
31+ * feel. Arranging your tiles never goes over the wire at all: the shared state
32+ * carries the hand as a multiset and each device keeps its own arrangement
33+ * (`handOrder.ts`). And a discard hides the tile at once, so the hand does not
34+ * hold a tile you have visibly thrown while the wire does its round trip.
35+ */
36+export class NetTable implements TableCtl {
37+ /** The engine. Authoritative on the host, a mirror everywhere else. */
38+ readonly engine: Game;
39+ /** Whether the undo button works here — the party screen, nowhere else. */
40+ allowUndo = false;
41+
42+ private wire: TableWire;
43+ private listeners = new Set<() => void>();
44+ private version = 0;
45+
46+ // ---- the local arrangement of this player's own hand -------------------
47+ private order: Tile[] = [];
48+ private orderRev = 0;
49+ /** The player slid the drawn tile into the row, so the 摸 marker comes off. */
50+ private drawnMerged = false;
51+ private drawnKey = '';
52+ /** A discard sent but not yet echoed back, hidden from the hand meanwhile. */
53+ private pending: { tile: Tile; len: number; at: number } | null = null;
54+
55+ private view: GameState | null = null;
56+ private viewKey = '';
57+ private lastAdopted: unknown = null;
58+
59+ private sounds = new Set<(cue: SoundCue) => void>();
60+
61+ constructor(wire: TableWire) {
62+ this.wire = wire;
63+ this.engine = new Game();
64+ this.engine.persist = false;
65+ this.engine.subscribe(() => this.bump());
66+ }
67+
68+ // ---- store plumbing ----------------------------------------------------
69+ subscribe = (fn: () => void) => {
70+ this.listeners.add(fn);
71+ return () => {
72+ this.listeners.delete(fn);
73+ };
74+ };
75+ getSnapshot = () => this.version;
76+ private bump() {
77+ this.version++;
78+ for (const l of this.listeners) l();
79+ }
80+
81+ /** The host published a new state and we are not the host: mirror it. */
82+ adopt(shared: unknown) {
83+ if (this.wire.isAuthority() || shared === this.lastAdopted || !shared) return;
84+ this.lastAdopted = shared;
85+ // The room hands its state out read-only and the engine mutates in place,
86+ // so the mirror gets its own copy.
87+ this.engine.restore(structuredClone(shared) as GameState);
88+ }
89+
90+ // ---- the state the UI draws --------------------------------------------
91+ get state(): GameState {
92+ const seat = this.wire.mySeat();
93+ const s = this.engine.state;
94+ if (seat === null) return s;
95+
96+ const key = `${this.version}:${this.orderRev}`;
97+ if (this.view && this.viewKey === key) return this.view;
98+
99+ // A fresh draw un-merges: the marker is about *this* tile, not the last.
100+ const drawnKey = `${s.handNumber}:${s.turn}:${s.drawnTile}:${s.players[seat].hand.length}`;
101+ if (drawnKey !== this.drawnKey) {
102+ this.drawnKey = drawnKey;
103+ this.drawnMerged = false;
104+ }
105+
106+ this.order = reconcileOrder(this.order, s.players[seat].hand);
107+ let hand = this.order;
108+ const p = this.pending;
109+ if (p) {
110+ if (s.players[seat].hand.length <= p.len || Date.now() - p.at > PENDING_TTL) {
111+ this.pending = null;
112+ } else {
113+ const i = hand.indexOf(p.tile);
114+ if (i >= 0) hand = [...hand.slice(0, i), ...hand.slice(i + 1)];
115+ }
116+ }
117+
118+ const players = s.players.slice();
119+ players[seat] = { ...players[seat], hand };
120+ this.view = {
121+ ...s,
122+ players,
123+ drawnSeparate: s.drawnSeparate && (s.turn !== seat || !this.drawnMerged),
124+ };
125+ this.viewKey = key;
126+ return this.view;
127+ }
128+
129+ // ---- actions -----------------------------------------------------------
130+ private act(seat: SeatId, act: Act, run: () => void) {
131+ if (this.wire.isAuthority()) run();
132+ else this.wire.sendAct(seat, act);
133+ }
134+
135+ discard(seat: SeatId, tile: Tile, thrown?: NetThrow) {
136+ if (this.wire.isAuthority()) return this.engine.discard(seat, tile);
137+ if (seat !== this.wire.mySeat()) return;
138+ if (this.pending) return; // one throw at a time
139+ const held = this.state.players[seat].hand.length;
140+ this.wire.sendAct(seat, { kind: 'discard', tile, thrown });
141+ this.pending = { tile, len: held - 1, at: Date.now() };
142+ this.orderRev++;
143+ this.bump();
144+ }
145+
146+ respond(seat: SeatId, claim: Claim | 'pass') {
147+ this.act(seat, { kind: 'respond', claim }, () => this.engine.respond(seat, claim));
148+ }
149+
150+ resolveNow() {
151+ if (this.wire.isAuthority()) return this.engine.resolveNow();
152+ const seat = this.wire.mySeat();
153+ if (seat !== null) this.wire.sendAct(seat, { kind: 'resolveNow' });
154+ }
155+
156+ declareConcealedKong(seat: SeatId, tile: Tile) {
157+ this.act(seat, { kind: 'ankong', tile }, () => this.engine.declareConcealedKong(seat, tile));
158+ }
159+
160+ declareAddedKong(seat: SeatId, tile: Tile) {
161+ this.act(seat, { kind: 'addkong', tile }, () => this.engine.declareAddedKong(seat, tile));
162+ }
163+
164+ declareSelfDraw(seat: SeatId) {
165+ this.act(seat, { kind: 'selfDraw' }, () => this.engine.declareSelfDraw(seat));
166+ }
167+
168+ nextHand() {
169+ if (this.wire.isAuthority()) return this.engine.nextHand();
170+ const seat = this.wire.mySeat();
171+ if (seat !== null) this.wire.sendAct(seat, { kind: 'nextHand' });
172+ }
173+
174+ newGame(bots?: boolean[], names?: string[]) {
175+ if (this.wire.isAuthority()) return this.engine.newGame(bots, names);
176+ const seat = this.wire.mySeat();
177+ if (seat !== null) this.wire.sendAct(seat, { kind: 'newGame' });
178+ }
179+
180+ applySettings(names: string[], rules: Rules) {
181+ if (this.wire.isAuthority()) this.engine.applySettings(names, rules);
182+ }
183+
184+ // Your own hand is arranged on your own device and nowhere else. Somebody
185+ // else's hand only reaches here on the party screen, whose engine is real.
186+ reorderHand(seat: SeatId, from: number, to: number) {
187+ if (seat === this.wire.mySeat()) {
188+ const hand = this.order;
189+ if (from === to || from < 0 || to < 0 || from >= hand.length || to >= hand.length) return;
190+ const s = this.engine.state;
191+ const last = hand.length - 1;
192+ if (s.drawnSeparate && s.turn === seat && (from === last || to === last)) this.drawnMerged = true;
193+ const [t] = hand.splice(from, 1);
194+ hand.splice(to, 0, t);
195+ this.orderRev++;
196+ this.bump();
197+ return;
198+ }
199+ if (this.wire.isAuthority()) this.engine.reorderHand(seat, from, to);
200+ }
201+
202+ sortHand(seat: SeatId) {
203+ if (seat === this.wire.mySeat()) {
204+ this.order = sortTiles(this.order);
205+ this.drawnMerged = true;
206+ this.orderRev++;
207+ this.bump();
208+ return;
209+ }
210+ if (this.wire.isAuthority()) this.engine.sortHand(seat);
211+ }
212+
213+ // ---- undo --------------------------------------------------------------
214+ undoable() {
215+ return this.wire.isAuthority() && this.allowUndo ? this.engine.undoable() : null;
216+ }
217+ undo() {
218+ if (this.wire.isAuthority() && this.allowUndo) this.engine.undo();
219+ }
220+
221+ // ---- queries, all answered by the engine off the same state ------------
222+ canSelfDrawWin(seat: SeatId) {
223+ return this.engine.canSelfDrawWin(seat);
224+ }
225+ concealedKongs(seat: SeatId) {
226+ return this.engine.concealedKongs(seat);
227+ }
228+ addedKongs(seat: SeatId) {
229+ return this.engine.addedKongs(seat);
230+ }
231+ tenpai(seat: SeatId) {
232+ return this.engine.tenpai(seat);
233+ }
234+ sacredTiles(seat: SeatId) {
235+ return this.engine.sacredTiles(seat);
236+ }
237+ nextDrawer() {
238+ return this.engine.nextDrawer();
239+ }
240+ get hasBots() {
241+ return this.engine.hasBots;
242+ }
243+ isBot(seat: SeatId) {
244+ return this.engine.isBot(seat);
245+ }
246+ get wallLeft() {
247+ return this.engine.wallLeft;
248+ }
249+ get roundWind() {
250+ return this.engine.roundWind;
251+ }
252+
253+ // ---- sound -------------------------------------------------------------
254+ /** Fed by the session: the engine's own cues on the host, fx events off it. */
255+ emitSound(cue: SoundCue) {
256+ for (const l of this.sounds) l(cue);
257+ }
258+ onSound = (fn: (cue: SoundCue) => void) => {
259+ this.sounds.add(fn);
260+ return () => {
261+ this.sounds.delete(fn);
262+ };
263+ };
264+}
modifiedsrc/styles.css+224 −3
⋯ 210 unchanged lines
211211 --tw: calc(var(--tile-w) * 0.64);
212212 }
213213 .tile.face {
214- background-image: url('/tiles/tiles.svg');
214+ background-image: url('./assets/tiles.svg');
215215 background-repeat: no-repeat;
216216 background-size: calc(var(--tw) * 43) calc(var(--tw) * 2.75);
217217 background-position: calc(var(--col) * var(--tw) * -1) 0;
218218 filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
219219 }
220220 .tile.back {
221- background-image: url('/tiles/back.png');
221+ background-image: url('./assets/back.png');
222222 background-size: 100% 100%;
223223 filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
224224 }
⋯ 501 unchanged lines
726726 width: var(--ws);
727727 height: var(--wd);
728728 margin: calc(var(--wd) / -2) 0 0 calc(var(--ws) / -2);
729- background-image: url('/tiles/back.png');
729+ background-image: url('./assets/back.png');
730730 background-size: 100% 100%;
731731 filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.45));
732732 }
⋯ 1257 unchanged lines
19901990 display: block;
19911991 flex: none;
19921992 }
1993+
1994+/* ---------- the party table's QRs ---------- */
1995+/* A free seat wears its QR at the end of the strip, inside the seat's own
1996+ rotation so it sits by the right edge of the table whoever it belongs to.
1997+ It must not cover the hand: an unclaimed seat is still played right here. */
1998+.qr-dock {
1999+ position: absolute;
2000+ right: 6px;
2001+ bottom: 34px;
2002+ z-index: 3;
2003+ display: flex;
2004+ flex-direction: column;
2005+ align-items: center;
2006+ gap: 4px;
2007+ padding: 6px;
2008+ border-radius: 10px;
2009+ background: rgba(10, 49, 38, 0.92);
2010+ border: 1px solid rgba(217, 180, 92, 0.45);
2011+}
2012+.seat-qr {
2013+ width: clamp(72px, calc(var(--depth) * 0.44), 132px);
2014+ aspect-ratio: 1;
2015+ border-radius: 6px;
2016+ overflow: hidden;
2017+ background: #fff;
2018+ padding: 3px;
2019+}
2020+.seat-qr svg {
2021+ display: block;
2022+ width: 100%;
2023+ height: 100%;
2024+}
2025+.qr-hint {
2026+ font-size: 10px;
2027+ line-height: 1.3;
2028+ color: var(--ivory);
2029+ text-align: center;
2030+ max-width: 140px;
2031+}
2032+.qr-hint em {
2033+ display: block;
2034+ font-style: normal;
2035+ opacity: 0.6;
2036+ font-size: 9px;
2037+}
2038+.nameplate .tag.phone {
2039+ background: rgba(29, 90, 168, 0.35);
2040+ border-color: rgba(29, 90, 168, 0.7);
2041+ cursor: pointer;
2042+ user-select: none;
2043+ touch-action: none;
2044+}
2045+
2046+/* ---------- the phone controller ---------- */
2047+/* A hand held in landscape: status, buttons, tiles, in that order down the
2048+ screen, with the common screen doing everything else. Tiles get the room —
2049+ they are the only thing this screen is for. */
2050+.ctl {
2051+ --tile-w: clamp(40px, 5.2vw, 76px);
2052+ position: fixed;
2053+ inset: 0;
2054+ display: flex;
2055+ flex-direction: column;
2056+ background: radial-gradient(ellipse at center, var(--felt) 0%, var(--felt-2) 80%);
2057+ color: var(--ivory);
2058+ overflow: hidden;
2059+ touch-action: none;
2060+}
2061+.ctl-top {
2062+ display: flex;
2063+ align-items: center;
2064+ gap: 8px;
2065+ padding: 8px 12px 4px;
2066+ flex-wrap: wrap;
2067+ font-size: 13px;
2068+}
2069+.ctl-top .wind {
2070+ font-weight: 900;
2071+ font-size: 18px;
2072+ color: var(--gold);
2073+}
2074+.ctl-top .wind em {
2075+ font-style: normal;
2076+ font-size: 10px;
2077+ margin-left: 2px;
2078+ opacity: 0.7;
2079+}
2080+.ctl-top .pname {
2081+ font-weight: 600;
2082+}
2083+.ctl-top .tag {
2084+ border: 1px solid rgba(246, 240, 226, 0.4);
2085+ border-radius: 20px;
2086+ padding: 1px 8px;
2087+ font-size: 11px;
2088+}
2089+.ctl-top .tag.ting {
2090+ border-color: var(--gold);
2091+ color: var(--gold);
2092+}
2093+.ctl-status {
2094+ margin-left: auto;
2095+ opacity: 0.75;
2096+}
2097+.ctl-status em {
2098+ font-style: normal;
2099+ opacity: 0.6;
2100+ margin-left: 4px;
2101+}
2102+.ctl-status.hot {
2103+ opacity: 1;
2104+ color: var(--gold);
2105+ font-weight: 600;
2106+}
2107+.ctl-last {
2108+ display: inline-flex;
2109+ align-items: center;
2110+ gap: 4px;
2111+}
2112+.ctl-top .chips {
2113+ font-weight: 600;
2114+ color: var(--gold);
2115+}
2116+.ctl-actions {
2117+ display: flex;
2118+ align-items: center;
2119+ justify-content: center;
2120+ gap: 8px;
2121+ flex-wrap: wrap;
2122+ min-height: var(--bar-h);
2123+ padding: 2px 10px;
2124+}
2125+.ctl-hint {
2126+ font-size: 11px;
2127+ opacity: 0.55;
2128+}
2129+.ctl-hand {
2130+ margin-top: auto;
2131+ display: flex;
2132+ justify-content: center;
2133+ padding: 0 8px calc(10px + env(safe-area-inset-bottom, 0px));
2134+ overflow-x: auto;
2135+}
2136+.ctl-hand .hand {
2137+ flex-wrap: nowrap;
2138+}
2139+/* The tile in the fingers once it has left the row, and its exit: flung, it
2140+ keeps going up off the screen — the common screen takes it from here. */
2141+.ctl-held {
2142+ position: fixed;
2143+ z-index: 5;
2144+ pointer-events: none;
2145+ transform: translate(-50%, -50%);
2146+}
2147+.ctl-held .tile {
2148+ width: 100%;
2149+ height: 100%;
2150+}
2151+.ctl-held.flung {
2152+ animation: fling-up 0.3s ease-in forwards;
2153+}
2154+@keyframes fling-up {
2155+ to {
2156+ transform: translate(-50%, -140vh) rotate(18deg);
2157+ opacity: 0.9;
2158+ }
2159+}
2160+.ctl-result {
2161+ position: fixed;
2162+ left: 50%;
2163+ top: 44%;
2164+ transform: translate(-50%, -50%);
2165+ z-index: 6;
2166+ background: rgba(10, 49, 38, 0.96);
2167+ border: 1px solid rgba(217, 180, 92, 0.5);
2168+ border-radius: 14px;
2169+ padding: 16px 22px;
2170+ text-align: center;
2171+ max-width: min(92vw, 480px);
2172+}
2173+.ctl-result h2 {
2174+ margin: 0 0 8px;
2175+ font-size: 18px;
2176+}
2177+.ctl-result .deltas {
2178+ display: flex;
2179+ gap: 12px;
2180+ flex-wrap: wrap;
2181+ justify-content: center;
2182+ margin-bottom: 12px;
2183+ font-size: 13px;
2184+}
2185+.ctl-result .deltas .up {
2186+ color: var(--gold);
2187+}
2188+.ctl-result .deltas .down {
2189+ color: #e08a84;
2190+}
2191+
2192+/* Seat picking, on the phone and in the online lobby. */
2193+.ctl-pick {
2194+ align-items: center;
2195+ justify-content: center;
2196+ text-align: center;
2197+ overflow-y: auto;
2198+ touch-action: pan-y;
2199+}
2200+.pick-grid {
2201+ display: grid;
2202+ grid-template-columns: repeat(2, minmax(150px, 220px));
2203+ gap: 10px;
2204+ justify-content: center;
2205+ margin: 12px 0;
2206+}
2207+.pick-grid .btn em {
2208+ display: block;
2209+ font-style: normal;
2210+ font-size: 11px;
2211+ opacity: 0.65;
2212+ margin-top: 2px;
2213+}
modifiedsrc/table/pool.ts+75 −3
1+import type { NetThrow } from '../game/ctl';
12 import type { GameState } from '../game/engine';
23 import { makeRng, type Tile } from '../game/tiles';
34 import type { SeatId } from '../game/types';
5+import { SEAT_ROT } from '../ui/rotation';
46 import {
57 aimAt,
68 canThrow,
⋯ 121 unchanged lines
128130 /** Bodies by `seat:index` into that seat's discards. */
129131 private entries = new Map<string, Body>();
130132 private release: Release | null = null;
133+ /** When the throw was let go of. Online its discard takes a round-trip to
134+ * come back, so a release has to be able to outlive a few frames. */
135+ private releasedAt = 0;
131136 /** What the wall looked like when we last measured, so we know to re-measure. */
132137 private measuredAt = '';
133138 private hand = -1;
⋯ 123 unchanged lines
257262 vy: vy * scale,
258263 spin,
259264 };
265+ this.releasedAt = Date.now();
266+ }
267+
268+ /**
269+ * A throw that happened on somebody's phone, arriving over the wire in their
270+ * own frame: `u` along their edge, `vy` into the table. It is stood at their
271+ * side of this screen and turned into the table's coordinates, so the tile
272+ * flies in from off their edge — from where they are sitting, as a thrown
273+ * tile should. The discard it belongs to arrives through the state as usual;
274+ * this only decides how it enters.
275+ */
276+ throwFromNet(seat: SeatId, t: NetThrow) {
277+ const geo = this.geo;
278+ if (!geo) return;
279+ const f = geo.felt;
280+ // Just beyond the felt, where the thrower's strip is.
281+ const m = geo.tile.h * 1.4;
282+ const u = Math.min(1, Math.max(0, t.u));
283+ let x: number;
284+ let y: number;
285+ let vx: number;
286+ let vy: number;
287+ // Each seat's frame: x runs to the player's right along their edge, y into
288+ // the table. The screen rotation for each seat is what `ROT` says it is.
289+ switch (seat) {
290+ case 0:
291+ x = f.x + u * f.w;
292+ y = f.y + f.h + m;
293+ vx = t.vx;
294+ vy = -t.vy;
295+ break;
296+ case 1:
297+ x = f.x + f.w + m;
298+ y = f.y + (1 - u) * f.h;
299+ vx = -t.vy;
300+ vy = -t.vx;
301+ break;
302+ case 2:
303+ x = f.x + (1 - u) * f.w;
304+ y = f.y - m;
305+ vx = -t.vx;
306+ vy = t.vy;
307+ break;
308+ default:
309+ x = f.x - m;
310+ y = f.y + u * f.h;
311+ vx = t.vy;
312+ vy = t.vx;
313+ break;
314+ }
315+ const speed = Math.hypot(vx, vy);
316+ const scale = speed > MAX_THROW_SPEED ? MAX_THROW_SPEED / speed : 1;
317+ this.release = {
318+ seat,
319+ x,
320+ y,
321+ // Facing its thrower, the way a tile lifted from their hand would be.
322+ angle: (SEAT_ROT[seat] * Math.PI) / 180,
323+ w: geo.tile.w,
324+ h: geo.tile.h,
325+ vx: vx * scale,
326+ vy: vy * scale,
327+ spin: t.spin,
328+ };
329+ this.releasedAt = Date.now();
260330 }
261331
262332 // ---- keeping up with the game ----------------------------------------
⋯ 49 unchanged lines
312382 this.entries.set(a.key, rebuilding ? this.settle(geo, state, a) : this.launch(geo, state, a));
313383 }
314384
315- // A throw is good for exactly one discard, and the tile it belonged to has
316- // either just arrived or is never going to.
317- if (added.length || this.release) {
385+ // A throw is good for exactly one discard. At a local table the discard is
386+ // in the state before the next frame, so the tile has either just arrived
387+ // or is never going to; online it is out on a round-trip, so the release
388+ // waits — but not forever, in case the host turned the move down.
389+ if (added.length || (this.release && Date.now() - this.releasedAt > 2500)) {
318390 this.release = null;
319391 this.held = null;
320392 }
⋯ 286 unchanged lines
modifiedsrc/ui/Center.tsx+15 −5
1-import type { Game } from '../game/engine';
1+import type { TableCtl } from '../game/ctl';
22 import type { Prefs } from '../game/prefs';
33 import type { SeatId } from '../game/types';
44 import { labelOf } from '../game/tiles';
⋯ 5 unchanged lines
1010 import { WallRing } from './WallRing';
1111
1212 interface Props {
13- game: Game;
13+ game: TableCtl;
1414 prefs: Prefs;
1515 /** Phone-sized table: the centre becomes a single bar between the seats. */
1616 compact?: boolean;
17+ /** The seat drawn at the bottom of this screen — 0 except online, where the
18+ * table is turned so it is your own. */
19+ viewSeat?: SeatId;
20+ /** Online: the one person reading this screen, whatever the bots are doing. */
21+ preferSeat?: SeatId | null;
1722 onPrefs: (p: Prefs) => void;
1823 onSettings: () => void;
1924 }
2025
21-export function Center({ game, prefs, compact, onPrefs, onSettings }: Props) {
26+export function Center({ game, prefs, compact, viewSeat = 0, preferSeat = null, onPrefs, onSettings }: Props) {
2227 const s = game.state;
2328 const r = s.result;
2429 // Point the live readout at whoever can move the game along.
⋯ 3 unchanged lines
2833 // Against the computer there is only one person reading this, and they are
2934 // sitting at the bottom of the screen.
3035 const human = s.bots.indexOf(false);
31- const facing = human >= 0 && game.hasBots ? (human as SeatId) : game.nextDrawer() ?? s.turn;
36+ const facing =
37+ preferSeat ?? (human >= 0 && game.hasBots ? (human as SeatId) : game.nextDrawer() ?? s.turn);
3238 // Deliberately not per-seat: whoever spots the mis-tap should be able to
3339 // reach it, not just the player who made it.
3440 const undoable = s.phase === 'gameEnd' ? null : game.undoable();
⋯ 15 unchanged lines
5056
5157 <div
5258 className="facing"
53- style={compact ? undefined : { transform: `rotate(${SEAT_ROT[facing]}deg)` }}
59+ style={
60+ compact
61+ ? undefined
62+ : { transform: `rotate(${SEAT_ROT[(((facing - viewSeat + 4) % 4) as SeatId)]}deg)` }
63+ }
5464 >
5565 {/* The tile just thrown is out there in the middle, lit, where everyone
5666 can see it — so saying it again here would only cover the pile. The
⋯ 136 unchanged lines
addedsrc/ui/Controller.tsx+273 −0
1+import { useEffect, useRef, useState } from 'react';
2+import { pendingOptions } from '../game/ctl';
3+import { labelOf, WIND_EN, WIND_NAMES, type Tile } from '../game/tiles';
4+import type { SeatId } from '../game/types';
5+import type { NetSession } from '../net/session';
6+import { seatFromUrl } from '../net/protocol';
7+import { Bi, CLAIM_LABEL } from './Bi';
8+import { Hand, type Throw } from './Hand';
9+import { TileView } from './TileView';
10+
11+const SEATS: SeatId[] = [0, 1, 2, 3];
12+
13+/**
14+ * The phone at the party table: just your own tiles, held in landscape, with
15+ * the common screen doing all the talking. What it keeps of the full table is
16+ * the two things that are yours — the buttons a claim window owes you, and the
17+ * throw. Flick a tile up off the top of the phone and it leaves your hand
18+ * here, crosses the wire, and comes flying in from your edge of the shared
19+ * screen.
20+ */
21+export function Controller({ session }: { session: NetSession }) {
22+ const seat = session.mySeat;
23+
24+ // The QR carries which seat it was stuck to; a phone that arrived some other
25+ // way — or lost the parameter on the way in — picks one by hand below.
26+ const asked = useRef(false);
27+ useEffect(() => {
28+ if (asked.current || seat !== null) return;
29+ const want = seatFromUrl();
30+ if (want !== null && session.seats[want].id === null) {
31+ asked.current = true;
32+ session.claimSeat(want);
33+ }
34+ }, [session, seat]);
35+
36+ if (seat === null) return <SeatPick session={session} />;
37+ return <SeatStrip session={session} seat={seat} />;
38+}
39+
40+function SeatPick({ session }: { session: NetSession }) {
41+ const seats = session.seats;
42+ const s = session.table.state;
43+ return (
44+ <div className="ctl ctl-pick">
45+ <h1>台灣麻將</h1>
46+ <p className="sub">選一個座位 · pick your seat</p>
47+ <div className="pick-grid">
48+ {SEATS.map((i) => {
49+ const taken = seats[i].id !== null;
50+ const wind = (i - s.dealer + 4) % 4;
51+ return (
52+ <button
53+ key={i}
54+ className="btn big-btn"
55+ disabled={taken}
56+ onClick={() => session.claimSeat(i)}
57+ >
58+ {WIND_NAMES[wind]} {WIND_EN[wind]}
59+ <em>{taken ? `已有人 · ${seats[i].name}` : seats[i].name || '空位 open'}</em>
60+ </button>
61+ );
62+ })}
63+ </div>
64+ </div>
65+ );
66+}
67+
68+/** A tile in the fingers, drawn by us once it has left the row. */
69+interface HeldTile {
70+ tile: Tile;
71+ x: number;
72+ y: number;
73+ size: number;
74+ /** Set when it has been let go of: it flies up off the screen and is gone. */
75+ flung: boolean;
76+}
77+
78+function SeatStrip({ session, seat }: { session: NetSession; seat: SeatId }) {
79+ const table = session.table;
80+ const s = table.state;
81+ const p = s.players[seat];
82+ const [sel, setSel] = useState<number | null>(null);
83+ const [held, setHeld] = useState<HeldTile | null>(null);
84+
85+ const myTurn = s.phase === 'turn' && s.turn === seat;
86+ const opts = pendingOptions(s, seat);
87+ const canProceed = table.nextDrawer() === seat;
88+ const canHu = myTurn && table.canSelfDrawWin(seat);
89+ const concealedKongs = myTurn ? table.concealedKongs(seat) : [];
90+ const addedKongs = myTurn ? table.addedKongs(seat) : [];
91+ const tenpai = s.phase !== 'handEnd' && s.phase !== 'gameEnd' ? table.tenpai(seat) : [];
92+ const drawnIndex = myTurn && s.drawnSeparate && s.drawnTile !== null ? p.hand.length - 1 : null;
93+ const selTile = sel === null ? null : p.hand[sel] ?? null;
94+ const seatWind = (seat - s.dealer + 4) % 4;
95+ const r = s.result;
96+
97+ useEffect(() => {
98+ setSel(null);
99+ }, [myTurn, s.handNumber, p.hand.length]);
100+
101+ const doDiscard = (i: number) => {
102+ table.discard(seat, p.hand[i]);
103+ setSel(null);
104+ };
105+
106+ // The flick. `Hand` decides when a drag has become a throw; all that is left
107+ // here is to say it in the seat's own frame — how far along the edge it left
108+ // from, and how fast it was going up and across — and let it go.
109+ const fling = (i: number, t: Throw) => {
110+ const tile = p.hand[i];
111+ if (tile === undefined) return setHeld(null);
112+ table.discard(seat, tile, {
113+ u: t.x / window.innerWidth,
114+ vx: t.vx,
115+ // Up off the top of the phone is into the table.
116+ vy: -t.vy,
117+ spin: t.spin,
118+ });
119+ setSel(null);
120+ setHeld((h) => (h ? { ...h, x: t.x, y: t.y, flung: true } : null));
121+ };
122+
123+ const carry = {
124+ onPickUp: (i: number, x: number, y: number, size: number) =>
125+ setHeld({ tile: p.hand[i], x, y, size, flung: false }),
126+ onMoveTo: (x: number, y: number) => setHeld((h) => (h && !h.flung ? { ...h, x, y } : h)),
127+ onThrow: fling,
128+ onCancel: () => setHeld(null),
129+ };
130+
131+ const status = myTurn
132+ ? { zh: '輪到你', en: 'your turn', hot: true }
133+ : opts
134+ ? { zh: '要叫牌嗎?', en: 'claim it?', hot: true }
135+ : { zh: `等 ${s.players[s.turn].name}`, en: 'waiting', hot: false };
136+
137+ return (
138+ <div className="ctl">
139+ <div className="ctl-top">
140+ <span className="wind">
141+ {WIND_NAMES[seatWind]}
142+ <em>{WIND_EN[seatWind]}</em>
143+ </span>
144+ <span className="pname">{p.name}</span>
145+ {seat === s.dealer && <span className="tag">莊</span>}
146+ {tenpai.length > 0 && <span className="tag ting">聽 {tenpai.map(labelOf).join('·')}</span>}
147+ <span className={`ctl-status ${status.hot ? 'hot' : ''}`}>
148+ {status.zh} <em>{status.en}</em>
149+ </span>
150+ {s.lastDiscard && s.phase !== 'handEnd' && (
151+ <span className="ctl-last">
152+ {s.players[s.lastDiscard.from].name} 打<TileView tile={s.lastDiscard.tile} size="small" />
153+ </span>
154+ )}
155+ <span className="chips">{p.score}</span>
156+ <button className="btn ghost sort" onClick={() => table.sortHand(seat)}>
157+ 理牌
158+ </button>
159+ </div>
160+
161+ <div className="ctl-actions">
162+ {opts?.map((o, i) => (
163+ <button
164+ key={i}
165+ className={`btn claim ${o.type}`}
166+ onClick={() => table.respond(seat, { type: o.type, with: o.with })}
167+ >
168+ <Bi zh={CLAIM_LABEL[o.type][0]} en={CLAIM_LABEL[o.type][1]} sub={o.with && o.with.map(labelOf).join(' ')} />
169+ </button>
170+ ))}
171+ {opts && !canProceed && (
172+ <button className="btn pass" onClick={() => table.respond(seat, 'pass')}>
173+ <Bi zh="過" en="Pass" />
174+ </button>
175+ )}
176+ {canProceed && (
177+ <button className={`btn proceed ${opts ? 'pass' : ''}`} onClick={() => table.resolveNow()}>
178+ <Bi
179+ zh={s.phase === 'robkong' ? '繼續' : opts ? '過.摸牌' : '摸牌'}
180+ en={s.phase === 'robkong' ? 'Continue' : opts ? 'Pass & draw' : 'Draw'}
181+ />
182+ </button>
183+ )}
184+ {canHu && (
185+ <button className="btn claim hu" onClick={() => table.declareSelfDraw(seat)}>
186+ <Bi zh="自摸" en="Self-draw" />
187+ </button>
188+ )}
189+ {concealedKongs.map((t) => (
190+ <button key={`ak${t}`} className="btn claim kong" onClick={() => table.declareConcealedKong(seat, t)}>
191+ <Bi zh="暗槓" en="Kong" sub={labelOf(t)} />
192+ </button>
193+ ))}
194+ {addedKongs.map((t) => (
195+ <button key={`gk${t}`} className="btn claim kong" onClick={() => table.declareAddedKong(seat, t)}>
196+ <Bi zh="加槓" en="Kong" sub={labelOf(t)} />
197+ </button>
198+ ))}
199+ {selTile !== null && sel !== null && myTurn && (
200+ <button className="btn discard" onClick={() => doDiscard(sel)}>
201+ <Bi zh="打出" en="Discard" sub={labelOf(selTile)} />
202+ </button>
203+ )}
204+ {myTurn && <span className="ctl-hint">往上甩出去 · flick a tile up to throw it</span>}
205+ </div>
206+
207+ <div className="ctl-hand">
208+ <Hand
209+ tiles={p.hand}
210+ drawnIndex={drawnIndex}
211+ selected={sel}
212+ canDiscard={myTurn}
213+ onSelect={setSel}
214+ onDiscard={doDiscard}
215+ onMove={(from, to) => table.reorderHand(seat, from, to)}
216+ carry={carry}
217+ />
218+ </div>
219+
220+ {/* The tile out of the row, following the finger — and, once flung, on
221+ its way up off the screen to reappear on the common one. */}
222+ {held && (
223+ <div
224+ className={`ctl-held ${held.flung ? 'flung' : ''}`}
225+ style={{ left: held.x, top: held.y, width: held.size, height: held.size * 1.375 }}
226+ onAnimationEnd={() => setHeld(null)}
227+ >
228+ <TileView tile={held.tile} />
229+ </div>
230+ )}
231+
232+ {r && s.phase === 'handEnd' && (
233+ <div className="ctl-result">
234+ {r.drawGame ? (
235+ <h2>流局 · Draw</h2>
236+ ) : (
237+ r.winners.map((w) => (
238+ <h2 key={w.seat}>
239+ {s.players[w.seat].name} {w.selfDraw ? '自摸' : '胡牌'} · {w.tai} 台
240+ </h2>
241+ ))
242+ )}
243+ <div className="deltas">
244+ {r.deltas.map((d, i) => (
245+ <span key={i} className={d > 0 ? 'up' : d < 0 ? 'down' : ''}>
246+ {s.players[i].name} {d > 0 ? `+${d}` : d}
247+ </span>
248+ ))}
249+ </div>
250+ <button className="btn big-btn" onClick={() => table.nextHand()}>
251+ <Bi zh="下一局" en="Next hand" />
252+ </button>
253+ </div>
254+ )}
255+
256+ {s.phase === 'gameEnd' && (
257+ <div className="ctl-result">
258+ <h2>一將終了 · Game over</h2>
259+ <div className="deltas">
260+ {s.players.map((q, i) => (
261+ <span key={i}>
262+ {q.name} {q.score}
263+ </span>
264+ ))}
265+ </div>
266+ <button className="btn big-btn" onClick={() => table.newGame(s.bots)}>
267+ <Bi zh="再來一將" en="New game" />
268+ </button>
269+ </div>
270+ )}
271+ </div>
272+ );
273+}
addedsrc/ui/OnlineLobby.tsx+83 −0
1+import { useEffect, useRef } from 'react';
2+import type { Prefs } from '../game/prefs';
3+import { WIND_EN, WIND_NAMES } from '../game/tiles';
4+import type { SeatId } from '../game/types';
5+import type { NetSession } from '../net/session';
6+import { Bi } from './Bi';
7+
8+const SEATS: SeatId[] = [0, 1, 2, 3];
9+
10+/**
11+ * The room before the deal: who is here, who is sitting where, and — for the
12+ * host — the button that starts it. Seats nobody takes go to the computer, so
13+ * two friends and two bots is a game, not a wait.
14+ */
15+export function OnlineLobby({ session, prefs }: { session: NetSession; prefs: Prefs }) {
16+ const seats = session.seats;
17+ const me = session.room.me.id;
18+
19+ // Walking in unseated: take the first free chair rather than making everyone
20+ // pick one. Moving is one tap on another seat.
21+ const claimed = useRef(false);
22+ useEffect(() => {
23+ if (claimed.current || session.mySeat !== null) return;
24+ if (seats.some((s) => s.id === null)) {
25+ claimed.current = true;
26+ session.claimSeat(null);
27+ }
28+ }, [session, seats]);
29+
30+ const seated = seats.filter((s) => s.id !== null).length;
31+
32+ return (
33+ <div className="lobby">
34+ <h1>台灣麻將</h1>
35+ <p className="sub">線上對戰 · online game · {session.playerCount} 人在房 in the room</p>
36+ <p className="hint">
37+ 把這頁的連結傳給朋友,他們一開就進同一房。
38+ <br />
39+ Share this page's invite link — anyone who opens it lands at this table.
40+ </p>
41+
42+ <div className="pick-grid">
43+ {SEATS.map((i) => {
44+ const slot = seats[i];
45+ const mine = slot.id === me;
46+ const taken = slot.id !== null && !mine;
47+ return (
48+ <button
49+ key={i}
50+ className={`btn big-btn ${mine ? '' : 'ghost'}`}
51+ disabled={taken}
52+ onClick={() => !mine && session.claimSeat(i)}
53+ >
54+ {WIND_NAMES[i]} {WIND_EN[i]}
55+ <em>
56+ {slot.id === null
57+ ? '空位,開局時由電腦代打 · open — a bot if nobody sits'
58+ : mine
59+ ? `${slot.name || '你'} · you`
60+ : slot.name}
61+ </em>
62+ </button>
63+ );
64+ })}
65+ </div>
66+
67+ <div className="lobby-buttons">
68+ {session.isHost ? (
69+ <button
70+ className="btn big-btn"
71+ disabled={seated === 0}
72+ onClick={() => session.startOnline(prefs.rules)}
73+ >
74+ <Bi zh="開局" en="Deal" sub={seated < 4 ? `${4 - seated} 個電腦 bots fill in` : undefined} />
75+ </button>
76+ ) : (
77+ <p className="hint">等房主開局 · waiting for the host to deal</p>
78+ )}
79+ </div>
80+ {session.err && <p className="hint">{session.err}</p>}
81+ </div>
82+ );
83+}
modifiedsrc/ui/Pool.tsx+23 −6
11 import { useEffect, useRef, useState } from 'react';
2-import type { Game } from '../game/engine';
2+// Imported rather than named by path, so the canvas draws the very files the
3+// CSS tiles use wherever the bundle ends up being served from.
4+import backUrl from '../assets/back.png';
5+import tilesUrl from '../assets/tiles.svg';
6+import type { TableCtl } from '../game/ctl';
37 import { barge, knock } from '../game/sound';
48 import { SPRITE_COL, SPRITE_COLS } from '../game/tiles';
59 import { faceOn, zScale } from '../table/physics';
⋯ 14 unchanged lines
2024 * state changes, a tile is picked up, or the window resizes, so a table nobody
2125 * is touching costs nothing.
2226 */
23-export function Pool({ game, pool }: { game: Game; pool: TablePool }) {
27+export function Pool({
28+ game,
29+ pool,
30+ viewSeat = 0,
31+}: {
32+ game: TableCtl;
33+ pool: TablePool;
34+ /** The seat drawn at the bottom of this screen — see App. An edge the physics
35+ * reports belongs to whoever is drawn there, not to seat 0 by birthright. */
36+ viewSeat?: SeatId;
37+}) {
2438 const canvasRef = useRef<HTMLCanvasElement>(null);
2539 const reachRef = useRef<HTMLDivElement>(null);
2640 const art = useSheet();
2741 const wash = useWash(pool);
2842
2943 // What the loop reads. Refreshed on every render, read on every frame.
30- const live = useRef({ state: game.state, art });
31- live.current = { state: game.state, art };
44+ const live = useRef({ state: game.state, art, viewSeat });
45+ live.current = { state: game.state, art, viewSeat };
3246
3347 // Poked by the effect below, and by the gesture through pool.wake.
3448 const wake = useRef<() => void>(() => {});
⋯ 38 unchanged lines
7387 // A contact quiet enough not to be worth hearing still counts as a tile
7488 // arriving in somebody's lap, which is why the two thresholds differ.
7589 if (i.strength > AUDIBLE) loudest = Math.max(loudest, i.strength);
76- if (i.edge) shove(pool, table, EDGE_SEAT[i.edge], i.seat, i.strength);
90+ if (i.edge) {
91+ const at = ((EDGE_SEAT[i.edge] + live.current.viewSeat) % 4) as SeatId;
92+ shove(pool, table, at, i.seat, i.strength);
93+ }
7794 }
7895 if (loudest > 0) knock(loudest);
7996 paint(ctx, canvas, pool, live.current.art);
⋯ 104 unchanged lines
184201 () => null,
185202 );
186203 };
187- void Promise.all([load('/tiles/tiles.svg'), load('/tiles/back.png')]).then(
204+ void Promise.all([load(tilesUrl), load(backUrl)]).then(
188205 ([faces, back]) => alive && setArt({ faces, back }),
189206 );
190207 return () => {
⋯ 347 unchanged lines
modifiedsrc/ui/Seat.tsx+85 −22
11 import { useEffect, useRef, useState } from 'react';
2-import type { Game } from '../game/engine';
2+import type { TableCtl } from '../game/ctl';
33 import { labelOf, WIND_EN, WIND_NAMES } from '../game/tiles';
44 import type { SeatId } from '../game/types';
55 import type { TablePool } from '../table/pool';
66 import { Bi, CLAIM_LABEL } from './Bi';
77 import { Hand } from './Hand';
8+import { SeatQr } from './SeatQr';
9+import { SEAT_ROT, SEAT_SLOT } from './rotation';
810 import { TileView } from './TileView';
911
10-const ROT: Record<SeatId, number> = { 0: 0, 1: -90, 2: 180, 3: 90 };
11-// seat 0 = bottom (near edge of the laptop), then counter-clockwise on screen:
12-// 1 = right, 2 = top, 3 = left — which is the natural 東南西北 seating order.
13-const SLOT: Record<SeatId, string> = { 0: 'slot-bottom', 1: 'slot-right', 2: 'slot-top', 3: 'slot-left' };
12+/**
13+ * A seat that exists somewhere else — a phone at the party table, or an online
14+ * player. What of it shows on this screen is decided here.
15+ */
16+export interface RemoteSeat {
17+ /** Party screen: the link that claims this seat, drawn as a QR while free. */
18+ qrUrl?: string;
19+ /** Who holds it from their own device, or null while nobody does. */
20+ holder?: string | null;
21+ /** Online host: the seat's player is gone; offer it to the computer. */
22+ onHandToBot?: () => void;
23+}
1424
1525 interface Props {
16- game: Game;
26+ game: TableCtl;
1727 seat: SeatId;
18- /** Phone-sized table: nothing is rotated and only seat 0 gets a full strip. */
28+ /**
29+ * Which edge of this screen the seat is drawn at. At a shared table that is
30+ * the seat itself; online the table is turned so your own seat is at the
31+ * bottom, wherever you are actually sitting.
32+ */
33+ pos?: SeatId;
34+ /**
35+ * Whether this device may act for the seat. The hotseat table says yes to
36+ * everyone, the online table only to your own chair — everything private
37+ * (the tiles' faces, the 聽 and 過水 badges, the buttons) keys off this.
38+ */
39+ local?: boolean;
40+ remote?: RemoteSeat;
41+ /** Phone-sized table: nothing is rotated and only the bottom gets a full strip. */
1942 compact?: boolean;
2043 /** The tiles in the middle. Absent on a phone, which has no middle. */
2144 pool?: TablePool;
2245 onHelp: () => void;
2346 }
2447
25-export function Seat({ game, seat, compact, pool, onHelp }: Props) {
48+export function Seat({ game, seat, pos = seat, local = true, remote, compact, pool, onHelp }: Props) {
2649 const s = game.state;
2750 const p = s.players[seat];
2851 const [sel, setSel] = useState<number | null>(null);
29- // A computer seat has no buttons and no readable hand: its tiles go face down
30- // and its 聽 / 過水 badges stay off, since both would give the hand away.
31- // At the end of a hand everyone turns their tiles over, as they would.
52+ // Held down to re-show a claimed seat's QR — for the player who lost theirs.
53+ const [peek, setPeek] = useState(false);
3254 const bot = s.bots[seat];
33- const concealed = bot && s.phase !== 'handEnd' && s.phase !== 'gameEnd';
34- const myTurn = s.phase === 'turn' && s.turn === seat && !bot;
35- const opts = bot ? undefined : s.options[seat];
55+ // Somebody this device answers for: a person here, not the computer, not a
56+ // hand that lives on another screen.
57+ const acts = local && !bot;
58+ // A hand this device may not read goes face down and its 聽 / 過水 badges
59+ // stay off, since both would give it away. At the end of a hand everyone
60+ // turns their tiles over, as they would.
61+ const concealed = !acts && s.phase !== 'handEnd' && s.phase !== 'gameEnd';
62+ const myTurn = s.phase === 'turn' && s.turn === seat && acts;
63+ const opts = acts ? s.options[seat] : undefined;
3664 const responded = s.responses[seat] !== undefined;
3765 const claiming = (s.phase === 'claims' || s.phase === 'robkong') && !!opts && !responded;
3866 // Whoever draws next closes the claim window — nobody has to wait on a
3967 // player who hasn't noticed their buttons.
40- const canProceed = game.nextDrawer() === seat && !bot;
68+ const canProceed = game.nextDrawer() === seat && acts;
4169
4270 useEffect(() => {
4371 setSel(null);
⋯ 2 unchanged lines
4674 const seatWind = (seat - s.dealer + 4) % 4;
4775 const isDealer = seat === s.dealer;
4876 const live = s.phase !== 'handEnd' && s.phase !== 'lobby';
49- const tenpai = live && !bot ? game.tenpai(seat) : [];
50- const sacred = live && !bot ? game.sacredTiles(seat) : [];
77+ const tenpai = live && acts ? game.tenpai(seat) : [];
78+ const sacred = live && acts ? game.sacredTiles(seat) : [];
5179 const concealedKongs = myTurn ? game.concealedKongs(seat) : [];
5280 const addedKongs = myTurn ? game.addedKongs(seat) : [];
5381 const canHu = myTurn && game.canSelfDrawWin(seat);
5482 const delta = s.phase === 'handEnd' ? s.result?.deltas[seat] ?? 0 : 0;
5583 // 一炮多響 can put more than one seat here, so this is a `some`, not a find.
5684 const won = s.phase === 'handEnd' && !!s.result?.winners.some((w) => w.seat === seat);
85+ // A deserted online seat, waiting for its person to come back — or for the
86+ // host to hand it to the computer so the table can move.
87+ const deserted = !!remote?.onHandToBot && !bot && remote.holder == null && live;
5788
5889 // Compact lays the discards out as one line that scrolls rather than a block
5990 // that wraps, so the newest tile — the only one anybody is looking for — has
⋯ 31 unchanged lines
91122 y,
92123 // Facing the way it did in the hand, so it does not snap round the
93124 // instant it is lifted.
94- angle: (ROT[seat] * Math.PI) / 180,
125+ angle: (SEAT_ROT[pos] * Math.PI) / 180,
95126 w: size,
96127 h: size * 1.375,
97128 });
⋯ 21 unchanged lines
119150
120151 return (
121152 // `data-seat` is how table/geometry.ts finds this seat's hand to work out
122- // where it throws from — see measure().
123- <div className={`slot ${SLOT[seat]} ${seat === 0 ? 'mine' : 'opp'}`} data-seat={seat}>
153+ // where it throws from — see measure(). It carries the seat itself, not the
154+ // edge it is drawn at: the physics is about who threw the tile.
155+ <div className={`slot ${SEAT_SLOT[pos]} ${pos === 0 ? 'mine' : 'opp'}`} data-seat={seat}>
124156 <div
125157 className={`seat ${myTurn ? 'active' : ''} ${claiming ? 'claiming' : ''}`}
126- style={compact ? undefined : { transform: `translate(-50%, -50%) rotate(${ROT[seat]}deg)` }}
158+ style={compact ? undefined : { transform: `translate(-50%, -50%) rotate(${SEAT_ROT[pos]}deg)` }}
127159 >
128160 {/* Points down the strip at whoever won. It is inside the seat, so it
129161 turns with it and lands on their edge of the table wherever they
⋯ 4 unchanged lines
134166 </div>
135167 )}
136168
169+ {/* Party screen: a free seat wears its QR; scanning it takes the hand
170+ to a phone. A claimed seat hides it, but its player can ask for it
171+ back — the 手機 tag below holds it down to reveal. */}
172+ {remote?.qrUrl && (remote.holder == null || peek) && (
173+ <div className="qr-dock">
174+ <SeatQr url={remote.qrUrl} />
175+ <span className="qr-hint">
176+ 掃碼用手機拿牌
177+ <em>scan to play from your phone</em>
178+ </span>
179+ </div>
180+ )}
181+
137182 {/* Only on a phone. At full size a discard is thrown into the middle
138183 and stays there for the hand, so the row would be the same tiles
139184 listed twice. */}
⋯ 65 unchanged lines
205250 {!claiming && !canProceed && responded && s.phase !== 'turn' && (
206251 <div className="waiting">已回應 Responded</div>
207252 )}
253+ {deserted && (
254+ <button className="btn ghost" onClick={remote!.onHandToBot}>
255+ <Bi zh="電腦代打" en="Hand to computer" />
256+ </button>
257+ )}
208258 {myTurn && (
209259 <>
210260 {canHu && (
⋯ 39 unchanged lines
250300 <em>{WIND_EN[seatWind]}</em>
251301 </span>
252302 <span className="pname">{p.name}</span>
303+ {remote?.holder != null && (
304+ <span
305+ className="tag phone"
306+ title="這手牌在手機上 · played from a phone. 按住看 QR · hold to show the QR again"
307+ onPointerDown={() => setPeek(true)}
308+ onPointerUp={() => setPeek(false)}
309+ onPointerCancel={() => setPeek(false)}
310+ onPointerLeave={() => setPeek(false)}
311+ >
312+ 📱 {remote.holder}
313+ </span>
314+ )}
315+ {deserted && <span className="tag sacred">已離線 offline</span>}
253316 {isDealer && (
254317 <span className="tag">
255318 莊 Dealer{s.dealerStreak > 0 ? ` 連${s.dealerStreak}` : ''}
⋯ 9 unchanged lines
265328 </span>
266329 )}
267330 <span className="chips">{p.score}</span>
268- {p.hand.length > 0 && s.phase !== 'handEnd' && !bot && (
331+ {p.hand.length > 0 && s.phase !== 'handEnd' && acts && (
269332 <button className="btn ghost sort" onClick={() => game.sortHand(seat)}>
270333 理牌 Sort
271334 </button>
⋯ 9 unchanged lines
addedsrc/ui/SeatQr.tsx+11 −0
1+import { useMemo } from 'react';
2+import { renderSVG } from 'uqr';
3+
4+/**
5+ * The QR that hands a seat to a phone. Just the code — where it sits and which
6+ * way it faces is its seat's business, and it reads at any angle anyway.
7+ */
8+export function SeatQr({ url }: { url: string }) {
9+ const svg = useMemo(() => renderSVG(url, { border: 1 }), [url]);
10+ return <div className="seat-qr" dangerouslySetInnerHTML={{ __html: svg }} />;
11+}
modifiedvite.config.ts+3 −0
⋯ 3 unchanged lines
44 // https://vite.dev/config/
55 export default defineConfig({
66 plugins: [react()],
7+ // Served from wherever antics puts the bundle, not from a domain root — so
8+ // every built URL has to be relative to the page.
9+ base: './',
710 // portless hands us PORT/HOST; vite doesn't read them on its own
811 server: {
912 port: process.env.PORT ? Number(process.env.PORT) : undefined,
⋯ 3 unchanged lines