examples/example-prompts.md
# Example prompts
Every prompt below works as-is or prefixed with the slash command, e.g. `/create-svg A fox in pixel art`. A bare `/create-svg` asks what to draw and shows the style menu.
What to type, what the router does with it, and what you should get. Run any prompt through the router yourself:
`python3 scripts/route_style.py "<prompt>" --no-brief`
## Single style
| Prompt | Routed to | Notes |
|---|---|---|
| `A fox in pixel art` | pixel-art | grid sprite via `pixel_grid.py`, no anti-aliasing |
| `an 8 bit dragon` | retro-8bit | "8 bit" alias; tighter palette than pixel-art |
| `Ukiyo-e heron by a river` | ukiyo-e | flat blocks, key-block outline, bokashi sky, seigaiha, cartouche |
| `Japanese woodblock print of a crane` | ukiyo-e | natural-language alias |
| `art deco luxury hotel logo` | art-deco (asset: logo) | symmetric, gold gradient, live text with fallback fonts |
| `blueprint of a steam engine` | blueprint | grid paper, dimensions, title block |
| `vintage travel poster for the Alps` | vintage-poster (asset: poster) | limited inks, title band, border |
| `early 2000s glossy internet aesthetic app icon` | y2k + frutiger-aero | shared alias = intentional blend |
## Modifiers (adjust a style, never replace it)
| Prompt | Result |
|---|---|
| `Ukiyo-e but darker: a heron by a river` | ukiyo-e + `darker` - night palette, flat blocks unchanged |
| `Art Nouveau with a modern palette: a peacock` | art-nouveau + `modern-palette` - ornament untouched, contemporary colours |
| `Comic book but realistic: a detective` | comic-book + `realistic` |
| `Make the cyberpunk one more minimal` | routed as a blend: cyberpunk-neon (primary) + minimal-modern (reduction secondary) |
## Blends (primary owns structure, secondary owns mood)
| Prompt | Primary / secondary |
|---|---|
| `cyberpunk + minimalist logo for a coffee brand` | cyberpunk-neon / minimal-modern: dark base, few shapes, one glowing edge, lots of negative space |
| `pixel art with a horror aesthetic, haunted house` | pixel-art / horror: grid intact, horror comes from palette, silhouette, dithered fog |
| `blueprint + sci-fi starship` | blueprint / sci-fi |
## Multi-style (comparable set)
| Prompt | Behaviour |
|---|---|
| `Create a lighthouse in 5 styles` | no styles named -> a contrasting set is chosen and announced; `lighthouse.svg` + `lighthouse-<style>.svg`, then a contact sheet |
| `Draw a fox in pixel art, ukiyo-e, cyberpunk, paper cutout and blueprint` | a list of 3+ styles is multi mode (blends use `+`, `with`, `meets`); exactly those, one shared scene spec (see `outputs/fox-styles.png`) |
## Convert
| Prompt | Behaviour |
|---|---|
| `Convert this SVG to cyberpunk` (attached logo.svg) | transform in place: palette remap, neon strokes, glow, grid underlay; report what was preserved |
| `Turn this PNG into a screen-print poster` | raster is never embedded - viewed, described, redrawn as native vector |
| `Convert my SVG to pixel art` | geometry-dependent style -> rebuilt from a subject spec, not recoloured |
## Cases where the skill asks first
| Prompt | Why |
|---|---|
| `Make me an SVG of a mountain cabin` | no style -> shows `route_style.py --menu` (never picks for a single artwork) |
| `Draw a fox in Japanese style` | ambiguous -> ukiyo-e / sumi-e / manga? |
## Prompts that are declined or redirected
| Prompt | Response |
|---|---|
| `Draw <trademarked character> in manga style` | offers an original character in the style |
| `In the style of <living artist>` | offers the underlying movement/technique instead |
examples/outputs/fox-art-nouveau.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Nouveau Fox</title>
<desc id="d">Art Nouveau: arched double frame, sun-ray halo, whiplash tendrils and lilies growing into the border, muted jewel tones, dark contours.</desc>
<defs>
<symbol id="lily" viewBox="-40 -80 80 90" overflow="visible">
<g stroke="#2d2a24" stroke-width="3" stroke-linejoin="round">
<path d="M0 0C-30-20-34-56-8-78-10-50-6-26 0 0Z" fill="#e8c9b8"/>
<path d="M0 0C30-20 34-56 8-78 10-50 6-26 0 0Z" fill="#e8c9b8"/>
<path d="M0 0C-10-30-6-60 0-80 6-60 10-30 0 0Z" fill="#f3e2d4"/>
</g>
</symbol>
<pattern id="scale" width="28" height="18" patternUnits="userSpaceOnUse">
<rect width="28" height="18" fill="#7f9c7e"/>
<path d="M0 18a14 14 0 0 1 28 0" fill="none" stroke="#5f7d62" stroke-width="2.5"/>
</pattern>
</defs>
<g id="art" stroke-linecap="round">
<rect id="ground" width="1024" height="1024" fill="#efe2c0"/>
<g id="frame" stroke="#2d2a24" stroke-width="6">
<path d="M70 1000V472A442 442 0 0 1 954 472V1000Z" fill="#d9c49a"/>
<path d="M100 880V472A412 412 0 0 1 924 472V880Z" fill="#c9d3b0" stroke-width="4"/>
</g>
<g id="halo">
<circle cx="512" cy="430" r="340" fill="#e7c77d" stroke="#2d2a24" stroke-width="4"/>
<g stroke="#c9a24f" stroke-width="5">
<path d="M512 430L182 430"/>
<path d="M512 430L183.8 395.5"/>
<path d="M512 430L189.2 361.4"/>
<path d="M512 430L198.2 328"/>
<path d="M512 430L210.5 295.8"/>
<path d="M512 430L226.2 265"/>
<path d="M512 430L245 236"/>
<path d="M512 430L266.8 209.2"/>
<path d="M512 430L291.2 184.8"/>
<path d="M512 430L318 163"/>
<path d="M512 430L347 144.2"/>
<path d="M512 430L377.8 128.5"/>
<path d="M512 430L410 116.2"/>
<path d="M512 430L443.4 107.2"/>
<path d="M512 430L477.5 101.8"/>
<path d="M512 430L512 100"/>
<path d="M512 430L546.5 101.8"/>
<path d="M512 430L580.6 107.2"/>
<path d="M512 430L614 116.2"/>
<path d="M512 430L646.2 128.5"/>
<path d="M512 430L677 144.2"/>
<path d="M512 430L706 163"/>
<path d="M512 430L732.8 184.8"/>
<path d="M512 430L757.2 209.2"/>
<path d="M512 430L779 236"/>
<path d="M512 430L797.8 265"/>
<path d="M512 430L813.5 295.8"/>
<path d="M512 430L825.8 328"/>
<path d="M512 430L834.8 361.4"/>
<path d="M512 430L840.2 395.5"/>
<path d="M512 430L842 430"/>
</g>
<g fill="#b5543c" stroke="#2d2a24" stroke-width="2">
<circle cx="160" cy="430" r="7"/>
<circle cx="165.3" cy="368.9" r="7"/>
<circle cx="181.2" cy="309.6" r="7"/>
<circle cx="207.2" cy="254" r="7"/>
<circle cx="242.4" cy="203.7" r="7"/>
<circle cx="285.7" cy="160.4" r="7"/>
<circle cx="336" cy="125.2" r="7"/>
<circle cx="391.6" cy="99.2" r="7"/>
<circle cx="450.9" cy="83.3" r="7"/>
<circle cx="512" cy="78" r="7"/>
<circle cx="573.1" cy="83.3" r="7"/>
<circle cx="632.4" cy="99.2" r="7"/>
<circle cx="688" cy="125.2" r="7"/>
<circle cx="738.3" cy="160.4" r="7"/>
<circle cx="781.6" cy="203.7" r="7"/>
<circle cx="816.8" cy="254" r="7"/>
<circle cx="842.8" cy="309.6" r="7"/>
<circle cx="858.7" cy="368.9" r="7"/>
<circle cx="864" cy="430" r="7"/>
</g>
<circle cx="512" cy="430" r="200" fill="#f0dca0" stroke="#2d2a24" stroke-width="3"/>
</g>
<path id="hill" d="M100 880V800C300 760 420 820 560 820S820 780 924 800V880Z" fill="url(#scale)" stroke="#2d2a24" stroke-width="4"/>
<g id="tendrils" fill="none" stroke="#2d2a24" stroke-linecap="round">
<path d="M100 880C150 760 60 640 150 540 220 460 160 380 230 330 270 300 300 320 290 350 282 372 256 366 262 346" stroke-width="10"/>
<path d="M100 880C150 760 60 640 150 540 220 460 160 380 230 330 270 300 300 320 290 350 282 372 256 366 262 346" stroke="#7f9c7e" stroke-width="5"/>
<path d="M924 880C880 780 960 660 880 560 820 480 880 400 820 350 786 322 752 338 762 364 770 384 796 378 790 358" stroke-width="10"/>
<path d="M924 880C880 780 960 660 880 560 820 480 880 400 820 350 786 322 752 338 762 364 770 384 796 378 790 358" stroke="#7f9c7e" stroke-width="5"/>
<path d="M150 540C110 520 96 480 120 456" stroke-width="6"/>
<path d="M880 560C920 540 934 500 910 474" stroke-width="6"/>
</g>
<g id="leaves" fill="#7f9c7e" stroke="#2d2a24" stroke-width="3">
<path d="M150 620C110 600 96 560 104 540 130 556 150 590 150 620Z"/>
<path d="M880 640C920 620 934 580 926 560 900 576 880 610 880 640Z"/>
<path d="M200 460C230 430 270 430 290 440 266 462 230 470 200 460Z"/>
</g>
<g id="lilies">
<use href="#lily" x="124" y="470" width="80" height="90" transform="rotate(-20 164 520)"/>
<use href="#lily" x="820" y="490" width="80" height="90" transform="rotate(18 860 540)"/>
<use href="#lily" x="60" y="720" width="70" height="80" transform="rotate(-8 95 760)"/>
</g>
<g id="fox" stroke="#2d2a24" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#b5543c"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#f3e2d4"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#c96a3f"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#b5543c"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#4a3a30"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#f3e2d4"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#3a2e26"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#c96a3f"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#f3e2d4"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#f3e2d4"/>
<path d="M352 300L358 248 392 322Z" fill="#4a3a30"/>
<path d="M452 306L468 262 478 342Z" fill="#4a3a30"/>
</g>
<g id="fur" fill="none" stroke="#2d2a24" stroke-width="3.5" stroke-linecap="round">
<path d="M560 540C600 560 620 600 610 640"/>
<path d="M640 600C680 630 690 670 676 710"/>
<path d="M800 540C840 600 850 660 830 720"/>
<path d="M860 600C880 650 880 700 860 740"/>
</g>
<g id="face">
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#2d2a24"/>
<circle cx="236" cy="488" r="10" fill="#2d2a24"/>
</g>
<g id="title-panel">
<path d="M70 1000V900H954V1000Z" fill="#2f4a45" stroke="#2d2a24" stroke-width="6"/>
<path d="M399.1 969.1Q398.7 969.1 398.1 968.8 397.6 968.4 397.3 967.3L380 897.6Q379.2 894.5 378.3 893.3 377.3 892.1 375.1 891.7L373.5 891.3Q371.9 891 371.9 889.9 371.9 888.8 373.6 888.8H392.4Q393.9 888.8 393.9 890 393.9 891.2 392.1 891.3L390 891.5Q388.1 891.7 387.6 892.7 387.1 893.8 387.7 896.2L400.4 949.1Q400.6 949.7 401.1 949.7 401.6 949.7 401.7 949.1L414.4 898.5Q415.9 892.2 411.1 891.5L409.4 891.3Q407.6 891 407.6 890 407.6 888.8 409.1 888.8H424.5Q426.2 888.8 426.2 889.9 426.2 891 424.6 891.3L423 891.7Q420.9 892.1 419.7 893.8 418.6 895.5 417.8 898.6L400.7 967.3Q400.5 968.4 400 968.8 399.5 969.1 399.1 969.1ZM449 969Q446.2 969 443.7 967.8 441.2 966.6 439.5 963.9 437.9 961.3 437.9 956.9V918.6Q437.9 916.6 437.3 915.9 436.7 915.1 435.1 915L433.3 914.8Q431.4 914.5 431.4 913.3 431.4 911.9 433.5 911.9H443.7Q445.4 911.9 445.4 913.5V954.6Q445.4 959.6 447.3 961.8 449.2 963.9 452.6 963.9 456 963.9 458.7 962 461.3 960.1 462.9 956.8 464.5 953.6 464.5 949.7V918.6Q464.5 916.6 463.9 915.9 463.3 915.1 461.7 915L459.9 914.8Q458 914.5 458 913.3 458 911.9 460.1 911.9H470.3Q472 911.9 472 913.5V956.2Q472 958.2 472.6 958.9 473.2 959.6 474.7 959.9L477 960.2Q478.5 960.3 478.5 961.3 478.5 962.3 476.7 962.6 473.3 963 471.3 964.4 469.2 965.7 467.7 967.6 466.7 968.5 466 968.5 465 968.5 465 967.2V962.2Q465 961.3 464.4 961.1 463.8 961 463.2 961.6 459.1 965.7 456 967.3 452.8 969 449 969ZM488.3 968Q487 968 487 966.8 487 965.7 488.2 965.5L489.7 965.2Q492.1 964.9 493.2 963.9 494.3 962.9 494.3 960.6V897.6Q494.3 895.6 493.7 894.9 493 894.1 491.5 894L489 893.6Q487.8 893.4 487.8 892.3 487.8 891.2 489 891 492.1 890.3 494.3 889.5 496.5 888.6 497.9 887.7 499.6 886.6 500.5 886.6 501.7 886.6 501.7 888.4V960.6Q501.7 962.9 502.5 963.9 503.2 964.8 505.5 965.1L508 965.5Q509.2 965.7 509.2 966.8 509.2 968 507.9 968ZM519.3 990.5Q517.6 990.5 517.6 989.3 517.6 988.1 519.1 988L520.6 987.8Q522.9 987.5 523.6 986.5 524.3 985.5 524.3 983.2V923.7Q524.3 921.7 523.7 921 523.1 920.3 521.6 920L519.3 919.7Q517.9 919.6 517.9 918.5 517.9 917.6 519.6 917.3 523 916.7 524.8 915.5 526.7 914.3 528.6 912.6 529.6 911.6 530.3 911.6 531.3 911.6 531.3 912.9V916.3Q531.3 917.1 531.9 917.3 532.6 917.6 533.5 916.7L534.7 915.5Q536.9 913.3 539.2 912.3 541.5 911.2 544.5 911.2 549.5 911.2 553.4 914.6 557.3 918 559.6 923.9 561.9 929.8 561.9 937.9 561.9 947 559 954 556.1 961.1 551.1 965 546.1 969 539.6 969 536.3 969 533.8 967.8 531.8 966.9 531.8 969V983.2Q531.8 985.5 532.5 986.3 533.3 987.1 535.6 987.5L539.6 988Q540.8 988.2 540.8 989.2 540.8 990.5 539.2 990.5ZM540 966.2Q546.1 966.2 549.9 959 553.7 951.7 553.7 939.4 553.7 928.1 550.4 922.1 547.1 916.2 541.1 916.2 537.3 916.2 534.6 919.3 531.8 922.4 531.8 927.9V955.6Q531.8 960.5 534.1 963.4 536.3 966.2 540 966.2ZM592.3 969Q587 969 582.8 965.5 578.6 962 576.2 955.5 573.7 949.1 573.7 940.6 573.7 931.9 576.2 925.3 578.8 918.7 583.1 915 587.5 911.2 592.9 911.2 599.4 911.2 603.4 916.6 607.5 921.9 607.5 933.9 607.5 937.5 604.7 937.5H583.8Q581.6 937.5 581.6 940.1 581.6 951.7 585 957.5 588.4 963.4 593.7 963.4 597.9 963.4 600.5 960.4 603.2 957.4 604.8 950.4 605.1 949.2 606.2 949.2 607.6 949.2 607.2 951.4 605.5 961.2 601.6 965.1 597.8 969 592.3 969ZM583.5 934.8H594.6Q599.9 934.8 599.9 929.3 599.9 922.1 598.1 918.1 596.4 914 592.8 914 588.5 914 585.6 918.9 582.7 923.9 581.8 933 581.6 934.8 583.5 934.8ZM630.6 969Q628.1 969 625.2 968.4 622.4 967.8 620.6 966.9 619.6 966.4 619.6 965L619.3 952.7Q619.3 950.8 620.7 950.8 621.9 950.8 622.4 952.6 624.2 960.4 626.9 963.3 629.6 966.1 633.5 966.1 636.7 966.1 638.8 963.7 640.8 961.2 640.8 956.8 640.8 952.4 638.4 949 636 945.6 630.3 941.5 624.6 937.6 621.9 933.7 619.3 929.8 619.3 925.2 619.3 918.9 623.7 915.1 628.1 911.2 635.3 911.2 637.5 911.2 639.6 911.7 641.7 912.2 643.4 913.3 644.4 914 644.4 915.1L644.6 927.2Q644.6 928.8 643.3 928.8 642.5 928.8 642.2 928.3 641.8 927.9 641.6 927.1 639.4 919.9 637.2 917.1 635 914.3 631.8 914.3 629 914.3 627.1 916.3 625.2 918.3 625.2 921.2 625.2 925.1 627.5 928 629.8 930.8 635.9 935 641.4 939 644.2 943.5 647 948.1 647 953.5 647 960.5 642.5 964.8 638 969 630.6 969Z" fill="#e7c77d"/>
<path d="M150 950C200 930 240 960 290 944M734 944C784 960 824 930 874 950" fill="none" stroke="#e7c77d" stroke-width="4" stroke-linecap="round"/>
</g>
<rect id="outer" x="24" y="24" width="976" height="976" fill="none" stroke="#2d2a24" stroke-width="8"/>
</g>
</svg>
examples/outputs/fox-bauhaus.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Bauhaus Fox</title>
<desc id="d">Bauhaus: fox built only from circles, quarter-circles, triangles and bars on a 64-unit grid in red, yellow, blue and black.</desc>
<rect id="ground" width="1024" height="1024" fill="#f1eadb"/>
<g id="composition">
<circle id="sun" cx="768" cy="256" r="160" fill="#f7b500"/>
<rect id="bar" y="852" width="1024" height="172" fill="#111111"/>
<path id="tail" d="M640 852A176 176 0 0 1 992 852Z" fill="#1d4e9f"/>
<path id="tail-tip" d="M864 852A64 64 0 0 1 992 852Z" fill="#f1eadb"/>
<path id="body" d="M416 852V532A320 320 0 0 1 736 852Z" fill="#d62828"/>
<rect id="chest" x="416" y="576" width="64" height="276" fill="#f1eadb"/>
<rect id="leg" x="480" y="704" width="64" height="148" fill="#111111"/>
<path id="head" d="M224 480L512 288 512 576Z" fill="#d62828"/>
<path id="muzzle" d="M224 480L384 531.2 512 576Z" fill="#f1eadb"/>
<path id="ear-l" d="M352 364L384 160 448 318Z" fill="#111111"/>
<path id="ear-r" d="M448 320L544 160 512 352Z" fill="#111111"/>
<circle id="eye" cx="384" cy="416" r="16" fill="#111111"/>
<circle id="nose" cx="224" cy="480" r="14" fill="#111111"/>
<rect id="rule" x="920" y="96" width="16" height="560" fill="#1d4e9f"/>
<g id="type" fill="#111111">
<path d="M15.3-38.7H62.3V-21.9H15.3ZM64-64.5V-47.7H16.3L25.6-56.9V0H5.6V-64.5ZM97.9-29.2Q97.9-25.2 99.4-22.4 100.9-19.5 103.8-17.9 106.7-16.3 111.1-16.3 115.6-16.3 118.6-17.9 121.5-19.4 122.9-22.3 124.3-25.2 124.3-29.2V-64.5H144.4V-28.1Q144.4-19.3 140.2-12.6 136.1-5.9 128.6-2.2 121.2 1.5 111.1 1.5 101.2 1.5 93.7-2.2 86.2-5.9 82.1-12.6 77.9-19.3 77.9-28.1V-64.5H97.9ZM233.7-27.4Q232.7-18.8 227.7-12.3 222.8-5.8 214.7-2.1 206.5 1.5 196 1.5 184.7 1.5 176.2-2.8 167.7-7 162.9-14.5 158.2-22.1 158.2-32.2 158.2-42.4 162.9-50 167.7-57.5 176.2-61.7 184.7-66 196-66 206.5-66 214.7-62.3 222.8-58.7 227.7-52.2 232.7-45.8 233.7-37.1H213.6Q212.7-40.8 210.4-43.4 208.2-46 204.6-47.4 200.9-48.8 196-48.8 190.5-48.8 186.6-46.9 182.6-44.9 180.5-41.2 178.3-37.4 178.3-32.2 178.3-27.1 180.5-23.4 182.6-19.7 186.6-17.7 190.5-15.7 196-15.7 200.9-15.7 204.6-17 208.2-18.3 210.4-21 212.7-23.6 213.6-27.4ZM247.8-64.5H267.8V0H247.8ZM298-64.5H318.1V0H298ZM259.1-41.7H306.1V-24H259.1ZM332.7-21.2H353Q353.4-18.9 355.2-17.2 357.1-15.6 360.3-14.7 363.4-13.8 367.7-13.8 373.7-13.8 377-15.1 380.3-16.5 380.3-19.3 380.3-21.3 378.4-22.6 376.5-23.8 370.9-24.3L359.6-25.2Q345.6-26.2 339.4-31.3 333.2-36.3 333.2-44.8 333.2-51.8 337.2-56.5 341.3-61.1 348.5-63.6 355.7-66 365.5-66 375-66 382.3-63.3 389.7-60.6 394-55.8 398.4-50.9 398.8-44.5H378.5Q378.1-46.4 376.6-47.8 375-49.2 372-50 369.1-50.7 365.1-50.7 359.7-50.7 356.6-49.4 353.5-48.2 353.5-45.6 353.5-43.7 355.3-42.5 357.2-41.3 362.2-40.8L374.6-39.8Q383.9-39.1 389.6-36.9 395.3-34.7 397.9-31 400.5-27.3 400.5-21.6 400.5-14.7 396.4-9.5 392.3-4.3 384.8-1.4 377.4 1.5 367.4 1.5 357.2 1.5 349.4-1.4 341.7-4.2 337.3-9.3 332.9-14.4 332.7-21.2Z" transform="translate(944 640) rotate(-90)"/>
<path d="M18.7-30V0H9.5V-26.7L13.7-22.8 .6-20.5V-27.6L11.9-30ZM42 .7Q38 .7 34.9-.5 31.8-1.8 29.9-3.8 28-5.9 27.6-8.5H36.1Q36.6-7.4 38-6.7 39.4-6 41.7-6 44.5-6 46.3-7 48.1-8 49-9.9 49.9-11.8 49.9-14.6 49.9-17.8 48.9-19.8 48-21.9 46.1-23 44.2-24 41.6-24 39.9-24 38.7-23.5 37.5-23 36.9-22.3 36.3-21.5 36.3-20.5 36.3-19.4 36.9-18.6 37.5-17.9 38.6-17.5 39.8-17 41.5-17 43.8-17 45.9-18 48-18.9 49.8-20.9L52.9-18.9Q51.7-16.4 49.8-14.5 47.8-12.6 45.2-11.5 42.5-10.4 39.1-10.4 35.3-10.4 32.6-11.6 29.9-12.9 28.4-15.1 27-17.3 27-20.2 27-23.2 28.7-25.6 30.4-28 33.7-29.3 37-30.7 41.8-30.7 47.4-30.7 51.1-28.7 54.8-26.7 56.7-23.1 58.6-19.6 58.5-15 58.6-10.5 56.7-6.9 54.8-3.4 51.2-1.3 47.5 .7 42 .7ZM65.4-19Q65.6-22.6 67.8-25.3 69.9-27.9 73.5-29.3 77-30.7 81.5-30.7 85.7-30.7 88.9-29.4 92.1-28.1 93.8-25.9 95.6-23.6 95.6-20.6 95.6-18.2 94.3-16.2 93-14.2 90.3-12.3 87.6-10.4 83.2-8.5L75.6-5 75.4-7.3H96.4V0H66.1V-6.4L79-13.5Q82-15.1 83.5-16.1 85.1-17.2 85.7-18.1 86.4-19 86.4-20 86.4-21.1 85.8-21.9 85.2-22.7 84-23.2 82.8-23.6 81.1-23.6 78.9-23.6 77.5-23 76.2-22.4 75.5-21.3 74.8-20.3 74.6-19ZM107-30H131.1V-23.2H109.8L113.4-24.7 111.8-14.5H109.6Q110.4-16 111.9-17.3 113.4-18.5 115.7-19.3 117.9-20 120.9-20 124.7-20 127.5-18.8 130.3-17.5 131.9-15.3 133.4-13.1 133.4-10.2 133.4-7 131.6-4.6 129.9-2.1 126.5-.7 123.2 .7 118.5 .7 114 .7 110.6-.6 107.2-2 105.3-4.2 103.4-6.5 103.2-9.2H112.4Q112.8-8 114.3-7.2 115.9-6.4 118.5-6.4 120.3-6.4 121.5-6.9 122.8-7.3 123.5-8.2 124.2-9 124.2-10.2 124.2-11.4 123.5-12.2 122.9-13.1 121.6-13.6 120.4-14.1 118.6-14.1 117.2-14.1 116-13.8 114.8-13.5 113.9-13 113-12.5 112.4-11.9H104.3Z" transform="translate(96 976)"/>
</g>
</g>
</svg>
examples/outputs/fox-blueprint.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Fox Drawing Sheet</title>
<desc id="d">Blueprint: white linework on cyan-blue grid paper, construction circles, centre lines, dimensions and a title block.</desc>
<defs>
<pattern id="grid-s" width="16" height="16" patternUnits="userSpaceOnUse">
<path d="M16 0H0V16" fill="none" stroke="#5f9be0" stroke-width="1" opacity="0.35"/>
</pattern>
<pattern id="grid-l" width="80" height="80" patternUnits="userSpaceOnUse">
<path d="M80 0H0V80" fill="none" stroke="#7fb4ee" stroke-width="1.5" opacity="0.5"/>
</pattern>
<pattern id="hatch" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<path d="M0 0V10" stroke="#e8f1ff" stroke-width="1.2" opacity="0.55"/>
</pattern>
<marker id="arr" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M0 1L10 5 0 9z" fill="#e8f1ff"/>
</marker>
</defs>
<rect id="paper" width="1024" height="1024" fill="#0f3a6d"/>
<rect width="1024" height="1024" fill="url(#grid-s)"/>
<rect width="1024" height="1024" fill="url(#grid-l)"/>
<rect id="border" x="30" y="30" width="964" height="964" fill="none" stroke="#e8f1ff" stroke-width="4"/>
<g id="construction" fill="none" stroke="#9cc8ff" stroke-width="1.5" stroke-dasharray="14 6 3 6">
<path d="M370 150V620M180 400H600"/>
<circle cx="370" cy="400" r="180"/>
<circle cx="370" cy="400" r="120"/>
<path d="M60 840H990"/>
<path d="M700 640L980 640"/>
</g>
<g id="fox" fill="none" stroke="#e8f1ff" stroke-width="3" stroke-linejoin="round" stroke-linecap="round">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" stroke-width="4"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="url(#hatch)"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" stroke-width="4"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="url(#hatch)"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="url(#hatch)"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" stroke-width="4"/>
<path d="M490 520L536 516 510 548 468 562Z"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
<path d="M352 300L358 248 392 322Z" fill="url(#hatch)"/>
<path d="M452 306L468 262 478 342Z" fill="url(#hatch)"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z"/>
<circle cx="236" cy="488" r="10" fill="#e8f1ff"/>
</g>
<g id="dimensions" fill="none" stroke="#e8f1ff" stroke-width="1.5" font-family="'Courier New',monospace" font-size="20">
<path d="M232 130V236M984 470V500M232 180H352M352 180H492"/>
<path d="M232 180H492" marker-start="url(#arr)" marker-end="url(#arr)"/>
<path d="M60 222V840" marker-start="url(#arr)" marker-end="url(#arr)"/>
<path d="M60 222H340M60 840H300" stroke-dasharray="5 4"/>
<path d="M232 880H962" marker-start="url(#arr)" marker-end="url(#arr)"/>
<path d="M232 850V890M962 850V890"/>
<path d="M440 100L400 150"/>
<text x="446" y="98" fill="#e8f1ff" stroke="none">A: EAR TIP</text>
<text x="264" y="170" fill="#e8f1ff" stroke="none">260</text>
<text x="70" y="540" fill="#e8f1ff" stroke="none" transform="rotate(-90 70 540)">618</text>
<text x="560" y="912" fill="#e8f1ff" stroke="none">730</text>
<path d="M760 400H930L904 476"/>
<text x="760" y="390" fill="#e8f1ff" stroke="none">B: BRUSH TIP</text>
</g>
<g id="title-block" fill="none" stroke="#e8f1ff" stroke-width="2">
<rect x="560" y="920" width="410" height="54"/>
<path d="M770 920V974"/>
<text x="572" y="944" fill="#e8f1ff" stroke="none" font-family="'Courier New',monospace" font-size="18">VULPES VULPES</text>
<text x="572" y="965" fill="#e8f1ff" stroke="none" font-family="'Courier New',monospace" font-size="14">SHEET 1/1 SCALE 1:8</text>
<text x="784" y="944" fill="#e8f1ff" stroke="none" font-family="'Courier New',monospace" font-size="14">DWG NO. FX-001</text>
<text x="784" y="965" fill="#e8f1ff" stroke="none" font-family="'Courier New',monospace" font-size="14">REV A</text>
</g>
</svg>
examples/outputs/fox-comic-book.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Comic Fox</title>
<desc id="d">Comic book: action burst, speed lines, halftone shading, thick-to-thin black inks, caption box and outlined lettering.</desc>
<defs>
<clipPath id="panel">
<rect width="1024" height="1024"/>
</clipPath>
<pattern id="ht-dark" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="12" height="12" fill="#e63946"/>
<circle cx="6" cy="6" r="3.4" fill="#9e1b2a"/>
</pattern>
<pattern id="ht-bg" width="18" height="18" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="18" height="18" fill="#ffd60a"/>
<circle cx="9" cy="9" r="4" fill="#ff9f1c"/>
</pattern>
<pattern id="ht-sky" width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="16" height="16" fill="#00b4d8"/>
<circle cx="8" cy="8" r="3" fill="#0077b6"/>
</pattern>
</defs>
<rect id="panel-bg" width="1024" height="1024" fill="url(#ht-sky)"/>
<polygon id="burst" clip-path="url(#panel)" points="1100,470 863,552 1058,709 807,705 936,920 702,830 750,1076 561,912 522,1159 400,940 278,1159 239,912 50,1076 98,830 -136,920 -7,705 -258,709 -63,552 -300,470 -63,388 -258,231 -7,235 -136,20 98,110 50,-136 239,28 278,-219 400,0 522,-219 561,28 750,-136 702,110 936,20 807,235 1058,231 863,388" fill="url(#ht-bg)" stroke="#111" stroke-width="6"/>
<g id="speed-lines" clip-path="url(#panel)" stroke="#fff8e1" stroke-width="5" opacity="0.75">
<path d="M729 487L1298 517"/>
<path d="M722 538L1280 657"/>
<path d="M708 588L1240 792"/>
<path d="M685 635L1179 920"/>
<path d="M656 677L1099 1036"/>
<path d="M620 715L1002 1138"/>
<path d="M579 746L890 1224"/>
<path d="M534 771L766 1292"/>
<path d="M485 788L632 1339"/>
<path d="M434 798L494 1365"/>
<path d="M382 799L352 1368"/>
<path d="M331 792L212 1350"/>
<path d="M281 778L77 1310"/>
<path d="M235 755L-49 1249"/>
<path d="M192 726L-166 1169"/>
<path d="M154 690L-268 1072"/>
<path d="M123 649L-354 960"/>
<path d="M98 604L-422 836"/>
<path d="M81 555L-469 702"/>
<path d="M71 504L-495 564"/>
<path d="M70 452L-498 422"/>
<path d="M77 401L-480 282"/>
<path d="M91 351L-440 147"/>
<path d="M114 305L-379 19"/>
<path d="M143 262L-299-96"/>
<path d="M179 224L-202-198"/>
<path d="M220 193L-90-284"/>
<path d="M265 168L33-352"/>
<path d="M314 151L167-399"/>
<path d="M365 141L305-425"/>
<path d="M417 140L447-428"/>
<path d="M468 147L587-410"/>
<path d="M518 161L722-370"/>
<path d="M565 184L850-309"/>
<path d="M607 213L966-229"/>
<path d="M645 249L1068-132"/>
<path d="M676 290L1154-20"/>
<path d="M701 335L1222 103"/>
<path d="M718 384L1269 237"/>
<path d="M728 435L1295 375"/>
</g>
<rect id="ground" y="846" width="1024" height="178" fill="#2a9d8f" stroke="#111" stroke-width="8" clip-path="url(#panel)"/>
<g id="fox-color">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#e63946"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#f77f00"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="url(#ht-dark)"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#fff8e1"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#3d1f14"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#fff8e1"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#2a1510"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#f77f00"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#fff8e1"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#fff8e1"/>
<path d="M352 300L358 248 392 322Z" fill="#111"/>
<path d="M452 306L468 262 478 342Z" fill="#111"/>
</g>
<g id="ink" fill="none" stroke="#111" stroke-linejoin="round" stroke-linecap="round">
<g stroke-width="13">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
</g>
<g stroke-width="6">
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z"/>
</g>
<path d="M560 600q30 20 44 52M620 560q22 18 30 40M820 600q20 30 20 70" stroke-width="5"/>
</g>
<g id="face">
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#111"/>
<circle cx="236" cy="488" r="12" fill="#111"/>
<circle cx="344" cy="446" r="4" fill="#fff"/>
</g>
<g id="balloon">
<path d="M560 150C560 60 920 60 920 150 920 240 700 250 640 236L560 300 590 222C570 205 560 180 560 150Z" fill="#fff" stroke="#111" stroke-width="7" stroke-linejoin="round"/>
<path d="M652.3 260V217.6L631.7 131.1H657.2L664.4 174.7 671.6 131.1H697.1L676.6 217.6V260ZM706.4 260V131.1H731.3V260ZM745.6 260V131.1H779.6Q790.3 131.1 796.6 135.9 802.9 140.6 805.6 149.4 808.3 158.2 808.3 170.4 808.3 182.1 806 190.7 803.7 199.4 797.9 204.2 792.1 209 781.4 209H770.9V260ZM770.9 185.8H772.4Q779.6 185.8 781.2 181.8 782.8 177.7 782.8 169.9 782.8 162.6 781.2 158.6 779.6 154.6 773.8 154.6H770.9ZM823.9 224.9L818.7 131.1H845L839.8 224.9ZM818.7 259.9V238.1H845V259.9Z" fill="#e63946" stroke="#111" stroke-width="6" paint-order="stroke" stroke-linejoin="round" transform="rotate(-4 740 200)"/>
</g>
<g id="caption">
<rect x="60" y="64" width="468" height="66" fill="#ffd60a" stroke="#111" stroke-width="6"/>
<path d="M79.4 112V81.1H88.8L91.4 99.9 94 81.1H103.5V112H97.8V89.7L94.3 112H88.7L85 89.7V112ZM108.2 112V81.1H120.6V87H114.5V93.1H120.4V98.9H114.5V106H121V112ZM124.2 112L127.2 81.1H137.7L140.6 112H134.8L134.3 107H130.6L130.2 112ZM131 102.1H133.9L132.5 86.3H132.2ZM144.5 112V81.1H150.9L153.7 95.9V81.1H159.7V112H153.7L150.6 96.5V112ZM166.9 112L163.6 81.1H169.6L171.3 100.6 173 81.1H178.8L180.5 100.6 182.1 81.1H188.2L184.9 112H177.4L175.9 97.1 174.4 112ZM192.1 112V81.1H198.2V92.1H201.2V81.1H207.3V112H201.2V98H198.2V112ZM211.8 112V81.1H217.7V112ZM222.2 112V81.1H228.4V106.7H234.8V112ZM238.5 112V81.1H250.9V87H244.8V93.1H250.6V98.9H244.8V106H251.3V112ZM256 116.1L257.2 112H254.9V106.7H261.5V111.8L259.7 116.1ZM276 112V81.1H282V112ZM286.4 112V81.1H292.8L295.6 95.9V81.1H301.6V112H295.6L292.5 96.5V112ZM319.5 112V87H315.8V81.1H329.3V87H325.6V112ZM333 112V81.1H339.2V92.1H342.1V81.1H348.3V112H342.1V98H339.2V112ZM353 112V81.1H365.4V87H359.3V93.1H365.1V98.9H359.3V106H365.8V112ZM382.7 112L379.4 81.1H385.4L387.1 100.6 388.9 81.1H394.6L396.3 100.6 397.9 81.1H404L400.7 112H393.2L391.7 97.1 390.2 112ZM415.3 112.3Q411.6 112.3 409.6 110 407.6 107.8 407.6 103.6V88.9Q407.6 84.9 409.6 82.8 411.5 80.8 415.3 80.8 419 80.8 421 82.8 422.9 84.9 422.9 88.9V103.6Q422.9 107.8 420.9 110 419 112.3 415.3 112.3ZM415.3 106.6Q416.7 106.6 416.7 103.9V88.8Q416.7 86.5 415.4 86.5 413.8 86.5 413.8 88.8V103.9Q413.8 105.3 414.2 106 414.5 106.6 415.3 106.6ZM434.8 112.3Q431.1 112.3 429.1 110 427.1 107.8 427.1 103.6V88.9Q427.1 84.9 429.1 82.8 431 80.8 434.8 80.8 438.5 80.8 440.5 82.8 442.4 84.9 442.4 88.9V103.6Q442.4 107.8 440.4 110 438.5 112.3 434.8 112.3ZM434.8 106.6Q436.2 106.6 436.2 103.9V88.8Q436.2 86.5 434.9 86.5 433.3 86.5 433.3 88.8V103.9Q433.3 105.3 433.7 106 434 106.6 434.8 106.6ZM446.9 112V81.1H455.4Q458.7 81.1 460.4 82.9 462.1 84.8 462.1 88.3V102.8Q462.1 107.2 460.6 109.6 459 112 455.2 112ZM453.1 106.5H454.2Q455.9 106.5 455.9 104.9V88.9Q455.9 87.4 455.5 86.9 455.1 86.5 453.8 86.5H453.1ZM474 112.3Q469.8 112.3 467.9 110.2 466 108.1 466 103.5V100.4H472.1V104.3Q472.1 105.4 472.5 106 472.8 106.6 473.6 106.6 474.4 106.6 474.8 106.1 475.1 105.6 475.1 104.5 475.1 103.1 474.8 102.1 474.5 101.1 473.8 100.3 473.1 99.4 471.9 98.2L469.1 95.6Q466 92.7 466 88.9 466 84.9 467.9 82.9 469.7 80.8 473.2 80.8 477.4 80.8 479.2 83 481 85.3 481 89.9H474.7V87.8Q474.7 87.2 474.3 86.8 474 86.5 473.4 86.5 472.6 86.5 472.3 86.9 471.9 87.3 471.9 88 471.9 88.6 472.3 89.4 472.6 90.1 473.7 91.1L477.3 94.5Q478.3 95.5 479.2 96.7 480.1 97.8 480.7 99.3 481.2 100.8 481.2 103 481.2 107.3 479.6 109.8 478 112.3 474 112.3ZM484.9 112V106.7H491.2V112ZM495.1 112V106.7H501.4V112ZM505.3 112V106.7H511.6V112Z" fill="#111"/>
</g>
<rect id="panel-border" x="10" y="10" width="1004" height="1004" fill="none" stroke="#111" stroke-width="20"/>
</svg>
examples/outputs/fox-cyberpunk-neon.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Neon Fox</title>
<desc id="d">Cyberpunk neon: dark base, glowing outline on the hero silhouette, magenta skyline haze, perspective grid floor, lit windows.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#07040f"/>
<stop offset="0.6" stop-color="#1c0a3a"/>
<stop offset="1" stop-color="#7a1462"/>
</linearGradient>
<radialGradient id="moon" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#ff4fd8" stop-opacity="0.9"/>
<stop offset="1" stop-color="#ff4fd8" stop-opacity="0"/>
</radialGradient>
<clipPath id="floor-clip">
<rect y="840" width="1024" height="184"/>
</clipPath>
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="9" result="b"/>
<feMerge>
<feMergeNode in="b"/>
<feMergeNode in="b"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect id="sky-bg" width="1024" height="1024" fill="url(#sky)"/>
<circle id="halo" cx="760" cy="330" r="240" fill="url(#moon)"/>
<g id="skyline" fill="#0d0820" opacity="0.95">
<rect y="662" width="110" height="178"/>
<rect x="116" y="682" width="70" height="158"/>
<rect x="196" y="607" width="70" height="233"/>
<rect x="276" y="468" width="70" height="372"/>
<rect x="356" y="691" width="90" height="149"/>
<rect x="446" y="593" width="60" height="247"/>
<rect x="506" y="677" width="90" height="163"/>
<rect x="606" y="685" width="60" height="155"/>
<rect x="676" y="643" width="70" height="197"/>
<rect x="756" y="553" width="70" height="287"/>
<rect x="836" y="688" width="60" height="152"/>
<rect x="896" y="558" width="70" height="282"/>
<rect x="966" y="593" width="58" height="247"/>
</g>
<g id="windows" fill="#25e6ff" opacity="0.75">
<rect x="82" y="680" width="8" height="12"/>
<rect x="28" y="714" width="8" height="12"/>
<rect x="46" y="714" width="8" height="12"/>
<rect x="64" y="782" width="8" height="12"/>
<rect x="10" y="816" width="8" height="12"/>
<rect x="144" y="700" width="8" height="12"/>
<rect x="126" y="734" width="8" height="12"/>
<rect x="162" y="768" width="8" height="12"/>
<rect x="224" y="659" width="8" height="12"/>
<rect x="242" y="659" width="8" height="12"/>
<rect x="242" y="693" width="8" height="12"/>
<rect x="286" y="520" width="8" height="12"/>
<rect x="286" y="588" width="8" height="12"/>
<rect x="286" y="622" width="8" height="12"/>
<rect x="322" y="690" width="8" height="12"/>
<rect x="286" y="724" width="8" height="12"/>
<rect x="322" y="724" width="8" height="12"/>
<rect x="304" y="758" width="8" height="12"/>
<rect x="286" y="792" width="8" height="12"/>
<rect x="384" y="709" width="8" height="12"/>
<rect x="420" y="811" width="8" height="12"/>
<rect x="492" y="611" width="8" height="12"/>
<rect x="474" y="645" width="8" height="12"/>
<rect x="456" y="713" width="8" height="12"/>
<rect x="474" y="713" width="8" height="12"/>
<rect x="474" y="781" width="8" height="12"/>
<rect x="516" y="695" width="8" height="12"/>
<rect x="552" y="729" width="8" height="12"/>
<rect x="552" y="797" width="8" height="12"/>
<rect x="634" y="805" width="8" height="12"/>
<rect x="722" y="661" width="8" height="12"/>
<rect x="722" y="729" width="8" height="12"/>
<rect x="766" y="571" width="8" height="12"/>
<rect x="784" y="571" width="8" height="12"/>
<rect x="784" y="639" width="8" height="12"/>
<rect x="766" y="707" width="8" height="12"/>
<rect x="766" y="741" width="8" height="12"/>
<rect x="784" y="775" width="8" height="12"/>
<rect x="846" y="706" width="8" height="12"/>
<rect x="864" y="706" width="8" height="12"/>
<rect x="846" y="740" width="8" height="12"/>
<rect x="882" y="808" width="8" height="12"/>
<rect x="924" y="576" width="8" height="12"/>
<rect x="924" y="610" width="8" height="12"/>
<rect x="906" y="644" width="8" height="12"/>
<rect x="942" y="644" width="8" height="12"/>
<rect x="924" y="678" width="8" height="12"/>
<rect x="942" y="678" width="8" height="12"/>
<rect x="942" y="746" width="8" height="12"/>
<rect x="994" y="611" width="8" height="12"/>
<rect x="976" y="645" width="8" height="12"/>
<rect x="976" y="747" width="8" height="12"/>
<rect x="994" y="747" width="8" height="12"/>
<rect x="994" y="781" width="8" height="12"/>
<rect x="976" y="815" width="8" height="12"/>
</g>
<g id="floor">
<rect y="840" width="1024" height="184" fill="#0a0518"/>
<g fill="none" stroke="#ff2fb8" stroke-width="2" opacity="0.75" clip-path="url(#floor-clip)">
<path d="M0 860H1024"/>
<path d="M0 885H1024"/>
<path d="M0 920H1024"/>
<path d="M0 965H1024"/>
<path d="M0 1024H1024"/>
<path d="M272 850L-748 1024"/>
<path d="M312 850L-538 1024"/>
<path d="M352 850L-328 1024"/>
<path d="M392 850L-118 1024"/>
<path d="M432 850L92 1024"/>
<path d="M472 850L302 1024"/>
<path d="M512 850L512 1024"/>
<path d="M552 850L722 1024"/>
<path d="M592 850L932 1024"/>
<path d="M632 850L1142 1024"/>
<path d="M672 850L1352 1024"/>
<path d="M712 850L1562 1024"/>
<path d="M752 850L1772 1024"/>
</g>
<path d="M0 840H1024" stroke="#25e6ff" stroke-width="4" opacity="0.9"/>
</g>
<g id="signs" fill="none" stroke-width="5" stroke-linecap="round" filter="url(#glow)">
<path d="M96 200v130h60" stroke="#ff2fb8"/>
<path d="M900 160h70M900 190h44" stroke="#25e6ff"/>
</g>
<g id="fox-fill" fill="#150a2b">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
</g>
<g id="fox-neon" fill="none" stroke-linejoin="round" stroke-linecap="round" filter="url(#glow)">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" stroke="#ff2fb8" stroke-width="6"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" stroke="#25e6ff" stroke-width="5"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" stroke="#ff7a2f" stroke-width="6"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" stroke="#ff2fb8" stroke-width="3" opacity="0.7"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" stroke="#ff7a2f" stroke-width="6"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" stroke="#25e6ff" stroke-width="4"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" stroke="#25e6ff" stroke-width="3" opacity="0.8"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" stroke="#ff7a2f" stroke-width="4"/>
<path d="M352 300L358 248 392 322Z" stroke="#ff2fb8" stroke-width="4"/>
<path d="M452 306L468 262 478 342Z" stroke="#ff2fb8" stroke-width="4"/>
</g>
<g id="visor">
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#25e6ff" filter="url(#glow)"/>
<circle cx="236" cy="488" r="9" fill="#ff2fb8" filter="url(#glow)"/>
</g>
</svg>
examples/outputs/fox-horror.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Night Fox</title>
<desc id="d">Horror: near-black forest, cold moon, layered fog, roughened silhouette revealing only red eyes, vignette.</desc>
<defs>
<radialGradient id="moon-g" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#dfe8e2"/>
<stop offset="0.6" stop-color="#b9c7bf"/>
<stop offset="1" stop-color="#8fa39a"/>
</radialGradient>
<radialGradient id="moonglow" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#8fa39a" stop-opacity="0.45"/>
<stop offset="1" stop-color="#8fa39a" stop-opacity="0"/>
</radialGradient>
<linearGradient id="fog-g" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#4a5b52" stop-opacity="0"/>
<stop offset="0.5" stop-color="#4a5b52"/>
<stop offset="1" stop-color="#4a5b52" stop-opacity="0"/>
</linearGradient>
<radialGradient id="vig" cx="0.45" cy="0.45" r="0.75">
<stop offset="0.55" stop-color="#050706" stop-opacity="0"/>
<stop offset="1" stop-color="#050706" stop-opacity="0.95"/>
</radialGradient>
<radialGradient id="eyeglow" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#ff2a1a" stop-opacity="0.9"/>
<stop offset="1" stop-color="#ff2a1a" stop-opacity="0"/>
</radialGradient>
<clipPath id="canvas">
<rect width="1024" height="1024"/>
</clipPath>
<filter id="rough" x="-5%" y="-5%" width="110%" height="110%">
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" seed="7"/>
<feDisplacementMap in="SourceGraphic" scale="9"/>
</filter>
</defs>
<rect id="night" width="1024" height="1024" fill="#0b120f"/>
<circle id="moon-halo" cx="470" cy="430" r="470" fill="url(#moonglow)" clip-path="url(#canvas)"/>
<circle id="moon" cx="470" cy="430" r="290" fill="url(#moon-g)"/>
<g id="craters" fill="#8fa39a" opacity="0.35">
<circle cx="600" cy="300" r="46"/>
<circle cx="330" cy="620" r="30"/>
<circle cx="640" cy="560" r="22"/>
<circle cx="360" cy="260" r="18"/>
</g>
<g id="far-trees" fill="#16211c" filter="url(#rough)" clip-path="url(#canvas)">
<path d="M34 900L52 0 67 0 86 900ZM60 512L-25 457 60 502ZM60 342L198 280 60 332ZM60 569L163 489 60 559ZM60 583L146 514 60 573ZM60 265L-90 203 60 255ZM60 571L-93 480 60 561ZM60 468L192 380 60 458Z"/>
<path d="M154 900L165 140 174 140 186 900ZM170 198L100 157 170 188ZM170 556L255 513 170 546ZM170 617L10 502 170 607ZM170 267L284 196 170 257ZM170 511L22 426 170 501ZM170 209L272 122 170 199ZM170 558L86 524 170 548Z"/>
<path d="M910 900L931-20 949-20 970 900ZM940 446L843 378 940 436ZM940 232L819 129 940 222ZM940 318L860 258 940 308ZM940 279L1039 196 940 269ZM940 449L793 352 940 439ZM940 168L864 102 940 158ZM940 554L797 478 940 544Z"/>
<path d="M846 900L855 200 864 200 874 900ZM860 555L727 471 860 545ZM860 617L759 531 860 607ZM860 318L777 263 860 308ZM860 538L717 463 860 528ZM860 258L717 144 860 248ZM860 318L783 277 860 308ZM860 290L928 242 860 280Z"/>
<path d="M610 900L617 280 623 280 630 900ZM620 451L759 329 620 441ZM620 407L475 294 620 397ZM620 590L469 521 620 580ZM620 508L521 464 620 498ZM620 590L519 540 620 580ZM620 360L695 299 620 350ZM620 631L692 602 620 621Z"/>
</g>
<path id="ground" d="M0 846C200 830 420 850 600 842S900 830 1024 846V1024H0Z" fill="#070a09"/>
<g id="fox" fill="#050706" filter="url(#rough)">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
</g>
<g id="rim" fill="none" stroke="#8fa39a" stroke-width="3" opacity="0.55" filter="url(#rough)">
<path d="M468 240L492 362C510 392 514 430 512 462L536 516"/>
<path d="M530 492C610 500 650 540 690 580 750 640 780 720 760 800"/>
<path d="M872 486C940 560 962 676 924 752"/>
</g>
<g id="eyes">
<circle cx="340" cy="449" r="40" fill="url(#eyeglow)"/>
<path d="M318 452Q338 438 362 446 342 460 318 452Z" fill="#ff2a1a"/>
<circle cx="346" cy="449" r="3" fill="#ffd1c9"/>
</g>
<g id="fog" fill="url(#fog-g)">
<rect y="620" width="1024" height="120" opacity="0.25"/>
<rect y="760" width="1024" height="140" opacity="0.35"/>
<rect y="830" width="1024" height="194" opacity="0.5"/>
</g>
<rect id="vignette" width="1024" height="1024" fill="url(#vig)"/>
</svg>
examples/outputs/fox-low-poly.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Low Poly Fox</title>
<desc id="d">Low poly: triangulated mesh, flat facets shaded by facing direction to a top-left light, dusk sky and meadow meshes.</desc>
<g id="sky" stroke-width="0.8" stroke-linejoin="round">
<polygon points="0,0 91.3,7.4 0,1" fill="#2d3c6b" stroke="#2d3c6b"/>
<polygon points="128,0 91.3,7.4 0,0" fill="#2d3c6b" stroke="#2d3c6b"/>
<polygon points="0,384 108.3,339.2 107,398.7" fill="#86686c" stroke="#86686c"/>
<polygon points="768,0 896,0 817.9,8.9" fill="#2b3966" stroke="#2b3966"/>
<polygon points="0,384 8.9,475.3 0,512" fill="#8f6a65" stroke="#8f6a65"/>
<polygon points="2.7,336.6 108.3,339.2 0,384" fill="#85696f" stroke="#85696f"/>
<polygon points="108.3,339.2 210.9,342.8 107,398.7" fill="#80656a" stroke="#80656a"/>
<polygon points="210.9,342.8 108.3,339.2 201.8,265.3" fill="#78626c" stroke="#78626c"/>
<polygon points="1012.4,413.8 923.4,403.3 1024,384" fill="#906f6f" stroke="#906f6f"/>
<polygon points="924.1,339.6 1024,337.5 1024,384" fill="#7a6166" stroke="#7a6166"/>
<polygon points="923.4,403.3 924.1,339.6 1024,384" fill="#7d6164" stroke="#7d6164"/>
<polygon points="924.1,339.6 1024,264 1024,337.5" fill="#78626d" stroke="#78626d"/>
<polygon points="1024,264 924.1,339.6 910,300" fill="#77616e" stroke="#77616e"/>
<polygon points="1024,264 912.1,262.7 1024,256" fill="#675767" stroke="#675767"/>
<polygon points="912.1,262.7 1024,264 910,300" fill="#6e5d6c" stroke="#6e5d6c"/>
<polygon points="912.1,262.7 1015,207.6 1024,256" fill="#5f5263" stroke="#5f5263"/>
<polygon points="1015,207.6 912.1,262.7 911.6,205.9" fill="#64586d" stroke="#64586d"/>
<polygon points="1015,207.6 911.6,205.9 907.1,128.2" fill="#504a62" stroke="#504a62"/>
<polygon points="201.1,70.1 211.4,0 256,0" fill="#2f3b64" stroke="#2f3b64"/>
<polygon points="211.4,0 201.1,70.1 128,0" fill="#2d3961" stroke="#2d3961"/>
<polygon points="203.6,206.2 305.6,208.9 201.8,265.3" fill="#5d5265" stroke="#5d5265"/>
<polygon points="89,846.7 105.2,820.7 133.6,843.6" fill="#f39f6e" stroke="#f39f6e"/>
<polygon points="7.4,410.1 0,384 107,398.7" fill="#8e6d6e" stroke="#8e6d6e"/>
<polygon points="7.4,410.1 8.9,475.3 0,384" fill="#94706e" stroke="#94706e"/>
<polygon points="0,263.5 2.7,336.6 0,384" fill="#7b636c" stroke="#7b636c"/>
<polygon points="8.9,475.3 94.7,538.8 0,512" fill="#a07469" stroke="#a07469"/>
<polygon points="209.2,406.3 205,474 107,398.7" fill="#8b6966" stroke="#8b6966"/>
<polygon points="210.9,342.8 209.2,406.3 107,398.7" fill="#7f6265" stroke="#7f6265"/>
<polygon points="209.2,406.3 242,477 205,474" fill="#96706a" stroke="#96706a"/>
<polygon points="308,329.1 210.9,342.8 201.8,265.3" fill="#705b65" stroke="#705b65"/>
<polygon points="403.6,0 401.1,66.6 384,0" fill="#313e6a" stroke="#313e6a"/>
<polygon points="401.1,66.6 307.5,73.2 384,0" fill="#343e65" stroke="#343e65"/>
<polygon points="307.5,73.2 201.1,70.1 256,0" fill="#364068" stroke="#364068"/>
<polygon points="307.5,73.2 401.1,66.6 406.5,133.2" fill="#404568" stroke="#404568"/>
<polygon points="400.7,850.6 356.2,844.2 404,819" fill="#f5a06f" stroke="#f5a06f"/>
<polygon points="511,438 541,493 514,467" fill="#a0776f" stroke="#a0776f"/>
<polygon points="1017.2,688.1 1024,640 1024,768" fill="#d5906f" stroke="#d5906f"/>
<polygon points="1020.6,481.3 1024,384 1024,512" fill="#9a726c" stroke="#9a726c"/>
<polygon points="1020.6,481.3 1012.4,413.8 1024,384" fill="#8a6866" stroke="#8a6866"/>
<polygon points="923.4,403.3 853.5,417.3 877.3,393.5" fill="#8a6a6a" stroke="#8a6a6a"/>
<polygon points="924.1,339.6 906.2,333.4 910,300" fill="#78616a" stroke="#78616a"/>
<polygon points="895.1,365.1 923.4,403.3 877.3,393.5" fill="#856769" stroke="#856769"/>
<polygon points="895.1,365.1 924.1,339.6 923.4,403.3" fill="#84676b" stroke="#84676b"/>
<polygon points="895.1,365.1 906.2,333.4 924.1,339.6" fill="#82676e" stroke="#82676e"/>
<polygon points="921.8,0 914.6,66.2 896,0" fill="#2e3a62" stroke="#2e3a62"/>
<polygon points="1024,128 1009.1,77.8 1024,0" fill="#3c436a" stroke="#3c436a"/>
<polygon points="1009.1,77.8 921.8,0 1024,0" fill="#313d68" stroke="#313d68"/>
<polygon points="921.8,0 1009.1,77.8 914.6,66.2" fill="#39436c" stroke="#39436c"/>
<polygon points="1009.1,77.8 1024,128 907.1,128.2" fill="#414261" stroke="#414261"/>
<polygon points="914.6,66.2 1009.1,77.8 907.1,128.2" fill="#3d4263" stroke="#3d4263"/>
<polygon points="401.1,66.6 509.1,130.4 406.5,133.2" fill="#484b6d" stroke="#484b6d"/>
<polygon points="509.1,130.4 407.3,198.1 406.5,133.2" fill="#4d4b66" stroke="#4d4b66"/>
<polygon points="822.7,64.6 768,0 817.9,8.9" fill="#313e69" stroke="#313e69"/>
<polygon points="822.7,64.6 914.6,66.2 907.1,128.2" fill="#3f4468" stroke="#3f4468"/>
<polygon points="896,0 822.7,64.6 817.9,8.9" fill="#303c65" stroke="#303c65"/>
<polygon points="914.6,66.2 822.7,64.6 896,0" fill="#343e66" stroke="#343e66"/>
<polygon points="1015,207.6 1024,128.9 1024,256" fill="#5a5169" stroke="#5a5169"/>
<polygon points="1024,128 1024,128.9 907.1,128.2" fill="#4d4d6d" stroke="#4d4d6d"/>
<polygon points="1024,128.9 1015,207.6 907.1,128.2" fill="#4f4c68" stroke="#4f4c68"/>
<polygon points="0,128 1.9,198.9 0,256" fill="#5b536c" stroke="#5b536c"/>
<polygon points="1.9,198.9 0,128 10.4,143.8" fill="#4e4a65" stroke="#4e4a65"/>
<polygon points="4.6,73.2 0,128 0,1" fill="#3c436a" stroke="#3c436a"/>
<polygon points="91.3,7.4 4.6,73.2 0,1" fill="#323d68" stroke="#323d68"/>
<polygon points="0,128 4.6,73.2 10.4,143.8" fill="#48496a" stroke="#48496a"/>
<polygon points="4.6,73.2 98.7,139.2 10.4,143.8" fill="#4a4b6c" stroke="#4a4b6c"/>
<polygon points="203.6,206.2 94.8,200.9 195.7,137.2" fill="#514b63" stroke="#514b63"/>
<polygon points="94.8,200.9 98.7,139.2 195.7,137.2" fill="#54516d" stroke="#54516d"/>
<polygon points="94.8,200.9 203.6,206.2 201.8,265.3" fill="#605569" stroke="#605569"/>
<polygon points="1.9,198.9 94.8,200.9 0,256" fill="#5b5165" stroke="#5b5165"/>
<polygon points="98.7,139.2 94.8,200.9 10.4,143.8" fill="#504c67" stroke="#504c67"/>
<polygon points="94.8,200.9 1.9,198.9 10.4,143.8" fill="#514b63" stroke="#514b63"/>
<polygon points="305.6,208.9 353,227 348,247" fill="#5d5265" stroke="#5d5265"/>
<polygon points="296.2,679.6 408.8,689.3 311.8,750.2" fill="#c88767" stroke="#c88767"/>
<polygon points="408.8,689.3 403.8,738.4 311.8,750.2" fill="#cb8967" stroke="#cb8967"/>
<polygon points="0,640 5.2,676.7 0,747" fill="#c9896a" stroke="#c9896a"/>
<polygon points="94.7,538.8 2.1,553.5 0,512" fill="#a8796a" stroke="#a8796a"/>
<polygon points="2.1,553.5 94.7,538.8 98.3,610.2" fill="#b4806d" stroke="#b4806d"/>
<polygon points="100.1,476.4 94.7,538.8 8.9,475.3" fill="#9c7167" stroke="#9c7167"/>
<polygon points="100.1,476.4 7.4,410.1 107,398.7" fill="#936f6d" stroke="#936f6d"/>
<polygon points="7.4,410.1 100.1,476.4 8.9,475.3" fill="#97706b" stroke="#97706b"/>
<polygon points="205,474 100.1,476.4 107,398.7" fill="#916c67" stroke="#916c67"/>
<polygon points="212.2,535.5 100.1,476.4 205,474" fill="#a5786e" stroke="#a5786e"/>
<polygon points="100.1,476.4 212.2,535.5 94.7,538.8" fill="#9c7165" stroke="#9c7165"/>
<polygon points="209.2,406.3 305,429 279,448" fill="#916d6b" stroke="#916d6b"/>
<polygon points="266,458 209.2,406.3 279,448" fill="#8c6966" stroke="#8c6966"/>
<polygon points="209.2,406.3 266,458 242,477" fill="#8c6864" stroke="#8c6864"/>
<polygon points="384,0 306.5,5.3 256,0" fill="#2c3a68" stroke="#2c3a68"/>
<polygon points="306.5,5.3 307.5,73.2 256,0" fill="#303c65" stroke="#303c65"/>
<polygon points="307.5,73.2 306.5,5.3 384,0" fill="#33406c" stroke="#33406c"/>
<polygon points="201.1,70.1 303.2,138.3 195.7,137.2" fill="#484a6b" stroke="#484a6b"/>
<polygon points="307.5,73.2 303.2,138.3 201.1,70.1" fill="#3e4263" stroke="#3e4263"/>
<polygon points="303.2,138.3 203.6,206.2 195.7,137.2" fill="#4b4861" stroke="#4b4861"/>
<polygon points="303.2,138.3 305.6,208.9 203.6,206.2" fill="#5b546e" stroke="#5b546e"/>
<polygon points="303.2,138.3 307.5,73.2 406.5,133.2" fill="#454666" stroke="#454666"/>
<polygon points="303.2,138.3 353,227 305.6,208.9" fill="#534c63" stroke="#534c63"/>
<polygon points="407.3,198.1 303.2,138.3 406.5,133.2" fill="#4c4964" stroke="#4c4964"/>
<polygon points="353,227 303.2,138.3 407.3,198.1" fill="#59526a" stroke="#59526a"/>
<polygon points="1024,847.5 1012.4,826.3 1024,768" fill="#ec9b6d" stroke="#ec9b6d"/>
<polygon points="1012.4,826.3 1024,847.5 979.5,842.1" fill="#e39567" stroke="#e39567"/>
<polygon points="1024,640 1017.4,554.6 1024,512" fill="#b98370" stroke="#b98370"/>
<polygon points="927.5,821.6 935,840.8 890.4,844.3" fill="#e59568" stroke="#e59568"/>
<polygon points="935,840.8 927.5,821.6 979.5,842.1" fill="#e59668" stroke="#e59668"/>
<polygon points="1010,758.2 1017.2,688.1 1024,768" fill="#df956f" stroke="#df956f"/>
<polygon points="1010,758.2 927.5,821.6 913.6,743.7" fill="#d68e67" stroke="#d68e67"/>
<polygon points="927.5,821.6 1010,758.2 979.5,842.1" fill="#dc9167" stroke="#dc9167"/>
<polygon points="1012.4,826.3 1010,758.2 1024,768" fill="#d88f67" stroke="#d88f67"/>
<polygon points="1010,758.2 1012.4,826.3 979.5,842.1" fill="#f3a071" stroke="#f3a071"/>
<polygon points="613.8,333.4 516.7,349.2 610,300" fill="#745d66" stroke="#745d66"/>
<polygon points="620.3,0 640,0 620.5,66.9" fill="#303d67" stroke="#303d67"/>
<polygon points="517.3,66.6 509.1,130.4 401.1,66.6" fill="#3f4366" stroke="#3f4366"/>
<polygon points="460,252 409.2,273.8 407.3,198.1" fill="#695b6e" stroke="#695b6e"/>
<polygon points="708.8,135.2 610.4,144.4 620.5,66.9" fill="#444665" stroke="#444665"/>
<polygon points="610.4,144.4 517.3,66.6 620.5,66.9" fill="#404467" stroke="#404467"/>
<polygon points="517.3,66.6 610.4,144.4 509.1,130.4" fill="#484a6c" stroke="#484a6c"/>
<polygon points="726.6,153.8 708.8,135.2 760,150" fill="#4b4864" stroke="#4b4864"/>
<polygon points="911.6,205.9 877.3,206.5 907.1,128.2" fill="#524c64" stroke="#524c64"/>
<polygon points="877.3,206.5 853.5,182.7 907.1,128.2" fill="#56516c" stroke="#56516c"/>
<polygon points="822.4,139.7 822.7,64.6 907.1,128.2" fill="#434565" stroke="#434565"/>
<polygon points="853.5,182.7 822.4,139.7 907.1,128.2" fill="#524f6c" stroke="#524f6c"/>
<polygon points="845.9,849.9 862,812 890.4,844.3" fill="#f8a271" stroke="#f8a271"/>
<polygon points="927.5,821.6 889,777 913.6,743.7" fill="#df936a" stroke="#df936a"/>
<polygon points="96.5,67 4.6,73.2 91.3,7.4" fill="#353e65" stroke="#353e65"/>
<polygon points="4.6,73.2 96.5,67 98.7,139.2" fill="#3d4162" stroke="#3d4162"/>
<polygon points="96.5,67 91.3,7.4 128,0" fill="#333f6b" stroke="#333f6b"/>
<polygon points="201.1,70.1 96.5,67 128,0" fill="#353e66" stroke="#353e66"/>
<polygon points="96.5,67 201.1,70.1 195.7,137.2" fill="#3f4365" stroke="#3f4365"/>
<polygon points="98.7,139.2 96.5,67 195.7,137.2" fill="#434463" stroke="#434463"/>
<polygon points="108.3,339.2 100.2,277.4 201.8,265.3" fill="#6c5965" stroke="#6c5965"/>
<polygon points="100.2,277.4 94.8,200.9 201.8,265.3" fill="#695b6d" stroke="#695b6d"/>
<polygon points="2.7,336.6 100.2,277.4 108.3,339.2" fill="#705b64" stroke="#705b64"/>
<polygon points="0,263.5 100.2,277.4 2.7,336.6" fill="#705d69" stroke="#705d69"/>
<polygon points="100.2,277.4 0,263.5 0,256" fill="#6b5a6a" stroke="#6b5a6a"/>
<polygon points="94.8,200.9 100.2,277.4 0,256" fill="#645768" stroke="#645768"/>
<polygon points="359,238 353,227 407.3,198.1" fill="#5b5164" stroke="#5b5164"/>
<polygon points="409.2,273.8 359,238 407.3,198.1" fill="#66596c" stroke="#66596c"/>
<polygon points="44.5,848.4 105.2,820.7 89,846.7" fill="#f29e6d" stroke="#f29e6d"/>
<polygon points="44.5,848.4 0,768 105.2,820.7" fill="#e19468" stroke="#e19468"/>
<polygon points="0,768 44.5,848.4 0,813.3" fill="#e09368" stroke="#e09368"/>
<polygon points="208.7,826.5 178.1,850 133.6,843.6" fill="#f29e6d" stroke="#f29e6d"/>
<polygon points="178.1,850 208.7,826.5 222.6,842.4" fill="#fea673" stroke="#fea673"/>
<polygon points="299,527 318,534 310.1,544.6" fill="#a7786a" stroke="#a7786a"/>
<polygon points="407,651 408.8,689.3 296.2,679.6" fill="#d39070" stroke="#d39070"/>
<polygon points="5.2,676.7 93.1,743.9 0,747" fill="#d08c69" stroke="#d08c69"/>
<polygon points="93.1,743.9 0,768 0,747" fill="#d68f69" stroke="#d68f69"/>
<polygon points="0,768 93.1,743.9 105.2,820.7" fill="#e99a70" stroke="#e99a70"/>
<polygon points="2.1,553.5 0,601.2 0,512" fill="#ac7a6a" stroke="#ac7a6a"/>
<polygon points="0,640 0,601.2 98.3,610.2" fill="#c78a71" stroke="#c78a71"/>
<polygon points="0,601.2 2.1,553.5 98.3,610.2" fill="#bd846f" stroke="#bd846f"/>
<polygon points="94.7,538.8 194.9,615.1 98.3,610.2" fill="#bd846f" stroke="#bd846f"/>
<polygon points="212.2,535.5 194.9,615.1 94.7,538.8" fill="#b37f6d" stroke="#b37f6d"/>
<polygon points="196.8,677.7 194.9,615.1 296.2,679.6" fill="#be8267" stroke="#be8267"/>
<polygon points="239,499 212.2,535.5 205,474" fill="#9d7268" stroke="#9d7268"/>
<polygon points="212.2,535.5 239,499 254,510" fill="#a17468" stroke="#a17468"/>
<polygon points="280,520 299,527 310.1,544.6" fill="#a8786a" stroke="#a8786a"/>
<polygon points="280,520 212.2,535.5 254,510" fill="#a97a6c" stroke="#a97a6c"/>
<polygon points="328,376 308,329.1 331,365" fill="#7c6267" stroke="#7c6267"/>
<polygon points="328,376 325,387 308,329.1" fill="#85696d" stroke="#85696d"/>
<polygon points="308,329.1 316.9,404.4 210.9,342.8" fill="#85696e" stroke="#85696e"/>
<polygon points="325,387 316.9,404.4 308,329.1" fill="#7f6367" stroke="#7f6367"/>
<polygon points="316.9,404.4 209.2,406.3 210.9,342.8" fill="#8b6c6e" stroke="#8b6c6e"/>
<polygon points="323,398 316.9,404.4 325,387" fill="#836465" stroke="#836465"/>
<polygon points="316.9,404.4 305,429 209.2,406.3" fill="#8b6a69" stroke="#8b6a69"/>
<polygon points="316.9,404.4 323,398 320,409" fill="#836565" stroke="#836565"/>
<polygon points="317,420 316.9,404.4 320,409" fill="#856665" stroke="#856665"/>
<polygon points="316.9,404.4 317,420 305,429" fill="#8d6b6a" stroke="#8d6b6a"/>
<polygon points="428,307 383,304 409.2,273.8" fill="#705d69" stroke="#705d69"/>
<polygon points="422,752 421,763 403.8,738.4" fill="#e59970" stroke="#e59970"/>
<polygon points="1012.9,606.5 1017.4,554.6 1024,640" fill="#b07b66" stroke="#b07b66"/>
<polygon points="1017.4,554.6 1012.9,606.5 923,568" fill="#b47f6c" stroke="#b47f6c"/>
<polygon points="926.5,646.9 1012.9,606.5 1024,640" fill="#c78a70" stroke="#c78a70"/>
<polygon points="912.5,486.6 1020.6,481.3 1024,512" fill="#9e7369" stroke="#9e7369"/>
<polygon points="912,545 912.5,486.6 1024,512" fill="#9f7367" stroke="#9f7367"/>
<polygon points="1012.4,413.8 912.5,486.6 923.4,403.3" fill="#936e6b" stroke="#936e6b"/>
<polygon points="1020.6,481.3 912.5,486.6 1012.4,413.8" fill="#9a736c" stroke="#9a736c"/>
<polygon points="912.5,486.6 853.5,417.3 923.4,403.3" fill="#896764" stroke="#896764"/>
<polygon points="912.5,486.6 866,502 853.5,417.3" fill="#946d67" stroke="#946d67"/>
<polygon points="1017.4,554.6 916,552 1024,512" fill="#a77769" stroke="#a77769"/>
<polygon points="916,552 912,545 1024,512" fill="#a9796a" stroke="#a9796a"/>
<polygon points="916,552 1017.4,554.6 923,568" fill="#a97868" stroke="#a97868"/>
<polygon points="514.7,9.3 620.3,0 620.5,66.9" fill="#313e69" stroke="#313e69"/>
<polygon points="517.3,66.6 514.7,9.3 620.5,66.9" fill="#343d64" stroke="#343d64"/>
<polygon points="620.3,0 514.7,9.3 512,0" fill="#2d3d6c" stroke="#2d3d6c"/>
<polygon points="514.7,9.3 517.3,66.6 401.1,66.6" fill="#353e65" stroke="#353e65"/>
<polygon points="514.7,9.3 403.6,0 512,0" fill="#2a3863" stroke="#2a3863"/>
<polygon points="403.6,0 514.7,9.3 401.1,66.6" fill="#2e3961" stroke="#2e3961"/>
<polygon points="409.2,273.8 454,263 447,274" fill="#655564" stroke="#655564"/>
<polygon points="460,252 454,263 409.2,273.8" fill="#655665" stroke="#655665"/>
<polygon points="726.6,153.8 694.9,164.9 708.8,135.2" fill="#52506d" stroke="#52506d"/>
<polygon points="610.4,144.4 694.9,164.9 666.5,182.7" fill="#56516d" stroke="#56516d"/>
<polygon points="694.9,164.9 610.4,144.4 708.8,135.2" fill="#504d6b" stroke="#504d6b"/>
<polygon points="720.7,75.4 716.8,0 768,0" fill="#2f3b63" stroke="#2f3b63"/>
<polygon points="822.7,64.6 720.7,75.4 768,0" fill="#343e65" stroke="#343e65"/>
<polygon points="720.7,75.4 708.8,135.2 620.5,66.9" fill="#404467" stroke="#404467"/>
<polygon points="720.7,75.4 822.7,64.6 760,150" fill="#3f4364" stroke="#3f4364"/>
<polygon points="708.8,135.2 720.7,75.4 760,150" fill="#454665" stroke="#454665"/>
<polygon points="716.8,0 720.7,75.4 640,0" fill="#2f3a63" stroke="#2f3a63"/>
<polygon points="640,0 720.7,75.4 620.5,66.9" fill="#343d64" stroke="#343d64"/>
<polygon points="912.1,262.7 895.1,234.9 911.6,205.9" fill="#65586b" stroke="#65586b"/>
<polygon points="895.1,234.9 877.3,206.5 911.6,205.9" fill="#5e5469" stroke="#5e5469"/>
<polygon points="825.1,164.9 822.4,139.7 853.5,182.7" fill="#504c67" stroke="#504c67"/>
<polygon points="847.8,816.2 862,812 845.9,849.9" fill="#ec9b6c" stroke="#ec9b6c"/>
<polygon points="888,795 889,777 927.5,821.6" fill="#e5976c" stroke="#e5976c"/>
<polygon points="888,795 927.5,821.6 890.4,844.3" fill="#e7986a" stroke="#e7986a"/>
<polygon points="44.5,848.4 0,840.6 0,813.3" fill="#f6a170" stroke="#f6a170"/>
<polygon points="311.7,841 308.1,825.8 356.2,844.2" fill="#eb9a6a" stroke="#eb9a6a"/>
<polygon points="380,548 404,596 353,543" fill="#ad7b6a" stroke="#ad7b6a"/>
<polygon points="406,640 407,651 296.2,679.6" fill="#bb8166" stroke="#bb8166"/>
<polygon points="407,651 411,686 408.8,689.3" fill="#c38568" stroke="#c38568"/>
<polygon points="408.8,689.3 414,708 403.8,738.4" fill="#d38e6c" stroke="#d38e6c"/>
<polygon points="414,708 416,719 403.8,738.4" fill="#cf8b69" stroke="#cf8b69"/>
<polygon points="93.1,743.9 193.5,737.6 105.2,820.7" fill="#e89a70" stroke="#e89a70"/>
<polygon points="105.2,820.7 193.5,737.6 133.6,843.6" fill="#e5976b" stroke="#e5976b"/>
<polygon points="193.5,737.6 208.7,826.5 133.6,843.6" fill="#dc9167" stroke="#dc9167"/>
<polygon points="208.7,826.5 193.5,737.6 311.8,750.2" fill="#da9169" stroke="#da9169"/>
<polygon points="193.5,737.6 296.2,679.6 311.8,750.2" fill="#c98766" stroke="#c98766"/>
<polygon points="193.5,737.6 196.8,677.7 296.2,679.6" fill="#cd8b6b" stroke="#cd8b6b"/>
<polygon points="103.4,677.5 93.1,743.9 5.2,676.7" fill="#d99371" stroke="#d99371"/>
<polygon points="103.4,677.5 0,640 98.3,610.2" fill="#cc8d71" stroke="#cc8d71"/>
<polygon points="103.4,677.5 5.2,676.7 0,640" fill="#ca8b6d" stroke="#ca8b6d"/>
<polygon points="194.9,615.1 103.4,677.5 98.3,610.2" fill="#b87f67" stroke="#b87f67"/>
<polygon points="103.4,677.5 194.9,615.1 196.8,677.7" fill="#bf8368" stroke="#bf8368"/>
<polygon points="193.5,737.6 103.4,677.5 196.8,677.7" fill="#c78768" stroke="#c78768"/>
<polygon points="103.4,677.5 193.5,737.6 93.1,743.9" fill="#d18c6a" stroke="#d18c6a"/>
<polygon points="296.9,606.1 194.9,615.1 212.2,535.5" fill="#b7816c" stroke="#b7816c"/>
<polygon points="296.9,606.1 280,520 310.1,544.6" fill="#a47464" stroke="#a47464"/>
<polygon points="280,520 296.9,606.1 212.2,535.5" fill="#ad7b6a" stroke="#ad7b6a"/>
<polygon points="194.9,615.1 296.9,606.1 296.2,679.6" fill="#be836a" stroke="#be836a"/>
<polygon points="341,540 296.9,606.1 310.1,544.6" fill="#b27f6d" stroke="#b27f6d"/>
<polygon points="296.9,606.1 341,540 353,543" fill="#b6816f" stroke="#b6816f"/>
<polygon points="404,596 296.9,606.1 353,543" fill="#b17d69" stroke="#b17d69"/>
<polygon points="404,607 296.9,606.1 404,596" fill="#bb826c" stroke="#bb826c"/>
<polygon points="296.9,606.1 406,640 296.2,679.6" fill="#bc8268" stroke="#bc8268"/>
<polygon points="406,640 296.9,606.1 405,629" fill="#bc836a" stroke="#bc836a"/>
<polygon points="308,329.1 333,354 331,365" fill="#7d6369" stroke="#7d6369"/>
<polygon points="335,343 333,354 308,329.1" fill="#765e65" stroke="#765e65"/>
<polygon points="341,302 339,313 308,329.1" fill="#725d67" stroke="#725d67"/>
<polygon points="434,296 428,307 409.2,273.8" fill="#6d5a66" stroke="#6d5a66"/>
<polygon points="608,839 578.8,847.7 586,839" fill="#f39f6e" stroke="#f39f6e"/>
<polygon points="578.8,847.7 564,839 586,839" fill="#e69668" stroke="#e69668"/>
<polygon points="912.5,486.6 908.5,542.4 895,516" fill="#9e7266" stroke="#9e7266"/>
<polygon points="908.5,542.4 912.5,486.6 912,545" fill="#ac7c6e" stroke="#ac7c6e"/>
<polygon points="880.6,515.5 912.5,486.6 895,516" fill="#a97b6f" stroke="#a97b6f"/>
<polygon points="912.5,486.6 880.6,515.5 866,502" fill="#a3776c" stroke="#a3776c"/>
<polygon points="707.3,620.2 710.5,547.2 816.7,552.2" fill="#af7b69" stroke="#af7b69"/>
<polygon points="1012.9,606.5 915.5,619.7 923,568" fill="#ba826c" stroke="#ba826c"/>
<polygon points="915.5,619.7 1012.9,606.5 926.5,646.9" fill="#b98068" stroke="#b98068"/>
<polygon points="1010,758.2 928.4,705.2 1017.2,688.1" fill="#d08c6a" stroke="#d08c6a"/>
<polygon points="866,502 825.1,435.1 853.5,417.3" fill="#97706b" stroke="#97706b"/>
<polygon points="825.1,435.1 825.9,485.8 793.4,446.2" fill="#926d67" stroke="#926d67"/>
<polygon points="825.9,485.8 825.1,435.1 866,502" fill="#a0766e" stroke="#a0766e"/>
<polygon points="906.2,266.6 912.1,262.7 910,300" fill="#6f5d6c" stroke="#6f5d6c"/>
<polygon points="906.2,266.6 895.1,234.9 912.1,262.7" fill="#635465" stroke="#635465"/>
<polygon points="825.1,164.9 793.4,153.8 822.4,139.7" fill="#514e6b" stroke="#514e6b"/>
<polygon points="822.7,64.6 793.4,153.8 760,150" fill="#4b4c6d" stroke="#4b4c6d"/>
<polygon points="822.4,139.7 793.4,153.8 822.7,64.6" fill="#444564" stroke="#444564"/>
<polygon points="862,812 876,804 890.4,844.3" fill="#e39568" stroke="#e39568"/>
<polygon points="876,804 888,795 890.4,844.3" fill="#f6a272" stroke="#f6a272"/>
<polygon points="311.7,841 267.1,845.3 308.1,825.8" fill="#f8a270" stroke="#f8a270"/>
<polygon points="208.7,826.5 267.1,845.3 222.6,842.4" fill="#f09d6d" stroke="#f09d6d"/>
<polygon points="267.1,845.3 208.7,826.5 311.8,750.2" fill="#e8986c" stroke="#e8986c"/>
<polygon points="308.1,825.8 267.1,845.3 311.8,750.2" fill="#de9267" stroke="#de9267"/>
<polygon points="380,548 405,585 404,596" fill="#b5806d" stroke="#b5806d"/>
<polygon points="411,686 413,697 408.8,689.3" fill="#d69170" stroke="#d69170"/>
<polygon points="413,697 414,708 408.8,689.3" fill="#db9472" stroke="#db9472"/>
<polygon points="416,719 418,730 403.8,738.4" fill="#ca8866" stroke="#ca8866"/>
<polygon points="296.9,606.1 405,618 405,629" fill="#b77f68" stroke="#b77f68"/>
<polygon points="405,618 296.9,606.1 404,607" fill="#bc836c" stroke="#bc836c"/>
<polygon points="312.2,261.7 347,258 345,269" fill="#665766" stroke="#665766"/>
<polygon points="312.2,261.7 305.6,208.9 348,247" fill="#64576a" stroke="#64576a"/>
<polygon points="347,258 312.2,261.7 348,247" fill="#655767" stroke="#655767"/>
<polygon points="305.6,208.9 312.2,261.7 201.8,265.3" fill="#67596b" stroke="#67596b"/>
<polygon points="312.2,261.7 308,329.1 201.8,265.3" fill="#6b5966" stroke="#6b5966"/>
<polygon points="312.2,261.7 341,302 308,329.1" fill="#6d5a66" stroke="#6d5a66"/>
<polygon points="341,302 312.2,261.7 342,291" fill="#6a5865" stroke="#6a5865"/>
<polygon points="339,313 338,324 308,329.1" fill="#765f68" stroke="#765f68"/>
<polygon points="368,271 366,267 409.2,273.8" fill="#6b5a6a" stroke="#6b5a6a"/>
<polygon points="366,267 359,238 409.2,273.8" fill="#695a6a" stroke="#695a6a"/>
<polygon points="383,304 376,286 409.2,273.8" fill="#725f6c" stroke="#725f6c"/>
<polygon points="376,286 368,271 409.2,273.8" fill="#695866" stroke="#695866"/>
<polygon points="441,285 434,296 409.2,273.8" fill="#6e5c69" stroke="#6e5c69"/>
<polygon points="441,285 409.2,273.8 447,274" fill="#665663" stroke="#665663"/>
<polygon points="652,839 674,839 667.8,850.6" fill="#f19d6d" stroke="#f19d6d"/>
<polygon points="630,839 623.3,843.3 608,839" fill="#e89769" stroke="#e89769"/>
<polygon points="421,763 420,774 403.8,738.4" fill="#d58e68" stroke="#d58e68"/>
<polygon points="420,774 418,785 403.8,738.4" fill="#e1966d" stroke="#e1966d"/>
<polygon points="400.2,809.7 418,785 404,819" fill="#e99a6d" stroke="#e99a6d"/>
<polygon points="356.2,844.2 400.2,809.7 404,819" fill="#e8986a" stroke="#e8986a"/>
<polygon points="403.8,738.4 400.2,809.7 311.8,750.2" fill="#e89a71" stroke="#e89a71"/>
<polygon points="418,785 400.2,809.7 403.8,738.4" fill="#e0946b" stroke="#e0946b"/>
<polygon points="400.2,809.7 308.1,825.8 311.8,750.2" fill="#e6986c" stroke="#e6986c"/>
<polygon points="308.1,825.8 400.2,809.7 356.2,844.2" fill="#f29e6f" stroke="#f29e6f"/>
<polygon points="853,579 848,590 816.7,552.2" fill="#ad7a68" stroke="#ad7a68"/>
<polygon points="857,523 825.9,485.8 866,502" fill="#ab7c70" stroke="#ab7c70"/>
<polygon points="857,523 860.7,544.6 816.7,552.2" fill="#af7d6e" stroke="#af7d6e"/>
<polygon points="825.9,485.8 857,523 816.7,552.2" fill="#a07467" stroke="#a07467"/>
<polygon points="510.8,406.2 618,406 511,438" fill="#926f6e" stroke="#926f6e"/>
<polygon points="516.7,349.2 618,406 510.8,406.2" fill="#8b6b6d" stroke="#8b6b6d"/>
<polygon points="541,493 521,483 514,467" fill="#9e746c" stroke="#9e746c"/>
<polygon points="528,490 521,483 541,493" fill="#9c7269" stroke="#9c7269"/>
<polygon points="674,839 696,839 667.8,850.6" fill="#fba471" stroke="#fba471"/>
<polygon points="928.4,705.2 942,675 1017.2,688.1" fill="#c38466" stroke="#c38466"/>
<polygon points="1017.2,688.1 942,675 1024,640" fill="#c28568" stroke="#c28568"/>
<polygon points="942,675 926.5,646.9 1024,640" fill="#cf8e71" stroke="#cf8e71"/>
<polygon points="925,728 1010,758.2 913.6,743.7" fill="#de956e" stroke="#de956e"/>
<polygon points="925,728 928.4,705.2 1010,758.2" fill="#de956f" stroke="#de956f"/>
<polygon points="642.7,206.5 624.9,234.9 619.9,212.8" fill="#5e5368" stroke="#5e5368"/>
<polygon points="642.7,206.5 610.4,144.4 666.5,182.7" fill="#504b62" stroke="#504b62"/>
<polygon points="642.7,206.5 619.9,212.8 610.4,144.4" fill="#544d64" stroke="#544d64"/>
<polygon points="467,262 517.9,271.6 471,282" fill="#665664" stroke="#665664"/>
<polygon points="517.9,271.6 480.8,303.1 471,282" fill="#6e5c69" stroke="#6e5c69"/>
<polygon points="516.7,349.2 517.9,271.6 610,300" fill="#735e69" stroke="#735e69"/>
<polygon points="480.8,303.1 517.9,271.6 516.7,349.2" fill="#6d5963" stroke="#6d5963"/>
<polygon points="418,730 420,741 403.8,738.4" fill="#d7906b" stroke="#d7906b"/>
<polygon points="420,741 422,752 403.8,738.4" fill="#e39870" stroke="#e39870"/>
<polygon points="336,335 335,343 308,329.1" fill="#745d64" stroke="#745d64"/>
<polygon points="338,324 336,335 308,329.1" fill="#755e66" stroke="#755e66"/>
<polygon points="312.2,261.7 344,280 342,291" fill="#6d5c6a" stroke="#6d5c6a"/>
<polygon points="344,280 312.2,261.7 345,269" fill="#705e6e" stroke="#705e6e"/>
<polygon points="486.4,339.5 480.8,303.1 516.7,349.2" fill="#745d65" stroke="#745d65"/>
<polygon points="848,590 825.1,609.7 816.7,552.2" fill="#ba836e" stroke="#ba836e"/>
<polygon points="825.1,609.7 707.3,620.2 816.7,552.2" fill="#b27d68" stroke="#b27d68"/>
<polygon points="860.7,544.6 863,559 816.7,552.2" fill="#ad7b6b" stroke="#ad7b6b"/>
<polygon points="863,559 853,579 816.7,552.2" fill="#b07d6b" stroke="#b07d6b"/>
<polygon points="618,406 609.2,476.3 511,438" fill="#8c6965" stroke="#8c6965"/>
<polygon points="609.2,476.3 618,406 666.5,417.3" fill="#8c6a67" stroke="#8c6a67"/>
<polygon points="613.8,333.4 624.9,365.1 516.7,349.2" fill="#7f646b" stroke="#7f646b"/>
<polygon points="624.9,365.1 618,406 516.7,349.2" fill="#7c6164" stroke="#7c6164"/>
<polygon points="707.3,620.2 687.1,580.2 710.5,547.2" fill="#b37e6a" stroke="#b37e6a"/>
<polygon points="825.9,485.8 760,450 793.4,446.2" fill="#98716b" stroke="#98716b"/>
<polygon points="760,450 825.9,485.8 816.7,552.2" fill="#9d7369" stroke="#9d7369"/>
<polygon points="517.9,271.6 613.8,266.6 610,300" fill="#695866" stroke="#695866"/>
<polygon points="624.9,234.9 613.8,266.6 619.9,212.8" fill="#635669" stroke="#635669"/>
<polygon points="610.4,144.4 517.8,198.3 509.1,130.4" fill="#4e4b66" stroke="#4e4b66"/>
<polygon points="619.9,212.8 517.8,198.3 610.4,144.4" fill="#59526b" stroke="#59526b"/>
<polygon points="613.8,266.6 517.8,198.3 619.9,212.8" fill="#65596e" stroke="#65596e"/>
<polygon points="517.8,198.3 613.8,266.6 517.9,271.6" fill="#695b6e" stroke="#695b6e"/>
<polygon points="509.1,130.4 517.8,198.3 407.3,198.1" fill="#534d66" stroke="#534d66"/>
<polygon points="517.8,198.3 460,252 407.3,198.1" fill="#5f556a" stroke="#5f556a"/>
<polygon points="517.8,198.3 467,262 460,252" fill="#65586b" stroke="#65586b"/>
<polygon points="517.8,198.3 517.9,271.6 467,262" fill="#67596b" stroke="#67596b"/>
<polygon points="396,550 405,585 380,548" fill="#b4806e" stroke="#b4806e"/>
<polygon points="396,550 407,558 405,585" fill="#b37f6d" stroke="#b37f6d"/>
<polygon points="318,534 329,537 310.1,544.6" fill="#a07264" stroke="#a07264"/>
<polygon points="329,537 341,540 310.1,544.6" fill="#a57668" stroke="#a57668"/>
<polygon points="421,318 415,329 391,320" fill="#735d66" stroke="#735d66"/>
<polygon points="383,304 421,318 391,320" fill="#705b65" stroke="#705b65"/>
<polygon points="428,307 421,318 383,304" fill="#735e69" stroke="#735e69"/>
<polygon points="445.2,846 452,843 474,843" fill="#f49f6e" stroke="#f49f6e"/>
<polygon points="825.1,609.7 744,656 707.3,620.2" fill="#bb8269" stroke="#bb8269"/>
<polygon points="744,656 825.1,609.7 812,645" fill="#bc8269" stroke="#bc8269"/>
<polygon points="825.1,609.7 822,634 812,645" fill="#c0856c" stroke="#c0856c"/>
<polygon points="760,450 723.3,469 726.6,446.2" fill="#8f6b65" stroke="#8f6b65"/>
<polygon points="710.5,547.2 723.3,469 816.7,552.2" fill="#aa7b6d" stroke="#aa7b6d"/>
<polygon points="723.3,469 760,450 816.7,552.2" fill="#986f66" stroke="#986f66"/>
<polygon points="618,406 642.7,393.5 666.5,417.3" fill="#916f6e" stroke="#916f6e"/>
<polygon points="624.9,365.1 642.7,393.5 618,406" fill="#88696b" stroke="#88696b"/>
<polygon points="820,825 847.8,816.2 845.9,849.9" fill="#f4a06f" stroke="#f4a06f"/>
<polygon points="801.4,847.5 820,825 845.9,849.9" fill="#ea996a" stroke="#ea996a"/>
<polygon points="744,656 786,666 750,671" fill="#cb8b6e" stroke="#cb8b6e"/>
<polygon points="786,666 758.7,686.4 753,680" fill="#c48568" stroke="#c48568"/>
<polygon points="750,671 786,666 753,680" fill="#c9896b" stroke="#c9896b"/>
<polygon points="830,623 822,634 825.1,609.7" fill="#b27c65" stroke="#b27c65"/>
<polygon points="837,612 830,623 825.1,609.7" fill="#b07b65" stroke="#b07b65"/>
<polygon points="843,601 825.1,609.7 848,590" fill="#be856f" stroke="#be856f"/>
<polygon points="843,601 837,612 825.1,609.7" fill="#be846d" stroke="#be846d"/>
<polygon points="584,506 553,496 609.2,476.3" fill="#a5796f" stroke="#a5796f"/>
<polygon points="553,496 541,493 511,438" fill="#a2776f" stroke="#a2776f"/>
<polygon points="609.2,476.3 553,496 511,438" fill="#987069" stroke="#987069"/>
<polygon points="723.3,469 694.9,435.1 726.6,446.2" fill="#9a736d" stroke="#9a736d"/>
<polygon points="694.9,435.1 609.2,476.3 666.5,417.3" fill="#96706c" stroke="#96706c"/>
<polygon points="800,827 812,826 801.4,847.5" fill="#e79769" stroke="#e79769"/>
<polygon points="812,826 820,825 801.4,847.5" fill="#e79769" stroke="#e79769"/>
<polygon points="793,661 744,656 812,645" fill="#c98a6d" stroke="#c98a6d"/>
<polygon points="793,661 786,666 744,656" fill="#c5876a" stroke="#c5876a"/>
<polygon points="754,822 763,824 756.9,850.4" fill="#f9a371" stroke="#f9a371"/>
<polygon points="763,824 801.4,847.5 756.9,850.4" fill="#faa371" stroke="#faa371"/>
<polygon points="763,824 800,827 801.4,847.5" fill="#ec9b6b" stroke="#ec9b6b"/>
<polygon points="731,834 754,822 756.9,850.4" fill="#ec9a6b" stroke="#ec9a6b"/>
<polygon points="843.7,605.1 843,601 848,590" fill="#b7806b" stroke="#b7806b"/>
<polygon points="683,574 672,563 710.5,547.2" fill="#ab7968" stroke="#ab7968"/>
<polygon points="687.1,580.2 683,574 710.5,547.2" fill="#ab7967" stroke="#ab7967"/>
<polygon points="672,563 628,530 710.5,547.2" fill="#af7d6d" stroke="#af7d6d"/>
<polygon points="628,530 723.3,469 710.5,547.2" fill="#9c7165" stroke="#9c7165"/>
<polygon points="694.9,435.1 628,530 609.2,476.3" fill="#976f67" stroke="#976f67"/>
<polygon points="628,530 694.9,435.1 723.3,469" fill="#a0756d" stroke="#a0756d"/>
<polygon points="599,513 584,506 609.2,476.3" fill="#a5786d" stroke="#a5786d"/>
<polygon points="628,530 599,513 609.2,476.3" fill="#9b7166" stroke="#9b7166"/>
<polygon points="823.4,341.2 895.1,365.1 877.3,393.5" fill="#ffe0a0" stroke="#ffe0a0"/>
<polygon points="853.5,417.3 823.4,341.2 877.3,393.5" fill="#fad497" stroke="#fad497"/>
<polygon points="895.1,365.1 823.4,341.2 906.2,333.4" fill="#ffd99b" stroke="#ffd99b"/>
<polygon points="906.2,333.4 823.4,341.2 910,300" fill="#f6d094" stroke="#f6d094"/>
<polygon points="642.7,206.5 703.8,272 624.9,234.9" fill="#ffe0a0" stroke="#ffe0a0"/>
<polygon points="710.4,344 703.8,272 760,300" fill="#f9d396" stroke="#f9d396"/>
<polygon points="710.4,344 642.7,393.5 624.9,365.1" fill="#fad497" stroke="#fad497"/>
<polygon points="823.4,341.2 710.4,344 760,300" fill="#ffe09f" stroke="#ffe09f"/>
<polygon points="825.1,435.1 805.9,413.4 853.5,417.3" fill="#fdd699" stroke="#fdd699"/>
<polygon points="805.9,413.4 823.4,341.2 853.5,417.3" fill="#f5d094" stroke="#f5d094"/>
<polygon points="805.9,413.4 825.1,435.1 793.4,446.2" fill="#f0cb90" stroke="#f0cb90"/>
<polygon points="760,450 805.9,413.4 793.4,446.2" fill="#fcd598" stroke="#fcd598"/>
<polygon points="805.9,413.4 710.4,344 823.4,341.2" fill="#f6d094" stroke="#f6d094"/>
<polygon points="824.6,264 906.2,266.6 910,300" fill="#f7d195" stroke="#f7d195"/>
<polygon points="823.4,341.2 824.6,264 910,300" fill="#ffdf9f" stroke="#ffdf9f"/>
<polygon points="906.2,266.6 824.6,264 895.1,234.9" fill="#ffde9e" stroke="#ffde9e"/>
<polygon points="824.6,264 823.4,341.2 760,300" fill="#fcd598" stroke="#fcd598"/>
<polygon points="895.1,234.9 824.6,264 877.3,206.5" fill="#f0cb91" stroke="#f0cb91"/>
<polygon points="621.8,330.9 613.8,333.4 610,300" fill="#ffde9e" stroke="#ffde9e"/>
<polygon points="703.8,272 621.8,330.9 610,300" fill="#f7d195" stroke="#f7d195"/>
<polygon points="710.4,344 621.8,330.9 703.8,272" fill="#f2cd92" stroke="#f2cd92"/>
<polygon points="621.8,330.9 624.9,365.1 613.8,333.4" fill="#ffde9e" stroke="#ffde9e"/>
<polygon points="621.8,330.9 710.4,344 624.9,365.1" fill="#ffe2a1" stroke="#ffe2a1"/>
<polygon points="613.8,266.6 615.6,271.8 610,300" fill="#f7d195" stroke="#f7d195"/>
<polygon points="615.6,271.8 703.8,272 610,300" fill="#ffd99a" stroke="#ffd99a"/>
<polygon points="615.6,271.8 613.8,266.6 624.9,234.9" fill="#ffe2a1" stroke="#ffe2a1"/>
<polygon points="703.8,272 615.6,271.8 624.9,234.9" fill="#ffd89a" stroke="#ffd89a"/>
<polygon points="719,194.9 642.7,206.5 666.5,182.7" fill="#fad497" stroke="#fad497"/>
<polygon points="719,194.9 703.8,272 642.7,206.5" fill="#f4cf93" stroke="#f4cf93"/>
<polygon points="694.9,164.9 719,194.9 666.5,182.7" fill="#f0cb91" stroke="#f0cb91"/>
<polygon points="719,194.9 694.9,164.9 726.6,153.8" fill="#ffe2a1" stroke="#ffe2a1"/>
<polygon points="719,194.9 726.6,153.8 760,150" fill="#ffe09f" stroke="#ffe09f"/>
<polygon points="703.8,272 719,194.9 760,300" fill="#f5cf94" stroke="#f5cf94"/>
<polygon points="707.1,399.1 694.9,435.1 666.5,417.3" fill="#ffdb9c" stroke="#ffdb9c"/>
<polygon points="642.7,393.5 707.1,399.1 666.5,417.3" fill="#f5d094" stroke="#f5d094"/>
<polygon points="710.4,344 707.1,399.1 642.7,393.5" fill="#ffde9e" stroke="#ffde9e"/>
<polygon points="694.9,435.1 707.1,399.1 726.6,446.2" fill="#fbd497" stroke="#fbd497"/>
<polygon points="805.9,413.4 707.1,399.1 710.4,344" fill="#ffdd9d" stroke="#ffdd9d"/>
<polygon points="707.1,399.1 760,450 726.6,446.2" fill="#ffd89a" stroke="#ffd89a"/>
<polygon points="707.1,399.1 805.9,413.4 760,450" fill="#f2cd92" stroke="#f2cd92"/>
<polygon points="817.8,207.9 824.6,264 760,300" fill="#ffe4a2" stroke="#ffe4a2"/>
<polygon points="719,194.9 817.8,207.9 760,300" fill="#fdd699" stroke="#fdd699"/>
<polygon points="817.8,207.9 793.4,153.8 825.1,164.9" fill="#ffe1a0" stroke="#ffe1a0"/>
<polygon points="793.4,153.8 817.8,207.9 760,150" fill="#ffdd9d" stroke="#ffdd9d"/>
<polygon points="817.8,207.9 719,194.9 760,150" fill="#f8d295" stroke="#f8d295"/>
<polygon points="817.8,207.9 825.1,164.9 853.5,182.7" fill="#fdd699" stroke="#fdd699"/>
<polygon points="877.3,206.5 817.8,207.9 853.5,182.7" fill="#ffe0a0" stroke="#ffe0a0"/>
<polygon points="824.6,264 817.8,207.9 877.3,206.5" fill="#fed799" stroke="#fed799"/>
</g>
<g id="ground" stroke-width="0.8" stroke-linejoin="round">
<polygon points="0,1024 2.6,1011.8 101.4,1024" fill="#2b4230" stroke="#2b4230"/>
<polygon points="1.2,949 2.6,1011.8 0,1024" fill="#324a34" stroke="#324a34"/>
<polygon points="640,1024 614,1016.7 605,958.6" fill="#365038" stroke="#365038"/>
<polygon points="719.7,956.1 640,1024 605,958.6" fill="#3c573d" stroke="#3c573d"/>
<polygon points="2.6,1011.8 94.5,943.1 101.4,1024" fill="#365038" stroke="#365038"/>
<polygon points="94.5,943.1 2.6,1011.8 1.2,949" fill="#3f5b3e" stroke="#3f5b3e"/>
<polygon points="896,1024 928.5,1019 1024,1024" fill="#2b412f" stroke="#2b412f"/>
<polygon points="1022.5,946 1024,896 1024,1024" fill="#3c553a" stroke="#3c553a"/>
<polygon points="719.7,956.1 710.1,1018 640,1024" fill="#324a34" stroke="#324a34"/>
<polygon points="814.4,955.7 896,1024 810,1024" fill="#334c36" stroke="#334c36"/>
<polygon points="512,1024 614,1016.7 640,1024" fill="#314a36" stroke="#314a36"/>
<polygon points="614,1016.7 512,1024 605,958.6" fill="#324a34" stroke="#324a34"/>
<polygon points="512,1024 514.4,954.6 605,958.6" fill="#3c583d" stroke="#3c583d"/>
<polygon points="928.5,1019 1018.1,1013.9 1024,1024" fill="#324c37" stroke="#324c37"/>
<polygon points="1018.1,1013.9 1022.5,946 1024,1024" fill="#334c35" stroke="#334c35"/>
<polygon points="1022.5,946 1018.1,1013.9 928.5,1019" fill="#38523a" stroke="#38523a"/>
<polygon points="914.8,952.2 1022.5,946 928.5,1019" fill="#3d593d" stroke="#3d593d"/>
<polygon points="896,1024 914.8,952.2 928.5,1019" fill="#37523a" stroke="#37523a"/>
<polygon points="1024,896 914.8,952.2 910.6,892.8" fill="#4a6744" stroke="#4a6744"/>
<polygon points="1022.5,946 914.8,952.2 1024,896" fill="#476443" stroke="#476443"/>
<polygon points="914.8,952.2 814.4,955.7 910.6,892.8" fill="#435e3f" stroke="#435e3f"/>
<polygon points="814.4,955.7 914.8,952.2 896,1024" fill="#3c583d" stroke="#3c583d"/>
<polygon points="710.1,1018 768,1024 640,1024" fill="#2b4230" stroke="#2b4230"/>
<polygon points="768,1024 710.1,1018 719.7,956.1" fill="#375139" stroke="#375139"/>
<polygon points="768,1024 814.4,955.7 810,1024" fill="#344d37" stroke="#344d37"/>
<polygon points="814.4,955.7 768,1024 719.7,956.1" fill="#364e36" stroke="#364e36"/>
<polygon points="845.9,849.9 890.4,844.3 910.6,892.8" fill="#5d7e51" stroke="#5d7e51"/>
<polygon points="0,896 94.5,943.1 1.2,949" fill="#415c3d" stroke="#415c3d"/>
<polygon points="0,896 1.2,949 0,1024" fill="#3d573b" stroke="#3d573b"/>
<polygon points="112.6,874.2 89,846.7 133.6,843.6" fill="#5c7c4f" stroke="#5c7c4f"/>
<polygon points="112.6,874.2 207,942.4 94.5,943.1" fill="#4d6c47" stroke="#4d6c47"/>
<polygon points="512,1024 500.6,1014.6 514.4,954.6" fill="#324a34" stroke="#324a34"/>
<polygon points="500.6,1014.6 406.4,956.4 514.4,954.6" fill="#385138" stroke="#385138"/>
<polygon points="411.6,873.6 356.2,844.2 400.7,850.6" fill="#5f8052" stroke="#5f8052"/>
<polygon points="408,843 411.6,873.6 400.7,850.6" fill="#58774c" stroke="#58774c"/>
<polygon points="411.6,873.6 408,843 430,843" fill="#5f8152" stroke="#5f8152"/>
<polygon points="406.4,956.4 411.6,873.6 514.4,954.6" fill="#476443" stroke="#476443"/>
<polygon points="112.6,874.2 209.4,890.5 207,942.4" fill="#516f49" stroke="#516f49"/>
<polygon points="256,1024 209.8,1024 207,942.4" fill="#365038" stroke="#365038"/>
<polygon points="301.3,946.5 256,1024 207,942.4" fill="#3e5a3e" stroke="#3e5a3e"/>
<polygon points="414.9,1016.8 512,1024 384,1024" fill="#2c4331" stroke="#2c4331"/>
<polygon points="414.9,1016.8 500.6,1014.6 512,1024" fill="#2e4632" stroke="#2e4632"/>
<polygon points="406.4,956.4 414.9,1016.8 384,1024" fill="#354f38" stroke="#354f38"/>
<polygon points="500.6,1014.6 414.9,1016.8 406.4,956.4" fill="#344c36" stroke="#344c36"/>
<polygon points="411.6,873.6 519.6,877.7 514.4,954.6" fill="#4f6d47" stroke="#4f6d47"/>
<polygon points="514.4,954.6 519.6,877.7 605,958.6" fill="#4a6845" stroke="#4a6845"/>
<polygon points="808,889 715.9,875.8 756.9,850.4" fill="#57774d" stroke="#57774d"/>
<polygon points="801.4,847.5 808,889 756.9,850.4" fill="#5d7f51" stroke="#5d7f51"/>
<polygon points="808,889 814.4,955.7 719.7,956.1" fill="#405b3c" stroke="#405b3c"/>
<polygon points="715.9,875.8 808,889 719.7,956.1" fill="#496542" stroke="#496542"/>
<polygon points="808,889 801.4,847.5 845.9,849.9" fill="#5d7f51" stroke="#5d7f51"/>
<polygon points="814.4,955.7 808,889 910.6,892.8" fill="#466140" stroke="#466140"/>
<polygon points="808,889 845.9,849.9 910.6,892.8" fill="#57774d" stroke="#57774d"/>
<polygon points="1016,880.4 1024,847.5 1024,896" fill="#56754b" stroke="#56754b"/>
<polygon points="1024,847.5 1016,880.4 979.5,842.1" fill="#537047" stroke="#537047"/>
<polygon points="1016,880.4 1024,896 910.6,892.8" fill="#54744b" stroke="#54744b"/>
<polygon points="979.5,842.1 1016,880.4 910.6,892.8" fill="#527048" stroke="#527048"/>
<polygon points="890.4,844.3 935,840.8 910.6,892.8" fill="#5d7e51" stroke="#5d7e51"/>
<polygon points="935,840.8 979.5,842.1 910.6,892.8" fill="#57764b" stroke="#57764b"/>
<polygon points="112.6,874.2 44.5,848.4 89,846.7" fill="#5e7f51" stroke="#5e7f51"/>
<polygon points="0,896 44.5,848.4 94.5,943.1" fill="#4c6944" stroke="#4c6944"/>
<polygon points="44.5,848.4 112.6,874.2 94.5,943.1" fill="#55754c" stroke="#55754c"/>
<polygon points="178.1,850 112.6,874.2 133.6,843.6" fill="#5b7b4e" stroke="#5b7b4e"/>
<polygon points="178.1,850 209.4,890.5 112.6,874.2" fill="#59794e" stroke="#59794e"/>
<polygon points="209.4,890.5 178.1,850 222.6,842.4" fill="#537048" stroke="#537048"/>
<polygon points="209.8,1024 128,1024 207,942.4" fill="#324a34" stroke="#324a34"/>
<polygon points="94.5,943.1 128,1024 101.4,1024" fill="#334c36" stroke="#334c36"/>
<polygon points="207,942.4 128,1024 94.5,943.1" fill="#405c3f" stroke="#405c3f"/>
<polygon points="256,1024 305.7,1010.3 384,1024" fill="#314a35" stroke="#314a35"/>
<polygon points="301.3,946.5 305.7,1010.3 256,1024" fill="#375239" stroke="#375239"/>
<polygon points="305.7,1010.3 406.4,956.4 384,1024" fill="#314933" stroke="#314933"/>
<polygon points="305.7,1010.3 301.3,946.5 406.4,956.4" fill="#3e5a3e" stroke="#3e5a3e"/>
<polygon points="296.8,892.6 411.6,873.6 406.4,956.4" fill="#4b6744" stroke="#4b6744"/>
<polygon points="301.3,946.5 296.8,892.6 406.4,956.4" fill="#415b3d" stroke="#415b3d"/>
<polygon points="411.6,873.6 296.8,892.6 356.2,844.2" fill="#59794e" stroke="#59794e"/>
<polygon points="209.4,890.5 296.8,892.6 207,942.4" fill="#506f49" stroke="#506f49"/>
<polygon points="296.8,892.6 301.3,946.5 207,942.4" fill="#425d3e" stroke="#425d3e"/>
<polygon points="519.6,877.7 616.4,873.3 605,958.6" fill="#4a6643" stroke="#4a6643"/>
<polygon points="616.4,873.3 719.7,956.1 605,958.6" fill="#446040" stroke="#446040"/>
<polygon points="616.4,873.3 715.9,875.8 719.7,956.1" fill="#4b6844" stroke="#4b6844"/>
<polygon points="616.4,873.3 667.8,850.6 715.9,875.8" fill="#5b7c4f" stroke="#5b7c4f"/>
<polygon points="578.8,847.7 616.4,873.3 519.6,877.7" fill="#516e47" stroke="#516e47"/>
<polygon points="296.8,892.6 311.7,841 356.2,844.2" fill="#526f47" stroke="#526f47"/>
<polygon points="578.8,847.7 608,839 616.4,873.3" fill="#57764b" stroke="#57764b"/>
<polygon points="564,839 519.6,877.7 534.3,842.8" fill="#5c7d50" stroke="#5c7d50"/>
<polygon points="564,839 578.8,847.7 519.6,877.7" fill="#57764b" stroke="#57764b"/>
<polygon points="0,874.7 44.5,848.4 0,896" fill="#58784d" stroke="#58784d"/>
<polygon points="0,874.7 0,840.6 44.5,848.4" fill="#5f8052" stroke="#5f8052"/>
<polygon points="267.1,845.3 311.7,841 296.8,892.6" fill="#55734a" stroke="#55734a"/>
<polygon points="267.1,845.3 209.4,890.5 222.6,842.4" fill="#5c7c4f" stroke="#5c7c4f"/>
<polygon points="267.1,845.3 296.8,892.6 209.4,890.5" fill="#516f48" stroke="#516f48"/>
<polygon points="616.4,873.3 652,839 667.8,850.6" fill="#5d7d50" stroke="#5d7d50"/>
<polygon points="630,839 652,839 616.4,873.3" fill="#5f8052" stroke="#5f8052"/>
<polygon points="608,839 623.3,843.3 616.4,873.3" fill="#5b7b4f" stroke="#5b7b4f"/>
<polygon points="623.3,843.3 630,839 616.4,873.3" fill="#608253" stroke="#608253"/>
<polygon points="519.6,877.7 524,843 534.3,842.8" fill="#58774c" stroke="#58774c"/>
<polygon points="502,843 524,843 519.6,877.7" fill="#5c7d50" stroke="#5c7d50"/>
<polygon points="667.8,850.6 696,839 715.9,875.8" fill="#5c7c4f" stroke="#5c7c4f"/>
<polygon points="489.7,841.8 502,843 519.6,877.7" fill="#5d7e50" stroke="#5d7e50"/>
<polygon points="474,843 489.7,841.8 519.6,877.7" fill="#608153" stroke="#608153"/>
<polygon points="445.2,846 411.6,873.6 430,843" fill="#5f8152" stroke="#5f8152"/>
<polygon points="445.2,846 519.6,877.7 411.6,873.6" fill="#55734a" stroke="#55734a"/>
<polygon points="445.2,846 474,843 519.6,877.7" fill="#5f8052" stroke="#5f8052"/>
<polygon points="696,839 712.3,840.7 715.9,875.8" fill="#547249" stroke="#547249"/>
<polygon points="712.3,840.7 719,838 715.9,875.8" fill="#5e7f51" stroke="#5e7f51"/>
<polygon points="715.9,875.8 731,834 756.9,850.4" fill="#59784d" stroke="#59784d"/>
<polygon points="719,838 731,834 715.9,875.8" fill="#618353" stroke="#618353"/>
</g>
<g id="fox" stroke-width="0.8" stroke-linejoin="round">
<polygon points="478.5,417.5 510.8,406.2 511,438" fill="#b65824" stroke="#b65824"/>
<polygon points="486.5,448.3 478.5,417.5 511,438" fill="#b95925" stroke="#b95925"/>
<polygon points="486.5,448.3 509.7,471.2 477.6,477.1" fill="#ba5a25" stroke="#ba5a25"/>
<polygon points="486.5,448.3 477.6,477.1 444.5,465.7" fill="#c25e27" stroke="#c25e27"/>
<polygon points="486.5,448.3 511,438 514,467" fill="#b15523" stroke="#b15523"/>
<polygon points="509.7,471.2 486.5,448.3 514,467" fill="#b25624" stroke="#b25624"/>
<polygon points="471,282 444.9,282.5 447,274" fill="#ff7e34" stroke="#ff7e34"/>
<polygon points="408,843 400.7,850.6 404,819" fill="#2c1813" stroke="#2c1813"/>
<polygon points="430,843 408,843 404,819" fill="#2d1913" stroke="#2d1913"/>
<polygon points="401.2,517.1 438.1,544.1 400.2,546.2" fill="#cac4b8" stroke="#cac4b8"/>
<polygon points="447.9,441.2 486.5,448.3 444.5,465.7" fill="#c55f27" stroke="#c55f27"/>
<polygon points="486.5,448.3 447.9,441.2 478.5,417.5" fill="#c45f27" stroke="#c45f27"/>
<polygon points="278.7,475 266,458 279,448" fill="#fffffe" stroke="#fffffe"/>
<polygon points="266,458 278.7,475 242,477" fill="#ffffff" stroke="#ffffff"/>
<polygon points="449.3,814.3 430,843 404,819" fill="#321b15" stroke="#321b15"/>
<polygon points="438.1,544.1 428,555 400.2,546.2" fill="#f7efe0" stroke="#f7efe0"/>
<polygon points="442,501 438.1,544.1 401.2,517.1" fill="#dcd5c8" stroke="#dcd5c8"/>
<polygon points="640.3,775 609.1,759 637.7,748" fill="#b35223" stroke="#b35223"/>
<polygon points="671.2,774.9 640.3,775 637.7,748" fill="#b65424" stroke="#b65424"/>
<polygon points="679.6,742.4 671.2,774.9 637.7,748" fill="#b85424" stroke="#b85424"/>
<polygon points="707.4,673.9 681.6,680.2 687.7,645.5" fill="#d2662a" stroke="#d2662a"/>
<polygon points="681.6,680.2 707.4,673.9 686.3,711.6" fill="#c15826" stroke="#c15826"/>
<polygon points="635.2,704.8 679.6,742.4 637.7,748" fill="#bf5826" stroke="#bf5826"/>
<polygon points="679.6,742.4 635.2,704.8 686.3,711.6" fill="#c15926" stroke="#c15926"/>
<polygon points="447.9,441.2 450.2,418.7 478.5,417.5" fill="#c35e27" stroke="#c35e27"/>
<polygon points="450.2,418.7 447.9,441.2 417.5,413" fill="#c96128" stroke="#c96128"/>
<polygon points="359,238 358,248 348,247" fill="#f87832" stroke="#f87832"/>
<polygon points="353,227 359,238 348,247" fill="#fb7932" stroke="#fb7932"/>
<polygon points="305,429 317,420 312.7,431.2" fill="#ff7f35" stroke="#ff7f35"/>
<polygon points="367.8,400.6 398.8,377.3 417.5,413" fill="#f27530" stroke="#f27530"/>
<polygon points="398.8,377.3 367.8,400.6 375.5,365.4" fill="#f47631" stroke="#f47631"/>
<polygon points="367.8,400.6 350,365.3 375.5,365.4" fill="#f87832" stroke="#f87832"/>
<polygon points="242,477 239,499 205,474" fill="#2e1c15" stroke="#2e1c15"/>
<polygon points="241,497 239,499 242,477" fill="#281813" stroke="#281813"/>
<polygon points="239,499 241,497 254,510" fill="#c8c1b5" stroke="#c8c1b5"/>
<polygon points="278.7,475 280,520 254,510" fill="#cac4b8" stroke="#cac4b8"/>
<polygon points="278.7,475 246,489 242,477" fill="#fffffd" stroke="#fffffd"/>
<polygon points="246,489 278.7,475 254,510" fill="#f1e9da" stroke="#f1e9da"/>
<polygon points="350,365.3 328,376 331,365" fill="#f67731" stroke="#f67731"/>
<polygon points="383,304 391,320 378,315" fill="#2f1a14" stroke="#2f1a14"/>
<polygon points="622.5,811.2 640.3,775 636.9,824.5" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="587.9,679.5 582.8,642.5 610.5,674.8" fill="#d06029" stroke="#d06029"/>
<polygon points="582.8,642.5 587.9,679.5 553.7,671.6" fill="#dd6b2c" stroke="#dd6b2c"/>
<polygon points="553.7,671.6 587.9,679.5 570,716.7" fill="#d9692b" stroke="#d9692b"/>
<polygon points="509.7,471.2 507.4,501.4 477.6,477.1" fill="#c05d26" stroke="#c05d26"/>
<polygon points="507.4,501.4 486.1,510.8 477.6,477.1" fill="#c66028" stroke="#c66028"/>
<polygon points="506.6,656.9 543.6,638.9 553.7,671.6" fill="#e46e2e" stroke="#e46e2e"/>
<polygon points="543.6,638.9 582.8,642.5 553.7,671.6" fill="#e36e2d" stroke="#e36e2d"/>
<polygon points="583.9,621 543.6,638.9 546.6,608.8" fill="#e8702e" stroke="#e8702e"/>
<polygon points="543.6,638.9 583.9,621 582.8,642.5" fill="#e06c2d" stroke="#e06c2d"/>
<polygon points="753,680 750.4,708.8 707.4,673.9" fill="#c35e27" stroke="#c35e27"/>
<polygon points="656.2,673.1 635.2,704.8 610.5,674.8" fill="#cd5e28" stroke="#cd5e28"/>
<polygon points="681.6,680.2 656.2,673.1 687.7,645.5" fill="#c15926" stroke="#c15926"/>
<polygon points="656.2,673.1 681.6,680.2 686.3,711.6" fill="#be5725" stroke="#be5725"/>
<polygon points="635.2,704.8 656.2,673.1 686.3,711.6" fill="#c65b27" stroke="#c65b27"/>
<polygon points="640.3,775 674.4,809.9 636.9,824.5" fill="#a64c21" stroke="#a64c21"/>
<polygon points="674.4,809.9 640.3,775 671.2,774.9" fill="#b15123" stroke="#b15123"/>
<polygon points="467,262 454,263 460,252" fill="#ff8035" stroke="#ff8035"/>
<polygon points="454,263 471,282 447,274" fill="#ff7d34" stroke="#ff7d34"/>
<polygon points="454,263 467,262 471,282" fill="#dd6b2c" stroke="#dd6b2c"/>
<polygon points="862,812 847.8,816.2 855.5,779" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="879.7,747.4 884.4,710.2 913.6,743.7" fill="#ab4e21" stroke="#ab4e21"/>
<polygon points="889,777 879.7,747.4 913.6,743.7" fill="#a44b20" stroke="#a44b20"/>
<polygon points="879.7,747.4 889,777 855.5,779" fill="#ac4f22" stroke="#ac4f22"/>
<polygon points="889,777 888,795 855.5,779" fill="#ae5022" stroke="#ae5022"/>
<polygon points="404,607 404,596 418.6,615.6" fill="#fff7e7" stroke="#fff7e7"/>
<polygon points="316.1,468.4 278.7,475 279,448" fill="#fffffc" stroke="#fffffc"/>
<polygon points="406,640 405,629 418.6,615.6" fill="#fff8e9" stroke="#fff8e9"/>
<polygon points="411,686 407,651 450.1,680.3" fill="#fff9e9" stroke="#fff9e9"/>
<polygon points="325,434 317,420 320,409" fill="#ff7e34" stroke="#ff7e34"/>
<polygon points="333,354 350,365.3 331,365" fill="#f37631" stroke="#f37631"/>
<polygon points="350,365.3 333,354 335,343" fill="#f77831" stroke="#f77831"/>
<polygon points="244,493 241,497 242,477" fill="#2b1a14" stroke="#2b1a14"/>
<polygon points="246,489 244,493 242,477" fill="#301d16" stroke="#301d16"/>
<polygon points="241,497 244,493 254,510" fill="#cbc4b8" stroke="#cbc4b8"/>
<polygon points="244,493 246,489 254,510" fill="#cfc8bc" stroke="#cfc8bc"/>
<polygon points="391,320 366.7,329.8 378,315" fill="#c76028" stroke="#c76028"/>
<polygon points="350,365.3 366.7,329.8 375.5,365.4" fill="#f67731" stroke="#f67731"/>
<polygon points="366.7,329.8 350,365.3 335,343" fill="#f67731" stroke="#f67731"/>
<polygon points="398.2,343.4 398.8,377.3 375.5,365.4" fill="#dd6b2c" stroke="#dd6b2c"/>
<polygon points="398.2,343.4 366.7,329.8 391,320" fill="#c66028" stroke="#c66028"/>
<polygon points="366.7,329.8 398.2,343.4 375.5,365.4" fill="#d3662a" stroke="#d3662a"/>
<polygon points="415,329 398.2,343.4 391,320" fill="#bc5b25" stroke="#bc5b25"/>
<polygon points="630,839 622.5,811.2 636.9,824.5" fill="#ae5022" stroke="#ae5022"/>
<polygon points="630,839 608,839 622.5,811.2" fill="#ae5022" stroke="#ae5022"/>
<polygon points="424,739 433,736 422,752" fill="#381f18" stroke="#381f18"/>
<polygon points="486,778 484,748 500,762" fill="#f17530" stroke="#f17530"/>
<polygon points="520.1,781.5 486,778 500,762" fill="#40241b" stroke="#40241b"/>
<polygon points="481.2,706.3 478,730 461,721" fill="#ed732f" stroke="#ed732f"/>
<polygon points="511,717.7 478,730 481.2,706.3" fill="#eb722f" stroke="#eb722f"/>
<polygon points="478,730 511,717.7 484,748" fill="#ef7430" stroke="#ef7430"/>
<polygon points="467.2,757.4 486,778 479.3,787.4" fill="#3b2019" stroke="#3b2019"/>
<polygon points="486,778 467.2,757.4 484,748" fill="#3c211a" stroke="#3c211a"/>
<polygon points="467.2,757.4 478,730 484,748" fill="#3b2019" stroke="#3b2019"/>
<polygon points="418,785 449.3,814.3 404,819" fill="#40231b" stroke="#40231b"/>
<polygon points="546.3,706.5 553.7,671.6 570,716.7" fill="#de6b2c" stroke="#de6b2c"/>
<polygon points="484,748 511.8,750.7 500,762" fill="#ea712f" stroke="#ea712f"/>
<polygon points="511,717.7 511.8,750.7 484,748" fill="#e7702e" stroke="#e7702e"/>
<polygon points="635.2,704.8 620.5,706 610.5,674.8" fill="#ca5d28" stroke="#ca5d28"/>
<polygon points="620.5,706 587.9,679.5 610.5,674.8" fill="#cd5e28" stroke="#cd5e28"/>
<polygon points="587.9,679.5 620.5,706 570,716.7" fill="#bc5625" stroke="#bc5625"/>
<polygon points="609.1,759 620.5,706 637.7,748" fill="#bc5725" stroke="#bc5725"/>
<polygon points="620.5,706 635.2,704.8 637.7,748" fill="#c15926" stroke="#c15926"/>
<polygon points="879.7,747.4 842.8,703.7 884.4,710.2" fill="#b15123" stroke="#b15123"/>
<polygon points="855.5,674 842.8,703.7 807.2,684.5" fill="#ec6c2e" stroke="#ec6c2e"/>
<polygon points="842.8,703.7 811.2,718.4 807.2,684.5" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="916,552 908.5,542.4 912,545" fill="#c7c0b4" stroke="#c7c0b4"/>
<polygon points="486.1,510.8 462,512 477.6,477.1" fill="#cb6229" stroke="#cb6229"/>
<polygon points="527,516 528,490 541,493" fill="#ea712f" stroke="#ea712f"/>
<polygon points="507.4,501.4 528,490 527,516" fill="#e7702e" stroke="#e7702e"/>
<polygon points="507.4,501.4 506,519 486.1,510.8" fill="#d6672b" stroke="#d6672b"/>
<polygon points="529,517 527,516 541,493" fill="#f2752f" stroke="#f2752f"/>
<polygon points="541.6,520.7 529,517 541,493" fill="#eb722f" stroke="#eb722f"/>
<polygon points="407,651 446.1,639.9 450.1,680.3" fill="#fffcec" stroke="#fffcec"/>
<polygon points="446.1,639.9 470,657 450.1,680.3" fill="#fef6e6" stroke="#fef6e6"/>
<polygon points="406,640 446.1,639.9 407,651" fill="#fff7e7" stroke="#fff7e7"/>
<polygon points="446.1,639.9 406,640 418.6,615.6" fill="#fffcec" stroke="#fffcec"/>
<polygon points="442.6,605.5 446.1,639.9 418.6,615.6" fill="#fffcec" stroke="#fffcec"/>
<polygon points="446.1,639.9 442.6,605.5 471.6,620.8" fill="#fff8e9" stroke="#fff8e9"/>
<polygon points="477,636 446.1,639.9 471.6,620.8" fill="#fdf4e5" stroke="#fdf4e5"/>
<polygon points="470,657 473.7,670.5 450.1,680.3" fill="#fdf5e6" stroke="#fdf5e6"/>
<polygon points="713,724.9 679.6,742.4 686.3,711.6" fill="#c35a26" stroke="#c35a26"/>
<polygon points="679.6,742.4 713,724.9 723.3,746.1" fill="#cc5e28" stroke="#cc5e28"/>
<polygon points="707.4,673.9 713,724.9 686.3,711.6" fill="#c45a27" stroke="#c45a27"/>
<polygon points="750.4,708.8 713,724.9 707.4,673.9" fill="#bf5825" stroke="#bf5825"/>
<polygon points="723.3,746.1 713,724.9 737.3,747.4" fill="#d06029" stroke="#d06029"/>
<polygon points="713,724.9 750.4,708.8 737.3,747.4" fill="#db642b" stroke="#db642b"/>
<polygon points="811.2,718.4 786.4,703 807.2,684.5" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="582.8,642.5 617.1,647.8 610.5,674.8" fill="#d05f29" stroke="#d05f29"/>
<polygon points="583.9,621 617.1,647.8 582.8,642.5" fill="#e26d2d" stroke="#e26d2d"/>
<polygon points="756.4,783.4 714,776.6 737.3,747.4" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="714,776.6 723.3,746.1 737.3,747.4" fill="#ba5524" stroke="#ba5524"/>
<polygon points="714,776.6 674.4,809.9 671.2,774.9" fill="#ab4e21" stroke="#ab4e21"/>
<polygon points="679.6,742.4 714,776.6 671.2,774.9" fill="#b85524" stroke="#b85524"/>
<polygon points="714,776.6 679.6,742.4 723.3,746.1" fill="#b55323" stroke="#b55323"/>
<polygon points="915.5,619.7 926.5,646.9 901,629" fill="#cec8bb" stroke="#cec8bb"/>
<polygon points="876,804 862,812 855.5,779" fill="#ae5022" stroke="#ae5022"/>
<polygon points="888,795 876,804 855.5,779" fill="#a54c20" stroke="#a54c20"/>
<polygon points="374.9,542.7 401.2,517.1 400.2,546.2" fill="#c6bfb4" stroke="#c6bfb4"/>
<polygon points="380,548 374.9,542.7 400.2,546.2" fill="#c1baaf" stroke="#c1baaf"/>
<polygon points="374.9,542.7 379.7,502.9 401.2,517.1" fill="#c0baaf" stroke="#c0baaf"/>
<polygon points="374.9,542.7 380,548 353,543" fill="#beb8ad" stroke="#beb8ad"/>
<polygon points="379.7,502.9 404,485 401.2,517.1" fill="#cbc5b8" stroke="#cbc5b8"/>
<polygon points="309.8,513.2 318,534 299,527" fill="#cac3b7" stroke="#cac3b7"/>
<polygon points="316.1,468.4 309.8,513.2 278.7,475" fill="#f5eedf" stroke="#f5eedf"/>
<polygon points="280,520 309.8,513.2 299,527" fill="#c8c2b6" stroke="#c8c2b6"/>
<polygon points="309.8,513.2 280,520 278.7,475" fill="#ccc5b9" stroke="#ccc5b9"/>
<polygon points="332.1,500.2 316.1,468.4 335.9,476.5" fill="#ede5d7" stroke="#ede5d7"/>
<polygon points="332.1,500.2 309.8,513.2 316.1,468.4" fill="#d1cbbe" stroke="#d1cbbe"/>
<polygon points="309.8,513.2 332.1,500.2 318,534" fill="#cbc4b8" stroke="#cbc4b8"/>
<polygon points="413,697 411,686 450.1,680.3" fill="#fff9e9" stroke="#fff9e9"/>
<polygon points="414,708 447.3,713.7 416,719" fill="#fdf5e6" stroke="#fdf5e6"/>
<polygon points="447.3,713.7 413,697 450.1,680.3" fill="#fff9e9" stroke="#fff9e9"/>
<polygon points="413,697 447.3,713.7 414,708" fill="#fff8e8" stroke="#fff8e8"/>
<polygon points="447.3,713.7 433,736 416,719" fill="#fbf3e4" stroke="#fbf3e4"/>
<polygon points="433,736 418,730 416,719" fill="#f6eedf" stroke="#f6eedf"/>
<polygon points="418,730 433,736 424,739" fill="#f3ebdc" stroke="#f3ebdc"/>
<polygon points="405,629 405,618 418.6,615.6" fill="#fef6e6" stroke="#fef6e6"/>
<polygon points="405,618 404,607 418.6,615.6" fill="#fcf4e5" stroke="#fcf4e5"/>
<polygon points="323,398 338.5,398.9 320,409" fill="#f47631" stroke="#f47631"/>
<polygon points="338.5,398.9 323,398 325,387" fill="#f77831" stroke="#f77831"/>
<polygon points="338.5,398.9 350,365.3 367.8,400.6" fill="#f87832" stroke="#f87832"/>
<polygon points="328,376 338.5,398.9 325,387" fill="#fa7932" stroke="#fa7932"/>
<polygon points="338.5,398.9 328,376 350,365.3" fill="#ff7b33" stroke="#ff7b33"/>
<polygon points="331,437 325,434 320,409" fill="#fe7b33" stroke="#fe7b33"/>
<polygon points="338.5,398.9 331,437 320,409" fill="#f87832" stroke="#f87832"/>
<polygon points="317,420 319,431 312.7,431.2" fill="#ff7c33" stroke="#ff7c33"/>
<polygon points="325,434 319,431 317,420" fill="#ff8035" stroke="#ff8035"/>
<polygon points="323,448 305,429 312.7,431.2" fill="#ffffff" stroke="#ffffff"/>
<polygon points="319,431 323,448 312.7,431.2" fill="#ffffff" stroke="#ffffff"/>
<polygon points="323,448 319,431 325,434" fill="#ffffff" stroke="#ffffff"/>
<polygon points="305,429 323,448 279,448" fill="#fffff9" stroke="#fffff9"/>
<polygon points="323,448 316.1,468.4 279,448" fill="#ffffff" stroke="#ffffff"/>
<polygon points="366.7,329.8 338,324 339,313" fill="#f87832" stroke="#f87832"/>
<polygon points="341,302 355,301 339,313" fill="#f17530" stroke="#f17530"/>
<polygon points="376,312 383,304 378,315" fill="#301a14" stroke="#301a14"/>
<polygon points="376,312 369.7,305.2 383,304" fill="#311b15" stroke="#311b15"/>
<polygon points="366.7,329.8 376,312 378,315" fill="#d2652a" stroke="#d2652a"/>
<polygon points="359,238 366,267 358,248" fill="#d0652a" stroke="#d0652a"/>
<polygon points="369.7,305.2 376,286 383,304" fill="#311b15" stroke="#311b15"/>
<polygon points="376,286 354,279 368,271" fill="#341c16" stroke="#341c16"/>
<polygon points="444.9,282.5 441,285 447,274" fill="#e6862e" stroke="#e6862e"/>
<polygon points="652,839 630,839 636.9,824.5" fill="#ab4f22" stroke="#ab4f22"/>
<polygon points="674.4,809.9 652,839 636.9,824.5" fill="#a94e21" stroke="#a94e21"/>
<polygon points="674,839 652,839 674.4,809.9" fill="#a54c20" stroke="#a54c20"/>
<polygon points="613.9,784.4 568.3,776.3 609.1,759" fill="#b45323" stroke="#b45323"/>
<polygon points="640.3,775 613.9,784.4 609.1,759" fill="#b75424" stroke="#b75424"/>
<polygon points="622.5,811.2 613.9,784.4 640.3,775" fill="#b15123" stroke="#b15123"/>
<polygon points="564,839 574,813.5 586,839" fill="#b35724" stroke="#b35724"/>
<polygon points="613.9,784.4 574,813.5 568.3,776.3" fill="#ac4f22" stroke="#ac4f22"/>
<polygon points="574,813.5 613.9,784.4 622.5,811.2" fill="#ad5022" stroke="#ad5022"/>
<polygon points="574,813.5 608,839 586,839" fill="#ad5022" stroke="#ad5022"/>
<polygon points="608,839 574,813.5 622.5,811.2" fill="#ad4f22" stroke="#ad4f22"/>
<polygon points="568.3,776.3 589,740.7 609.1,759" fill="#b55323" stroke="#b55323"/>
<polygon points="620.5,706 589,740.7 570,716.7" fill="#bd5725" stroke="#bd5725"/>
<polygon points="589,740.7 620.5,706 609.1,759" fill="#bc5625" stroke="#bc5625"/>
<polygon points="524,843 533.4,825.9 534.3,842.8" fill="#392018" stroke="#392018"/>
<polygon points="487,789 486,778 520.1,781.5" fill="#bf5d26" stroke="#bf5d26"/>
<polygon points="486,778 487,789 479.3,787.4" fill="#311b15" stroke="#311b15"/>
<polygon points="520.1,781.5 519.4,810.6 495,810" fill="#3c2219" stroke="#3c2219"/>
<polygon points="524,843 519.4,810.6 533.4,825.9" fill="#3a2118" stroke="#3a2118"/>
<polygon points="519.4,810.6 524,843 502,843" fill="#381f17" stroke="#381f17"/>
<polygon points="478,730 448.5,748 461,721" fill="#391f18" stroke="#391f18"/>
<polygon points="467.2,757.4 448.5,748 478,730" fill="#3a2019" stroke="#3a2019"/>
<polygon points="433,736 448.5,748 422,752" fill="#391f18" stroke="#391f18"/>
<polygon points="448.5,748 421,763 422,752" fill="#3c211a" stroke="#3c211a"/>
<polygon points="505.1,675.9 546.3,706.5 511,717.7" fill="#e8702e" stroke="#e8702e"/>
<polygon points="505.1,675.9 511,717.7 481.2,706.3" fill="#e66f2e" stroke="#e66f2e"/>
<polygon points="505.1,675.9 506.6,656.9 553.7,671.6" fill="#ef7430" stroke="#ef7430"/>
<polygon points="546.3,706.5 505.1,675.9 553.7,671.6" fill="#ec722f" stroke="#ec722f"/>
<polygon points="473.7,670.5 505.1,675.9 481.2,706.3" fill="#eb722f" stroke="#eb722f"/>
<polygon points="812,645 855.5,674 807.2,684.5" fill="#f06e2f" stroke="#f06e2f"/>
<polygon points="812,645 855.7,647.2 855.5,674" fill="#f06e2f" stroke="#f06e2f"/>
<polygon points="882.7,546.6 908.5,542.4 916,552" fill="#c9c3b6" stroke="#c9c3b6"/>
<polygon points="880.6,515.5 882.7,546.6 860.7,544.6" fill="#fffeef" stroke="#fffeef"/>
<polygon points="908.5,542.4 882.7,546.6 895,516" fill="#d6cfc3" stroke="#d6cfc3"/>
<polygon points="882.7,546.6 880.6,515.5 895,516" fill="#d7d0c3" stroke="#d7d0c3"/>
<polygon points="880.6,515.5 857,523 866,502" fill="#ffffff" stroke="#ffffff"/>
<polygon points="857,523 880.6,515.5 860.7,544.6" fill="#fffff7" stroke="#fffff7"/>
<polygon points="477,636 501.9,612.7 506.6,656.9" fill="#e8702e" stroke="#e8702e"/>
<polygon points="543.6,638.9 501.9,612.7 546.6,608.8" fill="#f17430" stroke="#f17430"/>
<polygon points="501.9,612.7 543.6,638.9 506.6,656.9" fill="#f07430" stroke="#f07430"/>
<polygon points="586.4,575.3 548,569.7 554.6,553.6" fill="#eb722f" stroke="#eb722f"/>
<polygon points="586,551.4 586.4,575.3 554.6,553.6" fill="#e8702e" stroke="#e8702e"/>
<polygon points="586.4,575.3 583.9,621 546.6,608.8" fill="#e46f2e" stroke="#e46f2e"/>
<polygon points="548,569.7 586.4,575.3 546.6,608.8" fill="#ea712f" stroke="#ea712f"/>
<polygon points="462,512 450.2,500.1 477.6,477.1" fill="#cd6329" stroke="#cd6329"/>
<polygon points="477.6,477.1 450.2,500.1 444.5,465.7" fill="#cf6429" stroke="#cf6429"/>
<polygon points="450.2,500.1 442,501 444.5,465.7" fill="#d6682b" stroke="#d6682b"/>
<polygon points="521,483 507.4,501.4 509.7,471.2" fill="#bd5b26" stroke="#bd5b26"/>
<polygon points="521,483 528,490 507.4,501.4" fill="#c55f27" stroke="#c55f27"/>
<polygon points="521,483 509.7,471.2 514,467" fill="#b45724" stroke="#b45724"/>
<polygon points="548,569.7 514,543 554.6,553.6" fill="#f17530" stroke="#f17530"/>
<polygon points="483,656.7 477,636 506.6,656.9" fill="#ed732f" stroke="#ed732f"/>
<polygon points="483,656.7 473.7,670.5 470,657" fill="#ee7330" stroke="#ee7330"/>
<polygon points="505.1,675.9 483,656.7 506.6,656.9" fill="#e66f2e" stroke="#e66f2e"/>
<polygon points="483,656.7 505.1,675.9 473.7,670.5" fill="#eb722f" stroke="#eb722f"/>
<polygon points="778.5,780.5 777.7,809.7 756.4,783.4" fill="#b15123" stroke="#b15123"/>
<polygon points="842.8,703.7 840.1,751.9 811.2,718.4" fill="#ea6c2e" stroke="#ea6c2e"/>
<polygon points="840.1,751.9 824.1,756.9 811.2,718.4" fill="#c65b27" stroke="#c65b27"/>
<polygon points="840.1,751.9 842.8,703.7 879.7,747.4" fill="#ab4e21" stroke="#ab4e21"/>
<polygon points="840.1,751.9 879.7,747.4 855.5,779" fill="#a94e21" stroke="#a94e21"/>
<polygon points="824.1,756.9 840.1,751.9 855.5,779" fill="#af5022" stroke="#af5022"/>
<polygon points="847.8,816.2 805.9,787.1 855.5,779" fill="#a44b20" stroke="#a44b20"/>
<polygon points="805.9,787.1 824.1,756.9 855.5,779" fill="#a54c20" stroke="#a54c20"/>
<polygon points="805.9,787.1 778.5,780.5 824.1,756.9" fill="#ab4e21" stroke="#ab4e21"/>
<polygon points="805.9,787.1 800,827 777.7,809.7" fill="#ab4f22" stroke="#ab4f22"/>
<polygon points="778.5,780.5 805.9,787.1 777.7,809.7" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="714.9,654 707.4,673.9 687.7,645.5" fill="#d2652a" stroke="#d2652a"/>
<polygon points="707.3,620.2 714.9,654 687.7,645.5" fill="#d3662a" stroke="#d3662a"/>
<polygon points="786.4,703 787.1,688.9 807.2,684.5" fill="#f26f30" stroke="#f26f30"/>
<polygon points="753,680 758.7,686.4 750.4,708.8" fill="#ea712f" stroke="#ea712f"/>
<polygon points="758.7,686.4 786.4,703 750.4,708.8" fill="#f06e2f" stroke="#f06e2f"/>
<polygon points="758.7,686.4 787.1,688.9 786.4,703" fill="#f37030" stroke="#f37030"/>
<polygon points="696,839 674,839 674.4,809.9" fill="#ac4f22" stroke="#ac4f22"/>
<polygon points="842.8,703.7 883.1,690.4 884.4,710.2" fill="#ba5624" stroke="#ba5624"/>
<polygon points="883.1,690.4 842.8,703.7 855.5,674" fill="#ea6c2e" stroke="#ea6c2e"/>
<polygon points="926.5,646.9 913,663 901,629" fill="#cbc5b8" stroke="#cbc5b8"/>
<polygon points="942,675 913,663 926.5,646.9" fill="#c1bbaf" stroke="#c1bbaf"/>
<polygon points="913,663 942,675 914,670" fill="#c3bdb1" stroke="#c3bdb1"/>
<polygon points="942,675 928.4,705.2 921,698" fill="#c8c2b6" stroke="#c8c2b6"/>
<polygon points="884.4,710.2 925,728 913.6,743.7" fill="#a84d21" stroke="#a84d21"/>
<polygon points="428,555 407,558 400.2,546.2" fill="#f1e9db" stroke="#f1e9db"/>
<polygon points="408.9,587.3 407,558 428,555" fill="#fffaeb" stroke="#fffaeb"/>
<polygon points="407,558 408.9,587.3 405,585" fill="#fffced" stroke="#fffced"/>
<polygon points="405,585 408.9,587.3 404,596" fill="#fff7e8" stroke="#fff7e8"/>
<polygon points="404,596 408.9,587.3 418.6,615.6" fill="#fffbeb" stroke="#fffbeb"/>
<polygon points="408.9,587.3 442.6,605.5 418.6,615.6" fill="#fff7e8" stroke="#fff7e8"/>
<polygon points="442,501 415.2,485.5 444.5,465.7" fill="#d1652a" stroke="#d1652a"/>
<polygon points="415.2,485.5 442,501 401.2,517.1" fill="#d1cabe" stroke="#d1cabe"/>
<polygon points="404,485 415.2,485.5 401.2,517.1" fill="#d4cec1" stroke="#d4cec1"/>
<polygon points="345.9,535 332.1,500.2 379.7,502.9" fill="#cac4b7" stroke="#cac4b7"/>
<polygon points="345.9,535 374.9,542.7 353,543" fill="#c1bbaf" stroke="#c1bbaf"/>
<polygon points="374.9,542.7 345.9,535 379.7,502.9" fill="#c8c1b5" stroke="#c8c1b5"/>
<polygon points="341,540 345.9,535 353,543" fill="#c0baaf" stroke="#c0baaf"/>
<polygon points="461,706 481.2,706.3 461,721" fill="#eb722f" stroke="#eb722f"/>
<polygon points="447.3,713.7 461,706 461,721" fill="#fff7e7" stroke="#fff7e7"/>
<polygon points="461,706 447.3,713.7 450.1,680.3" fill="#fffbec" stroke="#fffbec"/>
<polygon points="461,706 473.7,670.5 481.2,706.3" fill="#ed732f" stroke="#ed732f"/>
<polygon points="473.7,670.5 461,706 450.1,680.3" fill="#f8f0e1" stroke="#f8f0e1"/>
<polygon points="420,741 418,730 424,739" fill="#fff7e7" stroke="#fff7e7"/>
<polygon points="420,741 424,739 422,752" fill="#f9f1e2" stroke="#f9f1e2"/>
<polygon points="336,335 366.7,329.8 335,343" fill="#f97832" stroke="#f97832"/>
<polygon points="336,335 338,324 366.7,329.8" fill="#f27530" stroke="#f27530"/>
<polygon points="365,307 366.7,329.8 339,313" fill="#fb7a32" stroke="#fb7a32"/>
<polygon points="355,301 365,307 339,313" fill="#f47631" stroke="#f47631"/>
<polygon points="365,307 355,301 369.7,305.2" fill="#3d211a" stroke="#3d211a"/>
<polygon points="365,307 376,312 366.7,329.8" fill="#e56f2e" stroke="#e56f2e"/>
<polygon points="376,312 365,307 369.7,305.2" fill="#351d16" stroke="#351d16"/>
<polygon points="366,267 357,256 358,248" fill="#361e17" stroke="#361e17"/>
<polygon points="358,248 357,256 348,247" fill="#ff7c33" stroke="#ff7c33"/>
<polygon points="357,256 347,258 348,247" fill="#f77831" stroke="#f77831"/>
<polygon points="347,258 357,256 345,269" fill="#f77831" stroke="#f77831"/>
<polygon points="354,290 354,279 376,286" fill="#3c2119" stroke="#3c2119"/>
<polygon points="355,301 354,290 369.7,305.2" fill="#3c211a" stroke="#3c211a"/>
<polygon points="354,290 376,286 369.7,305.2" fill="#371e17" stroke="#371e17"/>
<polygon points="355,275 366,267 368,271" fill="#351d17" stroke="#351d17"/>
<polygon points="354,279 355,275 368,271" fill="#3d221a" stroke="#3d221a"/>
<polygon points="357,256 355,275 345,269" fill="#fe7b33" stroke="#fe7b33"/>
<polygon points="355,275 357,256 366,267" fill="#3b2019" stroke="#3b2019"/>
<polygon points="441,285 455,309 434,296" fill="#ff7d34" stroke="#ff7d34"/>
<polygon points="455,309 441,285 444.9,282.5" fill="#ff7c33" stroke="#ff7c33"/>
<polygon points="568.3,776.3 546,799 544,783" fill="#bd5b26" stroke="#bd5b26"/>
<polygon points="574,813.5 546,799 568.3,776.3" fill="#ba5a25" stroke="#ba5a25"/>
<polygon points="546,799 519.4,810.6 520.1,781.5" fill="#392018" stroke="#392018"/>
<polygon points="449.3,814.3 445.2,846 430,843" fill="#2c1813" stroke="#2c1813"/>
<polygon points="471.9,814.7 449.3,814.3 479.3,787.4" fill="#2d1913" stroke="#2d1913"/>
<polygon points="489,805 471.9,814.7 479.3,787.4" fill="#2c1813" stroke="#2c1813"/>
<polygon points="488,794 489,805 479.3,787.4" fill="#2d1913" stroke="#2d1913"/>
<polygon points="487,789 488,794 479.3,787.4" fill="#311b15" stroke="#311b15"/>
<polygon points="489,805 488,794 495,810" fill="#bc5b25" stroke="#bc5b25"/>
<polygon points="488,794 520.1,781.5 495,810" fill="#3c2219" stroke="#3c2219"/>
<polygon points="488,794 487,789 520.1,781.5" fill="#84421f" stroke="#84421f"/>
<polygon points="448.5,748 443,733 461,721" fill="#3b2019" stroke="#3b2019"/>
<polygon points="443,733 448.5,748 433,736" fill="#3c2119" stroke="#3c2119"/>
<polygon points="443,733 447.3,713.7 461,721" fill="#fff7e8" stroke="#fff7e8"/>
<polygon points="447.3,713.7 443,733 433,736" fill="#fffaea" stroke="#fffaea"/>
<polygon points="448.5,748 436.6,778.9 421,763" fill="#3d211a" stroke="#3d211a"/>
<polygon points="436.6,778.9 420,774 421,763" fill="#3c211a" stroke="#3c211a"/>
<polygon points="436.6,778.9 448.5,748 467.2,757.4" fill="#3b2019" stroke="#3b2019"/>
<polygon points="436.6,778.9 467.2,757.4 479.3,787.4" fill="#3c2119" stroke="#3c2119"/>
<polygon points="449.3,814.3 436.6,778.9 479.3,787.4" fill="#3b2019" stroke="#3b2019"/>
<polygon points="418,785 436.6,778.9 449.3,814.3" fill="#3d221a" stroke="#3d221a"/>
<polygon points="420,774 436.6,778.9 418,785" fill="#3b2019" stroke="#3b2019"/>
<polygon points="546,799 537.5,778.4 544,783" fill="#3a2118" stroke="#3a2118"/>
<polygon points="537.5,778.4 546,799 520.1,781.5" fill="#3c2219" stroke="#3c2219"/>
<polygon points="522,756 520.1,781.5 500,762" fill="#3f241a" stroke="#3f241a"/>
<polygon points="511.8,750.7 522,756 500,762" fill="#d9692b" stroke="#d9692b"/>
<polygon points="522,756 537.5,778.4 520.1,781.5" fill="#3b2219" stroke="#3b2219"/>
<polygon points="543,772 568.3,776.3 544,783" fill="#bf5c26" stroke="#bf5c26"/>
<polygon points="537.5,778.4 543,772 544,783" fill="#3c2219" stroke="#3c2219"/>
<polygon points="882.7,546.6 863,559 860.7,544.6" fill="#f8f0e2" stroke="#f8f0e2"/>
<polygon points="915.5,619.7 916.6,569.1 923,568" fill="#c3bdb2" stroke="#c3bdb2"/>
<polygon points="916.6,569.1 916,552 923,568" fill="#bfb9ae" stroke="#bfb9ae"/>
<polygon points="916.6,569.1 882.7,546.6 916,552" fill="#cbc5b9" stroke="#cbc5b9"/>
<polygon points="485,610 477,636 471.6,620.8" fill="#fffcec" stroke="#fffcec"/>
<polygon points="485,610 501.9,612.7 477,636" fill="#e9712f" stroke="#e9712f"/>
<polygon points="586.4,575.3 611.5,586.6 583.9,621" fill="#e46e2e" stroke="#e46e2e"/>
<polygon points="450.2,500.1 453,506 442,501" fill="#d8682b" stroke="#d8682b"/>
<polygon points="453,506 450.2,500.1 462,512" fill="#dc6b2c" stroke="#dc6b2c"/>
<polygon points="442,501 453,506 438.1,544.1" fill="#e4ddcf" stroke="#e4ddcf"/>
<polygon points="453,506 462,512 438.1,544.1" fill="#efe8d9" stroke="#efe8d9"/>
<polygon points="519.1,576.7 514,543 548,569.7" fill="#f07430" stroke="#f07430"/>
<polygon points="519.1,576.7 548,569.7 546.6,608.8" fill="#eb722f" stroke="#eb722f"/>
<polygon points="501.9,612.7 519.1,576.7 546.6,608.8" fill="#ee7330" stroke="#ee7330"/>
<polygon points="524,531 514,543 506,519" fill="#fff8e8" stroke="#fff8e8"/>
<polygon points="529,517 524,531 527,516" fill="#fffded" stroke="#fffded"/>
<polygon points="524,531 529,517 541.6,520.7" fill="#f27631" stroke="#f27631"/>
<polygon points="524,531 541.6,520.7 554.6,553.6" fill="#eb722f" stroke="#eb722f"/>
<polygon points="514,543 524,531 554.6,553.6" fill="#f07430" stroke="#f07430"/>
<polygon points="462,512 473.2,540 438.1,544.1" fill="#f8f0e1" stroke="#f8f0e1"/>
<polygon points="483,656.7 475,643 477,636" fill="#eb722f" stroke="#eb722f"/>
<polygon points="475,643 446.1,639.9 477,636" fill="#fffcec" stroke="#fffcec"/>
<polygon points="824.1,756.9 771.1,752.6 811.2,718.4" fill="#df672c" stroke="#df672c"/>
<polygon points="778.5,780.5 771.1,752.6 824.1,756.9" fill="#b55323" stroke="#b55323"/>
<polygon points="771.1,752.6 786.4,703 811.2,718.4" fill="#eb6c2e" stroke="#eb6c2e"/>
<polygon points="750.4,708.8 771.1,752.6 737.3,747.4" fill="#f37631" stroke="#f37631"/>
<polygon points="786.4,703 771.1,752.6 750.4,708.8" fill="#e66a2d" stroke="#e66a2d"/>
<polygon points="771.1,752.6 756.4,783.4 737.3,747.4" fill="#c66028" stroke="#c66028"/>
<polygon points="771.1,752.6 778.5,780.5 756.4,783.4" fill="#b05123" stroke="#b05123"/>
<polygon points="816.9,817.3 805.9,787.1 847.8,816.2" fill="#a94e21" stroke="#a94e21"/>
<polygon points="805.9,787.1 816.9,817.3 800,827" fill="#a84d21" stroke="#a84d21"/>
<polygon points="750,671 753,680 707.4,673.9" fill="#c05d26" stroke="#c05d26"/>
<polygon points="914.2,685.8 921,698 884.4,710.2" fill="#ad4f22" stroke="#ad4f22"/>
<polygon points="883.1,690.4 914.2,685.8 884.4,710.2" fill="#b35223" stroke="#b35223"/>
<polygon points="913,663 879.7,654.6 901,629" fill="#b25223" stroke="#b25223"/>
<polygon points="879.7,654.6 873.8,612 901,629" fill="#e4692d" stroke="#e4692d"/>
<polygon points="873.8,612 879.7,654.6 855.7,647.2" fill="#eb6c2e" stroke="#eb6c2e"/>
<polygon points="855.7,647.2 879.7,654.6 855.5,674" fill="#eb6c2e" stroke="#eb6c2e"/>
<polygon points="879.7,654.6 883.1,690.4 855.5,674" fill="#da642b" stroke="#da642b"/>
<polygon points="883.1,690.4 879.7,654.6 914,670" fill="#b75424" stroke="#b75424"/>
<polygon points="879.7,654.6 913,663 914,670" fill="#ae5022" stroke="#ae5022"/>
<polygon points="921,698 923,713 884.4,710.2" fill="#a64c21" stroke="#a64c21"/>
<polygon points="923,713 925,728 884.4,710.2" fill="#a74d21" stroke="#a74d21"/>
<polygon points="928.4,705.2 923,713 921,698" fill="#c0baae" stroke="#c0baae"/>
<polygon points="925,728 923,713 928.4,705.2" fill="#c1baaf" stroke="#c1baaf"/>
<polygon points="396,550 380,548 400.2,546.2" fill="#c2bcb0" stroke="#c2bcb0"/>
<polygon points="407,558 396,550 400.2,546.2" fill="#71a648" stroke="#71a648"/>
<polygon points="415.2,485.5 398.1,435.5 444.5,465.7" fill="#c76028" stroke="#c76028"/>
<polygon points="398.1,435.5 367.8,400.6 417.5,413" fill="#f67731" stroke="#f67731"/>
<polygon points="398.1,435.5 374.8,434.6 367.8,400.6" fill="#ef7430" stroke="#ef7430"/>
<polygon points="398.1,435.5 447.9,441.2 444.5,465.7" fill="#ca6228" stroke="#ca6228"/>
<polygon points="447.9,441.2 398.1,435.5 417.5,413" fill="#de6b2c" stroke="#de6b2c"/>
<polygon points="374.8,434.6 347,443 367.8,400.6" fill="#fc7a32" stroke="#fc7a32"/>
<polygon points="347,443 338.5,398.9 367.8,400.6" fill="#f37631" stroke="#f37631"/>
<polygon points="347,443 331,437 338.5,398.9" fill="#ff7b33" stroke="#ff7b33"/>
<polygon points="372.1,483.7 332.1,500.2 335.9,476.5" fill="#dad3c6" stroke="#dad3c6"/>
<polygon points="332.1,500.2 372.1,483.7 379.7,502.9" fill="#cdc7ba" stroke="#cdc7ba"/>
<polygon points="332.1,500.2 329,537 318,534" fill="#c5beb3" stroke="#c5beb3"/>
<polygon points="345.9,535 329,537 332.1,500.2" fill="#bfb9ae" stroke="#bfb9ae"/>
<polygon points="329,537 345.9,535 341,540" fill="#c0baae" stroke="#c0baae"/>
<polygon points="354,290 353,294 342,291" fill="#fa7932" stroke="#fa7932"/>
<polygon points="353,294 354,290 355,301" fill="#3d221a" stroke="#3d221a"/>
<polygon points="353,294 341,302 342,291" fill="#f87832" stroke="#f87832"/>
<polygon points="353,294 355,301 341,302" fill="#fb7932" stroke="#fb7932"/>
<polygon points="346.7,280.6 355,275 354,279" fill="#f37531" stroke="#f37531"/>
<polygon points="354,290 346.7,280.6 354,279" fill="#fb7932" stroke="#fb7932"/>
<polygon points="346.7,280.6 344,280 345,269" fill="#fa7932" stroke="#fa7932"/>
<polygon points="355,275 346.7,280.6 345,269" fill="#f87832" stroke="#f87832"/>
<polygon points="344,280 346.7,280.6 342,291" fill="#f47631" stroke="#f47631"/>
<polygon points="346.7,280.6 354,290 342,291" fill="#f17530" stroke="#f17530"/>
<polygon points="471,282 472,297 444.9,282.5" fill="#341d16" stroke="#341d16"/>
<polygon points="472,297 455,309 444.9,282.5" fill="#3b2019" stroke="#3b2019"/>
<polygon points="480.8,303.1 472,297 471,282" fill="#b25624" stroke="#b25624"/>
<polygon points="460,317 472,297 480.8,303.1" fill="#2d1913" stroke="#2d1913"/>
<polygon points="455,309 472,297 460,317" fill="#311b15" stroke="#311b15"/>
<polygon points="435,314.8 455,309 460,317" fill="#ff7c33" stroke="#ff7c33"/>
<polygon points="450.8,333.4 435,314.8 460,317" fill="#fe7b33" stroke="#fe7b33"/>
<polygon points="434,296 435,314.8 428,307" fill="#ff7d34" stroke="#ff7d34"/>
<polygon points="455,309 435,314.8 434,296" fill="#ff7f34" stroke="#ff7f34"/>
<polygon points="435,314.8 421,318 428,307" fill="#ff7e34" stroke="#ff7e34"/>
<polygon points="435,314.8 450.8,333.4 415,329" fill="#ff7c33" stroke="#ff7c33"/>
<polygon points="421,318 435,314.8 415,329" fill="#ff7e34" stroke="#ff7e34"/>
<polygon points="436.3,364.1 450.2,418.7 417.5,413" fill="#ec722f" stroke="#ec722f"/>
<polygon points="398.8,377.3 436.3,364.1 417.5,413" fill="#f57731" stroke="#f57731"/>
<polygon points="450.8,333.4 436.3,364.1 415,329" fill="#fc7a32" stroke="#fc7a32"/>
<polygon points="398.2,343.4 436.3,364.1 398.8,377.3" fill="#f67731" stroke="#f67731"/>
<polygon points="415,329 436.3,364.1 398.2,343.4" fill="#ff7f34" stroke="#ff7f34"/>
<polygon points="547,815 546,799 574,813.5" fill="#b85925" stroke="#b85925"/>
<polygon points="519.4,810.6 547,815 533.4,825.9" fill="#382017" stroke="#382017"/>
<polygon points="546,799 547,815 519.4,810.6" fill="#392018" stroke="#392018"/>
<polygon points="452,843 445.2,846 449.3,814.3" fill="#2c1812" stroke="#2c1812"/>
<polygon points="471.9,814.7 452,843 449.3,814.3" fill="#2d1813" stroke="#2d1813"/>
<polygon points="452,843 471.9,814.7 474,843" fill="#2e1913" stroke="#2e1913"/>
<polygon points="489.7,841.8 489,821 502,843" fill="#3b2118" stroke="#3b2118"/>
<polygon points="489,821 489,805 495,810" fill="#b35724" stroke="#b35724"/>
<polygon points="489,821 471.9,814.7 489,805" fill="#2e1914" stroke="#2e1914"/>
<polygon points="519.4,810.6 489,821 495,810" fill="#382017" stroke="#382017"/>
<polygon points="489,821 519.4,810.6 502,843" fill="#3b2118" stroke="#3b2118"/>
<polygon points="546.3,706.5 542.7,739.8 511,717.7" fill="#e56f2e" stroke="#e56f2e"/>
<polygon points="542.7,739.8 511.8,750.7 511,717.7" fill="#de6b2c" stroke="#de6b2c"/>
<polygon points="542.7,739.8 546.3,706.5 570,716.7" fill="#c66028" stroke="#c66028"/>
<polygon points="589,740.7 542.7,739.8 570,716.7" fill="#b85623" stroke="#b85623"/>
<polygon points="542.7,739.8 589,740.7 568.3,776.3" fill="#bb5625" stroke="#bb5625"/>
<polygon points="541,758 542.7,739.8 568.3,776.3" fill="#c66028" stroke="#c66028"/>
<polygon points="522,756 541,767 537.5,778.4" fill="#3e231a" stroke="#3e231a"/>
<polygon points="541,767 543,772 537.5,778.4" fill="#3e231a" stroke="#3e231a"/>
<polygon points="541,767 541,758 568.3,776.3" fill="#c05d26" stroke="#c05d26"/>
<polygon points="543,772 541,767 568.3,776.3" fill="#c45f27" stroke="#c45f27"/>
<polygon points="744,656 714.9,654 707.3,620.2" fill="#c45f27" stroke="#c45f27"/>
<polygon points="714.9,654 744,656 707.4,673.9" fill="#ca6228" stroke="#ca6228"/>
<polygon points="744,656 750,671 707.4,673.9" fill="#bd5b26" stroke="#bd5b26"/>
<polygon points="812,645 822,634 855.7,647.2" fill="#f26f30" stroke="#f26f30"/>
<polygon points="853,579 872,569 874,573" fill="#ee6d2f" stroke="#ee6d2f"/>
<polygon points="863,559 872,569 853,579" fill="#ea6b2e" stroke="#ea6b2e"/>
<polygon points="872,569 882.7,546.6 874,573" fill="#fffff3" stroke="#fffff3"/>
<polygon points="872,569 863,559 882.7,546.6" fill="#fffff0" stroke="#fffff0"/>
<polygon points="896,616 891,603 915.5,619.7" fill="#d5cec1" stroke="#d5cec1"/>
<polygon points="891,603 916.6,569.1 915.5,619.7" fill="#d4cdc0" stroke="#d4cdc0"/>
<polygon points="916.6,569.1 891,603 888,597" fill="#d6cfc2" stroke="#d6cfc2"/>
<polygon points="891,603 873.8,612 888,597" fill="#e3682d" stroke="#e3682d"/>
<polygon points="873.8,612 891,603 896,616" fill="#dd662b" stroke="#dd662b"/>
<polygon points="837,612 873.8,612 855.7,647.2" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="873.8,612 883,592 888,597" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="883,592 873.8,612 848,590" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="873.8,612 899,622 901,629" fill="#cd5e28" stroke="#cd5e28"/>
<polygon points="899,622 873.8,612 896,616" fill="#db642b" stroke="#db642b"/>
<polygon points="899,622 915.5,619.7 901,629" fill="#d5cec1" stroke="#d5cec1"/>
<polygon points="899,622 896,616 915.5,619.7" fill="#d0c9bd" stroke="#d0c9bd"/>
<polygon points="882.7,546.6 886.8,581.5 874,573" fill="#fffff1" stroke="#fffff1"/>
<polygon points="916.6,569.1 886.8,581.5 882.7,546.6" fill="#e0d9cc" stroke="#e0d9cc"/>
<polygon points="886.8,581.5 916.6,569.1 888,597" fill="#dad3c6" stroke="#dad3c6"/>
<polygon points="883,592 886.8,581.5 888,597" fill="#fffff3" stroke="#fffff3"/>
<polygon points="619.9,617.2 617.1,647.8 583.9,621" fill="#da6a2c" stroke="#da6a2c"/>
<polygon points="611.5,586.6 619.9,617.2 583.9,621" fill="#df6c2d" stroke="#df6c2d"/>
<polygon points="541.6,520.7 584,506 554.6,553.6" fill="#ee7330" stroke="#ee7330"/>
<polygon points="584,506 586,551.4 554.6,553.6" fill="#e56f2e" stroke="#e56f2e"/>
<polygon points="485,610 483.2,585.4 501.9,612.7" fill="#f27530" stroke="#f27530"/>
<polygon points="483.2,585.4 519.1,576.7 501.9,612.7" fill="#f07430" stroke="#f07430"/>
<polygon points="524,531 519,518 527,516" fill="#fff9ea" stroke="#fff9ea"/>
<polygon points="519,518 524,531 506,519" fill="#fcf4e5" stroke="#fcf4e5"/>
<polygon points="519,518 507.4,501.4 527,516" fill="#e56f2e" stroke="#e56f2e"/>
<polygon points="519,518 506,519 507.4,501.4" fill="#e8702e" stroke="#e8702e"/>
<polygon points="480,521 462,512 486.1,510.8" fill="#cf6429" stroke="#cf6429"/>
<polygon points="480,521 473.2,540 462,512" fill="#eae2d4" stroke="#eae2d4"/>
<polygon points="446.1,639.9 472,650 470,657" fill="#fffcec" stroke="#fffcec"/>
<polygon points="475,643 472,650 446.1,639.9" fill="#fff7e8" stroke="#fff7e8"/>
<polygon points="472,650 483,656.7 470,657" fill="#ed722f" stroke="#ed722f"/>
<polygon points="472,650 475,643 483,656.7" fill="#f17430" stroke="#f17430"/>
<polygon points="820,825 816.9,817.3 847.8,816.2" fill="#a64c21" stroke="#a64c21"/>
<polygon points="787.1,688.9 786,666 807.2,684.5" fill="#f37030" stroke="#f37030"/>
<polygon points="758.7,686.4 786,666 787.1,688.9" fill="#ef6e2f" stroke="#ef6e2f"/>
<polygon points="723.6,810 696,839 674.4,809.9" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="714,776.6 723.6,810 674.4,809.9" fill="#a74d21" stroke="#a74d21"/>
<polygon points="723.6,810 712.3,840.7 696,839" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="712.3,840.7 723.6,810 719,838" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="723.6,810 714,776.6 756.4,783.4" fill="#a44b20" stroke="#a44b20"/>
<polygon points="915,677 883.1,690.4 914,670" fill="#ac4f22" stroke="#ac4f22"/>
<polygon points="915,677 914.2,685.8 883.1,690.4" fill="#af5122" stroke="#af5122"/>
<polygon points="942,675 915,677 914,670" fill="#c3bcb1" stroke="#c3bcb1"/>
<polygon points="919,691 942,675 921,698" fill="#c6c0b4" stroke="#c6c0b4"/>
<polygon points="914.2,685.8 919,691 921,698" fill="#a64c20" stroke="#a64c20"/>
<polygon points="398,481 415.2,485.5 404,485" fill="#ca6128" stroke="#ca6128"/>
<polygon points="398,481 398.1,435.5 415.2,485.5" fill="#cc6229" stroke="#cc6229"/>
<polygon points="398,481 404,485 379.7,502.9" fill="#d2cbbf" stroke="#d2cbbf"/>
<polygon points="332,445 338,457 323,448" fill="#2e1b15" stroke="#2e1b15"/>
<polygon points="316.1,468.4 338,457 335.9,476.5" fill="#fffff5" stroke="#fffff5"/>
<polygon points="323,448 338,457 316.1,468.4" fill="#fffff4" stroke="#fffff4"/>
<polygon points="368,463 372.1,483.7 335.9,476.5" fill="#f6eedf" stroke="#f6eedf"/>
<polygon points="486.4,339.5 478,337 480.8,303.1" fill="#b15623" stroke="#b15623"/>
<polygon points="478,337 470,331 480.8,303.1" fill="#b25624" stroke="#b25624"/>
<polygon points="470,331 478,337 450.8,333.4" fill="#b85925" stroke="#b85925"/>
<polygon points="466,324 450.8,333.4 460,317" fill="#c96128" stroke="#c96128"/>
<polygon points="466,324 470,331 450.8,333.4" fill="#bd5b26" stroke="#bd5b26"/>
<polygon points="466,324 460,317 480.8,303.1" fill="#2d1813" stroke="#2d1813"/>
<polygon points="470,331 466,324 480.8,303.1" fill="#2c1813" stroke="#2c1813"/>
<polygon points="547,815 549,828 533.4,825.9" fill="#392018" stroke="#392018"/>
<polygon points="549,828 564,839 534.3,842.8" fill="#ba5a25" stroke="#ba5a25"/>
<polygon points="533.4,825.9 549,828 534.3,842.8" fill="#3a2118" stroke="#3a2118"/>
<polygon points="549,828 574,813.5 564,839" fill="#bb5a25" stroke="#bb5a25"/>
<polygon points="549,828 547,815 574,813.5" fill="#b15523" stroke="#b15523"/>
<polygon points="471.9,814.7 482,841 474,843" fill="#2d1913" stroke="#2d1913"/>
<polygon points="489,821 482,841 471.9,814.7" fill="#2c1813" stroke="#2c1813"/>
<polygon points="482,841 489.7,841.8 474,843" fill="#2e1914" stroke="#2e1914"/>
<polygon points="482,841 489,821 489.7,841.8" fill="#2d1813" stroke="#2d1813"/>
<polygon points="541,767 528,754 541,758" fill="#3e231a" stroke="#3e231a"/>
<polygon points="528,754 541,767 522,756" fill="#3e231a" stroke="#3e231a"/>
<polygon points="528,754 542.7,739.8 541,758" fill="#bf5c26" stroke="#bf5c26"/>
<polygon points="528,754 522,756 511.8,750.7" fill="#c45f27" stroke="#c45f27"/>
<polygon points="542.7,739.8 528,754 511.8,750.7" fill="#c96128" stroke="#c96128"/>
<polygon points="822,634 830,623 855.7,647.2" fill="#f57030" stroke="#f57030"/>
<polygon points="830,623 837,612 855.7,647.2" fill="#f06e2f" stroke="#f06e2f"/>
<polygon points="886.8,581.5 877,577 874,573" fill="#fffff4" stroke="#fffff4"/>
<polygon points="877,577 853,579 874,573" fill="#ed6d2f" stroke="#ed6d2f"/>
<polygon points="619.9,617.2 637.4,646.6 617.1,647.8" fill="#e16d2d" stroke="#e16d2d"/>
<polygon points="656.2,673.1 637.4,646.6 687.7,645.5" fill="#c45a26" stroke="#c45a26"/>
<polygon points="637.4,646.6 656.2,673.1 610.5,674.8" fill="#ce5f28" stroke="#ce5f28"/>
<polygon points="617.1,647.8 637.4,646.6 610.5,674.8" fill="#d26129" stroke="#d26129"/>
<polygon points="553,496 541.6,520.7 541,493" fill="#ef7330" stroke="#ef7330"/>
<polygon points="553,496 584,506 541.6,520.7" fill="#e56f2e" stroke="#e56f2e"/>
<polygon points="483.2,585.4 495,553 519.1,576.7" fill="#ed732f" stroke="#ed732f"/>
<polygon points="442.6,605.5 476,582 471.6,620.8" fill="#fcf4e5" stroke="#fcf4e5"/>
<polygon points="476,582 485,610 471.6,620.8" fill="#fcf4e5" stroke="#fcf4e5"/>
<polygon points="476,582 483.2,585.4 485,610" fill="#fff8e9" stroke="#fff8e9"/>
<polygon points="506,519 486,524 486.1,510.8" fill="#dd6b2c" stroke="#dd6b2c"/>
<polygon points="486,524 480,521 486.1,510.8" fill="#d5672b" stroke="#d5672b"/>
<polygon points="480,521 486,524 473.2,540" fill="#f1eadb" stroke="#f1eadb"/>
<polygon points="486,524 495,553 473.2,540" fill="#f0e8da" stroke="#f0e8da"/>
<polygon points="816.9,817.3 812,826 800,827" fill="#a64c21" stroke="#a64c21"/>
<polygon points="820,825 812,826 816.9,817.3" fill="#ac4f22" stroke="#ac4f22"/>
<polygon points="793,661 812,645 807.2,684.5" fill="#f16f2f" stroke="#f16f2f"/>
<polygon points="786,666 793,661 807.2,684.5" fill="#f06e2f" stroke="#f06e2f"/>
<polygon points="800,827 763,824 777.7,809.7" fill="#ab4e21" stroke="#ab4e21"/>
<polygon points="723.6,810 731,834 719,838" fill="#ae5022" stroke="#ae5022"/>
<polygon points="731,834 723.6,810 754,822" fill="#a94e21" stroke="#a94e21"/>
<polygon points="777.7,809.7 756.8,812.3 756.4,783.4" fill="#a94e21" stroke="#a94e21"/>
<polygon points="756.8,812.3 723.6,810 756.4,783.4" fill="#a44b20" stroke="#a44b20"/>
<polygon points="723.6,810 756.8,812.3 754,822" fill="#ad4f22" stroke="#ad4f22"/>
<polygon points="763,824 756.8,812.3 777.7,809.7" fill="#aa4e21" stroke="#aa4e21"/>
<polygon points="756.8,812.3 763,824 754,822" fill="#a84d21" stroke="#a84d21"/>
<polygon points="917,684 915,677 942,675" fill="#bfb9ae" stroke="#bfb9ae"/>
<polygon points="919,691 917,684 942,675" fill="#c3bdb1" stroke="#c3bdb1"/>
<polygon points="915,677 917,684 914.2,685.8" fill="#ab4f21" stroke="#ab4f21"/>
<polygon points="917,684 919,691 914.2,685.8" fill="#a64c21" stroke="#a64c21"/>
<polygon points="331.9,442.7 332,445 323,448" fill="#b6aa9e" stroke="#b6aa9e"/>
<polygon points="331,437 331.9,442.7 325,434" fill="#fffffd" stroke="#fffffd"/>
<polygon points="331.9,442.7 323,448 325,434" fill="#fffffc" stroke="#fffffc"/>
<polygon points="359,455 347,443 374.8,434.6" fill="#f57631" stroke="#f57631"/>
<polygon points="368,463 359,455 374.8,434.6" fill="#f47631" stroke="#f47631"/>
<polygon points="368,463 380,469 372.1,483.7" fill="#f0e8da" stroke="#f0e8da"/>
<polygon points="398.1,435.5 380,469 374.8,434.6" fill="#f87832" stroke="#f87832"/>
<polygon points="380,469 368,463 374.8,434.6" fill="#f77731" stroke="#f77731"/>
<polygon points="468.4,377.2 478,337 486.4,339.5" fill="#b05523" stroke="#b05523"/>
<polygon points="468.4,377.2 436.3,364.1 450.8,333.4" fill="#d2662a" stroke="#d2662a"/>
<polygon points="478,337 468.4,377.2 450.8,333.4" fill="#b95925" stroke="#b95925"/>
<polygon points="478.5,417.5 468.4,377.2 510.8,406.2" fill="#c15d27" stroke="#c15d27"/>
<polygon points="450.2,418.7 468.4,377.2 478.5,417.5" fill="#c45f27" stroke="#c45f27"/>
<polygon points="436.3,364.1 468.4,377.2 450.2,418.7" fill="#d6682b" stroke="#d6682b"/>
<polygon points="468.4,377.2 516.7,349.2 510.8,406.2" fill="#b65824" stroke="#b65824"/>
<polygon points="468.4,377.2 486.4,339.5 516.7,349.2" fill="#7e7519" stroke="#7e7519"/>
<polygon points="873.8,612 843.7,605.1 848,590" fill="#e96b2e" stroke="#e96b2e"/>
<polygon points="837,612 843.7,605.1 873.8,612" fill="#ef6e2f" stroke="#ef6e2f"/>
<polygon points="843,601 843.7,605.1 837,612" fill="#f26f30" stroke="#f26f30"/>
<polygon points="882,587 886.8,581.5 883,592" fill="#fffff6" stroke="#fffff6"/>
<polygon points="882,587 877,577 886.8,581.5" fill="#fffff5" stroke="#fffff5"/>
<polygon points="877,577 882,587 853,579" fill="#e66a2d" stroke="#e66a2d"/>
<polygon points="853,579 882,587 848,590" fill="#f16f2f" stroke="#f16f2f"/>
<polygon points="882,587 883,592 848,590" fill="#e86b2e" stroke="#e86b2e"/>
<polygon points="639.5,602.3 637.4,646.6 619.9,617.2" fill="#da6a2c" stroke="#da6a2c"/>
<polygon points="639.5,602.3 619.9,617.2 611.5,586.6" fill="#db6a2c" stroke="#db6a2c"/>
<polygon points="641.3,571.2 639.5,602.3 611.5,586.6" fill="#df6c2d" stroke="#df6c2d"/>
<polygon points="683,574 641.3,571.2 672,563" fill="#d5672b" stroke="#d5672b"/>
<polygon points="683,574 639.5,602.3 641.3,571.2" fill="#d5672b" stroke="#d5672b"/>
<polygon points="641.3,571.2 628,530 672,563" fill="#d7682b" stroke="#d7682b"/>
<polygon points="495,553 501,551 519.1,576.7" fill="#ee7330" stroke="#ee7330"/>
<polygon points="486,524 501,551 495,553" fill="#f2eadc" stroke="#f2eadc"/>
<polygon points="479,558 495,553 483.2,585.4" fill="#f77731" stroke="#f77731"/>
<polygon points="476,582 479,558 483.2,585.4" fill="#f37631" stroke="#f37631"/>
<polygon points="466,562 479,558 476,582" fill="#f47631" stroke="#f47631"/>
<polygon points="495,553 479,558 473.2,540" fill="#fdf5e6" stroke="#fdf5e6"/>
<polygon points="331.9,442.7 336,441 332,445" fill="#ffffff" stroke="#ffffff"/>
<polygon points="338,457 336,441 347,443" fill="#2e1c15" stroke="#2e1c15"/>
<polygon points="336,441 338,457 332,445" fill="#2f1c16" stroke="#2f1c16"/>
<polygon points="347,443 336,441 331,437" fill="#ff7e34" stroke="#ff7e34"/>
<polygon points="336,441 331.9,442.7 331,437" fill="#ffffff" stroke="#ffffff"/>
<polygon points="350,454 338,457 347,443" fill="#2e1b15" stroke="#2e1b15"/>
<polygon points="338,457 350,454 335.9,476.5" fill="#fffff3" stroke="#fffff3"/>
<polygon points="350,454 368,463 335.9,476.5" fill="#fffff2" stroke="#fffff2"/>
<polygon points="350,454 359,455 368,463" fill="#fffff1" stroke="#fffff1"/>
<polygon points="398,481 388,476 398.1,435.5" fill="#de6b2c" stroke="#de6b2c"/>
<polygon points="388,476 380,469 398.1,435.5" fill="#ee7330" stroke="#ee7330"/>
<polygon points="380,469 388,476 372.1,483.7" fill="#dad3c6" stroke="#dad3c6"/>
<polygon points="372.1,483.7 388,476 379.7,502.9" fill="#d1cabe" stroke="#d1cabe"/>
<polygon points="388,476 398,481 379.7,502.9" fill="#d2cbbf" stroke="#d2cbbf"/>
<polygon points="678.5,607.9 683,574 687.1,580.2" fill="#d1652a" stroke="#d1652a"/>
<polygon points="683,574 678.5,607.9 639.5,602.3" fill="#dc6a2c" stroke="#dc6a2c"/>
<polygon points="678.5,607.9 687.1,580.2 707.3,620.2" fill="#cf6429" stroke="#cf6429"/>
<polygon points="678.5,607.9 707.3,620.2 687.7,645.5" fill="#d7682b" stroke="#d7682b"/>
<polygon points="637.4,646.6 678.5,607.9 687.7,645.5" fill="#dc6a2c" stroke="#dc6a2c"/>
<polygon points="639.5,602.3 678.5,607.9 637.4,646.6" fill="#e06c2d" stroke="#e06c2d"/>
<polygon points="584,506 599,513 586,551.4" fill="#e36e2d" stroke="#e36e2d"/>
<polygon points="514,543 506.9,546.2 506,519" fill="#f7f0e1" stroke="#f7f0e1"/>
<polygon points="506.9,546.2 486,524 506,519" fill="#eee7d9" stroke="#eee7d9"/>
<polygon points="506.9,546.2 501,551 486,524" fill="#f6efe0" stroke="#f6efe0"/>
<polygon points="519.1,576.7 506.9,546.2 514,543" fill="#ed722f" stroke="#ed722f"/>
<polygon points="501,551 506.9,546.2 519.1,576.7" fill="#e8702e" stroke="#e8702e"/>
<polygon points="442.8,583.2 408.9,587.3 428,555" fill="#fffeee" stroke="#fffeee"/>
<polygon points="408.9,587.3 442.8,583.2 442.6,605.5" fill="#fffff1" stroke="#fffff1"/>
<polygon points="442.8,583.2 476,582 442.6,605.5" fill="#fffff0" stroke="#fffff0"/>
<polygon points="473.2,540 470,557 438.1,544.1" fill="#fffcec" stroke="#fffcec"/>
<polygon points="479,558 470,557 473.2,540" fill="#fffced" stroke="#fffced"/>
<polygon points="470,557 479,558 466,562" fill="#fdf5e6" stroke="#fdf5e6"/>
<polygon points="359,455 352,452 347,443" fill="#2e1c15" stroke="#2e1c15"/>
<polygon points="352,452 350,454 347,443" fill="#2e1c15" stroke="#2e1c15"/>
<polygon points="350,454 352,452 359,455" fill="#fffff1" stroke="#fffff1"/>
<polygon points="599,513 607.3,554.5 586,551.4" fill="#e7702e" stroke="#e7702e"/>
<polygon points="607.3,554.5 599,513 628,530" fill="#dc6a2c" stroke="#dc6a2c"/>
<polygon points="607.3,554.5 628,530 641.3,571.2" fill="#e26d2d" stroke="#e26d2d"/>
<polygon points="607.3,554.5 641.3,571.2 611.5,586.6" fill="#e26d2d" stroke="#e26d2d"/>
<polygon points="607.3,554.5 586.4,575.3 586,551.4" fill="#e16d2d" stroke="#e16d2d"/>
<polygon points="607.3,554.5 611.5,586.6 586.4,575.3" fill="#e16d2d" stroke="#e16d2d"/>
<polygon points="449,566 466,562 476,582" fill="#f37531" stroke="#f37531"/>
<polygon points="442.8,583.2 449,566 476,582" fill="#fffeef" stroke="#fffeef"/>
<polygon points="470,557 441,556 438.1,544.1" fill="#fcf4e4" stroke="#fcf4e4"/>
<polygon points="441,556 470,557 466,562" fill="#fdf5e6" stroke="#fdf5e6"/>
<polygon points="449,566 441,556 466,562" fill="#ef7430" stroke="#ef7430"/>
<polygon points="441,556 428,555 438.1,544.1" fill="#fffbeb" stroke="#fffbeb"/>
<polygon points="440,561 449,566 442.8,583.2" fill="#fffeef" stroke="#fffeef"/>
<polygon points="440,561 441,556 449,566" fill="#f88747" stroke="#f88747"/>
<polygon points="440,561 442.8,583.2 428,555" fill="#fffbeb" stroke="#fffbeb"/>
<polygon points="441,556 440,561 428,555" fill="#f27530" stroke="#f27530"/>
</g>
</svg>
examples/outputs/fox-paper-cutout.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Paper Fox</title>
<desc id="d">Paper cut-out: stacked flat sheets, no outlines, soft cast shadow under every layer, a little fibre texture.</desc>
<defs>
<filter id="sh" x="-10%" y="-10%" width="125%" height="125%">
<feDropShadow dx="5" dy="9" stdDeviation="6" flood-color="#3b2a1a" flood-opacity=".38"/>
</filter>
<filter id="shs" x="-10%" y="-10%" width="125%" height="125%">
<feDropShadow dx="3" dy="4" stdDeviation="3" flood-color="#3b2a1a" flood-opacity=".35"/>
</filter>
<linearGradient id="dusk" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f7d9a0"/>
<stop offset="1" stop-color="#f3efe2"/>
</linearGradient>
</defs>
<rect id="sheet" width="1024" height="1024" fill="url(#dusk)"/>
<g id="sun" filter="url(#shs)">
<circle cx="780" cy="270" r="120" fill="#f4a259"/>
<circle cx="780" cy="270" r="78" fill="#f7c873"/>
</g>
<g id="clouds" fill="#fffaf0" filter="url(#shs)">
<path d="M80 250h190a34 34 0 0 0-22-60 50 50 0 0 0-92-10 40 40 0 0 0-76 70z"/>
<path d="M420 150h140a26 26 0 0 0-16-46 38 38 0 0 0-70-8 30 30 0 0 0-54 54z"/>
</g>
<g id="hills">
<path d="M0 610C160 540 300 580 440 640S760 700 1024 600V1024H0Z" fill="#7fb59a" filter="url(#sh)"/>
<path d="M0 730C180 690 330 720 500 770S820 800 1024 730V1024H0Z" fill="#4e8f7a" filter="url(#sh)"/>
<path d="M0 840C220 810 420 830 600 850S880 850 1024 830V1024H0Z" fill="#2f6b62" filter="url(#sh)"/>
</g>
<g id="trees" fill="#2f6b62" filter="url(#shs)">
<path d="M90 640l46-120 46 120z"/>
<path d="M104 590l32-90 32 90z" fill="#3f8272"/>
<path d="M930 660l40-104 40 104z"/>
</g>
<g id="fox" filter="url(#sh)">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#e0662c"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#fff4e0"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#f07a35"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#e0662c" filter="url(#shs)"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#5a3324"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#fff4e0" filter="url(#shs)"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#3a2019"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#f07a35"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#fff4e0"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#fff4e0" filter="url(#shs)"/>
<path d="M352 300L358 248 392 322Z" fill="#3a2019"/>
<path d="M452 306L468 262 478 342Z" fill="#3a2019"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#2b1a14"/>
<circle cx="236" cy="488" r="10" fill="#2b1a14"/>
</g>
</svg>
examples/outputs/fox-pixel-art.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 52" shape-rendering="crispEdges">
<title>Pixel fox</title>
<rect id="background" width="60" height="52" fill="#cfe3d4"/>
<g id="sprite">
<path id="outline" fill="#2b1a14" d="M12 3h1v1h-1zM11 4h1v6h-1zM13 4h1v2h-1zM20 5h1v1h-1zM14 6h1v2h-1zM19 6h1v1h-1zM21 6h1v3h-1zM18 7h1v1h-1zM15 8h1v2h-1zM17 8h1v2h-1zM22 9h1v5h-1zM10 10h1v5h-1zM16 10h1v1h-1zM23 14h1v3h-1zM9 15h1v3h-1zM24 17h1v5h-1zM8 18h1v1h-1zM6 19h2v1h-2zM5 20h1v1h-1zM3 21h2v1h-2zM25 22h3v1h-3zM49 22h1v1h-1zM3 23h1v1h-1zM28 23h2v1h-2zM48 23h1v2h-1zM50 23h1v1h-1zM4 24h2v1h-2zM30 24h2v1h-2zM51 24h1v1h-1zM6 25h3v1h-3zM32 25h1v1h-1zM47 25h1v2h-1zM52 25h1v2h-1zM9 26h3v1h-3zM33 26h2v1h-2zM12 27h4v1h-4zM35 27h1v1h-1zM48 27h1v1h-1zM53 27h1v2h-1zM15 28h1v8h-1zM36 28h1v1h-1zM47 28h1v3h-1zM37 29h1v1h-1zM54 29h1v5h-1zM38 30h1v2h-1zM46 31h1v1h-1zM39 32h1v1h-1zM45 32h1v2h-1zM40 33h1v2h-1zM43 34h2v1h-2zM55 34h1v2h-1zM41 35h2v1h-2zM16 36h1v9h-1zM54 36h1v4h-1zM53 40h1v3h-1zM52 43h1v1h-1zM51 44h1v1h-1zM15 45h1v2h-1zM49 45h2v1h-2zM47 46h2v1h-2zM14 47h1v1h-1zM39 47h8v1h-8zM15 48h24v1h-24z"/>
<path id="c-o" fill="#e9712f" d="M12 4h1v2h-1zM13 6h1v1h-1zM20 6h1v1h-1zM19 7h1v2h-1zM14 8h1v1h-1zM18 8h1v16h-1zM12 9h1v10h-1zM21 9h1v16h-1zM11 10h1v9h-1zM13 10h1v10h-1zM15 10h1v12h-1zM17 10h1v13h-1zM19 10h1v14h-1zM14 11h1v10h-1zM16 11h1v12h-1zM20 11h1v13h-1zM22 14h1v11h-1zM10 15h1v4h-1zM23 17h1v8h-1zM9 18h1v1h-1zM24 22h1v3h-1zM25 23h3v18h-3zM28 24h2v10h-2zM30 25h2v8h-2zM24 26h1v16h-1zM32 26h1v8h-1zM22 27h2v15h-2zM33 27h2v7h-2zM20 28h2v1h-2zM35 28h1v7h-1zM21 29h1v1h-1zM36 29h1v6h-1zM37 30h1v6h-1zM21 32h1v8h-1zM38 32h1v5h-1zM31 33h1v1h-1zM39 33h1v6h-1zM28 34h1v2h-1zM40 35h1v6h-1zM20 36h1v4h-1zM41 38h1v7h-1zM26 41h2v7h-2zM22 42h1v3h-1zM40 46h1v1h-1z"/>
<path id="c-b" fill="#4a2a1f" d="M12 6h1v3h-1zM13 7h1v3h-1zM20 7h1v4h-1zM14 9h1v2h-1zM19 9h1v1h-1zM18 40h4v8h-4zM17 41h1v7h-1zM25 41h1v7h-1zM23 42h2v6h-2zM16 45h1v3h-1zM22 45h1v3h-1zM15 47h1v1h-1z"/>
<path id="c-c" fill="#fbf3e4" d="M8 19h5v1h-5zM6 20h6v5h-6zM5 21h1v3h-1zM12 21h3v6h-3zM15 22h1v5h-1zM4 23h1v1h-1zM16 23h2v13h-2zM49 23h1v6h-1zM18 24h3v4h-3zM50 24h1v6h-1zM9 25h3v1h-3zM21 25h4v1h-4zM48 25h1v2h-1zM51 25h1v8h-1zM21 26h3v1h-3zM21 27h1v1h-1zM52 27h1v9h-1zM18 28h2v12h-2zM20 29h1v7h-1zM53 29h1v11h-1zM21 30h1v2h-1zM54 34h1v2h-1zM17 36h1v5h-1z"/>
<path id="c-k" fill="#2b1a14" d="M12 20h2v1h-2zM3 22h2v1h-2z"/>
<path id="c-d" fill="#c9551f" d="M48 28h1v18h-1zM49 29h1v16h-1zM50 30h1v15h-1zM47 31h1v15h-1zM46 32h1v15h-1zM30 33h1v15h-1zM51 33h1v11h-1zM29 34h1v14h-1zM31 34h4v14h-4zM45 34h1v13h-1zM35 35h2v13h-2zM43 35h2v12h-2zM28 36h1v12h-1zM37 36h1v12h-1zM41 36h2v2h-2zM52 36h1v7h-1zM38 37h1v11h-1zM42 38h1v9h-1zM39 39h1v8h-1zM40 41h1v5h-1zM41 45h1v2h-1z"/>
</g>
</svg>
examples/outputs/fox-risograph.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Riso Fox</title>
<desc id="d">Risograph: fluorescent pink and blue inks overprinting (multiply), halftone dot shading, slight misregistration, paper grain.</desc>
<defs>
<clipPath id="sheet">
<rect width="1024" height="1024"/>
</clipPath>
<pattern id="dot-p" width="14" height="14" patternUnits="userSpaceOnUse" patternTransform="rotate(15)">
<circle cx="7" cy="7" r="4.2" fill="#ff48b0"/>
</pattern>
<pattern id="dot-b" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(75)">
<circle cx="6" cy="6" r="3.1" fill="#0078bf"/>
</pattern>
<pattern id="dot-b2" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(75)">
<circle cx="5" cy="5" r="1.7" fill="#0078bf"/>
</pattern>
<filter id="grain" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="4"/>
<feColorMatrix values="0 0 0 0 0.25 0 0 0 0 0.2 0 0 0 0 0.15 0 0 0 -1.6 1.05"/>
<feComposite in2="SourceGraphic" operator="in"/>
</filter>
</defs>
<rect id="paper" width="1024" height="1024" fill="#f4efe4"/>
<g id="ink-blue" style="mix-blend-mode:multiply" clip-path="url(#sheet)">
<g transform="translate(-5 3)">
<path d="M268.4 330L241.6 80.8H290.3L304 238.1 318.1 80.8H364.5L377.8 238.1 391.1 80.8H440.4L413.2 330H353.5L341 210.1 329.1 330ZM459 330V80.8H507.2V330ZM533 330V80.8H582.8V287.5H634.1V330ZM654.2 330V80.8H722.8Q749.7 80.8 763.3 95.7 776.9 110.7 776.9 139.4V256.1Q776.9 291.5 764.5 310.7 752.1 330 721.1 330ZM704.4 286H713Q726.7 286 726.7 272.7V144.1Q726.7 131.6 723.4 128 720.1 124.4 709.9 124.4H704.4Z" fill="#0078bf" opacity="0.9"/>
<rect y="846" width="1024" height="100" fill="url(#dot-b2)"/>
<path d="M0 846H1024" stroke="#0078bf" stroke-width="8"/>
<path d="M282.8 985V950.6H296.6V957.3H289.7V962.8H296.2V969.4H289.7V985ZM317 985.3Q312.9 985.3 310.7 982.8 308.5 980.3 308.5 975.6V959.3Q308.5 954.9 310.7 952.6 312.8 950.3 317 950.3 321.2 950.3 323.3 952.6 325.5 954.9 325.5 959.3V975.6Q325.5 980.3 323.3 982.8 321.1 985.3 317 985.3ZM317 979Q318.6 979 318.6 976V959.2Q318.6 956.6 317.1 956.6 315.4 956.6 315.4 959.2V976Q315.4 977.6 315.8 978.3 316.2 979 317 979ZM337.2 985L340.9 966.8 338 950.6H344.7L346.5 962.4 348.1 950.6H354.8L351.9 966.8 355.6 985H348.6L346.4 971.6 344.2 985ZM395 985.3Q391.2 985.3 389 983 386.7 980.7 386.7 976.5V960.6Q386.7 955.6 388.7 953 390.7 950.3 395.1 950.3 397.6 950.3 399.5 951.2 401.4 952.1 402.5 953.9 403.6 955.7 403.6 958.4V964.3H396.7V959.3Q396.7 957.7 396.3 957.2 396 956.6 395.1 956.6 394.2 956.6 393.8 957.3 393.5 958 393.5 959.2V976.4Q393.5 977.8 393.9 978.4 394.4 979 395.1 979 396 979 396.4 978.3 396.7 977.5 396.7 976.4V970.2H403.7V976.7Q403.7 981.2 401.4 983.3 399.1 985.3 395 985.3ZM415.9 985V950.6H422.8V979.1H429.9V985ZM449.8 985.3Q445.5 985.3 443.5 982.9 441.4 980.5 441.4 975.8V950.6H448.1V975.5Q448.1 976.4 448.2 977.2 448.3 978 448.7 978.5 449 979 449.8 979 450.6 979 450.9 978.5 451.3 978 451.4 977.2 451.5 976.4 451.5 975.5V950.6H458.1V975.8Q458.1 980.5 456.1 982.9 454.1 985.3 449.8 985.3ZM470.8 985V950.6H480.1Q483.8 950.6 485.6 952.5 487.3 954.3 487.3 958.5V960Q487.3 962.4 486.4 963.9 485.5 965.4 483.7 965.9 486.1 966.5 486.9 968.6 487.7 970.8 487.7 973.9 487.7 977.2 487 979.7 486.4 982.2 484.8 983.6 483.1 985 480.1 985ZM477.5 963.6H478.9Q479.9 963.6 480.1 962.8 480.4 962.1 480.4 961.1V957.6Q480.4 955.9 478.9 955.9H477.5ZM478.2 978.9Q480.9 978.9 480.9 976.4V972.1Q480.9 970.6 480.4 969.8 480 968.9 478.8 968.9H477.5V978.9Q477.9 978.9 478.2 978.9ZM538.7 970.9V965.1H544.9V970.9ZM596.8 985V950.6H603.9L607 967.1V950.6H613.7V985H607L603.5 967.8V985ZM634.9 985.3Q630.8 985.3 628.6 982.8 626.4 980.3 626.4 975.6V959.3Q626.4 954.9 628.6 952.6 630.8 950.3 634.9 950.3 639.1 950.3 641.2 952.6 643.4 954.9 643.4 959.3V975.6Q643.4 980.3 641.2 982.8 639 985.3 634.9 985.3ZM635 979Q636.5 979 636.5 976V959.2Q636.5 956.6 635 956.6 633.3 956.6 633.3 959.2V976Q633.3 977.6 633.7 978.3 634.1 979 635 979ZM655.7 985V979.2H662.7V985ZM703 985.3Q698.7 985.3 696.3 982.9 693.9 980.4 693.9 975.8V960.3Q693.9 955.5 696.2 952.9 698.5 950.3 703 950.3 707.7 950.3 709.9 952.9 712.1 955.5 712.1 960.3V975.8Q712.1 980.4 709.8 982.9 707.4 985.3 703 985.3ZM703 979Q703.8 979 704.3 978.3 704.7 977.5 704.7 976.6V959.7Q704.7 958.5 704.5 957.5 704.2 956.6 703 956.6 701.9 956.6 701.6 957.5 701.3 958.5 701.3 959.7V976.6Q701.3 977.5 701.8 978.3 702.3 979 703 979ZM725.8 985Q726.1 980.2 727.1 976 728.1 971.8 729.5 968.2 730.8 964.6 732.1 961.6L734 957H724.3V950.6H741.6V954.4Q741.6 956.4 741 958.5 740.3 960.6 739.2 963.2 736.7 968.7 735.3 974.2 733.9 979.6 733.5 985Z" fill="#0078bf"/>
<g id="fox-blue">
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="url(#dot-b)"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#0078bf"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#0078bf"/>
<path d="M352 300L358 248 392 322Z" fill="#0078bf"/>
<path d="M452 306L468 262 478 342Z" fill="#0078bf"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#0078bf"/>
<circle cx="236" cy="488" r="11" fill="#0078bf"/>
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="url(#dot-b2)"/>
</g>
</g>
</g>
<g id="ink-pink" style="mix-blend-mode:multiply" transform="translate(4 -3)">
<circle cx="800" cy="210" r="120" fill="url(#dot-p)"/>
<g id="fox-pink" fill="#ff48b0">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z"/>
</g>
<g fill="#f4efe4">
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
<path d="M490 520L536 516 510 548 468 562Z"/>
</g>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="url(#dot-p)" opacity="0.35"/>
</g>
<rect id="grain-overlay" width="1024" height="1024" filter="url(#grain)" opacity="0.5" fill="#f4efe4"/>
</svg>
examples/outputs/fox-sticker.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Fox Sticker</title>
<desc id="d">Sticker: thick white die-cut border with soft shadow, saturated flat fills, dark outlines, glossy highlight.</desc>
<defs>
<g id="cut">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z"/>
</g>
<filter id="lift" x="-10%" y="-10%" width="120%" height="125%">
<feDropShadow dx="0" dy="14" stdDeviation="12" flood-color="#1c1c28" flood-opacity=".35"/>
</filter>
</defs>
<g id="border" filter="url(#lift)">
<use href="#cut" fill="#fff" stroke="#fff" stroke-width="64" stroke-linejoin="round"/>
</g>
<g id="art" stroke="#1c1c28" stroke-width="9" stroke-linejoin="round" stroke-linecap="round">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#ff6b35"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#fffaf0"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#ff7f3f"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#ff5d73" stroke-width="6"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#3b2230"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#fffaf0" stroke-width="6"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#2b1a24"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#ff7f3f"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#fffaf0" stroke-width="6"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#fffaf0" stroke-width="6"/>
<path d="M352 300L358 248 392 322Z" fill="#ff5d73" stroke-width="5"/>
<path d="M452 306L468 262 478 342Z" fill="#ff5d73" stroke-width="5"/>
</g>
<g id="face" fill="#1c1c28">
<ellipse cx="342" cy="452" rx="20" ry="14"/>
<circle cx="348" cy="447" r="5" fill="#fff"/>
<circle cx="238" cy="488" r="13"/>
<ellipse cx="385" cy="505" rx="22" ry="12" fill="#ff9fb0"/>
</g>
<g id="gloss" fill="#fff" opacity="0.55">
<path d="M470 540C560 520 640 560 700 630 690 640 676 644 664 640 620 590 560 564 488 566 478 560 470 552 470 540Z"/>
<circle cx="716" cy="668" r="9"/>
<path d="M372 280L384 250 396 292Z"/>
</g>
<g id="badge" transform="rotate(-10 820 250)">
<circle cx="820" cy="250" r="92" fill="#ffc857" stroke="#fff" stroke-width="18"/>
<circle cx="820" cy="250" r="92" fill="none" stroke="#1c1c28" stroke-width="7"/>
<path d="M761.1 278V214.4H773.8V237.1H779.8V214.4H792.4V278H779.8V249.2H773.8V278ZM800.1 278V214.4H825.5V226.7H813V239.1H825V251H813V265.6H826.3V278ZM840.2 278V257.1L830.1 214.4H842.7L846.2 235.9 849.8 214.4H862.3L852.2 257.1V278ZM869.2 260.7L866.7 214.4H879.7L877.1 260.7ZM866.7 278V267.2H879.7V278Z" fill="#1c1c28"/>
</g>
</svg>
examples/outputs/fox-sumi-e.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Ink Fox</title>
<desc id="d">Sumi-e: wet ink wash tail, single-gesture tapered brush strokes, mist, vast negative space and a vermilion seal.</desc>
<defs>
<linearGradient id="wash-g" x1="1" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#2a2a28"/>
<stop offset="0.55" stop-color="#6b6a64" stop-opacity="0.85"/>
<stop offset="1" stop-color="#b9b5a8" stop-opacity="0.3"/>
</linearGradient>
<linearGradient id="mist-g" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f4eddc" stop-opacity="0"/>
<stop offset="1" stop-color="#f4eddc"/>
</linearGradient>
<filter id="bleed" x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence type="fractalNoise" baseFrequency="0.04" numOctaves="2" seed="2"/>
<feDisplacementMap in="SourceGraphic" scale="14"/>
<feGaussianBlur stdDeviation="2.2"/>
</filter>
<filter id="dry" x="-5%" y="-5%" width="110%" height="110%">
<feTurbulence type="fractalNoise" baseFrequency="0.6 0.04" numOctaves="2" seed="9" result="n"/>
<feColorMatrix in="n" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.5 1.55" result="m"/>
<feComposite in="SourceGraphic" in2="m" operator="in"/>
</filter>
</defs>
<rect id="paper" width="1024" height="1024" fill="#f4eddc"/>
<g id="far" opacity="0.22" filter="url(#bleed)">
<path d="M0 700C160 640 260 690 380 650S600 610 700 660 900 680 1024 620V760H0Z" fill="#6b6a64"/>
</g>
<g id="wash" filter="url(#bleed)">
<path d="M691.9 838.7L704.2 844.5 717.2 851 731 852.7 744.5 856 758.2 858.6 771.8 857.6 785.6 860 799.5 860.3 813.5 860 825.6 853.2 839.4 851.3 851.9 845.7 867.5 845.3 879.8 838.5 886.8 824.5 900.5 819.3 908.9 807.8 914.8 794.7 921.3 782.4 924.7 768.5 929 755.6 935.9 744 936.9 729.9 940.1 716.7 942.1 703.2 942.1 689.3 944.1 675.7 943.3 661.5 943.2 647.3 939.8 633 937.1 618.5 932.9 604 928.7 589.3 922.8 574.7 916.4 560 910.4 544.8 902.5 529.9 893.7 515.1 884.1 500.1 873.7 485.1 870.3 486.9 876.7 503.8 882.1 520.3 886.4 536.4 889.9 552 893.7 566.8 895.4 581.6 896 595.8 896.7 609.4 895.8 622.5 895 634.9 892 646.8 890.9 658.1 887.8 668.7 886.1 679 881.2 688.2 876.9 697 872.7 705.4 865.2 711.8 862.3 719.8 855.8 725.5 847.4 729.2 841 733.6 833.2 736 826.8 739.1 824.9 747.3 815.4 745.6 810.6 749.8 808.2 759.3 801.9 762.4 796.3 768 788.4 767.3 781.8 771.8 774.5 775.6 766.7 780.1 758.2 779.8 749.2 781.7 739.8 782.9 730.3 780.9 719.7 782.3 708.1 781.3Z" fill="url(#wash-g)"/>
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#8c8a84" opacity="0.13"/>
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#8c8a84" opacity="0.1"/>
</g>
<g id="strokes" fill="#1a1a1a" filter="url(#dry)">
<path d="M350.6 221.8L349.5 226.1 348.6 230.3 347.8 234.6 346.8 238.8 345.9 243 344.9 247.2 343.9 251.4 343.3 255.7 342.4 259.9 341 264 340.3 268.3 339 272.5 338.6 276.8 337.3 281.1 336.3 285.4 336 289.9 334.6 294.2 333.9 298.8 333.8 303.4 333 308 331.7 312.6 330.5 317.3 330.2 322.2 329.4 327.1 328.3 332 327.7 337.1 326.6 342.2 325.4 347.3 324.3 352.6 323.5 358.1 322.5 363.6 321.4 369.2 320 374.9 319 380.8 318.1 386.8 316.1 392.8 315.1 399.1 313.7 405.4 312.8 412 310.6 418.5 325.4 421.5 325.9 414.6 327.6 408.2 328.7 401.7 330.2 395.5 330.5 389.2 331.9 383.3 333.2 377.4 334 371.6 335 366 336 360.4 337.3 355 338.1 349.7 338.9 344.4 339.6 339.2 340.8 334.2 341.5 329.2 342.4 324.3 343.7 319.6 344.2 314.7 344.5 310 345.2 305.3 346.7 300.9 347.5 296.4 347.5 291.8 348.6 287.4 349.1 282.9 349.1 278.5 350 274.2 350.1 269.8 350.7 265.5 350.5 261.1 350.9 256.8 351.6 252.5 351.8 248.2 352 243.9 352.3 239.6 352.5 235.3 353 231 353.3 226.6 353.4 222.2Z"/>
<path d="M351.1 222.5L352.9 226.2 354.9 229.8 356.7 233.5 358.2 237.2 359.9 240.7 361.6 244.3 363.3 247.7 364.7 251.2 366.3 254.7 367.8 258.1 369.4 261.3 370.7 264.7 372.1 268 373.3 271.4 374.6 274.6 376.1 277.6 377.8 280.6 379.3 283.5 380.8 286.5 382.4 289.3 383.7 292.2 384.8 295.2 386.2 298 387.4 300.8 388.4 303.7 389.8 306.4 391 309 392.4 311.6 393.7 314.1 394.7 316.8 396 319.2 396.9 321.8 397.7 324.4 399 326.7 400.4 328.9 401 331.5 402.2 333.7 403.3 336 404.2 338.2 405.4 340.3 414.6 335.7 413.7 333.5 412.5 331.5 411.5 329.3 410.4 327 408.9 324.9 408.1 322.5 407.1 320 405.7 317.7 404.3 315.4 403.2 312.8 401.8 310.4 400.8 307.7 399.7 305 398.4 302.4 397.4 299.6 395.8 297 394.4 294.2 393.2 291.3 391.5 288.6 390.1 285.7 388.9 282.6 387.5 279.6 386.1 276.6 384.8 273.4 383.3 270.3 381.6 267.2 379.6 264.3 377.8 261.1 375.8 258.1 374.1 254.8 372.1 251.6 370.1 248.4 368 245.2 366.1 241.8 364 238.5 361.9 235.1 359.5 231.9 357.3 228.4 355.2 224.9 352.9 221.5Z"/>
<path d="M466.2 240.7L467.3 245.3 468.5 249.8 469.5 254.3 470.5 258.7 471.8 262.9 472.7 267.2 473 271.6 474.2 275.7 475 279.9 475.1 284.3 476.2 288.3 476.5 292.6 477.4 296.7 477.8 300.9 479.4 304.9 479.9 309.2 480.6 313.5 481.9 317.7 482.7 322.1 483.7 326.5 485.2 330.8 485.4 335.5 486.5 340.2 487.7 344.9 488.9 349.7 489 354.8 490.3 359.9 491 365.2 491.8 370.6 492.9 376.1 493.7 381.8 495.2 387.6 495.8 393.6 497.2 399.8 497.8 406.2 499 412.7 500.8 419.4 501.9 426.3 503.1 433.5 503.7 441 516.3 439 514.6 431.7 513.4 424.5 512.4 417.6 512.1 410.7 511.2 404.1 509.8 397.8 509.3 391.5 508 385.6 507.7 379.6 506.7 373.9 506 368.3 505.1 362.9 504.1 357.6 503.7 352.4 502.1 347.5 501.6 342.5 501.1 337.6 500.5 332.8 499 328.3 498.8 323.5 498.1 319 497.1 314.6 496.6 310.1 495.5 305.8 494.1 301.6 493.7 297.2 492.1 293.1 491 288.9 489.2 284.9 488.1 280.7 485.9 276.9 484.3 272.8 483.1 268.6 480.8 264.7 479.2 260.5 477.7 256.3 475.8 252.1 473.8 247.9 471.9 243.6 469.8 239.3Z"/>
<path d="M232.9 487.1L237.2 483.9 241.5 480.8 245.5 477.8 249.3 474.9 253.1 472.2 256.8 469.7 260.2 467.2 263.4 464.8 266.4 462.3 269.5 460.3 272.3 458.1 275 456.1 277.6 454.1 280.2 452.4 282.8 450.9 285 449.1 287.2 447.5 289.3 445.9 291 444.1 293 442.8 295 441.6 296.8 440.4 298.5 439.2 300.2 438.1 301.5 436.6 303.3 435.8 304.9 434.8 306.3 433.8 307.7 432.7 309.2 432 310.5 430.9 312.3 430.6 313.7 429.8 315 428.8 316.4 427.9 318 427.6 319.4 426.7 321 426.2 322.6 425.5 324.1 424.5 319.9 415.5 318.2 416 316.5 416.8 315 417.8 313.4 418.5 312 419.7 310.4 420.4 308.8 421.1 307.2 422 306 423.4 304.4 424.2 303 425.3 301.3 426.2 299.7 427.2 298.2 428.3 296.8 429.7 294.8 430.5 293.2 431.8 291.5 433.2 289.7 434.6 288 436.2 286.1 437.8 284 439.1 281.9 440.7 279.8 442.4 277.5 444.2 275.4 446.4 273.2 448.5 270.7 450.6 268.1 452.9 265.4 455.1 262.7 457.7 259.6 460.1 256.6 462.7 253.4 465.5 250.1 468.5 246.7 471.6 243.1 474.7 239.2 477.9 235.3 481.4 231.1 484.9Z"/>
<path d="M235.2 493.3L240 496.4 244.9 499.4 249.9 502.5 255.1 505.3 260.3 508.3 265.8 510.8 271.2 513.8 276.7 516.7 282.4 519.3 288.1 522 294.1 524.2 300 526.9 306 529.3 311.9 531.9 318.1 534 324.4 535.6 330.7 537.7 336.9 539.6 343.1 541.9 349.4 543.5 355.9 544.9 362.1 546.9 368.5 548.1 374.8 549.9 381.2 551.1 387.5 552.4 393.6 554.2 399.9 555.1 406.1 556.3 412.1 557.8 418.2 558.8 424.3 559.5 430.2 560.8 436.2 561.4 442 562.3 447.8 562.7 453.4 563.7 458.9 564.2 464.4 564.8 469.7 565.2 470.3 558.8 465 558.2 459.6 557.7 454.2 556.9 448.6 556.5 443 555.4 437.2 554.5 431.5 553.3 425.5 552.6 419.6 551.2 413.7 549.9 407.5 549.1 401.5 547.7 395.4 546 389.2 545 383 543.4 376.9 541.5 370.6 540.2 364.5 538.1 358.2 536.7 352.1 534.5 345.9 532.5 339.6 530.9 333.4 529 327.3 527 321.3 524.6 315.3 522.4 309.1 520.7 303.1 518.6 297.1 516.7 291.3 514.2 285.4 512.2 279.7 509.9 273.9 507.9 268.2 505.7 262.8 503 257.4 500.7 252.1 498.2 246.9 495.8 241.8 493.3 236.8 490.7Z"/>
<path d="M511.3 475.2L517.7 476.8 524.1 478.3 530.4 480.2 536.8 482.1 542.9 485.4 549.2 487.9 555.3 491.3 561.8 494 567.7 498.1 574.2 501 579.9 505.8 586 509.8 592.4 513.7 598.2 518.5 604.5 522.9 611.4 526.4 617.3 531.7 623.3 537 630.3 541.3 635.8 547.5 642.2 553 648.1 559.1 655 564.4 661.4 570.5 666.2 578 672.1 584.8 678.5 591.4 684.7 598.3 690.2 606 695 614.3 701 622 705.2 630.9 711 639 716.8 647.4 721 656.7 726.3 665.7 730 675.6 734.6 685.2 739.7 694.7 743.7 705.1 768.3 694.9 763.8 684.5 760 673.7 755.4 663.5 749.6 654 745.1 643.9 739.3 634.8 734.7 625.1 729.9 615.7 723.3 607.5 718.2 598.5 711.8 590.6 705.7 582.6 700.3 574.3 694.8 566.1 688.5 558.7 681.2 552.4 675.2 545.1 669.4 537.7 662.6 531.4 656.2 525 648.7 519.8 642.7 513.2 635.5 508.1 628.1 503.5 621.8 497.6 614.6 493.1 607 489.5 599.8 485.4 592.4 482.1 584.5 479.9 577.5 476.5 569.8 474.6 562.7 471.9 555.2 470.4 548 468.6 540.6 468 533.5 466.7 526.5 465.7 519.5 465 512.7 464.8Z"/>
<path d="M743.8 703.4L745.9 709.1 746.5 714.7 748.7 719.9 748.9 725.3 750.1 730.4 751.2 735.4 750.8 740.4 751.8 745.3 751.1 750.1 751.4 754.8 750.9 759.4 749.8 763.7 748.3 767.9 748.3 772.4 747.1 776.6 745.2 780.4 743.4 784.1 742.2 788.2 740.5 792.1 737.8 795.3 736.5 799.6 733.2 802.3 731 806.1 728.1 809.4 724.9 812.5 721.4 815.3 717.5 817.7 714 820.9 709.7 823.1 705.4 825.4 701 827.8 696.2 829.7 691.3 831.9 686 833.5 680.6 834.9 674.8 835.9 669 837.1 662.9 838 656.5 838.5 649.9 839 650.1 845 656.8 845.2 663.4 845.1 669.8 844.8 676.1 844.5 682.2 843.4 688.1 842.4 693.9 841.2 699.6 840.1 705 838.4 710.4 836.7 715.5 834.6 720.3 832 725.4 830 729.7 826.9 733.9 823.8 738.1 820.7 742.2 817.4 746.5 814.3 749.4 809.9 753.7 806.7 756.2 802.1 759.4 797.9 762.6 793.6 764.7 788.7 766.4 783.6 768.2 778.6 770.9 773.7 771.6 768.2 772.2 762.6 773 757.1 774.2 751.4 773.9 745.6 774.8 739.8 773.9 733.8 774 727.8 773.6 721.6 771.9 715.5 771.8 709 769.5 702.9 768.2 696.6Z"/>
<path d="M411.6 557.8L409.2 563.7 408.8 570.1 406.8 576.1 406.3 582.5 405.1 588.6 403.2 594.7 403.3 601 402.1 607.2 401.6 613.5 401.1 619.7 400.6 626 400.6 632.4 399.6 638.7 400.3 645.1 400.2 651.4 400.6 657.9 400.5 664.3 400.6 670.8 400.5 677.4 401.4 684 402.1 690.6 402.5 697.3 402.4 704.1 403.8 710.8 404 717.7 405 724.6 405.5 731.7 406.5 738.7 407.1 745.9 408.1 753.1 408.9 760.4 409.6 767.8 410.7 775.2 411.9 782.8 412.9 790.4 413.4 798.2 414.6 806 415.7 814 416.4 822.1 417.5 830.2 422.5 829.8 422.1 821.5 421.3 813.5 420.8 805.4 420.5 797.5 419.5 789.8 419 782.1 418.8 774.5 418.5 766.9 417.8 759.6 417.3 752.2 417 745 416.3 737.9 416.2 730.8 415.7 723.8 415.6 716.9 414.8 710.1 415.4 703.3 414.6 696.7 414.4 690.1 414.6 683.5 415.1 677 414.7 670.6 414.7 664.3 414.7 658 415.2 651.7 415.4 645.5 416.5 639.4 416.2 633.3 417 627.2 417.5 621.2 418.2 615.2 419.1 609.3 419.4 603.3 421.4 597.5 421.5 591.5 422.4 585.6 424.5 579.9 425.1 573.9 427.6 568.3 428.4 562.2Z"/>
<path d="M460.1 743.2L460.6 746.8 461.4 750.3 460.7 753.9 462.3 757.1 462 760.6 461.8 763.9 463.4 767 463.1 770.3 463.3 773.4 464.6 776.3 464.5 779.3 464.4 782.3 465.1 785.2 465.1 788 466.7 790.6 467.3 793.3 468.2 795.9 468.3 798.5 468.5 801.1 469.7 803.5 470.1 805.9 471.4 808.2 471.6 810.6 473.1 812.7 473.6 814.9 473.9 817.2 474.6 819.3 475.6 821.3 476.7 823.2 477.2 825.3 478 827.2 478.6 829.1 478.9 831.1 480.1 832.8 480.3 834.7 481 836.5 482.3 838 482.8 839.8 483.4 841.4 484.4 843 491.6 841 491.7 839.2 491.4 837.5 491 835.8 491.4 833.8 491.2 832 490.5 830.3 490.8 828.2 490.2 826.4 489.9 824.4 489.8 822.4 489.4 820.4 489.6 818.3 489.7 816.1 489.5 813.9 488.9 811.8 488.5 809.6 489.1 807.1 488.4 804.9 488.8 802.4 488.3 800 488.6 797.5 487.9 795 487.1 792.6 487.1 789.9 486.8 787.3 487.5 784.4 486.6 781.7 486.4 778.9 485.4 776.1 484.4 773.3 484.8 770.2 484.1 767.2 482.9 764.2 483.6 760.9 482.5 757.8 481.3 754.6 482 751.1 480.4 747.8 480.3 744.3 479.9 740.8Z"/>
<path d="M512.3 772.6L513.2 774.7 513.8 777 514.9 779.1 515 781.5 515.7 783.6 516.6 785.7 517.8 787.7 518.1 789.9 519 791.9 519.7 794 519.6 796.3 520.2 798.3 521.8 800 521.5 802.2 522.6 804 523.6 805.8 524.5 807.7 524.8 809.6 525.7 811.3 526.2 813.2 527 814.9 528.1 816.5 528.2 818.4 528.7 820.1 529.4 821.7 530.3 823.2 531.3 824.7 531.9 826.2 532.4 827.8 532.3 829.4 533.3 830.7 533.8 832.2 533.9 833.6 534.7 834.9 535.1 836.3 535.5 837.6 536.1 838.8 536.3 840 536.5 841.2 537.2 842.4 542.8 841.6 543.2 840.3 543 839 542.8 837.7 542.9 836.3 542.9 834.8 542.7 833.4 542.9 831.8 542.5 830.3 542.3 828.8 542.5 827 541.8 825.5 541.5 823.9 541.3 822.2 541.5 820.3 541.5 818.5 541.3 816.7 540.8 814.9 539.9 813.2 540.1 811.2 539.8 809.3 539.2 807.5 539 805.4 538.3 803.6 538 801.5 537.8 799.4 537.6 797.3 536.1 795.6 536.5 793.2 535.8 791.2 534.4 789.3 533.8 787.2 533.4 785 532.3 783 532.1 780.7 531.6 778.5 530.9 776.3 529.5 774.3 529.2 771.9 528.3 769.7 527.7 767.4Z"/>
</g>
<g id="accents" fill="#1a1a1a">
<path d="M318 452Q340 438 364 450 340 458 318 452Z"/>
<circle cx="238" cy="488" r="9"/>
<path d="M364 300L368 262 386 318Z" opacity="0.7"/>
<path d="M458 300L468 268 474 330Z" opacity="0.7"/>
</g>
<rect id="mist" y="780" width="1024" height="244" fill="url(#mist-g)"/>
<g id="seal" transform="rotate(-3 880 830)">
<rect x="840" y="780" width="84" height="100" rx="4" fill="#c1272d"/>
<g fill="#f4eddc">
<rect x="852" y="794" width="60" height="8"/>
<rect x="878" y="794" width="8" height="72"/>
<rect x="852" y="826" width="60" height="7"/>
<path d="M856 866L878 842 884 848 864 872Z"/>
<path d="M908 866L886 842 880 848 900 872Z"/>
</g>
</g>
</svg>
examples/outputs/fox-ukiyo-e-darker.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Fox at Night</title>
<desc id="d">Ukiyo-e, darker modifier: flat colour blocks, black key-block outlines, bokashi sky, kasumi mist, seigaiha waves, red cartouche.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0f1a33"/>
<stop offset="1" stop-color="#3b4a6b"/>
</linearGradient>
<pattern id="seigaiha" width="80" height="40" patternUnits="userSpaceOnUse">
<rect width="80" height="40" fill="#16284a"/>
<g fill="none" stroke="#9fb0c8" stroke-width="3">
<path id="wv" d="M0 40a40 40 0 0 1 80 0M14 40a26 26 0 0 1 52 0M28 40a12 12 0 0 1 24 0"/>
<use href="#wv" x="-40" y="-20"/>
<use href="#wv" x="40" y="-20"/>
</g>
</pattern>
</defs>
<rect id="paper" width="1024" height="1024" fill="#e4d5b0"/>
<rect id="sky-band" width="1024" height="640" fill="url(#sky)"/>
<circle id="moon" cx="760" cy="290" r="116" fill="#f1e4b8"/>
<g id="kasumi" fill="#4c5b7a" opacity="0.85">
<path d="M0 610C120 580 230 640 350 610S560 580 640 620 860 640 1024 600V690C900 720 780 670 660 700S420 730 300 690 90 700 0 680Z"/>
<path d="M420 470C520 450 600 490 700 470S860 450 1024 480V520C900 540 800 510 700 530S520 520 420 510Z" fill="#2c3a5a"/>
</g>
<g id="cartouche">
<rect x="70" y="70" width="86" height="270" fill="#c9412c" stroke="#1c1a1a" stroke-width="5"/>
<rect x="82" y="82" width="62" height="246" fill="#e8dcbc"/>
<g fill="none" stroke="#1c1a1a" stroke-width="7" stroke-linecap="round" stroke-linejoin="round">
<path d="M96 118h34M113 104v44M96 150q17-14 34 0"/>
<path d="M96 200h34M96 232h34M113 190v70M98 260l30 26"/>
</g>
<rect x="94" y="300" width="26" height="20" fill="#c9412c"/>
</g>
<g id="water">
<rect y="840" width="1024" height="184" fill="url(#seigaiha)"/>
<rect y="837" width="1024" height="6" fill="#1c1a1a"/>
</g>
<g id="grass" fill="none" stroke="#1c1a1a" stroke-width="5" stroke-linecap="round">
<path d="M140 840q4-30-6-46M150 840q2-22-3-30"/>
<path d="M300 840q4-30 8-46M310 840q2-22 4-30"/>
<path d="M560 840q4-30-8-46M570 840q2-22-4-30"/>
<path d="M620 840q4-30 8-46M630 840q2-22 4-30"/>
<path d="M980 840q4-30-6-46M990 840q2-22-3-30"/>
<path d="M90 840q4-30 6-46M100 840q2-22 3-30"/>
</g>
<g id="fox" stroke="#1c1a1a" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<g id="tail">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#a6431f"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#e8dcbc"/>
<g fill="none" stroke-width="4">
<path d="M800 560l34-30"/>
<path d="M830 600l36-34"/>
<path d="M790 620l40-30"/>
<path d="M830 660l40-30"/>
<path d="M800 700l38-24"/>
<path d="M860 720l30-30"/>
<path d="M760 740l34-20"/>
<path d="M840 770l30-22"/>
<path d="M890 690l22-30"/>
<path d="M880 610l26-30"/>
</g>
</g>
<g id="body">
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#c2542a"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#a6431f"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#3a2019"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#e8dcbc"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#3a2019"/>
<g fill="none" stroke-width="4">
<path d="M560 560l30 10"/>
<path d="M600 590l30 12"/>
<path d="M650 640l26 18"/>
<path d="M690 700l20 22"/>
<path d="M620 700l22 20"/>
<path d="M660 770l14 20"/>
</g>
</g>
<g id="head">
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#c2542a"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#e8dcbc"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#e8dcbc"/>
<path d="M352 300L358 248 392 322Z" fill="#3a2019"/>
<path d="M452 306L468 262 478 342Z" fill="#3a2019"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#1c1a1a"/>
<circle cx="236" cy="488" r="10" fill="#1c1a1a"/>
</g>
</g>
</svg>
examples/outputs/fox-ukiyo-e.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Fox at Dusk</title>
<desc id="d">Ukiyo-e style: flat colour blocks, black key-block outlines, bokashi sky, kasumi mist, seigaiha waves, red cartouche.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#9fc0c4"/>
<stop offset="1" stop-color="#efe3c4"/>
</linearGradient>
<pattern id="seigaiha" width="80" height="40" patternUnits="userSpaceOnUse">
<rect width="80" height="40" fill="#2b4a6b"/>
<g fill="none" stroke="#e9dfc4" stroke-width="3">
<path id="wv" d="M0 40a40 40 0 0 1 80 0M14 40a26 26 0 0 1 52 0M28 40a12 12 0 0 1 24 0"/>
<use href="#wv" x="-40" y="-20"/>
<use href="#wv" x="40" y="-20"/>
</g>
</pattern>
</defs>
<rect id="paper" width="1024" height="1024" fill="#efe3c4"/>
<rect id="sky-band" width="1024" height="640" fill="url(#sky)"/>
<circle id="sun" cx="760" cy="290" r="116" fill="#c9412c"/>
<g id="kasumi" fill="#f5ecd2">
<path d="M0 610C120 580 230 640 350 610S560 580 640 620 860 640 1024 600V690C900 720 780 670 660 700S420 730 300 690 90 700 0 680Z"/>
<path d="M420 470C520 450 600 490 700 470S860 450 1024 480V520C900 540 800 510 700 530S520 520 420 510Z" fill="#e6dab9"/>
</g>
<g id="cartouche">
<rect x="70" y="70" width="86" height="270" fill="#c9412c" stroke="#1c1a1a" stroke-width="5"/>
<rect x="82" y="82" width="62" height="246" fill="#f5ecd2"/>
<g fill="none" stroke="#1c1a1a" stroke-width="7" stroke-linecap="round" stroke-linejoin="round">
<path d="M96 118h34M113 104v44M96 150q17-14 34 0"/>
<path d="M96 200h34M96 232h34M113 190v70M98 260l30 26"/>
</g>
<rect x="94" y="300" width="26" height="20" fill="#c9412c"/>
</g>
<g id="water">
<rect y="840" width="1024" height="184" fill="url(#seigaiha)"/>
<rect y="837" width="1024" height="6" fill="#1c1a1a"/>
</g>
<g id="grass" fill="none" stroke="#1c1a1a" stroke-width="5" stroke-linecap="round">
<path d="M140 840q4-30-6-46M150 840q2-22-3-30"/>
<path d="M300 840q4-30 8-46M310 840q2-22 4-30"/>
<path d="M560 840q4-30-8-46M570 840q2-22-4-30"/>
<path d="M620 840q4-30 8-46M630 840q2-22 4-30"/>
<path d="M980 840q4-30-6-46M990 840q2-22-3-30"/>
<path d="M90 840q4-30 6-46M100 840q2-22 3-30"/>
</g>
<g id="fox" stroke="#1c1a1a" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<g id="tail">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#d8642c"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#f5ecd2"/>
<g fill="none" stroke-width="4">
<path d="M800 560l34-30"/>
<path d="M830 600l36-34"/>
<path d="M790 620l40-30"/>
<path d="M830 660l40-30"/>
<path d="M800 700l38-24"/>
<path d="M860 720l30-30"/>
<path d="M760 740l34-20"/>
<path d="M840 770l30-22"/>
<path d="M890 690l22-30"/>
<path d="M880 610l26-30"/>
</g>
</g>
<g id="body">
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#e9712f"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#d8642c"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#3a2019"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#f5ecd2"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#3a2019"/>
<g fill="none" stroke-width="4">
<path d="M560 560l30 10"/>
<path d="M600 590l30 12"/>
<path d="M650 640l26 18"/>
<path d="M690 700l20 22"/>
<path d="M620 700l22 20"/>
<path d="M660 770l14 20"/>
</g>
</g>
<g id="head">
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#e9712f"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#f5ecd2"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#f5ecd2"/>
<path d="M352 300L358 248 392 322Z" fill="#3a2019"/>
<path d="M452 306L468 262 478 342Z" fill="#3a2019"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#1c1a1a"/>
<circle cx="236" cy="488" r="10" fill="#1c1a1a"/>
</g>
</g>
</svg>
examples/outputs/fox.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">Red fox</title>
<desc id="d">Neutral flat illustration of a seated red fox; the layout reference for the style set.</desc>
<rect width="1024" height="1024" fill="#f4efe6"/>
<g id="ground">
<ellipse cx="640" cy="846" rx="380" ry="16" fill="#e5dccb"/>
</g>
<g id="tail">
<path d="M700 800C770 846 880 838 924 752 962 676 940 560 872 486 866 566 842 628 790 664 758 686 730 694 700 694Z" fill="#d9642b"/>
<path d="M872 486C940 560 962 676 926 742 922 680 900 600 850 540 858 522 866 504 872 486Z" fill="#fbf3e4"/>
</g>
<g id="body">
<path d="M430 500C520 470 610 500 690 580 750 640 780 720 760 800 752 828 736 840 700 840L470 840C450 840 446 822 452 806L436 700C422 640 404 590 410 540Z" fill="#e9712f"/>
<path d="M600 640C680 640 750 700 752 780 752 816 736 840 700 840L580 840C560 800 556 700 600 640Z" fill="#d9642b"/>
<path d="M500 760L540 752 548 826C550 838 544 844 532 844L484 844C486 830 494 822 498 800Z" fill="#4a2a1f"/>
<path d="M410 545C440 562 472 570 490 600 470 650 458 700 458 770L426 770C412 700 396 610 410 545Z" fill="#fbf3e4"/>
<path d="M424 740L456 730 482 730 490 822C494 836 486 844 472 844L404 844C388 844 388 826 404 820L418 790Z" fill="#3a2019"/>
</g>
<g id="head">
<path d="M232 486C262 462 292 438 318 420L334 352 352 222 410 338 468 240 492 362C510 392 514 430 512 462L536 516 488 528 468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#e9712f"/>
<path d="M490 520L536 516 510 548 468 562Z" fill="#fbf3e4"/>
<path d="M232 486C262 462 292 438 318 430 340 440 372 470 420 492 452 508 480 516 488 528L468 562C430 552 380 552 340 540 312 534 282 520 256 512 242 504 234 496 232 486Z" fill="#fbf3e4"/>
<path d="M352 300L358 248 392 322Z" fill="#3a2019"/>
<path d="M452 306L468 262 478 342Z" fill="#3a2019"/>
<path d="M318 452Q338 436 362 448 340 462 318 452Z" fill="#2b1a14"/>
<circle cx="236" cy="488" r="10" fill="#2b1a14"/>
</g>
</svg>
examples/outputs/island-isometric.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="t d">
<title id="t">Isometric Island</title>
<desc id="d">Isometric: 30-degree axonometric island town, three-tone faces from a single top-left light, no perspective.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#bfe3f2"/>
<stop offset="1" stop-color="#f6f1e7"/>
</linearGradient>
</defs>
<rect id="sky-bg" width="1600" height="900" fill="url(#sky)"/>
<g id="clouds" fill="#fff" opacity="0.9">
<ellipse cx="300" cy="170" rx="120" ry="34"/>
<ellipse cx="350" cy="148" rx="70" ry="40"/>
<ellipse cx="1280" cy="220" rx="140" ry="36"/>
<ellipse cx="1240" cy="196" rx="70" ry="40"/>
</g>
<ellipse id="shadow" cx="800" cy="790" rx="470" ry="66" fill="#1d3557" opacity="0.12"/>
<g id="island">
<polygon points="800,200 1236.5,452 800,704 363.5,452" fill="#8bd17c"/>
<polygon points="363.5,495.2 800,747.2 800,704 363.5,452" fill="#5f9e56"/>
<polygon points="1236.5,495.2 800,747.2 800,704 1236.5,452" fill="#4c8446"/>
<polygon points="800,243.2 1236.5,495.2 800,747.2 363.5,495.2" fill="#c9a26b"/>
<polygon points="363.5,574.4 800,826.4 800,747.2 363.5,495.2" fill="#a57d4d"/>
<polygon points="1236.5,574.4 800,826.4 800,747.2 1236.5,495.2" fill="#8a663c"/>
<polygon points="581.8,378.2 924.7,576.2 862.4,612.2 519.4,414.2" fill="#d8d2c4"/>
<polygon points="519.4,416 862.4,614 862.4,612.2 519.4,414.2" fill="#bdb5a3"/>
<polygon points="924.7,578 862.4,614 862.4,612.2 924.7,576.2" fill="#aaa190"/>
<polygon points="1018.2,378.2 1080.6,414.2 862.4,540.2 800,504.2" fill="#d8d2c4"/>
<polygon points="800,506 862.4,542 862.4,540.2 800,504.2" fill="#bdb5a3"/>
<polygon points="1080.6,416 862.4,542 862.4,540.2 1080.6,414.2" fill="#aaa190"/>
<polygon points="800,92 924.7,164 800,236 675.3,164" fill="#f2f0ea"/>
<polygon points="675.3,344 800,416 800,236 675.3,164" fill="#d9d4c9"/>
<polygon points="924.7,344 800,416 800,236 924.7,164" fill="#bdb6a8"/>
<polygon points="684.6,317 697.1,324.2 697.1,304.4 684.6,297.2" fill="#4a90c2"/>
<polygon points="715.8,335 728.3,342.2 728.3,322.4 715.8,315.2" fill="#4a90c2"/>
<polygon points="747,353 759.5,360.2 759.5,340.4 747,333.2" fill="#4a90c2"/>
<polygon points="778.2,371 790.6,378.2 790.6,358.4 778.2,351.2" fill="#4a90c2"/>
<polygon points="684.6,281 697.1,288.2 697.1,268.4 684.6,261.2" fill="#4a90c2"/>
<polygon points="715.8,299 728.3,306.2 728.3,286.4 715.8,279.2" fill="#4a90c2"/>
<polygon points="747,317 759.5,324.2 759.5,304.4 747,297.2" fill="#4a90c2"/>
<polygon points="778.2,335 790.6,342.2 790.6,322.4 778.2,315.2" fill="#4a90c2"/>
<polygon points="684.6,245 697.1,252.2 697.1,232.4 684.6,225.2" fill="#4a90c2"/>
<polygon points="715.8,263 728.3,270.2 728.3,250.4 715.8,243.2" fill="#4a90c2"/>
<polygon points="747,281 759.5,288.2 759.5,268.4 747,261.2" fill="#4a90c2"/>
<polygon points="778.2,299 790.6,306.2 790.6,286.4 778.2,279.2" fill="#4a90c2"/>
<polygon points="684.6,209 697.1,216.2 697.1,196.4 684.6,189.2" fill="#4a90c2"/>
<polygon points="715.8,227 728.3,234.2 728.3,214.4 715.8,207.2" fill="#4a90c2"/>
<polygon points="747,245 759.5,252.2 759.5,232.4 747,225.2" fill="#4a90c2"/>
<polygon points="778.2,263 790.6,270.2 790.6,250.4 778.2,243.2" fill="#4a90c2"/>
<polygon points="800,59.6 949.6,146 800,232.4 650.4,146" fill="#e76f51"/>
<polygon points="650.4,164 800,250.4 800,232.4 650.4,146" fill="#c65a3f"/>
<polygon points="949.6,164 800,250.4 800,232.4 949.6,146" fill="#a84a33"/>
<polygon points="1080.6,362 1174.1,416 1080.6,470 987.1,416" fill="#f4d58d"/>
<polygon points="987.1,488 1080.6,542 1080.6,470 987.1,416" fill="#d9b56a"/>
<polygon points="1174.1,488 1080.6,542 1080.6,470 1174.1,416" fill="#bf9a52"/>
<polygon points="996.4,461 1008.9,468.2 1008.9,448.4 996.4,441.2" fill="#6b4b33"/>
<polygon points="1027.6,479 1040.1,486.2 1040.1,466.4 1027.6,459.2" fill="#6b4b33"/>
<polygon points="1058.8,497 1071.2,504.2 1071.2,484.4 1058.8,477.2" fill="#6b4b33"/>
<polygon points="1080.6,362 1174.1,416 1127.3,392.6 1033.8,338.6" fill="#c65a3f"/>
<polygon points="1033.8,338.6 1127.3,392.6 1080.6,470 987.1,416" fill="#e76f51"/>
<polygon points="1174.1,416 1080.6,470 1127.3,392.6" fill="#c65a3f"/>
<polygon points="519.4,362 612.9,416 535,461 441.5,407" fill="#a8dadc"/>
<polygon points="441.5,479 535,533 535,461 441.5,407" fill="#86b9bc"/>
<polygon points="612.9,488 535,533 535,461 612.9,416" fill="#6e9fa2"/>
<polygon points="450.8,452 463.3,459.2 463.3,439.4 450.8,432.2" fill="#1d3557"/>
<polygon points="482,470 494.5,477.2 494.5,457.4 482,450.2" fill="#1d3557"/>
<polygon points="513.2,488 525.7,495.2 525.7,475.4 513.2,468.2" fill="#1d3557"/>
<polygon points="519.4,362 612.9,416 574,395.3 480.4,341.3" fill="#35627e"/>
<polygon points="480.4,341.3 574,395.3 535,461 441.5,407" fill="#457b9d"/>
<polygon points="612.9,416 535,461 574,395.3" fill="#35627e"/>
<polygon points="800,470 877.9,515 800,560 722.1,515" fill="#ffb4a2"/>
<polygon points="722.1,641 800,686 800,560 722.1,515" fill="#e5989b"/>
<polygon points="877.9,641 800,686 800,560 877.9,515" fill="#c9797d"/>
<polygon points="731.4,614 743.9,621.2 743.9,601.4 731.4,594.2" fill="#6d597a"/>
<polygon points="762.6,632 775.1,639.2 775.1,619.4 762.6,612.2" fill="#6d597a"/>
<polygon points="731.4,578 743.9,585.2 743.9,565.4 731.4,558.2" fill="#6d597a"/>
<polygon points="762.6,596 775.1,603.2 775.1,583.4 762.6,576.2" fill="#6d597a"/>
<polygon points="800,448.4 890.4,500.6 800,552.8 709.6,500.6" fill="#6d597a"/>
<polygon points="709.6,515 800,567.2 800,552.8 709.6,500.6" fill="#574766"/>
<polygon points="890.4,515 800,567.2 800,552.8 890.4,500.6" fill="#433652"/>
</g>
<g id="trees">
<g transform="translate(955.9 290)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(899.8 365.6)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(1024.5 509.6)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(588 311.6)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(603.6 511.4)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(959 551)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
<g transform="translate(781.3 340.4)">
<rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>
<circle cy="-48" r="26" fill="#3f8f5a"/>
<circle cx="-8" cy="-56" r="14" fill="#62b27a"/>
</g>
</g>
</svg>
examples/outputs/landscape-low-poly.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="t d">
<title id="t">Low Poly Dusk</title>
<desc id="d">Low poly landscape: three triangulated mountain ranges under a dusk sky with a faceted sun.</desc>
<g id="mesh" stroke-width="0.8" stroke-linejoin="round">
<polygon points="600,900 500,900 509,893.9" fill="#262454" stroke="#262454"/>
<polygon points="0.7,24.6 0,90 0,0" fill="#27254a" stroke="#27254a"/>
<polygon points="0,180 0,90 44.5,114.2" fill="#4d3a55" stroke="#4d3a55"/>
<polygon points="341,22.8 300,0 400,0" fill="#1d1f4a" stroke="#1d1f4a"/>
<polygon points="600,0 700,0 629.8,3.4" fill="#1b1e4a" stroke="#1b1e4a"/>
<polygon points="20.3,866.1 100,900 0,900" fill="#252351" stroke="#252351"/>
<polygon points="0,810 20.3,866.1 0,900" fill="#262454" stroke="#262454"/>
<polygon points="464.9,411.7 379.2,387.2 443.9,341.1" fill="#624883" stroke="#624883"/>
<polygon points="379.2,387.2 386.5,352.2 443.9,341.1" fill="#5d447c" stroke="#5d447c"/>
<polygon points="615.4,245.2 682.1,238.9 622.2,278.5" fill="#7f5660" stroke="#7f5660"/>
<polygon points="567.2,858.4 600,900 509,893.9" fill="#201e46" stroke="#201e46"/>
<polygon points="288.4,372.6 267.9,378.9 269.7,357.2" fill="#a96c66" stroke="#a96c66"/>
<polygon points="239,277.6 231.5,220.5 252,269.8" fill="#7c535d" stroke="#7c535d"/>
<polygon points="231.5,220.5 239,277.6 185.2,263.8" fill="#754f58" stroke="#754f58"/>
<polygon points="255.2,369.9 267.9,378.9 190.8,401.9" fill="#a084a5" stroke="#a084a5"/>
<polygon points="240,349.2 255.2,369.9 190.8,401.9" fill="#9f84a5" stroke="#9f84a5"/>
<polygon points="267.9,378.9 255.2,369.9 269.7,357.2" fill="#a56964" stroke="#a56964"/>
<polygon points="0.2,272.6 0,360 0,270" fill="#88595d" stroke="#88595d"/>
<polygon points="0,360 0.2,272.6 5.8,306.4" fill="#8c5b5d" stroke="#8c5b5d"/>
<polygon points="200,0 300,0 248.1,8" fill="#1b1e49" stroke="#1b1e49"/>
<polygon points="118.2,51.1 81.7,71 105,3.3" fill="#2c2850" stroke="#2c2850"/>
<polygon points="118.2,51.1 172.2,68.5 121.9,107.2" fill="#352c4d" stroke="#352c4d"/>
<polygon points="81.7,71 118.2,51.1 121.9,107.2" fill="#392f52" stroke="#392f52"/>
<polygon points="32.5,77.5 81.7,71 44.5,114.2" fill="#392f4e" stroke="#392f4e"/>
<polygon points="81.7,71 32.5,77.5 0.7,24.6" fill="#322c51" stroke="#322c51"/>
<polygon points="756.8,385.9 789.2,374.5 799.7,415.3" fill="#a286a8" stroke="#a286a8"/>
<polygon points="344.6,43.3 341,22.8 400,0" fill="#24244f" stroke="#24244f"/>
<polygon points="416.9,36.3 400,0 500,0" fill="#1f214b" stroke="#1f214b"/>
<polygon points="431.8,52.9 416.9,36.3 500,0" fill="#282650" stroke="#282650"/>
<polygon points="416.9,36.3 344.6,43.3 400,0" fill="#24244c" stroke="#24244c"/>
<polygon points="399.5,157.8 438.3,64 471.8,116.5" fill="#463654" stroke="#463654"/>
<polygon points="742.4,28.7 700,0 800,0" fill="#1f224e" stroke="#1f224e"/>
<polygon points="720,167.8 722.3,116.7 749,166.8" fill="#513b52" stroke="#513b52"/>
<polygon points="14,781.3 0,810 0,720" fill="#262454" stroke="#262454"/>
<polygon points="464.9,411.7 401.3,437.2 379.2,387.2" fill="#74569c" stroke="#74569c"/>
<polygon points="400,900 430.9,889.3 500,900" fill="#23214d" stroke="#23214d"/>
<polygon points="415.3,704.2 453.7,662.9 445.9,705.1" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="682.1,238.9 695.3,256.6 622.2,278.5" fill="#7c535d" stroke="#7c535d"/>
<polygon points="556.2,399.6 586.5,409.5 572.4,428.4" fill="#b16f65" stroke="#b16f65"/>
<polygon points="586.5,409.5 616.7,438.1 572.4,428.4" fill="#b87367" stroke="#b87367"/>
<polygon points="616.7,438.1 586.5,409.5 647.4,388.3" fill="#b27065" stroke="#b27065"/>
<polygon points="691.7,456.4 647.4,388.3 714.9,416.3" fill="#b37064" stroke="#b37064"/>
<polygon points="691.7,456.4 616.7,438.1 647.4,388.3" fill="#c37a6c" stroke="#c37a6c"/>
<polygon points="646,866.5 700,900 600,900" fill="#262455" stroke="#262455"/>
<polygon points="500,900 484.2,849.2 509,893.9" fill="#29275a" stroke="#29275a"/>
<polygon points="430.9,889.3 484.2,849.2 500,900" fill="#201e47" stroke="#201e47"/>
<polygon points="484.2,849.2 430.9,889.3 451.2,809.3" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1338,513.9 1313.1,499.1 1329.2,477.4" fill="#5e457e" stroke="#5e457e"/>
<polygon points="1403.7,382.9 1412.5,326.1 1491.4,396.3" fill="#a76b65" stroke="#a76b65"/>
<polygon points="1582.7,651.6 1600,630 1600,720" fill="#1d1b40" stroke="#1d1b40"/>
<polygon points="1474.9,618.8 1431.7,599.9 1490.4,566.7" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1593.4,425.5 1600,360 1600,450" fill="#b27065" stroke="#b27065"/>
<polygon points="1593.4,425.5 1557.9,398.4 1600,360" fill="#b37168" stroke="#b37168"/>
<polygon points="1600,360 1594.8,294.9 1600,270" fill="#966265" stroke="#966265"/>
<polygon points="1592.1,304.1 1594.8,294.9 1600,360" fill="#966163" stroke="#966163"/>
<polygon points="1264.5,875.1 1300,900 1200,900" fill="#22204a" stroke="#22204a"/>
<polygon points="1300,900 1372.5,864.7 1400,900" fill="#23214e" stroke="#23214e"/>
<polygon points="1400,900 1372.5,864.7 1402.9,843.7" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="1592.1,865 1600,810 1600,900" fill="#201f48" stroke="#201f48"/>
<polygon points="1600,810 1592.1,865 1543.3,826.5" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="1543.5,698.6 1506.7,768 1488.2,668.7" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1600,810 1591.6,736.8 1600,720" fill="#201e47" stroke="#201e47"/>
<polygon points="1200.5,381.1 1163.8,361.8 1185.8,352.8" fill="#ab8db1" stroke="#ab8db1"/>
<polygon points="1152.5,397.3 1163.8,361.8 1200.5,381.1" fill="#74559b" stroke="#74559b"/>
<polygon points="1187.8,416.1 1152.5,397.3 1200.5,381.1" fill="#634985" stroke="#634985"/>
<polygon points="1500,0 1455.3,64.6 1400,0" fill="#22234b" stroke="#22234b"/>
<polygon points="1400,0 1455.3,64.6 1424.2,63.9" fill="#2c2850" stroke="#2c2850"/>
<polygon points="1600,90 1600,180 1572.6,154.1" fill="#513c55" stroke="#513c55"/>
<polygon points="1315.5,20.2 1300,0 1400,0" fill="#1d204b" stroke="#1d204b"/>
<polygon points="1366.5,64.5 1400,0 1424.2,63.9" fill="#2c2850" stroke="#2c2850"/>
<polygon points="85.9,287.1 85.8,329.3 5.8,306.4" fill="#966265" stroke="#966265"/>
<polygon points="85.8,329.3 85.9,287.1 95.5,326.1" fill="#8f5d5f" stroke="#8f5d5f"/>
<polygon points="0,630 0,540 19.4,593.5" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="0,630 49.4,662.9 0,720" fill="#2a285d" stroke="#2a285d"/>
<polygon points="321.2,351 386.5,352.2 379.2,387.2" fill="#6f5295" stroke="#6f5295"/>
<polygon points="185.2,263.8 216.1,310.5 208.5,313.1" fill="#895a5e" stroke="#895a5e"/>
<polygon points="239,277.6 216.1,310.5 185.2,263.8" fill="#87595f" stroke="#87595f"/>
<polygon points="216.1,310.5 211.9,325.4 208.5,313.1" fill="#905d5f" stroke="#905d5f"/>
<polygon points="211.9,325.4 216.1,310.5 240,349.2" fill="#976162" stroke="#976162"/>
<polygon points="315.1,204.5 274.4,162.9 324.6,166.6" fill="#5c4155" stroke="#5c4155"/>
<polygon points="274.4,162.9 315.1,204.5 231.5,220.5" fill="#644658" stroke="#644658"/>
<polygon points="196.6,216 231.5,220.5 185.2,263.8" fill="#77515e" stroke="#77515e"/>
<polygon points="196.6,216 185.2,263.8 144.3,246.7" fill="#7a525e" stroke="#7a525e"/>
<polygon points="154,183.3 196.6,216 144.3,246.7" fill="#714e5e" stroke="#714e5e"/>
<polygon points="100,0 200,0 105,3.3" fill="#1c204e" stroke="#1c204e"/>
<polygon points="100,0 0.7,24.6 0,0" fill="#1d1f49" stroke="#1d1f49"/>
<polygon points="100,0 81.7,71 0.7,24.6" fill="#28264f" stroke="#28264f"/>
<polygon points="81.7,71 100,0 105,3.3" fill="#25244e" stroke="#25244e"/>
<polygon points="166.3,144 201.1,154.9 202.2,165.9" fill="#563f57" stroke="#563f57"/>
<polygon points="95.1,124.3 81.7,71 121.9,107.2" fill="#413352" stroke="#413352"/>
<polygon points="81.7,71 95.1,124.3 44.5,114.2" fill="#443655" stroke="#443655"/>
<polygon points="43.2,182.9 0,180 44.5,114.2" fill="#574056" stroke="#574056"/>
<polygon points="112.8,226.9 154,183.3 144.3,246.7" fill="#734f5e" stroke="#734f5e"/>
<polygon points="353.6,100.6 438.3,64 399.5,157.8" fill="#41324f" stroke="#41324f"/>
<polygon points="200,0 123.6,15.1 105,3.3" fill="#1c1f4a" stroke="#1c1f4a"/>
<polygon points="123.6,15.1 118.2,51.1 105,3.3" fill="#222249" stroke="#222249"/>
<polygon points="123.6,15.1 200,0 172.2,68.5" fill="#26254e" stroke="#26254e"/>
<polygon points="118.2,51.1 123.6,15.1 172.2,68.5" fill="#2b284d" stroke="#2b284d"/>
<polygon points="0,90 6.4,83.3 44.5,114.2" fill="#3e3150" stroke="#3e3150"/>
<polygon points="6.4,83.3 32.5,77.5 44.5,114.2" fill="#3c3050" stroke="#3c3050"/>
<polygon points="6.4,83.3 0,90 0.7,24.6" fill="#332c4f" stroke="#332c4f"/>
<polygon points="32.5,77.5 6.4,83.3 0.7,24.6" fill="#312a4d" stroke="#312a4d"/>
<polygon points="900,0 847.1,44.2 800,0" fill="#20224c" stroke="#20224c"/>
<polygon points="902.1,129.7 883,202.6 830.2,145.3" fill="#584057" stroke="#584057"/>
<polygon points="945.4,81.7 923.1,109.9 885.2,71.6" fill="#392e4d" stroke="#392e4d"/>
<polygon points="923.1,109.9 945.4,81.7 956,152.2" fill="#443450" stroke="#443450"/>
<polygon points="747.8,256 695.3,256.6 729.6,212.8" fill="#77505c" stroke="#77505c"/>
<polygon points="783.3,238.9 749,166.8 830.2,145.3" fill="#624559" stroke="#624559"/>
<polygon points="1139.6,87.2 1168.3,46.2 1187.4,100.1" fill="#392f51" stroke="#392f51"/>
<polygon points="1168.3,46.2 1139.6,87.2 1123.4,66.7" fill="#352d51" stroke="#352d51"/>
<polygon points="1156.1,28 1100,0 1200,0" fill="#1e204b" stroke="#1e204b"/>
<polygon points="1168.3,46.2 1156.1,28 1200,0" fill="#25254f" stroke="#25254f"/>
<polygon points="1100,0 1156.1,28 1123.4,66.7" fill="#28264f" stroke="#28264f"/>
<polygon points="1156.1,28 1168.3,46.2 1123.4,66.7" fill="#2c284e" stroke="#2c284e"/>
<polygon points="1254.5,145.7 1224.8,139.1 1265.8,117.8" fill="#4e3a55" stroke="#4e3a55"/>
<polygon points="1205.4,360.6 1200.5,381.1 1185.8,352.8" fill="#cca9d4" stroke="#cca9d4"/>
<polygon points="344.6,43.3 341.9,36.4 341,22.8" fill="#27254b" stroke="#27254b"/>
<polygon points="344.6,43.3 354.1,59.6 348.5,62.7" fill="#2e294d" stroke="#2e294d"/>
<polygon points="416.9,36.3 354.1,59.6 344.6,43.3" fill="#2c284e" stroke="#2c284e"/>
<polygon points="700,0 684.5,51.4 629.8,3.4" fill="#21224c" stroke="#21224c"/>
<polygon points="861.9,76.5 774.6,56.3 847.1,44.2" fill="#2f294b" stroke="#2f294b"/>
<polygon points="600,0 506,7.4 500,0" fill="#1b1f4b" stroke="#1b1f4b"/>
<polygon points="514.2,54.6 506,7.4 600,0" fill="#24244f" stroke="#24244f"/>
<polygon points="602.7,68.7 514.2,54.6 600,0" fill="#2c2850" stroke="#2c2850"/>
<polygon points="602.7,68.7 600,0 629.8,3.4" fill="#24244d" stroke="#24244d"/>
<polygon points="684.5,51.4 602.7,68.7 629.8,3.4" fill="#2b284f" stroke="#2b284f"/>
<polygon points="438.3,64 510.6,91.3 471.8,116.5" fill="#3c3050" stroke="#3c3050"/>
<polygon points="514.2,54.6 510.6,91.3 438.3,64" fill="#332b4c" stroke="#332b4c"/>
<polygon points="510.6,91.3 495.3,127.4 471.8,116.5" fill="#473755" stroke="#473755"/>
<polygon points="495.3,127.4 510.6,91.3 538.8,103.5" fill="#443553" stroke="#443553"/>
<polygon points="466.7,190 495.3,127.4 527.1,204.7" fill="#5e4358" stroke="#5e4358"/>
<polygon points="680.8,180.2 722.3,116.7 720,167.8" fill="#563f57" stroke="#563f57"/>
<polygon points="495.3,127.4 570.4,156.6 527.1,204.7" fill="#5d435a" stroke="#5d435a"/>
<polygon points="570.4,156.6 495.3,127.4 538.8,103.5" fill="#4a3852" stroke="#4a3852"/>
<polygon points="727.9,199.2 720,167.8 749,166.8" fill="#5f4358" stroke="#5f4358"/>
<polygon points="173.1,860.4 135.1,861 128.7,809.7" fill="#29275a" stroke="#29275a"/>
<polygon points="78.8,832.9 20.3,866.1 0,810" fill="#272557" stroke="#272557"/>
<polygon points="9.7,721.7 14,781.3 0,720" fill="#2a275c" stroke="#2a275c"/>
<polygon points="14,781.3 9.7,721.7 56.9,740.6" fill="#262454" stroke="#262454"/>
<polygon points="49.4,662.9 9.7,721.7 0,720" fill="#201f48" stroke="#201f48"/>
<polygon points="401.3,437.2 352.6,455.1 379.2,387.2" fill="#584175" stroke="#584175"/>
<polygon points="331.6,737.5 275.1,727.7 299.3,703.1" fill="#252352" stroke="#252352"/>
<polygon points="301.5,864.7 400,900 300,900" fill="#242250" stroke="#242250"/>
<polygon points="301.5,864.7 357.8,829.6 400,900" fill="#22204b" stroke="#22204b"/>
<polygon points="392.5,825 430.9,889.3 400,900" fill="#2a275c" stroke="#2a275c"/>
<polygon points="430.9,889.3 392.5,825 451.2,809.3" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="415.3,704.2 362.4,693.8 395.3,633.8" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="453.7,662.9 535.5,716.8 445.9,705.1" fill="#272556" stroke="#272556"/>
<polygon points="349.7,646 362.4,693.8 351.5,688.3" fill="#262453" stroke="#262453"/>
<polygon points="362.4,693.8 349.7,646 395.3,633.8" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="186,648.2 183.5,687.1 142.7,653.6" fill="#201e46" stroke="#201e46"/>
<polygon points="186,648.2 207.3,664.7 183.5,687.1" fill="#262454" stroke="#262454"/>
<polygon points="135.6,643.2 186,648.2 142.7,653.6" fill="#453a74" stroke="#453a74"/>
<polygon points="867.3,881.9 853.2,817.6 888.5,849.9" fill="#272556" stroke="#272556"/>
<polygon points="823.8,767 774.5,734.3 840,748.3" fill="#242250" stroke="#242250"/>
<polygon points="823.8,767 853.2,817.6 785.4,813.2" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="634.5,791.5 668.5,814.3 651.9,841.1" fill="#272557" stroke="#272557"/>
<polygon points="693.3,787 634.5,791.5 649.5,756.2" fill="#272556" stroke="#272556"/>
<polygon points="634.5,791.5 693.3,787 668.5,814.3" fill="#23214e" stroke="#23214e"/>
<polygon points="1100,900 1148.3,896.2 1200,900" fill="#23214d" stroke="#23214d"/>
<polygon points="1148.3,896.2 1100,900 1086.5,861.1" fill="#272557" stroke="#272557"/>
<polygon points="1012.5,835.1 1010.9,866.8 990.1,833.3" fill="#29265a" stroke="#29265a"/>
<polygon points="1073.1,893.1 1100,900 1000,900" fill="#21204a" stroke="#21204a"/>
<polygon points="616.7,438.1 577.9,453.2 572.4,428.4" fill="#bc7567" stroke="#bc7567"/>
<polygon points="577.9,453.2 572.5,448.7 572.4,428.4" fill="#ca7e6e" stroke="#ca7e6e"/>
<polygon points="688.2,340.2 756.8,385.9 714.9,416.3" fill="#a56962" stroke="#a56962"/>
<polygon points="647.4,388.3 688.2,340.2 714.9,416.3" fill="#ae6e67" stroke="#ae6e67"/>
<polygon points="695.3,256.6 688.2,340.2 622.2,278.5" fill="#8b5b60" stroke="#8b5b60"/>
<polygon points="688.2,340.2 598.6,306.9 622.2,278.5" fill="#946164" stroke="#946164"/>
<polygon points="598.6,306.9 688.2,340.2 647.4,388.3" fill="#a26866" stroke="#a26866"/>
<polygon points="523.1,295.5 546.5,262.1 585.2,311.7" fill="#8d5d62" stroke="#8d5d62"/>
<polygon points="546.5,262.1 523.1,295.5 513.9,273.8" fill="#7f545b" stroke="#7f545b"/>
<polygon points="491.7,391.7 478,347.2 505.2,354.2" fill="#caa7d2" stroke="#caa7d2"/>
<polygon points="552.3,336.4 523.1,295.5 585.2,311.7" fill="#986365" stroke="#986365"/>
<polygon points="552.3,336.4 505.2,354.2 523.1,295.5" fill="#9a6464" stroke="#9a6464"/>
<polygon points="660.8,479.5 637.9,488.1 616.7,438.1" fill="#d68471" stroke="#d68471"/>
<polygon points="691.7,456.4 660.8,479.5 616.7,438.1" fill="#c07767" stroke="#c07767"/>
<polygon points="660.8,479.5 676.7,522.4 637.9,488.1" fill="#9b80a0" stroke="#9b80a0"/>
<polygon points="670.1,859.5 646,866.5 651.9,841.1" fill="#2a275c" stroke="#2a275c"/>
<polygon points="668.5,814.3 670.1,859.5 651.9,841.1" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="567.2,858.4 607.7,878.8 600,900" fill="#272557" stroke="#272557"/>
<polygon points="607.7,878.8 646,866.5 600,900" fill="#201e46" stroke="#201e46"/>
<polygon points="607.7,878.8 567.2,858.4 651.9,841.1" fill="#211f49" stroke="#211f49"/>
<polygon points="646,866.5 607.7,878.8 651.9,841.1" fill="#201e46" stroke="#201e46"/>
<polygon points="700,900 731.7,896 800,900" fill="#24224f" stroke="#24224f"/>
<polygon points="785.4,813.2 778.4,818.3 764.5,807.4" fill="#252351" stroke="#252351"/>
<polygon points="579.6,709.3 592.7,743.7 549.1,732.2" fill="#201e47" stroke="#201e47"/>
<polygon points="547.9,851.2 567.2,858.4 509,893.9" fill="#22204c" stroke="#22204c"/>
<polygon points="484.2,849.2 547.9,851.2 509,893.9" fill="#262454" stroke="#262454"/>
<polygon points="535.5,716.8 502.1,778.4 445.9,705.1" fill="#262454" stroke="#262454"/>
<polygon points="525.6,802.3 502.1,778.4 549.1,732.2" fill="#211f49" stroke="#211f49"/>
<polygon points="502.1,778.4 535.5,716.8 549.1,732.2" fill="#201e47" stroke="#201e47"/>
<polygon points="1338,513.9 1304.3,545.9 1313.1,499.1" fill="#7b5ba5" stroke="#7b5ba5"/>
<polygon points="1274.6,540.2 1304.3,545.9 1294.3,552.4" fill="#75569c" stroke="#75569c"/>
<polygon points="1304.3,545.9 1327.3,564.6 1294.3,552.4" fill="#7858a0" stroke="#7858a0"/>
<polygon points="1304.3,545.9 1338,513.9 1352.3,549" fill="#5f467f" stroke="#5f467f"/>
<polygon points="1327.3,564.6 1304.3,545.9 1352.3,549" fill="#694d8c" stroke="#694d8c"/>
<polygon points="1376.1,493.6 1338,513.9 1329.2,477.4" fill="#7758a0" stroke="#7758a0"/>
<polygon points="1376.1,493.6 1381.8,518.1 1338,513.9" fill="#664b88" stroke="#664b88"/>
<polygon points="1374.4,322 1389.6,303.2 1412.5,326.1" fill="#915e60" stroke="#915e60"/>
<polygon points="1389.6,303.2 1441.2,265.5 1412.5,326.1" fill="#925f64" stroke="#925f64"/>
<polygon points="1441.2,265.5 1460.2,283.1 1412.5,326.1" fill="#8d5d62" stroke="#8d5d62"/>
<polygon points="1412.5,326.1 1498,317.2 1491.4,396.3" fill="#9f6664" stroke="#9f6664"/>
<polygon points="1460.2,283.1 1498,317.2 1412.5,326.1" fill="#915f62" stroke="#915f62"/>
<polygon points="1498,317.2 1539.5,336.5 1491.4,396.3" fill="#9e6563" stroke="#9e6563"/>
<polygon points="1539.5,336.5 1498,317.2 1522,289.9" fill="#8d5c5e" stroke="#8d5c5e"/>
<polygon points="1403.7,382.9 1433.5,427.7 1417.3,429" fill="#b47166" stroke="#b47166"/>
<polygon points="1403.7,382.9 1351.5,380 1357.5,349.8" fill="#ab6d67" stroke="#ab6d67"/>
<polygon points="1351.5,380 1317.2,373.2 1357.5,349.8" fill="#a66a65" stroke="#a66a65"/>
<polygon points="1489.4,484.7 1517.2,484.1 1490.4,566.7" fill="#3c3366" stroke="#3c3366"/>
<polygon points="1550.9,589.5 1600,540 1600,630" fill="#3e3468" stroke="#3e3468"/>
<polygon points="1506.7,768 1445.9,734.3 1488.2,668.7" fill="#211f49" stroke="#211f49"/>
<polygon points="1445.9,734.3 1435.2,708.6 1488.2,668.7" fill="#1e1c41" stroke="#1e1c41"/>
<polygon points="1435.2,708.6 1475.9,660.2 1488.2,668.7" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1539.5,336.5 1526.3,400.3 1491.4,396.3" fill="#af6f68" stroke="#af6f68"/>
<polygon points="1557.9,398.4 1552.3,351.1 1600,360" fill="#a26762" stroke="#a26762"/>
<polygon points="1552.3,351.1 1526.3,400.3 1539.5,336.5" fill="#a56a65" stroke="#a56a65"/>
<polygon points="1264.5,875.1 1202.4,877.9 1205.1,861.3" fill="#252351" stroke="#252351"/>
<polygon points="1202.4,877.9 1264.5,875.1 1200,900" fill="#23214d" stroke="#23214d"/>
<polygon points="1500,900 1550,866.8 1600,900" fill="#262353" stroke="#262353"/>
<polygon points="1506.7,768 1569.4,726.9 1543.3,826.5" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1569.4,726.9 1600,810 1543.3,826.5" fill="#282659" stroke="#282659"/>
<polygon points="1569.4,726.9 1591.6,736.8 1600,810" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1312.4,658.6 1339.8,626 1355.6,667.5" fill="#40356b" stroke="#40356b"/>
<polygon points="1136,288.8 1139.1,372.1 1114.2,372.3" fill="#5d457d" stroke="#5d457d"/>
<polygon points="1228.8,561.1 1206.1,601.3 1208,572.2" fill="#3e3469" stroke="#3e3469"/>
<polygon points="1206.1,601.3 1228.8,561.1 1231.3,621.8" fill="#3a3163" stroke="#3a3163"/>
<polygon points="1233.4,676.2 1251,713.6 1238.8,711.7" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1482,71.8 1455.3,64.6 1500,0" fill="#2a264a" stroke="#2a264a"/>
<polygon points="1540,21.2 1482,71.8 1500,0" fill="#27254d" stroke="#27254d"/>
<polygon points="1482,71.8 1540,21.2 1517.1,88.7" fill="#332c51" stroke="#332c51"/>
<polygon points="1581.6,210 1600,180 1600,270" fill="#734f5e" stroke="#734f5e"/>
<polygon points="1594.8,294.9 1569,260.5 1600,270" fill="#84575f" stroke="#84575f"/>
<polygon points="1300,0 1270.2,10.1 1200,0" fill="#1c1f4b" stroke="#1c1f4b"/>
<polygon points="1270.2,10.1 1300,0 1315.5,20.2" fill="#1f214c" stroke="#1f214c"/>
<polygon points="1323.9,111.7 1366.5,64.5 1354,131.2" fill="#433554" stroke="#433554"/>
<polygon points="178.1,459.3 161.6,474.3 138.8,441.1" fill="#73559a" stroke="#73559a"/>
<polygon points="178.1,459.3 136.7,431.7 190.8,401.9" fill="#5f4680" stroke="#5f4680"/>
<polygon points="136.7,431.7 178.1,459.3 138.8,441.1" fill="#705295" stroke="#705295"/>
<polygon points="93.3,452.4 67.8,433.3 105.1,408.5" fill="#584176" stroke="#584176"/>
<polygon points="136.7,431.7 129.7,447 105.1,408.5" fill="#725498" stroke="#725498"/>
<polygon points="129.7,447 93.3,452.4 105.1,408.5" fill="#73559a" stroke="#73559a"/>
<polygon points="129.7,447 136.7,431.7 138.8,441.1" fill="#5c447c" stroke="#5c447c"/>
<polygon points="129.7,447 161.6,474.3 113.1,495.7" fill="#725499" stroke="#725499"/>
<polygon points="161.6,474.3 129.7,447 138.8,441.1" fill="#7b5ba4" stroke="#7b5ba4"/>
<polygon points="82.1,474.4 60.2,472.5 56.3,463.5" fill="#725499" stroke="#725499"/>
<polygon points="73.8,521.8 82.1,474.4 113.1,495.7" fill="#4c4080" stroke="#4c4080"/>
<polygon points="82.1,474.4 73.8,521.8 60.2,472.5" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="185.2,263.8 151.2,278.1 144.3,246.7" fill="#7d545d" stroke="#7d545d"/>
<polygon points="85.9,287.1 107.1,300.9 95.5,326.1" fill="#946064" stroke="#946064"/>
<polygon points="177.6,372.1 240,349.2 190.8,401.9" fill="#644a86" stroke="#644a86"/>
<polygon points="177.6,372.1 211.9,325.4 240,349.2" fill="#a588ab" stroke="#a588ab"/>
<polygon points="136.7,431.7 177.6,372.1 190.8,401.9" fill="#5c447b" stroke="#5c447b"/>
<polygon points="124.7,674.6 135.6,643.2 142.7,653.6" fill="#3f356a" stroke="#3f356a"/>
<polygon points="183.5,687.1 124.7,674.6 142.7,653.6" fill="#262454" stroke="#262454"/>
<polygon points="56.9,740.6 86.6,714.6 102,733.2" fill="#201f48" stroke="#201f48"/>
<polygon points="142.8,551.7 145.7,535.8 184.9,542.6" fill="#74569c" stroke="#74569c"/>
<polygon points="161.6,474.3 157.7,506.4 113.1,495.7" fill="#644a86" stroke="#644a86"/>
<polygon points="320.5,394 321.2,351 379.2,387.2" fill="#6e5193" stroke="#6e5193"/>
<polygon points="352.6,455.1 320.5,394 379.2,387.2" fill="#584176" stroke="#584176"/>
<polygon points="267.9,378.9 320.5,394 300.6,446.2" fill="#77589f" stroke="#77589f"/>
<polygon points="288.4,372.6 320.5,394 267.9,378.9" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="294,348.4 288.4,372.6 269.7,357.2" fill="#a26864" stroke="#a26864"/>
<polygon points="256.3,360.2 255.2,369.9 240,349.2" fill="#c6a4ce" stroke="#c6a4ce"/>
<polygon points="267.6,355.9 256.3,360.2 240,349.2" fill="#9c6460" stroke="#9c6460"/>
<polygon points="255.2,369.9 256.3,360.2 269.7,357.2" fill="#a66a66" stroke="#a66a66"/>
<polygon points="256.3,360.2 267.6,355.9 269.7,357.2" fill="#a16763" stroke="#a16763"/>
<polygon points="190.1,183.3 196.6,216 154,183.3" fill="#634557" stroke="#634557"/>
<polygon points="190.1,183.3 166.3,144 202.2,165.9" fill="#563e54" stroke="#563e54"/>
<polygon points="200,0 209.7,24.3 172.2,68.5" fill="#27254d" stroke="#27254d"/>
<polygon points="209.7,24.3 214.4,54.4 172.2,68.5" fill="#2f2a51" stroke="#2f2a51"/>
<polygon points="209.7,24.3 200,0 248.1,8" fill="#1f214c" stroke="#1f214c"/>
<polygon points="130.1,120.8 95.1,124.3 121.9,107.2" fill="#483754" stroke="#483754"/>
<polygon points="95.1,124.3 130.1,120.8 96.6,150.8" fill="#4d3a54" stroke="#4d3a54"/>
<polygon points="172.2,68.5 130.1,120.8 121.9,107.2" fill="#413453" stroke="#413453"/>
<polygon points="158.7,130.9 130.1,120.8 172.2,68.5" fill="#443554" stroke="#443554"/>
<polygon points="253.1,93.7 216.1,100.2 237.3,89.7" fill="#3b2f4d" stroke="#3b2f4d"/>
<polygon points="216.1,100.2 158.7,130.9 172.2,68.5" fill="#433555" stroke="#433555"/>
<polygon points="166.3,144 216.1,100.2 201.1,154.9" fill="#4d3a54" stroke="#4d3a54"/>
<polygon points="216.1,100.2 166.3,144 158.7,130.9" fill="#473650" stroke="#473650"/>
<polygon points="216.1,100.2 274.4,162.9 201.1,154.9" fill="#513c56" stroke="#513c56"/>
<polygon points="216.1,100.2 253.1,93.7 274.4,162.9" fill="#4a3855" stroke="#4a3855"/>
<polygon points="214.4,54.4 216.1,100.2 172.2,68.5" fill="#362d4f" stroke="#362d4f"/>
<polygon points="216.1,100.2 214.4,54.4 237.3,89.7" fill="#3a3051" stroke="#3a3051"/>
<polygon points="95.1,124.3 67,149.2 44.5,114.2" fill="#483650" stroke="#483650"/>
<polygon points="67,149.2 43.2,182.9 44.5,114.2" fill="#553e57" stroke="#553e57"/>
<polygon points="0,180 28.7,222.4 0,270" fill="#724e5c" stroke="#724e5c"/>
<polygon points="43.2,182.9 28.7,222.4 0,180" fill="#68495b" stroke="#68495b"/>
<polygon points="112.8,226.9 115.7,186.3 154,183.3" fill="#664759" stroke="#664759"/>
<polygon points="117.3,239.1 112.8,226.9 144.3,246.7" fill="#7a535f" stroke="#7a535f"/>
<polygon points="347.4,141.2 353.6,100.6 399.5,157.8" fill="#4c3953" stroke="#4c3953"/>
<polygon points="881.4,89.7 861.9,76.5 885.2,71.6" fill="#362c4c" stroke="#362c4c"/>
<polygon points="923.1,109.9 881.4,89.7 885.2,71.6" fill="#3a2f4e" stroke="#3a2f4e"/>
<polygon points="881.4,89.7 923.1,109.9 902.1,129.7" fill="#433451" stroke="#433451"/>
<polygon points="900,0 869.6,44.1 847.1,44.2" fill="#25244a" stroke="#25244a"/>
<polygon points="861.9,76.5 869.6,44.1 885.2,71.6" fill="#312a4d" stroke="#312a4d"/>
<polygon points="869.6,44.1 861.9,76.5 847.1,44.2" fill="#2d284b" stroke="#2d284b"/>
<polygon points="994.2,17.8 900,0 1000,0" fill="#1d204c" stroke="#1d204c"/>
<polygon points="883,202.6 929.1,242.8 873.6,258.8" fill="#b2816e" stroke="#b2816e"/>
<polygon points="783.3,238.9 838.8,225.4 834.1,268.2" fill="#7c5460" stroke="#7c5460"/>
<polygon points="838.8,225.4 783.3,238.9 830.2,145.3" fill="#664758" stroke="#664758"/>
<polygon points="927.5,163.9 883,202.6 902.1,129.7" fill="#594056" stroke="#594056"/>
<polygon points="883,202.6 927.5,163.9 944.5,180.8" fill="#604458" stroke="#604458"/>
<polygon points="944.5,180.8 927.5,163.9 956,152.2" fill="#5a4157" stroke="#5a4157"/>
<polygon points="927.5,163.9 923.1,109.9 956,152.2" fill="#523d57" stroke="#523d57"/>
<polygon points="923.1,109.9 927.5,163.9 902.1,129.7" fill="#4d3a54" stroke="#4d3a54"/>
<polygon points="1139.6,87.2 1082.8,109.8 1123.4,66.7" fill="#392e4d" stroke="#392e4d"/>
<polygon points="1082.8,109.8 1139.6,87.2 1122.8,126.6" fill="#423451" stroke="#423451"/>
<polygon points="945.4,81.7 1013.9,131.5 956,152.2" fill="#45354f" stroke="#45354f"/>
<polygon points="767.2,291.9 783.3,238.9 834.1,268.2" fill="#82575f" stroke="#82575f"/>
<polygon points="783.3,238.9 768,233 749,166.8" fill="#704e5e" stroke="#704e5e"/>
<polygon points="727.9,199.2 768,233 729.6,212.8" fill="#6d4b5b" stroke="#6d4b5b"/>
<polygon points="768,233 727.9,199.2 749,166.8" fill="#68495b" stroke="#68495b"/>
<polygon points="1151,118.7 1139.6,87.2 1187.4,100.1" fill="#423452" stroke="#423452"/>
<polygon points="1139.6,87.2 1151,118.7 1122.8,126.6" fill="#483856" stroke="#483856"/>
<polygon points="1374.4,322 1324.2,309.5 1389.6,303.2" fill="#8c5b5e" stroke="#8c5b5e"/>
<polygon points="1091.8,266.2 1090.6,234.8 1117.7,268.9" fill="#b17f6b" stroke="#b17f6b"/>
<polygon points="1132.8,238.8 1136,288.8 1117.7,268.9" fill="#af7d69" stroke="#af7d69"/>
<polygon points="1090.6,234.8 1132.8,238.8 1117.7,268.9" fill="#ad7d6a" stroke="#ad7d6a"/>
<polygon points="1132.8,238.8 1090.6,234.8 1120.7,216.2" fill="#a57867" stroke="#a57867"/>
<polygon points="1120.7,216.2 1126.3,185.2 1166.9,219.7" fill="#aa7c6c" stroke="#aa7c6c"/>
<polygon points="1190.5,276 1164.3,310 1136,288.8" fill="#b5816a" stroke="#b5816a"/>
<polygon points="1164.3,310 1139.1,372.1 1136,288.8" fill="#c1a0c8" stroke="#c1a0c8"/>
<polygon points="1139.1,372.1 1164.3,310 1163.8,361.8" fill="#624883" stroke="#624883"/>
<polygon points="438.3,64 354.2,77.1 431.8,52.9" fill="#312a4b" stroke="#312a4b"/>
<polygon points="353.6,100.6 354.2,77.1 438.3,64" fill="#372e4e" stroke="#372e4e"/>
<polygon points="354.2,77.1 416.9,36.3 431.8,52.9" fill="#322c52" stroke="#322c52"/>
<polygon points="354.2,77.1 354.1,59.6 416.9,36.3" fill="#312b50" stroke="#312b50"/>
<polygon points="354.1,59.6 354.2,77.1 348.5,62.7" fill="#322b4d" stroke="#322b4d"/>
<polygon points="742.4,28.7 723.1,40.5 700,0" fill="#222249" stroke="#222249"/>
<polygon points="602.7,68.7 618.1,123.2 610.9,119" fill="#433553" stroke="#433553"/>
<polygon points="618.1,123.2 602.7,68.7 684.5,51.4" fill="#3b3153" stroke="#3b3153"/>
<polygon points="783.1,26.6 742.4,28.7 800,0" fill="#232450" stroke="#232450"/>
<polygon points="847.1,44.2 783.1,26.6 800,0" fill="#252550" stroke="#252550"/>
<polygon points="774.6,56.3 783.1,26.6 847.1,44.2" fill="#2c2850" stroke="#2c2850"/>
<polygon points="501.8,37.7 431.8,52.9 500,0" fill="#242349" stroke="#242349"/>
<polygon points="506,7.4 501.8,37.7 500,0" fill="#21234e" stroke="#21234e"/>
<polygon points="501.8,37.7 438.3,64 431.8,52.9" fill="#2e294e" stroke="#2e294e"/>
<polygon points="501.8,37.7 514.2,54.6 438.3,64" fill="#2e294e" stroke="#2e294e"/>
<polygon points="501.8,37.7 506,7.4 514.2,54.6" fill="#252449" stroke="#252449"/>
<polygon points="602.7,68.7 548.5,93.9 514.2,54.6" fill="#332b4c" stroke="#332b4c"/>
<polygon points="510.6,91.3 548.5,93.9 538.8,103.5" fill="#3f3252" stroke="#3f3252"/>
<polygon points="548.5,93.9 510.6,91.3 514.2,54.6" fill="#3a3051" stroke="#3a3051"/>
<polygon points="495.3,127.4 461.2,150.9 471.8,116.5" fill="#4b3852" stroke="#4b3852"/>
<polygon points="466.7,190 461.2,150.9 495.3,127.4" fill="#5a415a" stroke="#5a415a"/>
<polygon points="642.4,202.9 682.1,238.9 615.4,245.2" fill="#6d4a57" stroke="#6d4a57"/>
<polygon points="642.4,202.9 680.8,180.2 682.1,238.9" fill="#6d4c5d" stroke="#6d4c5d"/>
<polygon points="570.4,156.6 574.6,205.7 527.1,204.7" fill="#67485c" stroke="#67485c"/>
<polygon points="574.6,205.7 546.5,262.1 527.1,204.7" fill="#6b4957" stroke="#6b4957"/>
<polygon points="680.8,180.2 708.1,208.4 682.1,238.9" fill="#6d4c5c" stroke="#6d4c5c"/>
<polygon points="708.1,208.4 727.9,199.2 729.6,212.8" fill="#6a4a5a" stroke="#6a4a5a"/>
<polygon points="708.1,208.4 680.8,180.2 720,167.8" fill="#604457" stroke="#604457"/>
<polygon points="727.9,199.2 708.1,208.4 720,167.8" fill="#634558" stroke="#634558"/>
<polygon points="695.3,256.6 708.1,208.4 729.6,212.8" fill="#714d5b" stroke="#714d5b"/>
<polygon points="708.1,208.4 695.3,256.6 682.1,238.9" fill="#79525f" stroke="#79525f"/>
<polygon points="173.1,860.4 164.6,874.5 135.1,861" fill="#23214e" stroke="#23214e"/>
<polygon points="164.6,874.5 200,900 100,900" fill="#22204c" stroke="#22204c"/>
<polygon points="135.1,861 164.6,874.5 100,900" fill="#211f48" stroke="#211f48"/>
<polygon points="200,900 164.6,874.5 204.7,871.9" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="164.6,874.5 173.1,860.4 204.7,871.9" fill="#252352" stroke="#252352"/>
<polygon points="100.3,855.8 135.1,861 100,900" fill="#29275b" stroke="#29275b"/>
<polygon points="20.3,866.1 100.3,855.8 100,900" fill="#22204b" stroke="#22204b"/>
<polygon points="78.8,832.9 100.3,855.8 20.3,866.1" fill="#22204c" stroke="#22204c"/>
<polygon points="113,768 56.9,740.6 102,733.2" fill="#211f48" stroke="#211f48"/>
<polygon points="125.3,744.5 113,768 102,733.2" fill="#2a285d" stroke="#2a285d"/>
<polygon points="113,768 125.3,744.5 144.4,773.4" fill="#29275a" stroke="#29275a"/>
<polygon points="70.6,802.9 14,781.3 56.9,740.6" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="113,768 70.6,802.9 56.9,740.6" fill="#29265a" stroke="#29265a"/>
<polygon points="14,781.3 70.6,802.9 0,810" fill="#272455" stroke="#272455"/>
<polygon points="70.6,802.9 78.8,832.9 0,810" fill="#211f49" stroke="#211f49"/>
<polygon points="9.7,721.7 55,729.4 56.9,740.6" fill="#262453" stroke="#262453"/>
<polygon points="55,729.4 9.7,721.7 49.4,662.9" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="55,729.4 86.6,714.6 56.9,740.6" fill="#211f49" stroke="#211f49"/>
<polygon points="86.6,714.6 55,729.4 49.4,662.9" fill="#272556" stroke="#272556"/>
<polygon points="310.7,483 285.2,485.9 300.6,446.2" fill="#644985" stroke="#644985"/>
<polygon points="424.1,631.9 415.3,704.2 395.3,633.8" fill="#201e47" stroke="#201e47"/>
<polygon points="424.1,631.9 453.7,662.9 415.3,704.2" fill="#282658" stroke="#282658"/>
<polygon points="479.6,467.6 401.3,437.2 464.9,411.7" fill="#624884" stroke="#624884"/>
<polygon points="476.8,564.4 467.4,494.7 542,543.3" fill="#4d4182" stroke="#4d4182"/>
<polygon points="327.8,773.1 331.6,737.5 345.7,770.8" fill="#272556" stroke="#272556"/>
<polygon points="357.8,829.6 327.8,773.1 345.7,770.8" fill="#1d1b41" stroke="#1d1b41"/>
<polygon points="223.6,867.1 301.5,864.7 300,900" fill="#211f4a" stroke="#211f4a"/>
<polygon points="200,900 223.6,867.1 300,900" fill="#242250" stroke="#242250"/>
<polygon points="223.6,867.1 200,900 204.7,871.9" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="357.8,829.6 367.1,827.9 400,900" fill="#272455" stroke="#272455"/>
<polygon points="367.1,827.9 392.5,825 400,900" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="367.1,827.9 357.8,829.6 345.7,770.8" fill="#2a285d" stroke="#2a285d"/>
<polygon points="392.5,825 367.1,827.9 345.7,770.8" fill="#272557" stroke="#272557"/>
<polygon points="502.1,778.4 433.3,738.9 445.9,705.1" fill="#29275a" stroke="#29275a"/>
<polygon points="416.9,769.4 433.3,738.9 451.2,809.3" fill="#262455" stroke="#262455"/>
<polygon points="433.3,738.9 502.1,778.4 451.2,809.3" fill="#262454" stroke="#262454"/>
<polygon points="411.3,778.9 392.5,825 345.7,770.8" fill="#282658" stroke="#282658"/>
<polygon points="392.5,825 411.3,778.9 451.2,809.3" fill="#272455" stroke="#272455"/>
<polygon points="411.3,778.9 416.9,769.4 451.2,809.3" fill="#29275b" stroke="#29275b"/>
<polygon points="357.6,710.5 362.4,693.8 415.3,704.2" fill="#23214d" stroke="#23214d"/>
<polygon points="331.6,737.5 357.6,710.5 345.7,770.8" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="572,706.3 579.6,709.3 549.1,732.2" fill="#201f48" stroke="#201f48"/>
<polygon points="535.5,716.8 572,706.3 549.1,732.2" fill="#1f1e45" stroke="#1f1e45"/>
<polygon points="579.6,709.3 572,706.3 568.2,667.3" fill="#29265a" stroke="#29265a"/>
<polygon points="572,706.3 535.5,716.8 568.2,667.3" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="348.6,617.8 349.7,646 325.1,629.7" fill="#393060" stroke="#393060"/>
<polygon points="329.8,652.4 349.7,646 351.5,688.3" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="349.7,646 329.8,652.4 325.1,629.7" fill="#483d7a" stroke="#483d7a"/>
<polygon points="315.5,624.7 281,640 281.3,607.3" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="275.1,727.7 264.1,719.2 299.3,703.1" fill="#41376e" stroke="#41376e"/>
<polygon points="917.1,781.3 938.6,804.4 888.5,849.9" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="943.4,832.3 938.6,804.4 990.1,833.3" fill="#252352" stroke="#252352"/>
<polygon points="973,874.6 943.4,832.3 990.1,833.3" fill="#272455" stroke="#272455"/>
<polygon points="1010.9,866.8 973,874.6 990.1,833.3" fill="#272455" stroke="#272455"/>
<polygon points="884.7,780.1 917.1,781.3 888.5,849.9" fill="#29275b" stroke="#29275b"/>
<polygon points="853.2,817.6 884.7,780.1 888.5,849.9" fill="#211f48" stroke="#211f48"/>
<polygon points="823.8,767 884.7,780.1 853.2,817.6" fill="#252353" stroke="#252353"/>
<polygon points="884.7,780.1 823.8,767 840,748.3" fill="#262455" stroke="#262455"/>
<polygon points="853.2,817.6 801,863.4 785.4,813.2" fill="#282659" stroke="#282659"/>
<polygon points="801,863.4 778.4,818.3 785.4,813.2" fill="#262453" stroke="#262453"/>
<polygon points="778.4,818.3 801,863.4 757.2,882.6" fill="#272557" stroke="#272557"/>
<polygon points="881.4,895.1 900,900 800,900" fill="#211f4a" stroke="#211f4a"/>
<polygon points="649.5,756.2 616.1,750.6 629.9,739.6" fill="#252351" stroke="#252351"/>
<polygon points="693.3,787 726.6,805 668.5,814.3" fill="#24224f" stroke="#24224f"/>
<polygon points="1214.5,826.6 1136,819.1 1137.4,786.5" fill="#252353" stroke="#252353"/>
<polygon points="1010.9,866.8 1024.2,853.3 1030.1,870.5" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1012.5,835.1 1024.2,853.3 1010.9,866.8" fill="#29275a" stroke="#29275a"/>
<polygon points="1062.9,816.7 1024.2,853.3 1012.5,835.1" fill="#201e47" stroke="#201e47"/>
<polygon points="1018.9,889.5 1073.1,893.1 1000,900" fill="#22204c" stroke="#22204c"/>
<polygon points="1073.1,893.1 1018.9,889.5 1030.1,870.5" fill="#262353" stroke="#262353"/>
<polygon points="1018.9,889.5 1010.9,866.8 1030.1,870.5" fill="#282558" stroke="#282558"/>
<polygon points="1081.3,867.9 1073.1,893.1 1030.1,870.5" fill="#22214c" stroke="#22214c"/>
<polygon points="1100,900 1081.3,867.9 1086.5,861.1" fill="#29275a" stroke="#29275a"/>
<polygon points="1073.1,893.1 1081.3,867.9 1100,900" fill="#272556" stroke="#272556"/>
<polygon points="1218.4,788.4 1214.5,826.6 1137.4,786.5" fill="#252352" stroke="#252352"/>
<polygon points="1218.4,788.4 1174.6,731.1 1227.1,773.3" fill="#29265a" stroke="#29265a"/>
<polygon points="1174.6,731.1 1218.4,788.4 1137.4,786.5" fill="#29265a" stroke="#29265a"/>
<polygon points="598.3,664.9 579.6,709.3 568.2,667.3" fill="#3d3366" stroke="#3d3366"/>
<polygon points="710,450.6 691.7,456.4 714.9,416.3" fill="#9e82a3" stroke="#9e82a3"/>
<polygon points="629.9,540.1 676.7,522.4 635.9,559.7" fill="#5e467e" stroke="#5e467e"/>
<polygon points="637.9,488.1 629.9,540.1 619.3,507.1" fill="#9a7fa0" stroke="#9a7fa0"/>
<polygon points="676.7,522.4 629.9,540.1 637.9,488.1" fill="#c2a0c9" stroke="#c2a0c9"/>
<polygon points="598,609.3 653.5,621.8 626.8,655.7" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="671.5,615.4 653.5,621.8 664,602.3" fill="#3a3163" stroke="#3a3163"/>
<polygon points="653.5,621.8 671.5,615.4 680.2,621.8" fill="#41376e" stroke="#41376e"/>
<polygon points="617.5,553.2 629.9,540.1 635.9,559.7" fill="#382f5f" stroke="#382f5f"/>
<polygon points="548.6,604.8 598,609.3 539.2,632" fill="#453a74" stroke="#453a74"/>
<polygon points="600.6,252.1 615.4,245.2 622.2,278.5" fill="#815760" stroke="#815760"/>
<polygon points="600.6,252.1 596.9,248.8 615.4,245.2" fill="#77505b" stroke="#77505b"/>
<polygon points="478,347.2 474,318.9 505.2,354.2" fill="#d0acd7" stroke="#d0acd7"/>
<polygon points="474,318.9 478,347.2 443.9,341.1" fill="#9a7fa0" stroke="#9a7fa0"/>
<polygon points="538.6,398.9 491.7,391.7 505.2,354.2" fill="#c1a0c9" stroke="#c1a0c9"/>
<polygon points="552.3,336.4 538.6,398.9 505.2,354.2" fill="#ab6d69" stroke="#ab6d69"/>
<polygon points="491.7,391.7 473.3,401.4 478,347.2" fill="#77579f" stroke="#77579f"/>
<polygon points="473.3,401.4 464.9,411.7 443.9,341.1" fill="#7a5aa3" stroke="#7a5aa3"/>
<polygon points="478,347.2 473.3,401.4 443.9,341.1" fill="#7b5ba4" stroke="#7b5ba4"/>
<polygon points="571.5,356.7 552.3,336.4 585.2,311.7" fill="#986261" stroke="#986261"/>
<polygon points="586.5,409.5 571.5,356.7 647.4,388.3" fill="#a86a63" stroke="#a86a63"/>
<polygon points="571.5,356.7 586.5,409.5 556.2,399.6" fill="#a66961" stroke="#a66961"/>
<polygon points="571.5,356.7 598.6,306.9 647.4,388.3" fill="#a66a67" stroke="#a66a67"/>
<polygon points="598.6,306.9 571.5,356.7 585.2,311.7" fill="#976263" stroke="#976263"/>
<polygon points="538.6,398.9 571.5,356.7 556.2,399.6" fill="#b27169" stroke="#b27169"/>
<polygon points="571.5,356.7 538.6,398.9 552.3,336.4" fill="#ad6e69" stroke="#ad6e69"/>
<polygon points="646,866.5 694.5,881.6 700,900" fill="#282558" stroke="#282558"/>
<polygon points="670.1,859.5 694.5,881.6 646,866.5" fill="#262454" stroke="#262454"/>
<polygon points="731.7,896 769.4,890.9 800,900" fill="#211f4a" stroke="#211f4a"/>
<polygon points="769.4,890.9 731.7,896 757.2,882.6" fill="#22204c" stroke="#22204c"/>
<polygon points="769.4,890.9 801,863.4 800,900" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="801,863.4 769.4,890.9 757.2,882.6" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="622.9,721.1 633.8,712.4 629.9,739.6" fill="#382f5f" stroke="#382f5f"/>
<polygon points="616.1,750.6 622.9,721.1 629.9,739.6" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="509,822.1 547.9,851.2 484.2,849.2" fill="#252352" stroke="#252352"/>
<polygon points="547.9,851.2 509,822.1 525.6,802.3" fill="#29275c" stroke="#29275c"/>
<polygon points="509,822.1 502.1,778.4 525.6,802.3" fill="#2a285d" stroke="#2a285d"/>
<polygon points="509,822.1 484.2,849.2 451.2,809.3" fill="#282659" stroke="#282659"/>
<polygon points="502.1,778.4 509,822.1 451.2,809.3" fill="#201e46" stroke="#201e46"/>
<polygon points="1351.5,380 1381.3,411.7 1351.5,404.6" fill="#b8746b" stroke="#b8746b"/>
<polygon points="1381.3,411.7 1351.5,380 1403.7,382.9" fill="#ad6d65" stroke="#ad6d65"/>
<polygon points="1381.3,411.7 1403.7,382.9 1417.3,429" fill="#b37166" stroke="#b37166"/>
<polygon points="1375,540.9 1398.5,557.2 1352.3,549" fill="#483c79" stroke="#483c79"/>
<polygon points="1398.5,557.2 1375,540.9 1381.8,518.1" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1338,513.9 1375,540.9 1352.3,549" fill="#7959a1" stroke="#7959a1"/>
<polygon points="1381.8,518.1 1375,540.9 1338,513.9" fill="#463b76" stroke="#463b76"/>
<polygon points="1371.6,442.5 1378.9,482.3 1329.2,477.4" fill="#624883" stroke="#624883"/>
<polygon points="1378.9,482.3 1376.1,493.6 1329.2,477.4" fill="#725498" stroke="#725498"/>
<polygon points="1366.2,330.8 1374.4,322 1412.5,326.1" fill="#905d5e" stroke="#905d5e"/>
<polygon points="1366.2,330.8 1403.7,382.9 1357.5,349.8" fill="#a46965" stroke="#a46965"/>
<polygon points="1403.7,382.9 1366.2,330.8 1412.5,326.1" fill="#9e6663" stroke="#9e6663"/>
<polygon points="1324.2,309.5 1366.2,330.8 1357.5,349.8" fill="#966161" stroke="#966161"/>
<polygon points="1366.2,330.8 1324.2,309.5 1374.4,322" fill="#996364" stroke="#996364"/>
<polygon points="1441.2,265.5 1402.5,240 1434,249.2" fill="#78515b" stroke="#78515b"/>
<polygon points="1402.5,240 1441.2,265.5 1389.6,303.2" fill="#7d535b" stroke="#7d535b"/>
<polygon points="1224.8,139.1 1255.2,168.2 1221.9,202.5" fill="#5a4156" stroke="#5a4156"/>
<polygon points="1254.5,145.7 1255.2,168.2 1224.8,139.1" fill="#533d54" stroke="#533d54"/>
<polygon points="1441.2,265.5 1483.6,253.1 1460.2,283.1" fill="#845861" stroke="#845861"/>
<polygon points="1483.6,253.1 1441.2,265.5 1434,249.2" fill="#78515a" stroke="#78515a"/>
<polygon points="1452.2,196.6 1483.6,253.1 1434,249.2" fill="#724d5a" stroke="#724d5a"/>
<polygon points="1483.6,253.1 1506.6,219.8 1498.2,255.9" fill="#7a535e" stroke="#7a535e"/>
<polygon points="1506.6,219.8 1483.6,253.1 1452.2,196.6" fill="#6f4c5a" stroke="#6f4c5a"/>
<polygon points="1498,317.2 1485.7,277.5 1522,289.9" fill="#88595e" stroke="#88595e"/>
<polygon points="1485.7,277.5 1498,317.2 1460.2,283.1" fill="#85575c" stroke="#85575c"/>
<polygon points="1485.7,277.5 1498.2,255.9 1522,289.9" fill="#81555d" stroke="#81555d"/>
<polygon points="1483.6,253.1 1485.7,277.5 1460.2,283.1" fill="#82565e" stroke="#82565e"/>
<polygon points="1485.7,277.5 1483.6,253.1 1498.2,255.9" fill="#825760" stroke="#825760"/>
<polygon points="1502.5,425.9 1469.1,425 1491.4,396.3" fill="#c27a6e" stroke="#c27a6e"/>
<polygon points="1469.1,425 1494,443.6 1471.6,467.2" fill="#4d4182" stroke="#4d4182"/>
<polygon points="1494,443.6 1469.1,425 1502.5,425.9" fill="#bc9bc3" stroke="#bc9bc3"/>
<polygon points="1600,540 1596.9,499.4 1600,450" fill="#614782" stroke="#614782"/>
<polygon points="1596.9,499.4 1562.9,475.3 1600,450" fill="#5d457d" stroke="#5d457d"/>
<polygon points="1550.9,589.5 1554.9,527 1600,540" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1517.2,484.1 1554.9,527 1490.4,566.7" fill="#4e4284" stroke="#4e4284"/>
<polygon points="1554.9,527 1550.9,589.5 1490.4,566.7" fill="#3f356b" stroke="#3f356b"/>
<polygon points="1565.5,449.4 1593.4,425.5 1600,450" fill="#9c81a2" stroke="#9c81a2"/>
<polygon points="1562.9,475.3 1565.5,449.4 1600,450" fill="#c7a4ce" stroke="#c7a4ce"/>
<polygon points="1582.7,651.6 1558,619.9 1600,630" fill="#473c78" stroke="#473c78"/>
<polygon points="1558,619.9 1550.9,589.5 1600,630" fill="#493d7a" stroke="#493d7a"/>
<polygon points="1445.9,734.3 1432.4,714.2 1435.2,708.6" fill="#272557" stroke="#272557"/>
<polygon points="1378.5,698.6 1399.6,715.6 1359.2,762.3" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1378.5,698.6 1305.6,685.5 1355.6,667.5" fill="#22204a" stroke="#22204a"/>
<polygon points="1305.6,685.5 1312.4,658.6 1355.6,667.5" fill="#252352" stroke="#252352"/>
<polygon points="1288.3,754.9 1305.6,685.5 1359.2,762.3" fill="#272556" stroke="#272556"/>
<polygon points="1305.6,685.5 1378.5,698.6 1359.2,762.3" fill="#282558" stroke="#282558"/>
<polygon points="1312.4,658.6 1305.6,685.5 1282.2,669.1" fill="#201e47" stroke="#201e47"/>
<polygon points="1367.8,671.5 1378.5,698.6 1355.6,667.5" fill="#4e4183" stroke="#4e4183"/>
<polygon points="1327.3,564.6 1339,592.2 1294.3,552.4" fill="#4f4285" stroke="#4f4285"/>
<polygon points="1339,592.2 1327.3,564.6 1352.3,549" fill="#594177" stroke="#594177"/>
<polygon points="1398.5,557.2 1339,592.2 1352.3,549" fill="#4c4080" stroke="#4c4080"/>
<polygon points="1339,592.2 1398.5,557.2 1431.7,599.9" fill="#453a74" stroke="#453a74"/>
<polygon points="1505.2,420.3 1502.5,425.9 1491.4,396.3" fill="#ba7569" stroke="#ba7569"/>
<polygon points="1526.3,400.3 1505.2,420.3 1491.4,396.3" fill="#b8746a" stroke="#b8746a"/>
<polygon points="1589.5,339.2 1592.1,304.1 1600,360" fill="#9e6665" stroke="#9e6665"/>
<polygon points="1552.3,351.1 1589.5,339.2 1600,360" fill="#a66a67" stroke="#a66a67"/>
<polygon points="1544.4,396.6 1552.3,351.1 1557.9,398.4" fill="#a96b64" stroke="#a96b64"/>
<polygon points="1552.3,351.1 1544.4,396.6 1526.3,400.3" fill="#ab6c65" stroke="#ab6c65"/>
<polygon points="1148.3,896.2 1181.5,895.9 1200,900" fill="#252351" stroke="#252351"/>
<polygon points="1181.5,895.9 1202.4,877.9 1200,900" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1319.7,867.3 1372.5,864.7 1300,900" fill="#211f49" stroke="#211f49"/>
<polygon points="1319.7,867.3 1328.3,824.7 1372.5,864.7" fill="#29275a" stroke="#29275a"/>
<polygon points="1264.5,875.1 1319.7,867.3 1300,900" fill="#211f48" stroke="#211f48"/>
<polygon points="1393.5,793.8 1356.3,801.5 1359.2,762.3" fill="#282557" stroke="#282557"/>
<polygon points="1356.3,801.5 1288.3,754.9 1359.2,762.3" fill="#262454" stroke="#262454"/>
<polygon points="1372.5,864.7 1356.3,801.5 1402.9,843.7" fill="#282658" stroke="#282658"/>
<polygon points="1328.3,824.7 1356.3,801.5 1372.5,864.7" fill="#201f48" stroke="#201f48"/>
<polygon points="1569.1,869.7 1592.1,865 1600,900" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="1550,866.8 1569.1,869.7 1600,900" fill="#262454" stroke="#262454"/>
<polygon points="1592.1,865 1569.1,869.7 1543.3,826.5" fill="#272556" stroke="#272556"/>
<polygon points="1414.9,896.6 1500,900 1400,900" fill="#262453" stroke="#262453"/>
<polygon points="1414.9,896.6 1475.9,894.2 1500,900" fill="#252351" stroke="#252351"/>
<polygon points="1475.9,894.2 1414.9,896.6 1467.9,880.6" fill="#21204a" stroke="#21204a"/>
<polygon points="1414.9,896.6 1400,900 1402.9,843.7" fill="#272556" stroke="#272556"/>
<polygon points="1467.9,880.6 1414.9,896.6 1402.9,843.7" fill="#29275b" stroke="#29275b"/>
<polygon points="1474,858.9 1467.9,880.6 1402.9,843.7" fill="#24224f" stroke="#24224f"/>
<polygon points="1468.4,829.6 1474,858.9 1402.9,843.7" fill="#23214d" stroke="#23214d"/>
<polygon points="1408.6,820.4 1468.4,829.6 1402.9,843.7" fill="#23214d" stroke="#23214d"/>
<polygon points="1356.3,801.5 1408.6,820.4 1402.9,843.7" fill="#262454" stroke="#262454"/>
<polygon points="1408.6,820.4 1356.3,801.5 1393.5,793.8" fill="#22204b" stroke="#22204b"/>
<polygon points="1552.3,703.3 1543.5,698.6 1582.7,651.6" fill="#201e47" stroke="#201e47"/>
<polygon points="1543.5,698.6 1552.3,703.3 1506.7,768" fill="#201e46" stroke="#201e46"/>
<polygon points="1552.3,703.3 1569.4,726.9 1506.7,768" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1591.6,736.8 1582.9,708.5 1600,720" fill="#272557" stroke="#272557"/>
<polygon points="1569.4,726.9 1582.9,708.5 1591.6,736.8" fill="#211f48" stroke="#211f48"/>
<polygon points="1552.3,703.3 1582.9,708.5 1569.4,726.9" fill="#262453" stroke="#262453"/>
<polygon points="1582.9,708.5 1582.7,651.6 1600,720" fill="#282658" stroke="#282658"/>
<polygon points="1582.9,708.5 1552.3,703.3 1582.7,651.6" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="1152.5,397.3 1140.3,379.2 1163.8,361.8" fill="#624883" stroke="#624883"/>
<polygon points="1140.3,379.2 1139.1,372.1 1163.8,361.8" fill="#664b88" stroke="#664b88"/>
<polygon points="1139.1,372.1 1140.3,379.2 1114.2,372.3" fill="#6b4f8f" stroke="#6b4f8f"/>
<polygon points="1215.9,403.3 1187.8,416.1 1200.5,381.1" fill="#c2a1c9" stroke="#c2a1c9"/>
<polygon points="1215.9,403.3 1231.6,407.9 1187.8,416.1" fill="#af91b6" stroke="#af91b6"/>
<polygon points="1304.3,545.9 1269.9,508.1 1313.1,499.1" fill="#634984" stroke="#634984"/>
<polygon points="1269.9,508.1 1304.3,545.9 1274.6,540.2" fill="#725498" stroke="#725498"/>
<polygon points="1251,713.6 1246.6,744.9 1238.8,711.7" fill="#29275a" stroke="#29275a"/>
<polygon points="1231.3,621.8 1240.9,666 1178.5,657.8" fill="#201f48" stroke="#201f48"/>
<polygon points="1240.9,666 1233.4,676.2 1178.5,657.8" fill="#242250" stroke="#242250"/>
<polygon points="1204.5,701.9 1233.4,676.2 1238.8,711.7" fill="#201e46" stroke="#201e46"/>
<polygon points="1540.8,0.9 1500,0 1600,0" fill="#1b204d" stroke="#1b204d"/>
<polygon points="1540.8,0.9 1540,21.2 1500,0" fill="#1f214e" stroke="#1f214e"/>
<polygon points="1540.8,0.9 1572,29.8 1540,21.2" fill="#22234e" stroke="#22234e"/>
<polygon points="1483.4,112.3 1517.1,88.7 1572.6,154.1" fill="#4b3957" stroke="#4b3957"/>
<polygon points="1567.1,231.4 1581.6,210 1600,270" fill="#79525f" stroke="#79525f"/>
<polygon points="1569,260.5 1567.1,231.4 1600,270" fill="#7b535d" stroke="#7b535d"/>
<polygon points="1567.1,231.4 1569,260.5 1565.4,233.2" fill="#79525e" stroke="#79525e"/>
<polygon points="1441.7,178.1 1410.7,186.1 1429.9,167.7" fill="#62465b" stroke="#62465b"/>
<polygon points="1410.7,186.1 1441.7,178.1 1452.2,196.6" fill="#624559" stroke="#624559"/>
<polygon points="1217.3,87.8 1168.3,46.2 1200,0" fill="#2a264a" stroke="#2a264a"/>
<polygon points="1270.2,10.1 1217.3,87.8 1200,0" fill="#27254c" stroke="#27254c"/>
<polygon points="1168.3,46.2 1217.3,87.8 1187.4,100.1" fill="#392f52" stroke="#392f52"/>
<polygon points="1224.8,139.1 1217.3,87.8 1265.8,117.8" fill="#43334f" stroke="#43334f"/>
<polygon points="1270.3,107.6 1270.2,10.1 1315.5,20.2" fill="#2a264b" stroke="#2a264b"/>
<polygon points="1217.3,87.8 1270.3,107.6 1265.8,117.8" fill="#443655" stroke="#443655"/>
<polygon points="1270.3,107.6 1217.3,87.8 1270.2,10.1" fill="#362d51" stroke="#362d51"/>
<polygon points="1323.9,111.7 1332.1,43.7 1366.5,64.5" fill="#342b4c" stroke="#342b4c"/>
<polygon points="1332.1,43.7 1315.5,20.2 1400,0" fill="#24244e" stroke="#24244e"/>
<polygon points="1366.5,64.5 1332.1,43.7 1400,0" fill="#29264e" stroke="#29264e"/>
<polygon points="1332.1,43.7 1270.3,107.6 1315.5,20.2" fill="#302a4e" stroke="#302a4e"/>
<polygon points="1270.3,107.6 1332.1,43.7 1323.9,111.7" fill="#3d3152" stroke="#3d3152"/>
<polygon points="1410.7,186.1 1355.5,137.5 1429.9,167.7" fill="#553d53" stroke="#553d53"/>
<polygon points="1321.6,170.8 1355.5,137.5 1410.7,186.1" fill="#563e53" stroke="#563e53"/>
<polygon points="12.5,417.5 0,448.2 0,360" fill="#b9746a" stroke="#b9746a"/>
<polygon points="0,448.2 30.4,451.1 0,450" fill="#b697bd" stroke="#b697bd"/>
<polygon points="0,448.2 12.5,417.5 30.4,451.1" fill="#c8a6d0" stroke="#c8a6d0"/>
<polygon points="36,624.7 49.4,662.9 0,630" fill="#201e47" stroke="#201e47"/>
<polygon points="223.3,492 246.4,470.4 267,493.9" fill="#604780" stroke="#604780"/>
<polygon points="246.4,470.4 267.9,378.9 300.6,446.2" fill="#735499" stroke="#735499"/>
<polygon points="267.9,378.9 246.4,470.4 190.8,401.9" fill="#634985" stroke="#634985"/>
<polygon points="199.6,459.6 246.4,470.4 223.3,492" fill="#715397" stroke="#715397"/>
<polygon points="199.6,459.6 178.1,459.3 190.8,401.9" fill="#5d447c" stroke="#5d447c"/>
<polygon points="246.4,470.4 199.6,459.6 190.8,401.9" fill="#7758a0" stroke="#7758a0"/>
<polygon points="40.3,485.4 30.4,451.1 56.3,463.5" fill="#715397" stroke="#715397"/>
<polygon points="40.3,485.4 25.5,487.7 30.4,451.1" fill="#75569d" stroke="#75569d"/>
<polygon points="60.2,472.5 40.3,485.4 56.3,463.5" fill="#5a4278" stroke="#5a4278"/>
<polygon points="73.8,521.8 40.3,485.4 60.2,472.5" fill="#393060" stroke="#393060"/>
<polygon points="3,515.3 0,540 0,450" fill="#7d5ca7" stroke="#7d5ca7"/>
<polygon points="30.4,451.1 12.2,490 0,450" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="25.5,487.7 12.2,490 30.4,451.1" fill="#5b437a" stroke="#5b437a"/>
<polygon points="12.2,490 3,515.3 0,450" fill="#74569c" stroke="#74569c"/>
<polygon points="59.4,445.6 67.8,433.3 93.3,452.4" fill="#75569d" stroke="#75569d"/>
<polygon points="59.4,445.6 82.1,474.4 56.3,463.5" fill="#7c5ba6" stroke="#7c5ba6"/>
<polygon points="82.1,474.4 59.4,445.6 93.3,452.4" fill="#77589f" stroke="#77589f"/>
<polygon points="108.2,460.6 129.7,447 113.1,495.7" fill="#5f4680" stroke="#5f4680"/>
<polygon points="129.7,447 108.2,460.6 93.3,452.4" fill="#674c8a" stroke="#674c8a"/>
<polygon points="82.1,474.4 108.2,460.6 113.1,495.7" fill="#584175" stroke="#584175"/>
<polygon points="108.2,460.6 82.1,474.4 93.3,452.4" fill="#77589f" stroke="#77589f"/>
<polygon points="175.7,291.9 185.2,263.8 208.5,313.1" fill="#8f5e64" stroke="#8f5e64"/>
<polygon points="175.7,291.9 151.2,278.1 185.2,263.8" fill="#895b62" stroke="#895b62"/>
<polygon points="151.2,278.1 123.6,277.2 144.3,246.7" fill="#855962" stroke="#855962"/>
<polygon points="123.6,277.2 117.3,239.1 144.3,246.7" fill="#775059" stroke="#775059"/>
<polygon points="123.6,277.2 107.1,300.9 85.9,287.1" fill="#8b5c61" stroke="#8b5c61"/>
<polygon points="31.8,578.9 36,552.3 62.8,547.1" fill="#40366d" stroke="#40366d"/>
<polygon points="0,540 31.8,578.9 19.4,593.5" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="36,552.3 31.8,578.9 0,540" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="117.3,732.9 125.3,744.5 102,733.2" fill="#23214e" stroke="#23214e"/>
<polygon points="86.6,714.6 117.3,732.9 102,733.2" fill="#252352" stroke="#252352"/>
<polygon points="101.6,687.5 86.6,714.6 49.4,662.9" fill="#262454" stroke="#262454"/>
<polygon points="117.3,732.9 101.6,687.5 124.7,674.6" fill="#201e47" stroke="#201e47"/>
<polygon points="101.6,687.5 117.3,732.9 86.6,714.6" fill="#272556" stroke="#272556"/>
<polygon points="142.8,551.7 114.5,552.9 145.7,535.8" fill="#41376e" stroke="#41376e"/>
<polygon points="145.7,535.8 114.5,552.9 113.1,495.7" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="114.5,552.9 73.8,521.8 113.1,495.7" fill="#3a3162" stroke="#3a3162"/>
<polygon points="131.4,566.8 114.5,552.9 142.8,551.7" fill="#41376e" stroke="#41376e"/>
<polygon points="73.8,521.8 114.5,552.9 62.8,547.1" fill="#493d7b" stroke="#493d7b"/>
<polygon points="114.5,552.9 131.4,566.8 124.6,582.2" fill="#4e4284" stroke="#4e4284"/>
<polygon points="145.7,535.8 149,524.3 184.9,542.6" fill="#715397" stroke="#715397"/>
<polygon points="149,524.3 157.7,506.4 184.9,542.6" fill="#74559b" stroke="#74559b"/>
<polygon points="149,524.3 145.7,535.8 113.1,495.7" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="157.7,506.4 149,524.3 113.1,495.7" fill="#6d5092" stroke="#6d5092"/>
<polygon points="185.7,479.9 157.7,506.4 161.6,474.3" fill="#725498" stroke="#725498"/>
<polygon points="185.7,479.9 199.6,459.6 223.3,492" fill="#7858a1" stroke="#7858a1"/>
<polygon points="185.7,479.9 223.3,492 184.9,542.6" fill="#75569d" stroke="#75569d"/>
<polygon points="157.7,506.4 185.7,479.9 184.9,542.6" fill="#574075" stroke="#574075"/>
<polygon points="178.1,459.3 185.7,479.9 161.6,474.3" fill="#5a4278" stroke="#5a4278"/>
<polygon points="199.6,459.6 185.7,479.9 178.1,459.3" fill="#7b5ba4" stroke="#7b5ba4"/>
<polygon points="300.1,362.7 320.5,394 288.4,372.6" fill="#c7a5cf" stroke="#c7a5cf"/>
<polygon points="320.5,394 300.1,362.7 321.2,351" fill="#957b9b" stroke="#957b9b"/>
<polygon points="294,348.4 300.1,362.7 288.4,372.6" fill="#cca9d3" stroke="#cca9d3"/>
<polygon points="300.1,362.7 294,348.4 321.2,351" fill="#c4a2cb" stroke="#c4a2cb"/>
<polygon points="320.5,394 317.4,445 300.6,446.2" fill="#584176" stroke="#584176"/>
<polygon points="317.4,445 320.5,394 352.6,455.1" fill="#73559a" stroke="#73559a"/>
<polygon points="253.5,303.1 216.1,310.5 239,277.6" fill="#8e5d61" stroke="#8e5d61"/>
<polygon points="253.5,303.1 239,277.6 252,269.8" fill="#8a5b61" stroke="#8a5b61"/>
<polygon points="262.4,306.8 253.5,303.1 252,269.8" fill="#87585d" stroke="#87585d"/>
<polygon points="216.1,310.5 253.5,303.1 240,349.2" fill="#946061" stroke="#946061"/>
<polygon points="404.4,194.9 457.7,197.1 410.1,274.2" fill="#6d4b59" stroke="#6d4b59"/>
<polygon points="398.7,318.2 385.8,283.2 410.1,274.2" fill="#997e9e" stroke="#997e9e"/>
<polygon points="385.8,283.2 398.7,318.2 369.1,295.9" fill="#a286a8" stroke="#a286a8"/>
<polygon points="301.3,53.7 344.6,43.3 348.5,62.7" fill="#2f2a4f" stroke="#2f2a4f"/>
<polygon points="301.3,53.7 341.9,36.4 344.6,43.3" fill="#2c284f" stroke="#2c284f"/>
<polygon points="214.4,54.4 242.1,73.1 237.3,89.7" fill="#382f53" stroke="#382f53"/>
<polygon points="242.1,73.1 253.1,93.7 237.3,89.7" fill="#3d3254" stroke="#3d3254"/>
<polygon points="153.5,159.3 190.1,183.3 154,183.3" fill="#5f4459" stroke="#5f4459"/>
<polygon points="190.1,183.3 153.5,159.3 166.3,144" fill="#543d52" stroke="#543d52"/>
<polygon points="115.7,186.3 153.5,159.3 154,183.3" fill="#5c4256" stroke="#5c4256"/>
<polygon points="166.3,144 153.5,159.3 158.7,130.9" fill="#553f59" stroke="#553f59"/>
<polygon points="153.5,159.3 130.1,120.8 158.7,130.9" fill="#4e3a54" stroke="#4e3a54"/>
<polygon points="130.1,120.8 153.5,159.3 96.6,150.8" fill="#4d3951" stroke="#4d3951"/>
<polygon points="153.5,159.3 115.7,186.3 96.6,150.8" fill="#5b4258" stroke="#5b4258"/>
<polygon points="196.6,216 215.5,183.8 231.5,220.5" fill="#6a495a" stroke="#6a495a"/>
<polygon points="190.1,183.3 215.5,183.8 196.6,216" fill="#67485a" stroke="#67485a"/>
<polygon points="215.5,183.8 190.1,183.3 202.2,165.9" fill="#5f4358" stroke="#5f4358"/>
<polygon points="215.5,183.8 274.4,162.9 231.5,220.5" fill="#604456" stroke="#604456"/>
<polygon points="201.1,154.9 215.5,183.8 202.2,165.9" fill="#594055" stroke="#594055"/>
<polygon points="274.4,162.9 215.5,183.8 201.1,154.9" fill="#573e53" stroke="#573e53"/>
<polygon points="210.7,25.4 209.7,24.3 248.1,8" fill="#22224b" stroke="#22224b"/>
<polygon points="209.7,24.3 210.7,25.4 214.4,54.4" fill="#28264d" stroke="#28264d"/>
<polygon points="73.8,151.8 95.1,124.3 96.6,150.8" fill="#4e3952" stroke="#4e3952"/>
<polygon points="73.8,151.8 67,149.2 95.1,124.3" fill="#4d3951" stroke="#4d3951"/>
<polygon points="115.7,186.3 73.8,151.8 96.6,150.8" fill="#5a4157" stroke="#5a4157"/>
<polygon points="67,149.2 73.8,151.8 43.2,182.9" fill="#594057" stroke="#594057"/>
<polygon points="50.8,192.8 115.7,186.3 112.8,226.9" fill="#6a4a5b" stroke="#6a4a5b"/>
<polygon points="50.8,192.8 28.7,222.4 43.2,182.9" fill="#664758" stroke="#664758"/>
<polygon points="73.8,151.8 50.8,192.8 43.2,182.9" fill="#594053" stroke="#594053"/>
<polygon points="50.8,192.8 73.8,151.8 115.7,186.3" fill="#5e4357" stroke="#5e4357"/>
<polygon points="374.7,167.2 347.4,141.2 399.5,157.8" fill="#543d54" stroke="#543d54"/>
<polygon points="404.4,194.9 374.7,167.2 399.5,157.8" fill="#60455a" stroke="#60455a"/>
<polygon points="253.1,93.7 287.4,102 274.4,162.9" fill="#463551" stroke="#463551"/>
<polygon points="881.4,89.7 855.1,104 861.9,76.5" fill="#3f3253" stroke="#3f3253"/>
<polygon points="855.1,104 774.6,56.3 861.9,76.5" fill="#392f51" stroke="#392f51"/>
<polygon points="855.1,104 902.1,129.7 830.2,145.3" fill="#493752" stroke="#493752"/>
<polygon points="855.1,104 881.4,89.7 902.1,129.7" fill="#443553" stroke="#443553"/>
<polygon points="869.6,44.1 902.1,23 885.2,71.6" fill="#2a264a" stroke="#2a264a"/>
<polygon points="902.1,23 869.6,44.1 900,0" fill="#22224a" stroke="#22224a"/>
<polygon points="902.1,23 945.4,81.7 885.2,71.6" fill="#322b50" stroke="#322b50"/>
<polygon points="902.1,23 994.2,17.8 945.4,81.7" fill="#28254a" stroke="#28254a"/>
<polygon points="994.2,17.8 902.1,23 900,0" fill="#20214c" stroke="#20214c"/>
<polygon points="1100,0 1047.7,19 1000,0" fill="#1e214e" stroke="#1e214e"/>
<polygon points="1047.7,19 994.2,17.8 1000,0" fill="#21234f" stroke="#21234f"/>
<polygon points="1047.7,19 1100,0 1123.4,66.7" fill="#26254f" stroke="#26254f"/>
<polygon points="1082.8,109.8 1047.7,19 1123.4,66.7" fill="#352d52" stroke="#352d52"/>
<polygon points="1047.7,19 1082.8,109.8 1034.9,103.7" fill="#372e4f" stroke="#372e4f"/>
<polygon points="999.1,209.5 1020.8,242.3 1001.5,245.6" fill="#a87a68" stroke="#a87a68"/>
<polygon points="955.4,203.1 883,202.6 944.5,180.8" fill="#624556" stroke="#624556"/>
<polygon points="955.4,203.1 929.1,242.8 883,202.6" fill="#a07464" stroke="#a07464"/>
<polygon points="955.4,203.1 999.1,209.5 1001.5,245.6" fill="#a17565" stroke="#a17565"/>
<polygon points="929.1,242.8 955.4,203.1 1001.5,245.6" fill="#a37665" stroke="#a37665"/>
<polygon points="834.1,268.2 856,223.7 873.6,258.8" fill="#764f5a" stroke="#764f5a"/>
<polygon points="838.8,225.4 856,223.7 834.1,268.2" fill="#734e5a" stroke="#734e5a"/>
<polygon points="856,223.7 883,202.6 873.6,258.8" fill="#704c5a" stroke="#704c5a"/>
<polygon points="883,202.6 856,223.7 830.2,145.3" fill="#634558" stroke="#634558"/>
<polygon points="856,223.7 838.8,225.4 830.2,145.3" fill="#694a5c" stroke="#694a5c"/>
<polygon points="1032,310.4 1097.4,283.8 1114.2,372.3" fill="#5c447b" stroke="#5c447b"/>
<polygon points="1097.4,283.8 1136,288.8 1114.2,372.3" fill="#77589f" stroke="#77589f"/>
<polygon points="1136,288.8 1097.4,283.8 1117.7,268.9" fill="#ac8fb3" stroke="#ac8fb3"/>
<polygon points="1097.4,283.8 1091.8,266.2 1117.7,268.9" fill="#bd9dc4" stroke="#bd9dc4"/>
<polygon points="994.9,313.3 1025.4,284.7 1032,310.4" fill="#a589ab" stroke="#a589ab"/>
<polygon points="1097.4,283.8 1073.1,261.5 1091.8,266.2" fill="#cdaad5" stroke="#cdaad5"/>
<polygon points="1073.1,261.5 1097.4,283.8 1046.6,267" fill="#b091b6" stroke="#b091b6"/>
<polygon points="755.6,244.6 768,233 783.3,238.9" fill="#734e5a" stroke="#734e5a"/>
<polygon points="755.6,244.6 767.2,291.9 747.8,256" fill="#835861" stroke="#835861"/>
<polygon points="767.2,291.9 755.6,244.6 783.3,238.9" fill="#825761" stroke="#825761"/>
<polygon points="755.6,244.6 747.8,256 729.6,212.8" fill="#724d59" stroke="#724d59"/>
<polygon points="768,233 755.6,244.6 729.6,212.8" fill="#6e4b58" stroke="#6e4b58"/>
<polygon points="1166.9,219.7 1203.7,130.7 1221.9,202.5" fill="#63465a" stroke="#63465a"/>
<polygon points="1203.7,130.7 1224.8,139.1 1221.9,202.5" fill="#584057" stroke="#584057"/>
<polygon points="1203.7,130.7 1151,118.7 1187.4,100.1" fill="#453550" stroke="#453550"/>
<polygon points="1217.3,87.8 1203.7,130.7 1187.4,100.1" fill="#463756" stroke="#463756"/>
<polygon points="1203.7,130.7 1217.3,87.8 1224.8,139.1" fill="#483753" stroke="#483753"/>
<polygon points="1317.2,373.2 1304,326 1357.5,349.8" fill="#a76b68" stroke="#a76b68"/>
<polygon points="1304,326 1324.2,309.5 1357.5,349.8" fill="#976262" stroke="#976262"/>
<polygon points="1319.8,401 1351.5,380 1351.5,404.6" fill="#b57269" stroke="#b57269"/>
<polygon points="1351.5,380 1319.8,401 1317.2,373.2" fill="#a66962" stroke="#a66962"/>
<polygon points="1319.8,401 1272.2,384 1317.2,373.2" fill="#d49374" stroke="#d49374"/>
<polygon points="1381.3,411.7 1356.8,432.9 1351.5,404.6" fill="#c3a1ca" stroke="#c3a1ca"/>
<polygon points="1356.8,432.9 1371.6,442.5 1329.2,477.4" fill="#5a4279" stroke="#5a4279"/>
<polygon points="1313.2,417.4 1356.8,432.9 1329.2,477.4" fill="#c4a2cb" stroke="#c4a2cb"/>
<polygon points="1146.9,233.7 1120.7,216.2 1166.9,219.7" fill="#ab7c6b" stroke="#ab7c6b"/>
<polygon points="1146.9,233.7 1132.8,238.8 1120.7,216.2" fill="#b1806e" stroke="#b1806e"/>
<polygon points="1084.8,170.8 1082.8,109.8 1122.8,126.6" fill="#503c56" stroke="#503c56"/>
<polygon points="1163.8,361.8 1173.7,320 1185.8,352.8" fill="#cfabd6" stroke="#cfabd6"/>
<polygon points="1164.3,310 1173.7,320 1163.8,361.8" fill="#cba8d3" stroke="#cba8d3"/>
<polygon points="1173.7,320 1202.2,328.5 1185.8,352.8" fill="#bf866d" stroke="#bf866d"/>
<polygon points="1173.7,320 1164.3,310 1190.5,276" fill="#b48069" stroke="#b48069"/>
<polygon points="1202.2,328.5 1173.7,320 1190.5,276" fill="#c48a71" stroke="#c48a71"/>
<polygon points="1197.1,256.8 1210.3,254.8 1190.5,276" fill="#b07e6a" stroke="#b07e6a"/>
<polygon points="1197.1,256.8 1166.9,219.7 1221.9,202.5" fill="#b1806e" stroke="#b1806e"/>
<polygon points="1210.3,254.8 1197.1,256.8 1221.9,202.5" fill="#af7e6c" stroke="#af7e6c"/>
<polygon points="1234.1,214.4 1210.3,254.8 1221.9,202.5" fill="#734f5d" stroke="#734f5d"/>
<polygon points="1255.2,168.2 1234.1,214.4 1221.9,202.5" fill="#654759" stroke="#654759"/>
<polygon points="1234.1,214.4 1255.2,168.2 1315.6,200.3" fill="#614456" stroke="#614456"/>
<polygon points="1226.7,387.2 1244.2,342.1 1272.2,384" fill="#ca8c70" stroke="#ca8c70"/>
<polygon points="1244.2,342.1 1226.7,387.2 1205.4,360.6" fill="#cc8e71" stroke="#cc8e71"/>
<polygon points="1215.9,403.3 1226.7,387.2 1231.6,407.9" fill="#937a98" stroke="#937a98"/>
<polygon points="1205.4,360.6 1226.7,387.2 1200.5,381.1" fill="#c98b6e" stroke="#c98b6e"/>
<polygon points="1226.7,387.2 1215.9,403.3 1200.5,381.1" fill="#c8a6cf" stroke="#c8a6cf"/>
<polygon points="697.3,45.9 684.5,51.4 700,0" fill="#26244b" stroke="#26244b"/>
<polygon points="723.1,40.5 697.3,45.9 700,0" fill="#242349" stroke="#242349"/>
<polygon points="684.5,51.4 697.3,45.9 719.5,106.1" fill="#332b4d" stroke="#332b4d"/>
<polygon points="697.3,45.9 723.1,40.5 719.5,106.1" fill="#332b4f" stroke="#332b4f"/>
<polygon points="761.7,83 855.1,104 830.2,145.3" fill="#433451" stroke="#433451"/>
<polygon points="763.7,75.7 723.1,40.5 742.4,28.7" fill="#2d284e" stroke="#2d284e"/>
<polygon points="763.7,75.7 761.7,83 723.1,40.5" fill="#332b4d" stroke="#332b4d"/>
<polygon points="855.1,104 763.7,75.7 774.6,56.3" fill="#382e50" stroke="#382e50"/>
<polygon points="761.7,83 763.7,75.7 855.1,104" fill="#3e3254" stroke="#3e3254"/>
<polygon points="783.1,26.6 772.5,49.5 742.4,28.7" fill="#292750" stroke="#292750"/>
<polygon points="772.5,49.5 783.1,26.6 774.6,56.3" fill="#2c2950" stroke="#2c2950"/>
<polygon points="772.5,49.5 763.7,75.7 742.4,28.7" fill="#2e294e" stroke="#2e294e"/>
<polygon points="763.7,75.7 772.5,49.5 774.6,56.3" fill="#322b4f" stroke="#322b4f"/>
<polygon points="592.1,121.5 602.7,68.7 610.9,119" fill="#443554" stroke="#443554"/>
<polygon points="592.1,121.5 548.5,93.9 602.7,68.7" fill="#3d304f" stroke="#3d304f"/>
<polygon points="592.1,121.5 570.4,156.6 538.8,103.5" fill="#4a3852" stroke="#4a3852"/>
<polygon points="548.5,93.9 592.1,121.5 538.8,103.5" fill="#40324f" stroke="#40324f"/>
<polygon points="449,156.9 399.5,157.8 471.8,116.5" fill="#513c54" stroke="#513c54"/>
<polygon points="461.2,150.9 449,156.9 471.8,116.5" fill="#4f3a53" stroke="#4f3a53"/>
<polygon points="449,156.9 404.4,194.9 399.5,157.8" fill="#5d4258" stroke="#5d4258"/>
<polygon points="404.4,194.9 449,156.9 457.7,197.1" fill="#5f4356" stroke="#5f4356"/>
<polygon points="449,156.9 461.2,150.9 466.7,190" fill="#5b4258" stroke="#5b4258"/>
<polygon points="457.7,197.1 449,156.9 466.7,190" fill="#604458" stroke="#604458"/>
<polygon points="110.1,820.4 100.3,855.8 78.8,832.9" fill="#201e46" stroke="#201e46"/>
<polygon points="70.6,802.9 110.1,820.4 78.8,832.9" fill="#262455" stroke="#262455"/>
<polygon points="135.1,861 110.1,820.4 128.7,809.7" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="100.3,855.8 110.1,820.4 135.1,861" fill="#262454" stroke="#262454"/>
<polygon points="310.7,483 306.8,492.2 285.2,485.9" fill="#684d8b" stroke="#684d8b"/>
<polygon points="404.6,627.1 424.1,631.9 395.3,633.8" fill="#24224f" stroke="#24224f"/>
<polygon points="424.1,631.9 404.6,627.1 422.6,604.4" fill="#3a3163" stroke="#3a3163"/>
<polygon points="467.4,494.7 545.4,494.9 542,543.3" fill="#4d4182" stroke="#4d4182"/>
<polygon points="479.6,467.6 545.4,494.9 467.4,494.7" fill="#473b77" stroke="#473b77"/>
<polygon points="545.4,494.9 583.6,537.8 542,543.3" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="545.4,494.9 563.6,479.3 563.9,483.6" fill="#3f356a" stroke="#3f356a"/>
<polygon points="592.4,500.4 545.4,494.9 563.9,483.6" fill="#473b77" stroke="#473b77"/>
<polygon points="545.4,494.9 592.4,500.4 583.6,537.8" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="488.4,578.2 476.8,564.4 542,543.3" fill="#41376e" stroke="#41376e"/>
<polygon points="394.5,465.8 352.6,455.1 401.3,437.2" fill="#634985" stroke="#634985"/>
<polygon points="431.5,298.2 398.7,318.2 410.1,274.2" fill="#ceabd6" stroke="#ceabd6"/>
<polygon points="398.7,318.2 431.5,298.2 431.6,317.8" fill="#a084a6" stroke="#a084a6"/>
<polygon points="546.5,262.1 501.6,235.5 527.1,204.7" fill="#77515e" stroke="#77515e"/>
<polygon points="501.6,235.5 546.5,262.1 513.9,273.8" fill="#815660" stroke="#815660"/>
<polygon points="501.6,235.5 466.7,190 527.1,204.7" fill="#684857" stroke="#684857"/>
<polygon points="501.6,235.5 457.7,197.1 466.7,190" fill="#644555" stroke="#644555"/>
<polygon points="395.1,333.1 431.6,317.8 443.9,341.1" fill="#604781" stroke="#604781"/>
<polygon points="395.1,333.1 398.7,318.2 431.6,317.8" fill="#664b88" stroke="#664b88"/>
<polygon points="386.5,352.2 395.1,333.1 443.9,341.1" fill="#73559a" stroke="#73559a"/>
<polygon points="398.7,318.2 395.1,333.1 369.1,295.9" fill="#c5a3cc" stroke="#c5a3cc"/>
<polygon points="207.3,664.7 247.2,703.4 183.5,687.1" fill="#252352" stroke="#252352"/>
<polygon points="247.2,703.4 193,723.9 183.5,687.1" fill="#262455" stroke="#262455"/>
<polygon points="247.2,703.4 264.1,719.2 193,723.9" fill="#24224f" stroke="#24224f"/>
<polygon points="269.3,647.3 247.2,703.4 207.3,664.7" fill="#41366d" stroke="#41366d"/>
<polygon points="247.2,703.4 269.3,647.3 299.3,703.1" fill="#4d4182" stroke="#4d4182"/>
<polygon points="264.1,719.2 247.2,703.4 299.3,703.1" fill="#3f356b" stroke="#3f356b"/>
<polygon points="259.2,765.9 264.1,719.2 275.1,727.7" fill="#282659" stroke="#282659"/>
<polygon points="264.1,719.2 259.2,765.9 193,723.9" fill="#211f48" stroke="#211f48"/>
<polygon points="427.2,714.8 415.3,704.2 445.9,705.1" fill="#24224f" stroke="#24224f"/>
<polygon points="433.3,738.9 427.2,714.8 445.9,705.1" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="407.3,734.5 357.6,710.5 415.3,704.2" fill="#23214d" stroke="#23214d"/>
<polygon points="427.2,714.8 407.3,734.5 415.3,704.2" fill="#272556" stroke="#272556"/>
<polygon points="357.6,710.5 407.3,734.5 345.7,770.8" fill="#29275b" stroke="#29275b"/>
<polygon points="362.4,693.8 344.2,708.2 351.5,688.3" fill="#282659" stroke="#282659"/>
<polygon points="357.6,710.5 344.2,708.2 362.4,693.8" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="344.2,708.2 299.3,703.1 351.5,688.3" fill="#24224f" stroke="#24224f"/>
<polygon points="344.2,708.2 331.6,737.5 299.3,703.1" fill="#29275a" stroke="#29275a"/>
<polygon points="344.2,708.2 357.6,710.5 331.6,737.5" fill="#262455" stroke="#262455"/>
<polygon points="385.3,614.5 404.6,627.1 395.3,633.8" fill="#282659" stroke="#282659"/>
<polygon points="319.1,658.2 315.5,624.7 325.1,629.7" fill="#493d7b" stroke="#493d7b"/>
<polygon points="329.8,652.4 319.1,658.2 325.1,629.7" fill="#3f356b" stroke="#3f356b"/>
<polygon points="319.1,658.2 281,640 315.5,624.7" fill="#3a3163" stroke="#3a3163"/>
<polygon points="299.3,703.1 319.1,658.2 351.5,688.3" fill="#282558" stroke="#282558"/>
<polygon points="319.1,658.2 329.8,652.4 351.5,688.3" fill="#29275a" stroke="#29275a"/>
<polygon points="269.3,647.3 319.1,658.2 299.3,703.1" fill="#4e4183" stroke="#4e4183"/>
<polygon points="281,640 280.5,640.3 281.3,607.3" fill="#3f356b" stroke="#3f356b"/>
<polygon points="280.5,640.3 319.1,658.2 269.3,647.3" fill="#453a74" stroke="#453a74"/>
<polygon points="319.1,658.2 280.5,640.3 281,640" fill="#4c4080" stroke="#4c4080"/>
<polygon points="186,648.2 202.5,644.1 207.3,664.7" fill="#3e3469" stroke="#3e3469"/>
<polygon points="202.5,644.1 269.3,647.3 207.3,664.7" fill="#443972" stroke="#443972"/>
<polygon points="179.4,595.7 186,648.2 135.6,643.2" fill="#382f5f" stroke="#382f5f"/>
<polygon points="179.4,595.7 135.6,643.2 124.6,582.2" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="131.4,566.8 179.4,595.7 124.6,582.2" fill="#463a75" stroke="#463a75"/>
<polygon points="179.4,595.7 131.4,566.8 142.8,551.7" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="938.6,804.4 921.9,849 888.5,849.9" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="921.9,849 938.6,804.4 943.4,832.3" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="973,874.6 921.9,849 943.4,832.3" fill="#29265a" stroke="#29265a"/>
<polygon points="900,900 922.3,897.9 1000,900" fill="#242251" stroke="#242251"/>
<polygon points="922.3,897.9 921.9,849 973,874.6" fill="#272455" stroke="#272455"/>
<polygon points="884.7,780.1 903.4,772.2 917.1,781.3" fill="#211f48" stroke="#211f48"/>
<polygon points="903.4,772.2 884.7,780.1 892.2,766.2" fill="#252351" stroke="#252351"/>
<polygon points="867.3,881.9 819.3,877.5 853.2,817.6" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="819.3,877.5 801,863.4 853.2,817.6" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="801,863.4 819.3,877.5 800,900" fill="#262455" stroke="#262455"/>
<polygon points="882.8,530.3 913.7,563 860.6,570.5" fill="#4f4285" stroke="#4f4285"/>
<polygon points="906.1,638.6 913.7,563 957.6,623.7" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="868.4,886.6 881.4,895.1 800,900" fill="#242250" stroke="#242250"/>
<polygon points="819.3,877.5 868.4,886.6 800,900" fill="#23214d" stroke="#23214d"/>
<polygon points="868.4,886.6 819.3,877.5 867.3,881.9" fill="#23214d" stroke="#23214d"/>
<polygon points="889,890.8 867.3,881.9 888.5,849.9" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="881.4,895.1 889,890.8 900,900" fill="#262453" stroke="#262453"/>
<polygon points="889,890.8 868.4,886.6 867.3,881.9" fill="#272556" stroke="#272556"/>
<polygon points="868.4,886.6 889,890.8 881.4,895.1" fill="#272556" stroke="#272556"/>
<polygon points="921.9,849 889,890.8 888.5,849.9" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="889,890.8 922.3,897.9 900,900" fill="#262454" stroke="#262454"/>
<polygon points="922.3,897.9 889,890.8 921.9,849" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="759,705.5 757.1,694.2 767.4,695.2" fill="#282658" stroke="#282658"/>
<polygon points="774.8,731.2 759,705.5 767.4,695.2" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="833.2,711.2 774.8,731.2 767.4,695.2" fill="#252352" stroke="#252352"/>
<polygon points="774.5,734.3 774.8,731.2 840,748.3" fill="#252352" stroke="#252352"/>
<polygon points="774.8,731.2 833.2,711.2 840,748.3" fill="#211f48" stroke="#211f48"/>
<polygon points="833.2,711.2 836.5,711.3 840,748.3" fill="#272455" stroke="#272455"/>
<polygon points="634.5,791.5 623.3,788.1 649.5,756.2" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="623.3,788.1 616.1,750.6 649.5,756.2" fill="#262454" stroke="#262454"/>
<polygon points="567.2,858.4 623.3,788.1 651.9,841.1" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="623.3,788.1 634.5,791.5 651.9,841.1" fill="#2a275c" stroke="#2a275c"/>
<polygon points="657.2,743.7 693.3,787 649.5,756.2" fill="#282658" stroke="#282658"/>
<polygon points="657.2,743.7 731.5,753.4 693.3,787" fill="#252352" stroke="#252352"/>
<polygon points="657.2,743.7 649.5,756.2 629.9,739.6" fill="#262454" stroke="#262454"/>
<polygon points="633.8,712.4 657.2,743.7 629.9,739.6" fill="#282658" stroke="#282658"/>
<polygon points="760.2,739 774.8,731.2 774.5,734.3" fill="#201e46" stroke="#201e46"/>
<polygon points="774.8,731.2 760.2,739 759,705.5" fill="#282658" stroke="#282658"/>
<polygon points="737.3,765.9 726.6,805 693.3,787" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="731.5,753.4 737.3,765.9 693.3,787" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="726.6,805 737.3,765.9 764.5,807.4" fill="#272455" stroke="#272455"/>
<polygon points="1208.4,847.8 1136,819.1 1214.5,826.6" fill="#242250" stroke="#242250"/>
<polygon points="1249.5,831.5 1208.4,847.8 1214.5,826.6" fill="#272455" stroke="#272455"/>
<polygon points="1208.4,847.8 1264.5,875.1 1205.1,861.3" fill="#24224f" stroke="#24224f"/>
<polygon points="1208.4,847.8 1249.5,831.5 1264.5,875.1" fill="#201e47" stroke="#201e47"/>
<polygon points="1112.1,832.1 1148.3,896.2 1086.5,861.1" fill="#282659" stroke="#282659"/>
<polygon points="1246.3,820.7 1249.5,831.5 1214.5,826.6" fill="#24224f" stroke="#24224f"/>
<polygon points="692,650.2 653.5,621.8 680.2,621.8" fill="#3c3265" stroke="#3c3265"/>
<polygon points="594.9,654.6 598,609.3 626.8,655.7" fill="#4f4285" stroke="#4f4285"/>
<polygon points="598.3,664.9 594.9,654.6 626.8,655.7" fill="#463b76" stroke="#463b76"/>
<polygon points="594.9,654.6 598.3,664.9 568.2,667.3" fill="#3e3469" stroke="#3e3469"/>
<polygon points="539.2,632 594.9,654.6 568.2,667.3" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="598,609.3 594.9,654.6 539.2,632" fill="#40366c" stroke="#40366c"/>
<polygon points="882.8,530.3 857.8,537.2 868.5,519.3" fill="#4e4284" stroke="#4e4284"/>
<polygon points="847,551 857.8,537.2 860.6,570.5" fill="#3a3162" stroke="#3a3162"/>
<polygon points="857.8,537.2 882.8,530.3 860.6,570.5" fill="#3e3468" stroke="#3e3468"/>
<polygon points="816.8,498.8 814.4,497.6 842.8,501.5" fill="#493d7b" stroke="#493d7b"/>
<polygon points="816.8,498.8 841.3,504.6 847,551" fill="#4d4182" stroke="#4d4182"/>
<polygon points="841.3,504.6 816.8,498.8 842.8,501.5" fill="#483c79" stroke="#483c79"/>
<polygon points="841.3,504.6 857.8,537.2 847,551" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="841.3,504.6 842.8,501.5 868.5,519.3" fill="#483c79" stroke="#483c79"/>
<polygon points="857.8,537.2 841.3,504.6 868.5,519.3" fill="#4c4080" stroke="#4c4080"/>
<polygon points="709,505.2 660.8,479.5 691.7,456.4" fill="#957b9a" stroke="#957b9a"/>
<polygon points="660.8,479.5 709,505.2 676.7,522.4" fill="#bb9bc2" stroke="#bb9bc2"/>
<polygon points="658.2,579.9 637.5,590.5 635.9,559.7" fill="#4c4080" stroke="#4c4080"/>
<polygon points="637.5,590.5 653.5,621.8 598,609.3" fill="#41376e" stroke="#41376e"/>
<polygon points="653.5,621.8 637.5,590.5 664,602.3" fill="#4d4081" stroke="#4d4081"/>
<polygon points="548.6,604.8 569.8,557.5 598,609.3" fill="#4d4182" stroke="#4d4182"/>
<polygon points="572.5,448.7 568.2,450.4 572.4,428.4" fill="#c3796a" stroke="#c3796a"/>
<polygon points="593.5,488.4 577.9,453.2 616.7,438.1" fill="#ce806e" stroke="#ce806e"/>
<polygon points="593.5,488.4 592.4,500.4 563.9,483.6" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="563.6,479.3 593.5,488.4 563.9,483.6" fill="#463a75" stroke="#463a75"/>
<polygon points="637.9,488.1 593.5,488.4 616.7,438.1" fill="#c67a68" stroke="#c67a68"/>
<polygon points="593.5,488.4 637.9,488.1 619.3,507.1" fill="#a084a6" stroke="#a084a6"/>
<polygon points="592.4,500.4 593.5,488.4 619.3,507.1" fill="#493d7b" stroke="#493d7b"/>
<polygon points="546.5,262.1 585.3,253.8 585.2,311.7" fill="#82565e" stroke="#82565e"/>
<polygon points="585.3,253.8 598.6,306.9 585.2,311.7" fill="#8c5c61" stroke="#8c5c61"/>
<polygon points="600.6,252.1 585.3,253.8 596.9,248.8" fill="#754f59" stroke="#754f59"/>
<polygon points="598.6,306.9 585.3,253.8 622.2,278.5" fill="#81555c" stroke="#81555c"/>
<polygon points="585.3,253.8 600.6,252.1 622.2,278.5" fill="#815660" stroke="#815660"/>
<polygon points="574.6,205.7 585.3,253.8 546.5,262.1" fill="#7a535f" stroke="#7a535f"/>
<polygon points="487.2,297.1 474,318.9 454.2,294.7" fill="#8a5b5e" stroke="#8a5b5e"/>
<polygon points="505.2,354.2 487.2,297.1 523.1,295.5" fill="#936062" stroke="#936062"/>
<polygon points="474,318.9 487.2,297.1 505.2,354.2" fill="#966162" stroke="#966162"/>
<polygon points="714,865.5 694.5,881.6 670.1,859.5" fill="#252351" stroke="#252351"/>
<polygon points="714,865.5 670.1,859.5 668.5,814.3" fill="#282658" stroke="#282658"/>
<polygon points="726.6,805 714,865.5 668.5,814.3" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="714,865.5 726.6,805 764.5,807.4" fill="#2a275c" stroke="#2a275c"/>
<polygon points="731.7,896 714,865.5 757.2,882.6" fill="#252353" stroke="#252353"/>
<polygon points="778.4,818.3 714,865.5 764.5,807.4" fill="#21204a" stroke="#21204a"/>
<polygon points="714,865.5 778.4,818.3 757.2,882.6" fill="#211f49" stroke="#211f49"/>
<polygon points="714,865.5 731.7,896 700,900" fill="#272557" stroke="#272557"/>
<polygon points="694.5,881.6 714,865.5 700,900" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="603.5,720.6 598.3,664.9 633.8,712.4" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="622.9,721.1 603.5,720.6 633.8,712.4" fill="#463b76" stroke="#463b76"/>
<polygon points="598.3,664.9 603.5,720.6 579.6,709.3" fill="#392f5f" stroke="#392f5f"/>
<polygon points="603.5,720.6 592.7,743.7 579.6,709.3" fill="#272556" stroke="#272556"/>
<polygon points="603.5,720.6 616.1,750.6 592.7,743.7" fill="#272556" stroke="#272556"/>
<polygon points="603.5,720.6 622.9,721.1 616.1,750.6" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="1396.4,465.7 1378.9,482.3 1371.6,442.5" fill="#7e5da8" stroke="#7e5da8"/>
<polygon points="1376.1,493.6 1396.4,465.7 1381.8,518.1" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1378.9,482.3 1396.4,465.7 1376.1,493.6" fill="#634985" stroke="#634985"/>
<polygon points="1321.6,170.8 1333.8,220 1315.6,200.3" fill="#67485a" stroke="#67485a"/>
<polygon points="1333.8,220 1321.6,170.8 1410.7,186.1" fill="#5f4254" stroke="#5f4254"/>
<polygon points="1406.4,221.5 1452.2,196.6 1434,249.2" fill="#6d4b59" stroke="#6d4b59"/>
<polygon points="1402.5,240 1406.4,221.5 1434,249.2" fill="#734e5a" stroke="#734e5a"/>
<polygon points="1406.4,221.5 1410.7,186.1 1452.2,196.6" fill="#644656" stroke="#644656"/>
<polygon points="1333.8,220 1406.4,221.5 1402.5,240" fill="#6e4b58" stroke="#6e4b58"/>
<polygon points="1406.4,221.5 1333.8,220 1410.7,186.1" fill="#674757" stroke="#674757"/>
<polygon points="1506.6,219.8 1527.9,237.5 1498.2,255.9" fill="#79525f" stroke="#79525f"/>
<polygon points="1569,260.5 1527.9,237.5 1565.4,233.2" fill="#78515d" stroke="#78515d"/>
<polygon points="1498.2,255.9 1527.9,237.5 1522,289.9" fill="#7d545d" stroke="#7d545d"/>
<polygon points="1527.9,237.5 1569,260.5 1522,289.9" fill="#7c535b" stroke="#7c535b"/>
<polygon points="1483.4,112.3 1487.7,180.9 1485,178.9" fill="#574057" stroke="#574057"/>
<polygon points="1487.7,180.9 1452.2,196.6 1485,178.9" fill="#604356" stroke="#604356"/>
<polygon points="1487.7,180.9 1506.6,219.8 1452.2,196.6" fill="#614455" stroke="#614455"/>
<polygon points="1464.3,425.3 1469.1,425 1471.6,467.2" fill="#393061" stroke="#393061"/>
<polygon points="1469.1,425 1464.3,425.3 1491.4,396.3" fill="#be776c" stroke="#be776c"/>
<polygon points="1464.3,425.3 1403.7,382.9 1491.4,396.3" fill="#b67369" stroke="#b67369"/>
<polygon points="1464.3,425.3 1433.5,427.7 1403.7,382.9" fill="#bb756a" stroke="#bb756a"/>
<polygon points="1494,443.6 1484.3,470.7 1471.6,467.2" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1587.5,509.2 1554.9,527 1562.9,475.3" fill="#7959a2" stroke="#7959a2"/>
<polygon points="1587.5,509.2 1596.9,499.4 1600,540" fill="#382f5f" stroke="#382f5f"/>
<polygon points="1554.9,527 1587.5,509.2 1600,540" fill="#40366c" stroke="#40366c"/>
<polygon points="1593.4,425.5 1562.1,423.8 1557.9,398.4" fill="#bf786c" stroke="#bf786c"/>
<polygon points="1565.5,449.4 1562.1,423.8 1593.4,425.5" fill="#c1796b" stroke="#c1796b"/>
<polygon points="1546.1,462.1 1565.5,449.4 1562.9,475.3" fill="#9c81a1" stroke="#9c81a1"/>
<polygon points="1495.6,634.1 1543.5,698.6 1488.2,668.7" fill="#29265a" stroke="#29265a"/>
<polygon points="1475.9,660.2 1495.6,634.1 1488.2,668.7" fill="#201f48" stroke="#201f48"/>
<polygon points="1495.6,634.1 1475.9,660.2 1474.9,618.8" fill="#4c4080" stroke="#4c4080"/>
<polygon points="1543.5,698.6 1553.8,622.6 1582.7,651.6" fill="#262454" stroke="#262454"/>
<polygon points="1553.8,622.6 1558,619.9 1582.7,651.6" fill="#282659" stroke="#282659"/>
<polygon points="1495.6,634.1 1553.8,622.6 1543.5,698.6" fill="#201e46" stroke="#201e46"/>
<polygon points="1558,619.9 1553.8,622.6 1550.9,589.5" fill="#504488" stroke="#504488"/>
<polygon points="1429.7,741 1432.4,714.2 1445.9,734.3" fill="#262453" stroke="#262453"/>
<polygon points="1399.6,715.6 1404.1,710.7 1414.5,721.2" fill="#272555" stroke="#272555"/>
<polygon points="1404.1,710.7 1399.6,715.6 1378.5,698.6" fill="#282658" stroke="#282658"/>
<polygon points="1339.8,626 1385.7,650 1355.6,667.5" fill="#483c79" stroke="#483c79"/>
<polygon points="1385.7,650 1367.8,671.5 1355.6,667.5" fill="#40366c" stroke="#40366c"/>
<polygon points="1385.7,650 1339,592.2 1431.7,599.9" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1339,592.2 1385.7,650 1339.8,626" fill="#4f4386" stroke="#4f4386"/>
<polygon points="1301,649.5 1339,592.2 1339.8,626" fill="#3d3366" stroke="#3d3366"/>
<polygon points="1301,649.5 1312.4,658.6 1282.2,669.1" fill="#41366d" stroke="#41366d"/>
<polygon points="1301,649.5 1231.3,621.8 1294.3,552.4" fill="#3f356a" stroke="#3f356a"/>
<polygon points="1339,592.2 1301,649.5 1294.3,552.4" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1562.5,324.8 1552.3,351.1 1539.5,336.5" fill="#935f5e" stroke="#935f5e"/>
<polygon points="1562.5,324.8 1589.5,339.2 1552.3,351.1" fill="#9e6665" stroke="#9e6665"/>
<polygon points="1202.4,877.9 1175.3,870.4 1205.1,861.3" fill="#201e46" stroke="#201e46"/>
<polygon points="1181.5,895.9 1175.3,870.4 1202.4,877.9" fill="#262454" stroke="#262454"/>
<polygon points="1175.3,870.4 1181.5,895.9 1148.3,896.2" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1175.3,870.4 1208.4,847.8 1205.1,861.3" fill="#22204c" stroke="#22204c"/>
<polygon points="1208.4,847.8 1175.3,870.4 1136,819.1" fill="#282659" stroke="#282659"/>
<polygon points="1112.1,832.1 1175.3,870.4 1148.3,896.2" fill="#282558" stroke="#282558"/>
<polygon points="1175.3,870.4 1112.1,832.1 1136,819.1" fill="#282658" stroke="#282658"/>
<polygon points="1246.3,820.7 1290.8,815.7 1249.5,831.5" fill="#211f49" stroke="#211f49"/>
<polygon points="1249.5,831.5 1290.8,815.7 1264.5,875.1" fill="#1e1c41" stroke="#1e1c41"/>
<polygon points="1290.8,815.7 1319.7,867.3 1264.5,875.1" fill="#282659" stroke="#282659"/>
<polygon points="1313.1,499.1 1273.5,450.5 1329.2,477.4" fill="#735499" stroke="#735499"/>
<polygon points="1269.9,508.1 1273.5,450.5 1313.1,499.1" fill="#725498" stroke="#725498"/>
<polygon points="1256.9,552.1 1274.6,540.2 1294.3,552.4" fill="#715498" stroke="#715498"/>
<polygon points="1231.3,621.8 1256.9,552.1 1294.3,552.4" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1228.8,561.1 1256.9,552.1 1231.3,621.8" fill="#3e3468" stroke="#3e3468"/>
<polygon points="1266.7,740.1 1246.6,744.9 1251,713.6" fill="#262455" stroke="#262455"/>
<polygon points="1266.7,740.1 1305.6,685.5 1288.3,754.9" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1305.6,685.5 1266.7,740.1 1251,713.6" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1283.8,759.1 1266.7,740.1 1288.3,754.9" fill="#282659" stroke="#282659"/>
<polygon points="1249,763.6 1266.7,740.1 1283.8,759.1" fill="#211f4a" stroke="#211f4a"/>
<polygon points="1266.7,740.1 1249,763.6 1246.6,744.9" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="1260.5,662.6 1240.9,666 1231.3,621.8" fill="#272556" stroke="#272556"/>
<polygon points="1260.5,662.6 1301,649.5 1282.2,669.1" fill="#41366d" stroke="#41366d"/>
<polygon points="1301,649.5 1260.5,662.6 1231.3,621.8" fill="#4d4182" stroke="#4d4182"/>
<polygon points="1206.1,601.3 1165.4,606.8 1208,572.2" fill="#3d3468" stroke="#3d3468"/>
<polygon points="1204.5,701.9 1233.3,751.7 1174.6,731.1" fill="#211f4a" stroke="#211f4a"/>
<polygon points="1174.6,731.1 1233.3,751.7 1227.1,773.3" fill="#282658" stroke="#282658"/>
<polygon points="1246.6,744.9 1233.3,751.7 1238.8,711.7" fill="#29275a" stroke="#29275a"/>
<polygon points="1233.3,751.7 1204.5,701.9 1238.8,711.7" fill="#272556" stroke="#272556"/>
<polygon points="1233.3,751.7 1249,763.6 1227.1,773.3" fill="#262454" stroke="#262454"/>
<polygon points="1249,763.6 1233.3,751.7 1246.6,744.9" fill="#201f48" stroke="#201f48"/>
<polygon points="1233.4,676.2 1195.1,683.3 1178.5,657.8" fill="#242250" stroke="#242250"/>
<polygon points="1204.5,701.9 1195.1,683.3 1233.4,676.2" fill="#211f48" stroke="#211f48"/>
<polygon points="1195.1,683.3 1204.5,701.9 1174.6,731.1" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1600,90 1593.5,75.4 1600,0" fill="#302a4f" stroke="#302a4f"/>
<polygon points="1593.5,75.4 1572,29.8 1600,0" fill="#2a2750" stroke="#2a2750"/>
<polygon points="1582.7,5.7 1540.8,0.9 1600,0" fill="#1b1e49" stroke="#1b1e49"/>
<polygon points="1572,29.8 1582.7,5.7 1600,0" fill="#1e2048" stroke="#1e2048"/>
<polygon points="1582.7,5.7 1572,29.8 1540.8,0.9" fill="#20224d" stroke="#20224d"/>
<polygon points="1499.2,92.5 1482,71.8 1517.1,88.7" fill="#392e4e" stroke="#392e4e"/>
<polygon points="1483.4,112.3 1499.2,92.5 1517.1,88.7" fill="#403352" stroke="#403352"/>
<polygon points="1482,71.8 1448.4,85.2 1455.3,64.6" fill="#352d4e" stroke="#352d4e"/>
<polygon points="1455.3,64.6 1448.4,85.2 1424.2,63.9" fill="#382f53" stroke="#382f53"/>
<polygon points="1567.1,231.4 1549,182.9 1581.6,210" fill="#6b4a5b" stroke="#6b4a5b"/>
<polygon points="1549,182.9 1527.9,237.5 1506.6,219.8" fill="#6f4d5d" stroke="#6f4d5d"/>
<polygon points="1600,180 1549,182.9 1572.6,154.1" fill="#5b4156" stroke="#5b4156"/>
<polygon points="1581.6,210 1549,182.9 1600,180" fill="#604456" stroke="#604456"/>
<polygon points="1487.7,180.9 1549,182.9 1506.6,219.8" fill="#66485a" stroke="#66485a"/>
<polygon points="1549,182.9 1567.1,231.4 1565.4,233.2" fill="#694857" stroke="#694857"/>
<polygon points="1527.9,237.5 1549,182.9 1565.4,233.2" fill="#694856" stroke="#694856"/>
<polygon points="1549,182.9 1483.4,112.3 1572.6,154.1" fill="#584059" stroke="#584059"/>
<polygon points="1549,182.9 1487.7,180.9 1483.4,112.3" fill="#5a4159" stroke="#5a4159"/>
<polygon points="1447.2,170.7 1441.7,178.1 1429.9,167.7" fill="#583f53" stroke="#583f53"/>
<polygon points="1452.2,196.6 1447.2,170.7 1485,178.9" fill="#5f4357" stroke="#5f4357"/>
<polygon points="1441.7,178.1 1447.2,170.7 1452.2,196.6" fill="#5c4154" stroke="#5c4154"/>
<polygon points="1279.4,141.1 1254.5,145.7 1265.8,117.8" fill="#4a3850" stroke="#4a3850"/>
<polygon points="1270.3,107.6 1279.4,141.1 1265.8,117.8" fill="#4a3854" stroke="#4a3854"/>
<polygon points="1279.4,141.1 1270.3,107.6 1323.9,111.7" fill="#4a3956" stroke="#4a3956"/>
<polygon points="1255.2,168.2 1285,156.1 1315.6,200.3" fill="#61455b" stroke="#61455b"/>
<polygon points="1285,156.1 1321.6,170.8 1315.6,200.3" fill="#61455b" stroke="#61455b"/>
<polygon points="1285,156.1 1255.2,168.2 1254.5,145.7" fill="#553e55" stroke="#553e55"/>
<polygon points="1279.4,141.1 1285,156.1 1254.5,145.7" fill="#4f3a51" stroke="#4f3a51"/>
<polygon points="1321.6,170.8 1285,156.1 1323.9,111.7" fill="#533d56" stroke="#533d56"/>
<polygon points="1285,156.1 1279.4,141.1 1323.9,111.7" fill="#4e3a54" stroke="#4e3a54"/>
<polygon points="1348.1,137.6 1323.9,111.7 1354,131.2" fill="#483650" stroke="#483650"/>
<polygon points="1348.1,137.6 1321.6,170.8 1323.9,111.7" fill="#4e3a52" stroke="#4e3a52"/>
<polygon points="1355.5,137.5 1348.1,137.6 1354,131.2" fill="#4b3851" stroke="#4b3851"/>
<polygon points="1321.6,170.8 1348.1,137.6 1355.5,137.5" fill="#574059" stroke="#574059"/>
<polygon points="267,493.9 262.9,477.3 285.2,485.9" fill="#715498" stroke="#715498"/>
<polygon points="246.4,470.4 262.9,477.3 267,493.9" fill="#7d5ca8" stroke="#7d5ca8"/>
<polygon points="285.2,485.9 262.9,477.3 300.6,446.2" fill="#5f467f" stroke="#5f467f"/>
<polygon points="262.9,477.3 246.4,470.4 300.6,446.2" fill="#664b89" stroke="#664b89"/>
<polygon points="40.3,485.4 28.2,523.9 25.5,487.7" fill="#3f3469" stroke="#3f3469"/>
<polygon points="28.2,523.9 12.2,490 25.5,487.7" fill="#3a3162" stroke="#3a3162"/>
<polygon points="12.2,490 28.2,523.9 3,515.3" fill="#504386" stroke="#504386"/>
<polygon points="28.2,523.9 40.3,485.4 73.8,521.8" fill="#4f4285" stroke="#4f4285"/>
<polygon points="28.2,523.9 73.8,521.8 62.8,547.1" fill="#3d3366" stroke="#3d3366"/>
<polygon points="36,552.3 28.2,523.9 62.8,547.1" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="12.5,417.5 29.7,399.6 30.4,451.1" fill="#967c9c" stroke="#967c9c"/>
<polygon points="29.7,399.6 12.5,417.5 0,360" fill="#b17068" stroke="#b17068"/>
<polygon points="175.7,291.9 162.6,300.3 151.2,278.1" fill="#8d5d62" stroke="#8d5d62"/>
<polygon points="123.6,277.2 136.7,301.6 107.1,300.9" fill="#8f5e63" stroke="#8f5e63"/>
<polygon points="132.5,365 136.7,431.7 105.1,408.5" fill="#5d457d" stroke="#5d457d"/>
<polygon points="132.5,365 177.6,372.1 136.7,431.7" fill="#7e5da8" stroke="#7e5da8"/>
<polygon points="132.5,365 64.9,359.2 85.8,329.3" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="132.5,365 85.8,329.3 95.5,326.1" fill="#c7a4ce" stroke="#c7a4ce"/>
<polygon points="129.1,340.6 132.5,365 95.5,326.1" fill="#c5a3cc" stroke="#c5a3cc"/>
<polygon points="2.2,628 36,624.7 0,630" fill="#23214e" stroke="#23214e"/>
<polygon points="2.2,628 34.7,603.9 36,624.7" fill="#211f49" stroke="#211f49"/>
<polygon points="2.2,628 0,630 19.4,593.5" fill="#3f356a" stroke="#3f356a"/>
<polygon points="34.7,603.9 2.2,628 19.4,593.5" fill="#3c3366" stroke="#3c3366"/>
<polygon points="36,624.7 42.1,598.9 60.1,605.9" fill="#272556" stroke="#272556"/>
<polygon points="34.7,603.9 42.1,598.9 36,624.7" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="124.7,674.6 103,635.3 135.6,643.2" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="135.6,643.2 103,635.3 124.6,582.2" fill="#3a3162" stroke="#3a3162"/>
<polygon points="103,635.3 87.9,632.3 124.6,582.2" fill="#3d3366" stroke="#3d3366"/>
<polygon points="101.6,687.5 103,635.3 124.7,674.6" fill="#2a285d" stroke="#2a285d"/>
<polygon points="117.3,732.9 156.4,733.5 125.3,744.5" fill="#272556" stroke="#272556"/>
<polygon points="125.3,744.5 156.4,733.5 144.4,773.4" fill="#1e1c43" stroke="#1e1c43"/>
<polygon points="156.4,733.5 117.3,732.9 124.7,674.6" fill="#272557" stroke="#272557"/>
<polygon points="67.8,648.1 101.6,687.5 49.4,662.9" fill="#272556" stroke="#272556"/>
<polygon points="36,624.7 67.8,648.1 49.4,662.9" fill="#272556" stroke="#272556"/>
<polygon points="103,635.3 67.8,648.1 87.9,632.3" fill="#201e46" stroke="#201e46"/>
<polygon points="67.8,648.1 103,635.3 101.6,687.5" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="67.8,648.1 36,624.7 60.1,605.9" fill="#211f49" stroke="#211f49"/>
<polygon points="87.9,632.3 67.8,648.1 60.1,605.9" fill="#282558" stroke="#282558"/>
<polygon points="114.5,552.9 77.6,573 62.8,547.1" fill="#453a74" stroke="#453a74"/>
<polygon points="77.6,573 31.8,578.9 62.8,547.1" fill="#40356b" stroke="#40356b"/>
<polygon points="77.6,573 114.5,552.9 124.6,582.2" fill="#3d3367" stroke="#3d3367"/>
<polygon points="42.1,598.9 77.6,573 60.1,605.9" fill="#40366d" stroke="#40366d"/>
<polygon points="77.6,573 42.1,598.9 31.8,578.9" fill="#42376f" stroke="#42376f"/>
<polygon points="87.9,632.3 77.6,573 124.6,582.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="77.6,573 87.9,632.3 60.1,605.9" fill="#3b3163" stroke="#3b3163"/>
<polygon points="330.8,317.8 325,301.6 355,295.4" fill="#89595c" stroke="#89595c"/>
<polygon points="321.2,351 330.8,317.8 386.5,352.2" fill="#b797be" stroke="#b797be"/>
<polygon points="330.8,317.8 355,295.4 369.1,295.9" fill="#ab8db1" stroke="#ab8db1"/>
<polygon points="330.8,317.8 395.1,333.1 386.5,352.2" fill="#75579d" stroke="#75579d"/>
<polygon points="395.1,333.1 330.8,317.8 369.1,295.9" fill="#ad8fb3" stroke="#ad8fb3"/>
<polygon points="267,311.3 267.6,355.9 240,349.2" fill="#9b6462" stroke="#9b6462"/>
<polygon points="253.5,303.1 267,311.3 240,349.2" fill="#966162" stroke="#966162"/>
<polygon points="267,311.3 253.5,303.1 262.4,306.8" fill="#905e61" stroke="#905e61"/>
<polygon points="323.5,216.3 298.3,268.5 315.1,204.5" fill="#6f4c59" stroke="#6f4c59"/>
<polygon points="298.3,268.5 262.4,306.8 252,269.8" fill="#8c5c63" stroke="#8c5c63"/>
<polygon points="231.5,220.5 298.3,268.5 252,269.8" fill="#77505a" stroke="#77505a"/>
<polygon points="315.1,204.5 298.3,268.5 231.5,220.5" fill="#77515f" stroke="#77515f"/>
<polygon points="363.2,248.6 404.4,194.9 410.1,274.2" fill="#724d59" stroke="#724d59"/>
<polygon points="385.8,283.2 363.2,248.6 410.1,274.2" fill="#845860" stroke="#845860"/>
<polygon points="363.2,248.6 298.3,268.5 323.5,216.3" fill="#77505c" stroke="#77505c"/>
<polygon points="355,295.4 363.2,248.6 369.1,295.9" fill="#7f545a" stroke="#7f545a"/>
<polygon points="363.2,248.6 385.8,283.2 369.1,295.9" fill="#875a61" stroke="#875a61"/>
<polygon points="341.9,36.4 293.2,21.7 341,22.8" fill="#25244d" stroke="#25244d"/>
<polygon points="301.3,53.7 293.2,21.7 341.9,36.4" fill="#27254a" stroke="#27254a"/>
<polygon points="293.2,21.7 300,0 341,22.8" fill="#22234f" stroke="#22234f"/>
<polygon points="300,0 293.2,21.7 248.1,8" fill="#1f224e" stroke="#1f224e"/>
<polygon points="225.7,52 242.1,73.1 214.4,54.4" fill="#322b50" stroke="#322b50"/>
<polygon points="225.7,52 210.7,25.4 248.1,8" fill="#242349" stroke="#242349"/>
<polygon points="210.7,25.4 225.7,52 214.4,54.4" fill="#2d2951" stroke="#2d2951"/>
<polygon points="242.1,73.1 264.1,56.8 253.1,93.7" fill="#342c4c" stroke="#342c4c"/>
<polygon points="264.1,56.8 293.2,21.7 301.3,53.7" fill="#2d2951" stroke="#2d2951"/>
<polygon points="293.2,21.7 264.1,56.8 248.1,8" fill="#27254f" stroke="#27254f"/>
<polygon points="264.1,56.8 225.7,52 248.1,8" fill="#2b2850" stroke="#2b2850"/>
<polygon points="225.7,52 264.1,56.8 242.1,73.1" fill="#342d52" stroke="#342d52"/>
<polygon points="58.5,237.4 50.8,192.8 112.8,226.9" fill="#6f4d5c" stroke="#6f4d5c"/>
<polygon points="50.8,192.8 58.5,237.4 28.7,222.4" fill="#6c4a59" stroke="#6c4a59"/>
<polygon points="14.5,272.3 85.9,287.1 5.8,306.4" fill="#87595f" stroke="#87595f"/>
<polygon points="14.5,272.3 24.4,262.7 85.9,287.1" fill="#81565d" stroke="#81565d"/>
<polygon points="0.2,272.6 14.5,272.3 5.8,306.4" fill="#87595f" stroke="#87595f"/>
<polygon points="14.5,272.3 0.2,272.6 0,270" fill="#845860" stroke="#845860"/>
<polygon points="24.4,262.7 14.5,272.3 0,270" fill="#82575f" stroke="#82575f"/>
<polygon points="347.4,141.2 337.4,96.3 353.6,100.6" fill="#453652" stroke="#453652"/>
<polygon points="337.4,96.3 354.2,77.1 353.6,100.6" fill="#3f3353" stroke="#3f3353"/>
<polygon points="354.2,77.1 337.4,96.3 348.5,62.7" fill="#362c4c" stroke="#362c4c"/>
<polygon points="337.4,96.3 301.3,53.7 348.5,62.7" fill="#382f53" stroke="#382f53"/>
<polygon points="1005.7,45.3 1047.7,19 1034.9,103.7" fill="#2f294d" stroke="#2f294d"/>
<polygon points="1047.7,19 1005.7,45.3 994.2,17.8" fill="#24234b" stroke="#24234b"/>
<polygon points="1073.1,261.5 1061.7,248.3 1080,234.9" fill="#b2806d" stroke="#b2806d"/>
<polygon points="1061.7,248.3 1073.1,261.5 1046.6,267" fill="#b17f6b" stroke="#b17f6b"/>
<polygon points="940.2,274.3 929.1,242.8 1001.5,245.6" fill="#ad7c69" stroke="#ad7c69"/>
<polygon points="929.1,242.8 940.2,274.3 873.6,258.8" fill="#a97a66" stroke="#a97a66"/>
<polygon points="1013.9,131.5 994.6,152 956,152.2" fill="#533e57" stroke="#533e57"/>
<polygon points="1005.2,156.7 994.6,152 1013.9,131.5" fill="#523c55" stroke="#523c55"/>
<polygon points="994.6,152 944.5,180.8 956,152.2" fill="#563e54" stroke="#563e54"/>
<polygon points="994.6,152 955.4,203.1 944.5,180.8" fill="#5d4256" stroke="#5d4256"/>
<polygon points="955.4,203.1 994.6,152 999.1,209.5" fill="#a27769" stroke="#a27769"/>
<polygon points="1025.4,284.7 1033.5,283.1 1032,310.4" fill="#a589ab" stroke="#a589ab"/>
<polygon points="1033.5,283.1 1097.4,283.8 1032,310.4" fill="#bc9cc3" stroke="#bc9cc3"/>
<polygon points="1097.4,283.8 1033.5,283.1 1046.6,267" fill="#b293b9" stroke="#b293b9"/>
<polygon points="1033.5,283.1 1020.8,242.3 1046.6,267" fill="#ab7b67" stroke="#ab7b67"/>
<polygon points="1033.5,283.1 1025.4,284.7 1020.8,242.3" fill="#af7d69" stroke="#af7d69"/>
<polygon points="1087.2,235.3 1090.6,234.8 1091.8,266.2" fill="#a87967" stroke="#a87967"/>
<polygon points="1073.1,261.5 1087.2,235.3 1091.8,266.2" fill="#aa7a67" stroke="#aa7a67"/>
<polygon points="1087.2,235.3 1073.1,261.5 1080,234.9" fill="#b2806d" stroke="#b2806d"/>
<polygon points="819.8,380.6 815,357.9 842.9,359.8" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="815,357.9 819.8,380.6 789.2,374.5" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="776,344.6 815,357.9 789.2,374.5" fill="#9d6561" stroke="#9d6561"/>
<polygon points="776.9,366.4 789.2,374.5 756.8,385.9" fill="#a98caf" stroke="#a98caf"/>
<polygon points="776,344.6 776.9,366.4 756.8,385.9" fill="#ac6e69" stroke="#ac6e69"/>
<polygon points="776.9,366.4 776,344.6 789.2,374.5" fill="#a76b66" stroke="#a76b66"/>
<polygon points="776,344.6 714.6,323.1 767.2,291.9" fill="#905e5f" stroke="#905e5f"/>
<polygon points="714.6,323.1 688.2,340.2 695.3,256.6" fill="#8e5d60" stroke="#8e5d60"/>
<polygon points="688.2,340.2 714.6,323.1 756.8,385.9" fill="#996260" stroke="#996260"/>
<polygon points="714.6,323.1 776,344.6 756.8,385.9" fill="#9d6462" stroke="#9d6462"/>
<polygon points="747.8,256 714.6,323.1 695.3,256.6" fill="#7f545b" stroke="#7f545b"/>
<polygon points="767.2,291.9 714.6,323.1 747.8,256" fill="#895a5f" stroke="#895a5f"/>
<polygon points="1203.7,130.7 1138.1,142.3 1151,118.7" fill="#4d3a55" stroke="#4d3a55"/>
<polygon points="1151,118.7 1138.1,142.3 1122.8,126.6" fill="#4c3954" stroke="#4c3954"/>
<polygon points="1138.1,142.3 1203.7,130.7 1166.9,219.7" fill="#5b4258" stroke="#5b4258"/>
<polygon points="1324.2,309.5 1285.3,314.8 1280.2,304.2" fill="#956164" stroke="#956164"/>
<polygon points="1304,326 1285.3,314.8 1324.2,309.5" fill="#976264" stroke="#976264"/>
<polygon points="1244.2,342.1 1286.1,359.2 1272.2,384" fill="#c78a6e" stroke="#c78a6e"/>
<polygon points="1272.2,384 1286.1,359.2 1317.2,373.2" fill="#cd8f71" stroke="#cd8f71"/>
<polygon points="1286.1,359.2 1304,326 1317.2,373.2" fill="#a26865" stroke="#a26865"/>
<polygon points="1286.1,359.2 1285.3,314.8 1304,326" fill="#9c6564" stroke="#9c6564"/>
<polygon points="1323,406.6 1319.8,401 1351.5,404.6" fill="#b9746a" stroke="#b9746a"/>
<polygon points="1319.8,401 1323,406.6 1313.2,417.4" fill="#a286a8" stroke="#a286a8"/>
<polygon points="1356.8,432.9 1323,406.6 1351.5,404.6" fill="#9e83a4" stroke="#9e83a4"/>
<polygon points="1323,406.6 1356.8,432.9 1313.2,417.4" fill="#c3a1ca" stroke="#c3a1ca"/>
<polygon points="1356.8,432.9 1379.3,430.9 1371.6,442.5" fill="#a589ab" stroke="#a589ab"/>
<polygon points="1379.3,430.9 1356.8,432.9 1381.3,411.7" fill="#a68aac" stroke="#a68aac"/>
<polygon points="1379.3,430.9 1381.3,411.7 1417.3,429" fill="#ba9ac1" stroke="#ba9ac1"/>
<polygon points="1379.3,430.9 1396.4,465.7 1371.6,442.5" fill="#cdaad5" stroke="#cdaad5"/>
<polygon points="1082.8,109.8 1033,133.1 1034.9,103.7" fill="#443450" stroke="#443450"/>
<polygon points="1033,133.1 1013.9,131.5 1034.9,103.7" fill="#4c3a57" stroke="#4c3a57"/>
<polygon points="1041.2,227.7 1020.8,242.3 999.1,209.5" fill="#a27565" stroke="#a27565"/>
<polygon points="1061.7,248.3 1041.2,227.7 1080,234.9" fill="#b4826f" stroke="#b4826f"/>
<polygon points="1020.8,242.3 1041.2,227.7 1046.6,267" fill="#b2806d" stroke="#b2806d"/>
<polygon points="1041.2,227.7 1061.7,248.3 1046.6,267" fill="#a77866" stroke="#a77866"/>
<polygon points="1067.3,207.5 1087.2,235.3 1080,234.9" fill="#a67867" stroke="#a67867"/>
<polygon points="1041.2,227.7 1067.3,207.5 1080,234.9" fill="#a57867" stroke="#a57867"/>
<polygon points="1087.2,195.6 1126.3,185.2 1120.7,216.2" fill="#a27768" stroke="#a27768"/>
<polygon points="1090.6,234.8 1087.2,195.6 1120.7,216.2" fill="#a27666" stroke="#a27666"/>
<polygon points="1087.2,235.3 1087.2,195.6 1090.6,234.8" fill="#a37666" stroke="#a37666"/>
<polygon points="1067.3,207.5 1087.2,195.6 1087.2,235.3" fill="#a77969" stroke="#a77969"/>
<polygon points="1203.4,337 1205.4,360.6 1185.8,352.8" fill="#ca8d71" stroke="#ca8d71"/>
<polygon points="1202.2,328.5 1203.4,337 1185.8,352.8" fill="#c0866d" stroke="#c0866d"/>
<polygon points="1203.4,337 1244.2,342.1 1205.4,360.6" fill="#bc836a" stroke="#bc836a"/>
<polygon points="1203.4,337 1202.2,328.5 1244.2,342.1" fill="#ba8269" stroke="#ba8269"/>
<polygon points="1210.3,254.8 1249.4,279.4 1190.5,276" fill="#ab7a66" stroke="#ab7a66"/>
<polygon points="1249.4,279.4 1202.2,328.5 1190.5,276" fill="#c28972" stroke="#c28972"/>
<polygon points="1234.1,214.4 1249.4,279.4 1210.3,254.8" fill="#b2806d" stroke="#b2806d"/>
<polygon points="1202.2,328.5 1249.4,279.4 1244.2,342.1" fill="#b9826a" stroke="#b9826a"/>
<polygon points="1157.7,238.8 1197.1,256.8 1190.5,276" fill="#b7846f" stroke="#b7846f"/>
<polygon points="1157.7,238.8 1190.5,276 1136,288.8" fill="#aa7a66" stroke="#aa7a66"/>
<polygon points="1157.7,238.8 1146.9,233.7 1166.9,219.7" fill="#b2816e" stroke="#b2816e"/>
<polygon points="1197.1,256.8 1157.7,238.8 1166.9,219.7" fill="#a47665" stroke="#a47665"/>
<polygon points="1132.8,238.8 1157.7,238.8 1136,288.8" fill="#ab7a67" stroke="#ab7a67"/>
<polygon points="1146.9,233.7 1157.7,238.8 1132.8,238.8" fill="#a47765" stroke="#a47765"/>
<polygon points="1261.1,399.9 1272.2,384 1270.3,399.9" fill="#d59374" stroke="#d59374"/>
<polygon points="1261.1,399.9 1226.7,387.2 1272.2,384" fill="#c98b6d" stroke="#c98b6d"/>
<polygon points="723.1,40.5 745.8,100.3 719.5,106.1" fill="#392f50" stroke="#392f50"/>
<polygon points="761.7,83 745.8,100.3 723.1,40.5" fill="#362d4f" stroke="#362d4f"/>
<polygon points="745.8,100.3 722.3,116.7 719.5,106.1" fill="#443553" stroke="#443553"/>
<polygon points="722.3,116.7 745.8,100.3 749,166.8" fill="#4d3b56" stroke="#4d3b56"/>
<polygon points="749,166.8 745.8,100.3 830.2,145.3" fill="#4f3b55" stroke="#4f3b55"/>
<polygon points="745.8,100.3 761.7,83 830.2,145.3" fill="#42334f" stroke="#42334f"/>
<polygon points="618.1,123.2 602.9,128 610.9,119" fill="#463550" stroke="#463550"/>
<polygon points="602.9,128 592.1,121.5 610.9,119" fill="#4a3955" stroke="#4a3955"/>
<polygon points="592.1,121.5 602.9,128 570.4,156.6" fill="#4d3a54" stroke="#4d3a54"/>
<polygon points="603,177.2 599.9,225.4 574.6,205.7" fill="#684859" stroke="#684859"/>
<polygon points="596.9,248.8 599.9,225.4 615.4,245.2" fill="#714d58" stroke="#714d58"/>
<polygon points="599.9,225.4 642.4,202.9 615.4,245.2" fill="#6d4b58" stroke="#6d4b58"/>
<polygon points="599.9,225.4 603,177.2 642.4,202.9" fill="#6b4b5d" stroke="#6b4b5d"/>
<polygon points="585.3,253.8 599.9,225.4 596.9,248.8" fill="#7c5460" stroke="#7c5460"/>
<polygon points="599.9,225.4 585.3,253.8 574.6,205.7" fill="#6f4c59" stroke="#6f4c59"/>
<polygon points="125.9,802 70.6,802.9 113,768" fill="#201f48" stroke="#201f48"/>
<polygon points="125.9,802 110.1,820.4 70.6,802.9" fill="#24224f" stroke="#24224f"/>
<polygon points="110.1,820.4 125.9,802 128.7,809.7" fill="#211f48" stroke="#211f48"/>
<polygon points="125.9,802 144.4,773.4 128.7,809.7" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="125.9,802 113,768 144.4,773.4" fill="#29265a" stroke="#29265a"/>
<polygon points="429.2,631.2 424.1,631.9 422.6,604.4" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="424.1,631.9 429.2,631.2 453.7,662.9" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="536.5,577.4 488.4,578.2 542,543.3" fill="#433870" stroke="#433870"/>
<polygon points="388.1,539.5 394.5,465.8 423.1,505.6" fill="#4c4080" stroke="#4c4080"/>
<polygon points="394.5,465.8 388.1,539.5 352.6,455.1" fill="#514488" stroke="#514488"/>
<polygon points="388.1,539.5 476.8,564.4 422.6,604.4" fill="#4e4183" stroke="#4e4183"/>
<polygon points="388.1,539.5 423.1,505.6 476.8,564.4" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="394.5,465.8 426.3,491.9 423.1,505.6" fill="#504387" stroke="#504387"/>
<polygon points="479.6,467.6 426.3,491.9 401.3,437.2" fill="#725499" stroke="#725499"/>
<polygon points="426.3,491.9 394.5,465.8 401.3,437.2" fill="#7758a0" stroke="#7758a0"/>
<polygon points="427.5,282.7 431.5,298.2 410.1,274.2" fill="#be9ec5" stroke="#be9ec5"/>
<polygon points="431.5,298.2 427.5,282.7 454.2,294.7" fill="#b293b8" stroke="#b293b8"/>
<polygon points="457.7,197.1 427.5,282.7 410.1,274.2" fill="#79515c" stroke="#79515c"/>
<polygon points="501.6,235.5 427.5,282.7 457.7,197.1" fill="#714c58" stroke="#714c58"/>
<polygon points="427.5,282.7 501.6,235.5 454.2,294.7" fill="#80555d" stroke="#80555d"/>
<polygon points="431.5,298.2 445.1,303.4 431.6,317.8" fill="#c5a3cd" stroke="#c5a3cd"/>
<polygon points="445.1,303.4 431.5,298.2 454.2,294.7" fill="#a589ac" stroke="#a589ac"/>
<polygon points="474,318.9 445.1,303.4 454.2,294.7" fill="#c7a5ce" stroke="#c7a5ce"/>
<polygon points="431.6,317.8 445.1,303.4 443.9,341.1" fill="#a185a7" stroke="#a185a7"/>
<polygon points="445.1,303.4 474,318.9 443.9,341.1" fill="#c5a3cd" stroke="#c5a3cd"/>
<polygon points="293.6,762.9 259.2,765.9 275.1,727.7" fill="#282658" stroke="#282658"/>
<polygon points="293.6,762.9 275.1,727.7 331.6,737.5" fill="#282659" stroke="#282659"/>
<polygon points="327.8,773.1 293.6,762.9 331.6,737.5" fill="#201e46" stroke="#201e46"/>
<polygon points="223.6,867.1 298.5,822 301.5,864.7" fill="#211f49" stroke="#211f49"/>
<polygon points="293.6,762.9 298.5,822 259.2,765.9" fill="#201e47" stroke="#201e47"/>
<polygon points="298.5,822 293.6,762.9 327.8,773.1" fill="#262454" stroke="#262454"/>
<polygon points="301.5,864.7 298.5,822 357.8,829.6" fill="#262353" stroke="#262353"/>
<polygon points="298.5,822 327.8,773.1 357.8,829.6" fill="#262455" stroke="#262455"/>
<polygon points="144.4,773.4 197.2,792.5 128.7,809.7" fill="#262454" stroke="#262454"/>
<polygon points="259.2,765.9 197.2,792.5 193,723.9" fill="#262453" stroke="#262453"/>
<polygon points="156.4,733.5 197.2,792.5 144.4,773.4" fill="#29275b" stroke="#29275b"/>
<polygon points="197.2,792.5 156.4,733.5 193,723.9" fill="#201f48" stroke="#201f48"/>
<polygon points="417.2,735.9 427.2,714.8 433.3,738.9" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="417.2,735.9 407.3,734.5 427.2,714.8" fill="#211f49" stroke="#211f49"/>
<polygon points="348.6,617.8 365.9,621 349.7,646" fill="#282658" stroke="#282658"/>
<polygon points="349.7,646 365.9,621 395.3,633.8" fill="#252352" stroke="#252352"/>
<polygon points="365.9,621 385.3,614.5 395.3,633.8" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="202.5,644.1 245.5,613 269.3,647.3" fill="#40356b" stroke="#40356b"/>
<polygon points="245.5,613 233.9,598.6 281.3,607.3" fill="#463b76" stroke="#463b76"/>
<polygon points="280.5,640.3 245.5,613 281.3,607.3" fill="#3f356a" stroke="#3f356a"/>
<polygon points="245.5,613 280.5,640.3 269.3,647.3" fill="#4f4285" stroke="#4f4285"/>
<polygon points="213.4,617.2 206.5,593 233.9,598.6" fill="#705396" stroke="#705396"/>
<polygon points="245.5,613 213.4,617.2 233.9,598.6" fill="#634985" stroke="#634985"/>
<polygon points="213.4,617.2 245.5,613 202.5,644.1" fill="#624883" stroke="#624883"/>
<polygon points="213.4,617.2 179.4,595.7 206.5,593" fill="#604781" stroke="#604781"/>
<polygon points="213.4,617.2 202.5,644.1 186,648.2" fill="#3c3265" stroke="#3c3265"/>
<polygon points="179.4,595.7 213.4,617.2 186,648.2" fill="#514488" stroke="#514488"/>
<polygon points="206.5,593 183.8,591 184.9,542.6" fill="#76579e" stroke="#76579e"/>
<polygon points="179.4,595.7 183.8,591 206.5,593" fill="#6b4f90" stroke="#6b4f90"/>
<polygon points="183.8,591 142.8,551.7 184.9,542.6" fill="#5e467e" stroke="#5e467e"/>
<polygon points="183.8,591 179.4,595.7 142.8,551.7" fill="#504387" stroke="#504387"/>
<polygon points="922.3,897.9 985.3,883.5 1000,900" fill="#23214e" stroke="#23214e"/>
<polygon points="985.3,883.5 922.3,897.9 973,874.6" fill="#242250" stroke="#242250"/>
<polygon points="985.3,883.5 973,874.6 1010.9,866.8" fill="#211f49" stroke="#211f49"/>
<polygon points="985.3,883.5 1018.9,889.5 1000,900" fill="#272556" stroke="#272556"/>
<polygon points="1018.9,889.5 985.3,883.5 1010.9,866.8" fill="#22204b" stroke="#22204b"/>
<polygon points="938.6,804.4 998.7,762.6 990.1,833.3" fill="#211f49" stroke="#211f49"/>
<polygon points="906.1,638.6 926,686.9 868.1,668.3" fill="#22204b" stroke="#22204b"/>
<polygon points="926,686.9 906.1,638.6 957.6,623.7" fill="#3d3366" stroke="#3d3366"/>
<polygon points="997.7,564 1029.8,591.9 957.6,623.7" fill="#3c3265" stroke="#3c3265"/>
<polygon points="854.9,677.3 836.5,711.3 833.2,711.2" fill="#1f1e45" stroke="#1f1e45"/>
<polygon points="860.5,626.9 906.1,638.6 868.1,668.3" fill="#282658" stroke="#282658"/>
<polygon points="854.9,677.3 860.5,626.9 868.1,668.3" fill="#262454" stroke="#262454"/>
<polygon points="860.5,626.9 854.9,677.3 808,627.5" fill="#201e48" stroke="#201e48"/>
<polygon points="637.5,590.5 664.4,588.7 664,602.3" fill="#423870" stroke="#423870"/>
<polygon points="664.4,588.7 637.5,590.5 658.2,579.9" fill="#634984" stroke="#634984"/>
<polygon points="671.5,615.4 690.9,602.6 680.2,621.8" fill="#3b3264" stroke="#3b3264"/>
<polygon points="616.1,750.6 598.7,778.7 592.7,743.7" fill="#29265a" stroke="#29265a"/>
<polygon points="623.3,788.1 598.7,778.7 616.1,750.6" fill="#1d1b40" stroke="#1d1b40"/>
<polygon points="598.7,778.7 525.6,802.3 549.1,732.2" fill="#29275b" stroke="#29275b"/>
<polygon points="592.7,743.7 598.7,778.7 549.1,732.2" fill="#282659" stroke="#282659"/>
<polygon points="598.7,778.7 623.3,788.1 567.2,858.4" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="547.9,851.2 598.7,778.7 567.2,858.4" fill="#1f1e45" stroke="#1f1e45"/>
<polygon points="598.7,778.7 547.9,851.2 525.6,802.3" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="657.2,743.7 736.4,709.7 731.5,753.4" fill="#22214c" stroke="#22214c"/>
<polygon points="736.4,709.7 705.2,674.8 727,687" fill="#272556" stroke="#272556"/>
<polygon points="736.4,709.7 657.2,743.7 705.2,674.8" fill="#211f48" stroke="#211f48"/>
<polygon points="736.4,709.7 757.1,694.2 759,705.5" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="760.2,739 736.4,709.7 759,705.5" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="736.4,709.7 760.2,739 731.5,753.4" fill="#2a275c" stroke="#2a275c"/>
<polygon points="740.5,680.1 736.4,709.7 727,687" fill="#1d1b40" stroke="#1d1b40"/>
<polygon points="736.4,709.7 740.5,680.1 757.1,694.2" fill="#262454" stroke="#262454"/>
<polygon points="764.8,746 760.2,739 774.5,734.3" fill="#211f4a" stroke="#211f4a"/>
<polygon points="823.8,767 764.8,746 774.5,734.3" fill="#262454" stroke="#262454"/>
<polygon points="737.3,765.9 764.8,746 764.5,807.4" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="764.8,746 785.4,813.2 764.5,807.4" fill="#29275b" stroke="#29275b"/>
<polygon points="764.8,746 823.8,767 785.4,813.2" fill="#262454" stroke="#262454"/>
<polygon points="760.2,739 764.8,746 731.5,753.4" fill="#21204a" stroke="#21204a"/>
<polygon points="764.8,746 737.3,765.9 731.5,753.4" fill="#22204b" stroke="#22204b"/>
<polygon points="1062.9,816.7 1072.2,826.1 1024.2,853.3" fill="#22204a" stroke="#22204a"/>
<polygon points="653.5,621.8 655.6,686.2 626.8,655.7" fill="#3e3468" stroke="#3e3468"/>
<polygon points="692,650.2 655.6,686.2 653.5,621.8" fill="#4d4081" stroke="#4d4081"/>
<polygon points="655.6,686.2 692,650.2 705.2,674.8" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="655.6,686.2 657.2,743.7 633.8,712.4" fill="#1e1c43" stroke="#1e1c43"/>
<polygon points="657.2,743.7 655.6,686.2 705.2,674.8" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="692,650.2 712,653.8 705.2,674.8" fill="#282658" stroke="#282658"/>
<polygon points="705.2,674.8 712,653.8 727,687" fill="#272556" stroke="#272556"/>
<polygon points="712,653.8 740.5,680.1 727,687" fill="#2a285d" stroke="#2a285d"/>
<polygon points="829.9,389 819.8,380.6 842.9,359.8" fill="#a185a7" stroke="#a185a7"/>
<polygon points="829.9,389 830.5,392.1 819.8,380.6" fill="#ceabd6" stroke="#ceabd6"/>
<polygon points="830.5,392.1 812.7,408.2 819.8,380.6" fill="#c09fc7" stroke="#c09fc7"/>
<polygon points="789.2,374.5 812.7,408.2 799.7,415.3" fill="#cfabd7" stroke="#cfabd7"/>
<polygon points="819.8,380.6 812.7,408.2 789.2,374.5" fill="#cfacd7" stroke="#cfacd7"/>
<polygon points="710,450.6 731.1,462.4 691.7,456.4" fill="#b798be" stroke="#b798be"/>
<polygon points="731.1,462.4 709,505.2 691.7,456.4" fill="#7d5ca7" stroke="#7d5ca7"/>
<polygon points="805,494.4 731.1,462.4 799.7,415.3" fill="#644985" stroke="#644985"/>
<polygon points="731.1,462.4 710,450.6 714.9,416.3" fill="#cfabd6" stroke="#cfabd6"/>
<polygon points="731.1,462.4 756.8,385.9 799.7,415.3" fill="#74569b" stroke="#74569b"/>
<polygon points="756.8,385.9 731.1,462.4 714.9,416.3" fill="#957b9b" stroke="#957b9b"/>
<polygon points="694,524.3 699,555.4 676.7,522.4" fill="#725498" stroke="#725498"/>
<polygon points="709,505.2 694,524.3 676.7,522.4" fill="#684d8b" stroke="#684d8b"/>
<polygon points="814.4,497.6 773.7,511.3 805,494.4" fill="#3e3469" stroke="#3e3469"/>
<polygon points="773.7,511.3 731.1,462.4 805,494.4" fill="#73559a" stroke="#73559a"/>
<polygon points="731.1,462.4 773.7,511.3 709,505.2" fill="#6e5193" stroke="#6e5193"/>
<polygon points="623.1,565.6 617.5,553.2 635.9,559.7" fill="#4c4080" stroke="#4c4080"/>
<polygon points="637.5,590.5 623.1,565.6 635.9,559.7" fill="#3f356b" stroke="#3f356b"/>
<polygon points="599.3,544.2 569.8,557.5 583.6,537.8" fill="#4d4182" stroke="#4d4182"/>
<polygon points="617.5,553.2 599.3,544.2 629.9,540.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="629.9,540.1 599.3,544.2 619.3,507.1" fill="#3a3061" stroke="#3a3061"/>
<polygon points="599.3,544.2 592.4,500.4 619.3,507.1" fill="#4e4284" stroke="#4e4284"/>
<polygon points="592.4,500.4 599.3,544.2 583.6,537.8" fill="#3c3366" stroke="#3c3366"/>
<polygon points="568.2,450.4 576.6,453.7 563.6,479.3" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="593.5,488.4 576.6,453.7 577.9,453.2" fill="#ce7f6d" stroke="#ce7f6d"/>
<polygon points="576.6,453.7 593.5,488.4 563.6,479.3" fill="#4c4080" stroke="#4c4080"/>
<polygon points="576.6,453.7 572.5,448.7 577.9,453.2" fill="#ca7e6d" stroke="#ca7e6d"/>
<polygon points="576.6,453.7 568.2,450.4 572.5,448.7" fill="#ca7e6d" stroke="#ca7e6d"/>
<polygon points="495.6,437.3 479.6,467.6 464.9,411.7" fill="#76579d" stroke="#76579d"/>
<polygon points="516.8,443.7 545.4,494.9 479.6,467.6" fill="#3c3265" stroke="#3c3265"/>
<polygon points="495.6,437.3 516.8,443.7 479.6,467.6" fill="#6f5294" stroke="#6f5294"/>
<polygon points="501.6,235.5 500.2,280.6 454.2,294.7" fill="#875a62" stroke="#875a62"/>
<polygon points="500.2,280.6 487.2,297.1 454.2,294.7" fill="#82565b" stroke="#82565b"/>
<polygon points="500.2,280.6 501.6,235.5 513.9,273.8" fill="#80555e" stroke="#80555e"/>
<polygon points="523.1,295.5 500.2,280.6 513.9,273.8" fill="#895a60" stroke="#895a60"/>
<polygon points="487.2,297.1 500.2,280.6 523.1,295.5" fill="#8f5e64" stroke="#8f5e64"/>
<polygon points="1433.5,427.7 1433.5,433.1 1417.3,429" fill="#c1786b" stroke="#c1786b"/>
<polygon points="1433.5,433.1 1464.3,425.3 1471.6,467.2" fill="#40366c" stroke="#40366c"/>
<polygon points="1464.3,425.3 1433.5,433.1 1433.5,427.7" fill="#463b77" stroke="#463b77"/>
<polygon points="1489.4,484.7 1457.6,502.6 1471.6,467.2" fill="#4e4183" stroke="#4e4183"/>
<polygon points="1457.6,502.6 1489.4,484.7 1490.4,566.7" fill="#3c3366" stroke="#3c3366"/>
<polygon points="1489.3,482.6 1489.4,484.7 1471.6,467.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1484.3,470.7 1489.3,482.6 1471.6,467.2" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1489.4,484.7 1489.3,482.6 1517.2,484.1" fill="#42376f" stroke="#42376f"/>
<polygon points="1596.9,499.4 1590.8,503.6 1562.9,475.3" fill="#76579d" stroke="#76579d"/>
<polygon points="1590.8,503.6 1587.5,509.2 1562.9,475.3" fill="#76579e" stroke="#76579e"/>
<polygon points="1587.5,509.2 1590.8,503.6 1596.9,499.4" fill="#40366c" stroke="#40366c"/>
<polygon points="1544.4,396.6 1546.8,403.3 1526.3,400.3" fill="#b9756b" stroke="#b9756b"/>
<polygon points="1546.8,403.3 1544.4,396.6 1557.9,398.4" fill="#b37168" stroke="#b37168"/>
<polygon points="1562.1,423.8 1546.8,403.3 1557.9,398.4" fill="#af6e64" stroke="#af6e64"/>
<polygon points="1529.9,479.4 1546.1,462.1 1562.9,475.3" fill="#bc9bc3" stroke="#bc9bc3"/>
<polygon points="1529.9,479.4 1554.9,527 1517.2,484.1" fill="#4f4285" stroke="#4f4285"/>
<polygon points="1554.9,527 1529.9,479.4 1562.9,475.3" fill="#624984" stroke="#624984"/>
<polygon points="1494,443.6 1529.9,479.4 1517.2,484.1" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1432.4,714.2 1415.4,726.7 1414.5,721.2" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1429.7,741 1415.4,726.7 1432.4,714.2" fill="#201e47" stroke="#201e47"/>
<polygon points="1415.4,726.7 1399.6,715.6 1414.5,721.2" fill="#252353" stroke="#252353"/>
<polygon points="1399.6,715.6 1415.4,726.7 1359.2,762.3" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1415.4,726.7 1393.5,793.8 1359.2,762.3" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1415.4,726.7 1429.7,741 1393.5,793.8" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="1408.6,820.4 1480.3,795.2 1468.4,829.6" fill="#201e47" stroke="#201e47"/>
<polygon points="1480.3,795.2 1429.7,741 1445.9,734.3" fill="#29275a" stroke="#29275a"/>
<polygon points="1480.3,795.2 1408.6,820.4 1393.5,793.8" fill="#262353" stroke="#262353"/>
<polygon points="1429.7,741 1480.3,795.2 1393.5,793.8" fill="#262455" stroke="#262455"/>
<polygon points="1480.3,795.2 1445.9,734.3 1506.7,768" fill="#272557" stroke="#272557"/>
<polygon points="1480.3,795.2 1506.7,768 1543.3,826.5" fill="#272556" stroke="#272556"/>
<polygon points="1385.7,650 1385.3,657.9 1367.8,671.5" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1312.4,658.6 1307.7,651.4 1339.8,626" fill="#3b3163" stroke="#3b3163"/>
<polygon points="1307.7,651.4 1301,649.5 1339.8,626" fill="#41376e" stroke="#41376e"/>
<polygon points="1301,649.5 1307.7,651.4 1312.4,658.6" fill="#493d7a" stroke="#493d7a"/>
<polygon points="1563,319.7 1539.5,336.5 1522,289.9" fill="#925f61" stroke="#925f61"/>
<polygon points="1563,319.7 1562.5,324.8 1539.5,336.5" fill="#976262" stroke="#976262"/>
<polygon points="1569,260.5 1563,319.7 1522,289.9" fill="#88595f" stroke="#88595f"/>
<polygon points="1563,319.7 1594.8,294.9 1592.1,304.1" fill="#89595d" stroke="#89595d"/>
<polygon points="1563,319.7 1569,260.5 1594.8,294.9" fill="#8e5d62" stroke="#8e5d62"/>
<polygon points="1589.5,339.2 1563,319.7 1592.1,304.1" fill="#915e5f" stroke="#915e5f"/>
<polygon points="1562.5,324.8 1563,319.7 1589.5,339.2" fill="#946060" stroke="#946060"/>
<polygon points="1319.7,867.3 1291.9,815.7 1328.3,824.7" fill="#29275b" stroke="#29275b"/>
<polygon points="1290.8,815.7 1291.9,815.7 1319.7,867.3" fill="#29275b" stroke="#29275b"/>
<polygon points="1356.3,801.5 1291.9,815.7 1288.3,754.9" fill="#282658" stroke="#282658"/>
<polygon points="1291.9,815.7 1356.3,801.5 1328.3,824.7" fill="#23214e" stroke="#23214e"/>
<polygon points="1291.9,815.7 1283.8,759.1 1288.3,754.9" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="1511.5,853.8 1512.8,880.9 1474,858.9" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1475.9,894.2 1512.8,880.9 1500,900" fill="#23214e" stroke="#23214e"/>
<polygon points="1528.4,868.7 1512.8,880.9 1511.5,853.8" fill="#29265a" stroke="#29265a"/>
<polygon points="1512.8,880.9 1475.9,894.2 1467.9,880.6" fill="#24224f" stroke="#24224f"/>
<polygon points="1474,858.9 1512.8,880.9 1467.9,880.6" fill="#282557" stroke="#282557"/>
<polygon points="1512.8,880.9 1550,866.8 1500,900" fill="#22204b" stroke="#22204b"/>
<polygon points="1512.8,880.9 1528.4,868.7 1550,866.8" fill="#23214e" stroke="#23214e"/>
<polygon points="1506.9,843.1 1474,858.9 1468.4,829.6" fill="#262454" stroke="#262454"/>
<polygon points="1506.9,843.1 1511.5,853.8 1474,858.9" fill="#23214d" stroke="#23214d"/>
<polygon points="1480.3,795.2 1506.9,843.1 1468.4,829.6" fill="#262454" stroke="#262454"/>
<polygon points="1506.9,843.1 1480.3,795.2 1543.3,826.5" fill="#262455" stroke="#262455"/>
<polygon points="1286.4,416.3 1319.8,401 1313.2,417.4" fill="#b092b7" stroke="#b092b7"/>
<polygon points="1272.2,384 1286.4,416.3 1270.3,399.9" fill="#db9776" stroke="#db9776"/>
<polygon points="1319.8,401 1286.4,416.3 1272.2,384" fill="#c98b6d" stroke="#c98b6d"/>
<polygon points="1249,763.6 1263.9,784.9 1227.1,773.3" fill="#201e47" stroke="#201e47"/>
<polygon points="1263.9,784.9 1249,763.6 1283.8,759.1" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="1291.9,815.7 1263.9,784.9 1283.8,759.1" fill="#211f48" stroke="#211f48"/>
<polygon points="1263.9,784.9 1291.9,815.7 1290.8,815.7" fill="#282658" stroke="#282658"/>
<polygon points="1268.7,678.1 1260.5,662.6 1282.2,669.1" fill="#4e4284" stroke="#4e4284"/>
<polygon points="1305.6,685.5 1268.7,678.1 1282.2,669.1" fill="#272556" stroke="#272556"/>
<polygon points="1268.7,678.1 1305.6,685.5 1251,713.6" fill="#252351" stroke="#252351"/>
<polygon points="1233.4,676.2 1268.7,678.1 1251,713.6" fill="#282557" stroke="#282557"/>
<polygon points="1240.9,666 1268.7,678.1 1233.4,676.2" fill="#24224f" stroke="#24224f"/>
<polygon points="1260.5,662.6 1268.7,678.1 1240.9,666" fill="#201e46" stroke="#201e46"/>
<polygon points="1171.7,619 1165.4,606.8 1206.1,601.3" fill="#211f48" stroke="#211f48"/>
<polygon points="1171.7,619 1231.3,621.8 1178.5,657.8" fill="#272455" stroke="#272455"/>
<polygon points="1171.7,619 1206.1,601.3 1231.3,621.8" fill="#242250" stroke="#242250"/>
<polygon points="1593.5,75.4 1577.6,75.9 1572,29.8" fill="#332c51" stroke="#332c51"/>
<polygon points="1540,21.2 1577.6,75.9 1517.1,88.7" fill="#312b4e" stroke="#312b4e"/>
<polygon points="1572,29.8 1577.6,75.9 1540,21.2" fill="#2a274d" stroke="#2a274d"/>
<polygon points="1577.6,75.9 1593.5,75.4 1600,90" fill="#392f50" stroke="#392f50"/>
<polygon points="1517.1,88.7 1577.6,75.9 1572.6,154.1" fill="#413350" stroke="#413350"/>
<polygon points="1577.6,75.9 1600,90 1572.6,154.1" fill="#443554" stroke="#443554"/>
<polygon points="1499.2,92.5 1481.1,96.4 1482,71.8" fill="#3b3050" stroke="#3b3050"/>
<polygon points="1481.1,96.4 1499.2,92.5 1483.4,112.3" fill="#403351" stroke="#403351"/>
<polygon points="1481.1,96.4 1448.4,85.2 1482,71.8" fill="#392f4f" stroke="#392f4f"/>
<polygon points="1468.3,129.5 1481.1,96.4 1483.4,112.3" fill="#433450" stroke="#433450"/>
<polygon points="3,515.3 11.7,525.7 0,540" fill="#514488" stroke="#514488"/>
<polygon points="28.2,523.9 11.7,525.7 3,515.3" fill="#443972" stroke="#443972"/>
<polygon points="11.7,525.7 36,552.3 0,540" fill="#4e4284" stroke="#4e4284"/>
<polygon points="11.7,525.7 28.2,523.9 36,552.3" fill="#3e3468" stroke="#3e3468"/>
<polygon points="59.4,445.6 66.8,418.9 67.8,433.3" fill="#5f467f" stroke="#5f467f"/>
<polygon points="29.7,399.6 66.8,418.9 30.4,451.1" fill="#7a5aa3" stroke="#7a5aa3"/>
<polygon points="67.8,433.3 66.8,418.9 105.1,408.5" fill="#654b88" stroke="#654b88"/>
<polygon points="35.8,353.5 29.7,399.6 0,360" fill="#a36862" stroke="#a36862"/>
<polygon points="29.7,399.6 35.8,353.5 64.9,359.2" fill="#a06661" stroke="#a06661"/>
<polygon points="35.8,353.5 0,360 5.8,306.4" fill="#a36968" stroke="#a36968"/>
<polygon points="85.8,329.3 35.8,353.5 5.8,306.4" fill="#996363" stroke="#996363"/>
<polygon points="64.9,359.2 35.8,353.5 85.8,329.3" fill="#a36866" stroke="#a36866"/>
<polygon points="162.6,300.3 164.6,315.6 145.7,304.6" fill="#a88baf" stroke="#a88baf"/>
<polygon points="136.7,301.6 121.4,311 107.1,300.9" fill="#8a5a5e" stroke="#8a5a5e"/>
<polygon points="107.1,300.9 121.4,311 95.5,326.1" fill="#8b5a5d" stroke="#8b5a5d"/>
<polygon points="121.4,311 129.1,340.6 95.5,326.1" fill="#a68aad" stroke="#a68aad"/>
<polygon points="144.7,292.3 123.6,277.2 151.2,278.1" fill="#885a61" stroke="#885a61"/>
<polygon points="144.7,292.3 136.7,301.6 123.6,277.2" fill="#885a5f" stroke="#885a5f"/>
<polygon points="136.7,301.6 144.7,292.3 145.7,304.6" fill="#87585c" stroke="#87585c"/>
<polygon points="144.7,292.3 162.6,300.3 145.7,304.6" fill="#905e63" stroke="#905e63"/>
<polygon points="162.6,300.3 144.7,292.3 151.2,278.1" fill="#82565b" stroke="#82565b"/>
<polygon points="31.8,578.9 33.3,591.3 19.4,593.5" fill="#3f356a" stroke="#3f356a"/>
<polygon points="42.1,598.9 33.3,591.3 31.8,578.9" fill="#504488" stroke="#504488"/>
<polygon points="33.3,591.3 34.7,603.9 19.4,593.5" fill="#3d3367" stroke="#3d3367"/>
<polygon points="33.3,591.3 42.1,598.9 34.7,603.9" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="176.6,716.4 124.7,674.6 183.5,687.1" fill="#282658" stroke="#282658"/>
<polygon points="176.6,716.4 156.4,733.5 124.7,674.6" fill="#262454" stroke="#262454"/>
<polygon points="193,723.9 176.6,716.4 183.5,687.1" fill="#272556" stroke="#272556"/>
<polygon points="156.4,733.5 176.6,716.4 193,723.9" fill="#23214e" stroke="#23214e"/>
<polygon points="327.3,462.7 317.4,445 352.6,455.1" fill="#6e5193" stroke="#6e5193"/>
<polygon points="327.5,463.6 327.3,462.7 352.6,455.1" fill="#433871" stroke="#433871"/>
<polygon points="317.4,445 327.3,462.7 300.6,446.2" fill="#634985" stroke="#634985"/>
<polygon points="327.3,462.7 310.7,483 300.6,446.2" fill="#7a5aa3" stroke="#7a5aa3"/>
<polygon points="327.3,462.7 327.5,463.6 310.7,483" fill="#3a3162" stroke="#3a3162"/>
<polygon points="330.8,317.8 286.9,332.6 325,301.6" fill="#905e5f" stroke="#905e5f"/>
<polygon points="294,348.4 286.9,332.6 321.2,351" fill="#9b6462" stroke="#9b6462"/>
<polygon points="286.9,332.6 330.8,317.8 321.2,351" fill="#a06767" stroke="#a06767"/>
<polygon points="267,311.3 286.9,332.6 267.6,355.9" fill="#945f5f" stroke="#945f5f"/>
<polygon points="267.6,355.9 286.9,332.6 269.7,357.2" fill="#a26865" stroke="#a26865"/>
<polygon points="286.9,332.6 294,348.4 269.7,357.2" fill="#a26866" stroke="#a26866"/>
<polygon points="349.1,186.3 374.7,167.2 404.4,194.9" fill="#65475c" stroke="#65475c"/>
<polygon points="363.2,248.6 349.1,186.3 404.4,194.9" fill="#664756" stroke="#664756"/>
<polygon points="349.1,186.3 363.2,248.6 323.5,216.3" fill="#694857" stroke="#694857"/>
<polygon points="349.1,186.3 315.1,204.5 324.6,166.6" fill="#624558" stroke="#624558"/>
<polygon points="349.1,186.3 323.5,216.3 315.1,204.5" fill="#6b4a5c" stroke="#6b4a5c"/>
<polygon points="374.7,167.2 349.1,186.3 347.4,141.2" fill="#5a4158" stroke="#5a4158"/>
<polygon points="363.2,248.6 305,283.9 298.3,268.5" fill="#835760" stroke="#835760"/>
<polygon points="325,301.6 305,283.9 355,295.4" fill="#895a5f" stroke="#895a5f"/>
<polygon points="305,283.9 363.2,248.6 355,295.4" fill="#885a62" stroke="#885a62"/>
<polygon points="286.9,332.6 305,283.9 325,301.6" fill="#8f5d60" stroke="#8f5d60"/>
<polygon points="305,283.9 286.9,332.6 267,311.3" fill="#925f62" stroke="#925f62"/>
<polygon points="305,283.9 267,311.3 262.4,306.8" fill="#86585c" stroke="#86585c"/>
<polygon points="298.3,268.5 305,283.9 262.4,306.8" fill="#895a60" stroke="#895a60"/>
<polygon points="24.4,262.7 85.5,258.5 85.9,287.1" fill="#7c525a" stroke="#7c525a"/>
<polygon points="58.5,237.4 85.5,258.5 24.4,262.7" fill="#77505a" stroke="#77505a"/>
<polygon points="85.5,258.5 123.6,277.2 85.9,287.1" fill="#885b62" stroke="#885b62"/>
<polygon points="123.6,277.2 85.5,258.5 117.3,239.1" fill="#79515b" stroke="#79515b"/>
<polygon points="117.3,239.1 85.5,258.5 112.8,226.9" fill="#7b5360" stroke="#7b5360"/>
<polygon points="85.5,258.5 58.5,237.4 112.8,226.9" fill="#7b535f" stroke="#7b535f"/>
<polygon points="58.5,237.4 33.5,240.5 28.7,222.4" fill="#6f4c58" stroke="#6f4c58"/>
<polygon points="33.5,240.5 58.5,237.4 24.4,262.7" fill="#7a525d" stroke="#7a525d"/>
<polygon points="28.7,222.4 33.5,240.5 0,270" fill="#724d58" stroke="#724d58"/>
<polygon points="33.5,240.5 24.4,262.7 0,270" fill="#775059" stroke="#775059"/>
<polygon points="337.4,96.3 298.7,136.5 287.4,102" fill="#463654" stroke="#463654"/>
<polygon points="274.4,162.9 298.7,136.5 324.6,166.6" fill="#513b51" stroke="#513b51"/>
<polygon points="287.4,102 298.7,136.5 274.4,162.9" fill="#4f3b56" stroke="#4f3b56"/>
<polygon points="337.4,96.3 284.3,77 301.3,53.7" fill="#3a3053" stroke="#3a3053"/>
<polygon points="284.3,77 337.4,96.3 287.4,102" fill="#3c304f" stroke="#3c304f"/>
<polygon points="284.3,77 264.1,56.8 301.3,53.7" fill="#352d53" stroke="#352d53"/>
<polygon points="284.3,77 287.4,102 253.1,93.7" fill="#3e3252" stroke="#3e3252"/>
<polygon points="264.1,56.8 284.3,77 253.1,93.7" fill="#352c4c" stroke="#352c4c"/>
<polygon points="994.2,17.8 998.6,68.1 945.4,81.7" fill="#2f294d" stroke="#2f294d"/>
<polygon points="1005.7,45.3 998.6,68.1 994.2,17.8" fill="#2a274c" stroke="#2a274c"/>
<polygon points="998.6,68.1 1005.7,45.3 1034.9,103.7" fill="#342c4d" stroke="#342c4d"/>
<polygon points="998.6,68.1 1013.9,131.5 945.4,81.7" fill="#3e3151" stroke="#3e3151"/>
<polygon points="1013.9,131.5 998.6,68.1 1034.9,103.7" fill="#3e314f" stroke="#3e314f"/>
<polygon points="1000.3,281.4 940.2,274.3 1001.5,245.6" fill="#b5826d" stroke="#b5826d"/>
<polygon points="940.2,274.3 1000.3,281.4 994.9,313.3" fill="#bc866f" stroke="#bc866f"/>
<polygon points="1000.3,281.4 1025.4,284.7 994.9,313.3" fill="#b7826b" stroke="#b7826b"/>
<polygon points="1020.8,242.3 1000.3,281.4 1001.5,245.6" fill="#a97966" stroke="#a97966"/>
<polygon points="1025.4,284.7 1000.3,281.4 1020.8,242.3" fill="#b5826d" stroke="#b5826d"/>
<polygon points="940.2,274.3 890.3,327.4 873.6,258.8" fill="#b4806a" stroke="#b4806a"/>
<polygon points="831.3,291.9 767.2,291.9 834.1,268.2" fill="#84575d" stroke="#84575d"/>
<polygon points="831.3,291.9 834.1,268.2 873.6,258.8" fill="#b8836e" stroke="#b8836e"/>
<polygon points="890.3,327.4 831.3,291.9 873.6,258.8" fill="#bc866f" stroke="#bc866f"/>
<polygon points="831.3,291.9 776,344.6 767.2,291.9" fill="#8a5a5d" stroke="#8a5a5d"/>
<polygon points="1138.1,142.3 1130.8,152.2 1122.8,126.6" fill="#523d57" stroke="#523d57"/>
<polygon points="1126.3,185.2 1130.8,152.2 1166.9,219.7" fill="#65475b" stroke="#65475b"/>
<polygon points="1130.8,152.2 1138.1,142.3 1166.9,219.7" fill="#5d4258" stroke="#5d4258"/>
<polygon points="1067.3,207.5 1015.7,181.4 1084.8,170.8" fill="#9d7466" stroke="#9d7466"/>
<polygon points="994.6,152 1015.7,181.4 999.1,209.5" fill="#9b7265" stroke="#9b7265"/>
<polygon points="1015.7,181.4 994.6,152 1005.2,156.7" fill="#583f55" stroke="#583f55"/>
<polygon points="1015.7,181.4 1041.2,227.7 999.1,209.5" fill="#a47868" stroke="#a47868"/>
<polygon points="1015.7,181.4 1067.3,207.5 1041.2,227.7" fill="#a67969" stroke="#a67969"/>
<polygon points="1087.2,195.6 1108.1,174.2 1126.3,185.2" fill="#a77b6c" stroke="#a77b6c"/>
<polygon points="1108.1,174.2 1130.8,152.2 1126.3,185.2" fill="#583f53" stroke="#583f53"/>
<polygon points="1108.1,174.2 1084.8,170.8 1122.8,126.6" fill="#573f56" stroke="#573f56"/>
<polygon points="1130.8,152.2 1108.1,174.2 1122.8,126.6" fill="#553e56" stroke="#553e56"/>
<polygon points="1270.2,312.1 1249.4,279.4 1280.2,304.2" fill="#bb856d" stroke="#bb856d"/>
<polygon points="1249.4,279.4 1270.2,312.1 1244.2,342.1" fill="#c0876f" stroke="#c0876f"/>
<polygon points="1285.3,314.8 1270.2,312.1 1280.2,304.2" fill="#c48b72" stroke="#c48b72"/>
<polygon points="1270.2,312.1 1286.1,359.2 1244.2,342.1" fill="#c2886e" stroke="#c2886e"/>
<polygon points="1286.1,359.2 1270.2,312.1 1285.3,314.8" fill="#b78168" stroke="#b78168"/>
<polygon points="1288,271.1 1234.1,214.4 1315.6,200.3" fill="#724e5b" stroke="#724e5b"/>
<polygon points="1288,271.1 1249.4,279.4 1234.1,214.4" fill="#7b525c" stroke="#7b525c"/>
<polygon points="1249.4,279.4 1288,271.1 1280.2,304.2" fill="#81555b" stroke="#81555b"/>
<polygon points="1288,271.1 1324.2,309.5 1280.2,304.2" fill="#8f5e63" stroke="#8f5e63"/>
<polygon points="1226.7,387.2 1243,408.1 1231.6,407.9" fill="#cf8f70" stroke="#cf8f70"/>
<polygon points="1261.1,399.9 1243,408.1 1226.7,387.2" fill="#c6896b" stroke="#c6896b"/>
<polygon points="626,130.9 602.9,128 618.1,123.2" fill="#4c3954" stroke="#4c3954"/>
<polygon points="602.9,128 626,130.9 603,177.2" fill="#523c55" stroke="#523c55"/>
<polygon points="680.8,180.2 626,130.9 722.3,116.7" fill="#4f3a53" stroke="#4f3a53"/>
<polygon points="642.4,202.9 626,130.9 680.8,180.2" fill="#5d4358" stroke="#5d4358"/>
<polygon points="603,177.2 626,130.9 642.4,202.9" fill="#5a4055" stroke="#5a4055"/>
<polygon points="722.3,116.7 626,130.9 719.5,106.1" fill="#4a3956" stroke="#4a3956"/>
<polygon points="626,130.9 684.5,51.4 719.5,106.1" fill="#3d314f" stroke="#3d314f"/>
<polygon points="626,130.9 618.1,123.2 684.5,51.4" fill="#413351" stroke="#413351"/>
<polygon points="602.9,128 589.1,173.9 570.4,156.6" fill="#553e56" stroke="#553e56"/>
<polygon points="589.1,173.9 602.9,128 603,177.2" fill="#563f55" stroke="#563f55"/>
<polygon points="589.1,173.9 574.6,205.7 570.4,156.6" fill="#5f4458" stroke="#5f4458"/>
<polygon points="589.1,173.9 603,177.2 574.6,205.7" fill="#624559" stroke="#624559"/>
<polygon points="499.5,639.7 535.5,716.8 453.7,662.9" fill="#211f49" stroke="#211f49"/>
<polygon points="485.2,633.3 499.5,639.7 453.7,662.9" fill="#201e47" stroke="#201e47"/>
<polygon points="499.5,639.7 539.2,632 568.2,667.3" fill="#23214c" stroke="#23214c"/>
<polygon points="535.5,716.8 499.5,639.7 568.2,667.3" fill="#272557" stroke="#272557"/>
<polygon points="429.2,631.2 451.2,618.7 453.7,662.9" fill="#3c3265" stroke="#3c3265"/>
<polygon points="451.2,618.7 485.2,633.3 453.7,662.9" fill="#493d7b" stroke="#493d7b"/>
<polygon points="451.2,618.7 429.2,631.2 422.6,604.4" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="485.2,633.3 451.2,618.7 488.4,578.2" fill="#3e3469" stroke="#3e3469"/>
<polygon points="476.8,564.4 451.2,618.7 422.6,604.4" fill="#3f356a" stroke="#3f356a"/>
<polygon points="488.4,578.2 451.2,618.7 476.8,564.4" fill="#3c3366" stroke="#3c3366"/>
<polygon points="563.1,552.2 536.5,577.4 542,543.3" fill="#504387" stroke="#504387"/>
<polygon points="536.5,577.4 563.1,552.2 569.8,557.5" fill="#3f356b" stroke="#3f356b"/>
<polygon points="583.6,537.8 563.1,552.2 542,543.3" fill="#42376f" stroke="#42376f"/>
<polygon points="569.8,557.5 563.1,552.2 583.6,537.8" fill="#3d3366" stroke="#3d3366"/>
<polygon points="390.1,598.8 388.1,539.5 422.6,604.4" fill="#4e4183" stroke="#4e4183"/>
<polygon points="404.6,627.1 390.1,598.8 422.6,604.4" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="385.3,614.5 390.1,598.8 404.6,627.1" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="390.1,598.8 365.9,621 348.6,617.8" fill="#433870" stroke="#433870"/>
<polygon points="365.9,621 390.1,598.8 385.3,614.5" fill="#3a3162" stroke="#3a3162"/>
<polygon points="334.6,540.4 327.5,463.6 352.6,455.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="388.1,539.5 334.6,540.4 352.6,455.1" fill="#4e4284" stroke="#4e4284"/>
<polygon points="327.5,463.6 334.6,540.4 310.7,483" fill="#3f356b" stroke="#3f356b"/>
<polygon points="334.6,540.4 306.8,492.2 310.7,483" fill="#4f4285" stroke="#4f4285"/>
<polygon points="334.6,540.4 319.8,538.2 306.8,492.2" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="319.8,538.2 334.6,540.4 311.6,555.4" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="390.1,598.8 334.6,540.4 388.1,539.5" fill="#3d3366" stroke="#3d3366"/>
<polygon points="476.8,564.4 451.3,505 467.4,494.7" fill="#3e3468" stroke="#3e3468"/>
<polygon points="423.1,505.6 451.3,505 476.8,564.4" fill="#3e3469" stroke="#3e3469"/>
<polygon points="207.1,823.5 298.5,822 223.6,867.1" fill="#211f4a" stroke="#211f4a"/>
<polygon points="207.1,823.5 223.6,867.1 204.7,871.9" fill="#282658" stroke="#282658"/>
<polygon points="173.1,860.4 207.1,823.5 204.7,871.9" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="207.1,823.5 173.1,860.4 128.7,809.7" fill="#252353" stroke="#252353"/>
<polygon points="197.2,792.5 207.1,823.5 128.7,809.7" fill="#201e46" stroke="#201e46"/>
<polygon points="417.2,735.9 406.8,760.8 407.3,734.5" fill="#272557" stroke="#272557"/>
<polygon points="411.3,778.9 406.8,760.8 416.9,769.4" fill="#29275a" stroke="#29275a"/>
<polygon points="406.8,760.8 433.3,738.9 416.9,769.4" fill="#211f48" stroke="#211f48"/>
<polygon points="406.8,760.8 417.2,735.9 433.3,738.9" fill="#282659" stroke="#282659"/>
<polygon points="406.8,760.8 411.3,778.9 345.7,770.8" fill="#22204a" stroke="#22204a"/>
<polygon points="407.3,734.5 406.8,760.8 345.7,770.8" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="1013.6,766.1 1012.5,835.1 990.1,833.3" fill="#211f48" stroke="#211f48"/>
<polygon points="998.7,762.6 1013.6,766.1 990.1,833.3" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1013.6,766.1 1062.9,816.7 1012.5,835.1" fill="#282659" stroke="#282659"/>
<polygon points="854.9,677.3 878.6,716.1 836.5,711.3" fill="#29265a" stroke="#29265a"/>
<polygon points="926,686.9 878.6,716.1 868.1,668.3" fill="#252352" stroke="#252352"/>
<polygon points="878.6,716.1 854.9,677.3 868.1,668.3" fill="#211f48" stroke="#211f48"/>
<polygon points="1062.3,590.4 1029.8,591.9 1029.3,565.9" fill="#4c4080" stroke="#4c4080"/>
<polygon points="1029.8,591.9 1017.9,572.2 1029.3,565.9" fill="#3b3264" stroke="#3b3264"/>
<polygon points="1017.9,572.2 1029.8,591.9 997.7,564" fill="#4d4182" stroke="#4d4182"/>
<polygon points="1204.7,540.7 1228.8,561.1 1208,572.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1204.7,540.7 1176.9,510.7 1269.9,508.1" fill="#6c4f90" stroke="#6c4f90"/>
<polygon points="1135.4,444.4 1151.5,477.6 1140,478.5" fill="#715398" stroke="#715398"/>
<polygon points="1151.5,477.6 1164.5,472.1 1176.9,510.7" fill="#614882" stroke="#614882"/>
<polygon points="1164.5,472.1 1135.4,444.4 1187.8,416.1" fill="#584176" stroke="#584176"/>
<polygon points="1164.5,472.1 1151.5,477.6 1135.4,444.4" fill="#7d5ca8" stroke="#7d5ca8"/>
<polygon points="1178.2,475.6 1273.5,450.5 1269.9,508.1" fill="#694d8c" stroke="#694d8c"/>
<polygon points="1176.9,510.7 1178.2,475.6 1269.9,508.1" fill="#6f5295" stroke="#6f5295"/>
<polygon points="1164.5,472.1 1178.2,475.6 1176.9,510.7" fill="#7b5aa4" stroke="#7b5aa4"/>
<polygon points="1231.6,407.9 1178.2,475.6 1187.8,416.1" fill="#5d447c" stroke="#5d447c"/>
<polygon points="1178.2,475.6 1164.5,472.1 1187.8,416.1" fill="#5a4278" stroke="#5a4278"/>
<polygon points="1151.5,477.6 1141.2,532.7 1140,478.5" fill="#604781" stroke="#604781"/>
<polygon points="1141.2,532.7 1151.5,477.6 1176.9,510.7" fill="#77589f" stroke="#77589f"/>
<polygon points="1171.7,619 1153.9,606.5 1165.4,606.8" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="1187.8,416.1 1146.3,410.4 1152.5,397.3" fill="#705396" stroke="#705396"/>
<polygon points="1135.4,444.4 1146.3,410.4 1187.8,416.1" fill="#7859a1" stroke="#7859a1"/>
<polygon points="1146.3,410.4 1140.3,379.2 1152.5,397.3" fill="#76579e" stroke="#76579e"/>
<polygon points="958,538.1 969.2,516.8 997.7,564" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="913.7,563 958,538.1 957.6,623.7" fill="#3f356a" stroke="#3f356a"/>
<polygon points="958,538.1 997.7,564 957.6,623.7" fill="#4e4284" stroke="#4e4284"/>
<polygon points="901.4,517 882.8,530.3 868.5,519.3" fill="#664b88" stroke="#664b88"/>
<polygon points="882.8,530.3 901.4,517 913.7,563" fill="#382f5f" stroke="#382f5f"/>
<polygon points="1140.3,379.2 1107.9,388.1 1114.2,372.3" fill="#6d5092" stroke="#6d5092"/>
<polygon points="1146.3,410.4 1107.9,388.1 1140.3,379.2" fill="#5d447c" stroke="#5d447c"/>
<polygon points="1063,523.3 1062.3,590.4 1029.3,565.9" fill="#40366c" stroke="#40366c"/>
<polygon points="832.7,597.2 764.4,588.6 784.8,551.2" fill="#4e4183" stroke="#4e4183"/>
<polygon points="764.4,588.6 832.7,597.2 808,627.5" fill="#473b77" stroke="#473b77"/>
<polygon points="906.1,638.6 877.8,609.7 913.7,563" fill="#3b3264" stroke="#3b3264"/>
<polygon points="860.5,626.9 877.8,609.7 906.1,638.6" fill="#252352" stroke="#252352"/>
<polygon points="773.7,511.3 747.5,543.6 709,505.2" fill="#6d5192" stroke="#6d5192"/>
<polygon points="694,524.3 747.5,543.6 699,555.4" fill="#77579f" stroke="#77579f"/>
<polygon points="747.5,543.6 694,524.3 709,505.2" fill="#705396" stroke="#705396"/>
<polygon points="690.9,602.6 698.5,625.5 680.2,621.8" fill="#3e3469" stroke="#3e3469"/>
<polygon points="698.5,625.5 692,650.2 680.2,621.8" fill="#4d4081" stroke="#4d4081"/>
<polygon points="678.8,576.8 664.4,588.7 658.2,579.9" fill="#634985" stroke="#634985"/>
<polygon points="699,555.4 678.8,576.8 676.7,522.4" fill="#7c5ba6" stroke="#7c5ba6"/>
<polygon points="676.7,522.4 678.8,576.8 635.9,559.7" fill="#5d447c" stroke="#5d447c"/>
<polygon points="678.8,576.8 658.2,579.9 635.9,559.7" fill="#6c5091" stroke="#6c5091"/>
<polygon points="747.5,543.6 714.7,592.2 699,555.4" fill="#5c447b" stroke="#5c447b"/>
<polygon points="714.7,592.2 678.8,576.8 699,555.4" fill="#634984" stroke="#634984"/>
<polygon points="714.7,592.2 698.5,625.5 690.9,602.6" fill="#3a3061" stroke="#3a3061"/>
<polygon points="688.9,597.6 671.5,615.4 664,602.3" fill="#5e457e" stroke="#5e457e"/>
<polygon points="688.9,597.6 690.9,602.6 671.5,615.4" fill="#5c447b" stroke="#5c447b"/>
<polygon points="664.4,588.7 688.9,597.6 664,602.3" fill="#6c5091" stroke="#6c5091"/>
<polygon points="678.8,576.8 688.9,597.6 664.4,588.7" fill="#634984" stroke="#634984"/>
<polygon points="688.9,597.6 714.7,592.2 690.9,602.6" fill="#614882" stroke="#614882"/>
<polygon points="714.7,592.2 688.9,597.6 678.8,576.8" fill="#705396" stroke="#705396"/>
<polygon points="1081.3,867.9 1079.9,856.5 1086.5,861.1" fill="#272556" stroke="#272556"/>
<polygon points="1072.2,826.1 1079.9,856.5 1024.2,853.3" fill="#211f48" stroke="#211f48"/>
<polygon points="1024.2,853.3 1079.9,856.5 1030.1,870.5" fill="#24224f" stroke="#24224f"/>
<polygon points="1079.9,856.5 1081.3,867.9 1030.1,870.5" fill="#242250" stroke="#242250"/>
<polygon points="1136,819.1 1106.8,810.3 1137.4,786.5" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1112.1,832.1 1106.8,810.3 1136,819.1" fill="#29265a" stroke="#29265a"/>
<polygon points="1106.8,810.3 1072.2,826.1 1062.9,816.7" fill="#22204a" stroke="#22204a"/>
<polygon points="655.6,686.2 648.4,684.8 626.8,655.7" fill="#4f4285" stroke="#4f4285"/>
<polygon points="648.4,684.8 655.6,686.2 633.8,712.4" fill="#3e3468" stroke="#3e3468"/>
<polygon points="648.4,684.8 598.3,664.9 626.8,655.7" fill="#3c3265" stroke="#3c3265"/>
<polygon points="598.3,664.9 648.4,684.8 633.8,712.4" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="609.9,587.6 637.5,590.5 598,609.3" fill="#453a75" stroke="#453a75"/>
<polygon points="609.9,587.6 623.1,565.6 637.5,590.5" fill="#4f4285" stroke="#4f4285"/>
<polygon points="569.8,557.5 609.9,587.6 598,609.3" fill="#4d4182" stroke="#4d4182"/>
<polygon points="599.3,544.2 609.9,587.6 569.8,557.5" fill="#3f356a" stroke="#3f356a"/>
<polygon points="623.1,565.6 609.9,587.6 617.5,553.2" fill="#3a3162" stroke="#3a3162"/>
<polygon points="609.9,587.6 599.3,544.2 617.5,553.2" fill="#504386" stroke="#504386"/>
<polygon points="516.8,443.7 499,432.3 538.6,398.9" fill="#584176" stroke="#584176"/>
<polygon points="499,432.3 516.8,443.7 495.6,437.3" fill="#715397" stroke="#715397"/>
<polygon points="538.6,398.9 499,432.3 491.7,391.7" fill="#7858a0" stroke="#7858a0"/>
<polygon points="499,432.3 473.3,401.4 491.7,391.7" fill="#614882" stroke="#614882"/>
<polygon points="473.3,401.4 499,432.3 464.9,411.7" fill="#74569b" stroke="#74569b"/>
<polygon points="499,432.3 495.6,437.3 464.9,411.7" fill="#715498" stroke="#715498"/>
<polygon points="568.2,450.4 537.8,438.1 572.4,428.4" fill="#a98baf" stroke="#a98baf"/>
<polygon points="537.8,438.1 516.8,443.7 538.6,398.9" fill="#634985" stroke="#634985"/>
<polygon points="537.8,438.1 568.2,450.4 563.6,479.3" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="537.8,438.1 556.2,399.6 572.4,428.4" fill="#b26f64" stroke="#b26f64"/>
<polygon points="537.8,438.1 538.6,398.9 556.2,399.6" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="545.4,494.9 537.8,438.1 563.6,479.3" fill="#4d4182" stroke="#4d4182"/>
<polygon points="516.8,443.7 537.8,438.1 545.4,494.9" fill="#3b3163" stroke="#3b3163"/>
<polygon points="1433.5,433.1 1416.5,451.4 1417.3,429" fill="#bf7769" stroke="#bf7769"/>
<polygon points="1416.5,451.4 1379.3,430.9 1417.3,429" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="1379.3,430.9 1416.5,451.4 1396.4,465.7" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="1396.4,465.7 1423.9,534.3 1381.8,518.1" fill="#4e4183" stroke="#4e4183"/>
<polygon points="1423.9,534.3 1398.5,557.2 1381.8,518.1" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="1423.9,534.3 1457.6,502.6 1490.4,566.7" fill="#3a3163" stroke="#3a3163"/>
<polygon points="1431.7,599.9 1423.9,534.3 1490.4,566.7" fill="#493d7a" stroke="#493d7a"/>
<polygon points="1398.5,557.2 1423.9,534.3 1431.7,599.9" fill="#3c3264" stroke="#3c3264"/>
<polygon points="1489.3,482.6 1491.2,474.5 1517.2,484.1" fill="#483c79" stroke="#483c79"/>
<polygon points="1491.2,474.5 1489.3,482.6 1484.3,470.7" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1491.2,474.5 1494,443.6 1517.2,484.1" fill="#4f4284" stroke="#4f4284"/>
<polygon points="1491.2,474.5 1484.3,470.7 1494,443.6" fill="#3d3468" stroke="#3d3468"/>
<polygon points="1546.8,403.3 1527.2,434.1 1526.3,400.3" fill="#b16f64" stroke="#b16f64"/>
<polygon points="1527.2,434.1 1546.8,403.3 1562.1,423.8" fill="#b97468" stroke="#b97468"/>
<polygon points="1527.2,434.1 1494,443.6 1502.5,425.9" fill="#b898bf" stroke="#b898bf"/>
<polygon points="1527.2,434.1 1529.9,479.4 1494,443.6" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="1529.9,479.4 1527.2,434.1 1546.1,462.1" fill="#d0acd7" stroke="#d0acd7"/>
<polygon points="1500.6,622.3 1553.8,622.6 1495.6,634.1" fill="#24224f" stroke="#24224f"/>
<polygon points="1500.6,622.3 1525.5,608.4 1553.8,622.6" fill="#23214d" stroke="#23214d"/>
<polygon points="1500.6,622.3 1495.6,634.1 1474.9,618.8" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="1500.6,622.3 1474.9,618.8 1490.4,566.7" fill="#40356b" stroke="#40356b"/>
<polygon points="1550.9,589.5 1527.9,602.2 1490.4,566.7" fill="#463b76" stroke="#463b76"/>
<polygon points="1527.9,602.2 1500.6,622.3 1490.4,566.7" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1500.6,622.3 1527.9,602.2 1525.5,608.4" fill="#201e47" stroke="#201e47"/>
<polygon points="1553.8,622.6 1527.9,602.2 1550.9,589.5" fill="#3f356b" stroke="#3f356b"/>
<polygon points="1525.5,608.4 1527.9,602.2 1553.8,622.6" fill="#272557" stroke="#272557"/>
<polygon points="1384.4,676.2 1404.1,710.7 1378.5,698.6" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="1367.8,671.5 1384.4,676.2 1378.5,698.6" fill="#504387" stroke="#504387"/>
<polygon points="1385.3,657.9 1384.4,676.2 1367.8,671.5" fill="#3c3264" stroke="#3c3264"/>
<polygon points="1537.2,847.5 1528.4,868.7 1511.5,853.8" fill="#1e1c43" stroke="#1e1c43"/>
<polygon points="1537.2,847.5 1506.9,843.1 1543.3,826.5" fill="#201f48" stroke="#201f48"/>
<polygon points="1506.9,843.1 1537.2,847.5 1511.5,853.8" fill="#23214e" stroke="#23214e"/>
<polygon points="1528.4,868.7 1537.2,847.5 1550,866.8" fill="#262453" stroke="#262453"/>
<polygon points="1569.1,869.7 1537.2,847.5 1543.3,826.5" fill="#272556" stroke="#272556"/>
<polygon points="1537.2,847.5 1569.1,869.7 1550,866.8" fill="#262454" stroke="#262454"/>
<polygon points="1244.3,812.5 1290.8,815.7 1246.3,820.7" fill="#24224f" stroke="#24224f"/>
<polygon points="1244.3,812.5 1263.9,784.9 1290.8,815.7" fill="#282659" stroke="#282659"/>
<polygon points="1218.4,788.4 1244.3,812.5 1214.5,826.6" fill="#282658" stroke="#282658"/>
<polygon points="1244.3,812.5 1246.3,820.7 1214.5,826.6" fill="#23214d" stroke="#23214d"/>
<polygon points="1244.3,812.5 1218.4,788.4 1227.1,773.3" fill="#262453" stroke="#262453"/>
<polygon points="1263.9,784.9 1244.3,812.5 1227.1,773.3" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1442.9,90.3 1468.3,129.5 1429.9,167.7" fill="#4a3852" stroke="#4a3852"/>
<polygon points="1481.1,96.4 1442.9,90.3 1448.4,85.2" fill="#3e3253" stroke="#3e3253"/>
<polygon points="1468.3,129.5 1442.9,90.3 1481.1,96.4" fill="#40324f" stroke="#40324f"/>
<polygon points="1442.9,90.3 1355.5,137.5 1354,131.2" fill="#483754" stroke="#483754"/>
<polygon points="1355.5,137.5 1442.9,90.3 1429.9,167.7" fill="#4a3851" stroke="#4a3851"/>
<polygon points="1448.4,85.2 1442.9,90.3 1424.2,63.9" fill="#3b3053" stroke="#3b3053"/>
<polygon points="1442.9,90.3 1366.5,64.5 1424.2,63.9" fill="#362d4f" stroke="#362d4f"/>
<polygon points="1366.5,64.5 1442.9,90.3 1354,131.2" fill="#423455" stroke="#423455"/>
<polygon points="1468.7,140.7 1447.2,170.7 1429.9,167.7" fill="#5a4159" stroke="#5a4159"/>
<polygon points="1468.3,129.5 1468.7,140.7 1429.9,167.7" fill="#503b53" stroke="#503b53"/>
<polygon points="1447.2,170.7 1468.7,140.7 1485,178.9" fill="#573e54" stroke="#573e54"/>
<polygon points="1468.7,140.7 1483.4,112.3 1485,178.9" fill="#533e57" stroke="#533e57"/>
<polygon points="1468.7,140.7 1468.3,129.5 1483.4,112.3" fill="#4a3852" stroke="#4a3852"/>
<polygon points="78.1,390.5 29.7,399.6 64.9,359.2" fill="#9a7f9f" stroke="#9a7f9f"/>
<polygon points="78.1,390.5 66.8,418.9 29.7,399.6" fill="#5d457d" stroke="#5d457d"/>
<polygon points="66.8,418.9 78.1,390.5 105.1,408.5" fill="#7859a1" stroke="#7859a1"/>
<polygon points="78.1,390.5 132.5,365 105.1,408.5" fill="#634984" stroke="#634984"/>
<polygon points="132.5,365 78.1,390.5 64.9,359.2" fill="#6d5092" stroke="#6d5092"/>
<polygon points="66.8,418.9 54.9,448.6 30.4,451.1" fill="#664b88" stroke="#664b88"/>
<polygon points="54.9,448.6 66.8,418.9 59.4,445.6" fill="#5a4278" stroke="#5a4278"/>
<polygon points="30.4,451.1 54.9,448.6 56.3,463.5" fill="#604781" stroke="#604781"/>
<polygon points="54.9,448.6 59.4,445.6 56.3,463.5" fill="#5c447b" stroke="#5c447b"/>
<polygon points="138.2,333.6 164.6,315.6 177.6,372.1" fill="#947b9a" stroke="#947b9a"/>
<polygon points="132.5,365 138.2,333.6 177.6,372.1" fill="#6f5295" stroke="#6f5295"/>
<polygon points="138.2,333.6 132.5,365 129.1,340.6" fill="#967c9b" stroke="#967c9b"/>
<polygon points="164.6,315.6 138.2,333.6 145.7,304.6" fill="#ceaad5" stroke="#ceaad5"/>
<polygon points="121.4,311 138.2,333.6 129.1,340.6" fill="#bf9ec6" stroke="#bf9ec6"/>
<polygon points="138.2,333.6 136.7,301.6 145.7,304.6" fill="#c5a3cc" stroke="#c5a3cc"/>
<polygon points="138.2,333.6 121.4,311 136.7,301.6" fill="#a286a8" stroke="#a286a8"/>
<polygon points="188,309.9 175.7,291.9 208.5,313.1" fill="#88595c" stroke="#88595c"/>
<polygon points="329.8,151.4 337.4,96.3 347.4,141.2" fill="#4c3954" stroke="#4c3954"/>
<polygon points="329.8,151.4 298.7,136.5 337.4,96.3" fill="#4d3a56" stroke="#4d3a56"/>
<polygon points="298.7,136.5 329.8,151.4 324.6,166.6" fill="#513b52" stroke="#513b52"/>
<polygon points="329.8,151.4 349.1,186.3 324.6,166.6" fill="#5d435a" stroke="#5d435a"/>
<polygon points="349.1,186.3 329.8,151.4 347.4,141.2" fill="#594057" stroke="#594057"/>
<polygon points="925.2,334.2 940.2,274.3 994.9,313.3" fill="#b9836b" stroke="#b9836b"/>
<polygon points="827.9,350.5 815,357.9 776,344.6" fill="#ce9073" stroke="#ce9073"/>
<polygon points="831.3,291.9 827.9,350.5 776,344.6" fill="#c3896f" stroke="#c3896f"/>
<polygon points="815,357.9 827.9,350.5 842.9,359.8" fill="#cf9073" stroke="#cf9073"/>
<polygon points="827.9,350.5 890.3,327.4 842.9,359.8" fill="#cb8e72" stroke="#cb8e72"/>
<polygon points="827.9,350.5 831.3,291.9 890.3,327.4" fill="#bb846b" stroke="#bb846b"/>
<polygon points="1015.7,181.4 1035,145.3 1084.8,170.8" fill="#563e53" stroke="#563e53"/>
<polygon points="1084.8,170.8 1035,145.3 1082.8,109.8" fill="#4d3951" stroke="#4d3951"/>
<polygon points="1035,145.3 1033,133.1 1082.8,109.8" fill="#493751" stroke="#493751"/>
<polygon points="1035,145.3 1015.7,181.4 1005.2,156.7" fill="#543d52" stroke="#543d52"/>
<polygon points="1035,145.3 1005.2,156.7 1013.9,131.5" fill="#543e57" stroke="#543e57"/>
<polygon points="1033,133.1 1035,145.3 1013.9,131.5" fill="#4c3952" stroke="#4c3952"/>
<polygon points="1108.1,174.2 1088.4,180.1 1084.8,170.8" fill="#a07669" stroke="#a07669"/>
<polygon points="1088.4,180.1 1108.1,174.2 1087.2,195.6" fill="#987063" stroke="#987063"/>
<polygon points="1088.4,180.1 1067.3,207.5 1084.8,170.8" fill="#9a7264" stroke="#9a7264"/>
<polygon points="1088.4,180.1 1087.2,195.6 1067.3,207.5" fill="#9b7264" stroke="#9b7264"/>
<polygon points="1333.8,220 1326.7,222.4 1315.6,200.3" fill="#684857" stroke="#684857"/>
<polygon points="1326.7,222.4 1288,271.1 1315.6,200.3" fill="#724e5b" stroke="#724e5b"/>
<polygon points="1243,408.1 1265.6,431.5 1231.6,407.9" fill="#d49271" stroke="#d49271"/>
<polygon points="1265.6,431.5 1178.2,475.6 1231.6,407.9" fill="#9a7fa0" stroke="#9a7fa0"/>
<polygon points="1178.2,475.6 1265.6,431.5 1273.5,450.5" fill="#b092b7" stroke="#b092b7"/>
<polygon points="1265.6,431.5 1243,408.1 1261.1,399.9" fill="#db9775" stroke="#db9775"/>
<polygon points="233.9,598.6 264.5,538.8 281.3,607.3" fill="#393061" stroke="#393061"/>
<polygon points="264.5,538.8 305.7,562.1 281.3,607.3" fill="#4e4183" stroke="#4e4183"/>
<polygon points="264.5,538.8 223.3,492 267,493.9" fill="#76579d" stroke="#76579d"/>
<polygon points="223.3,492 264.5,538.8 184.9,542.6" fill="#75569d" stroke="#75569d"/>
<polygon points="264.5,538.8 206.5,593 184.9,542.6" fill="#5e457e" stroke="#5e457e"/>
<polygon points="206.5,593 264.5,538.8 233.9,598.6" fill="#5b437a" stroke="#5b437a"/>
<polygon points="516.2,620.8 548.6,604.8 539.2,632" fill="#3b3264" stroke="#3b3264"/>
<polygon points="332.8,599.6 390.1,598.8 348.6,617.8" fill="#423870" stroke="#423870"/>
<polygon points="332.8,599.6 334.6,540.4 390.1,598.8" fill="#4c4080" stroke="#4c4080"/>
<polygon points="334.6,540.4 332.8,599.6 311.6,555.4" fill="#3b3264" stroke="#3b3264"/>
<polygon points="332.8,599.6 305.7,562.1 311.6,555.4" fill="#504386" stroke="#504386"/>
<polygon points="315.5,624.7 332.8,599.6 325.1,629.7" fill="#40356b" stroke="#40356b"/>
<polygon points="332.8,599.6 348.6,617.8 325.1,629.7" fill="#493e7c" stroke="#493e7c"/>
<polygon points="332.8,599.6 315.5,624.7 281.3,607.3" fill="#3d3367" stroke="#3d3367"/>
<polygon points="305.7,562.1 332.8,599.6 281.3,607.3" fill="#4c4081" stroke="#4c4081"/>
<polygon points="426.3,491.9 442.7,495.2 423.1,505.6" fill="#715397" stroke="#715397"/>
<polygon points="442.7,495.2 451.3,505 423.1,505.6" fill="#614882" stroke="#614882"/>
<polygon points="451.3,505 442.7,495.2 467.4,494.7" fill="#674c8a" stroke="#674c8a"/>
<polygon points="442.7,495.2 479.6,467.6 467.4,494.7" fill="#644a86" stroke="#644a86"/>
<polygon points="442.7,495.2 426.3,491.9 479.6,467.6" fill="#624883" stroke="#624883"/>
<polygon points="298.5,822 206.8,809 259.2,765.9" fill="#22204b" stroke="#22204b"/>
<polygon points="207.1,823.5 206.8,809 298.5,822" fill="#23214d" stroke="#23214d"/>
<polygon points="206.8,809 197.2,792.5 259.2,765.9" fill="#22204b" stroke="#22204b"/>
<polygon points="206.8,809 207.1,823.5 197.2,792.5" fill="#282659" stroke="#282659"/>
<polygon points="1011.5,736.7 1013.6,766.1 998.7,762.6" fill="#1f1e46" stroke="#1f1e46"/>
<polygon points="969.6,742.5 1011.5,736.7 998.7,762.6" fill="#22204b" stroke="#22204b"/>
<polygon points="836.5,711.3 861.3,737 840,748.3" fill="#282659" stroke="#282659"/>
<polygon points="878.6,716.1 861.3,737 836.5,711.3" fill="#262353" stroke="#262353"/>
<polygon points="861.3,737 878.6,716.1 892.2,766.2" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="861.3,737 884.7,780.1 840,748.3" fill="#282658" stroke="#282658"/>
<polygon points="884.7,780.1 861.3,737 892.2,766.2" fill="#282659" stroke="#282659"/>
<polygon points="913.8,736.6 903.4,772.2 892.2,766.2" fill="#211f48" stroke="#211f48"/>
<polygon points="878.6,716.1 913.8,736.6 892.2,766.2" fill="#272556" stroke="#272556"/>
<polygon points="913.8,736.6 878.6,716.1 926,686.9" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="971.5,680.1 945.2,690.9 957.6,623.7" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="945.2,690.9 926,686.9 957.6,623.7" fill="#3c3366" stroke="#3c3366"/>
<polygon points="1089.2,587.9 1063,523.3 1113.9,557" fill="#4f4284" stroke="#4f4284"/>
<polygon points="1063,523.3 1089.2,587.9 1062.3,590.4" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1017.9,572.2 1016.7,567 1029.3,565.9" fill="#3e3469" stroke="#3e3469"/>
<polygon points="1016.7,567 1017.9,572.2 997.7,564" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1016.7,567 1063,523.3 1029.3,565.9" fill="#3b3264" stroke="#3b3264"/>
<polygon points="969.2,516.8 1016.7,567 997.7,564" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="1246.6,539.8 1204.7,540.7 1269.9,508.1" fill="#654b88" stroke="#654b88"/>
<polygon points="1246.6,539.8 1269.9,508.1 1274.6,540.2" fill="#624883" stroke="#624883"/>
<polygon points="1256.9,552.1 1246.6,539.8 1274.6,540.2" fill="#74569c" stroke="#74569c"/>
<polygon points="1246.6,539.8 1256.9,552.1 1228.8,561.1" fill="#634984" stroke="#634984"/>
<polygon points="1204.7,540.7 1246.6,539.8 1228.8,561.1" fill="#694d8c" stroke="#694d8c"/>
<polygon points="1204.7,540.7 1162.5,537 1176.9,510.7" fill="#6e5193" stroke="#6e5193"/>
<polygon points="1162.5,537 1204.7,540.7 1208,572.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="958,538.1 938.9,524.7 969.2,516.8" fill="#41376e" stroke="#41376e"/>
<polygon points="938.9,524.7 958,538.1 913.7,563" fill="#3c3366" stroke="#3c3366"/>
<polygon points="901.4,517 938.9,524.7 913.7,563" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="1111.5,420.4 1146.3,410.4 1135.4,444.4" fill="#654a87" stroke="#654a87"/>
<polygon points="1111.5,420.4 1107.9,388.1 1146.3,410.4" fill="#73559a" stroke="#73559a"/>
<polygon points="751.6,646.7 717.6,637.3 737.5,623.5" fill="#3d3367" stroke="#3d3367"/>
<polygon points="712,653.8 751.6,646.7 740.5,680.1" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="717.6,637.3 751.6,646.7 712,653.8" fill="#252352" stroke="#252352"/>
<polygon points="753.1,606.2 764.4,588.6 808,627.5" fill="#483c79" stroke="#483c79"/>
<polygon points="714.7,592.2 753.1,606.2 737.5,623.5" fill="#4f4285" stroke="#4f4285"/>
<polygon points="753.1,606.2 751.6,646.7 737.5,623.5" fill="#3f356b" stroke="#3f356b"/>
<polygon points="751.6,646.7 753.1,606.2 787,645.5" fill="#4d4081" stroke="#4d4081"/>
<polygon points="854.9,677.3 796.1,641.7 808,627.5" fill="#272556" stroke="#272556"/>
<polygon points="796.1,641.7 753.1,606.2 808,627.5" fill="#483c79" stroke="#483c79"/>
<polygon points="753.1,606.2 796.1,641.7 787,645.5" fill="#4f4386" stroke="#4f4386"/>
<polygon points="796.1,641.7 854.9,677.3 833.2,711.2" fill="#262453" stroke="#262453"/>
<polygon points="846.4,606.2 860.5,626.9 808,627.5" fill="#201e46" stroke="#201e46"/>
<polygon points="832.7,597.2 846.4,606.2 808,627.5" fill="#3b3163" stroke="#3b3163"/>
<polygon points="846.4,606.2 877.8,609.7 860.5,626.9" fill="#252352" stroke="#252352"/>
<polygon points="840.2,583.6 832.7,597.2 784.8,551.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="840.2,583.6 846.4,606.2 832.7,597.2" fill="#3f356a" stroke="#3f356a"/>
<polygon points="747.5,543.6 784.5,548.6 784.8,551.2" fill="#443972" stroke="#443972"/>
<polygon points="784.5,548.6 747.5,543.6 773.7,511.3" fill="#3e3468" stroke="#3e3468"/>
<polygon points="784.5,548.6 773.7,511.3 814.4,497.6" fill="#3d3367" stroke="#3d3367"/>
<polygon points="784.5,548.6 814.4,497.6 816.8,498.8" fill="#3e3469" stroke="#3e3469"/>
<polygon points="784.5,548.6 847,551 784.8,551.2" fill="#41366d" stroke="#41366d"/>
<polygon points="784.5,548.6 816.8,498.8 847,551" fill="#41376e" stroke="#41376e"/>
<polygon points="763.3,583 714.7,592.2 747.5,543.6" fill="#3c3265" stroke="#3c3265"/>
<polygon points="764.4,588.6 763.3,583 784.8,551.2" fill="#393060" stroke="#393060"/>
<polygon points="763.3,583 747.5,543.6 784.8,551.2" fill="#4f4285" stroke="#4f4285"/>
<polygon points="753.1,606.2 763.3,583 764.4,588.6" fill="#3e3468" stroke="#3e3468"/>
<polygon points="763.3,583 753.1,606.2 714.7,592.2" fill="#3f356b" stroke="#3f356b"/>
<polygon points="1095.4,830.3 1106.8,810.3 1112.1,832.1" fill="#201e47" stroke="#201e47"/>
<polygon points="1106.8,810.3 1095.4,830.3 1072.2,826.1" fill="#211f49" stroke="#211f49"/>
<polygon points="1095.4,830.3 1079.9,856.5 1072.2,826.1" fill="#282658" stroke="#282658"/>
<polygon points="1095.4,830.3 1112.1,832.1 1086.5,861.1" fill="#282558" stroke="#282558"/>
<polygon points="1079.9,856.5 1095.4,830.3 1086.5,861.1" fill="#201f48" stroke="#201f48"/>
<polygon points="1013.6,766.1 1068.8,773 1062.9,816.7" fill="#29265a" stroke="#29265a"/>
<polygon points="1068.8,773 1106.8,810.3 1062.9,816.7" fill="#272556" stroke="#272556"/>
<polygon points="698.5,625.5 715.8,633.9 692,650.2" fill="#504387" stroke="#504387"/>
<polygon points="715.8,633.9 712,653.8 692,650.2" fill="#21204a" stroke="#21204a"/>
<polygon points="715.8,633.9 717.6,637.3 712,653.8" fill="#201e47" stroke="#201e47"/>
<polygon points="717.6,637.3 715.8,633.9 737.5,623.5" fill="#41376e" stroke="#41376e"/>
<polygon points="715.8,633.9 714.7,592.2 737.5,623.5" fill="#4c4080" stroke="#4c4080"/>
<polygon points="714.7,592.2 715.8,633.9 698.5,625.5" fill="#3d3367" stroke="#3d3367"/>
<polygon points="1416.5,451.4 1432.1,462.5 1396.4,465.7" fill="#443972" stroke="#443972"/>
<polygon points="1432.1,462.5 1423.9,534.3 1396.4,465.7" fill="#3e3469" stroke="#3e3469"/>
<polygon points="1423.9,534.3 1432.1,462.5 1457.6,502.6" fill="#4c4081" stroke="#4c4081"/>
<polygon points="1457.6,502.6 1432.1,462.5 1471.6,467.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1432.1,462.5 1433.5,433.1 1471.6,467.2" fill="#4c4080" stroke="#4c4080"/>
<polygon points="1432.1,462.5 1416.5,451.4 1433.5,433.1" fill="#3b3163" stroke="#3b3163"/>
<polygon points="1515,422.5 1505.2,420.3 1526.3,400.3" fill="#b57166" stroke="#b57166"/>
<polygon points="1527.2,434.1 1515,422.5 1526.3,400.3" fill="#b77367" stroke="#b77367"/>
<polygon points="1505.2,420.3 1515,422.5 1502.5,425.9" fill="#ba7568" stroke="#ba7568"/>
<polygon points="1515,422.5 1527.2,434.1 1502.5,425.9" fill="#bd7669" stroke="#bd7669"/>
<polygon points="1554.7,447.7 1562.1,423.8 1565.5,449.4" fill="#c87c6d" stroke="#c87c6d"/>
<polygon points="1554.7,447.7 1527.2,434.1 1562.1,423.8" fill="#c57b6c" stroke="#c57b6c"/>
<polygon points="1546.1,462.1 1554.7,447.7 1565.5,449.4" fill="#bd9dc4" stroke="#bd9dc4"/>
<polygon points="1527.2,434.1 1554.7,447.7 1546.1,462.1" fill="#ceaad5" stroke="#ceaad5"/>
<polygon points="1427.1,679.1 1384.4,676.2 1385.3,657.9" fill="#473b77" stroke="#473b77"/>
<polygon points="1427.1,679.1 1475.9,660.2 1435.2,708.6" fill="#3e3468" stroke="#3e3468"/>
<polygon points="1427.1,679.1 1385.3,657.9 1385.7,650" fill="#4c4080" stroke="#4c4080"/>
<polygon points="1427.1,679.1 1385.7,650 1431.7,599.9" fill="#3c3264" stroke="#3c3264"/>
<polygon points="1474.9,618.8 1427.1,679.1 1431.7,599.9" fill="#4f4284" stroke="#4f4284"/>
<polygon points="1475.9,660.2 1427.1,679.1 1474.9,618.8" fill="#40356b" stroke="#40356b"/>
<polygon points="185.4,314 164.6,315.6 162.6,300.3" fill="#c3a1ca" stroke="#c3a1ca"/>
<polygon points="185.4,314 162.6,300.3 175.7,291.9" fill="#8c5c5f" stroke="#8c5c5f"/>
<polygon points="188,309.9 185.4,314 175.7,291.9" fill="#89595d" stroke="#89595d"/>
<polygon points="164.6,315.6 185.4,314 177.6,372.1" fill="#a084a6" stroke="#a084a6"/>
<polygon points="177.6,372.1 185.4,314 211.9,325.4" fill="#c8a5cf" stroke="#c8a5cf"/>
<polygon points="211.9,325.4 185.4,314 208.5,313.1" fill="#946062" stroke="#946062"/>
<polygon points="185.4,314 188,309.9 208.5,313.1" fill="#8b5a5c" stroke="#8b5a5c"/>
<polygon points="918.7,327.1 890.3,327.4 940.2,274.3" fill="#b37f68" stroke="#b37f68"/>
<polygon points="925.2,334.2 918.7,327.1 940.2,274.3" fill="#b68069" stroke="#b68069"/>
<polygon points="1020.4,379.3 1032,310.4 1114.2,372.3" fill="#6e5193" stroke="#6e5193"/>
<polygon points="890.3,327.4 889.6,335.3 842.9,359.8" fill="#cd9074" stroke="#cd9074"/>
<polygon points="918.7,327.1 889.6,335.3 890.3,327.4" fill="#c0876d" stroke="#c0876d"/>
<polygon points="1288,271.1 1329.9,238.4 1324.2,309.5" fill="#875a62" stroke="#875a62"/>
<polygon points="1326.7,222.4 1329.9,238.4 1288,271.1" fill="#76505b" stroke="#76505b"/>
<polygon points="1329.9,238.4 1326.7,222.4 1333.8,220" fill="#75505e" stroke="#75505e"/>
<polygon points="1324.2,309.5 1329.9,238.4 1389.6,303.2" fill="#80555a" stroke="#80555a"/>
<polygon points="1329.9,238.4 1402.5,240 1389.6,303.2" fill="#7a515a" stroke="#7a515a"/>
<polygon points="1329.9,238.4 1333.8,220 1402.5,240" fill="#76505d" stroke="#76505d"/>
<polygon points="1265.6,431.5 1285.1,426.4 1273.5,450.5" fill="#9d81a2" stroke="#9d81a2"/>
<polygon points="1285.1,426.4 1265.6,431.5 1286.4,416.3" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="1285.1,426.4 1286.4,416.3 1313.2,417.4" fill="#af91b6" stroke="#af91b6"/>
<polygon points="1285.1,426.4 1313.2,417.4 1329.2,477.4" fill="#a387a9" stroke="#a387a9"/>
<polygon points="1273.5,450.5 1285.1,426.4 1329.2,477.4" fill="#c5a3cc" stroke="#c5a3cc"/>
<polygon points="1271.1,402.6 1261.1,399.9 1270.3,399.9" fill="#d29171" stroke="#d29171"/>
<polygon points="1271.1,402.6 1265.6,431.5 1261.1,399.9" fill="#d49272" stroke="#d49272"/>
<polygon points="1286.4,416.3 1271.1,402.6 1270.3,399.9" fill="#d99675" stroke="#d99675"/>
<polygon points="1265.6,431.5 1271.1,402.6 1286.4,416.3" fill="#cd8d6e" stroke="#cd8d6e"/>
<polygon points="264.5,538.8 276.3,535.5 305.7,562.1" fill="#483c79" stroke="#483c79"/>
<polygon points="276.3,535.5 264.5,538.8 267,493.9" fill="#75569d" stroke="#75569d"/>
<polygon points="516.2,620.8 536.2,584.3 548.6,604.8" fill="#3c3366" stroke="#3c3366"/>
<polygon points="536.2,584.3 569.8,557.5 548.6,604.8" fill="#3a3061" stroke="#3a3061"/>
<polygon points="536.2,584.3 536.5,577.4 569.8,557.5" fill="#3d3366" stroke="#3d3366"/>
<polygon points="536.5,577.4 536.2,584.3 488.4,578.2" fill="#40366c" stroke="#40366c"/>
<polygon points="536.2,584.3 516.2,620.8 488.4,578.2" fill="#4e4183" stroke="#4e4183"/>
<polygon points="496.3,629.5 499.5,639.7 485.2,633.3" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="496.3,629.5 485.2,633.3 488.4,578.2" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="516.2,620.8 496.3,629.5 488.4,578.2" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="1145.8,723.4 1195.1,683.3 1174.6,731.1" fill="#211f49" stroke="#211f49"/>
<polygon points="1195.1,683.3 1145.8,723.4 1178.5,657.8" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1145.8,723.4 1109.7,684.5 1178.5,657.8" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1109.7,684.5 1140.4,637 1178.5,657.8" fill="#272556" stroke="#272556"/>
<polygon points="1140.4,637 1171.7,619 1178.5,657.8" fill="#201e47" stroke="#201e47"/>
<polygon points="1153.9,606.5 1140.4,637 1118.4,614.7" fill="#3d3367" stroke="#3d3367"/>
<polygon points="1140.4,637 1153.9,606.5 1171.7,619" fill="#272557" stroke="#272557"/>
<polygon points="1062.3,590.4 1075.5,632 1029.8,591.9" fill="#3a3162" stroke="#3a3162"/>
<polygon points="1075.5,632 1089.2,587.9 1118.4,614.7" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1089.2,587.9 1075.5,632 1062.3,590.4" fill="#40366c" stroke="#40366c"/>
<polygon points="989.7,695.3 1011.5,736.7 969.6,742.5" fill="#282658" stroke="#282658"/>
<polygon points="903.4,772.2 922.8,736.3 917.1,781.3" fill="#201e47" stroke="#201e47"/>
<polygon points="913.8,736.6 922.8,736.3 903.4,772.2" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1159.6,535.2 1141.2,532.7 1176.9,510.7" fill="#644985" stroke="#644985"/>
<polygon points="1162.5,537 1159.6,535.2 1176.9,510.7" fill="#614882" stroke="#614882"/>
<polygon points="1141.2,532.7 1147.3,547.1 1113.9,557" fill="#624883" stroke="#624883"/>
<polygon points="1159.6,535.2 1147.3,547.1 1141.2,532.7" fill="#7a5aa3" stroke="#7a5aa3"/>
<polygon points="1147.3,547.1 1159.6,535.2 1162.5,537" fill="#674c8a" stroke="#674c8a"/>
<polygon points="1153.9,606.5 1147.3,547.1 1165.4,606.8" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1165.4,606.8 1147.3,547.1 1208,572.2" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1147.3,547.1 1162.5,537 1208,572.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1106.5,432.1 1111.5,420.4 1135.4,444.4" fill="#705396" stroke="#705396"/>
<polygon points="773.1,666.4 751.6,646.7 787,645.5" fill="#40366c" stroke="#40366c"/>
<polygon points="773.1,666.4 833.2,711.2 767.4,695.2" fill="#252353" stroke="#252353"/>
<polygon points="757.1,694.2 773.1,666.4 767.4,695.2" fill="#1d1c41" stroke="#1d1c41"/>
<polygon points="773.1,666.4 796.1,641.7 833.2,711.2" fill="#262455" stroke="#262455"/>
<polygon points="796.1,641.7 773.1,666.4 787,645.5" fill="#382f5f" stroke="#382f5f"/>
<polygon points="740.5,680.1 773.1,666.4 757.1,694.2" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="751.6,646.7 773.1,666.4 740.5,680.1" fill="#272557" stroke="#272557"/>
<polygon points="846.4,606.2 862.5,588.6 877.8,609.7" fill="#40366c" stroke="#40366c"/>
<polygon points="840.2,583.6 862.5,588.6 846.4,606.2" fill="#493d7b" stroke="#493d7b"/>
<polygon points="847,551 845.8,572.4 784.8,551.2" fill="#453a74" stroke="#453a74"/>
<polygon points="845.8,572.4 840.2,583.6 784.8,551.2" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="845.8,572.4 847,551 860.6,570.5" fill="#4f4285" stroke="#4f4285"/>
<polygon points="1011.5,736.7 1054.5,729.7 1013.6,766.1" fill="#201e46" stroke="#201e46"/>
<polygon points="1054.5,729.7 1068.8,773 1013.6,766.1" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1106.8,810.3 1122.9,761.6 1137.4,786.5" fill="#1e1c42" stroke="#1e1c42"/>
<polygon points="1068.8,773 1122.9,761.6 1106.8,810.3" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="1122.9,761.6 1174.6,731.1 1137.4,786.5" fill="#201e46" stroke="#201e46"/>
<polygon points="1432.4,714.2 1424.8,701.6 1435.2,708.6" fill="#514488" stroke="#514488"/>
<polygon points="1424.8,701.6 1432.4,714.2 1414.5,721.2" fill="#3a3162" stroke="#3a3162"/>
<polygon points="1404.1,710.7 1424.8,701.6 1414.5,721.2" fill="#40366c" stroke="#40366c"/>
<polygon points="1384.4,676.2 1426.3,694.2 1404.1,710.7" fill="#4f4284" stroke="#4f4284"/>
<polygon points="1427.1,679.1 1426.3,694.2 1384.4,676.2" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="1426.3,694.2 1424.8,701.6 1404.1,710.7" fill="#3d3367" stroke="#3d3367"/>
<polygon points="1426.3,694.2 1427.1,679.1 1435.2,708.6" fill="#4e4284" stroke="#4e4284"/>
<polygon points="1424.8,701.6 1426.3,694.2 1435.2,708.6" fill="#4f4285" stroke="#4f4285"/>
<polygon points="1006,476.9 1016.7,567 969.2,516.8" fill="#3f356a" stroke="#3f356a"/>
<polygon points="1016.7,567 1006,476.9 1063,523.3" fill="#4f4386" stroke="#4f4386"/>
<polygon points="305.7,562.1 297.9,527.4 311.6,555.4" fill="#514488" stroke="#514488"/>
<polygon points="276.3,535.5 297.9,527.4 305.7,562.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="297.9,527.4 319.8,538.2 311.6,555.4" fill="#4c4080" stroke="#4c4080"/>
<polygon points="319.8,538.2 297.9,527.4 306.8,492.2" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="306.8,492.2 297.9,527.4 285.2,485.9" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="297.9,527.4 267,493.9 285.2,485.9" fill="#5c447b" stroke="#5c447b"/>
<polygon points="297.9,527.4 276.3,535.5 267,493.9" fill="#7b5ba5" stroke="#7b5ba5"/>
<polygon points="496.3,629.5 504.1,635.2 499.5,639.7" fill="#262454" stroke="#262454"/>
<polygon points="504.1,635.2 496.3,629.5 516.2,620.8" fill="#22204c" stroke="#22204c"/>
<polygon points="499.5,639.7 504.1,635.2 539.2,632" fill="#242250" stroke="#242250"/>
<polygon points="504.1,635.2 516.2,620.8 539.2,632" fill="#24224f" stroke="#24224f"/>
<polygon points="985.4,670.9 989.7,695.3 971.5,680.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="1029.8,591.9 985.4,670.9 957.6,623.7" fill="#3d3468" stroke="#3d3468"/>
<polygon points="945.2,690.9 938.3,722.5 926,686.9" fill="#272556" stroke="#272556"/>
<polygon points="938.3,722.5 913.8,736.6 926,686.9" fill="#282658" stroke="#282658"/>
<polygon points="938.3,722.5 922.8,736.3 913.8,736.6" fill="#21204a" stroke="#21204a"/>
<polygon points="1147.3,547.1 1117.6,569.1 1113.9,557" fill="#3e3469" stroke="#3e3469"/>
<polygon points="1117.6,569.1 1147.3,547.1 1153.9,606.5" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1117.6,569.1 1153.9,606.5 1118.4,614.7" fill="#493d7b" stroke="#493d7b"/>
<polygon points="1089.2,587.9 1117.6,569.1 1118.4,614.7" fill="#3b3163" stroke="#3b3163"/>
<polygon points="1117.6,569.1 1089.2,587.9 1113.9,557" fill="#3e3469" stroke="#3e3469"/>
<polygon points="1122.5,462.5 1135.4,444.4 1140,478.5" fill="#604781" stroke="#604781"/>
<polygon points="1122.5,462.5 1106.5,432.1 1135.4,444.4" fill="#7d5ca7" stroke="#7d5ca7"/>
<polygon points="877.8,609.7 864.1,582 913.7,563" fill="#3c3366" stroke="#3c3366"/>
<polygon points="862.5,588.6 864.1,582 877.8,609.7" fill="#4d4182" stroke="#4d4182"/>
<polygon points="913.7,563 864.1,582 860.6,570.5" fill="#40366c" stroke="#40366c"/>
<polygon points="864.1,582 845.8,572.4 860.6,570.5" fill="#433871" stroke="#433871"/>
<polygon points="864.1,582 862.5,588.6 840.2,583.6" fill="#443973" stroke="#443973"/>
<polygon points="845.8,572.4 864.1,582 840.2,583.6" fill="#493e7c" stroke="#493e7c"/>
<polygon points="985.4,670.9 1002.9,682.7 989.7,695.3" fill="#4b3f7e" stroke="#4b3f7e"/>
<polygon points="1054.5,729.7 1112.2,719.5 1068.8,773" fill="#201f48" stroke="#201f48"/>
<polygon points="938.9,524.7 917.1,498.8 969.2,516.8" fill="#4c4080" stroke="#4c4080"/>
<polygon points="917.1,498.8 928,469.8 969.2,516.8" fill="#4c3f7f" stroke="#4c3f7f"/>
<polygon points="917.1,498.8 938.9,524.7 901.4,517" fill="#463b77" stroke="#463b77"/>
<polygon points="928,469.8 961.9,457.4 969.2,516.8" fill="#3b3163" stroke="#3b3163"/>
<polygon points="961.9,457.4 1006,476.9 969.2,516.8" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="1077.4,500.8 1122.5,462.5 1140,478.5" fill="#5d447c" stroke="#5d447c"/>
<polygon points="1141.2,532.7 1077.4,500.8 1140,478.5" fill="#5b437a" stroke="#5b437a"/>
<polygon points="1077.4,500.8 1141.2,532.7 1113.9,557" fill="#73559a" stroke="#73559a"/>
<polygon points="1063,523.3 1077.4,500.8 1113.9,557" fill="#4e4284" stroke="#4e4284"/>
<polygon points="980,669.5 971.5,680.1 957.6,623.7" fill="#514488" stroke="#514488"/>
<polygon points="985.4,670.9 980,669.5 957.6,623.7" fill="#514488" stroke="#514488"/>
<polygon points="980,669.5 985.4,670.9 971.5,680.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="950.8,748.7 938.3,722.5 969.6,742.5" fill="#29265a" stroke="#29265a"/>
<polygon points="938.3,722.5 950.8,748.7 922.8,736.3" fill="#201e47" stroke="#201e47"/>
<polygon points="922.8,736.3 950.8,748.7 917.1,781.3" fill="#272556" stroke="#272556"/>
<polygon points="950.8,748.7 938.6,804.4 917.1,781.3" fill="#201e46" stroke="#201e46"/>
<polygon points="950.8,748.7 998.7,762.6 938.6,804.4" fill="#272556" stroke="#272556"/>
<polygon points="950.8,748.7 969.6,742.5 998.7,762.6" fill="#272556" stroke="#272556"/>
<polygon points="953.1,701.8 989.7,695.3 969.6,742.5" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="938.3,722.5 953.1,701.8 969.6,742.5" fill="#262453" stroke="#262453"/>
<polygon points="953.1,701.8 938.3,722.5 945.2,690.9" fill="#4a3e7c" stroke="#4a3e7c"/>
<polygon points="953.1,701.8 945.2,690.9 971.5,680.1" fill="#3f356a" stroke="#3f356a"/>
<polygon points="989.7,695.3 953.1,701.8 971.5,680.1" fill="#40366d" stroke="#40366d"/>
<polygon points="1044.7,670.5 985.4,670.9 1029.8,591.9" fill="#3c3265" stroke="#3c3265"/>
<polygon points="1044.7,670.5 1002.9,682.7 985.4,670.9" fill="#463a75" stroke="#463a75"/>
<polygon points="1122.9,751.4 1122.9,761.6 1068.8,773" fill="#23214d" stroke="#23214d"/>
<polygon points="1112.2,719.5 1122.9,751.4 1068.8,773" fill="#201e47" stroke="#201e47"/>
<polygon points="1122.9,751.4 1112.2,719.5 1145.8,723.4" fill="#272455" stroke="#272455"/>
<polygon points="1122.9,751.4 1145.8,723.4 1174.6,731.1" fill="#262455" stroke="#262455"/>
<polygon points="1122.9,761.6 1122.9,751.4 1174.6,731.1" fill="#22204b" stroke="#22204b"/>
<polygon points="1086.8,694.9 1112.2,719.5 1054.5,729.7" fill="#1f1d45" stroke="#1f1d45"/>
<polygon points="907.2,479.7 901.4,517 868.5,519.3" fill="#584175" stroke="#584175"/>
<polygon points="907.2,479.7 917.1,498.8 901.4,517" fill="#77589f" stroke="#77589f"/>
<polygon points="842.8,501.5 907.2,479.7 868.5,519.3" fill="#654a87" stroke="#654a87"/>
<polygon points="917.1,498.8 907.2,479.7 928,469.8" fill="#5a4379" stroke="#5a4379"/>
<polygon points="860.5,412.9 907.2,479.7 842.8,501.5" fill="#75569c" stroke="#75569c"/>
<polygon points="814.4,497.6 835,421.5 842.8,501.5" fill="#5a4379" stroke="#5a4379"/>
<polygon points="835,421.5 860.5,412.9 842.8,501.5" fill="#5f467f" stroke="#5f467f"/>
<polygon points="812.7,408.2 835,421.5 799.7,415.3" fill="#74569c" stroke="#74569c"/>
<polygon points="835,421.5 812.7,408.2 830.5,392.1" fill="#584176" stroke="#584176"/>
<polygon points="860.5,412.9 835,421.5 830.5,392.1" fill="#76579e" stroke="#76579e"/>
<polygon points="835,421.5 805,494.4 799.7,415.3" fill="#75569c" stroke="#75569c"/>
<polygon points="835,421.5 814.4,497.6 805,494.4" fill="#5d457d" stroke="#5d457d"/>
<polygon points="928,469.8 894.3,406.9 954.6,436.4" fill="#bc9cc3" stroke="#bc9cc3"/>
<polygon points="907.2,479.7 894.3,406.9 928,469.8" fill="#7b5aa4" stroke="#7b5aa4"/>
<polygon points="971.6,394.6 960.3,401.6 948.9,384" fill="#77579f" stroke="#77579f"/>
<polygon points="961.9,457.4 992.5,424 1006,476.9" fill="#3d3367" stroke="#3d3367"/>
<polygon points="928.3,345.4 923.5,343.5 925.2,334.2" fill="#bb8369" stroke="#bb8369"/>
<polygon points="923.5,343.5 918.7,327.1 925.2,334.2" fill="#ba836a" stroke="#ba836a"/>
<polygon points="923.5,343.5 889.6,335.3 918.7,327.1" fill="#be856c" stroke="#be856c"/>
<polygon points="1076.6,408 1041,405.2 1020.4,379.3" fill="#6b4f90" stroke="#6b4f90"/>
<polygon points="1107.9,388.1 1076.6,408 1114.2,372.3" fill="#5b437a" stroke="#5b437a"/>
<polygon points="1076.6,408 1020.4,379.3 1114.2,372.3" fill="#674c8a" stroke="#674c8a"/>
<polygon points="1111.5,420.4 1076.6,408 1107.9,388.1" fill="#654a87" stroke="#654a87"/>
<polygon points="1106.5,432.1 1076.6,408 1111.5,420.4" fill="#6e5193" stroke="#6e5193"/>
<polygon points="1048.7,455.9 1076.6,408 1106.5,432.1" fill="#705396" stroke="#705396"/>
<polygon points="1076.6,408 1048.7,455.9 1041,405.2" fill="#76579f" stroke="#76579f"/>
<polygon points="1032.9,685.3 1054.5,729.7 1011.5,736.7" fill="#29275a" stroke="#29275a"/>
<polygon points="1032.9,685.3 1086.8,694.9 1054.5,729.7" fill="#252353" stroke="#252353"/>
<polygon points="1086.8,694.9 1032.9,685.3 1044.7,670.5" fill="#252352" stroke="#252352"/>
<polygon points="1110.5,703.6 1086.8,694.9 1109.7,684.5" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1086.8,694.9 1110.5,703.6 1112.2,719.5" fill="#2a275c" stroke="#2a275c"/>
<polygon points="1145.8,723.4 1110.5,703.6 1109.7,684.5" fill="#282558" stroke="#282558"/>
<polygon points="1112.2,719.5 1110.5,703.6 1145.8,723.4" fill="#262454" stroke="#262454"/>
<polygon points="1086.4,674.3 1086.8,694.9 1044.7,670.5" fill="#242251" stroke="#242251"/>
<polygon points="1086.8,694.9 1086.4,674.3 1109.7,684.5" fill="#262454" stroke="#262454"/>
<polygon points="1086.4,674.3 1140.4,637 1109.7,684.5" fill="#3e3468" stroke="#3e3468"/>
<polygon points="1140.4,637 1086.4,674.3 1118.4,614.7" fill="#3a3061" stroke="#3a3061"/>
<polygon points="1086.4,674.3 1075.5,632 1118.4,614.7" fill="#3a3061" stroke="#3a3061"/>
<polygon points="882.7,389.2 860.5,412.9 830.5,392.1" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="873.4,408.3 907.2,479.7 860.5,412.9" fill="#73559a" stroke="#73559a"/>
<polygon points="873.4,408.3 894.3,406.9 907.2,479.7" fill="#5f467f" stroke="#5f467f"/>
<polygon points="882.7,389.2 873.4,408.3 860.5,412.9" fill="#997e9e" stroke="#997e9e"/>
<polygon points="873.4,408.3 882.7,389.2 894.3,406.9" fill="#be9dc5" stroke="#be9dc5"/>
<polygon points="960.3,401.6 929.9,394.3 948.9,384" fill="#a286a8" stroke="#a286a8"/>
<polygon points="894.3,406.9 929.9,394.3 954.6,436.4" fill="#997f9f" stroke="#997f9f"/>
<polygon points="929.9,394.3 960.3,401.6 954.6,436.4" fill="#74569c" stroke="#74569c"/>
<polygon points="960.9,444.8 992.5,424 961.9,457.4" fill="#3e3469" stroke="#3e3469"/>
<polygon points="960.3,401.6 971.5,412.9 954.6,436.4" fill="#7858a0" stroke="#7858a0"/>
<polygon points="971.5,412.9 960.9,444.8 954.6,436.4" fill="#624883" stroke="#624883"/>
<polygon points="960.9,444.8 971.5,412.9 992.5,424" fill="#4a3e7d" stroke="#4a3e7d"/>
<polygon points="971.5,412.9 960.3,401.6 971.6,394.6" fill="#644a85" stroke="#644a85"/>
<polygon points="1012.8,369.4 994.9,313.3 1032,310.4" fill="#644985" stroke="#644985"/>
<polygon points="1012.8,369.4 1004.2,372.7 994.9,313.3" fill="#73559a" stroke="#73559a"/>
<polygon points="1020.4,379.3 1012.8,369.4 1032,310.4" fill="#5b437a" stroke="#5b437a"/>
<polygon points="1004.2,372.7 1012.8,369.4 1020.4,379.3" fill="#684d8b" stroke="#684d8b"/>
<polygon points="969.9,358.6 971.6,394.6 948.9,384" fill="#957c9b" stroke="#957c9b"/>
<polygon points="969.9,358.6 1004.2,372.7 971.6,394.6" fill="#7859a1" stroke="#7859a1"/>
<polygon points="1004.2,372.7 969.9,358.6 994.9,313.3" fill="#a084a5" stroke="#a084a5"/>
<polygon points="1004.2,372.7 994.4,421.7 971.6,394.6" fill="#584176" stroke="#584176"/>
<polygon points="994.4,421.7 971.5,412.9 971.6,394.6" fill="#77589f" stroke="#77589f"/>
<polygon points="971.5,412.9 994.4,421.7 992.5,424" fill="#75579d" stroke="#75579d"/>
<polygon points="1122.5,462.5 1054.6,474.1 1106.5,432.1" fill="#644a86" stroke="#644a86"/>
<polygon points="1054.6,474.1 1048.7,455.9 1106.5,432.1" fill="#664b89" stroke="#664b89"/>
<polygon points="1077.4,500.8 1054.6,474.1 1122.5,462.5" fill="#684c8b" stroke="#684c8b"/>
<polygon points="1048.7,455.9 1054.6,474.1 1006,476.9" fill="#41366d" stroke="#41366d"/>
<polygon points="1006,476.9 1054.6,474.1 1063,523.3" fill="#3a3162" stroke="#3a3162"/>
<polygon points="1054.6,474.1 1077.4,500.8 1063,523.3" fill="#4f4386" stroke="#4f4386"/>
<polygon points="1012.8,428.4 1048.7,455.9 1006,476.9" fill="#504387" stroke="#504387"/>
<polygon points="1048.7,455.9 1012.8,428.4 1041,405.2" fill="#614782" stroke="#614782"/>
<polygon points="1044.7,670.5 1016.1,686.7 1002.9,682.7" fill="#443972" stroke="#443972"/>
<polygon points="1032.9,685.3 1016.1,686.7 1044.7,670.5" fill="#22204b" stroke="#22204b"/>
<polygon points="1016.1,686.7 1032.9,685.3 1011.5,736.7" fill="#1f1d44" stroke="#1f1d44"/>
<polygon points="989.7,695.3 1016.1,686.7 1011.5,736.7" fill="#1e1d43" stroke="#1e1d43"/>
<polygon points="1002.9,682.7 1016.1,686.7 989.7,695.3" fill="#453a75" stroke="#453a75"/>
<polygon points="1086.4,674.3 1067.3,649.5 1075.5,632" fill="#4d4183" stroke="#4d4183"/>
<polygon points="1067.3,649.5 1086.4,674.3 1044.7,670.5" fill="#42376f" stroke="#42376f"/>
<polygon points="1075.5,632 1067.3,649.5 1029.8,591.9" fill="#504387" stroke="#504387"/>
<polygon points="1067.3,649.5 1044.7,670.5 1029.8,591.9" fill="#4e4183" stroke="#4e4183"/>
<polygon points="889.6,335.3 877.3,370.7 842.9,359.8" fill="#cc8e72" stroke="#cc8e72"/>
<polygon points="877.3,370.7 829.9,389 842.9,359.8" fill="#ba9ac1" stroke="#ba9ac1"/>
<polygon points="829.9,389 877.3,370.7 830.5,392.1" fill="#a084a6" stroke="#a084a6"/>
<polygon points="877.3,370.7 882.7,389.2 830.5,392.1" fill="#a78aad" stroke="#a78aad"/>
<polygon points="877.3,370.7 907.4,388.5 882.7,389.2" fill="#bb9bc2" stroke="#bb9bc2"/>
<polygon points="882.7,389.2 907.4,388.5 894.3,406.9" fill="#9d82a3" stroke="#9d82a3"/>
<polygon points="907.4,388.5 929.9,394.3 894.3,406.9" fill="#bd9dc4" stroke="#bd9dc4"/>
<polygon points="960.9,444.8 958.2,448.2 954.6,436.4" fill="#4d4182" stroke="#4d4182"/>
<polygon points="958.2,448.2 960.9,444.8 961.9,457.4" fill="#3a3061" stroke="#3a3061"/>
<polygon points="958.2,448.2 928,469.8 954.6,436.4" fill="#40356b" stroke="#40356b"/>
<polygon points="958.2,448.2 961.9,457.4 928,469.8" fill="#41376e" stroke="#41376e"/>
<polygon points="1041,405.2 1031.4,398.4 1020.4,379.3" fill="#7959a2" stroke="#7959a2"/>
<polygon points="1031.4,398.4 1030.1,403.1 1020.4,379.3" fill="#725498" stroke="#725498"/>
<polygon points="1030.1,403.1 1031.4,398.4 1041,405.2" fill="#73559a" stroke="#73559a"/>
<polygon points="966,350.7 969.9,358.6 948.9,384" fill="#a589ab" stroke="#a589ab"/>
<polygon points="966,350.7 928.3,345.4 925.2,334.2" fill="#bc846a" stroke="#bc846a"/>
<polygon points="966,350.7 925.2,334.2 994.9,313.3" fill="#cb8e73" stroke="#cb8e73"/>
<polygon points="969.9,358.6 966,350.7 994.9,313.3" fill="#937a98" stroke="#937a98"/>
<polygon points="1030.1,403.1 1019.5,413.1 1020.4,379.3" fill="#7758a0" stroke="#7758a0"/>
<polygon points="1019.5,413.1 1030.1,403.1 1041,405.2" fill="#6c5091" stroke="#6c5091"/>
<polygon points="1012.8,428.4 1019.5,413.1 1041,405.2" fill="#654b87" stroke="#654b87"/>
<polygon points="1019.5,413.1 1004.2,372.7 1020.4,379.3" fill="#76579e" stroke="#76579e"/>
<polygon points="1019.5,413.1 994.4,421.7 1004.2,372.7" fill="#7c5ca6" stroke="#7c5ca6"/>
<polygon points="924.3,366.6 907.4,388.5 877.3,370.7" fill="#ca8c6f" stroke="#ca8c6f"/>
<polygon points="923.5,343.5 924.3,366.6 889.6,335.3" fill="#bd846a" stroke="#bd846a"/>
<polygon points="924.3,366.6 877.3,370.7 889.6,335.3" fill="#c88c70" stroke="#c88c70"/>
<polygon points="924.3,366.6 923.5,343.5 928.3,345.4" fill="#bf856b" stroke="#bf856b"/>
<polygon points="907.4,388.5 924.3,366.6 929.9,394.3" fill="#ca8c6e" stroke="#ca8c6e"/>
<polygon points="929.9,394.3 924.3,366.6 948.9,384" fill="#fadb9b" stroke="#fadb9b"/>
<polygon points="924.3,366.6 966,350.7 948.9,384" fill="#f5d798" stroke="#f5d798"/>
<polygon points="966,350.7 924.3,366.6 928.3,345.4" fill="#c3886d" stroke="#c3886d"/>
<polygon points="1019.5,413.1 1004.3,426.3 994.4,421.7" fill="#684c8b" stroke="#684c8b"/>
<polygon points="1004.3,426.3 1019.5,413.1 1012.8,428.4" fill="#5c447b" stroke="#5c447b"/>
<polygon points="994.4,421.7 1004.3,426.3 992.5,424" fill="#4b3f7f" stroke="#4b3f7f"/>
<polygon points="992.5,424 1004.3,426.3 1006,476.9" fill="#514488" stroke="#514488"/>
<polygon points="1004.3,426.3 1012.8,428.4 1006,476.9" fill="#493d7b" stroke="#493d7b"/>
</g>
</svg>
examples/outputs/logo-art-deco-hotel.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">The Meridian Hotel</title>
<desc id="d">Art Deco luxury hotel logo: gold sunburst, stepped tower, chamfered frame, spaced serif lettering on midnight navy.</desc>
<defs>
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#f6e2a0"/>
<stop offset="0.45" stop-color="#d7a94a"/>
<stop offset="0.7" stop-color="#f1d78c"/>
<stop offset="1" stop-color="#a87422"/>
</linearGradient>
<path id="ray" d="M512 520L503 220H521Z"/>
</defs>
<rect id="ground" width="1024" height="1024" fill="#0c1a2b"/>
<g id="frame" fill="none" stroke="url(#gold)">
<path d="M110 70H914L954 110V914L914 954H110L70 914V110Z" stroke-width="7"/>
<path d="M126 92H898L932 126V898L898 932H126L92 898V126Z" stroke-width="2.5"/>
<path d="M70 190H92M932 190H954M70 834H92M932 834H954" stroke-width="5"/>
</g>
<g id="sunburst" fill="url(#gold)" opacity="0.92">
<use href="#ray" transform="rotate(-80 512 520)"/>
<use href="#ray" transform="rotate(-70 512 520)"/>
<use href="#ray" transform="rotate(-60 512 520)"/>
<use href="#ray" transform="rotate(-50 512 520)"/>
<use href="#ray" transform="rotate(-40 512 520)"/>
<use href="#ray" transform="rotate(-30 512 520)"/>
<use href="#ray" transform="rotate(-20 512 520)"/>
<use href="#ray" transform="rotate(-10 512 520)"/>
<use href="#ray" transform="rotate(0 512 520)"/>
<use href="#ray" transform="rotate(10 512 520)"/>
<use href="#ray" transform="rotate(20 512 520)"/>
<use href="#ray" transform="rotate(30 512 520)"/>
<use href="#ray" transform="rotate(40 512 520)"/>
<use href="#ray" transform="rotate(50 512 520)"/>
<use href="#ray" transform="rotate(60 512 520)"/>
<use href="#ray" transform="rotate(70 512 520)"/>
<use href="#ray" transform="rotate(80 512 520)"/>
</g>
<g id="halo" fill="none" stroke="url(#gold)" stroke-width="4">
<path d="M400 510a112 112 0 0 1 224 0"/>
<path d="M364 510a148 148 0 0 1 296 0"/>
<path d="M328 510a184 184 0 0 1 368 0"/>
</g>
<g id="tower" stroke="url(#gold)" stroke-width="5">
<path d="M440 560V400H470V330H554V400H584V560Z" fill="#0c1a2b"/>
<path d="M486 330V270H538V330" fill="#0c1a2b"/>
<path d="M500 270L512 190 524 270Z" fill="url(#gold)"/>
<g fill="none" stroke-width="3">
<path d="M480 340V548"/>
<path d="M512 340V548"/>
<path d="M544 340V548"/>
</g>
</g>
<g id="plinth" fill="url(#gold)">
<rect x="400" y="560" width="224" height="16"/>
<rect x="360" y="588" width="304" height="8"/>
<rect x="330" y="608" width="364" height="4"/>
</g>
<g id="ornament" fill="url(#gold)">
<path fill="url(#gold)" d="M452 618l10 10-10 10-10-10z"/>
<path fill="url(#gold)" d="M512 618l10 10-10 10-10-10z"/>
<path fill="url(#gold)" d="M572 618l10 10-10 10-10-10z"/>
</g>
<g id="wordmark" fill="url(#gold)">
<g aria-label="THE MERIDIAN">
<path d="M203.3 726Q201.9 726 201.9 724.9 201.9 723.9 203.1 723.8L207.5 723.1Q209.4 722.8 210 722.1 210.7 721.4 210.7 719.4V661.8Q210.7 660.4 210.1 659.9 209.6 659.3 207.7 659.3 204.2 659.3 200.7 662.4 197.3 665.4 196.2 672.2L195.7 675.4Q195.6 676.7 194.4 676.7 193 676.7 193.2 675L194 657.6Q194.2 655.4 195.7 655.4 196.4 655.4 197 655.8 197.6 656.1 199 656.5 200.4 656.9 203.4 656.9H224.8Q227.9 656.9 229.3 656.5 230.7 656.1 231.3 655.8 231.9 655.4 232.5 655.4 234 655.4 234.2 657.6L235.1 675Q235.3 676.7 233.9 676.7 232.7 676.7 232.5 675.4L232 672.2Q231.1 665.4 227.6 662.4 224.2 659.3 220.5 659.3 218.7 659.3 218.1 659.9 217.5 660.4 217.5 661.8V719.4Q217.5 721.4 218.3 722.1 219 722.8 220.8 723.1L225.1 723.8Q226.4 723.9 226.4 724.9 226.4 726 224.9 726Z"/>
<path d="M251.6 726Q250.1 726 250.1 724.9 250.1 724.1 251.4 723.8L253.6 723.4Q255.6 723 256.2 722.4 256.8 721.7 256.8 719.7V663.2Q256.8 661.2 256.2 660.5 255.6 659.9 253.6 659.5L251.4 659.1Q250.1 658.8 250.1 657.9 250.1 656.9 251.6 656.9H269.1Q270.6 656.9 270.6 658 270.6 658.9 269.5 659.1L266.9 659.5Q264.9 659.8 264.3 660.5 263.7 661.3 263.7 663.3V687.4Q263.7 688.8 265 688.8H284Q285.4 688.8 285.4 687.4V663.3Q285.4 661.3 284.7 660.5 284.1 659.8 282.1 659.5L279.5 659.1Q278.4 658.9 278.4 658 278.4 656.9 279.9 656.9H297.5Q298.9 656.9 298.9 657.9 298.9 658.8 297.6 659.1L295.4 659.5Q293.4 659.9 292.8 660.5 292.2 661.2 292.2 663.2V719.7Q292.2 721.7 292.8 722.4 293.4 723 295.4 723.4L297.6 723.8Q298.9 724.1 298.9 724.9 298.9 726 297.5 726H279.9Q278.4 726 278.4 724.8 278.4 724 279.5 723.8L282.1 723.4Q284.1 723.1 284.7 722.4 285.4 721.6 285.4 719.6V693Q285.4 691.6 284 691.6H265Q263.7 691.6 263.7 693V719.6Q263.7 721.6 264.3 722.4 264.9 723.1 266.9 723.4L269.5 723.8Q270.6 724 270.6 724.8 270.6 726 269.1 726Z"/>
<path d="M316 726Q314.6 726 314.6 724.9 314.6 724.1 315.8 723.8L318.1 723.4Q320.1 723 320.7 722.4 321.3 721.7 321.3 719.7V663.2Q321.3 661.2 320.7 660.5 320.1 659.9 318.1 659.5L315.8 659.1Q314.6 658.8 314.6 657.9 314.6 656.9 316 656.9H348.3Q350.5 656.9 350.7 659.1L351.5 675Q351.7 676.7 350.2 676.7 349.1 676.7 348.9 675.4L348.3 671.9Q347.2 665.3 344 662.3 340.7 659.3 334.9 659.3 331.1 659.3 329.6 660.4 328.1 661.5 328.1 664.3V687.4Q328.1 688.8 329.5 688.8H334.3Q338.5 688.8 339.5 684.2L340.4 679.4Q340.8 678 342.1 678.2 343.1 678.4 343.1 679.8V701.4Q343.1 702.9 342.1 703.1 340.8 703.2 340.4 701.8L339.5 697.1Q338.8 693.7 337.6 692.7 336.4 691.6 334.2 691.6H329.5Q328.1 691.6 328.1 693V717.1Q328.1 720.5 329.9 722.1 331.7 723.6 336.4 723.6 341.2 723.6 344.6 721.2 348 718.7 349.3 712L350.5 706.3Q350.8 705.1 351.9 705.1 353.3 705.1 353.1 706.7L352.1 723.8Q351.9 726 349.7 726Z"/>
<path d="M426.9 725Q426.5 725 426.1 724.8 425.6 724.5 425.4 723.5L410.9 664.7Q410.8 664 410.3 664 409.8 664.1 409.8 664.8L407.9 717.7Q407.8 720.4 408.8 721.8 409.7 723.2 412.1 723.6L413.4 723.8Q414.6 723.9 414.6 724.9 414.6 726 413.2 726H399.5Q398 726 398 724.9 398 723.9 399.3 723.8L400.5 723.6Q403 723.2 403.9 721.8 404.8 720.4 404.9 717.7L406.9 663.2Q406.9 661.2 406.3 660.5 405.6 659.9 403.4 659.5L401.2 659.1Q399.9 658.8 399.9 657.9 399.9 656.9 401.4 656.9H412.9Q415.9 656.9 416.5 659.8L428.2 707.7Q428.4 708.3 428.8 708.3 429.2 708.3 429.3 707.7L441.4 659.3Q442 656.9 444.5 656.9H455.8Q457.3 656.9 457.3 657.9 457.3 658.8 456 659.1L453.8 659.5Q451.8 659.9 451.1 660.5 450.4 661.2 450.5 663.2L452.5 719.7Q452.5 721.7 453.1 722.4 453.7 723 455.7 723.4L457.9 723.8Q459.2 724.1 459.2 724.9 459.2 726 457.7 726H440.4Q438.9 726 438.9 724.9 438.9 724.1 440.2 723.8L442.4 723.4Q444.4 723 445.1 722.4 445.7 721.7 445.6 719.7L444 664.8Q444 664.1 443.6 664 443.1 664 442.9 664.6L428.4 723.5Q428.2 724.5 427.7 724.8 427.3 725 426.9 725Z"/>
<path d="M475.8 726Q474.4 726 474.4 724.9 474.4 724.1 475.6 723.8L477.8 723.4Q479.9 723 480.5 722.4 481.1 721.7 481.1 719.7V663.2Q481.1 661.2 480.5 660.5 479.9 659.9 477.8 659.5L475.6 659.1Q474.4 658.8 474.4 657.9 474.4 656.9 475.8 656.9H508.1Q510.3 656.9 510.5 659.1L511.3 675Q511.4 676.7 510 676.7 508.9 676.7 508.7 675.4L508.1 671.9Q507 665.3 503.8 662.3 500.5 659.3 494.7 659.3 490.9 659.3 489.4 660.4 487.9 661.5 487.9 664.3V687.4Q487.9 688.8 489.3 688.8H494.1Q498.3 688.8 499.3 684.2L500.2 679.4Q500.6 678 501.8 678.2 502.9 678.4 502.9 679.8V701.4Q502.9 702.9 501.8 703.1 500.6 703.2 500.2 701.8L499.3 697.1Q498.6 693.7 497.4 692.7 496.2 691.6 494 691.6H489.3Q487.9 691.6 487.9 693V717.1Q487.9 720.5 489.7 722.1 491.5 723.6 496.2 723.6 501 723.6 504.4 721.2 507.8 718.7 509.1 712L510.3 706.3Q510.6 705.1 511.7 705.1 513.1 705.1 512.9 706.7L511.9 723.8Q511.7 726 509.5 726Z"/>
<path d="M571.3 726.9Q563 726.9 560.6 715.9L558.4 705.3Q557.4 700.1 556.1 697.4 554.8 694.7 552.5 693.7 550.3 692.8 546.3 692.8 545 692.8 544.2 693.5 543.5 694.2 543.5 695.3V719.3Q543.5 721.3 544.2 722 545 722.7 547.7 723.2L551.2 723.8Q552.3 724 552.3 724.8 552.3 726 550.9 726H531.4Q529.9 726 529.9 724.9 529.9 724.1 531.2 723.8L533.4 723.4Q535.4 723 536 722.4 536.6 721.7 536.6 719.7V663.2Q536.6 661.2 536 660.5 535.4 659.9 533.4 659.5L531.2 659.1Q529.9 658.8 529.9 657.9 529.9 656.9 531.4 656.9H550.9Q556.5 656.9 560.9 659.1 565.4 661.3 567.8 665.2 570.3 669.1 570.3 674.2 570.3 680.2 566.7 685.1 563 689.9 557.5 691.9 556.8 692.1 556.8 692.5 556.7 693 557.4 693.3 560.5 694.7 562.2 697.3 564 699.9 565 704.2L567.1 714Q568.1 719 569.6 721.1 571 723.2 573.2 723.2 574 723.2 574.6 723 575.2 722.7 576.2 722.1 577 721.6 577.6 721.8 578.3 722.1 578.3 722.8 578.3 724.5 576.2 725.7 574.1 726.9 571.3 726.9ZM549.9 690.4Q556.4 690.4 559.8 686 563.1 681.6 563.1 674.4 563.1 667.2 559.3 663.3 555.4 659.3 548.6 659.3 543.5 659.3 543.5 663.1V685.1Q543.5 690.4 549.9 690.4Z"/>
<path d="M592.7 726Q591.2 726 591.2 724.9 591.2 724.1 592.5 723.8L594.7 723.4Q596.7 723 597.3 722.4 597.9 721.7 597.9 719.7V663.2Q597.9 661.2 597.3 660.5 596.7 659.9 594.7 659.5L592.5 659.1Q591.2 658.8 591.2 657.9 591.2 656.9 592.7 656.9H610Q611.5 656.9 611.5 657.9 611.5 658.8 610.2 659.1L608 659.5Q606 659.9 605.4 660.5 604.8 661.2 604.8 663.2V719.7Q604.8 721.7 605.4 722.4 606 723 608 723.4L610.2 723.8Q611.5 724.1 611.5 724.9 611.5 726 610 726Z"/>
<path d="M628.6 726Q627.2 726 627.2 724.9 627.2 724.1 628.4 723.8L630.6 723.4Q633.9 722.8 633.9 719.7V663.2Q633.9 661.2 633.3 660.5 632.7 659.9 630.6 659.5L628.4 659.1Q627.2 658.8 627.2 657.9 627.2 656.9 628.6 656.9H648Q655.5 656.9 661 661.2 666.4 665.6 669.4 673.5 672.4 681.4 672.4 691.7 672.4 702.2 669.3 709.9 666.2 717.6 660.5 721.8 654.8 726 647.1 726ZM647.4 723.5Q656 723.5 660.4 715.3 664.9 707.2 664.9 691.7 664.9 676.4 660.6 667.9 656.4 659.4 648 659.4 640.7 659.4 640.7 666.4V716.5Q640.7 720.3 642.3 721.9 643.9 723.5 647.4 723.5Z"/>
<path d="M691.5 726Q690.1 726 690.1 724.9 690.1 724.1 691.4 723.8L693.6 723.4Q695.6 723 696.2 722.4 696.8 721.7 696.8 719.7V663.2Q696.8 661.2 696.2 660.5 695.6 659.9 693.6 659.5L691.4 659.1Q690.1 658.8 690.1 657.9 690.1 656.9 691.5 656.9H708.9Q710.4 656.9 710.4 657.9 710.4 658.8 709.1 659.1L706.9 659.5Q704.9 659.9 704.3 660.5 703.6 661.2 703.6 663.2V719.7Q703.6 721.7 704.3 722.4 704.9 723 706.9 723.4L709.1 723.8Q710.4 724.1 710.4 724.9 710.4 726 708.9 726Z"/>
<path d="M724 726Q722.5 726 722.5 725 722.5 724.1 723.9 723.8L725.3 723.5Q727.2 723.1 728.2 721.6 729.1 720.1 729.8 717.5L744.7 657.5Q744.9 656.5 745.3 656.2 745.7 655.9 746.1 655.9 746.6 655.9 747 656.2 747.5 656.5 747.7 657.5L762.8 718.3Q763.5 721 764.4 722.1 765.2 723.1 767.1 723.5L768.5 723.8Q769.8 724.1 769.8 725 769.8 726 768.4 726H752Q750.6 726 750.6 724.9 750.6 723.9 752.3 723.8L754.1 723.6Q755.7 723.4 756.2 722.5 756.6 721.7 756.1 719.6L752.5 704.7Q751.9 702.2 749.3 702.2H739.2Q736.7 702.2 736 704.7L732.8 717.6Q731.3 723 735.7 723.6L737.1 723.8Q738.7 724.1 738.7 724.9 738.7 726 737.4 726ZM738.9 699.3H749.5Q751 699.3 750.7 697.7L744.6 672H744.1L737.7 697.7Q737.4 699.3 738.9 699.3Z"/>
<path d="M821.3 726.9Q819.8 726.9 818.8 724.6L792.7 668.1Q792.4 667.4 792 667.5 791.6 667.6 791.6 668.4V717.7Q791.6 720.4 792.5 721.8 793.4 723.2 795.8 723.6L797.1 723.8Q798.3 723.9 798.3 724.9 798.3 726 796.9 726H783.1Q781.7 726 781.7 724.9 781.7 723.9 782.9 723.8L784.2 723.6Q786.7 723.2 787.6 721.8 788.6 720.4 788.6 717.7V663.2Q788.6 661.2 788 660.5 787.4 659.9 785.2 659.5L782.9 659.1Q781.7 658.8 781.7 657.9 781.7 656.9 783.1 656.9H793.4Q795.8 656.9 796.8 659L819.4 708.8Q819.7 709.4 820.1 709.3 820.6 709.2 820.6 708.6V665.1Q820.6 662.1 819.5 660.9 818.5 659.7 816.8 659.4L815.1 659.1Q813.9 658.8 813.9 657.9 813.9 656.9 815.3 656.9H829Q830.5 656.9 830.5 657.9 830.5 659 829.2 659.1L828 659.3Q825.5 659.7 824.5 660.9 823.6 662.1 823.6 665.1V723.8Q823.6 725.5 822.9 726.2 822.3 726.9 821.3 726.9Z"/>
</g>
<g aria-label="HOTEL & RESIDENCES">
<path d="M293.9 790V771.8H296.7V779.6H304.4V771.8H307.2V790H304.4V782.1H296.7V790Z"/>
<path d="M328.6 790.4Q325.2 790.4 323.2 788.5 321.3 786.6 321.3 783V778.8Q321.3 775.2 323.2 773.3 325.2 771.4 328.6 771.4 331.9 771.4 333.9 773.3 335.9 775.2 335.9 778.8V783Q335.9 786.6 333.9 788.5 331.9 790.4 328.6 790.4ZM328.6 787.8Q330.7 787.8 331.9 786.6 333.1 785.3 333.1 783.1V778.7Q333.1 776.5 331.9 775.2 330.7 774 328.6 774 326.5 774 325.3 775.2 324.1 776.5 324.1 778.7V783.1Q324.1 785.3 325.3 786.6 326.5 787.8 328.6 787.8Z"/>
<path d="M354.3 790V774.3H348.7V771.8H362.7V774.3H357.1V790Z"/>
<path d="M376 790V771.8H387.5V774.3H378.8V779.6H386.8V782.1H378.8V787.5H387.6V790Z"/>
<path d="M401.3 790V771.8H404.1V787.5H412.9V790Z"/>
<path d="M448 790Q446.5 790 445.3 789.4 444.1 788.9 443.4 787.8 442.7 786.7 442.7 785.1 442.7 784 443.1 783.2 443.5 782.3 444.2 781.8 444.9 781.2 445.8 781V780.6Q444.5 780.2 443.7 779.3 442.8 778.3 442.8 776.7 442.8 775.1 443.5 774 444.2 772.9 445.4 772.4 446.6 771.8 448.1 771.8H451.2V774.1H448.6Q447.3 774.1 446.4 774.8 445.5 775.4 445.5 776.9 445.5 777.9 445.9 778.5 446.4 779.1 447.1 779.4 447.7 779.7 448.6 779.7H451.6V777.1H454.3V779.7H456.8V781.9H454.3V790ZM448.5 787.7H451.6V781.9H448.5Q447.2 781.9 446.3 782.7 445.4 783.4 445.4 784.9 445.4 786.3 446.3 787 447.2 787.7 448.5 787.7Z"/>
<path d="M487.4 790V771.8H495Q496.7 771.8 498 772.4 499.3 773 500 774.1 500.7 775.2 500.7 776.7V776.9Q500.7 778.7 499.9 779.7 499.1 780.7 497.9 781.1V781.5Q498.9 781.6 499.5 782.2 500.1 782.8 500.1 784V790H497.2V784.4Q497.2 783.7 496.9 783.2 496.5 782.8 495.7 782.8H490.2V790ZM490.2 780.3H494.7Q496.2 780.3 497 779.5 497.9 778.7 497.9 777.4V777.2Q497.9 775.9 497 775.1 496.2 774.3 494.7 774.3H490.2Z"/>
<path d="M514.5 790V771.8H526V774.3H517.4V779.6H525.3V782.1H517.4V787.5H526.1V790Z"/>
<path d="M546 790.4Q544 790.4 542.4 789.6 540.8 788.9 539.9 787.5 539 786.1 539 784V783.4H541.8V784Q541.8 786 543 786.9 544.1 787.9 546 787.9 548 787.9 549 787.1 550 786.3 550 785 550 784.1 549.5 783.6 549 783 548.2 782.7 547.3 782.4 546.1 782.1L545.2 781.8Q543.4 781.4 542.1 780.8 540.8 780.2 540.1 779.2 539.4 778.2 539.4 776.6 539.4 775 540.2 773.8 541 772.7 542.4 772.1 543.8 771.4 545.7 771.4 547.6 771.4 549.1 772.1 550.6 772.7 551.5 774 552.3 775.3 552.3 777.2V778.1H549.5V777.2Q549.5 776 549.1 775.3 548.6 774.6 547.7 774.2 546.9 773.9 545.7 773.9 544.1 773.9 543.1 774.6 542.2 775.3 542.2 776.5 542.2 777.3 542.6 777.9 543 778.4 543.8 778.7 544.6 779.1 545.8 779.3L546.7 779.6Q548.5 780 549.8 780.6 551.2 781.2 552 782.2 552.7 783.3 552.7 784.9 552.7 786.5 551.9 787.8 551.1 789 549.6 789.7 548.1 790.4 546 790.4Z"/>
<path d="M566.4 790V771.8H569.3V790Z"/>
<path d="M583.1 790V787.5H585.6V774.3H583.1V771.8H590.4Q594.1 771.8 596 773.6 597.9 775.5 597.9 779.2V782.6Q597.9 786.3 596 788.2 594.1 790 590.4 790ZM588.4 787.5H590.4Q592.8 787.5 594 786.3 595.1 785.1 595.1 782.7V779.1Q595.1 776.7 594 775.5 592.8 774.4 590.4 774.4H588.4Z"/>
<path d="M611.9 790V771.8H623.3V774.3H614.7V779.6H622.6V782.1H614.7V787.5H623.5V790Z"/>
<path d="M637.2 790V771.8H642.6L647.5 788.1H647.9V771.8H650.7V790H645.3L640.4 773.6H640V790Z"/>
<path d="M672 790.4Q668.7 790.4 666.8 788.5 664.8 786.6 664.8 783V778.8Q664.8 775.2 666.8 773.3 668.7 771.4 672 771.4 675.3 771.4 677.1 773.3 678.9 775.1 678.9 778.2V778.4H676.1V778.1Q676.1 776.3 675.1 775.1 674.1 774 672 774 670 774 668.8 775.2 667.6 776.5 667.6 778.7V783.1Q667.6 785.3 668.8 786.6 670 787.8 672 787.8 674.1 787.8 675.1 786.7 676.1 785.5 676.1 783.7V783.2H678.9V783.6Q678.9 786.7 677.1 788.5 675.3 790.4 672 790.4Z"/>
<path d="M692.8 790V771.8H704.2V774.3H695.6V779.6H703.5V782.1H695.6V787.5H704.4V790Z"/>
<path d="M724.3 790.4Q722.2 790.4 720.6 789.6 719 788.9 718.1 787.5 717.2 786.1 717.2 784V783.4H720V784Q720 786 721.2 786.9 722.3 787.9 724.3 787.9 726.2 787.9 727.2 787.1 728.2 786.3 728.2 785 728.2 784.1 727.7 783.6 727.2 783 726.4 782.7 725.5 782.4 724.3 782.1L723.4 781.8Q721.6 781.4 720.3 780.8 719.1 780.2 718.4 779.2 717.6 778.2 717.6 776.6 717.6 775 718.4 773.8 719.2 772.7 720.6 772.1 722 771.4 723.9 771.4 725.8 771.4 727.3 772.1 728.8 772.7 729.7 774 730.5 775.3 730.5 777.2V778.1H727.8V777.2Q727.8 776 727.3 775.3 726.8 774.6 725.9 774.2 725.1 773.9 723.9 773.9 722.3 773.9 721.4 774.6 720.4 775.3 720.4 776.5 720.4 777.3 720.8 777.9 721.2 778.4 722 778.7 722.8 779.1 724 779.3L724.9 779.6Q726.7 780 728 780.6 729.4 781.2 730.2 782.2 731 783.3 731 784.9 731 786.5 730.1 787.8 729.3 789 727.8 789.7 726.3 790.4 724.3 790.4Z"/>
</g>
<g aria-label="EST · 1928">
<path d="M418.5 868V854H427.3V856H420.6V860H426.7V861.9H420.6V866H427.4V868Z"/>
<path d="M445 868.3Q443.4 868.3 442.2 867.7 441 867.2 440.3 866.1 439.6 865 439.6 863.4V862.9H441.7V863.4Q441.7 864.9 442.6 865.6 443.5 866.4 445 866.4 446.5 866.4 447.3 865.8 448 865.1 448 864.1 448 863.5 447.7 863 447.3 862.6 446.6 862.4 446 862.1 445.1 861.9L444.3 861.7Q443 861.4 442 860.9 441 860.5 440.5 859.7 439.9 858.9 439.9 857.7 439.9 856.4 440.5 855.6 441.1 854.7 442.2 854.2 443.3 853.7 444.8 853.7 446.2 853.7 447.4 854.2 448.5 854.7 449.2 855.7 449.8 856.7 449.8 858.2V858.9H447.7V858.2Q447.7 857.3 447.3 856.7 447 856.1 446.3 855.9 445.6 855.6 444.8 855.6 443.5 855.6 442.8 856.1 442.1 856.7 442.1 857.6 442.1 858.3 442.4 858.7 442.7 859.1 443.3 859.3 443.9 859.6 444.8 859.8L445.5 860Q446.9 860.3 447.9 860.8 449 861.2 449.6 862 450.2 862.8 450.2 864.1 450.2 865.3 449.5 866.3 448.9 867.2 447.7 867.8 446.6 868.3 445 868.3Z"/>
<path d="M466.4 868V856H462.1V854H472.8V856H468.5V868Z"/>
<path d="M501.7 863.9Q501 863.9 500.5 863.4 500 863 500 862.3 500 861.5 500.5 861.1 501 860.6 501.7 860.6 502.4 860.6 502.8 861.1 503.3 861.6 503.3 862.3 503.3 863 502.8 863.4 502.4 863.9 501.7 863.9Z"/>
<path d="M535.1 868V855.5H534.8L532.5 860.2H530.3L533.4 854H537.2V868Z"/>
<path d="M555.3 868.3Q553.8 868.3 552.6 867.8 551.5 867.2 550.9 866.2 550.3 865.3 550.3 864H552.5Q552.5 865 553.1 865.7 553.8 866.4 555.3 866.4 556.8 866.4 557.6 865.6 558.4 864.8 558.4 863.4V861.7H558.1Q557.8 862.2 557 862.7 556.2 863.1 554.8 863.1 553.6 863.1 552.5 862.6 551.5 862 550.9 861 550.2 860 550.2 858.6V858.4Q550.2 857 550.9 855.9 551.5 854.9 552.7 854.3 553.9 853.7 555.4 853.7 556.9 853.7 558.1 854.3 559.2 854.9 559.9 856 560.5 857 560.5 858.4V863.3Q560.5 864.9 559.9 866 559.2 867.1 558 867.7 556.9 868.3 555.3 868.3ZM555.4 861.4Q556.3 861.4 557 861 557.7 860.7 558 860 558.4 859.4 558.4 858.6V858.4Q558.4 857.1 557.6 856.4 556.8 855.6 555.4 855.6 554 855.6 553.2 856.4 552.3 857.1 552.3 858.4V858.6Q552.3 859.4 552.7 860 553.1 860.7 553.8 861 554.5 861.4 555.4 861.4Z"/>
<path d="M573.2 868V866.4Q573.2 865 573.7 864 574.2 863.1 575.1 862.5 576 861.9 577.4 861.5L578.6 861.1Q579.4 860.9 580 860.5 580.6 860.1 580.9 859.5 581.2 859 581.2 858.2V858.1Q581.2 856.9 580.4 856.3 579.6 855.6 578.3 855.6 577 855.6 576.1 856.3 575.3 857 575.3 858.4V858.8H573.2V858.5Q573.2 856.9 573.9 855.9 574.6 854.8 575.7 854.3 576.9 853.7 578.3 853.7 579.8 853.7 580.9 854.3 582 854.8 582.7 855.8 583.3 856.8 583.3 858.1V858.3Q583.3 859.6 582.8 860.5 582.2 861.4 581.3 862 580.3 862.6 579.1 863L577.9 863.3Q577 863.6 576.4 864 575.8 864.3 575.6 864.7 575.3 865.2 575.3 865.9V866.1H583.2V868Z"/>
<path d="M600.9 868.3Q599.3 868.3 598.1 867.8 596.9 867.3 596.2 866.4 595.5 865.5 595.5 864.3V864.1Q595.5 863.1 595.9 862.4 596.3 861.8 596.9 861.4 597.4 861 597.9 860.9V860.6Q597.4 860.4 596.9 860.1 596.4 859.7 596.1 859.1 595.7 858.5 595.7 857.6V857.4Q595.7 856.3 596.4 855.5 597 854.6 598.2 854.2 599.4 853.7 600.9 853.7 602.4 853.7 603.6 854.2 604.7 854.6 605.4 855.5 606 856.3 606 857.4V857.6Q606 858.5 605.7 859.1 605.4 859.7 604.9 860.1 604.4 860.4 603.9 860.6V860.9Q604.4 861 604.9 861.4 605.5 861.8 605.9 862.4 606.3 863.1 606.3 864.1V864.3Q606.3 865.5 605.6 866.4 604.9 867.3 603.7 867.8 602.5 868.3 600.9 868.3ZM600.9 866.4Q602.4 866.4 603.3 865.8 604.2 865.2 604.2 864.2V864Q604.2 862.9 603.3 862.3 602.5 861.7 600.9 861.7 599.3 861.7 598.5 862.3 597.6 862.9 597.6 864V864.2Q597.6 865.2 598.5 865.8 599.4 866.4 600.9 866.4ZM600.9 859.9Q602.3 859.9 603.1 859.3 603.9 858.8 603.9 857.8V857.6Q603.9 856.7 603.1 856.1 602.3 855.6 600.9 855.6 599.5 855.6 598.7 856.1 597.8 856.7 597.8 857.6V857.8Q597.8 858.8 598.6 859.3 599.5 859.9 600.9 859.9Z"/>
</g>
</g>
<g id="rules" stroke="url(#gold)" stroke-width="2">
<path d="M206 812H430M594 812H818"/>
<path d="M256 828H430M594 828H768" stroke-width="1"/>
</g>
</svg>
examples/outputs/poster-vintage-highlands.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 1120" role="img" aria-labelledby="t d">
<title id="t">Visit the Highlands</title>
<desc id="d">Vintage travel poster: sunburst over layered mountains and a lake, pines in the foreground, bold condensed title band, cream paper border.</desc>
<defs>
<clipPath id="art">
<rect x="50" y="50" width="700" height="800"/>
</clipPath>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f4b24a"/>
<stop offset="1" stop-color="#f7dfa6"/>
</linearGradient>
<path id="pine" d="M0-150L-18-95H-10L-30-45H-18L-44 0H-6V20H6V0H44L18-45H30L10-95H18Z"/>
<pattern id="grain" width="6" height="6" patternUnits="userSpaceOnUse">
<circle cx="1.5" cy="1.5" r="0.8" fill="#5b3a1e" opacity="0.22"/>
<circle cx="4.5" cy="4.5" r="0.6" fill="#5b3a1e" opacity="0.18"/>
</pattern>
</defs>
<rect id="paper" width="800" height="1120" fill="#f1e6cc"/>
<g id="scene" clip-path="url(#art)">
<rect x="50" y="50" width="700" height="800" fill="url(#sky)"/>
<g id="sunburst" fill="#f7dfa6" opacity="0.55">
<path d="M400 470L1296.6 391.6 1288.9 329.2Z"/>
<path d="M400 470L1260.7 206.9 1240.2 147.5Z"/>
<path d="M400 470L1187.2 33.7 1154.8-20.2Z"/>
<path d="M400 470L1079.2-120.5 1036.4-166.4Z"/>
<path d="M400 470L941.6-248.8 890.2-284.8Z"/>
<path d="M400 470L780.4-345.7 722.5-370.2Z"/>
<path d="M400 470L602.5-406.9 540.8-418.9Z"/>
<path d="M400 470L415.7-429.9 352.9-428.8Z"/>
<path d="M400 470L228.3-413.5 167.1-399.3Z"/>
<path d="M400 470L48.3-358.5-8.6-331.9Z"/>
<path d="M400 470L-116.2-267.2-166.4-229.4Z"/>
<path d="M400 470L-258.2-143.8-299.4-96.4Z"/>
<path d="M400 470L-371.5 6.5-401.9 61.4Z"/>
<path d="M400 470L-451 177-469.3 237.1Z"/>
<path d="M400 470L-493.3 360.3-498.8 422.9Z"/>
</g>
<circle cx="400" cy="470" r="112" fill="#d9482b"/>
<circle cx="400" cy="470" r="82" fill="#e9713a"/>
<g id="far-range">
<path d="M50 560L170 430 250 510 340 400 470 560Z" fill="#c9a89a"/>
<path d="M400 560L520 420 610 500 680 440 750 520V600H400Z" fill="#c9a89a"/>
</g>
<g id="mid-range">
<path d="M50 640L200 470 300 590 420 500 560 640Z" fill="#3f7f86"/>
<path d="M200 470L245 545 215 560 180 520Z" fill="#2f666d"/>
<path d="M420 500L470 570 440 590 400 540Z" fill="#2f666d"/>
<path d="M420 500L455 545 433 553Z" fill="#f1e6cc"/>
<path d="M200 470L228 507 207 512Z" fill="#f1e6cc"/>
<path d="M460 650L620 500 750 610V700H460Z" fill="#2d5f7a"/>
<path d="M620 500L665 570 640 585 600 540Z" fill="#234c63"/>
</g>
<g id="lake">
<rect x="50" y="660" width="700" height="190" fill="#1f4e6b"/>
<g fill="#d9482b" opacity="0.85">
<rect x="350" y="674" width="100" height="6"/>
<rect x="368" y="690" width="64" height="5"/>
<rect x="384" y="704" width="32" height="4"/>
</g>
<g fill="#6fa3a6" opacity="0.7">
<rect x="120" y="720" width="120" height="3"/>
<rect x="520" y="752" width="150" height="3"/>
<rect x="230" y="790" width="90" height="3"/>
</g>
</g>
<path id="shore" d="M50 780C160 745 260 770 330 800S520 830 750 770V850H50Z" fill="#2c5a3e"/>
<g id="pines" fill="#173b2c">
<use href="#pine" transform="translate(60 790) scale(1.5)"/>
<use href="#pine" transform="translate(130 815) scale(1.15)"/>
<use href="#pine" transform="translate(215 800) scale(1.3)"/>
<use href="#pine" transform="translate(620 800) scale(1.4)"/>
<use href="#pine" transform="translate(700 780) scale(1.7)"/>
<use href="#pine" transform="translate(760 822) scale(1.1)"/>
<use href="#pine" transform="translate(555 830) scale(0.9)"/>
</g>
<rect id="grain-overlay" x="50" y="50" width="700" height="800" fill="url(#grain)"/>
</g>
<rect id="frame" x="50" y="50" width="700" height="800" fill="none" stroke="#173b2c" stroke-width="8"/>
<g id="titleband">
<g aria-label="HIGHLANDS" fill="#d9482b">
<path d="M65.9 1050V940H87.8V979.3H98.1V940H120V1050H98.1V1000.1H87.8V1050Z"/>
<path d="M150.3 1050V940H171.5V1050Z"/>
<path d="M224.5 1051Q200.9 1051 200.9 1017.2V969.8Q200.9 939 227.9 939 239.1 939 245 943.5 250.9 947.9 253.2 956.5 255.4 965.1 255.4 977.4H233.9V967.6Q233.9 964.1 233 961.7 232 959.2 228.9 959.2 225.1 959.2 223.9 961.8 222.7 964.4 222.7 967.4V1020.8Q222.7 1025.2 223.8 1028 225 1030.8 228.4 1030.8 231.9 1030.8 233 1028 234.2 1025.2 234.2 1020.6V1004.9H228.3V985.9H255.2V1050H246.4L242.6 1040.8Q236.9 1051 224.5 1051Z"/>
<path d="M285.4 1050V940H307.3V979.3H317.7V940H339.5V1050H317.7V1000.1H307.3V1050Z"/>
<path d="M370.8 1050V940H392.8V1031.2H415.4V1050Z"/>
<path d="M440.2 1050L450.8 940H488.2L498.6 1050H477.8L476.2 1032.2H463L461.6 1050ZM464.5 1014.7H474.5L469.7 958.8H468.7Z"/>
<path d="M526.9 1050V940H549.5L559.6 992.7V940H580.9V1050H559.4L548.4 995V1050Z"/>
<path d="M612.2 1050V940H642.4Q654.3 940 660.3 946.6 666.3 953.2 666.3 965.9V1017.4Q666.3 1033 660.8 1041.5 655.3 1050 641.7 1050ZM634.3 1030.6H638.1Q644.2 1030.6 644.2 1024.7V967.9Q644.2 962.4 642.7 960.8 641.2 959.2 636.7 959.2H634.3Z"/>
<path d="M722.9 1051Q707.9 1051 701.2 1043.5 694.6 1036 694.6 1019.6V1008.9H716.3V1022.6Q716.3 1026.4 717.5 1028.6 718.6 1030.8 721.5 1030.8 724.5 1030.8 725.7 1029 726.8 1027.2 726.8 1023.2 726.8 1018.2 725.8 1014.8 724.8 1011.4 722.3 1008.3 719.9 1005.2 715.5 1001.1L705.6 991.7Q694.6 981.2 694.6 967.8 694.6 953.8 701.1 946.4 707.6 939 720 939 735.1 939 741.5 947.1 747.8 955.1 747.8 971.6H725.4V964Q725.4 961.8 724.2 960.5 722.9 959.2 720.7 959.2 718.1 959.2 716.8 960.7 715.6 962.2 715.6 964.5 715.6 966.8 716.9 969.5 718.1 972.2 721.8 975.7L734.5 987.9Q738.3 991.5 741.5 995.5 744.7 999.6 746.6 1004.9 748.6 1010.2 748.6 1017.9 748.6 1033.4 742.8 1042.2 737.1 1051 722.9 1051Z"/>
</g>
<g aria-label="HIGHLANDS" fill="#173b2c">
<path d="M59.9 1044V934H81.8V973.3H92.1V934H114V1044H92.1V994.1H81.8V1044Z"/>
<path d="M144.3 1044V934H165.5V1044Z"/>
<path d="M218.5 1045Q194.9 1045 194.9 1011.2V963.8Q194.9 933 221.9 933 233.1 933 239 937.5 244.9 941.9 247.2 950.5 249.4 959.1 249.4 971.4H227.9V961.6Q227.9 958.1 227 955.7 226 953.2 222.9 953.2 219.1 953.2 217.9 955.8 216.7 958.4 216.7 961.4V1014.8Q216.7 1019.2 217.8 1022 219 1024.8 222.4 1024.8 225.9 1024.8 227 1022 228.2 1019.2 228.2 1014.6V998.9H222.3V979.9H249.2V1044H240.4L236.6 1034.8Q230.9 1045 218.5 1045Z"/>
<path d="M279.4 1044V934H301.3V973.3H311.7V934H333.5V1044H311.7V994.1H301.3V1044Z"/>
<path d="M364.8 1044V934H386.8V1025.2H409.4V1044Z"/>
<path d="M434.2 1044L444.8 934H482.2L492.6 1044H471.8L470.2 1026.2H457L455.6 1044ZM458.5 1008.7H468.5L463.7 952.8H462.7Z"/>
<path d="M520.9 1044V934H543.5L553.6 986.7V934H574.9V1044H553.4L542.4 989V1044Z"/>
<path d="M606.2 1044V934H636.4Q648.3 934 654.3 940.6 660.3 947.2 660.3 959.9V1011.4Q660.3 1027 654.8 1035.5 649.3 1044 635.7 1044ZM628.3 1024.6H632.1Q638.2 1024.6 638.2 1018.7V961.9Q638.2 956.4 636.7 954.8 635.2 953.2 630.7 953.2H628.3Z"/>
<path d="M716.9 1045Q701.9 1045 695.2 1037.5 688.6 1030 688.6 1013.6V1002.9H710.3V1016.6Q710.3 1020.4 711.5 1022.6 712.6 1024.8 715.5 1024.8 718.5 1024.8 719.7 1023 720.8 1021.2 720.8 1017.2 720.8 1012.2 719.8 1008.8 718.8 1005.4 716.3 1002.3 713.9 999.2 709.5 995.1L699.6 985.7Q688.6 975.2 688.6 961.8 688.6 947.8 695.1 940.4 701.6 933 714 933 729.1 933 735.5 941.1 741.8 949.1 741.8 965.6H719.4V958Q719.4 955.8 718.2 954.5 716.9 953.2 714.7 953.2 712.1 953.2 710.8 954.7 709.6 956.2 709.6 958.5 709.6 960.8 710.9 963.5 712.1 966.2 715.8 969.7L728.5 981.9Q732.3 985.5 735.5 989.5 738.7 993.6 740.6 998.9 742.6 1004.2 742.6 1011.9 742.6 1027.4 736.8 1036.2 731.1 1045 716.9 1045Z"/>
</g>
<g aria-label="VISIT THE" fill="#173b2c">
<path d="M205.2 922L200.5 884.2H207.7L210.5 909.9 212.9 884.2H220.1L215.5 922Z"/>
<path d="M253.7 922V884.2H261V922Z"/>
<path d="M304.8 922.3Q299.6 922.3 297.3 919.8 295 917.2 295 911.6V907.9H302.5V912.6Q302.5 913.9 302.9 914.6 303.3 915.4 304.3 915.4 305.3 915.4 305.7 914.8 306.1 914.2 306.1 912.8 306.1 911.1 305.7 909.9 305.4 908.7 304.5 907.7 303.7 906.6 302.2 905.2L298.8 902Q295 898.4 295 893.7 295 888.9 297.2 886.4 299.5 883.8 303.7 883.8 308.9 883.8 311.1 886.6 313.3 889.4 313.3 895H305.6V892.4Q305.6 891.7 305.2 891.2 304.7 890.8 304 890.8 303.1 890.8 302.7 891.3 302.2 891.8 302.2 892.6 302.2 893.4 302.7 894.3 303.1 895.3 304.4 896.5L308.7 900.6Q310 901.9 311.1 903.3 312.2 904.7 312.9 906.5 313.6 908.3 313.6 911 313.6 916.3 311.6 919.3 309.6 922.3 304.8 922.3Z"/>
<path d="M347.5 922V884.2H354.8V922Z"/>
<path d="M392.8 922V891.4H388.3V884.2H404.8V891.4H400.3V922Z"/>
<path d="M484 922V891.4H479.5V884.2H496V891.4H491.5V922Z"/>
<path d="M529.9 922V884.2H537.4V897.7H541V884.2H548.5V922H541V904.9H537.4V922Z"/>
<path d="M583.6 922V884.2H598.7V891.5H591.3V898.9H598.4V906H591.3V914.6H599.2V922Z"/>
</g>
<g aria-label="RAIL & STEAMER · EVERY SUMMER" fill="#3f7f86">
<path d="M112.6 1088.3Q110.2 1088.3 109.5 1085.1L108.8 1082Q108.5 1080.4 108.2 1079.7 107.8 1078.9 107.1 1078.6 106.5 1078.3 105.3 1078.3 104.9 1078.3 104.7 1078.5 104.5 1078.7 104.5 1079V1086Q104.5 1086.6 104.7 1086.8 104.9 1087 105.7 1087.2L106.7 1087.4Q107.1 1087.4 107.1 1087.7 107.1 1088 106.6 1088H101Q100.5 1088 100.5 1087.7 100.5 1087.4 100.9 1087.4L101.5 1087.2Q102.1 1087.1 102.3 1086.9 102.5 1086.7 102.5 1086.2V1069.7Q102.5 1069.1 102.3 1068.9 102.1 1068.7 101.5 1068.6L100.9 1068.5Q100.5 1068.4 100.5 1068.1 100.5 1067.8 101 1067.8H106.6Q108.3 1067.8 109.6 1068.5 110.9 1069.1 111.6 1070.3 112.3 1071.4 112.3 1072.9 112.3 1074.6 111.3 1076.1 110.2 1077.5 108.6 1078.1 108.4 1078.1 108.4 1078.2 108.3 1078.4 108.5 1078.5 109.4 1078.9 110 1079.6 110.5 1080.4 110.8 1081.6L111.4 1084.5Q111.7 1086 112.1 1086.6 112.5 1087.2 113.2 1087.2 113.4 1087.2 113.6 1087.1 113.7 1087 114 1086.9 114.3 1086.7 114.4 1086.8 114.6 1086.9 114.6 1087.1 114.6 1087.6 114 1087.9 113.4 1088.3 112.6 1088.3ZM106.4 1077.6Q108.3 1077.6 109.2 1076.3 110.2 1075.1 110.2 1072.9 110.2 1070.9 109.1 1069.7 108 1068.5 106 1068.5 104.5 1068.5 104.5 1069.7V1076.1Q104.5 1077.6 106.4 1077.6Z"/>
<path d="M123.5 1088Q123.1 1088 123.1 1087.7 123.1 1087.4 123.5 1087.4L123.9 1087.3Q124.5 1087.2 124.8 1086.7 125.1 1086.3 125.2 1085.5L129.6 1068Q129.6 1067.7 129.8 1067.6 129.9 1067.6 130 1067.6 130.1 1067.6 130.3 1067.6 130.4 1067.7 130.5 1068L134.9 1085.8Q135.1 1086.5 135.3 1086.9 135.6 1087.2 136.1 1087.3L136.5 1087.4Q136.9 1087.4 136.9 1087.7 136.9 1088 136.5 1088H131.7Q131.3 1088 131.3 1087.7 131.3 1087.4 131.8 1087.4L132.3 1087.3Q132.8 1087.2 132.9 1087 133.1 1086.7 132.9 1086.1L131.9 1081.8Q131.7 1081.1 130.9 1081.1H128Q127.3 1081.1 127.1 1081.8L126.1 1085.5Q125.7 1087.1 127 1087.3L127.4 1087.4Q127.9 1087.4 127.9 1087.7 127.9 1088 127.5 1088ZM127.9 1080.2H131Q131.4 1080.2 131.4 1079.7L129.6 1072.2H129.4L127.5 1079.7Q127.5 1080.2 127.9 1080.2Z"/>
<path d="M146.6 1088Q146.2 1088 146.2 1087.7 146.2 1087.4 146.6 1087.4L147.2 1087.2Q147.8 1087.1 148 1086.9 148.2 1086.7 148.2 1086.2V1069.7Q148.2 1069.1 148 1068.9 147.8 1068.7 147.2 1068.6L146.6 1068.5Q146.2 1068.4 146.2 1068.1 146.2 1067.8 146.6 1067.8H151.7Q152.1 1067.8 152.1 1068.1 152.1 1068.4 151.8 1068.5L151.1 1068.6Q150.5 1068.7 150.3 1068.9 150.2 1069.1 150.2 1069.7V1086.2Q150.2 1086.7 150.3 1086.9 150.5 1087.1 151.1 1087.2L151.8 1087.4Q152.1 1087.4 152.1 1087.7 152.1 1088 151.7 1088Z"/>
<path d="M162.9 1088Q162.4 1088 162.4 1087.7 162.4 1087.4 162.8 1087.4L163.5 1087.2Q164 1087.1 164.2 1086.9 164.4 1086.7 164.4 1086.2V1069.7Q164.4 1069.1 164.2 1068.9 164 1068.7 163.5 1068.6L162.8 1068.5Q162.4 1068.4 162.4 1068.1 162.4 1067.8 162.9 1067.8H168Q168.4 1067.8 168.4 1068.2 168.4 1068.4 168.1 1068.5L167.3 1068.6Q166.8 1068.7 166.6 1068.9 166.4 1069.1 166.4 1069.7V1085.4Q166.4 1086.4 167 1086.9 167.5 1087.3 168.4 1087.3 169.1 1087.3 169.7 1087 170.4 1086.8 171 1086 171.5 1085.3 171.8 1083.9L172.2 1082Q172.3 1081.6 172.6 1081.6 173 1081.6 172.9 1082.1L172.6 1087.4Q172.6 1088 171.9 1088Z"/>
<path d="M202.5 1088.3Q201 1088.3 199.9 1087.6 198.8 1086.9 198.2 1085.7 197.7 1084.6 197.7 1083.3 197.7 1081.2 198.8 1079.5 199.9 1077.8 201.8 1076.6 202.1 1076.4 201.9 1076.1 201.1 1074.7 200.7 1073.7 200.2 1072.7 200.2 1071.6 200.2 1070.4 200.8 1069.5 201.3 1068.6 202.3 1068 203.3 1067.5 204.7 1067.5 205.8 1067.5 206.6 1068 207.5 1068.5 207.9 1069.3 208.3 1070.1 208.3 1070.9 208.3 1072.4 207.4 1073.6 206.5 1074.7 204.5 1075.8 204.2 1076 204.4 1076.3L207.9 1082Q208.1 1082.2 208.2 1082.2 208.4 1082.2 208.5 1082 208.8 1081.3 209.1 1080.7 209.4 1080 209.5 1079.5 209.8 1078.7 209.7 1078.4 209.6 1078 209.1 1077.9L208.1 1077.8Q207.7 1077.7 207.7 1077.4 207.7 1077.1 208.1 1077.1H213Q213.4 1077.1 213.4 1077.4 213.4 1077.8 213 1077.8L212.5 1077.8Q211.9 1077.9 211.3 1078.3 210.8 1078.7 210.3 1079.7 210.1 1080.4 209.7 1081.2 209.4 1082 209 1082.8 208.7 1083.3 209 1083.7L210.6 1086.3Q210.9 1086.8 211.1 1087 211.3 1087.2 211.7 1087.2L212.9 1087.4Q213.2 1087.4 213.2 1087.7 213.2 1088 212.8 1088H210Q209.4 1088 209.2 1087.6L207.9 1085.5Q207.6 1085.1 207.3 1085.5 206.2 1087 205 1087.6 203.8 1088.3 202.5 1088.3ZM204.1 1075.2Q205.4 1074.4 206 1073.3 206.6 1072.2 206.6 1071.1 206.6 1070.4 206.5 1069.7 206.3 1069.1 205.8 1068.7 205.4 1068.2 204.5 1068.2 203.4 1068.2 202.7 1069.1 202 1070 202 1071 202 1071.9 202.3 1072.8 202.7 1073.6 203.6 1075 203.7 1075.4 204.1 1075.2ZM202.9 1086.9Q203.9 1086.9 204.9 1086.5 205.8 1086 206.8 1084.8 207.1 1084.3 206.8 1083.9L202.7 1077.4Q202.4 1077 202.1 1077.2 200.8 1078.2 200.2 1079.6 199.5 1081 199.5 1082.7 199.5 1084.5 200.3 1085.7 201.2 1086.9 202.9 1086.9Z"/>
<path d="M241.4 1088.3Q240.9 1088.3 240.2 1088.1 239.5 1088 238.9 1087.9 238.3 1087.7 237.9 1087.5 237.7 1087.4 237.6 1087.3 237.5 1087.1 237.5 1086.8L237.4 1082.4Q237.4 1081.9 237.8 1081.9 238.1 1081.9 238.2 1082.4L238.4 1083.4Q239.4 1087.4 242 1087.4 243.4 1087.4 244.2 1086.5 245.1 1085.6 245.1 1083.9 245.1 1082.2 244.2 1080.8 243.3 1079.3 241.5 1077.9 239.4 1076.5 238.4 1075.1 237.5 1073.7 237.5 1072.1 237.5 1070.1 238.9 1068.8 240.4 1067.6 242.6 1067.6 243.5 1067.6 244.2 1067.8 245 1068 245.5 1068.3 245.7 1068.4 245.8 1068.6 245.9 1068.7 245.9 1068.9L246 1073.2Q246 1073.6 245.6 1073.6 245.4 1073.6 245.2 1073.2L245.1 1072.5Q244.4 1070.4 243.8 1069.4 243.1 1068.4 241.9 1068.4 240.7 1068.4 240 1069.2 239.3 1070 239.3 1071.4 239.3 1072.6 240 1073.7 240.8 1074.8 242.8 1076.4 244.9 1078.1 245.8 1079.6 246.8 1081.2 246.8 1083 246.8 1084.6 246.1 1085.8 245.4 1086.9 244.2 1087.6 243 1088.3 241.4 1088.3Z"/>
<path d="M260.2 1088Q259.8 1088 259.8 1087.7 259.8 1087.4 260.2 1087.4L261.4 1087.2Q262 1087.1 262.2 1086.9 262.4 1086.7 262.4 1086.1V1069.3Q262.4 1068.9 262.2 1068.7 262 1068.5 261.5 1068.5 260.5 1068.5 259.5 1069.4 258.5 1070.3 258.1 1072.3L258 1073.2Q258 1073.6 257.6 1073.6 257.2 1073.6 257.3 1073.1L257.5 1068.1Q257.6 1067.4 258 1067.4 258.2 1067.4 258.4 1067.5 258.5 1067.6 258.9 1067.7 259.4 1067.8 260.2 1067.8H266.5Q267.4 1067.8 267.8 1067.7 268.2 1067.6 268.4 1067.5 268.6 1067.4 268.7 1067.4 269.2 1067.4 269.2 1068.1L269.5 1073.1Q269.5 1073.6 269.1 1073.6 268.8 1073.6 268.7 1073.2L268.6 1072.3Q268.3 1070.3 267.3 1069.4 266.3 1068.5 265.2 1068.5 264.7 1068.5 264.5 1068.7 264.4 1068.9 264.4 1069.3V1086.1Q264.4 1086.7 264.6 1086.9 264.8 1087.1 265.3 1087.2L266.6 1087.4Q266.9 1087.4 266.9 1087.7 266.9 1088 266.5 1088Z"/>
<path d="M280 1088Q279.6 1088 279.6 1087.7 279.6 1087.4 280 1087.4L280.6 1087.2Q281.2 1087.1 281.4 1086.9 281.6 1086.7 281.6 1086.2V1069.7Q281.6 1069.1 281.4 1068.9 281.2 1068.7 280.6 1068.6L280 1068.5Q279.6 1068.4 279.6 1068.1 279.6 1067.8 280 1067.8H289.4Q290.1 1067.8 290.1 1068.5L290.4 1073.1Q290.4 1073.6 290 1073.6 289.7 1073.6 289.6 1073.2L289.4 1072.2Q289.1 1070.3 288.2 1069.4 287.2 1068.5 285.6 1068.5 284.4 1068.5 284 1068.9 283.6 1069.2 283.6 1070V1076.7Q283.6 1077.1 284 1077.1H285.4Q286.6 1077.1 286.9 1075.8L287.2 1074.4Q287.3 1074 287.6 1074.1 287.9 1074.1 287.9 1074.5V1080.8Q287.9 1081.3 287.6 1081.3 287.3 1081.4 287.2 1080.9L286.9 1079.6Q286.7 1078.6 286.3 1078.3 286 1078 285.3 1078H284Q283.6 1078 283.6 1078.4V1085.4Q283.6 1086.4 284.1 1086.9 284.6 1087.3 286 1087.3 287.4 1087.3 288.4 1086.6 289.4 1085.9 289.8 1083.9L290.1 1082.3Q290.2 1081.9 290.5 1081.9 290.9 1081.9 290.8 1082.4L290.6 1087.4Q290.5 1088 289.9 1088Z"/>
<path d="M300.9 1088Q300.5 1088 300.5 1087.7 300.5 1087.4 300.9 1087.4L301.3 1087.3Q301.9 1087.2 302.2 1086.7 302.5 1086.3 302.7 1085.5L307 1068Q307.1 1067.7 307.2 1067.6 307.3 1067.6 307.4 1067.6 307.6 1067.6 307.7 1067.6 307.8 1067.7 307.9 1068L312.3 1085.8Q312.5 1086.5 312.7 1086.9 313 1087.2 313.5 1087.3L313.9 1087.4Q314.3 1087.4 314.3 1087.7 314.3 1088 313.9 1088H309.1Q308.7 1088 308.7 1087.7 308.7 1087.4 309.2 1087.4L309.7 1087.3Q310.2 1087.2 310.3 1087 310.5 1086.7 310.3 1086.1L309.3 1081.8Q309.1 1081.1 308.3 1081.1H305.4Q304.7 1081.1 304.5 1081.8L303.5 1085.5Q303.1 1087.1 304.4 1087.3L304.8 1087.4Q305.3 1087.4 305.3 1087.7 305.3 1088 304.9 1088ZM305.3 1080.2H308.4Q308.8 1080.2 308.8 1079.7L307 1072.2H306.8L305 1079.7Q304.9 1080.2 305.3 1080.2Z"/>
<path d="M331.9 1087.7Q331.8 1087.7 331.7 1087.6 331.5 1087.6 331.5 1087.3L327.2 1070.1Q327.2 1069.9 327.1 1069.9 326.9 1069.9 326.9 1070.1L326.4 1085.6Q326.3 1086.4 326.6 1086.8 326.9 1087.2 327.6 1087.3L328 1087.4Q328.3 1087.4 328.3 1087.7 328.3 1088 327.9 1088H323.9Q323.5 1088 323.5 1087.7 323.5 1087.4 323.8 1087.4L324.2 1087.3Q324.9 1087.2 325.2 1086.8 325.5 1086.4 325.5 1085.6L326.1 1069.7Q326.1 1069.1 325.9 1068.9 325.7 1068.7 325 1068.6L324.4 1068.5Q324 1068.4 324 1068.1 324 1067.8 324.5 1067.8H327.8Q328.7 1067.8 328.9 1068.7L332.3 1082.7Q332.3 1082.8 332.5 1082.8 332.6 1082.8 332.6 1082.7L336.1 1068.5Q336.3 1067.8 337 1067.8H340.3Q340.8 1067.8 340.8 1068.1 340.8 1068.4 340.4 1068.5L339.7 1068.6Q339.2 1068.7 339 1068.9 338.8 1069.1 338.8 1069.7L339.4 1086.2Q339.4 1086.7 339.6 1086.9 339.7 1087.1 340.3 1087.2L341 1087.4Q341.3 1087.4 341.3 1087.7 341.3 1088 340.9 1088H335.8Q335.4 1088 335.4 1087.7 335.4 1087.4 335.8 1087.4L336.4 1087.2Q337 1087.1 337.2 1086.9 337.4 1086.7 337.4 1086.2L336.9 1070.1Q336.9 1069.9 336.8 1069.9 336.6 1069.9 336.6 1070.1L332.3 1087.3Q332.3 1087.6 332.1 1087.6 332 1087.7 331.9 1087.7Z"/>
<path d="M351.9 1088Q351.5 1088 351.5 1087.7 351.5 1087.4 351.9 1087.4L352.5 1087.2Q353.1 1087.1 353.3 1086.9 353.5 1086.7 353.5 1086.2V1069.7Q353.5 1069.1 353.3 1068.9 353.1 1068.7 352.5 1068.6L351.9 1068.5Q351.5 1068.4 351.5 1068.1 351.5 1067.8 351.9 1067.8H361.3Q362 1067.8 362 1068.5L362.3 1073.1Q362.3 1073.6 361.9 1073.6 361.6 1073.6 361.5 1073.2L361.3 1072.2Q361 1070.3 360.1 1069.4 359.1 1068.5 357.4 1068.5 356.3 1068.5 355.9 1068.9 355.5 1069.2 355.5 1070V1076.7Q355.5 1077.1 355.8 1077.1H357.2Q358.5 1077.1 358.8 1075.8L359 1074.4Q359.1 1074 359.5 1074.1 359.8 1074.1 359.8 1074.5V1080.8Q359.8 1081.3 359.5 1081.3 359.1 1081.4 359 1080.9L358.8 1079.6Q358.6 1078.6 358.2 1078.3 357.9 1078 357.2 1078H355.8Q355.5 1078 355.5 1078.4V1085.4Q355.5 1086.4 356 1086.9 356.5 1087.3 357.9 1087.3 359.3 1087.3 360.3 1086.6 361.2 1085.9 361.6 1083.9L362 1082.3Q362.1 1081.9 362.4 1081.9 362.8 1081.9 362.7 1082.4L362.5 1087.4Q362.4 1088 361.8 1088Z"/>
<path d="M385.5 1088.3Q383.1 1088.3 382.4 1085.1L381.8 1082Q381.5 1080.4 381.1 1079.7 380.7 1078.9 380 1078.6 379.4 1078.3 378.2 1078.3 377.8 1078.3 377.6 1078.5 377.4 1078.7 377.4 1079V1086Q377.4 1086.6 377.6 1086.8 377.8 1087 378.6 1087.2L379.7 1087.4Q380 1087.4 380 1087.7 380 1088 379.6 1088H373.9Q373.4 1088 373.4 1087.7 373.4 1087.4 373.8 1087.4L374.5 1087.2Q375 1087.1 375.2 1086.9 375.4 1086.7 375.4 1086.2V1069.7Q375.4 1069.1 375.2 1068.9 375 1068.7 374.5 1068.6L373.8 1068.5Q373.4 1068.4 373.4 1068.1 373.4 1067.8 373.9 1067.8H379.6Q381.2 1067.8 382.5 1068.5 383.8 1069.1 384.5 1070.3 385.2 1071.4 385.2 1072.9 385.2 1074.6 384.2 1076.1 383.1 1077.5 381.5 1078.1 381.3 1078.1 381.3 1078.2 381.3 1078.4 381.5 1078.5 382.4 1078.9 382.9 1079.6 383.4 1080.4 383.7 1081.6L384.3 1084.5Q384.6 1086 385 1086.6 385.4 1087.2 386.1 1087.2 386.3 1087.2 386.5 1087.1 386.7 1087 386.9 1086.9 387.2 1086.7 387.4 1086.8 387.6 1086.9 387.6 1087.1 387.6 1087.6 386.9 1087.9 386.3 1088.3 385.5 1088.3ZM379.3 1077.6Q381.2 1077.6 382.2 1076.3 383.1 1075.1 383.1 1072.9 383.1 1070.9 382 1069.7 380.9 1068.5 378.9 1068.5 377.4 1068.5 377.4 1069.7V1076.1Q377.4 1077.6 379.3 1077.6Z"/>
<path d="M412 1079.2Q411.4 1079.2 411 1078.7 410.6 1078.3 410.6 1077.7 410.6 1077 411 1076.6 411.4 1076.2 412 1076.2 412.6 1076.2 413.1 1076.6 413.5 1077 413.5 1077.7 413.5 1078.3 413.1 1078.7 412.6 1079.2 412 1079.2Z"/>
<path d="M437.7 1088Q437.3 1088 437.3 1087.7 437.3 1087.4 437.7 1087.4L438.3 1087.2Q438.9 1087.1 439.1 1086.9 439.3 1086.7 439.3 1086.2V1069.7Q439.3 1069.1 439.1 1068.9 438.9 1068.7 438.3 1068.6L437.7 1068.5Q437.3 1068.4 437.3 1068.1 437.3 1067.8 437.7 1067.8H447.1Q447.8 1067.8 447.8 1068.5L448.1 1073.1Q448.1 1073.6 447.7 1073.6 447.4 1073.6 447.3 1073.2L447.1 1072.2Q446.8 1070.3 445.9 1069.4 444.9 1068.5 443.3 1068.5 442.1 1068.5 441.7 1068.9 441.3 1069.2 441.3 1070V1076.7Q441.3 1077.1 441.7 1077.1H443.1Q444.3 1077.1 444.6 1075.8L444.9 1074.4Q445 1074 445.3 1074.1 445.6 1074.1 445.6 1074.5V1080.8Q445.6 1081.3 445.3 1081.3 445 1081.4 444.9 1080.9L444.6 1079.6Q444.4 1078.6 444 1078.3 443.7 1078 443 1078H441.7Q441.3 1078 441.3 1078.4V1085.4Q441.3 1086.4 441.8 1086.9 442.3 1087.3 443.7 1087.3 445.1 1087.3 446.1 1086.6 447.1 1085.9 447.5 1083.9L447.8 1082.3Q447.9 1081.9 448.2 1081.9 448.6 1081.9 448.5 1082.4L448.3 1087.4Q448.2 1088 447.6 1088Z"/>
<path d="M465.1 1088.3Q465 1088.3 464.9 1088.2 464.8 1088.1 464.7 1087.8L460.3 1070.1Q460.1 1069.3 459.8 1069 459.6 1068.7 459 1068.6L458.6 1068.5Q458.2 1068.4 458.2 1068.1 458.2 1067.8 458.6 1067.8H463.4Q463.8 1067.8 463.8 1068.1 463.8 1068.5 463.4 1068.5L462.8 1068.5Q462.3 1068.6 462.2 1068.8 462.1 1069.1 462.2 1069.7L465.5 1083.2Q465.5 1083.4 465.7 1083.4 465.8 1083.4 465.8 1083.2L469 1070.3Q469.4 1068.7 468.2 1068.5L467.8 1068.5Q467.3 1068.4 467.3 1068.1 467.3 1067.8 467.7 1067.8H471.6Q472 1067.8 472 1068.1 472 1068.4 471.6 1068.5L471.2 1068.6Q470.7 1068.7 470.4 1069.1 470.1 1069.5 469.9 1070.3L465.6 1087.8Q465.5 1088.1 465.4 1088.2 465.3 1088.3 465.1 1088.3Z"/>
<path d="M481.7 1088Q481.3 1088 481.3 1087.7 481.3 1087.4 481.7 1087.4L482.3 1087.2Q482.9 1087.1 483.1 1086.9 483.3 1086.7 483.3 1086.2V1069.7Q483.3 1069.1 483.1 1068.9 482.9 1068.7 482.3 1068.6L481.7 1068.5Q481.3 1068.4 481.3 1068.1 481.3 1067.8 481.7 1067.8H491.1Q491.8 1067.8 491.8 1068.5L492.1 1073.1Q492.1 1073.6 491.7 1073.6 491.4 1073.6 491.3 1073.2L491.1 1072.2Q490.8 1070.3 489.9 1069.4 488.9 1068.5 487.3 1068.5 486.1 1068.5 485.7 1068.9 485.3 1069.2 485.3 1070V1076.7Q485.3 1077.1 485.7 1077.1H487.1Q488.3 1077.1 488.6 1075.8L488.9 1074.4Q489 1074 489.3 1074.1 489.6 1074.1 489.6 1074.5V1080.8Q489.6 1081.3 489.3 1081.3 489 1081.4 488.9 1080.9L488.6 1079.6Q488.4 1078.6 488 1078.3 487.7 1078 487 1078H485.7Q485.3 1078 485.3 1078.4V1085.4Q485.3 1086.4 485.8 1086.9 486.3 1087.3 487.7 1087.3 489.1 1087.3 490.1 1086.6 491.1 1085.9 491.5 1083.9L491.8 1082.3Q491.9 1081.9 492.2 1081.9 492.6 1081.9 492.5 1082.4L492.3 1087.4Q492.2 1088 491.6 1088Z"/>
<path d="M515.3 1088.3Q512.9 1088.3 512.2 1085.1L511.6 1082Q511.3 1080.4 510.9 1079.7 510.5 1078.9 509.9 1078.6 509.2 1078.3 508.1 1078.3 507.7 1078.3 507.4 1078.5 507.2 1078.7 507.2 1079V1086Q507.2 1086.6 507.4 1086.8 507.7 1087 508.4 1087.2L509.5 1087.4Q509.8 1087.4 509.8 1087.7 509.8 1088 509.4 1088H503.7Q503.3 1088 503.3 1087.7 503.3 1087.4 503.6 1087.4L504.3 1087.2Q504.9 1087.1 505 1086.9 505.2 1086.7 505.2 1086.2V1069.7Q505.2 1069.1 505 1068.9 504.9 1068.7 504.3 1068.6L503.6 1068.5Q503.3 1068.4 503.3 1068.1 503.3 1067.8 503.7 1067.8H509.4Q511 1067.8 512.3 1068.5 513.6 1069.1 514.3 1070.3 515.1 1071.4 515.1 1072.9 515.1 1074.6 514 1076.1 512.9 1077.5 511.3 1078.1 511.1 1078.1 511.1 1078.2 511.1 1078.4 511.3 1078.5 512.2 1078.9 512.7 1079.6 513.2 1080.4 513.5 1081.6L514.1 1084.5Q514.4 1086 514.8 1086.6 515.3 1087.2 515.9 1087.2 516.1 1087.2 516.3 1087.1 516.5 1087 516.8 1086.9 517 1086.7 517.2 1086.8 517.4 1086.9 517.4 1087.1 517.4 1087.6 516.8 1087.9 516.1 1088.3 515.3 1088.3ZM509.1 1077.6Q511 1077.6 512 1076.3 513 1075.1 513 1072.9 513 1070.9 511.8 1069.7 510.7 1068.5 508.7 1068.5 507.2 1068.5 507.2 1069.7V1076.1Q507.2 1077.6 509.1 1077.6Z"/>
<path d="M529.9 1088Q529.5 1088 529.5 1087.7 529.5 1087.4 529.9 1087.4L531.1 1087.2Q531.7 1087.1 531.9 1086.9 532.1 1086.7 532.1 1086.1V1079.5Q532.1 1078.8 531.8 1078.3L527.7 1069.6Q527.5 1069.2 527.2 1068.9 527 1068.7 526.5 1068.5L526.2 1068.5Q525.9 1068.4 525.9 1068.1 525.9 1067.8 526.3 1067.8H531.1Q531.5 1067.8 531.5 1068.1 531.5 1068.5 531 1068.5L530.4 1068.5Q530 1068.6 529.8 1068.8 529.6 1069.1 529.9 1069.7L533.3 1077.1Q533.4 1077.2 533.6 1077.2 533.8 1077.2 533.9 1077.1L537.2 1070.3Q537.5 1069.6 537.4 1069.1 537.2 1068.7 536.5 1068.6L535.9 1068.5Q535.4 1068.4 535.4 1068.1 535.4 1067.8 535.8 1067.8H539.8Q540.2 1067.8 540.2 1068.1 540.2 1068.4 539.8 1068.5L539.5 1068.5Q539.2 1068.6 538.9 1069 538.6 1069.4 538.1 1070.3L534.4 1077.7Q534.2 1078.1 534.1 1078.4 534.1 1078.8 534.1 1079.2V1086.1Q534.1 1086.7 534.3 1086.9 534.5 1087.1 535 1087.2L536.3 1087.4Q536.6 1087.4 536.6 1087.7 536.6 1088 536.2 1088Z"/>
<path d="M568.2 1088.3Q567.6 1088.3 566.9 1088.1 566.2 1088 565.6 1087.9 565 1087.7 564.7 1087.5 564.5 1087.4 564.4 1087.3 564.3 1087.1 564.3 1086.8L564.1 1082.4Q564.1 1081.9 564.5 1081.9 564.8 1081.9 564.9 1082.4L565.2 1083.4Q566.1 1087.4 568.7 1087.4 570.1 1087.4 571 1086.5 571.8 1085.6 571.8 1083.9 571.8 1082.2 570.9 1080.8 570.1 1079.3 568.2 1077.9 566.1 1076.5 565.2 1075.1 564.2 1073.7 564.2 1072.1 564.2 1070.1 565.7 1068.8 567.1 1067.6 569.4 1067.6 570.2 1067.6 571 1067.8 571.8 1068 572.3 1068.3 572.4 1068.4 572.5 1068.6 572.6 1068.7 572.6 1068.9L572.7 1073.2Q572.7 1073.6 572.4 1073.6 572.1 1073.6 572 1073.2L571.8 1072.5Q571.2 1070.4 570.5 1069.4 569.9 1068.4 568.6 1068.4 567.4 1068.4 566.7 1069.2 566 1070 566 1071.4 566 1072.6 566.8 1073.7 567.6 1074.8 569.5 1076.4 571.6 1078.1 572.6 1079.6 573.6 1081.2 573.6 1083 573.6 1084.6 572.9 1085.8 572.2 1086.9 570.9 1087.6 569.7 1088.3 568.2 1088.3Z"/>
<path d="M591.4 1088.3Q589.8 1088.3 588.7 1087.5 587.5 1086.8 586.9 1085.6 586.2 1084.3 586.2 1082.6V1069.7Q586.2 1069.1 586.1 1068.9 585.9 1068.7 585.3 1068.6L584.6 1068.5Q584.3 1068.4 584.3 1068.1 584.3 1067.8 584.7 1067.8H589.8Q590.2 1067.8 590.2 1068.2 590.2 1068.4 589.9 1068.5L589.2 1068.6Q588.6 1068.7 588.4 1068.9 588.2 1069.1 588.2 1069.7V1082.7Q588.2 1084.8 589.2 1086 590.1 1087.1 591.8 1087.1 593.6 1087.1 594.5 1086 595.5 1084.8 595.5 1082.7V1070.3Q595.5 1068.8 594.5 1068.6L593.8 1068.5Q593.5 1068.4 593.5 1068.2 593.5 1067.8 593.9 1067.8H597.9Q598.3 1067.8 598.3 1068.1 598.3 1068.4 598 1068.5L597.3 1068.6Q596.4 1068.8 596.4 1070.2V1082.6Q596.4 1085.2 595 1086.7 593.6 1088.3 591.4 1088.3Z"/>
<path d="M616.7 1087.7Q616.6 1087.7 616.5 1087.6 616.4 1087.6 616.3 1087.3L612.1 1070.1Q612 1069.9 611.9 1069.9 611.8 1069.9 611.8 1070.1L611.2 1085.6Q611.2 1086.4 611.5 1086.8 611.7 1087.2 612.4 1087.3L612.8 1087.4Q613.2 1087.4 613.2 1087.7 613.2 1088 612.7 1088H608.7Q608.3 1088 608.3 1087.7 608.3 1087.4 608.7 1087.4L609 1087.3Q609.8 1087.2 610 1086.8 610.3 1086.4 610.3 1085.6L610.9 1069.7Q610.9 1069.1 610.7 1068.9 610.5 1068.7 609.9 1068.6L609.2 1068.5Q608.9 1068.4 608.9 1068.1 608.9 1067.8 609.3 1067.8H612.7Q613.5 1067.8 613.7 1068.7L617.1 1082.7Q617.2 1082.8 617.3 1082.8 617.4 1082.8 617.4 1082.7L621 1068.5Q621.1 1067.8 621.9 1067.8H625.2Q625.6 1067.8 625.6 1068.1 625.6 1068.4 625.2 1068.5L624.6 1068.6Q624 1068.7 623.8 1068.9 623.6 1069.1 623.6 1069.7L624.2 1086.2Q624.2 1086.7 624.4 1086.9 624.6 1087.1 625.1 1087.2L625.8 1087.4Q626.2 1087.4 626.2 1087.7 626.2 1088 625.7 1088H620.7Q620.2 1088 620.2 1087.7 620.2 1087.4 620.6 1087.4L621.3 1087.2Q621.8 1087.1 622 1086.9 622.2 1086.7 622.2 1086.2L621.7 1070.1Q621.7 1069.9 621.6 1069.9 621.5 1069.9 621.4 1070.1L617.2 1087.3Q617.1 1087.6 617 1087.6 616.9 1087.7 616.7 1087.7Z"/>
<path d="M644.6 1087.7Q644.5 1087.7 644.4 1087.6 644.2 1087.6 644.2 1087.3L640 1070.1Q639.9 1069.9 639.8 1069.9 639.6 1069.9 639.6 1070.1L639.1 1085.6Q639.1 1086.4 639.3 1086.8 639.6 1087.2 640.3 1087.3L640.7 1087.4Q641 1087.4 641 1087.7 641 1088 640.6 1088H636.6Q636.2 1088 636.2 1087.7 636.2 1087.4 636.6 1087.4L636.9 1087.3Q637.7 1087.2 637.9 1086.8 638.2 1086.4 638.2 1085.6L638.8 1069.7Q638.8 1069.1 638.6 1068.9 638.4 1068.7 637.8 1068.6L637.1 1068.5Q636.8 1068.4 636.8 1068.1 636.8 1067.8 637.2 1067.8H640.5Q641.4 1067.8 641.6 1068.7L645 1082.7Q645 1082.8 645.2 1082.8 645.3 1082.8 645.3 1082.7L648.9 1068.5Q649 1067.8 649.8 1067.8H653.1Q653.5 1067.8 653.5 1068.1 653.5 1068.4 653.1 1068.5L652.5 1068.6Q651.9 1068.7 651.7 1068.9 651.5 1069.1 651.5 1069.7L652.1 1086.2Q652.1 1086.7 652.3 1086.9 652.4 1087.1 653 1087.2L653.7 1087.4Q654 1087.4 654 1087.7 654 1088 653.6 1088H648.5Q648.1 1088 648.1 1087.7 648.1 1087.4 648.5 1087.4L649.1 1087.2Q649.7 1087.1 649.9 1086.9 650.1 1086.7 650.1 1086.2L649.6 1070.1Q649.6 1069.9 649.5 1069.9 649.4 1069.9 649.3 1070.1L645 1087.3Q645 1087.6 644.9 1087.6 644.7 1087.7 644.6 1087.7Z"/>
<path d="M664.6 1088Q664.2 1088 664.2 1087.7 664.2 1087.4 664.6 1087.4L665.2 1087.2Q665.8 1087.1 666 1086.9 666.2 1086.7 666.2 1086.2V1069.7Q666.2 1069.1 666 1068.9 665.8 1068.7 665.2 1068.6L664.6 1068.5Q664.2 1068.4 664.2 1068.1 664.2 1067.8 664.6 1067.8H674.1Q674.7 1067.8 674.8 1068.5L675 1073.1Q675 1073.6 674.6 1073.6 674.3 1073.6 674.2 1073.2L674.1 1072.2Q673.7 1070.3 672.8 1069.4 671.8 1068.5 670.2 1068.5 669 1068.5 668.6 1068.9 668.2 1069.2 668.2 1070V1076.7Q668.2 1077.1 668.6 1077.1H670Q671.2 1077.1 671.5 1075.8L671.8 1074.4Q671.9 1074 672.2 1074.1 672.5 1074.1 672.5 1074.5V1080.8Q672.5 1081.3 672.2 1081.3 671.9 1081.4 671.8 1080.9L671.5 1079.6Q671.3 1078.6 670.9 1078.3 670.6 1078 669.9 1078H668.6Q668.2 1078 668.2 1078.4V1085.4Q668.2 1086.4 668.7 1086.9 669.2 1087.3 670.6 1087.3 672 1087.3 673 1086.6 674 1085.9 674.4 1083.9L674.7 1082.3Q674.8 1081.9 675.1 1081.9 675.5 1081.9 675.5 1082.4L675.2 1087.4Q675.1 1088 674.5 1088Z"/>
<path d="M698.2 1088.3Q695.8 1088.3 695.1 1085.1L694.5 1082Q694.2 1080.4 693.8 1079.7 693.4 1078.9 692.8 1078.6 692.1 1078.3 691 1078.3 690.6 1078.3 690.3 1078.5 690.1 1078.7 690.1 1079V1086Q690.1 1086.6 690.3 1086.8 690.6 1087 691.3 1087.2L692.4 1087.4Q692.7 1087.4 692.7 1087.7 692.7 1088 692.3 1088H686.6Q686.2 1088 686.2 1087.7 686.2 1087.4 686.5 1087.4L687.2 1087.2Q687.8 1087.1 687.9 1086.9 688.1 1086.7 688.1 1086.2V1069.7Q688.1 1069.1 687.9 1068.9 687.8 1068.7 687.2 1068.6L686.5 1068.5Q686.2 1068.4 686.2 1068.1 686.2 1067.8 686.6 1067.8H692.3Q693.9 1067.8 695.2 1068.5 696.5 1069.1 697.2 1070.3 698 1071.4 698 1072.9 698 1074.6 696.9 1076.1 695.8 1077.5 694.2 1078.1 694 1078.1 694 1078.2 694 1078.4 694.2 1078.5 695.1 1078.9 695.6 1079.6 696.1 1080.4 696.4 1081.6L697 1084.5Q697.3 1086 697.7 1086.6 698.2 1087.2 698.8 1087.2 699 1087.2 699.2 1087.1 699.4 1087 699.7 1086.9 699.9 1086.7 700.1 1086.8 700.3 1086.9 700.3 1087.1 700.3 1087.6 699.7 1087.9 699 1088.3 698.2 1088.3ZM692 1077.6Q693.9 1077.6 694.9 1076.3 695.9 1075.1 695.9 1072.9 695.9 1070.9 694.7 1069.7 693.6 1068.5 691.6 1068.5 690.1 1068.5 690.1 1069.7V1076.1Q690.1 1077.6 692 1077.6Z"/>
</g>
</g>
<g id="rules" fill="#173b2c">
<rect x="50" y="868" width="700" height="4"/>
</g>
</svg>
examples/outputs/ship-pixel-art.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 34" shape-rendering="crispEdges">
<title>Pixel starship</title>
<rect id="background" width="40" height="34" fill="#141433"/>
<g id="sprite">
<path id="outline" fill="#0b0b1a" d="M18 4h1v1h-1zM21 4h1v1h-1zM17 5h1v2h-1zM22 5h1v2h-1zM16 7h1v2h-1zM23 7h1v2h-1zM15 9h1v2h-1zM24 9h1v2h-1zM14 11h1v3h-1zM25 11h1v3h-1zM9 14h1v1h-1zM13 14h1v2h-1zM26 14h1v2h-1zM30 14h1v1h-1zM8 15h1v2h-1zM10 15h1v5h-1zM29 15h1v5h-1zM31 15h1v2h-1zM12 16h1v2h-1zM27 16h1v2h-1zM7 17h1v2h-1zM32 17h1v2h-1zM11 18h1v1h-1zM28 18h1v1h-1zM6 19h1v2h-1zM33 19h1v2h-1zM5 21h1v2h-1zM34 21h1v2h-1zM4 23h1v2h-1zM35 23h1v2h-1zM19 25h2v1h-2zM7 26h2v1h-2zM13 26h2v1h-2zM17 26h2v1h-2zM21 26h2v1h-2zM25 26h2v1h-2zM31 26h2v1h-2zM6 27h1v1h-1zM9 27h1v3h-1zM12 27h1v2h-1zM15 27h1v2h-1zM17 27h1v2h-1zM22 27h1v2h-1zM24 27h1v2h-1zM27 27h1v2h-1zM30 27h1v3h-1zM33 27h1v1h-1zM4 28h2v1h-2zM34 28h2v1h-2zM11 29h1v1h-1zM16 29h1v1h-1zM23 29h1v1h-1zM28 29h1v1h-1z"/>
<path id="c-w" fill="#f2f2f2" d="M19 4h2v2h-2zM18 5h1v1h-1zM21 5h1v1h-1zM16 9h1v3h-1zM23 9h1v3h-1zM15 11h1v4h-1zM24 11h1v4h-1zM14 14h1v3h-1zM25 14h1v3h-1zM13 16h1v3h-1zM26 16h1v3h-1zM19 17h2v3h-2zM12 18h1v2h-1zM18 18h1v3h-1zM21 18h1v3h-1zM27 18h1v2h-1zM11 19h1v2h-1zM17 19h1v3h-1zM22 19h1v3h-1zM28 19h1v2h-1zM10 20h1v3h-1zM29 20h1v3h-1zM16 21h1v2h-1zM23 21h1v2h-1zM9 22h1v3h-1zM15 22h1v2h-1zM24 22h1v2h-1zM30 22h1v3h-1zM14 23h1v1h-1zM25 23h1v1h-1zM8 24h1v2h-1zM31 24h1v2h-1z"/>
<path id="c-l" fill="#9bd0ff" d="M18 6h4v2h-4zM17 7h1v4h-1zM22 7h1v4h-1zM19 8h2v3h-2z"/>
<path id="c-b" fill="#3a7bd5" d="M18 8h1v7h-1zM21 8h1v7h-1zM17 11h1v6h-1zM22 11h1v6h-1zM16 12h1v6h-1zM23 12h1v6h-1zM15 15h1v5h-1zM24 15h1v5h-1zM14 17h1v6h-1zM25 17h1v6h-1z"/>
<path id="c-d" fill="#1f3f8f" d="M19 11h2v6h-2zM18 15h1v3h-1zM21 15h1v3h-1zM17 17h1v2h-1zM22 17h1v2h-1zM16 18h1v3h-1zM23 18h1v3h-1zM15 20h1v2h-1zM24 20h1v2h-1z"/>
<path id="c-r" fill="#ff4d6d" d="M9 15h1v7h-1zM30 15h1v7h-1zM8 17h1v7h-1zM31 17h1v7h-1zM7 19h1v7h-1zM32 19h1v7h-1zM6 21h1v6h-1zM33 21h1v6h-1zM5 23h1v5h-1zM34 23h1v5h-1zM4 25h1v3h-1zM35 25h1v3h-1z"/>
<path id="c-g" fill="#6c757d" d="M13 19h1v2h-1zM26 19h1v2h-1zM12 20h1v2h-1zM19 20h2v4h-2zM27 20h1v2h-1zM11 21h1v5h-1zM18 21h1v3h-1zM21 21h1v3h-1zM28 21h1v5h-1zM17 22h1v3h-1zM22 22h1v3h-1zM10 23h1v4h-1zM16 23h1v2h-1zM23 23h1v2h-1zM29 23h1v4h-1zM14 24h2v2h-2zM24 24h2v2h-2zM9 25h1v2h-1zM30 25h1v2h-1z"/>
<path id="c-k" fill="#2b2d42" d="M13 21h1v5h-1zM26 21h1v5h-1zM12 22h1v5h-1zM27 22h1v5h-1zM18 24h4v1h-4zM16 25h3v1h-3zM21 25h3v1h-3zM11 26h1v1h-1zM15 26h2v1h-2zM23 26h2v1h-2zM28 26h1v1h-1z"/>
<path id="c-o" fill="#ffb703" d="M10 27h2v1h-2zM16 27h1v1h-1zM23 27h1v1h-1zM28 27h2v1h-2zM11 28h1v1h-1zM28 28h1v1h-1z"/>
<path id="c-y" fill="#ffe66d" d="M10 28h1v2h-1zM16 28h1v1h-1zM23 28h1v1h-1zM29 28h1v2h-1z"/>
</g>
</svg>
examples/src/fox-pixel.txt
; Pixel fox sprite (54x46 cells), derived from the flat fox base, then hand-fixed (eye, nose). Build:
; python3 scripts/pixel_grid.py examples/src/fox-pixel.txt -o examples/outputs/fox-pixel-art.svg --outline "#2b1a14" --pad 3 --bg "#cfe3d4" --title "Pixel fox"
@o=#e9712f
@d=#c9551f
@c=#fbf3e4
@b=#4a2a1f
@k=#2b1a14
......................................................
.........o............................................
.........o............................................
.........bo......o....................................
.........bb.....ob....................................
.........bbo...oob....................................
.........obb...obbo...................................
........ooobo.ooobo...................................
........ooooooooooo...................................
........ooooooooooo...................................
........ooooooooooo...................................
........oooooooooooo..................................
.......ooooooooooooo..................................
.......ooooooooooooo..................................
.......oooooooooooooo.................................
......ooooooooooooooo.................................
.....cccccooooooooooo.................................
...cccccckkoooooooooo.................................
..ccccccccccooooooooo.................................
kkcccccccccccooooooooo................................
.ccccccccccccccoooooooooo.....................c.......
...cccccccccccccccooooooooo...................cc......
......ccccccccccccccccooooooo................cccc.....
.........ccccccccccccooooooooo...............cccc.....
.............ccccccooooooooooooo..............cccc....
.............ccccoooooooooooooooo............dcccc....
.............cccccoooooooooooooooo...........ddcccc...
.............ccccccoooooooooooooooo..........dddccc...
.............ccccccoooooooooooooooo.........ddddccc...
.............cccccoooooooooooooooooo.......dddddccc...
.............cccccooooooooodooooooooo......ddddddcc...
.............cccccooooooooddddddooooo.....dddddddccc..
.............cccccooooooooddddddddoooo..dddddddddccc..
..............cccooooooooddddddddddoooddddddddddddc...
..............cccoooooooodddddddddddooddddddddddddc...
..............cccoooooooodddddddddddooodddddddddddc...
..............cccooooooooddddddddddddoodddddddddddc...
..............cbbbbooooooddddddddddddooddddddddddd....
..............bbbbboooboodddddddddddddoddddddddddd....
..............bbbbbobbboodddddddddddddoddddddddddd....
..............bbbbbobbboodddddddddddddodddddddddd.....
..............bbbbbobbboodddddddddddddoddddddddd......
.............bbbbbbbbbbooddddddddddddddddddddd........
.............bbbbbbbbbbooddddddddddddodddddd..........
............bbbbbbbbbbbooddddddddddd..................
......................................................
examples/src/make_fox_set.py
#!/usr/bin/env python3
"""Generates the multi-style fox example set (same scene spec, different visual languages).
Scene spec (frozen, shared by every variant)
subject : red fox, seated, side view facing left, big brush tail curling up on the right
canvas : 0 0 1024 1024
anchors : head centre ~(370, 400), muzzle tip (232, 486), ground line y=840,
subject bbox ~ x232..962, y222..844
Usage: python3 make_fox_set.py OUTDIR (writes fox.svg + fox-<style-id>.svg, except pixel art
which is built with scripts/pixel_grid.py from the ASCII in make_pixel_fox.py)
"""
import sys
from pathlib import Path
# ---- shared geometry (the "scene spec" as path data) ------------------------------------
TAIL = "M700 800 C770 846 880 838 924 752 C962 676 940 560 872 486 C866 566 842 628 790 664 C758 686 730 694 700 694 Z"
TAIL_TIP = "M872 486 C940 560 962 676 926 742 C922 680 900 600 850 540 C858 522 866 504 872 486Z"
BODY = "M430 500 C520 470 610 500 690 580 C750 640 780 720 760 800 C752 828 736 840 700 840 L470 840 C450 840 446 822 452 806 L436 700 C422 640 404 590 410 540 Z"
HAUNCH = "M600 640 C680 640 750 700 752 780 C752 816 736 840 700 840 L580 840 C560 800 556 700 600 640Z"
CHEST = "M410 545 C440 562 472 570 490 600 C470 650 458 700 458 770 L426 770 C412 700 396 610 410 545Z"
LEG = "M424 740 L456 730 L482 730 L490 822 C494 836 486 844 472 844 L404 844 C388 844 388 826 404 820 L418 790Z"
LEG2 = "M500 760 L540 752 L548 826 C550 838 544 844 532 844 L484 844 C486 830 494 822 498 800Z"
HEAD = "M232 486 C262 462 292 438 318 420 L334 352 L352 222 L410 338 L468 240 L492 362 C510 392 514 430 512 462 L536 516 L488 528 L468 562 C430 552 380 552 340 540 C312 534 282 520 256 512 C242 504 234 496 232 486Z"
MUZZLE = "M232 486 C262 462 292 438 318 430 C340 440 372 470 420 492 C452 508 480 516 488 528 L468 562 C430 552 380 552 340 540 C312 534 282 520 256 512 C242 504 234 496 232 486Z"
EAR_L = "M352 300 L358 248 L392 322Z"
EAR_R = "M452 306 L468 262 L478 342Z"
EYE = "M318 452 Q338 436 362 448 Q340 462 318 452Z"
NOSE = ("236", "488", "10") # cx, cy, r
CHEEK = "M490 520 L536 516 L510 548 L468 562Z"
HDR = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">\n'
def nose(fill):
return '<circle cx="%s" cy="%s" r="%s" fill="%s"/>' % (NOSE + (fill,))
# ---- 1. flat base ------------------------------------------------------------------------
def base():
return HDR + """<title id="t">Red fox</title><desc id="d">Neutral flat illustration of a seated red fox; the layout reference for the style set.</desc>
<rect width="1024" height="1024" fill="#f4efe6"/>
<g id="ground"><ellipse cx="640" cy="846" rx="380" ry="16" fill="#e5dccb"/></g>
<g id="tail"><path d="%(TAIL)s" fill="#d9642b"/><path d="%(TAIL_TIP)s" fill="#fbf3e4"/></g>
<g id="body"><path d="%(BODY)s" fill="#e9712f"/><path d="%(HAUNCH)s" fill="#d9642b"/><path d="%(LEG2)s" fill="#4a2a1f"/><path d="%(CHEST)s" fill="#fbf3e4"/><path d="%(LEG)s" fill="#3a2019"/></g>
<g id="head"><path d="%(HEAD)s" fill="#e9712f"/><path d="%(CHEEK)s" fill="#fbf3e4"/><path d="%(MUZZLE)s" fill="#fbf3e4"/><path d="%(EAR_L)s" fill="#3a2019"/><path d="%(EAR_R)s" fill="#3a2019"/><path d="%(EYE)s" fill="#2b1a14"/>%(NOSE)s</g>
</svg>
""" % dict(globals(), NOSE=nose("#2b1a14"))
# ---- 2. ukiyo-e --------------------------------------------------------------------------
def ukiyo():
ink = "#1c1a1a"
hatch = "".join('<path d="M%d %dl%d %d"/>' % (x, y, dx, dy) for x, y, dx, dy in [
(800, 560, 34, -30), (830, 600, 36, -34), (790, 620, 40, -30), (830, 660, 40, -30), (800, 700, 38, -24),
(860, 720, 30, -30), (760, 740, 34, -20), (840, 770, 30, -22), (890, 690, 22, -30), (880, 610, 26, -30)])
fur = "".join('<path d="M%d %dl%d %d"/>' % (x, y, dx, dy) for x, y, dx, dy in [
(560, 560, 30, 10), (600, 590, 30, 12), (650, 640, 26, 18), (690, 700, 20, 22), (620, 700, 22, 20), (660, 770, 14, 20)])
grass = "".join('<path d="M%d 840q4 -30 %d -46M%d 840q2 -22 %d -30"/>' % (x, d, x + 10, d // 2) for x, d in [
(140, -6), (300, 8), (560, -8), (620, 8), (980, -6), (90, 6)])
return HDR + """<title id="t">Fox at Dusk</title><desc id="d">Ukiyo-e style: flat colour blocks, black key-block outlines, bokashi sky, kasumi mist, seigaiha waves, red cartouche.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#9fc0c4"/><stop offset="1" stop-color="#efe3c4"/></linearGradient>
<pattern id="seigaiha" width="80" height="40" patternUnits="userSpaceOnUse">
<rect width="80" height="40" fill="#2b4a6b"/>
<g fill="none" stroke="#e9dfc4" stroke-width="3">
<path id="wv" d="M0 40a40 40 0 0 1 80 0M14 40a26 26 0 0 1 52 0M28 40a12 12 0 0 1 24 0"/>
<use href="#wv" x="-40" y="-20"/><use href="#wv" x="40" y="-20"/>
</g></pattern>
</defs>
<rect id="paper" width="1024" height="1024" fill="#efe3c4"/>
<rect id="sky-band" width="1024" height="640" fill="url(#sky)"/>
<circle id="sun" cx="760" cy="290" r="116" fill="#c9412c"/>
<g id="kasumi" fill="#f5ecd2"><path d="M0 610C120 580 230 640 350 610S560 580 640 620 860 640 1024 600V690C900 720 780 670 660 700S420 730 300 690 90 700 0 680Z"/><path d="M420 470C520 450 600 490 700 470S860 450 1024 480V520C900 540 800 510 700 530S520 520 420 510Z" fill="#e6dab9"/></g>
<g id="cartouche"><rect x="70" y="70" width="86" height="270" fill="#c9412c" stroke="%(ink)s" stroke-width="5"/><rect x="82" y="82" width="62" height="246" fill="#f5ecd2"/>
<g fill="none" stroke="%(ink)s" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"><path d="M96 118h34M113 104v44M96 150q17 -14 34 0"/><path d="M96 200h34M96 232h34M113 190v70M98 260l30 26"/></g>
<rect x="94" y="300" width="26" height="20" fill="#c9412c"/></g>
<g id="water"><rect y="840" width="1024" height="184" fill="url(#seigaiha)"/><rect y="837" width="1024" height="6" fill="%(ink)s"/></g>
<g id="grass" fill="none" stroke="%(ink)s" stroke-width="5" stroke-linecap="round">%(grass)s</g>
<g id="fox" stroke="%(ink)s" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<g id="tail"><path d="%(TAIL)s" fill="#d8642c"/><path d="%(TAIL_TIP)s" fill="#f5ecd2"/><g fill="none" stroke-width="4">%(hatch)s</g></g>
<g id="body"><path d="%(BODY)s" fill="#e9712f"/><path d="%(HAUNCH)s" fill="#d8642c"/><path d="%(LEG2)s" fill="#3a2019"/><path d="%(CHEST)s" fill="#f5ecd2"/><path d="%(LEG)s" fill="#3a2019"/><g fill="none" stroke-width="4">%(fur)s</g></g>
<g id="head"><path d="%(HEAD)s" fill="#e9712f"/><path d="%(CHEEK)s" fill="#f5ecd2"/><path d="%(MUZZLE)s" fill="#f5ecd2"/><path d="%(EAR_L)s" fill="#3a2019"/><path d="%(EAR_R)s" fill="#3a2019"/><path d="%(EYE)s" fill="%(ink)s"/>%(NOSE)s</g>
</g>
</svg>
""" % dict(globals(), ink=ink, hatch=hatch, fur=fur, grass=grass, NOSE=nose(ink))
# ---- 3. cyberpunk neon -------------------------------------------------------------------
def cyber():
import random
rnd = random.Random(7)
bld, x = [], 0
while x < 1024:
w = rnd.choice([70, 90, 110, 60]); h = rnd.randint(140, 380)
w = min(w, 1024 - x)
bld.append((x, 840 - h, w, h)); x += w + rnd.choice([0, 6, 10])
towers = "".join('<rect x="%d" y="%d" width="%d" height="%d"/>' % b for b in bld)
wins = []
for (bx, by, bw, bh) in bld:
for wy in range(by + 18, 820, 34):
for wx in range(bx + 10, bx + bw - 12, 18):
if rnd.random() < 0.22:
wins.append('<rect x="%d" y="%d" width="8" height="12"/>' % (wx, wy))
wins = "".join(wins[:70])
grid_h = "".join('<path d="M0 %d H1024"/>' % y for y in (860, 885, 920, 965, 1024))
grid_v = "".join('<path d="M%d 850 L%d 1024"/>' % (512 + (i - 6) * 40, 512 + (i - 6) * 210) for i in range(13))
return HDR + """<title id="t">Neon Fox</title><desc id="d">Cyberpunk neon: dark base, glowing outline on the hero silhouette, magenta skyline haze, perspective grid floor, lit windows.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#07040f"/><stop offset=".6" stop-color="#1c0a3a"/><stop offset="1" stop-color="#7a1462"/></linearGradient>
<radialGradient id="moon" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#ff4fd8" stop-opacity=".9"/><stop offset="1" stop-color="#ff4fd8" stop-opacity="0"/></radialGradient>
<clipPath id="floor-clip"><rect y="840" width="1024" height="184"/></clipPath>
<filter id="glow" x="-20%%" y="-20%%" width="140%%" height="140%%" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="9" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<rect id="sky-bg" width="1024" height="1024" fill="url(#sky)"/>
<circle id="halo" cx="760" cy="330" r="240" fill="url(#moon)"/>
<g id="skyline" fill="#0d0820" opacity=".95">%(towers)s</g>
<g id="windows" fill="#25e6ff" opacity=".75">%(wins)s</g>
<g id="floor"><rect y="840" width="1024" height="184" fill="#0a0518"/>
<g fill="none" stroke="#ff2fb8" stroke-width="2" opacity=".75" clip-path="url(#floor-clip)">%(grid_h)s%(grid_v)s</g>
<path d="M0 840H1024" stroke="#25e6ff" stroke-width="4" opacity=".9"/></g>
<g id="signs" fill="none" stroke-width="5" stroke-linecap="round" filter="url(#glow)"><path d="M96 200v130h60" stroke="#ff2fb8"/><path d="M900 160h70M900 190h44" stroke="#25e6ff"/></g>
<g id="fox-fill" fill="#150a2b"><path d="%(TAIL)s"/><path d="%(BODY)s"/><path d="%(LEG2)s"/><path d="%(LEG)s"/><path d="%(HEAD)s"/></g>
<g id="fox-neon" fill="none" stroke-linejoin="round" stroke-linecap="round" filter="url(#glow)">
<path d="%(TAIL)s" stroke="#ff2fb8" stroke-width="6"/><path d="%(TAIL_TIP)s" stroke="#25e6ff" stroke-width="5"/>
<path d="%(BODY)s" stroke="#ff7a2f" stroke-width="6"/><path d="%(HAUNCH)s" stroke="#ff2fb8" stroke-width="3" opacity=".7"/>
<path d="%(HEAD)s" stroke="#ff7a2f" stroke-width="6"/><path d="%(MUZZLE)s" stroke="#25e6ff" stroke-width="4"/>
<path d="%(CHEST)s" stroke="#25e6ff" stroke-width="3" opacity=".8"/><path d="%(LEG)s" stroke="#ff7a2f" stroke-width="4"/>
<path d="%(EAR_L)s" stroke="#ff2fb8" stroke-width="4"/><path d="%(EAR_R)s" stroke="#ff2fb8" stroke-width="4"/></g>
<g id="visor"><path d="%(EYE)s" fill="#25e6ff" filter="url(#glow)"/><circle cx="236" cy="488" r="9" fill="#ff2fb8" filter="url(#glow)"/></g>
</svg>
""" % dict(globals(), towers=towers, wins=wins, grid_h=grid_h, grid_v=grid_v)
# ---- 4. paper cut-out --------------------------------------------------------------------
def paper():
return HDR + """<title id="t">Paper Fox</title><desc id="d">Paper cut-out: stacked flat sheets, no outlines, soft cast shadow under every layer, a little fibre texture.</desc>
<defs>
<filter id="sh" x="-10%%" y="-10%%" width="125%%" height="125%%"><feDropShadow dx="5" dy="9" stdDeviation="6" flood-color="#3b2a1a" flood-opacity=".38"/></filter>
<filter id="shs" x="-10%%" y="-10%%" width="125%%" height="125%%"><feDropShadow dx="3" dy="4" stdDeviation="3" flood-color="#3b2a1a" flood-opacity=".35"/></filter>
<linearGradient id="dusk" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f7d9a0"/><stop offset="1" stop-color="#f3efe2"/></linearGradient>
</defs>
<rect id="sheet" width="1024" height="1024" fill="url(#dusk)"/>
<g id="sun" filter="url(#shs)"><circle cx="780" cy="270" r="120" fill="#f4a259"/><circle cx="780" cy="270" r="78" fill="#f7c873"/></g>
<g id="clouds" fill="#fffaf0" filter="url(#shs)"><path d="M80 250h190a34 34 0 0 0 -22 -60a50 50 0 0 0 -92 -10a40 40 0 0 0 -76 70z"/><path d="M420 150h140a26 26 0 0 0 -16 -46a38 38 0 0 0 -70 -8a30 30 0 0 0 -54 54z"/></g>
<g id="hills"><path d="M0 610C160 540 300 580 440 640S760 700 1024 600V1024H0Z" fill="#7fb59a" filter="url(#sh)"/><path d="M0 730C180 690 330 720 500 770S820 800 1024 730V1024H0Z" fill="#4e8f7a" filter="url(#sh)"/><path d="M0 840C220 810 420 830 600 850S880 850 1024 830V1024H0Z" fill="#2f6b62" filter="url(#sh)"/></g>
<g id="trees" fill="#2f6b62" filter="url(#shs)"><path d="M90 640l46 -120l46 120z"/><path d="M104 590l32 -90l32 90z" fill="#3f8272"/><path d="M930 660l40 -104l40 104z"/></g>
<g id="fox" filter="url(#sh)">
<path d="%(TAIL)s" fill="#e0662c"/><path d="%(TAIL_TIP)s" fill="#fff4e0"/>
<path d="%(BODY)s" fill="#f07a35"/><path d="%(HAUNCH)s" fill="#e0662c" filter="url(#shs)"/>
<path d="%(LEG2)s" fill="#5a3324"/><path d="%(CHEST)s" fill="#fff4e0" filter="url(#shs)"/><path d="%(LEG)s" fill="#3a2019"/>
<path d="%(HEAD)s" fill="#f07a35"/><path d="%(CHEEK)s" fill="#fff4e0"/><path d="%(MUZZLE)s" fill="#fff4e0" filter="url(#shs)"/>
<path d="%(EAR_L)s" fill="#3a2019"/><path d="%(EAR_R)s" fill="#3a2019"/><path d="%(EYE)s" fill="#2b1a14"/>%(NOSE)s</g>
</svg>
""" % dict(globals(), NOSE=nose("#2b1a14"))
# ---- 5. blueprint ------------------------------------------------------------------------
def blueprint():
ln = "#e8f1ff"
return HDR + """<title id="t">Fox Drawing Sheet</title><desc id="d">Blueprint: white linework on cyan-blue grid paper, construction circles, centre lines, dimensions and a title block.</desc>
<defs>
<pattern id="grid-s" width="16" height="16" patternUnits="userSpaceOnUse"><path d="M16 0H0V16" fill="none" stroke="#5f9be0" stroke-width="1" opacity=".35"/></pattern>
<pattern id="grid-l" width="80" height="80" patternUnits="userSpaceOnUse"><path d="M80 0H0V80" fill="none" stroke="#7fb4ee" stroke-width="1.5" opacity=".5"/></pattern>
<pattern id="hatch" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><path d="M0 0V10" stroke="%(ln)s" stroke-width="1.2" opacity=".55"/></pattern>
<marker id="arr" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0 1L10 5L0 9z" fill="%(ln)s"/></marker>
</defs>
<rect id="paper" width="1024" height="1024" fill="#0f3a6d"/>
<rect width="1024" height="1024" fill="url(#grid-s)"/><rect width="1024" height="1024" fill="url(#grid-l)"/>
<rect id="border" x="30" y="30" width="964" height="964" fill="none" stroke="%(ln)s" stroke-width="4"/>
<g id="construction" fill="none" stroke="#9cc8ff" stroke-width="1.5" stroke-dasharray="14 6 3 6">
<path d="M370 150V620M180 400H600"/><circle cx="370" cy="400" r="180"/><circle cx="370" cy="400" r="120"/><path d="M60 840H990"/><path d="M700 640 L980 640"/></g>
<g id="fox" fill="none" stroke="%(ln)s" stroke-width="3" stroke-linejoin="round" stroke-linecap="round">
<path d="%(TAIL)s" stroke-width="4"/><path d="%(TAIL_TIP)s" fill="url(#hatch)"/>
<path d="%(BODY)s" stroke-width="4"/><path d="%(HAUNCH)s"/><path d="%(LEG2)s" fill="url(#hatch)"/><path d="%(CHEST)s"/><path d="%(LEG)s" fill="url(#hatch)"/>
<path d="%(HEAD)s" stroke-width="4"/><path d="%(CHEEK)s"/><path d="%(MUZZLE)s"/><path d="%(EAR_L)s" fill="url(#hatch)"/><path d="%(EAR_R)s" fill="url(#hatch)"/><path d="%(EYE)s"/><circle cx="236" cy="488" r="10" fill="%(ln)s"/></g>
<g id="dimensions" fill="none" stroke="%(ln)s" stroke-width="1.5" font-family="'Courier New',monospace" font-size="20">
<path d="M232 130V236M984 470V500M232 180H352M352 180H492" stroke-dasharray="none"/>
<path d="M232 180H492" marker-start="url(#arr)" marker-end="url(#arr)"/>
<path d="M60 222V840" marker-start="url(#arr)" marker-end="url(#arr)"/><path d="M60 222H340M60 840H300" stroke-dasharray="5 4"/>
<path d="M232 880H962" marker-start="url(#arr)" marker-end="url(#arr)"/><path d="M232 850V890M962 850V890"/>
<path d="M440 100L400 150" /><text x="446" y="98" fill="%(ln)s" stroke="none">A: EAR TIP</text>
<text x="264" y="170" fill="%(ln)s" stroke="none">260</text><text x="70" y="540" fill="%(ln)s" stroke="none" transform="rotate(-90 70 540)">618</text>
<text x="560" y="912" fill="%(ln)s" stroke="none">730</text>
<path d="M760 400H930L904 476" /><text x="760" y="390" fill="%(ln)s" stroke="none">B: BRUSH TIP</text>
</g>
<g id="title-block" fill="none" stroke="%(ln)s" stroke-width="2"><rect x="560" y="920" width="410" height="54"/><path d="M770 920V974"/>
<text x="572" y="944" fill="%(ln)s" stroke="none" font-family="'Courier New',monospace" font-size="18">VULPES VULPES</text><text x="572" y="965" fill="%(ln)s" stroke="none" font-family="'Courier New',monospace" font-size="14">SHEET 1/1 SCALE 1:8</text><text x="784" y="944" fill="%(ln)s" stroke="none" font-family="'Courier New',monospace" font-size="14">DWG NO. FX-001</text><text x="784" y="965" fill="%(ln)s" stroke="none" font-family="'Courier New',monospace" font-size="14">REV A</text></g>
</svg>
""" % dict(globals(), ln=ln)
if __name__ == "__main__":
out = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
out.mkdir(parents=True, exist_ok=True)
for name, fn in [("fox", base), ("fox-ukiyo-e", ukiyo), ("fox-cyberpunk-neon", cyber),
("fox-paper-cutout", paper), ("fox-blueprint", blueprint)]:
(out / (name + ".svg")).write_text(fn(), encoding="utf-8")
print("wrote", out / (name + ".svg"))
examples/src/make_logo_poster.py
#!/usr/bin/env python3
"""Generates two non-illustration examples: an Art Deco hotel logo and a vintage travel poster.
Usage: python3 make_logo_poster.py OUTDIR
Text is live (fonts not embedded; generic fallbacks given) - convert to outlines before print.
"""
import math, sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent.parent / "scripts"))
from text_to_path import text_path, text_group # noqa: E402 (text is outlined: no font dependency)
F = HERE / "fonts"
ANTON, SERIF, GROT = (str(F / n) for n in ("anton-latin-400-normal.woff2", "instrument-serif-latin-400-normal.woff2",
"space-grotesk-latin-500-normal.woff2"))
def tg(font, text, size, x, y, width=None, attrs=""):
return text_group(font, text, size=size, x=x, y=y, anchor="middle", width=width, attrs=attrs)
def deco_logo():
cx, cy = 512, 520
rays = "".join('<use href="#ray" transform="rotate(%d %d %d)"/>' % (a, cx, cy) for a in range(-80, 81, 10))
arcs = "".join('<path d="M%d %d a%d %d 0 0 1 %d 0"/>' % (cx - r, cy - 10, r, r, 2 * r) for r in (112, 148, 184))
flutes = "".join('<path d="M%d 340V548"/>' % x for x in (480, 512, 544))
diamonds = "".join('<path d="M%d 618l10 10l-10 10l-10 -10z"/>' % x for x in (452, 512, 572))
return f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="t d">
<title id="t">The Meridian Hotel</title><desc id="d">Art Deco luxury hotel logo: gold sunburst, stepped tower, chamfered frame, spaced serif lettering on midnight navy.</desc>
<defs>
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f6e2a0"/><stop offset=".45" stop-color="#d7a94a"/><stop offset=".7" stop-color="#f1d78c"/><stop offset="1" stop-color="#a87422"/></linearGradient>
<path id="ray" d="M{cx} {cy} L{cx-9} {cy-300} H{cx+9} Z"/>
</defs>
<rect id="ground" width="1024" height="1024" fill="#0c1a2b"/>
<g id="frame" fill="none" stroke="url(#gold)">
<path d="M110 70H914L954 110V914L914 954H110L70 914V110Z" stroke-width="7"/>
<path d="M126 92H898L932 126V898L898 932H126L92 898V126Z" stroke-width="2.5"/>
<path d="M70 190H92M932 190H954M70 834H92M932 834H954" stroke-width="5"/></g>
<g id="sunburst" fill="url(#gold)" opacity=".92">{rays}</g>
<g id="halo" fill="none" stroke="url(#gold)" stroke-width="4">{arcs}</g>
<g id="tower" stroke="url(#gold)" stroke-width="5" stroke-linejoin="miter">
<path d="M440 560V400H470V330H554V400H584V560Z" fill="#0c1a2b"/>
<path d="M486 330V270H538V330" fill="#0c1a2b"/>
<path d="M500 270L512 190L524 270Z" fill="url(#gold)"/>
<g fill="none" stroke-width="3">{flutes}</g></g>
<g id="plinth" fill="url(#gold)"><rect x="400" y="560" width="224" height="16"/><rect x="360" y="588" width="304" height="8"/><rect x="330" y="608" width="364" height="4"/></g>
<g id="ornament" fill="url(#gold)">{diamonds.replace('<path','<path fill="url(#gold)"')}</g>
<g id="wordmark" fill="url(#gold)">
{tg(SERIF, 'THE MERIDIAN', 96, 512, 726, 640)}
{tg(GROT, 'HOTEL & RESIDENCES', 26, 512, 790, 440)}
{tg(GROT, 'EST · 1928', 20, 512, 868, 190)}</g>
<g id="rules" stroke="url(#gold)" stroke-width="2"><path d="M206 812H430M594 812H818"/><path d="M256 828H430M594 828H768" stroke-width="1"/></g>
</svg>
"""
def poster():
rays = "".join('<path d="M400 470 L%.1f %.1f L%.1f %.1f Z"/>' % (
400 + 900 * math.cos(math.radians(a)), 470 - 900 * math.sin(math.radians(a)),
400 + 900 * math.cos(math.radians(a + 4)), 470 - 900 * math.sin(math.radians(a + 4)))
for a in range(5, 180, 12))
pines = "".join('<use href="#pine" transform="translate(%d %d) scale(%s)"/>' % (x, y, s) for x, y, s in [
(60, 790, 1.5), (130, 815, 1.15), (215, 800, 1.3), (620, 800, 1.4), (700, 780, 1.7), (760, 822, 1.1), (555, 830, .9)])
return f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 1120" role="img" aria-labelledby="t d">
<title id="t">Visit the Highlands</title><desc id="d">Vintage travel poster: sunburst over layered mountains and a lake, pines in the foreground, bold condensed title band, cream paper border.</desc>
<defs>
<clipPath id="art"><rect x="50" y="50" width="700" height="800"/></clipPath>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f4b24a"/><stop offset="1" stop-color="#f7dfa6"/></linearGradient>
<path id="pine" d="M0 -150L-18 -95H-10L-30 -45H-18L-44 0H-6V20H6V0H44L18 -45H30L10 -95H18Z"/>
<pattern id="grain" width="6" height="6" patternUnits="userSpaceOnUse"><circle cx="1.5" cy="1.5" r=".8" fill="#5b3a1e" opacity=".22"/><circle cx="4.5" cy="4.5" r=".6" fill="#5b3a1e" opacity=".18"/></pattern>
</defs>
<rect id="paper" width="800" height="1120" fill="#f1e6cc"/>
<g id="scene" clip-path="url(#art)">
<rect x="50" y="50" width="700" height="800" fill="url(#sky)"/>
<g id="sunburst" fill="#f7dfa6" opacity=".55">{rays}</g>
<circle cx="400" cy="470" r="112" fill="#d9482b"/><circle cx="400" cy="470" r="82" fill="#e9713a"/>
<g id="far-range"><path d="M50 560L170 430L250 510L340 400L470 560Z" fill="#c9a89a"/><path d="M400 560L520 420L610 500L680 440L750 520V600H400Z" fill="#c9a89a"/></g>
<g id="mid-range"><path d="M50 640L200 470L300 590L420 500L560 640Z" fill="#3f7f86"/><path d="M200 470L245 545L215 560L180 520Z" fill="#2f666d"/><path d="M420 500L470 570L440 590L400 540Z" fill="#2f666d"/><path d="M420 500L455 545L433 553Z" fill="#f1e6cc"/><path d="M200 470L228 507L207 512Z" fill="#f1e6cc"/><path d="M460 650L620 500L750 610V700H460Z" fill="#2d5f7a"/><path d="M620 500L665 570L640 585L600 540Z" fill="#234c63"/></g>
<g id="lake"><rect x="50" y="660" width="700" height="190" fill="#1f4e6b"/><g fill="#d9482b" opacity=".85"><rect x="350" y="674" width="100" height="6"/><rect x="368" y="690" width="64" height="5"/><rect x="384" y="704" width="32" height="4"/></g><g fill="#6fa3a6" opacity=".7"><rect x="120" y="720" width="120" height="3"/><rect x="520" y="752" width="150" height="3"/><rect x="230" y="790" width="90" height="3"/></g></g>
<path id="shore" d="M50 780C160 745 260 770 330 800S520 830 750 770V850H50Z" fill="#2c5a3e"/>
<g id="pines" fill="#173b2c">{pines}</g>
<rect id="grain-overlay" x="50" y="50" width="700" height="800" fill="url(#grain)"/>
</g>
<rect id="frame" x="50" y="50" width="700" height="800" fill="none" stroke="#173b2c" stroke-width="8"/>
<g id="titleband">
{tg(ANTON, 'HIGHLANDS', 128, 406, 1050, 690, 'fill="#d9482b"')}
{tg(ANTON, 'HIGHLANDS', 128, 400, 1044, 690, 'fill="#173b2c"')}
{tg(ANTON, 'VISIT THE', 44, 400, 922, 400, 'fill="#173b2c"')}
{tg(SERIF, 'RAIL & STEAMER · EVERY SUMMER', 28, 400, 1088, 600, 'fill="#3f7f86"')}</g>
<g id="rules" fill="#173b2c"><rect x="50" y="868" width="700" height="4"/></g>
</svg>
"""
if __name__ == "__main__":
out = Path(sys.argv[1] if len(sys.argv) > 1 else "."); out.mkdir(parents=True, exist_ok=True)
(out / "logo-art-deco-hotel.svg").write_text(deco_logo(), encoding="utf-8")
(out / "poster-vintage-highlands.svg").write_text(poster(), encoding="utf-8")
print("wrote logo + poster to", out)
examples/src/make_showcase.py
#!/usr/bin/env python3
"""More fox variants (same frozen scene spec as make_fox_set.py) + non-fox showcase pieces.
Usage: python3 make_showcase.py OUTDIR [name ...]
Low-poly needs a flat render of fox.svg as its colour source: it renders one with
scripts/render_svg.py (Chromium) if OUTDIR/fox.svg exists.
"""
import math
import random
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
SKILL = HERE.parent.parent
sys.path.insert(0, str(HERE))
sys.path.insert(0, str(SKILL / "scripts"))
from make_fox_set import (TAIL, TAIL_TIP, BODY, HAUNCH, CHEST, LEG, LEG2, HEAD, MUZZLE, # noqa: E402
EAR_L, EAR_R, EYE, CHEEK, HDR)
from text_to_path import text_path # noqa: E402
FONTS = HERE / "fonts"
ANTON = str(FONTS / "anton-latin-400-normal.woff2")
UNB = str(FONTS / "unbounded-latin-800-normal.woff2")
SERIF = str(FONTS / "instrument-serif-latin-400-normal.woff2")
FOX_PARTS = [TAIL, BODY, LEG2, LEG, HEAD] # silhouette pieces
def silhouette(**attrs):
a = " ".join('%s="%s"' % (k.replace("_", "-"), v) for k, v in attrs.items())
return "".join('<path d="%s" %s/>' % (p, a) for p in FOX_PARTS)
# ------------------------------------------------------------------------------------------
# LOW POLY: mesh from contour + interior points, facets shaded from a height field
# ------------------------------------------------------------------------------------------
def low_poly(out):
import numpy as np
from PIL import Image
from scipy import ndimage
from scipy.spatial import Delaunay
png, keyed = out / "_fox_flat.png", out / "_fox_keyed.svg"
src = (out / "fox.svg").read_text(encoding="utf-8")
keyed.write_text(src.replace('fill="#f4efe6"', 'fill="#00ff00"').replace('fill="#e5dccb"', 'fill="#00ff00"'), encoding="utf-8")
subprocess.run([sys.executable, str(SKILL / "scripts/render_svg.py"), str(keyed), "-o", str(png), "-w", "1024"],
check=True, capture_output=True)
im = np.asarray(Image.open(png).convert("RGB")).astype(float)
png.unlink(); keyed.unlink()
mask = ~((im[..., 1] > 200) & (im[..., 0] < 80) & (im[..., 2] < 80))
mask = ndimage.binary_opening(mask, iterations=2)
dist = ndimage.distance_transform_edt(mask)
height = np.sqrt(dist) * 9.0
gy, gx = np.gradient(ndimage.gaussian_filter(height, 6))
rnd = random.Random(3)
pts = []
edge = mask ^ ndimage.binary_erosion(mask, iterations=1)
ey, ex = np.nonzero(edge)
order = list(range(0, len(ex), 22))
pts += [(ex[i], ey[i]) for i in order]
# interior colour boundaries (chest, muzzle, legs) keep their shapes
q = (im[..., 0] // 24) * 10000 + (im[..., 1] // 24) * 100 + im[..., 2] // 24
cb = (np.abs(np.diff(q, axis=0, prepend=q[:1])) + np.abs(np.diff(q, axis=1, prepend=q[:, :1]))) > 0
cb &= ndimage.binary_erosion(mask, iterations=4)
cy, cx = np.nonzero(cb)
pts += [(cx[i], cy[i]) for i in range(0, len(cx), 30)]
for y in range(0, 1025, 34): # interior fill (denser in the fox)
for x in range(0, 1025, 34):
jx, jy = x + rnd.uniform(-11, 11), y + rnd.uniform(-11, 11)
xi, yi = int(min(1023, max(0, jx))), int(min(1023, max(0, jy)))
if mask[yi, xi] or (x % 102 == 0 and y % 68 == 0):
pts.append((jx, jy))
for i in range(0, 1025, 128): # frame
pts += [(i, 0), (i, 1024), (0, i), (1024, i)]
for i in range(28): # sun rim
a = i * math.pi * 2 / 28
pts.append((760 + 150 * math.cos(a), 300 + 150 * math.sin(a)))
pts.append((760, 300))
for i in range(24): # ground ridge
pts.append((i * 1024 / 23, 846 + rnd.uniform(-6, 6)))
pts = np.clip(np.array(pts, float), 0, 1024)
tri = Delaunay(pts)
light = np.array([-0.55, -0.65, 0.52]); light /= np.linalg.norm(light)
polys = {"sky": [], "ground": [], "fox": []}
for s in tri.simplices:
p = pts[s]
c = p.mean(0)
xi, yi = int(min(1023, c[0])), int(min(1023, c[1]))
a = abs((p[1][0] - p[0][0]) * (p[2][1] - p[0][1]) - (p[1][1] - p[0][1]) * (p[2][0] - p[0][0])) / 2
if a < 4:
continue
if mask[yi, xi]:
col = im[yi, xi]
n = np.array([-gx[yi, xi], -gy[yi, xi], 1.0]); n /= np.linalg.norm(n)
k = 0.78 + 0.34 * max(0, n @ light) + rnd.uniform(-0.025, 0.025)
layer = "fox"
elif c[1] > 846:
t = (c[1] - 846) / 178
col = np.array([0x5c, 0x7c, 0x4f]) * (1 - t) + np.array([0x2e, 0x46, 0x33]) * t
k = 1 + rnd.uniform(-0.07, 0.07)
layer = "ground"
else:
t = c[1] / 846
col = np.array([0x2b, 0x3a, 0x67]) * (1 - t) + np.array([0xf2, 0x9e, 0x6d]) * t
d = math.hypot(c[0] - 760, c[1] - 300)
if d < 150:
col = np.array([0xff, 0xd8, 0x9a], float)
k = 1 + rnd.uniform(-0.06, 0.06)
layer = "sky"
rgb = np.clip(col * k, 0, 255).astype(int)
hexc = "#%02x%02x%02x" % tuple(rgb)
polys[layer].append('<polygon points="%s" fill="%s" stroke="%s"/>' % (
" ".join("%.1f,%.1f" % tuple(v) for v in p), hexc, hexc))
body = "".join('<g id="%s" stroke-width="0.8" stroke-linejoin="round">%s</g>' % (k, "".join(v)) for k, v in polys.items())
return HDR + ('<title id="t">Low Poly Fox</title><desc id="d">Low poly: triangulated mesh, flat facets shaded by '
'facing direction to a top-left light, dusk sky and meadow meshes.</desc>' + body + "</svg>\n")
# ------------------------------------------------------------------------------------------
# RISOGRAPH: two inks, multiply overprint, halftone, misregistration, grain
# ------------------------------------------------------------------------------------------
def riso(out):
pink, blue, paper = "#ff48b0", "#0078bf", "#f4efe4"
title, _ = text_path(ANTON, "WILD", size=290, x=512, y=330, anchor="middle", tracking=6)
sub, _ = text_path(ANTON, "FOX CLUB · NO. 07", size=40, x=512, y=985, anchor="middle", tracking=10)
return HDR + """<title id="t">Riso Fox</title><desc id="d">Risograph: fluorescent pink and blue inks overprinting (multiply), halftone dot shading, slight misregistration, paper grain.</desc>
<defs>
<clipPath id="sheet"><rect width="1024" height="1024"/></clipPath>
<pattern id="dot-p" width="14" height="14" patternUnits="userSpaceOnUse" patternTransform="rotate(15)"><circle cx="7" cy="7" r="4.2" fill="{pink}"/></pattern>
<pattern id="dot-b" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(75)"><circle cx="6" cy="6" r="3.1" fill="{blue}"/></pattern>
<pattern id="dot-b2" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(75)"><circle cx="5" cy="5" r="1.7" fill="{blue}"/></pattern>
<filter id="grain" x="0" y="0" width="100%" height="100%"><feTurbulence type="fractalNoise" baseFrequency=".9" numOctaves="2" seed="4"/><feColorMatrix values="0 0 0 0 0.25 0 0 0 0 0.2 0 0 0 0 0.15 0 0 0 -1.6 1.05"/><feComposite in2="SourceGraphic" operator="in"/></filter>
</defs>
<rect id="paper" width="1024" height="1024" fill="{paper}"/>
<g id="ink-blue" style="mix-blend-mode:multiply" clip-path="url(#sheet)"><g transform="translate(-5 3)">
<path d="{title}" fill="{blue}" opacity=".9"/>
<rect x="0" y="846" width="1024" height="100" fill="url(#dot-b2)"/>
<path d="M0 846H1024" stroke="{blue}" stroke-width="8"/>
<path d="{sub}" fill="{blue}"/>
<g id="fox-blue"><path d="{HAUNCH}" fill="url(#dot-b)"/><path d="{LEG}" fill="{blue}"/><path d="{LEG2}" fill="{blue}"/><path d="{EAR_L}" fill="{blue}"/><path d="{EAR_R}" fill="{blue}"/><path d="{EYE}" fill="{blue}"/><circle cx="236" cy="488" r="11" fill="{blue}"/><path d="{TAIL}" fill="url(#dot-b2)"/></g>
</g></g>
<g id="ink-pink" style="mix-blend-mode:multiply" transform="translate(4 -3)">
<circle cx="800" cy="210" r="120" fill="url(#dot-p)"/>
<g id="fox-pink"><path d="{TAIL}" fill="{pink}"/><path d="{BODY}" fill="{pink}"/><path d="{HEAD}" fill="{pink}"/><path d="{LEG2}" fill="{pink}"/></g>
<g fill="{paper}"><path d="{TAIL_TIP}"/><path d="{CHEST}"/><path d="{MUZZLE}"/><path d="{CHEEK}"/></g>
<path d="{CHEST}" fill="url(#dot-p)" opacity=".35"/>
</g>
<rect id="grain-overlay" width="1024" height="1024" filter="url(#grain)" opacity=".5" fill="{paper}"/>
</svg>
""".format(**dict(globals(), pink=pink, blue=blue, paper=paper, title=title, sub=sub))
# ------------------------------------------------------------------------------------------
# COMIC BOOK: burst, speed lines, halftone shading, thick ink, lettering
# ------------------------------------------------------------------------------------------
def comic(out):
cx, cy = 400, 470
burst = []
for i in range(36):
r = 700 if i % 2 == 0 else 470
a = math.radians(i * 10)
burst.append("%.0f,%.0f" % (cx + r * math.cos(a), cy + r * math.sin(a)))
speed = "".join('<path d="M%d %d L%d %d"/>' % (cx + 330 * math.cos(math.radians(a)), cy + 330 * math.sin(math.radians(a)),
cx + 900 * math.cos(math.radians(a)), cy + 900 * math.sin(math.radians(a)))
for a in range(3, 360, 9))
yip, _ = text_path(ANTON, "YIP!", size=150, x=740, y=260, anchor="middle", tracking=4)
cap, _ = text_path(ANTON, "MEANWHILE, IN THE WOODS...", size=36, x=78, y=112, tracking=2)
ink = "#111"
return HDR + """<title id="t">Comic Fox</title><desc id="d">Comic book: action burst, speed lines, halftone shading, thick-to-thin black inks, caption box and outlined lettering.</desc>
<defs>
<clipPath id="panel"><rect width="1024" height="1024"/></clipPath>
<pattern id="ht-dark" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><rect width="12" height="12" fill="#e63946"/><circle cx="6" cy="6" r="3.4" fill="#9e1b2a"/></pattern>
<pattern id="ht-bg" width="18" height="18" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><rect width="18" height="18" fill="#ffd60a"/><circle cx="9" cy="9" r="4" fill="#ff9f1c"/></pattern>
<pattern id="ht-sky" width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><rect width="16" height="16" fill="#00b4d8"/><circle cx="8" cy="8" r="3" fill="#0077b6"/></pattern>
</defs>
<rect id="panel-bg" width="1024" height="1024" fill="url(#ht-sky)"/>
<polygon id="burst" clip-path="url(#panel)" points="{burst}" fill="url(#ht-bg)" stroke="{ink}" stroke-width="6" stroke-linejoin="miter"/>
<g id="speed-lines" clip-path="url(#panel)" stroke="#fff8e1" stroke-width="5" opacity=".75">{speed}</g>
<rect id="ground" y="846" width="1024" height="178" fill="#2a9d8f" stroke="{ink}" stroke-width="8" clip-path="url(#panel)"/>
<g id="fox-color">
<path d="{TAIL}" fill="#e63946"/><path d="{BODY}" fill="#f77f00"/><path d="{HAUNCH}" fill="url(#ht-dark)"/><path d="{TAIL_TIP}" fill="#fff8e1"/>
<path d="{LEG2}" fill="#3d1f14"/><path d="{CHEST}" fill="#fff8e1"/><path d="{LEG}" fill="#2a1510"/>
<path d="{HEAD}" fill="#f77f00"/><path d="{CHEEK}" fill="#fff8e1"/><path d="{MUZZLE}" fill="#fff8e1"/><path d="{EAR_L}" fill="{ink}"/><path d="{EAR_R}" fill="{ink}"/></g>
<g id="ink" fill="none" stroke="{ink}" stroke-linejoin="round" stroke-linecap="round">
<g stroke-width="13"><path d="{TAIL}"/><path d="{BODY}"/><path d="{HEAD}"/></g>
<g stroke-width="6"><path d="{TAIL_TIP}"/><path d="{HAUNCH}"/><path d="{CHEST}"/><path d="{MUZZLE}"/><path d="{LEG}"/><path d="{LEG2}"/></g>
<path d="M560 600q30 20 44 52M620 560q22 18 30 40M820 600q20 30 20 70" stroke-width="5"/></g>
<g id="face"><path d="{EYE}" fill="{ink}"/><circle cx="236" cy="488" r="12" fill="{ink}"/><circle cx="344" cy="446" r="4" fill="#fff"/></g>
<g id="balloon"><path d="M560 150 C560 60 920 60 920 150 C920 240 700 250 640 236 L560 300 L590 222 C570 205 560 180 560 150Z" fill="#fff" stroke="{ink}" stroke-width="7" stroke-linejoin="round"/>
<path d="{yip}" fill="#e63946" stroke="{ink}" stroke-width="6" paint-order="stroke" stroke-linejoin="round" transform="rotate(-4 740 200)"/></g>
<g id="caption"><rect x="60" y="64" width="468" height="66" fill="#ffd60a" stroke="{ink}" stroke-width="6"/><path d="{cap}" fill="{ink}"/></g>
<rect id="panel-border" x="10" y="10" width="1004" height="1004" fill="none" stroke="{ink}" stroke-width="20"/>
</svg>
""".format(**dict(globals(), burst=" ".join(burst), speed=speed, yip=yip, cap=cap, ink=ink))
# ------------------------------------------------------------------------------------------
# STICKER: die-cut white border, drop shadow, glossy streak, dark outlines
# ------------------------------------------------------------------------------------------
def sticker(out):
ink = "#1c1c28"
return HDR + """<title id="t">Fox Sticker</title><desc id="d">Sticker: thick white die-cut border with soft shadow, saturated flat fills, dark outlines, glossy highlight.</desc>
<defs>
<g id="cut">{sil}</g>
<filter id="lift" x="-10%" y="-10%" width="120%" height="125%"><feDropShadow dx="0" dy="14" stdDeviation="12" flood-color="#1c1c28" flood-opacity=".35"/></filter>
</defs>
<g id="border" filter="url(#lift)"><use href="#cut" fill="#fff" stroke="#fff" stroke-width="64" stroke-linejoin="round"/></g>
<g id="art" stroke="{ink}" stroke-width="9" stroke-linejoin="round" stroke-linecap="round">
<path d="{TAIL}" fill="#ff6b35"/><path d="{TAIL_TIP}" fill="#fffaf0"/><path d="{BODY}" fill="#ff7f3f"/><path d="{HAUNCH}" fill="#ff5d73" stroke-width="6"/>
<path d="{LEG2}" fill="#3b2230"/><path d="{CHEST}" fill="#fffaf0" stroke-width="6"/><path d="{LEG}" fill="#2b1a24"/>
<path d="{HEAD}" fill="#ff7f3f"/><path d="{CHEEK}" fill="#fffaf0" stroke-width="6"/><path d="{MUZZLE}" fill="#fffaf0" stroke-width="6"/>
<path d="{EAR_L}" fill="#ff5d73" stroke-width="5"/><path d="{EAR_R}" fill="#ff5d73" stroke-width="5"/></g>
<g id="face" fill="{ink}"><ellipse cx="342" cy="452" rx="20" ry="14"/><circle cx="348" cy="447" r="5" fill="#fff"/><circle cx="238" cy="488" r="13"/><ellipse cx="385" cy="505" rx="22" ry="12" fill="#ff9fb0"/></g>
<g id="gloss" fill="#fff" opacity=".55"><path d="M470 540 C560 520 640 560 700 630 C690 640 676 644 664 640 C620 590 560 564 488 566 C478 560 470 552 470 540Z"/><circle cx="716" cy="668" r="9"/><path d="M372 280 L384 250 L396 292Z"/></g>
<g id="badge" transform="rotate(-10 820 250)"><circle cx="820" cy="250" r="92" fill="#ffc857" stroke="#fff" stroke-width="18"/><circle cx="820" cy="250" r="92" fill="none" stroke="{ink}" stroke-width="7"/><path d="{hey}" fill="{ink}"/></g>
</svg>
""".format(**dict(globals(), ink=ink, sil=silhouette(), hey=text_path(ANTON, "HEY!", size=74, x=820, y=278, anchor="middle", tracking=2)[0]))
# ------------------------------------------------------------------------------------------
# SUMI-E: tapered brush strokes, ink wash, mist, hanko seal
# ------------------------------------------------------------------------------------------
def _bez(p0, p1, p2, p3, n=40):
out = []
for i in range(n + 1):
t = i / n
a = (1 - t) ** 3; b = 3 * (1 - t) ** 2 * t; c = 3 * (1 - t) * t * t; d = t ** 3
out.append((a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0], a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1]))
return out
def brush(pts, w0, w1, wmid=None, seed=0, rough=0.9):
"""Tapered brush stroke polygon along a polyline: width w0 -> (wmid) -> w1, slightly ragged."""
rnd = random.Random(seed)
L, R = [], []
n = len(pts)
for i, (x, y) in enumerate(pts):
t = i / (n - 1)
w = (w0 + (w1 - w0) * t) if wmid is None else (w0 + (wmid - w0) * (t / .35) if t < .35 else wmid + (w1 - wmid) * ((t - .35) / .65))
w *= 1 + rnd.uniform(-.08, .08) * rough
xa, ya = pts[max(0, i - 1)]; xb, yb = pts[min(n - 1, i + 1)]
dx, dy = xb - xa, yb - ya; l = math.hypot(dx, dy) or 1
nx, ny = -dy / l, dx / l
L.append((x + nx * w / 2, y + ny * w / 2)); R.append((x - nx * w / 2, y - ny * w / 2))
poly = L + R[::-1]
return "M" + " L".join("%.1f %.1f" % p for p in poly) + "Z"
def sumie(out):
ink = "#1a1a1a"
strokes = [
# (bezier control points, w0, w1, wmid, seed, fill)
(((352, 222), (344, 280), (336, 330), (318, 420)), 3, 14, 12, 1), # left ear edge
(((352, 222), (380, 270), (396, 310), (410, 338)), 2, 10, 9, 2),
(((468, 240), (490, 300), (494, 340), (510, 440)), 4, 12, 16, 3), # right ear + back of head
(((232, 486), (290, 440), (300, 430), (322, 420)), 3, 10, 8, 4), # brow / snout top
(((236, 492), (300, 530), (400, 556), (470, 562)), 3, 6, 10, 5), # jaw
(((512, 470), (600, 480), (700, 560), (756, 700)), 10, 26, 30, 6), # back
(((756, 700), (780, 780), (740, 842), (650, 842)), 26, 6, 22, 7), # rump
(((420, 560), (398, 640), (410, 720), (420, 830)), 18, 5, 16, 8), # chest line
(((470, 742), (476, 790), (482, 820), (488, 842)), 20, 8, 22, 9), # foreleg
(((520, 770), (530, 800), (538, 826), (540, 842)), 16, 6, 16, 10),
]
paths = "".join('<path d="%s"/>' % brush(_bez(*s[0]), s[1], s[2], s[3], seed=s[4]) for s in strokes)
tail_stroke = brush(_bez((700, 810), (860, 860), (990, 700), (872, 486)), 60, 4, 110, seed=11)
seal, _ = text_path(SERIF, "狐" if False else "KITSUNE", size=26, x=0, y=0)
return HDR + """<title id="t">Ink Fox</title><desc id="d">Sumi-e: wet ink wash tail, single-gesture tapered brush strokes, mist, vast negative space and a vermilion seal.</desc>
<defs>
<linearGradient id="wash-g" x1="1" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#2a2a28"/><stop offset=".55" stop-color="#6b6a64" stop-opacity=".85"/><stop offset="1" stop-color="#b9b5a8" stop-opacity=".3"/></linearGradient>
<linearGradient id="mist-g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f4eddc" stop-opacity="0"/><stop offset="1" stop-color="#f4eddc"/></linearGradient>
<filter id="bleed" x="-10%" y="-10%" width="120%" height="120%"><feTurbulence type="fractalNoise" baseFrequency=".035" numOctaves="2" seed="2"/><feDisplacementMap in="SourceGraphic" scale="14"/><feGaussianBlur stdDeviation="2.2"/></filter>
<filter id="dry" x="-5%" y="-5%" width="110%" height="110%"><feTurbulence type="fractalNoise" baseFrequency=".6 .04" numOctaves="2" seed="9" result="n"/><feColorMatrix in="n" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.5 1.55" result="m"/><feComposite in="SourceGraphic" in2="m" operator="in"/></filter>
</defs>
<rect id="paper" width="1024" height="1024" fill="#f4eddc"/>
<g id="far" opacity=".22" filter="url(#bleed)"><path d="M0 700 C160 640 260 690 380 650 S600 610 700 660 S900 680 1024 620 V760 H0Z" fill="#6b6a64"/></g>
<g id="wash" filter="url(#bleed)"><path d="{tail}" fill="url(#wash-g)"/><path d="{BODY}" fill="#8c8a84" opacity=".13"/><path d="{HEAD}" fill="#8c8a84" opacity=".1"/></g>
<g id="strokes" fill="{ink}" filter="url(#dry)">{paths}</g>
<g id="accents" fill="{ink}"><path d="M318 452 Q340 438 364 450 Q340 458 318 452Z"/><circle cx="238" cy="488" r="9"/><path d="M364 300 L368 262 L386 318Z" opacity=".7"/><path d="M458 300 L468 268 L474 330Z" opacity=".7"/></g>
<rect id="mist" y="780" width="1024" height="244" fill="url(#mist-g)"/>
<g id="seal" transform="rotate(-3 880 830)"><rect x="840" y="780" width="84" height="100" rx="4" fill="#c1272d"/><g fill="#f4eddc"><rect x="852" y="794" width="60" height="8"/><rect x="878" y="794" width="8" height="72"/><rect x="852" y="826" width="60" height="7"/><path d="M856 866 L878 842 L884 848 L864 872Z"/><path d="M908 866 L886 842 L880 848 L900 872Z"/></g></g>
</svg>
""".format(**dict(globals(), ink=ink, paths=paths, tail=tail_stroke,
cal="".join('<path d="%s"/>' % brush(_bez((930, 120 + i * 80), (936, 140 + i * 80), (926, 160 + i * 80), (932, 178 + i * 80), 12), 9, 2, 7, seed=20 + i) for i in range(6))))
# ------------------------------------------------------------------------------------------
# HORROR: near-black forest, cold moon, fog, silhouette, red eyes, vignette
# ------------------------------------------------------------------------------------------
def horror(out):
rnd = random.Random(13)
def tree(x, h, w, seed):
r = random.Random(seed)
d = "M%d 900 L%d %d L%d %d L%d 900Z" % (x - w, x - w * .3, 900 - h, x + w * .3, 900 - h, x + w)
br = ""
for i in range(7):
y = 900 - h * r.uniform(.35, .95); s = r.choice([-1, 1]); L = r.uniform(60, 160)
br += "M%d %d L%d %d L%d %d Z" % (x, y, x + s * L, y - L * r.uniform(.4, .9), x, y - 10)
return d + br
trees = "".join('<path d="%s"/>' % tree(x, h, w, s) for x, h, w, s in [(60, 900, 26, 1), (170, 760, 16, 2), (940, 920, 30, 3), (860, 700, 14, 4), (620, 620, 10, 5)])
fog = "".join('<rect x="0" y="%d" width="1024" height="%d" fill="url(#fog-g)" opacity="%.2f"/>' % (y, h, o) for y, h, o in [(620, 120, .25), (760, 140, .35), (830, 194, .5)])
return HDR + """<title id="t">Night Fox</title><desc id="d">Horror: near-black forest, cold moon, layered fog, roughened silhouette revealing only red eyes, vignette.</desc>
<defs>
<radialGradient id="moon-g" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#dfe8e2"/><stop offset=".6" stop-color="#b9c7bf"/><stop offset="1" stop-color="#8fa39a"/></radialGradient>
<radialGradient id="moonglow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#8fa39a" stop-opacity=".45"/><stop offset="1" stop-color="#8fa39a" stop-opacity="0"/></radialGradient>
<linearGradient id="fog-g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#4a5b52" stop-opacity="0"/><stop offset=".5" stop-color="#4a5b52"/><stop offset="1" stop-color="#4a5b52" stop-opacity="0"/></linearGradient>
<radialGradient id="vig" cx=".45" cy=".45" r=".75"><stop offset=".55" stop-color="#050706" stop-opacity="0"/><stop offset="1" stop-color="#050706" stop-opacity=".95"/></radialGradient>
<radialGradient id="eyeglow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#ff2a1a" stop-opacity=".9"/><stop offset="1" stop-color="#ff2a1a" stop-opacity="0"/></radialGradient>
<clipPath id="canvas"><rect width="1024" height="1024"/></clipPath>
<filter id="rough" x="-5%" y="-5%" width="110%" height="110%"><feTurbulence type="turbulence" baseFrequency=".05" numOctaves="2" seed="7"/><feDisplacementMap in="SourceGraphic" scale="9"/></filter>
</defs>
<rect id="night" width="1024" height="1024" fill="#0b120f"/>
<circle id="moon-halo" cx="470" cy="430" r="470" fill="url(#moonglow)" clip-path="url(#canvas)"/>
<circle id="moon" cx="470" cy="430" r="290" fill="url(#moon-g)"/>
<g id="craters" fill="#8fa39a" opacity=".35"><circle cx="600" cy="300" r="46"/><circle cx="330" cy="620" r="30"/><circle cx="640" cy="560" r="22"/><circle cx="360" cy="260" r="18"/></g>
<g id="far-trees" fill="#16211c" filter="url(#rough)" clip-path="url(#canvas)">{trees}</g>
<path id="ground" d="M0 846 C200 830 420 850 600 842 S900 830 1024 846 V1024 H0Z" fill="#070a09"/>
<g id="fox" fill="#050706" filter="url(#rough)"><path d="{TAIL}"/><path d="{BODY}"/><path d="{LEG2}"/><path d="{LEG}"/><path d="{HEAD}"/></g>
<g id="rim" fill="none" stroke="#8fa39a" stroke-width="3" opacity=".55" filter="url(#rough)"><path d="M468 240 L492 362 C510 392 514 430 512 462 L536 516"/><path d="M530 492 C610 500 650 540 690 580 C750 640 780 720 760 800"/><path d="M872 486 C940 560 962 676 924 752"/></g>
<g id="eyes"><circle cx="340" cy="449" r="40" fill="url(#eyeglow)"/><path d="M318 452 Q338 438 362 446 Q342 460 318 452Z" fill="#ff2a1a"/><circle cx="346" cy="449" r="3" fill="#ffd1c9"/></g>
<g id="fog">{fog}</g>
<rect id="vignette" width="1024" height="1024" fill="url(#vig)"/>
</svg>
""".format(**dict(globals(), trees=trees, fog=fog))
# ------------------------------------------------------------------------------------------
# BAUHAUS: fox rebuilt from circle / square / triangle on a unit grid, primaries, rotated type
# ------------------------------------------------------------------------------------------
def bauhaus(out):
U = 64 # grid unit; canvas = 16 U
R, Y, B, K, W = "#d62828", "#f7b500", "#1d4e9f", "#111111", "#f1eadb"
word, _ = text_path(UNB, "FUCHS", size=86, x=0, y=0, tracking=6)
word2, _ = text_path(UNB, "1925", size=40, x=0, y=0, tracking=4)
return HDR + """<title id="t">Bauhaus Fox</title><desc id="d">Bauhaus: fox built only from circles, quarter-circles, triangles and bars on a 64-unit grid in red, yellow, blue and black.</desc>
<!-- grid unit U=64; all coordinates are multiples of U/2 -->
<rect id="ground" width="1024" height="1024" fill="{W}"/>
<g id="composition">
<circle id="sun" cx="{c1}" cy="{c2}" r="{r1}" fill="{Y}"/>
<rect id="bar" x="0" y="{gy}" width="1024" height="{h1}" fill="{K}"/>
<path id="tail" d="M{tx} {gy} A{tr} {tr} 0 0 1 {tx2} {gy} Z" fill="{B}"/>
<path id="tail-tip" d="M{ttx} {gy} A{ttr} {ttr} 0 0 1 {ttx2} {gy} Z" fill="{W}"/>
<path id="body" d="M{bx} {gy} V{by} A{br} {br} 0 0 1 {bx2} {gy} Z" fill="{R}"/>
<rect id="chest" x="{chx}" y="{chy}" width="{chw}" height="{chh}" fill="{W}"/>
<rect id="leg" x="{lx}" y="{ly}" width="{lw}" height="{lh}" fill="{K}"/>
<path id="head" d="M{h0x} {h0y} L{h1x} {h1y} L{h2x} {h2y} Z" fill="{R}"/>
<path id="muzzle" d="M{h0x} {h0y} L{m1x} {m1y} L{h2x} {h2y} Z" fill="{W}"/>
<path id="ear-l" d="M{e1} L{e2} L{e3} Z" fill="{K}"/>
<path id="ear-r" d="M{f1} L{f2} L{f3} Z" fill="{K}"/>
<circle id="eye" cx="{ex}" cy="{ey}" r="{er}" fill="{K}"/>
<circle id="nose" cx="{h0x}" cy="{h0y}" r="14" fill="{K}"/>
<rect id="rule" x="{rx}" y="96" width="16" height="560" fill="{B}"/>
<g id="type" fill="{K}"><path d="{word}" transform="translate(944 640) rotate(-90)"/><path d="{word2}" transform="translate(96 976)"/></g>
</g>
</svg>
""".format(**dict(
R=R, Y=Y, B=B, K=K, W=W, word=word, word2=word2,
c1=12 * U, c2=4 * U, r1=2.5 * U, gy=13 * U + 20, h1=1024 - (13 * U + 20),
tx=10 * U, tx2=15.5 * U, tr=2.75 * U, ttx=13.5 * U, ttx2=15.5 * U, ttr=1 * U,
bx=6.5 * U, by=13 * U + 20 - 5 * U, br=5 * U, bx2=11.5 * U + 0,
chx=6.5 * U, chy=9 * U, chw=1 * U, chh=4 * U + 20,
lx=7.5 * U, ly=11 * U, lw=U, lh=2 * U + 20,
h0x=3.5 * U, h0y=7.5 * U, h1x=8 * U, h1y=4.5 * U, h2x=8 * U, h2y=9 * U, m1x=6 * U, m1y=8.3 * U,
e1="%d %d" % (5.5 * U, 5.5 * U + 12), e2="%d %d" % (6 * U, 2.5 * U), e3="%d %d" % (7 * U, 5 * U - 2),
f1="%d %d" % (7 * U, 5 * U), f2="%d %d" % (8.5 * U, 2.5 * U), f3="%d %d" % (8 * U, 5.5 * U),
ex=6 * U, ey=6.5 * U, er=U / 4, rx=14.5 * U - 8))
# ------------------------------------------------------------------------------------------
# UKIYO-E BUT DARKER (modifier demo): night palette, moon, lantern glow; structure unchanged
# ------------------------------------------------------------------------------------------
def ukiyo_night(out):
from make_fox_set import ukiyo
s = ukiyo()
swaps = [('<stop offset="0" stop-color="#9fc0c4"/><stop offset="1" stop-color="#efe3c4"/>',
'<stop offset="0" stop-color="#0f1a33"/><stop offset="1" stop-color="#3b4a6b"/>'),
('<circle id="sun" cx="760" cy="290" r="116" fill="#c9412c"/>',
'<circle id="moon" cx="760" cy="290" r="116" fill="#f1e4b8"/>'),
('<g id="kasumi" fill="#f5ecd2">', '<g id="kasumi" fill="#4c5b7a" opacity=".85">'),
('fill="#e6dab9"/></g>', 'fill="#2c3a5a"/></g>'),
('<rect id="paper" width="1024" height="1024" fill="#efe3c4"/>', '<rect id="paper" width="1024" height="1024" fill="#e4d5b0"/>'),
('"#2b4a6b"', '"#16284a"'), ('stroke="#e9dfc4" stroke-width="3"', 'stroke="#9fb0c8" stroke-width="3"'),
('#e9712f', '#c2542a'), ('#d8642c', '#a6431f'), ('#f5ecd2', '#e8dcbc'),
('<title id="t">Fox at Dusk</title>', '<title id="t">Fox at Night</title>'),
('Ukiyo-e style:', 'Ukiyo-e, darker modifier:')]
for a, b in swaps:
s = s.replace(a, b)
return s
# ------------------------------------------------------------------------------------------
# ART NOUVEAU: arched double frame, halo, whiplash tendrils, lilies, ornamental title
# ------------------------------------------------------------------------------------------
def nouveau(out):
ink = "#2d2a24"
rays = "".join('<path d="M512 430 L%.1f %.1f" />' % (512 + 330 * math.cos(math.radians(a)), 430 + 330 * math.sin(math.radians(a))) for a in range(180, 361, 6))
ring = "".join('<circle cx="%.1f" cy="%.1f" r="7"/>' % (512 + 352 * math.cos(math.radians(a)), 430 + 352 * math.sin(math.radians(a))) for a in range(180, 361, 10))
title, _ = text_path(SERIF, "Vulpes", size=110, x=512, y=968, anchor="middle", tracking=6)
return HDR + """<title id="t">Nouveau Fox</title><desc id="d">Art Nouveau: arched double frame, sun-ray halo, whiplash tendrils and lilies growing into the border, muted jewel tones, dark contours.</desc>
<defs>
<symbol id="lily" viewBox="-40 -80 80 90" overflow="visible"><g stroke="{ink}" stroke-width="3" stroke-linejoin="round"><path d="M0 0 C-30 -20 -34 -56 -8 -78 C-10 -50 -6 -26 0 0Z" fill="#e8c9b8"/><path d="M0 0 C30 -20 34 -56 8 -78 C10 -50 6 -26 0 0Z" fill="#e8c9b8"/><path d="M0 0 C-10 -30 -6 -60 0 -80 C6 -60 10 -30 0 0Z" fill="#f3e2d4"/></g></symbol>
<pattern id="scale" width="28" height="18" patternUnits="userSpaceOnUse"><rect width="28" height="18" fill="#7f9c7e"/><path d="M0 18a14 14 0 0 1 28 0" fill="none" stroke="#5f7d62" stroke-width="2.5"/></pattern>
</defs>
<g id="art" stroke-linecap="round">
<rect id="ground" width="1024" height="1024" fill="#efe2c0"/>
<g id="frame" stroke="{ink}" stroke-width="6">
<path d="M70 1000 V472 A442 442 0 0 1 954 472 V1000 Z" fill="#d9c49a"/>
<path d="M100 880 V472 A412 412 0 0 1 924 472 V880 Z" fill="#c9d3b0" stroke-width="4"/></g>
<g id="halo"><circle cx="512" cy="430" r="340" fill="#e7c77d" stroke="{ink}" stroke-width="4"/><g stroke="#c9a24f" stroke-width="5">{rays}</g><g fill="#b5543c" stroke="{ink}" stroke-width="2">{ring}</g><circle cx="512" cy="430" r="200" fill="#f0dca0" stroke="{ink}" stroke-width="3"/></g>
<path id="hill" d="M100 880 V800 C300 760 420 820 560 820 S820 780 924 800 V880Z" fill="url(#scale)" stroke="{ink}" stroke-width="4"/>
<g id="tendrils" fill="none" stroke="{ink}" stroke-linecap="round">
<path d="M100 880 C150 760 60 640 150 540 C220 460 160 380 230 330 C270 300 300 320 290 350 C282 372 256 366 262 346" stroke-width="10"/>
<path d="M100 880 C150 760 60 640 150 540 C220 460 160 380 230 330 C270 300 300 320 290 350 C282 372 256 366 262 346" stroke="#7f9c7e" stroke-width="5"/>
<path d="M924 880 C880 780 960 660 880 560 C820 480 880 400 820 350 C786 322 752 338 762 364 C770 384 796 378 790 358" stroke-width="10"/>
<path d="M924 880 C880 780 960 660 880 560 C820 480 880 400 820 350 C786 322 752 338 762 364 C770 384 796 378 790 358" stroke="#7f9c7e" stroke-width="5"/>
<path d="M150 540 C110 520 96 480 120 456" stroke-width="6"/><path d="M880 560 C920 540 934 500 910 474" stroke-width="6"/></g>
<g id="leaves" fill="#7f9c7e" stroke="{ink}" stroke-width="3"><path d="M150 620 C110 600 96 560 104 540 C130 556 150 590 150 620Z"/><path d="M880 640 C920 620 934 580 926 560 C900 576 880 610 880 640Z"/><path d="M200 460 C230 430 270 430 290 440 C266 462 230 470 200 460Z"/></g>
<g id="lilies"><use href="#lily" x="124" y="470" width="80" height="90" transform="rotate(-20 164 520)"/><use href="#lily" x="820" y="490" width="80" height="90" transform="rotate(18 860 540)"/><use href="#lily" x="60" y="720" width="70" height="80" transform="rotate(-8 95 760)"/></g>
<g id="fox" stroke="{ink}" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<path d="{TAIL}" fill="#b5543c"/><path d="{TAIL_TIP}" fill="#f3e2d4"/><path d="{BODY}" fill="#c96a3f"/><path d="{HAUNCH}" fill="#b5543c"/>
<path d="{LEG2}" fill="#4a3a30"/><path d="{CHEST}" fill="#f3e2d4"/><path d="{LEG}" fill="#3a2e26"/>
<path d="{HEAD}" fill="#c96a3f"/><path d="{CHEEK}" fill="#f3e2d4"/><path d="{MUZZLE}" fill="#f3e2d4"/><path d="{EAR_L}" fill="#4a3a30"/><path d="{EAR_R}" fill="#4a3a30"/></g>
<g id="fur" fill="none" stroke="{ink}" stroke-width="3.5" stroke-linecap="round"><path d="M560 540 C600 560 620 600 610 640"/><path d="M640 600 C680 630 690 670 676 710"/><path d="M800 540 C840 600 850 660 830 720"/><path d="M860 600 C880 650 880 700 860 740"/></g>
<g id="face"><path d="{EYE}" fill="{ink}"/><circle cx="236" cy="488" r="10" fill="{ink}"/></g>
<g id="title-panel"><path d="M70 1000 V900 H954 V1000Z" fill="#2f4a45" stroke="{ink}" stroke-width="6"/><path d="{title}" fill="#e7c77d"/><path d="M150 950 C200 930 240 960 290 944 M734 944 C784 960 824 930 874 950" fill="none" stroke="#e7c77d" stroke-width="4" stroke-linecap="round"/></g>
<rect id="outer" x="24" y="24" width="976" height="976" fill="none" stroke="{ink}" stroke-width="8"/>
</g>
</svg>
""".format(**dict(globals(), ink=ink, rays=rays, ring=ring, title=title))
# ------------------------------------------------------------------------------------------
# LOW-POLY LANDSCAPE (non-fox): procedural mountains
# ------------------------------------------------------------------------------------------
def lowpoly_landscape(out):
import numpy as np
from scipy.spatial import Delaunay
rnd = random.Random(21)
W, H = 1600, 900
def ridge(x, base, amp, f, ph):
return base - amp * (0.6 * abs(math.sin(x * f + ph)) + 0.4 * abs(math.sin(x * f * 2.3 + ph * 1.7)))
layers = [(560, 300, .0042, .3), (660, 240, .0061, 1.9), (760, 160, .0083, 4.2)]
pts = [(x, y) for x in range(0, W + 1, 100) for y in (0, H)] + [(0, y) for y in range(0, H, 90)] + [(W, y) for y in range(0, H, 90)]
for x in range(0, W + 1, 30):
for (b, a, f, ph) in layers:
pts.append((min(W, max(0, x + rnd.uniform(-8, 8))), ridge(x, b, a, f, ph)))
for _ in range(900):
pts.append((rnd.uniform(0, W), rnd.uniform(0, H)))
pts = np.array(pts)
tri = Delaunay(pts)
sky_top, sky_bot = np.array([0x1b, 0x1f, 0x4b]), np.array([0xff, 0x9a, 0x76])
cols = [np.array([0x6b, 0x4f, 0x8f]), np.array([0x45, 0x3a, 0x74]), np.array([0x24, 0x22, 0x4f])]
out_p = []
for s in tri.simplices:
p = pts[s]; c = p.mean(0)
layer = None
for i, (b, a, f, ph) in enumerate(layers):
if c[1] > ridge(c[0], b, a, f, ph):
layer = i
if layer is None:
t = c[1] / 600
col = sky_top * (1 - t) + sky_bot * t
d = math.hypot(c[0] - 1050, c[1] - 430)
if d < 130:
col = np.array([0xff, 0xe0, 0x9e])
elif d < 260:
col = col * .6 + np.array([0xff, 0xc0, 0x80]) * .4
k = 1 + rnd.uniform(-.05, .05)
else:
col = cols[layer].astype(float)
v1, v2 = p[1] - p[0], p[2] - p[0]
n = np.array([v1[1] * 0 - 0, 0, 0])
slope = (p[:, 1].max() - p[:, 1].min()) / (p[:, 0].max() - p[:, 0].min() + 1)
side = 1 if (p[:, 0][p[:, 1].argmin()] < c[0]) else -1
k = 1 + side * .12 * min(1, slope) + rnd.uniform(-.06, .06)
if layer == 0 and c[1] < ridge(c[0], *layers[0]) + 40:
col = col * .5 + np.array([0xf7, 0xd6, 0xe0]) * .5
rgb = np.clip(col * k, 0, 255).astype(int)
hx = "#%02x%02x%02x" % tuple(rgb)
out_p.append('<polygon points="%s" fill="%s" stroke="%s"/>' % (" ".join("%.1f,%.1f" % tuple(v) for v in p), hx, hx))
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="t d">'
'<title id="t">Low Poly Dusk</title><desc id="d">Low poly landscape: three triangulated mountain ranges under a dusk sky with a faceted sun.</desc>'
'<g id="mesh" stroke-width=".8" stroke-linejoin="round">%s</g></svg>\n' % "".join(out_p))
# ------------------------------------------------------------------------------------------
# ISOMETRIC (non-fox): tiny island town
# ------------------------------------------------------------------------------------------
def isometric(out):
cx, cy, S = 800, 416, 36 # screen origin and cell size
def iso(x, y, z):
return (cx + (x - y) * S * 0.866, cy + (x + y) * S * 0.5 - z * S)
def box(x, y, w, d, h, top, left, right, z=0):
P = lambda a, b, c: "%.1f,%.1f" % iso(a, b, c)
t = '<polygon points="%s %s %s %s" fill="%s"/>' % (P(x, y, z + h), P(x + w, y, z + h), P(x + w, y + d, z + h), P(x, y + d, z + h), top)
l = '<polygon points="%s %s %s %s" fill="%s"/>' % (P(x, y + d, z), P(x + w, y + d, z), P(x + w, y + d, z + h), P(x, y + d, z + h), left)
r = '<polygon points="%s %s %s %s" fill="%s"/>' % (P(x + w, y, z), P(x + w, y + d, z), P(x + w, y + d, z + h), P(x + w, y, z + h), right)
return t + l + r
def roof(x, y, w, d, z, h, c1, c2):
P = lambda a, b, c: "%.1f,%.1f" % iso(a, b, c)
return ('<polygon points="%s %s %s %s" fill="%s"/>' % (P(x, y, z), P(x + w, y, z), P(x + w, y + d / 2, z + h), P(x, y + d / 2, z + h), c2) +
'<polygon points="%s %s %s %s" fill="%s"/>' % (P(x, y + d / 2, z + h), P(x + w, y + d / 2, z + h), P(x + w, y + d, z), P(x, y + d, z), c1) +
'<polygon points="%s %s %s" fill="%s"/>' % (P(x + w, y, z), P(x + w, y + d, z), P(x + w, y + d / 2, z + h), c2))
def windows(x, y, w, d, z0, h, col):
s = ""
for zz in range(int(z0) + 1, int(z0 + h), 1):
for i in range(int(w)):
a, b = iso(x + i + .3, y + d, zz - .1), iso(x + i + .7, y + d, zz - .1)
c, e = iso(x + i + .7, y + d, zz + .45), iso(x + i + .3, y + d, zz + .45)
s += '<polygon points="%.1f,%.1f %.1f,%.1f %.1f,%.1f %.1f,%.1f" fill="%s"/>' % (a + b + c + e + (col,))
return s
def tree(x, y):
tx, ty = iso(x, y, 0)
return ('<g transform="translate(%.1f %.1f)"><rect x="-4" y="-26" width="8" height="26" fill="#6b4b33"/>'
'<circle cy="-48" r="26" fill="#3f8f5a"/><circle cx="-8" cy="-56" r="14" fill="#62b27a"/></g>' % (tx, ty))
parts = []
parts.append(box(-7, -7, 14, 14, 1.2, "#8bd17c", "#5f9e56", "#4c8446", z=-2.2)) # island top
parts.append(box(-7, -7, 14, 14, 2.2, "#c9a26b", "#a57d4d", "#8a663c", z=-4.4)) # cliff
parts.append(box(-5.5, 1.5, 11, 2, .05, "#d8d2c4", "#bdb5a3", "#aaa190", z=-1)) # road
parts.append(box(1.5, -5.5, 2, 7, .05, "#d8d2c4", "#bdb5a3", "#aaa190", z=-1))
parts.append(box(-5, -5, 4, 4, 5, "#f2f0ea", "#d9d4c9", "#bdb6a8", z=-1) + windows(-5, -5, 4, 4, -1, 5, "#4a90c2"))
parts.append(box(-5.4, -5.4, 4.8, 4.8, .5, "#e76f51", "#c65a3f", "#a84a33", z=4))
parts.append(box(4, -5, 3, 3, 2, "#f4d58d", "#d9b56a", "#bf9a52", z=-1) + windows(4, -5, 3, 3, -1, 2, "#6b4b33"))
parts.append(roof(4, -5, 3, 3, 1, 1.4, "#e76f51", "#c65a3f"))
parts.append(box(-5, 4, 3, 2.5, 2, "#a8dadc", "#86b9bc", "#6e9fa2", z=-1) + windows(-5, 4, 3, 2.5, -1, 2, "#1d3557"))
parts.append(roof(-5, 4, 3, 2.5, 1, 1.2, "#457b9d", "#35627e"))
parts.append(box(4, 4, 2.5, 2.5, 3.5, "#ffb4a2", "#e5989b", "#c9797d", z=-1) + windows(4, 4, 2.5, 2.5, -1, 3.5, "#6d597a"))
parts.append(box(3.8, 3.8, 2.9, 2.9, .4, "#6d597a", "#574766", "#433652", z=2.5))
trees = "".join(tree(x, y) for x, y in [(-1, -6), (0.2, -3), (6.2, -1), (-6.3, 0.5), (-0.5, 5.8), (6.3, 1.2), (-2.4, -1.8)])
clouds = ('<g id="clouds" fill="#fff" opacity=".9"><ellipse cx="300" cy="170" rx="120" ry="34"/><ellipse cx="350" cy="148" rx="70" ry="40"/>'
'<ellipse cx="1280" cy="220" rx="140" ry="36"/><ellipse cx="1240" cy="196" rx="70" ry="40"/></g>')
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="t d">'
'<title id="t">Isometric Island</title><desc id="d">Isometric: 30-degree axonometric island town, three-tone faces from a single top-left light, no perspective.</desc>'
'<defs><linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#bfe3f2"/><stop offset="1" stop-color="#f6f1e7"/></linearGradient></defs>'
'<rect id="sky-bg" width="1600" height="900" fill="url(#sky)"/>' + clouds +
'<ellipse id="shadow" cx="800" cy="790" rx="470" ry="66" fill="#1d3557" opacity=".12"/>'
'<g id="island">' + "".join(parts) + '</g><g id="trees">' + trees + '</g></svg>\n')
# ------------------------------------------------------------------------------------------
PIXEL_SHIP = r"""; Pixel spaceship (mirrored). Build:
; python3 scripts/pixel_grid.py examples/src/ship-pixel.txt --mirror -o OUT --outline "#0b0b1a" --pad 4 --bg "#141433"
@w=#f2f2f2
@l=#9bd0ff
@b=#3a7bd5
@d=#1f3f8f
@r=#ff4d6d
@o=#ffb703
@y=#ffe66d
@g=#6c757d
@k=#2b2d42
...............w
..............ww
..............ll
.............lll
.............lbl
............wlbl
............wlbl
...........wwbbd
...........wbbbd
...........wbbbd
..........wwbbbd
.....r....wbbbdd
.....r...wwbbbdd
....rr...wbbbddw
....rr..wwbbddww
...rrr.wwgbbdwww
...rrrwwggbddwwg
..rrrrwggkbdwwgg
..rrrwwgkkbwwggg
.rrrrwggkkwwgggg
.rrrwwggkkggggkk
rrrrwgggkkggkkk.
rrr..ggkk..kk...
rr....oo....o...
......yo....y...
......y.........
"""
NAMES = {
"fox-low-poly": low_poly, "fox-risograph": riso, "fox-comic-book": comic, "fox-sticker": sticker,
"fox-sumi-e": sumie, "fox-horror": horror, "fox-bauhaus": bauhaus, "fox-ukiyo-e-darker": ukiyo_night,
"fox-art-nouveau": nouveau, "landscape-low-poly": lowpoly_landscape, "island-isometric": isometric,
}
if __name__ == "__main__":
out = Path(sys.argv[1] if len(sys.argv) > 1 else "."); out.mkdir(parents=True, exist_ok=True)
wanted = sys.argv[2:] or list(NAMES) + ["ship-pixel-art"]
for name in wanted:
if name == "ship-pixel-art":
src = HERE / "ship-pixel.txt"
src.write_text(PIXEL_SHIP, encoding="utf-8")
subprocess.run([sys.executable, str(SKILL / "scripts/pixel_grid.py"), str(src), "--mirror", "-o", str(out / "ship-pixel-art.svg"),
"--outline", "#0b0b1a", "--pad", "4", "--bg", "#141433", "--title", "Pixel starship"], check=True)
continue
(out / (name + ".svg")).write_text(NAMES[name](out), encoding="utf-8")
print("wrote", out / (name + ".svg"))
examples/src/ship-pixel.txt
; Pixel spaceship (mirrored). Build:
; python3 scripts/pixel_grid.py examples/src/ship-pixel.txt --mirror -o OUT --outline "#0b0b1a" --pad 4 --bg "#141433"
@w=#f2f2f2
@l=#9bd0ff
@b=#3a7bd5
@d=#1f3f8f
@r=#ff4d6d
@o=#ffb703
@y=#ffe66d
@g=#6c757d
@k=#2b2d42
...............w
..............ww
..............ll
.............lll
.............lbl
............wlbl
............wlbl
...........wwbbd
...........wbbbd
...........wbbbd
..........wwbbbd
.....r....wbbbdd
.....r...wwbbbdd
....rr...wbbbddw
....rr..wwbbddww
...rrr.wwgbbdwww
...rrrwwggbddwwg
..rrrrwggkbdwwgg
..rrrwwgkkbwwggg
.rrrrwggkkwwgggg
.rrrwwggkkggggkk
rrrrwgggkkggkkk.
rrr..ggkk..kk...
rr....oo....o...
......yo....y...
......y.........
README.md
# SVG Style Studio - `/create-svg`
A Claude Code skill that turns "draw X in style Y" into polished, editable, **genuine vector** SVG - then proves it looks right by validating, optimising, rendering and looking at the result.
- 44 styles across 10 families, each a full visual language (geometry, line, palette, texture, light, composition, typography) with machine-checkable conformance rules
- natural-language routing ("8 bit", "Japanese woodblock"), modifiers ("but darker"), blends ("cyberpunk + minimalist"), multi-style sets, and SVG conversion
- a 12-point QC pipeline backed by scripts (validator, optimiser, headless-Chromium renderer)
## Install
The skill's id is **`create-svg`**, so once installed anyone can type `/create-svg ...`.
- Claude Code, project: copy this folder to `<repo>/.claude/skills/create-svg/`
- Claude Code, personal: `~/.claude/skills/create-svg/`
- Claude apps: upload the zip of this folder as a skill
Requirements: Python 3.9+, `lxml`, `Pillow`; rendering needs `playwright` with Chromium (validate/optimise/route work without it, and the skill then reports that the visual check was not performed).
## Use
Type `/create-svg` followed by what you want (or just ask for SVG art - the skill triggers on its own):
```
/create-svg a fox in pixel art
/create-svg # asks what to draw + shows the style menu
A fox in pixel art
Ukiyo-e but darker: a heron by a river
cyberpunk + minimalist logo for a coffee brand
Create a lighthouse in 5 styles
Convert this SVG to cyberpunk
```
More in [`examples/example-prompts.md`](examples/example-prompts.md). No style given for a single artwork -> it shows a menu and asks.
## Layout
```
SKILL.md workflow + rules (kept short; loaded first)
scripts/
route_style.py NL -> mode, styles, modifiers, asset, blend plan, style brief(s); --menu --list --show --lint
validate_svg.py syntax, refs, viewBox, clipping, z-order, invisible, gradients/masks/filters, --style conformance
optimize_svg.py cruft strip, precision, path re-serialise, dedupe defs, <use>, hoist attrs, unused defs
render_svg.py headless-Chromium PNG, contact sheets, --analyze (coverage, colours, contrast, balance)
pixel_grid.py ASCII sprite -> crisp merged-rect SVG
text_to_path.py outline lettering from any .ttf/.otf/.woff2 (logos, posters: no font dependency)
convert_svg.py inspect | recolor | strokes | inject (convert mode)
new_style.py scaffold a new style
svgtools.py shared parsing / geometry helpers
styles/
registry/*.json one file per family; the source of truth for every style
references/*.md per-style recipes, layer stacks, QA checklists (anchors = style ids)
modifiers.json darker, minimalist, realistic, modern-palette, ...
reference/ composition.md, svg-craft.md, modes.md, qa-pipeline.md
examples/ outputs/ (finished SVGs + fox-styles.png), src/ (generators + pixel sprite), example-prompts.md
```
Progressive disclosure: `SKILL.md` -> router prints only the matched style briefs -> `styles/references/<family>.md#<id>` and `reference/*.md` are read when needed.
## Styles (44)
core-modern: minimal-modern, flat-design, geometric, monochrome, luxury-premium - pixel-retro: pixel-art, retro-8bit, retro-16bit - digital-glossy: cyberpunk-neon, y2k, frutiger-aero, glassmorphism, clay-soft-3d, isometric, low-poly - craft: paper-cutout, hand-drawn-doodle, sticker, childrens-illustration, editorial-illustration, organic-botanical - comics: comic-book, manga - east-asian: sumi-e, ukiyo-e - decorative-eras: art-nouveau, art-deco, bauhaus, brutalist, vintage-poster, medieval-manuscript, gothic - print: screen-print, risograph, graffiti-street-art - technical: blueprint, technical-illustration, scientific-illustration, infographic - genre: cinematic-poster, fantasy, sci-fi, horror, retro-futurism
## Add a style (3 steps)
```bash
# 1. scaffold registry entry + reference stub (TODO-filled)
python3 scripts/new_style.py --id vaporwave --name "Vaporwave" --family genre \
--alias "vapor wave" --alias "vaporwave aesthetic" --description "..." --best-for illustration,poster
python3 scripts/new_style.py --checks # list machine checks you can use
# 2. fill every TODO in styles/registry/genre.json and write the recipe in styles/references/genre.md
# (palette, geometry, stroke, texture, composition, svg_techniques, avoid, checks, compatible/conflicts)
# 3. lint - must report 0 errors
python3 scripts/route_style.py --lint
python3 scripts/route_style.py "vaporwave dolphin" --no-brief # confirm routing
```
Use `weak_aliases` for common words that should only match with style context (e.g. "luxury", "futuristic"). Add a modifier by appending a row to `styles/modifiers.json`.
## QC pipeline
```bash
python3 scripts/validate_svg.py art.svg --style <id> # errors must be 0; --strict fails on warnings too
python3 scripts/optimize_svg.py art.svg --in-place # --precision 0 for pixel grids
python3 scripts/render_svg.py art.svg -o preview.png -w 1024 --analyze # then LOOK at preview.png
```
Full checklist: `reference/qa-pipeline.md`.
## Examples (`examples/outputs/`)
**One fox, 15 styles** (same frozen scene spec, see `fox-styles.png`): `fox.svg` (flat base) · `fox-pixel-art` · `fox-ukiyo-e` · `fox-ukiyo-e-darker` (modifier demo) · `fox-cyberpunk-neon` · `fox-paper-cutout` · `fox-blueprint` · `fox-low-poly` · `fox-risograph` · `fox-comic-book` · `fox-sticker` · `fox-sumi-e` · `fox-horror` · `fox-bauhaus` · `fox-art-nouveau`
**Other formats** (see `showcase-more.png`):
| File | Style | Notes |
|---|---|---|
| `logo-art-deco-hotel.svg` | art-deco | logo; lettering outlined with `text_to_path.py` |
| `poster-vintage-highlands.svg` | vintage-poster | portrait poster, outlined title |
| `ship-pixel-art.svg` | pixel-art | mirrored sprite via `pixel_grid.py --mirror` |
| `island-isometric.svg` | isometric | generated axonometric boxes |
| `landscape-low-poly.svg` | low-poly | Delaunay mesh, facet shading |
Every file passes `validate_svg.py --style <id> --strict`. Generators: `examples/src/make_fox_set.py`, `make_showcase.py`, `make_logo_poster.py`, sprites `fox-pixel.txt`, `ship-pixel.txt`; fonts in `examples/src/fonts/` are OFL (Anton, Instrument Serif, Space Grotesk, Unbounded).
## Limitations (honest list)
- **Fonts are not embedded.** Live `<text>` uses the viewer's fonts; outline lettering with `scripts/text_to_path.py` (needs a font file + fontTools) for logos, posters and print. Wordmark widths are pinned with `textLength` so layout survives font substitution, but letterforms will differ.
- **The validator is static.** Bounding boxes are approximate (curves sampled, text estimated, `clipPath` extents handled for simple shapes only, filters' visual spread not modelled). Treat clipping warnings as hints and confirm on the render.
- **Rendering needs Playwright + Chromium.** Without it the visual inspection is not performed and the skill must say so. Other renderers (Inkscape, resvg) may differ on filters, `mix-blend-mode`, CSS variables and `textLength`.
- **Raster inputs are redrawn, not traced**; conversion of messy traced SVGs preserves structure only as far as the source has any.
- **Style quality is execution-dependent.** The registry and checks constrain and detect, but a convincing ukiyo-e or art nouveau still needs the drawing done with care; expect 1-3 QA iterations on complex subjects.
- Style checks catch technique violations (curves in pixel art, gradients in flat design), not taste.
- Trademarked characters, real people and living artists' specific work are declined in favour of original designs in the requested style.
reference/composition.md
# Composition intelligence
Decide composition **before** writing any SVG. Write a 6-10 line design brief (see the template at the bottom), then draw.
## 1. Pick the canvas
| Asset | viewBox | Notes |
|---|---|---|
| Icon | `0 0 64 64` or `0 0 24 24` | keep 2-4 % padding; stroke 2-3 units at 24 |
| Logo / mark | `0 0 512 512` | square; centre with optical correction; must work in one colour |
| Illustration / scene | `0 0 1200 900` (4:3) or `0 0 1024 1024` | choose 16:9 (`1600 900`) for cinematic |
| Character / sticker | `0 0 1024 1024` | transparent background |
| Poster | `0 0 900 1350` (2:3) | margins ≥ 6 % |
| Infographic | `0 0 1000 1400` (or as long as the content needs) | 12-column mental grid |
| Pixel art | grid size (`0 0 32 32`, `0 0 64 64`, `0 0 128 72`) | integer scaling only |
| Pattern tile | `0 0 200 200` | edges must wrap |
Never set fixed `width`/`height` on `<svg>` unless asked: `viewBox` alone keeps the file responsive.
## 2. Illustrations & scenes
- **Focal point**: one. Decide it, then put the highest contrast, the sharpest detail and the most saturated colour there.
- **Planes**: foreground (large, dark or framing), middle ground (subject), background (small, light, low contrast). State them in the brief.
- **Silhouette**: fill the subject with a single colour mentally - if it does not read, redesign the pose.
- **Balance**: asymmetric balance via scale/colour weight; keep the visual centre of mass within ~15 % of the centre unless deliberately off-balance.
- **Negative space**: reserve it on purpose (for text, for breathing, for the eye's path).
- **Hierarchy**: 1 hero, 2-3 supporting, everything else quiet. Cap the number of distinct scales.
- **Eye path**: lay out a route (leading lines, gaze direction, light path) from entry point → focal point.
- **Scale**: subject fills 55-75 % of the canvas (validator warns < 30 %).
## 3. Icons
- Optical alignment beats mathematical alignment (circles overshoot squares by ~3 %).
- One stroke weight, one corner radius, one cap/join policy.
- Padding ≥ 4 %; must remain recognisable at 24 px - render at that size to check.
- Silhouette recognition first, detail second. Test in solid black.
## 4. Logos
- Simple, memorable, geometric consistency (shared radii/angles, shared stroke).
- Use negative space deliberately (the classic hidden-shape idea).
- Monochrome must work: check by rendering with `convert_svg.py recolor --grayscale`.
- Convert text to paths (fonts are not embedded) and keep the mark ≤ 3 colours.
- Deliver the mark alone plus optional lock-up with wordmark; both scalable.
## 5. Posters
- Hierarchy: **title → focal image → tagline → supporting info**. Title ≥ 2× the size of the second level.
- Margins ≥ 6 %; align text to a shared edge or the centre axis.
- Rhythm: repeat a shape/colour at three scales to move the eye.
- Cinematic: low horizon or dramatic diagonal, light source that creates a path, foreground framing.
- Text is `<text>` with generic fallbacks *or* outlined paths when exact typography matters - say which in the reply.
## 6. Infographics
- Reading order first (Z or F pattern), then modules on a grid.
- Group by proximity, separate with whitespace, not lines.
- Consistent visual grammar: one icon style, one chart style, one label style.
- Compute geometry from data; label everything; never fabricate numbers (use clearly marked placeholder data).
## 7. Design-brief template (write it, keep it in your head or in a comment)
```
SUBJECT : red fox, seated, three-quarter view, looking left
STYLE : ukiyo-e (primary) MODIFIERS: darker
CANVAS : 0 0 1200 900, subject fills ~60%, focal point at (600, 380)
PLANES : fg = cropped pine branch top-right / mid = fox on rock / bg = Fuji + bokashi sky
LIGHT : no cast light; contrast via indigo vs cream fur
PALETTE : cream #efe3c8, prussian #1f3c6e, vermilion #c8372d, ochre #d9a441, sumi #1c1a19
DETAILS : seigaiha water pattern, cartouche top-left with red seal
LAYERS : paper > colour-blocks > bokashi > patterns > key-block > texture > cartouche
CHECKS : validate --style ukiyo-e ; render 1024 ; inspect focal point + edges
```
reference/modes.md
# Modes, blending & modifiers
Everything here is driven by `scripts/route_style.py "<user request>"`, which returns `mode`, `styles`, `modifiers`, `blend_plan`, `source`, `asset`, `file_names`.
## 0. Style selection protocol
1. Run the router. Trust an explicit style match.
2. `needs_style: true` → **do not choose for the user.** Show the concise menu (`route_style.py --menu`) and ask one question. (Multi-style mode is the exception: "5 styles" without names means *you* pick a contrasting set and say which.)
3. `confirm_style: true` (only a generic word matched, e.g. "luxury hotel") → one-line confirmation, offering the menu.
3b. `ambiguous: true` (e.g. "Japanese style" → Ukiyo-e / Sumi-e / Manga) → ask which, offering just the candidates.
4. A **list of 3+ style names** separated by commas / "and" / "or" / "vs" is read as multi-style; blends need `+`, `with`, `meets`, or similar, and are capped at 3.
5. Two aliases may share a phrase (e.g. "technical schematic" → Blueprint + Technical Illustration). That is intentional: treat as a blend (first = primary).
## 1. Single-style mode
Design brief → build → QA pipeline (`qa-pipeline.md`). Read the routed style brief AND its reference section (`styles/references/<file>#<id>`) before drawing.
## 2. Blending styles ("cyberpunk + minimalist", "pixel art with a horror aesthetic")
Ownership model - the router prints this as `blend_plan`:
| Channel | Owner |
|---|---|
| Geometry, stroke rules, composition, typography, level of detail, **hard constraints** | **Primary** |
| Palette character, lighting, texture, atmosphere, motifs, decorative vocabulary | **Secondary** (adapted to fit primary's limits) |
| Detail level / negative space (when secondary is a *reduction* style: minimal, monochrome, geometric, flat, luxury) | Secondary as a modifier (`as_secondary` in the registry) |
Rules
1. **Primary's hard checks survive.** Pixel art + horror = still integer grid, no blur, ≤ 24 colours; horror arrives through palette, silhouette, dithered fog, composition.
2. **Emulate, don't import, forbidden techniques.** Pixel art forbids filters → build glow as stepped concentric rings.
3. **Budget palettes**, don't add: secondary hues replace, not join, the primary's set.
4. **Sanity test**: cover the palette → still reads as the primary; cover the drawing → the mood reads as the secondary.
5. A declared **conflict** (registry `conflicts_with`) is allowed if the user asked for it, but say what you dropped ("kept ukiyo-e flat blocks; dropped glassmorphism's blur").
6. Max 3 styles. More than that = mush; ask which two matter most.
7. Validate against the **primary** only: `validate_svg.py out.svg --style <primary>`.
Style ↔ modifier examples
| Request | Primary | Secondary / modifier | What to do |
|---|---|---|---|
| cyberpunk + minimalist | Cyberpunk Neon | Minimal (reduction) | dark base, 3-4 shapes, ONE neon edge with glow, ≥ 40 % negative space |
| Ukiyo-e but darker | Ukiyo-e | `darker` | night palette: deeper indigo blocks, moon/lantern accents; flat blocks + key-block unchanged |
| pixel art with a horror aesthetic | Pixel Art | Horror | grid intact; sickly ramps, one red accent, heavy shadow, half-hidden forms |
| Art Nouveau with a modern palette | Art Nouveau | `modern-palette` | curves/ornament identical; contemporary base + 1-2 saturated accents |
| blueprint + sci-fi | Blueprint | Sci-Fi | technical sheet layout; subject is a starship in orthographic views |
| comic book but realistic | Comic Book | `realistic` | inks/halftone stay; anatomy + lighting realistic |
## 3. Modifiers
The router lists matched modifiers with an `effect`. Apply each effect **on top of** the style; never let a modifier delete the style's signature technique.
Vocabulary lives in `styles/modifiers.json` (add rows freely: `id`, `triggers`, `effect`).
## 4. Multi-style mode ("Create this in 5 styles")
Goal: comparable outputs. The *only* thing that changes is style.
1. **Freeze a scene spec** before drawing anything (write it in your notes, or as `<name>.spec.txt` in the output folder):
```
subject : red fox, seated, 3/4 view facing left, tail wrapped around feet
canvas : 0 0 1024 1024
anchor : head centre (512, 380), body base y=820, subject bbox ≈ 200..830 x 140..860
props : pine branch top-right, small moon top-left
```
2. If a base illustration already exists (`fox.svg`), keep its geometry as the *layout reference*.
3. For each style: read its brief, redraw/transform to the **same anchors**, respecting that style's rules. Same pose, same crop, same props (translated into each style's vocabulary).
4. Naming: `<subject>.svg` (base or neutral flat version, optional) + `<subject>-<style-id>.svg` for each variant (`fox-pixel-art.svg`, `fox-ukiyo-e.svg` …). The router prints `file_names`.
5. Validate each with its own `--style`, render all, then build a comparison sheet:
`render_svg.py fox-*.svg --sheet fox-styles.png --cols 3`
6. Inspect the sheet: same subject recognisable in all? similar scale/position? styles clearly different from each other? Fix outliers.
7. No styles listed → the router chooses contrasting families (`multi_styles`). Say which you picked and why (one line).
## 5. Convert mode ("Convert this SVG to cyberpunk")
Decide **transform vs rebuild**:
| Input | Approach |
|---|---|
| SVG with clean structure (named groups, few hundred shapes) | **Transform in place** - preserve geometry/composition |
| SVG that is a flat trace / thousands of tiny paths | Transform palette + add layers; if the style needs different geometry (pixel, low-poly, isometric) rebuild from the *subject spec* extracted from a render |
| Raster (PNG/JPG/photo) | **Never embed it.** View the image, write a subject spec, and redraw as native vector in the target style |
| Style needs different geometry (pixel art, isometric, low poly) | Rebuild (recolouring cannot get there) |
Transform pipeline (all in `scripts/convert_svg.py`)
1. `inspect in.svg` → palette (dark→light), stroke widths, named groups, gradients/filters.
2. Plan the transformation from the style brief: palette · stroke treatment · lighting · texture · effects · decoration.
3. `recolor` - `--palette "<dark→light hexes>"` remaps by luminance rank (keeps light/dark structure); `--map` for hero colours; `--darken/--lighten/--saturate/--hue-shift` for modifiers.
4. `strokes` - `--linecap/--linejoin`, `--width-scale`, `--outline "#111:3"` (adds outlines to fills lacking them; skips the background), `--remove` for flat styles.
5. `inject` - `--wrap-id subject` (so filters target the art), `--drop-background` (otherwise the old background hides new underlays), `--defs` (filters/patterns/gradients), `--underlay` (new sky/grid), `--overlay` (scanlines/grain/vignette), `--set "#subject:filter=url(#glow)"`.
6. Validate + render; compare against the original (`render_svg.py in.svg out.svg --sheet cmp.png`); verify composition and subject are preserved.
7. Report what was preserved vs changed in one short paragraph.
Fragment files for `inject` are plain SVG elements (no `<svg>` wrapper). Keep style-specific snippets small and reusable; several are in `styles/references/*.md`.
## 6. Ambiguity cheatsheet
- "make this look like a vintage poster" with an *image* → convert mode, raster → redraw.
- "in the style of <named living artist>" → do not imitate a specific living artist's work; offer the closest movement/technique (e.g. flat colour blocks + halftone) instead.
- Trademarked characters/logos → do not reproduce; offer an original character in the requested style.
reference/qa-pipeline.md
# Quality-control pipeline
Run this loop after **every** SVG you write. Passing the parser is not "done" - visual quality is.
```
write → validate → optimise → validate again → render → LOOK → fix → (repeat) → deliver
```
## Commands
```bash
S=.claude/skills/create-svg/scripts # adjust to where the skill lives
python3 $S/validate_svg.py art.svg --style <style-id> # 1-10 (+ style conformance)
python3 $S/optimize_svg.py art.svg --in-place # 10 (add --precision 0 for pixel art)
python3 $S/validate_svg.py art.svg --style <style-id> --quiet # re-check after optimisation
python3 $S/render_svg.py art.svg -o /tmp/preview.png -w 1024 --analyze # 11
# then open /tmp/preview.png with the Read tool and LOOK at it # 12
```
Sheets: `render_svg.py a.svg b.svg c.svg --sheet compare.png --cols 3`.
## The 12 checks
| # | Check | How |
|---|---|---|
| 1 | Valid SVG syntax | `validate_svg` (`xml-syntax`, `xmlns`, `root`) |
| 2 | Broken references | `broken-url-ref`, `broken-href`, `duplicate-id` |
| 3 | `viewBox` | `viewbox` (missing/invalid → error) |
| 4 | Important content not clipped | `clipped`, `off-canvas`, `tight-margin`, `small-subject`; render `edge_touch` |
| 5 | Layering / z-order | `z-background`, `z-cover`, `z-hidden` (incl. text hidden behind shapes) |
| 6 | Accidental invisible objects | `invisible`, `zero-size` |
| 7 | Gradients, masks, filters work | `gradient-stops`, `empty-clippath`, `empty-mask`, `empty-filter`, `filter-input`, `filter-region` |
| 8 | Matches the requested style | `--style <id>` (style checks) **and your eyes against the brief's checklist** |
| 9 | Balance & spacing | stats `content_margins_pct`, `weight_offset_pct`, render `ink_balance_*`, plus looking |
| 10 | Optimised | `optimize_svg.py`; validator `precision`, `duplicate-geometry`, `huge-path`, `size` |
| 11 | Preview rendered | `render_svg.py` (headless Chromium) |
| 12 | Rendered result inspected | Read the PNG; use the checklist below |
Severity: **error** = must fix. **warn** = fix unless you can justify it (say why in the reply). **info** = consider.
## Looking at the render - what to check
Take the PNG and answer honestly:
1. **Squint test**: blur your eyes - is there one clear focal point and a readable silhouette?
2. **Style test**: would someone name the style without being told? Check the brief's *signature techniques* are all visibly present (e.g. ukiyo-e: flat blocks + key-block outline + pattern + cartouche; pixel art: crisp grid, no soft edges; cyberpunk: dark base + glow on hero edges).
3. **Clipping**: nothing cut by the canvas unless it is deliberate bleed.
4. **Edges/quality**: jagged joins, gaps between shapes, wobbly geometry that should be exact, mis-aligned mirrored halves.
5. **Balance**: is the weight sensible? are margins even? is there dead space that looks accidental?
6. **Legibility**: text readable at the intended size; contrast OK.
7. **Polish**: does it look *designed* or *assembled*? Look for the "few rectangles and circles" trap - add layered detail, secondary forms, texture appropriate to the style.
Render at more than one size when relevant (`-w 128` for icons/logos, `-w 1600` for detail).
## Analysis fields (`--analyze`)
`blank` (nothing drawn) · `alpha_coverage_pct` · `content_bbox_pct` / `edge_touch` (transparent art) · `subject_bbox_pct` / `subject_edge_touch` / `subject_fill_pct` (full-bleed art) · `rendered_colors`, `top32_share_pct` (≈100 for crisp pixel art; lower means anti-aliasing leaks) · `mean_luma`, `contrast` (is it as dark/high-contrast as the style demands?) · `ink_balance_lr_pct` / `_tb_pct` (0 = balanced).
## Iteration rules
- Fix in priority: errors → clipping/z-order → style mismatch → composition → polish.
- After each fix re-run validate + render. Stop when: 0 errors, no unexplained warnings, the render passes the looking checklist, and you would be comfortable putting it in a portfolio.
- Max ~4 loops per file; if still off, say precisely what remains and why (don't loop forever).
- If Chromium/rendering is unavailable: say so, rely on validator + careful reasoning, and mark the visual inspection as **not performed**.
## Common defects → fixes
| Defect | Fix |
|---|---|
| `dark-base` / `light-base` warning | change the background/base fill; dark styles need a dark first layer |
| `min-gradients`, `min-filters` | add the style's signature lighting (glow, gloss, metal) |
| `forbid-*` | replace the technique with the style-legal alternative (see the brief) |
| `z-hidden` (info) | remove wasted shape or reorder |
| `small-subject` | tighten the viewBox or scale the subject group |
| pixel art `curves` / `grid` | rebuild from ASCII via `pixel_grid.py` |
| Too plain | add mid-ground detail, secondary forms, pattern/texture, atmosphere, rim light, props - as the style dictates |
reference/svg-craft.md
# SVG craft rules
Every output is genuine, editable, scalable vector. No `<image>` with raster data, ever.
## Skeleton
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 900" role="img" aria-labelledby="t d">
<title id="t">Red fox on a rock</title>
<desc id="d">Ukiyo-e style illustration of a fox in front of Mount Fuji.</desc>
<defs>
<!-- gradients, patterns, filters, clipPaths, masks, symbols: defined ONCE -->
</defs>
<g id="background">…</g>
<g id="scene">
<g id="far">…</g><g id="mid">…</g>
</g>
<g id="subject">…</g>
<g id="details">…</g>
<g id="overlay">…</g> <!-- texture, vignette, scanlines: last -->
</svg>
```
- `xmlns` always; `viewBox` always; **no fixed width/height** (responsive).
- `<title>` (2-4 word name) + optional `<desc>`.
- Named groups (`id=`) for every semantic part: `background`, `subject`, `ink`, `neon-signs`, `key-block` … The optimiser keeps them.
- Layer order = paint order: background → far → mid → subject → details → overlay.
## Reuse
- Anything appearing ≥ 3 times becomes a `<symbol>`/`<g id>` in `<defs>` and is instanced with `<use href="#id">`. Per-instance differences go on the `<use>` (`transform`, `fill` if the source leaves fill unset, CSS variables).
- Palette as CSS custom properties (`--ink`, `--accent`) *when the style benefits from recolouring*; otherwise plain hex. Remember some converters ignore CSS variables.
- Symmetry: author half, mirror with `<use transform="matrix(-1 0 0 1 W 0)">`.
## Element choice
- Prefer `<path>`, `<rect>`, `<circle>`, `<ellipse>`, `<polygon>`, `<polyline>`, `<line>`.
- Use the simplest primitive that gives the shape (`<circle>` beats a 4-arc path; `<rect rx>` beats a hand-drawn rounded path).
- Paths: absolute commands for readability, relative for compactness in the optimiser; smooth joins need mirrored control handles; use `A` arcs for true circles/pointed arches; close shapes with `Z`.
- Path budget: ≤ ~2000 chars per path in hand-authored art; split by meaning, not by size.
- Precision: 1-2 decimals for a 1000-unit canvas (`optimize_svg.py --precision 2`), 0 for pixel grids.
## Paint servers & effects
| Need | Preferred technique | Filter needed? |
|---|---|---|
| Soft light/glow | `radialGradient` with opacity stops | no |
| Neon halo | second wider stroke at low opacity, or `feGaussianBlur` on a group | only for real blur |
| Drop shadow | offset dark copy with low opacity | only if soft edge required |
| Texture | `<pattern>` tile | no (unless organic noise: `feTurbulence`) |
| Fade-out | `<mask>` with a gradient | no |
| Trim/shape | `<clipPath>` | no |
| Gloss | clipped shape + gradient | no |
Filter rules
- **Do not use a filter when a gradient/stack of shapes gives the same result.**
- Define each filter once; apply to *groups*; set `x/y/width/height` (e.g. `-30% / 160%`) so blurs are not clipped; `color-interpolation-filters="sRGB"` for predictable glow colour.
- Budget: ≤ 3 distinct filters, ≤ ~12 filtered elements.
- `feTurbulence` textures: keep `numOctaves ≤ 3`, apply to one overlay rect at low opacity.
Masks vs clip-paths: clip-path = hard shape trim; mask = luminance/alpha-controlled fade. Both must contain at least one shape (validator errors on empty ones).
Blend modes: `mix-blend-mode: multiply` is fine in browsers but not portable - provide explicit overlap fills for print styles (screen print, riso).
## Text
- Fonts are **not embedded**. Always give a generic fallback (`serif`, `sans-serif`, `monospace`).
- Logos, posters with exact type, and anything for print: convert to paths with `scripts/text_to_path.py` (`--glyphs` gives one path per letter inside `<g aria-label="TEXT">`, which the validator counts as typography), or tell the user text is live and which fonts are assumed.
- Text sits after the art it lives on (z-order) and never under an opaque shape.
## Structure hygiene (what `optimize_svg.py` enforces)
- no comments/editor metadata, no empty groups/defs, no unused defs
- duplicate gradients/filters merged; repeated long paths → `<use>`
- shared presentation attributes hoisted into groups
- bare `<g>` wrappers unwrapped; **named groups preserved**
## Common failure modes
| Symptom | Cause | Fix |
|---|---|---|
| Blank/partial render | missing `viewBox`, broken `url(#id)`, empty `<clipPath>` | run `validate_svg.py` |
| Glow cut off in a box | filter region too small | widen `x/y/width/height` |
| Gradient invisible | `gradientUnits` mismatch or zero-size bbox (horizontal line + objectBoundingBox) | use `userSpaceOnUse` |
| Hairline gaps between tiles | anti-aliased seams | overlap 0.2-0.5 units or same-colour hairline stroke |
| Blurry pixel art | non-integer coords / missing `crispEdges` | integer grid, `shape-rendering="crispEdges"` |
| Text wrong font | font not installed | outline text or use generic fallbacks |
| Huge file | thousands of tiny paths, 6-decimal numbers | simplify, `optimize_svg.py` |
scripts/convert_svg.py
#!/usr/bin/env python3
"""convert_svg.py - transform an EXISTING SVG into another style without
rebuilding it (Style Preservation mode).
Sub-commands
inspect IN.svg structure/palette/stroke report (JSON with --json)
recolor IN.svg -o OUT.svg ... palette transform
--palette "#0b0221,#ff2a6d,#05d9e8,#d1f7ff" remap by luminance rank (dark->light)
--map "#aabbcc=#112233,#fff=#000" explicit colour swaps (applied first)
--darken 0.25 | --lighten 0.2 HSL lightness shift (-1..1 via sign)
--saturate 1.3 saturation multiplier
--hue-shift 20 degrees
--grayscale drop saturation entirely
strokes IN.svg -o OUT.svg ... line treatment
--width-scale 1.5 | --set-width 3
--linecap round|butt|square --linejoin round|miter|bevel
--outline "#111111:3" add a stroke to every filled shape that has none
--remove remove all strokes (flat / cut-paper look)
inject IN.svg -o OUT.svg ... add style layers to the existing art
--defs FRAG.xml elements appended to <defs> (filters, patterns, gradients)
--underlay FRAG.xml elements inserted BEFORE the artwork (background, sky, grid)
--overlay FRAG.xml elements appended AFTER the artwork (scanlines, grain, vignette)
--set "#subject:filter=url(#glow)" set an attribute on a selector (#id, .class or tag)
--wrap-id subject wrap all original artwork in <g id="subject"> (so filters/masks can target it)
--drop-background remove the original full-canvas background rect(s) (else they hide the underlay)
--viewbox "0 0 W H" change canvas (e.g. to add a border)
Fragments are plain SVG elements (no <svg> wrapper needed).
Typical conversion ("make this cyberpunk"):
inspect -> recolor --palette ... -> strokes --linecap round --width-scale 0.8
-> inject --wrap-id subject --defs glow.xml --underlay bg.xml --overlay scan.xml
--set "#subject:filter=url(#neon)"
then validate_svg.py + render_svg.py.
"""
from __future__ import annotations
import argparse
import colorsys
import copy
import json
import re
import sys
from collections import Counter
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import ( # noqa: E402
SVG_NS, SHAPE_TAGS, StyleResolver, fnum, hex_to_hsl, iter_elements, local,
luminance, norm_color, parse_svg, parse_style_attr, parse_viewbox, walk_painted, ids_map,
)
from lxml import etree # noqa: E402
COLOR_PROPS = ("fill", "stroke", "stop-color", "flood-color", "lighting-color", "color")
def load(path):
root, err = parse_svg(path)
if root is None:
sys.exit("cannot parse %s: %s" % (path, err))
return root
def save(root, out):
etree.cleanup_namespaces(root)
etree.indent(root, space=" ")
Path(out).write_text(etree.tostring(root, encoding="unicode") + "\n", encoding="utf-8")
print("wrote", out)
def hex_from_hsl(h, s, l):
r, g, b = colorsys.hls_to_rgb((h % 360) / 360.0, max(0, min(1, l)), max(0, min(1, s)))
return "#%02x%02x%02x" % (round(r * 255), round(g * 255), round(b * 255))
def color_slots(root):
"""Yield (element, kind, name, raw_value) for every colour-bearing attribute/style entry."""
for el in iter_elements(root):
for prop in COLOR_PROPS:
v = el.get(prop)
if v is not None:
yield el, "attr", prop, v
st = el.get("style")
if st:
for k, v in parse_style_attr(st).items():
if k in COLOR_PROPS:
yield el, "style", k, v
if local(el.tag) == "style" and el.text:
for m in re.finditer(r"(fill|stroke|stop-color|color)\s*:\s*(#[0-9a-fA-F]{3,8}|rgb[a]?\([^)]*\)|[a-zA-Z]+)", el.text):
yield el, "css", m.group(1), m.group(2)
def set_slot(el, kind, name, old, new):
if kind == "attr":
el.set(name, new)
elif kind == "style":
d = parse_style_attr(el.get("style"))
d[name] = new
el.set("style", ";".join("%s:%s" % kv for kv in d.items()))
elif kind == "css":
el.text = re.sub(r"(%s\s*:\s*)%s" % (re.escape(name), re.escape(old)), lambda m: m.group(1) + new, el.text)
def is_canvas_rect(el, vb, frac=0.95):
"""True for a <rect> that covers (almost) the whole viewBox - i.e. a background."""
if local(el.tag) != "rect" or vb is None:
return False
x, y = fnum(el.get("x")), fnum(el.get("y"))
w, h = fnum(el.get("width")), fnum(el.get("height"))
return w >= frac * vb[2] and h >= frac * vb[3] and x <= vb[0] + 0.03 * vb[2] and y <= vb[1] + 0.03 * vb[3]
# ---------------------------------------------------------------- inspect
def cmd_inspect(a):
root = load(a.file)
ids = ids_map(root)
res = StyleResolver(root)
tags = Counter(local(e.tag) for e in iter_elements(root))
usage = Counter()
for el, kind, name, v in color_slots(root):
c = norm_color(v)
if c:
usage[c] += 1
stroke_w = Counter()
for el, m in walk_painted(root, ids):
if (res.get(el, "stroke") or "none") != "none":
stroke_w[round(fnum(res.get(el, "stroke-width", "1"), 1), 2)] += 1
palette = [{"color": c, "uses": n, "luminance": round(luminance(c), 3),
"hue": round(hex_to_hsl(c)[0]), "sat": round(hex_to_hsl(c)[1], 2)} for c, n in usage.most_common()]
palette.sort(key=lambda p: p["luminance"])
groups = [{"id": g.get("id"), "children": len([c for c in g if isinstance(c.tag, str)])}
for g in root.iter("{%s}g" % SVG_NS) if g.get("id")]
info = {
"viewBox": parse_viewbox(root),
"elements": dict(tags),
"named_groups": groups,
"palette_dark_to_light": palette,
"stroke_widths": dict(stroke_w),
"gradients": tags.get("linearGradient", 0) + tags.get("radialGradient", 0),
"filters": tags.get("filter", 0),
"patterns": tags.get("pattern", 0),
"text": ["".join(t.itertext()).strip() for t in root.iter("{%s}text" % SVG_NS)][:10],
}
if a.json:
print(json.dumps(info, indent=2))
return
print("viewBox:", info["viewBox"])
print("elements:", ", ".join("%s=%d" % kv for kv in sorted(tags.items())))
print("named groups:", ", ".join("#%s(%d)" % (g["id"], g["children"]) for g in groups) or "none (consider --wrap-id when injecting)")
print("stroke widths:", dict(stroke_w) or "none (fill-only art)")
print("gradients=%d filters=%d patterns=%d" % (info["gradients"], info["filters"], info["patterns"]))
print("palette (dark -> light):")
for p in palette:
print(" %s L=%.3f hue=%3d sat=%.2f x%d" % (p["color"], p["luminance"], p["hue"], p["sat"], p["uses"]))
# ---------------------------------------------------------------- recolor
def cmd_recolor(a):
root = load(a.file)
slots = list(color_slots(root))
mapping = {}
if a.map:
for kv in a.map.split(","):
k, v = kv.split("=", 1)
nk = norm_color(k.strip())
if nk is None:
sys.exit("cannot parse colour %r in --map" % k)
mapping[nk] = v.strip()
usage = Counter()
for _e, _k, _n, v in slots:
c = norm_color(v)
if c:
usage[c] += 1
if a.palette:
pal = [norm_color(x.strip()) for x in a.palette.split(",")]
if any(p is None for p in pal):
sys.exit("bad colour in --palette")
pal.sort(key=luminance)
pal_l = [luminance(p) for p in pal]
unmapped = [c for c in usage if c not in mapping]
lums = sorted(luminance(c) for c in unmapped)
# weight by usage so quantiles reflect how much of the art is dark/light
weighted = sorted(((luminance(c), usage[c]) for c in unmapped))
total = sum(w for _l, w in weighted) or 1
run = 0.0
rank = {}
for l, w in weighted:
rank[l] = (run + w / 2) / total
run += w
for c in unmapped:
q = rank[luminance(c)]
# nearest palette colour by quantile position
idx = min(len(pal) - 1, int(q * len(pal)))
mapping[c] = pal[idx]
adjust = any(x is not None for x in (a.darken, a.lighten, a.saturate, a.hue_shift)) or a.grayscale
for el, kind, name, v in slots:
c = norm_color(v)
if not c:
continue
new = mapping.get(c, c)
if adjust:
nc = norm_color(new) or c
h, s, l = hex_to_hsl(nc)
if a.darken:
l = l * (1 - a.darken)
if a.lighten:
l = l + (1 - l) * a.lighten
if a.saturate is not None:
s = min(1.0, s * a.saturate)
if a.hue_shift:
h += a.hue_shift
if a.grayscale:
s = 0
new = hex_from_hsl(h, s, l)
if new != v:
set_slot(el, kind, name, v, new)
save(root, a.out)
# ---------------------------------------------------------------- strokes
def cmd_strokes(a):
root = load(a.file)
res = StyleResolver(root)
ids = ids_map(root)
outline = None
if a.outline:
col, _, w = a.outline.partition(":")
outline = (col, w or "2")
n = 0
vb = parse_viewbox(root)
for el, _m in walk_painted(root, ids):
if local(el.tag) not in SHAPE_TAGS:
continue
if outline and is_canvas_rect(el, vb):
continue # never outline the background
has_stroke = (res.get(el, "stroke") or "none") != "none"
if a.remove and has_stroke:
for k in ("stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-dasharray"):
if k in el.attrib:
del el.attrib[k]
el.set("stroke", "none")
n += 1
continue
if not has_stroke:
if outline and (res.get(el, "fill", "black") or "black") != "none":
el.set("stroke", outline[0])
el.set("stroke-width", outline[1])
has_stroke = True
n += 1
else:
continue
if a.set_width is not None:
el.set("stroke-width", str(a.set_width))
elif a.width_scale is not None:
el.set("stroke-width", ("%.3f" % (fnum(res.get(el, "stroke-width", "1"), 1) * a.width_scale)).rstrip("0").rstrip("."))
if a.linecap:
el.set("stroke-linecap", a.linecap)
if a.linejoin:
el.set("stroke-linejoin", a.linejoin)
n += 1
print("touched %d shapes" % n)
save(root, a.out)
# ---------------------------------------------------------------- inject
def load_fragment(path):
text = Path(path).read_text(encoding="utf-8")
wrapped = '<svg xmlns="%s" xmlns:xlink="http://www.w3.org/1999/xlink">%s</svg>' % (SVG_NS, text)
frag, err = parse_svg(wrapped)
if frag is None:
sys.exit("bad fragment %s: %s" % (path, err))
return list(frag)
def select(root, sel):
if sel.startswith("#"):
return [e for e in iter_elements(root) if e.get("id") == sel[1:]]
if sel.startswith("."):
return [e for e in iter_elements(root) if sel[1:] in (e.get("class") or "").split()]
return [e for e in iter_elements(root) if local(e.tag) == sel]
def cmd_inject(a):
root = load(a.file)
if a.viewbox:
root.set("viewBox", a.viewbox)
if a.drop_background:
vb = parse_viewbox(root)
dropped = 0
for el in list(iter_elements(root)):
par = el.getparent()
if par is not None and is_canvas_rect(el, vb) and not any(local(x.tag) in ("defs", "clipPath", "mask", "pattern") for x in el.iterancestors()):
par.remove(el)
dropped += 1
print("dropped %d canvas-sized background rect(s)" % dropped)
art_start = 0
for i, ch in enumerate(root):
if isinstance(ch.tag, str) and local(ch.tag) in ("title", "desc", "defs", "metadata"):
art_start = i + 1
if a.wrap_id:
wrapper = etree.Element("{%s}g" % SVG_NS)
wrapper.set("id", a.wrap_id)
movers = [ch for ch in root if isinstance(ch.tag, str) and local(ch.tag) not in ("title", "desc", "defs", "metadata", "style")]
first = list(root).index(movers[0]) if movers else len(root)
for m in movers:
wrapper.append(m)
root.insert(first, wrapper)
if a.defs:
defs = next((c for c in root if isinstance(c.tag, str) and local(c.tag) == "defs"), None)
if defs is None:
defs = etree.Element("{%s}defs" % SVG_NS)
root.insert(art_start if art_start else 0, defs)
for el in load_fragment(a.defs):
defs.append(el)
if a.underlay:
# after title/desc/defs, before artwork
idx = 0
for i, ch in enumerate(root):
if isinstance(ch.tag, str) and local(ch.tag) in ("title", "desc", "defs", "metadata", "style"):
idx = i + 1
for off, el in enumerate(load_fragment(a.underlay)):
root.insert(idx + off, el)
if a.overlay:
for el in load_fragment(a.overlay):
root.append(el)
for spec in a.set or []:
sel, _, kv = spec.partition(":")
k, _, v = kv.partition("=")
targets = select(root, sel)
if not targets:
print("warning: selector %r matched nothing" % sel, file=sys.stderr)
for t in targets:
t.set(k, v)
save(root, a.out)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
sub = ap.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("inspect")
p.add_argument("file")
p.add_argument("--json", action="store_true")
p.set_defaults(fn=cmd_inspect)
p = sub.add_parser("recolor")
p.add_argument("file")
p.add_argument("-o", "--out", required=True)
p.add_argument("--palette")
p.add_argument("--map")
p.add_argument("--darken", type=float)
p.add_argument("--lighten", type=float)
p.add_argument("--saturate", type=float)
p.add_argument("--hue-shift", type=float, dest="hue_shift")
p.add_argument("--grayscale", action="store_true")
p.set_defaults(fn=cmd_recolor)
p = sub.add_parser("strokes")
p.add_argument("file")
p.add_argument("-o", "--out", required=True)
p.add_argument("--width-scale", type=float, dest="width_scale")
p.add_argument("--set-width", type=float, dest="set_width")
p.add_argument("--linecap", choices=["round", "butt", "square"])
p.add_argument("--linejoin", choices=["round", "miter", "bevel"])
p.add_argument("--outline")
p.add_argument("--remove", action="store_true")
p.set_defaults(fn=cmd_strokes)
p = sub.add_parser("inject")
p.add_argument("file")
p.add_argument("-o", "--out", required=True)
p.add_argument("--defs")
p.add_argument("--underlay")
p.add_argument("--overlay")
p.add_argument("--set", action="append")
p.add_argument("--wrap-id", dest="wrap_id")
p.add_argument("--drop-background", action="store_true", dest="drop_background",
help="remove the original canvas-sized background rect(s) so an --underlay shows through")
p.add_argument("--viewbox")
p.set_defaults(fn=cmd_inject)
a = ap.parse_args()
a.fn(a)
if __name__ == "__main__":
main()
scripts/new_style.py
#!/usr/bin/env python3
"""new_style.py - scaffold a new style for SVG Style Studio.
Adding a style takes three steps:
1. python3 new_style.py --id vaporwave --name "Vaporwave" --family genre \
--alias "vapor wave" --alias "aesthetic" --description "..." --file genre
-> appends a filled-in TEMPLATE to styles/registry/genre.json (or creates a new
registry file) and a reference stub section to styles/references/genre.md
2. Edit the TODO fields in the JSON (palette, geometry, stroke, texture, composition,
techniques, avoid, checks) and write the recipe in the reference stub.
3. python3 route_style.py --lint # must print 0 errors
Usage
new_style.py --id ID --name NAME --family FAMILY [--alias A ...] [--extends PARENT]
[--description TEXT] [--file REGISTRY_BASENAME] [--best-for illustration,poster]
new_style.py --checks # list supported machine checks for the "checks" block
The template is deliberately full of TODO markers so lint (and you) notice incomplete entries.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import REFERENCE_DIR, REGISTRY_DIR, load_registry, slug # noqa: E402
from route_style import KNOWN_CHECKS # noqa: E402
CHECK_HELP = {
"forbid_elements": "list of tags the style avoids, e.g. [\"filter\", \"circle\"]",
"require_elements_any": "at least one of these tags must be present, e.g. [\"pattern\"]",
"max_colors / min_colors": "distinct colour budget",
"forbid_path_curves": "true -> only M/L/H/V/Z path commands (straight-edged styles)",
"integer_coords": "true -> <rect> coordinates must be integers (grid styles)",
"require_shape_rendering": "e.g. \"crispEdges\" on the root / >= 90% of shapes",
"max_filters / min_filters": "filtered-element cap / required <filter> count",
"max_gradients / min_gradients": "gradient count bounds",
"min_elements / max_elements": "painted-element bounds (detail level)",
"min_groups": "minimum <g> count (layering)",
"require_text / forbid_text": "typography expected / avoided",
"min_stroke_widths / max_stroke_widths": "line-hierarchy or consistency",
"require_stroke / forbid_stroke": "outline-based vs fill-only",
"require_linecap / require_linejoin": "e.g. \"round\"",
"dark_base / light_base": "true -> first painted background must be dark / light",
"allow_bleed": "true -> content may touch/exceed the canvas edge (patterns, full-bleed posters)",
}
TEMPLATE = {
"id": None,
"name": None,
"family": None,
"aliases": [],
"keywords": ["TODO: weaker trigger words, e.g. typical subjects/motifs of this style"],
"description": "TODO: one or two sentences describing the visual language, not just the colours.",
"visual_characteristics": ["TODO: 3-5 concrete, checkable traits"],
"palette": {"guidance": "TODO: base, accents, value structure, saturation rules.", "swatches": ["#111111", "#eeeeee"]},
"geometry": "TODO: shape vocabulary, proportions, grids, corner treatment, symmetry, perspective.",
"stroke": "TODO: widths, caps, joins, hierarchy, or 'none'.",
"texture": "TODO: patterns, grain, halftone, imperfections.",
"composition": "TODO: layout rules, focal strategy, negative space, layering/depth.",
"typography": "TODO: type treatment or 'avoid text'.",
"lighting_depth": "TODO: lighting model, shadows, depth cues.",
"svg_techniques": ["TODO: concrete SVG constructions (patterns, gradients, masks, filters with parameters)"],
"avoid": ["TODO: things that break the style"],
"compatible_with": [],
"conflicts_with": [],
"best_for": ["illustration"],
"detail_level": "medium",
"checks": {"min_elements": 10},
"reference": None,
}
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--id")
ap.add_argument("--name")
ap.add_argument("--family")
ap.add_argument("--alias", action="append", default=[])
ap.add_argument("--extends")
ap.add_argument("--description")
ap.add_argument("--file", help="registry file basename to append to (default: the family name)")
ap.add_argument("--best-for", dest="best_for")
ap.add_argument("--checks", action="store_true", help="list supported check keys and exit")
a = ap.parse_args()
if a.checks:
for k, v in CHECK_HELP.items():
print("%-40s %s" % (k, v))
missing = KNOWN_CHECKS - {t.strip() for k in CHECK_HELP for t in k.split("/")}
if missing:
print("(undocumented: %s)" % ", ".join(sorted(missing)))
return
if not (a.id and a.name and a.family):
ap.error("--id, --name and --family are required")
sid = slug(a.id)
reg = load_registry()
if sid in reg:
sys.exit("style id %r already exists (%s)" % (sid, reg[sid]["_file"]))
if a.extends and a.extends not in reg:
sys.exit("--extends %r is not a known style" % a.extends)
entry = json.loads(json.dumps(TEMPLATE))
entry["id"], entry["name"], entry["family"] = sid, a.name, a.family
entry["aliases"] = a.alias or [a.name.lower()]
if a.description:
entry["description"] = a.description
if a.extends:
entry["extends"] = a.extends
if a.best_for:
entry["best_for"] = [x.strip() for x in a.best_for.split(",") if x.strip()]
base = slug(a.file or a.family)
entry["reference"] = "%s.md#%s" % (base, sid)
reg_file = REGISTRY_DIR / (base + ".json")
items = []
if reg_file.exists():
data = json.loads(reg_file.read_text(encoding="utf-8"))
items = data["styles"] if isinstance(data, dict) else data
items.append(entry)
reg_file.write_text(json.dumps(items, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
ref_file = REFERENCE_DIR / (base + ".md")
stub = (
"\n---\n\n## %s {#%s}\n\n"
"**Layer stack**: TODO `back` → `mid` → `front` → `overlay`\n\n"
"**Design moves**\n- TODO\n\n"
"**Recipe**\n```svg\n<!-- TODO: working SVG snippet of the style's signature technique -->\n```\n\n"
"**QA checklist**\n- [ ] TODO\n"
) % (a.name, sid)
if ref_file.exists():
ref_file.write_text(ref_file.read_text(encoding="utf-8").rstrip() + "\n" + stub, encoding="utf-8")
else:
ref_file.write_text("# %s styles\n\nReference for: %s.\n%s" % (base.replace("-", " ").title(), a.name, stub), encoding="utf-8")
print("added %r to %s and a reference stub to %s" % (sid, reg_file, ref_file))
print("next: fill in every TODO, add compatible_with/conflicts_with, then run: python3 route_style.py --lint")
if __name__ == "__main__":
main()
scripts/optimize_svg.py
#!/usr/bin/env python3
"""optimize_svg.py - structure-preserving SVG optimiser for SVG Style Studio.
Usage:
optimize_svg.py in.svg -o out.svg [--precision 2] [--minify] [--no-use] [--no-hoist]
optimize_svg.py in.svg --in-place
optimize_svg.py in.svg --dry-run (report only)
What it does (all appearance-preserving):
* strips comments, editor metadata (inkscape/sodipodi/sketch/...), empty <defs>/<g>
* trims numeric precision in path data, points, transforms and numeric attributes
(use --precision 0 for pixel-grid art)
* re-serialises path data compactly (implicit repeats, no redundant separators)
* removes default-valued presentation attributes when nothing above overrides them
* removes unreferenced gradients / filters / clipPaths / masks / patterns / symbols / markers
* merges byte-identical gradients, filters, clipPaths, masks, patterns and rewrites refs
* converts >=3 identical, sufficiently long <path d> / <polygon points> into <use> of one definition
* hoists presentation attributes shared by every child into the parent <g>
* unwraps bare <g> wrappers (named/ID'd/styled/transformed groups are KEPT - they are the
semantic structure that keeps the file editable)
It never rasterises, never reorders painted elements, and never touches text content.
"""
from __future__ import annotations
import argparse
import copy
import re
import sys
from collections import Counter, defaultdict
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import ( # noqa: E402
NON_RENDERED, NUM_RE, SVG_NS, XLINK_NS, INHERITED, PathParseError,
get_href, iter_elements, local, parse_svg,
)
from lxml import etree # noqa: E402
EDITOR_PREFIXES = ("inkscape", "sodipodi", "sketch", "serif", "figma", "adobe", "i", "x", "a", "xmlns:inkscape")
EDITOR_NS_HINTS = ("inkscape", "sodipodi", "sketch", "serif", "illustrator", "adobe", "bohemian", "creativecommons", "purl.org/dc", "w3.org/1999/02/22-rdf")
NUMERIC_ATTRS = {
"x", "y", "width", "height", "cx", "cy", "r", "rx", "ry", "x1", "y1", "x2", "y2", "fx", "fy",
"offset", "stroke-width", "stroke-miterlimit", "stroke-dashoffset", "opacity", "fill-opacity",
"stroke-opacity", "stop-opacity", "font-size", "stdDeviation", "dx", "dy", "k1", "k2", "k3", "k4",
"viewBox", "points", "transform", "gradientTransform", "patternTransform", "stroke-dasharray",
"radius", "baseFrequency", "scale", "surfaceScale", "diffuseConstant", "specularExponent",
}
DEFAULTS = {
"opacity": "1", "fill-opacity": "1", "stroke-opacity": "1", "stop-opacity": "1",
"display": "inline", "visibility": "visible", "fill-rule": "nonzero", "clip-rule": "nonzero",
"stroke-linecap": "butt", "stroke-linejoin": "miter", "stroke-miterlimit": "4",
"stroke-dashoffset": "0", "stroke-dasharray": "none", "font-style": "normal", "font-weight": "normal",
}
HOISTABLE = ["fill", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "shape-rendering",
"fill-opacity", "stroke-opacity", "fill-rule", "stroke-dasharray", "stroke-miterlimit"]
DEDUPE_TAGS = {"linearGradient", "radialGradient", "filter", "clipPath", "mask", "pattern"}
def fmt(v, prec, lead_zero=True):
s = ("%." + str(prec) + "f") % v if prec > 0 else "%d" % round(v)
if "." in s:
s = s.rstrip("0").rstrip(".")
if s in ("-0", ""):
s = "0"
if not lead_zero:
s = re.sub(r"^(-?)0\.", r"\1.", s)
return s
def round_numbers(text, prec):
return NUM_RE.sub(lambda m: fmt(float(m.group()), prec), text)
# ---------------------------------------------------------------- path data
_ARGC = {"M": 2, "L": 2, "H": 1, "V": 1, "C": 6, "S": 4, "Q": 4, "T": 2, "A": 7, "Z": 0}
def reserialize_path(d, prec):
"""Compact path data. Raises PathParseError on malformed data."""
i, n = 0, len(d)
groups = [] # (cmd, [floats/ints])
cmd = None
def skip():
nonlocal i
while i < n and d[i] in " ,\t\r\n":
i += 1
def num():
nonlocal i
skip()
m = NUM_RE.match(d, i)
if not m:
raise PathParseError("bad number at %d" % i)
i = m.end()
return float(m.group())
def flag():
nonlocal i
skip()
if i < n and d[i] in "01":
i += 1
return int(d[i - 1])
raise PathParseError("bad arc flag at %d" % i)
while True:
skip()
if i >= n:
break
if d[i].isalpha():
cmd = d[i]
i += 1
if cmd in "Zz":
groups.append((cmd, []))
continue
elif cmd is None or cmd in "Zz":
raise PathParseError("unexpected number")
up = cmd.upper()
if up not in _ARGC:
raise PathParseError("unknown command %r" % cmd)
if up == "A":
vals = [num(), num(), num(), flag(), flag(), num(), num()]
else:
vals = [num() for _ in range(_ARGC[up])]
groups.append((cmd, vals))
if up == "M":
cmd = "l" if cmd == "m" else "L"
out = []
prev = None
for cmd, vals in groups:
toks = []
for idx, v in enumerate(vals):
if cmd.upper() == "A" and idx in (3, 4):
toks.append(str(int(v)))
else:
toks.append(fmt(v, prec, lead_zero=False))
body = ""
for k, t in enumerate(toks):
body += t if (k == 0 or t.startswith("-")) else " " + t
if cmd in "Zz":
out.append(cmd)
elif prev == cmd:
# implicit repeat of the same command: only a separator is needed
out.append(body if body.startswith("-") else " " + body)
else:
out.append(cmd + body)
prev = cmd
return "".join(out)
# ---------------------------------------------------------------- helpers
def q(el):
return etree.QName(el.tag)
def is_editor_ns(uri):
return any(h in (uri or "") for h in EDITOR_NS_HINTS)
def strip_editor_cruft(root, stats):
for el in list(root.iter()):
if not isinstance(el.tag, str):
continue
ns = etree.QName(el.tag).namespace
if ns and is_editor_ns(ns):
el.getparent().remove(el)
stats["cruft"] += 1
continue
if local(el.tag) == "metadata":
el.getparent().remove(el)
stats["cruft"] += 1
continue
for k in list(el.attrib):
kns = etree.QName(k).namespace
if kns and is_editor_ns(kns):
del el.attrib[k]
stats["cruft"] += 1
elif k in ("data-name",) and False:
pass
def strip_comments(root, stats):
for c in list(root.iter(etree.Comment, etree.ProcessingInstruction)):
par = c.getparent()
if par is not None:
# keep the text tail
if c.tail and c.tail.strip():
prev = c.getprevious()
if prev is not None:
prev.tail = (prev.tail or "") + c.tail
else:
par.text = (par.text or "") + c.tail
par.remove(c)
stats["comments"] += 1
def trim_numbers(root, prec, stats):
for el in iter_elements(root):
t = local(el.tag)
if t == "path" and el.get("d"):
try:
new = reserialize_path(el.get("d"), prec)
if new != el.get("d"):
stats["numbers"] += 1
el.set("d", new)
except PathParseError:
pass # leave malformed data untouched; validator reports it
for k in list(el.attrib):
name = local(k)
if name in NUMERIC_ATTRS and name != "d":
v = el.get(k)
nv = round_numbers(v, prec)
if nv != v:
el.set(k, nv)
stats["numbers"] += 1
if el.get("style"):
st = el.get("style")
ns = re.sub(r"(?<![#\w])(-?\d*\.\d{%d,})" % (prec + 1), lambda m: fmt(float(m.group(1)), prec), st)
if ns != st:
el.set("style", ns)
def ancestor_defines(el, prop):
node = el.getparent()
while node is not None and isinstance(node.tag, str):
if node.get(prop) is not None:
return True
st = node.get("style") or ""
if re.search(r"(^|;)\s*%s\s*:" % re.escape(prop), st):
return True
node = node.getparent()
return False
def drop_defaults(root, stats):
for el in iter_elements(root):
for prop, dv in DEFAULTS.items():
if el.get(prop) is not None and el.get(prop).strip() == dv:
if prop in INHERITED and ancestor_defines(el, prop):
continue
if prop in ("opacity", "display", "stop-opacity", "visibility") or True:
del el.attrib[prop]
stats["defaults"] += 1
if local(el.tag) == "rect":
for a in ("x", "y"):
if el.get(a) in ("0", "0.0"):
del el.attrib[a]
stats["defaults"] += 1
def gather_refs(root):
"""Return set of ids referenced by url(#..) or href."""
refs = set()
for el in iter_elements(root):
for k, v in el.attrib.items():
for m in re.finditer(r"url\(\s*['\"]?#([^)'\" ]+)", v):
refs.add(m.group(1))
if local(k) == "href" and v.startswith("#"):
refs.add(v[1:])
if local(el.tag) == "style" and el.text:
for m in re.finditer(r"url\(\s*['\"]?#([^)'\" ]+)", el.text):
refs.add(m.group(1))
return refs
def remove_unused_defs(root, stats):
removable = {"linearGradient", "radialGradient", "filter", "clipPath", "mask", "pattern", "symbol", "marker"}
changed = True
while changed:
changed = False
refs = gather_refs(root)
for el in list(iter_elements(root)):
if local(el.tag) in removable and el.get("id") and el.get("id") not in refs:
el.getparent().remove(el)
stats["unused_defs"] += 1
changed = True
# a <style> block may target ids/classes; leave <path id> etc. alone
def canonical(el):
c = copy.deepcopy(el)
if "id" in c.attrib:
del c.attrib["id"]
return etree.tostring(c, method="c14n")
def rewrite_ref(root, old, new):
for el in iter_elements(root):
for k, v in list(el.attrib.items()):
nv = v
if ("#" + old) in v:
nv = re.sub(r"url\(\s*(['\"]?)#%s\1\s*\)" % re.escape(old), "url(#%s)" % new, nv)
if local(k) == "href" and nv == "#" + old:
nv = "#" + new
if nv != v:
el.set(k, nv)
if local(el.tag) == "style" and el.text and ("#" + old) in el.text:
el.text = re.sub(r"url\(\s*(['\"]?)#%s\1\s*\)" % re.escape(old), "url(#%s)" % new, el.text)
def merge_duplicate_defs(root, stats):
seen = {}
for el in list(iter_elements(root)):
if local(el.tag) in DEDUPE_TAGS and el.get("id"):
key = canonical(el)
if key in seen and seen[key] is not el:
keep = seen[key].get("id")
old = el.get("id")
rewrite_ref(root, old, keep)
el.getparent().remove(el)
stats["merged_defs"] += 1
else:
seen[key] = el
def unique_id(root, base):
used = {e.get("id") for e in iter_elements(root) if e.get("id")}
n = 1
while "%s%d" % (base, n) in used:
n += 1
return "%s%d" % (base, n)
def ensure_defs(root):
for ch in root:
if isinstance(ch.tag, str) and local(ch.tag) == "defs":
return ch
defs = etree.Element("{%s}defs" % SVG_NS)
# after <title>/<desc> if present
idx = 0
for i, ch in enumerate(root):
if isinstance(ch.tag, str) and local(ch.tag) in ("title", "desc"):
idx = i + 1
root.insert(idx, defs)
return defs
def dedupe_to_use(root, stats, min_len=60, min_count=3):
groups = defaultdict(list)
for el in iter_elements(root):
t = local(el.tag)
# only shapes that are painted (not already inside defs/clipPath/mask)
anc = [local(a.tag) for a in el.iterancestors()]
if any(a in NON_RENDERED for a in anc):
continue
if t == "path" and len(el.get("d", "")) >= min_len:
groups[("path", "d", el.get("d").strip())].append(el)
elif t == "polygon" and len(el.get("points", "")) >= min_len:
groups[("polygon", "points", el.get("points").strip())].append(el)
defs = None
for (tag, attr, data), els in groups.items():
if len(els) < min_count:
continue
if defs is None:
defs = ensure_defs(root)
new_id = unique_id(root, "shape")
proto = etree.SubElement(defs, "{%s}%s" % (SVG_NS, tag))
proto.set("id", new_id)
proto.set(attr, data)
for el in els:
use = etree.Element("{%s}use" % SVG_NS)
use.set("href", "#" + new_id)
for k, v in el.attrib.items():
if k == attr:
continue
use.set(k, v)
use.tail = el.tail
el.getparent().replace(el, use)
stats["use_converted"] += len(els)
def hoist_common(root, stats):
for g in list(iter_elements(root)):
if local(g.tag) not in ("g",):
continue
kids = [c for c in g if isinstance(c.tag, str)]
if len(kids) < 3:
continue
if any(local(c.tag) not in ("path", "rect", "circle", "ellipse", "line", "polyline", "polygon", "use") for c in kids):
continue
if any(c.get("style") or c.get("class") for c in kids) or g.get("style"):
continue
for prop in HOISTABLE:
if g.get(prop) is not None:
continue
vals = {c.get(prop) for c in kids}
if len(vals) == 1 and None not in vals:
v = vals.pop()
# a <use> child inherits into its shadow tree only if the referenced
# element does not set the property itself; those prototypes never do.
g.set(prop, v)
for c in kids:
del c.attrib[prop]
stats["hoisted"] += 1
def prune_empty_and_unwrap(root, stats):
changed = True
while changed:
changed = False
for el in list(iter_elements(root)):
t = local(el.tag)
par = el.getparent()
if par is None:
continue
kids = [c for c in el if isinstance(c.tag, str)]
if t == "defs" and not kids:
par.remove(el)
stats["empty"] += 1
changed = True
elif t == "g" and not kids and not (el.text or "").strip() and not el.get("id"):
par.remove(el)
stats["empty"] += 1
changed = True
elif t == "g" and not el.attrib and kids:
idx = list(par).index(el)
for off, ch in enumerate(list(el)):
par.insert(idx + off, ch)
# preserve tail
if el.tail and el.tail.strip():
last = par[idx + len(kids) - 1]
last.tail = (last.tail or "") + el.tail
par.remove(el)
stats["unwrapped"] += 1
changed = True
def optimize(root, precision=2, use_convert=True, hoist=True):
stats = Counter()
strip_comments(root, stats)
strip_editor_cruft(root, stats)
trim_numbers(root, precision, stats)
drop_defaults(root, stats)
merge_duplicate_defs(root, stats)
if use_convert:
dedupe_to_use(root, stats)
if hoist:
hoist_common(root, stats)
remove_unused_defs(root, stats)
prune_empty_and_unwrap(root, stats)
etree.cleanup_namespaces(root)
return stats
def serialise(root, minify=False):
if minify:
for el in root.iter():
if isinstance(el.tag, str):
if el.text is not None and not el.text.strip() and len(el) >= 0 and local(el.tag) not in ("text", "tspan", "style"):
el.text = None
if el.tail is not None and not el.tail.strip():
el.tail = None
else:
# pretty print: clear whitespace-only text so indent() works, but keep <text>/<style> content
for el in root.iter():
if isinstance(el.tag, str) and local(el.tag) not in ("text", "tspan", "style"):
if el.text is not None and not el.text.strip():
el.text = None
if el.tail is not None and not el.tail.strip():
el.tail = None
# do not re-indent inside text
etree.indent(root, space=" ")
data = etree.tostring(root, encoding="unicode", xml_declaration=False)
return data + "\n"
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("file")
ap.add_argument("-o", "--out")
ap.add_argument("--in-place", action="store_true")
ap.add_argument("--dry-run", action="store_true")
ap.add_argument("--precision", type=int, default=2)
ap.add_argument("--minify", action="store_true", help="no indentation (smaller, less editable)")
ap.add_argument("--no-use", action="store_true", help="do not convert repeated shapes to <use>")
ap.add_argument("--no-hoist", action="store_true", help="do not hoist shared attributes into groups")
args = ap.parse_args()
root, err = parse_svg(args.file)
if root is None:
sys.exit("cannot parse %s: %s" % (args.file, err))
before = len(Path(args.file).read_bytes())
stats = optimize(root, args.precision, not args.no_use, not args.no_hoist)
text = serialise(root, args.minify)
after = len(text.encode("utf-8"))
summary = ", ".join("%s=%d" % kv for kv in sorted(stats.items())) or "nothing to change"
print("%s: %d -> %d bytes (%.0f%%) [%s]" % (args.file, before, after, 100.0 * after / max(before, 1), summary))
if args.dry_run:
return
out = args.file if args.in_place else args.out
if not out:
sys.exit("give -o OUT.svg, --in-place, or --dry-run")
Path(out).write_text(text, encoding="utf-8")
if __name__ == "__main__":
main()
scripts/pixel_grid.py
#!/usr/bin/env python3
"""pixel_grid.py - turn an ASCII sprite into a crisp, editable SVG.
Pixel styles must sit on a strict grid with hard edges. Hand-typing hundreds of
<rect>s is error-prone, so author the sprite as text and let this tool merge it
into one <path> per colour (greedy rectangle merge, integer coordinates,
shape-rendering="crispEdges").
Sprite file format
; comment lines start with ';'
@k=#1b1b2f palette entry: '@' + one character + '=' + colour
@o=#e8702a
................ '.' (or space) is transparent, any other character is a palette key
....kk......kk....
...kook....kook...
Usage
pixel_grid.py sprite.txt -o sprite.svg [--bg "#2b2d42"] [--pad 2] [--mirror]
[--outline "#111111"] [--title "Pixel fox"] [--palette "k=#111,o=#f80"]
--mirror the sprite text is the LEFT half; the right half is mirrored automatically
--outline add a 1-cell outline around all opaque pixels (4-neighbour), in the given colour
--bg solid background rect behind the sprite (full canvas, includes padding)
--pad transparent/bg margin in cells around the sprite
--frame alias for --pad 0 --bg none (default behaviour)
"""
from __future__ import annotations
import argparse
import sys
from collections import OrderedDict
from pathlib import Path
from xml.sax.saxutils import escape, quoteattr
TRANSPARENT = {".", " "}
def parse_sprite(text, palette_override=None):
palette = OrderedDict()
rows = []
for raw in text.splitlines():
line = raw.rstrip("\n")
if not line.strip():
if rows:
rows.append("") # blank rows inside a sprite are transparent rows
continue
if line.lstrip().startswith(";"):
continue
if line.startswith("@"):
body = line[1:].split(";")[0].strip()
if "=" not in body or len(body.split("=")[0].strip()) != 1:
raise SystemExit("bad palette line: %r (use @k=#rrggbb)" % line)
key, val = body.split("=", 1)
palette[key.strip()] = val.strip()
continue
rows.append(line)
while rows and rows[-1] == "":
rows.pop()
if palette_override:
for kv in palette_override.split(","):
k, v = kv.split("=", 1)
palette[k.strip()] = v.strip()
if not rows:
raise SystemExit("sprite has no pixel rows")
return rows, palette
def to_grid(rows, mirror=False):
width = max(len(r) for r in rows)
grid = []
for r in rows:
r = r.ljust(width, ".")
if mirror:
r = r + r[::-1]
grid.append(list(r))
return grid
def add_outline(grid, key="\0"):
h, w = len(grid), len(grid[0])
out = [row[:] for row in grid]
for y in range(h):
for x in range(w):
if grid[y][x] in TRANSPARENT:
for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nx, ny = x + dx, y + dy
if 0 <= nx < w and 0 <= ny < h and grid[ny][nx] not in TRANSPARENT and grid[ny][nx] != key:
out[y][x] = key
break
return out
def pad_grid(grid, n):
if n <= 0:
return grid
w = len(grid[0]) + 2 * n
blank = ["."] * w
return [blank[:] for _ in range(n)] + [["."] * n + row + ["."] * n for row in grid] + [blank[:] for _ in range(n)]
def merge_rects(grid, key):
"""Greedy maximal-rectangle cover of all cells == key. Returns [(x, y, w, h)]."""
h, w = len(grid), len(grid[0])
seen = [[False] * w for _ in range(h)]
rects = []
for y in range(h):
for x in range(w):
if grid[y][x] != key or seen[y][x]:
continue
x2 = x
while x2 + 1 < w and grid[y][x2 + 1] == key and not seen[y][x2 + 1]:
x2 += 1
y2 = y
while y2 + 1 < h and all(grid[y2 + 1][i] == key and not seen[y2 + 1][i] for i in range(x, x2 + 1)):
y2 += 1
for yy in range(y, y2 + 1):
for xx in range(x, x2 + 1):
seen[yy][xx] = True
rects.append((x, y, x2 - x + 1, y2 - y + 1))
return rects
def build_svg(grid, palette, bg=None, title="Pixel art", outline=None):
h, w = len(grid), len(grid[0])
keys = []
for row in grid:
for c in row:
if c not in TRANSPARENT and c not in keys:
keys.append(c)
missing = [k for k in keys if k != "\0" and k not in palette]
if missing:
raise SystemExit("no palette colour for key(s): %s" % ", ".join(repr(m) for m in missing))
lines = [
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %d %d" shape-rendering="crispEdges">' % (w, h),
" <title>%s</title>" % escape(title),
]
if bg and bg.lower() != "none":
lines.append(' <rect id="background" width="%d" height="%d" fill=%s/>' % (w, h, quoteattr(bg)))
lines.append(' <g id="sprite">')
# outline first so the sprite paints over it
order = (["\0"] if "\0" in keys else []) + [k for k in keys if k != "\0"]
for k in order:
color = outline if k == "\0" else palette[k]
rects = merge_rects(grid, k)
d = "".join("M%d %dh%dv%dh-%dz" % (x, y, rw, rh, rw) for x, y, rw, rh in rects)
name = "outline" if k == "\0" else "c-" + (k if k.isalnum() else "x%02x" % ord(k))
lines.append(' <path id=%s fill=%s d="%s"/>' % (quoteattr(name), quoteattr(color), d))
lines.append(" </g>")
lines.append("</svg>")
return "\n".join(lines) + "\n", len(keys)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("sprite", help="sprite text file or - for stdin")
ap.add_argument("-o", "--out", required=True)
ap.add_argument("--bg")
ap.add_argument("--pad", type=int, default=0)
ap.add_argument("--mirror", action="store_true")
ap.add_argument("--outline")
ap.add_argument("--title", default="Pixel art")
ap.add_argument("--palette", help='extra/override entries, e.g. "k=#111111,o=#ff8800"')
args = ap.parse_args()
text = sys.stdin.read() if args.sprite == "-" else Path(args.sprite).read_text(encoding="utf-8")
rows, palette = parse_sprite(text, args.palette)
grid = to_grid(rows, args.mirror)
if args.outline:
grid = add_outline(grid)
grid = pad_grid(grid, args.pad)
svg, n = build_svg(grid, palette, args.bg, args.title, args.outline)
Path(args.out).write_text(svg, encoding="utf-8")
print("wrote %s (%dx%d grid, %d colours, %d bytes)" % (args.out, len(grid[0]), len(grid), n, len(svg)))
if __name__ == "__main__":
main()
scripts/render_svg.py
#!/usr/bin/env python3
"""render_svg.py - render SVGs to PNG with headless Chromium (Playwright),
optionally build a labelled contact sheet and print pixel-level analysis.
Usage:
render_svg.py art.svg -> art.png next to the file (1024px wide)
render_svg.py art.svg -o /tmp/prev.png -w 800 --bg "#ffffff"
render_svg.py a.svg b.svg c.svg --sheet compare.png --cols 3
render_svg.py art.svg --analyze -> also print JSON pixel stats
--bg accepts a CSS colour, "transparent", or "checker".
The SVG is rendered through <img>, i.e. the way it behaves when embedded or
opened as a standalone file: no scripts, no external fetches.
Analysis fields (see SKILL.md, step "inspect"):
blank nothing visible was drawn
alpha_coverage_pct share of pixels that are not fully transparent
content_bbox_pct bounding box of non-transparent pixels as % of the canvas
edge_touch which canvas edges have painted pixels (useful when the
art is NOT meant to bleed)
rendered_colors number of distinct RGB values in the raster
top32_share_pct share of pixels covered by the 32 most common colours
(pixel art with crisp edges stays ~100%; anti-aliasing
leaks it)
mean_luma/contrast average luminance and its standard deviation
"""
from __future__ import annotations
import argparse
import base64
import json
import math
import sys
from collections import Counter
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import parse_svg, parse_viewbox, fnum # noqa: E402
CHECKER_CSS = (
"background-color:#fff;background-image:linear-gradient(45deg,#d8d8d8 25%,transparent 25%,transparent 75%,#d8d8d8 75%),"
"linear-gradient(45deg,#d8d8d8 25%,transparent 25%,transparent 75%,#d8d8d8 75%);background-size:24px 24px;background-position:0 0,12px 12px;"
)
def canvas_size(svg_path, width):
root, err = parse_svg(str(svg_path))
if root is None:
raise SystemExit("cannot parse %s: %s" % (svg_path, err))
vb = parse_viewbox(root)
if vb:
w, h = vb[2], vb[3]
else:
w, h = fnum(root.get("width"), 512), fnum(root.get("height"), 512)
if w <= 0 or h <= 0:
w, h = 512, 512
return width, max(1, int(round(width * h / w)))
def render_many(jobs, bg="#ffffff", timeout_ms=20000):
"""jobs: list of (svg_path, out_png, width). Returns list of error strings/None."""
from playwright.sync_api import sync_playwright
errors = []
with sync_playwright() as p:
browser = p.chromium.launch()
for svg_path, out_png, width in jobs:
try:
w, h = canvas_size(svg_path, width)
data = base64.b64encode(Path(svg_path).read_bytes()).decode()
if bg == "transparent":
body_bg = "background:transparent;"
elif bg == "checker":
body_bg = CHECKER_CSS
else:
body_bg = "background:%s;" % bg
html = (
"<!doctype html><html><body style='margin:0;%s'>"
"<img id='i' src='data:image/svg+xml;base64,%s' style='display:block;width:%dpx;height:%dpx'>"
"</body></html>" % (body_bg, data, w, h)
)
ctx = browser.new_context(viewport={"width": w, "height": h}, device_scale_factor=1)
page = ctx.new_page()
page.set_content(html, wait_until="load", timeout=timeout_ms)
ok = page.evaluate("() => { const i = document.getElementById('i'); return i.complete && i.naturalWidth > 0; }")
page.screenshot(path=str(out_png), omit_background=(bg == "transparent"),
clip={"x": 0, "y": 0, "width": w, "height": h})
ctx.close()
errors.append(None if ok else "browser could not decode the SVG (invalid or unsupported)")
except Exception as exc: # noqa: BLE001
errors.append("render failed: %s" % str(exc).splitlines()[0][:200])
browser.close()
return errors
def analyze_png(svg_path, width=512):
"""Render on transparent + analyse. Returns dict."""
import tempfile
from PIL import Image
with tempfile.TemporaryDirectory() as td:
out = Path(td) / "a.png"
err = render_many([(svg_path, out, width)], bg="transparent")[0]
if err:
return {"error": err}
im = Image.open(out).convert("RGBA")
w, h = im.size
px = im.load()
alpha = im.getchannel("A")
hist = alpha.histogram()
total = w * h
painted = total - hist[0]
res = {"size": [w, h], "blank": painted == 0}
if painted == 0:
return res
res["alpha_coverage_pct"] = round(100 * painted / total, 1)
bbox = alpha.point(lambda v: 255 if v > 8 else 0).getbbox()
if bbox:
res["content_bbox_pct"] = [round(100 * bbox[0] / w, 1), round(100 * bbox[1] / h, 1),
round(100 * bbox[2] / w, 1), round(100 * bbox[3] / h, 1)]
res["edge_touch"] = {
"left": bbox[0] <= 0, "top": bbox[1] <= 0, "right": bbox[2] >= w, "bottom": bbox[3] >= h,
}
# colours on white composite
from PIL import ImageChops
white = Image.new("RGBA", im.size, (255, 255, 255, 255))
flat = Image.alpha_composite(white, im).convert("RGB")
colors = flat.getcolors(maxcolors=total) or []
res["rendered_colors"] = len(colors)
top32 = sum(c for c, _ in sorted(colors, reverse=True)[:32])
res["top32_share_pct"] = round(100 * top32 / total, 1)
hist_l = flat.convert("L").histogram()
mean = sum(i * n for i, n in enumerate(hist_l)) / total
var = sum(n * (i - mean) ** 2 for i, n in enumerate(hist_l)) / total
res["mean_luma"] = round(mean / 255, 3)
res["contrast"] = round(math.sqrt(var) / 255, 3)
if res["alpha_coverage_pct"] >= 99.0:
# full-bleed art: measure the subject against the dominant border colour
border = Counter()
fp = flat.load()
for x in range(w):
border[fp[x, 0]] += 1
border[fp[x, h - 1]] += 1
for y in range(h):
border[fp[0, y]] += 1
border[fp[w - 1, y]] += 1
bgc = border.most_common(1)[0][0]
diff = ImageChops.difference(flat, Image.new("RGB", flat.size, bgc)).convert("L")
sb = diff.point(lambda v: 255 if v > 40 else 0).getbbox()
res["full_bleed"] = True
if sb:
res["subject_bbox_pct"] = [round(100 * sb[0] / w, 1), round(100 * sb[1] / h, 1),
round(100 * sb[2] / w, 1), round(100 * sb[3] / h, 1)]
res["subject_edge_touch"] = {"left": sb[0] <= 0, "top": sb[1] <= 0, "right": sb[2] >= w, "bottom": sb[3] >= h}
res["subject_fill_pct"] = round(100 * (sb[2] - sb[0]) * (sb[3] - sb[1]) / total, 1)
# left/right & top/bottom ink balance (alpha-weighted)
lw = rw = tw = bw = 0
a = alpha.load()
for y in range(0, h, 2):
for x in range(0, w, 2):
v = a[x, y]
if not v:
continue
if x < w / 2:
lw += v
else:
rw += v
if y < h / 2:
tw += v
else:
bw += v
if lw + rw:
res["ink_balance_lr_pct"] = round(100 * (rw - lw) / (rw + lw), 1)
if tw + bw:
res["ink_balance_tb_pct"] = round(100 * (bw - tw) / (bw + tw), 1)
return res
def build_sheet(items, out_path, cols, cell=420, bg="#ffffff"):
"""items: list of (png_path, label)."""
from PIL import Image, ImageDraw, ImageFont
try:
font = ImageFont.load_default(size=15)
except TypeError: # older Pillow
font = ImageFont.load_default()
pad, label_h = 16, 30
rows = (len(items) + cols - 1) // cols
W = cols * (cell + pad) + pad
H = rows * (cell + label_h + pad) + pad
sheet = Image.new("RGB", (W, H), "#e9e9ee")
d = ImageDraw.Draw(sheet)
for i, (png, label) in enumerate(items):
r, c = divmod(i, cols)
x = pad + c * (cell + pad)
y = pad + r * (cell + label_h + pad)
im = Image.open(png).convert("RGBA")
im.thumbnail((cell, cell), Image.LANCZOS)
tile = Image.new("RGB", (cell, cell), bg)
tile.paste(im, ((cell - im.width) // 2, (cell - im.height) // 2), im)
sheet.paste(tile, (x, y + label_h))
d.text((x + 2, y + 6), label, fill="#222222", font=font)
sheet.save(out_path)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("files", nargs="+")
ap.add_argument("-o", "--out", help="output PNG (single input only)")
ap.add_argument("-w", "--width", type=int, default=1024)
ap.add_argument("--bg", default="#ffffff")
ap.add_argument("--sheet", help="write a contact sheet PNG of all inputs")
ap.add_argument("--cols", type=int, default=3)
ap.add_argument("--analyze", action="store_true", help="print pixel analysis JSON")
ap.add_argument("--outdir", help="directory for PNGs (default: next to each SVG)")
args = ap.parse_args()
if args.out and len(args.files) > 1:
sys.exit("-o works with a single input; use --outdir or --sheet")
jobs = []
for f in args.files:
fp = Path(f)
if args.out:
out = Path(args.out)
else:
out = (Path(args.outdir) if args.outdir else fp.parent) / (fp.stem + ".png")
out.parent.mkdir(parents=True, exist_ok=True)
jobs.append((fp, out, args.width))
errs = render_many(jobs, bg=args.bg)
rc = 0
for (fp, out, _), err in zip(jobs, errs):
if err:
print("FAIL %s: %s" % (fp, err))
rc = 1
else:
print("rendered %s -> %s" % (fp, out))
if args.sheet:
ok = [(o, Path(f).stem) for (f, o, _), e in zip(jobs, errs) if not e]
if ok:
Path(args.sheet).parent.mkdir(parents=True, exist_ok=True)
build_sheet(ok, args.sheet, args.cols)
print("contact sheet -> %s" % args.sheet)
if args.analyze:
out = {}
for (fp, _o, _w), e in zip(jobs, errs):
out[str(fp)] = {"error": e} if e else analyze_png(fp)
print(json.dumps(out, indent=2))
sys.exit(rc)
if __name__ == "__main__":
main()
scripts/route_style.py
#!/usr/bin/env python3
"""route_style.py - map a natural-language request to styles, modifiers, mode
and asset type, and print the design brief Claude should follow.
Usage
route_style.py "Create a fox in ukiyo-e style" resolve + brief
route_style.py "pixel art with a horror aesthetic" --json
route_style.py --menu concise style menu (ask the user with this)
route_style.py --list all ids / names / aliases
route_style.py --show ukiyo-e full brief for one style
route_style.py --compat cyberpunk-neon pixel-art blend plan for a pair
route_style.py --lint validate the whole registry
Output fields (--json)
needs_style true -> no style could be inferred: show the menu and ASK the user
ambiguous true -> several unrelated styles matched a vague phrase: ask which
styles [{id, role: primary|secondary|accent, matched}]
modifiers [{id, phrase, effect}]
mode single | multi | convert
source none | svg | raster (what the user handed over, if anything)
asset illustration | icon | logo | poster | infographic | pattern | ...
count requested number of variants (multi mode)
blend_plan how compatible styles are combined (structure vs surface ownership)
validate_cmd the validate_svg.py invocation to run afterwards
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from collections import OrderedDict
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import REFERENCE_DIR, REGISTRY_DIR, SKILL_DIR, load_registry, slug # noqa: E402
MODIFIERS_FILE = SKILL_DIR / "styles" / "modifiers.json"
REQUIRED_FIELDS = [
"id", "name", "family", "aliases", "description", "visual_characteristics", "palette",
"geometry", "stroke", "texture", "composition", "typography", "lighting_depth",
"svg_techniques", "avoid", "best_for", "reference",
]
ASSET_TYPES = OrderedDict([
("logo", ["logo", "logotype", "wordmark", "emblem", "monogram", "brand mark", "crest", "badge"]),
("icon", ["icon", "icon set", "glyph", "pictogram", "favicon", "app icon"]),
("poster", ["poster", "flyer", "cover", "key art", "banner", "billboard", "album art", "book cover", "postcard"]),
("infographic", ["infographic", "chart", "diagram", "timeline", "data viz", "flowchart", "explainer"]),
("pattern", ["pattern", "wallpaper", "texture tile", "seamless", "background tile"]),
("sticker", ["sticker", "die cut", "die-cut", "patch"]),
("character", ["character", "mascot", "avatar", "portrait", "creature"]),
("scene", ["scene", "landscape", "cityscape", "skyline", "interior", "environment", "city", "street", "forest", "seascape"]),
])
NUM_WORDS = {"two": 2, "three": 3, "four": 4, "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10}
# ---------------------------------------------------------------------------
def norm(text):
t = text.lower()
t = t.replace("&", " and ").replace("’", "'")
t = re.sub(r"[/_\-–—]", " ", t)
t = re.sub(r"[^a-z0-9+ '\.]", " ", t)
return re.sub(r"\s+", " ", t).strip()
def phrase_re(p):
p = norm(p)
return re.compile(r"(?<![a-z0-9])" + re.escape(p).replace(r"\ ", r"\s+") + r"(?![a-z0-9])")
def load_modifiers():
if not MODIFIERS_FILE.exists():
return []
return json.loads(MODIFIERS_FILE.read_text(encoding="utf-8"))["modifiers"]
class Router:
def __init__(self):
self.reg = load_registry()
self.mods = load_modifiers()
self.alias_index = [] # (regex, phrase_len_words, style_id, phrase)
self.kw_index = []
for sid, s in self.reg.items():
phrases = set([s["name"], sid] + s.get("aliases", []))
for ph in phrases:
n = norm(ph)
if n:
self.alias_index.append((phrase_re(n), len(n.split()), sid, n))
for kw in s.get("keywords", []):
self.kw_index.append((phrase_re(kw), sid, norm(kw)))
self.weak_index = []
for sid, s in self.reg.items():
for ph in s.get("weak_aliases", []):
n = norm(ph)
if n:
self.weak_index.append((phrase_re(n), sid, n))
self.alias_index.sort(key=lambda x: (-len(x[3]), x[3]))
self.mod_index = []
for m in self.mods:
for tr in m["triggers"]:
self.mod_index.append((phrase_re(tr), m, norm(tr)))
self.mod_index.sort(key=lambda x: -len(x[2]))
# ------------------------------------------------------------------
def find_styles(self, text):
"""Strong (alias/name) matches with spans; longest match wins overlaps.
Returns list of dicts in order of appearance."""
taken = [] # spans
found = []
for rx, nwords, sid, ph in self.alias_index:
for m in rx.finditer(text):
span = m.span()
if any(not (span[1] <= a or span[0] >= b) for a, b in taken):
continue
found.append({"id": sid, "matched": ph, "span": span})
# group ids that matched the very same span (e.g. '8 bit' -> pixel-art + retro-8bit)
by_span = OrderedDict()
for f in sorted(found, key=lambda f: f["span"]):
by_span.setdefault(f["span"], []).append(f)
results = []
used = []
for span, group in sorted(by_span.items(), key=lambda kv: (-(kv[0][1] - kv[0][0]), kv[0][0])):
if any(not (span[1] <= a or span[0] >= b) for a, b in used):
continue
used.append(span)
results.append((span, group))
results.sort(key=lambda r: r[0][0])
out = []
for span, group in results:
# when several styles share the very same phrase, the style that lists it
# earliest in its aliases is primary (see README: adding aliases)
def rank(g):
al = [norm(a) for a in self.reg[g["id"]].get("aliases", [])]
return al.index(g["matched"]) if g["matched"] in al else -1
for g in sorted(group, key=rank):
out.append(g)
# weak aliases: generic words that are usually SUBJECT descriptors ("luxury hotel",
# "futuristic motorcycle"). Accepted only when nothing stronger matched, or with
# explicit style context ("luxury style", "+ luxury", "with a luxury touch").
for rx, sid, ph in self.weak_index:
for m in rx.finditer(text):
span = m.span()
if any(not (span[1] <= a or span[0] >= b) for a, b in used):
continue
before, after = text[max(0, span[0] - 16):span[0]], text[span[1]:span[1] + 18]
ctx = bool(re.search(r"(\+|\bplus\b|\bx\b|\bmeets\b|\bmixed with\b|\bblended with\b|\bwith an?\b)\s*$", before)) or \
bool(re.match(r"\s*(style|aesthetic|look|feel|vibe|theme|design|branding|finish|touch|accents?)\b", after))
if ctx or not out:
out.append({"id": sid, "matched": ph, "span": span, "weak": not ctx})
used.append(span)
return out, used
def keyword_candidates(self, text, exclude_spans):
scores = {}
for rx, sid, kw in self.kw_index:
for m in rx.finditer(text):
if any(not (m.span()[1] <= a or m.span()[0] >= b) for a, b in exclude_spans):
continue
scores.setdefault(sid, []).append(kw)
ranked = sorted(scores.items(), key=lambda kv: (-len(set(kv[1])), kv[0]))
return [(sid, sorted(set(kws))) for sid, kws in ranked]
def find_modifiers(self, text, style_spans):
mods, seen = [], set()
for rx, m, tr in self.mod_index:
for hit in rx.finditer(text):
s = hit.span()
if any(not (s[1] <= a or s[0] >= b) for a, b in style_spans):
continue
if m["id"] in seen:
continue
seen.add(m["id"])
mods.append({"id": m["id"], "phrase": tr, "effect": m["effect"]})
return mods
# ------------------------------------------------------------------
def detect_mode(self, text):
count = None
multi = False
m = re.search(r"\b(\d+|two|three|four|five|six|seven|eight|nine|ten)\s+(?:different\s+|distinct\s+|various\s+)?(?:visual\s+)?(?:art\s+)?(?:styles?|versions?|variations?|looks?|takes?)\b", text)
if m:
v = m.group(1)
count = int(v) if v.isdigit() else NUM_WORDS[v]
multi = True
if re.search(r"\b(several|multiple|many|all|each|every)\s+(?:different\s+)?styles?\b|\bdifferent\s+styles\b|\bstyle\s+(?:comparison|sampler|grid|sheet)\b|\bcompare\s+styles\b|\bin\s+(?:these|those)\s+styles\b|\bstyle\s+variations\b", text):
multi = True
source = "none"
convert = False
if re.search(r"\.svg\b|\b(this|the|my|attached|existing|above|following)\s+(?:svg|vector)\b", text):
source = "svg"
elif re.search(r"\.(png|jpe?g|webp|gif|bmp)\b|\b(this|the|my|attached|uploaded)\s+(image|photo|picture|screenshot|drawing|sketch|png|jpg|jpeg)\b", text):
source = "raster"
if re.search(r"\b(convert|restyle|re style|transform|turn|make)\s+(?:this|the|my|it|that)\b.*?\b(into|to|look|style|like|as)\b", text) or re.search(r"\bconvert\b|\brestyle\b|\bre style\b|\bin the style of\b.*\b(this|attached)\b", text):
convert = source != "none" or bool(re.search(r"\b(convert|restyle|re style)\b", text))
if source != "none" and re.search(r"\b(look like|into a|into an|as a|as an|to a|to an|style)\b", text):
convert = True
mode = "multi" if multi else ("convert" if convert else "single")
return mode, count, source
def detect_asset(self, text):
for asset, words in ASSET_TYPES.items():
for w in words:
if phrase_re(w).search(text):
return asset
return "illustration"
# ------------------------------------------------------------------
def resolve(self, prompt):
text = norm(prompt)
hits, spans = self.find_styles(text)
mode, count, source = self.detect_mode(text)
asset = self.detect_asset(text)
# unique ids in order
ids = []
for h in hits:
if h["id"] not in [i["id"] for i in ids]:
ids.append({"id": h["id"], "matched": h["matched"], "span": h["span"], "weak": h.get("weak", False)})
# a specialised style (extends X) already carries X's rules: drop the parent
parents = {self.reg[i["id"]].get("extends") for i in ids if self.reg[i["id"]].get("extends")}
ids = [i for i in ids if i["id"] not in parents]
# an explicit LIST of 3+ styles ("pixel art, ukiyo-e, cyberpunk and blueprint") is a
# multi-style request, not a blend (blends use "+", "with", "meets", "x", "mixed with")
if mode == "single" and len(ids) >= 3:
ordered = sorted((i for i in ids if i["span"] != (0, 0)), key=lambda i: i["span"][0])
sep = re.compile(r"^\s*(,|,?\s*and|,?\s*or|vs\.?|versus|&|/)?\s*$")
if len(ordered) >= 3 and all(sep.match(text[a["span"][1]:b["span"][0]]) for a, b in zip(ordered, ordered[1:])):
mode = "multi"
count = count or len(ordered)
# multi-id for one phrase => same-phrase group
same_phrase = OrderedDict()
for h in ids:
same_phrase.setdefault(h["span"], []).append(h)
ambiguous = False
candidates = []
if not ids:
cands = self.keyword_candidates(text, spans)
if cands:
top = len(cands[0][1])
best = [c for c in cands if len(c[1]) == top][:3]
if top >= 2 and len(best) == 1:
ids = [{"id": best[0][0], "matched": "+".join(best[0][1]), "span": (0, 0)}]
elif top >= 1:
ambiguous = True
candidates = [{"id": c[0], "keywords": c[1]} for c in cands[:4]]
mods = self.find_modifiers(text, spans)
# roles
styles = []
aesthetic_secondary = bool(re.search(r"\b(with|plus|and)\s+(?:an?\s+|the\s+)?[a-z0-9 ']{0,30}?\b(aesthetic|vibe|mood|feel|flavou?r|theme|influences?|touches?|accents?|palette|lighting|atmosphere)\b", text))
role_order = ["primary", "secondary", "accent"]
if mode == "multi":
styles = [{"id": h["id"], "role": "variant", "matched": h["matched"]} for h in ids]
else:
for i, h in enumerate(ids[:3]):
styles.append({"id": h["id"], "role": role_order[min(i, 2)], "matched": h["matched"]})
if h.get("weak"):
styles[-1]["weak"] = True
# style + style from ONE phrase (e.g. '8 bit'): the child/specialised style leads
for grp in same_phrase.values():
if len(grp) > 1:
pass
# a child style implies its parent (retro-8bit extends pixel-art)
res = {
"prompt": prompt,
"mode": mode,
"count": count,
"source": source,
"asset": asset,
"styles": styles,
"modifiers": mods,
"needs_style": not styles and not ambiguous and mode != "multi",
"ambiguous": ambiguous,
"candidates": candidates,
"aesthetic_phrase": aesthetic_secondary,
# only a generic word matched ("luxury hotel", "futuristic motorcycle"): probably a subject
# descriptor, not a style request -> confirm before committing
"confirm_style": bool(styles) and mode != "multi" and all(x.get("weak") for x in styles),
}
# multi mode with no (or one) explicit style: choose a diverse set
if mode == "multi" and len(styles) < 2:
n = max(count or 5, len(styles))
pool = self.diverse(n, asset, [s["id"] for s in styles])
res["multi_styles"] = pool
elif mode == "multi":
res["multi_styles"] = [s["id"] for s in styles]
# in multi mode each style is separate (not a blend)
if mode != "multi":
res["blend_plan"] = self.blend_plan([s["id"] for s in styles]) if len(styles) > 1 else []
res["validate_cmd"] = self.validate_cmd(res)
res["subject_hint"] = self.subject_hint(text, [i["id"] for i in ids], mods, asset)
res["file_names"] = self.file_names(res)
return res
# ------------------------------------------------------------------
def diverse(self, n, asset, already):
"""Pick n styles spread over families, preferring styles that suit the asset."""
fams = OrderedDict()
for sid, s in self.reg.items():
fams.setdefault(s["family"], []).append(sid)
picks = list(already)
# preference order inside families: styles listing the asset type in best_for first
def pref(sid):
bf = self.reg[sid].get("best_for", [])
return (0 if asset in bf or "illustration" in bf and asset in ("scene", "character") else 1, sid)
# round-robin across families in a fixed, contrast-friendly order
order = ["pixel-retro", "east-asian", "digital-glossy", "craft", "technical", "decorative-eras", "print", "genre", "comics", "core-modern"]
fam_names = [f for f in order if f in fams] + [f for f in fams if f not in order]
idx = {f: 0 for f in fam_names}
while len(picks) < n:
progressed = False
for f in fam_names:
cand = sorted(fams[f], key=pref)
while idx[f] < len(cand) and cand[idx[f]] in picks:
idx[f] += 1
if idx[f] < len(cand):
picks.append(cand[idx[f]])
idx[f] += 1
progressed = True
if len(picks) >= n:
break
if not progressed:
break
return picks[:n]
def conflicts(self, a, b):
A, B = self.reg[a], self.reg[b]
return b in A.get("conflicts_with", []) or a in B.get("conflicts_with", [])
def compatible(self, a, b):
A, B = self.reg[a], self.reg[b]
return b in A.get("compatible_with", []) or a in B.get("compatible_with", [])
def blend_plan(self, ids):
if len(ids) < 2:
return []
p = self.reg[ids[0]]
lines = []
lines.append("PRIMARY %s owns STRUCTURE: geometry, stroke rules, composition, typography, level of detail, and every hard check in its brief." % p["name"])
for sid in ids[1:]:
s = self.reg[sid]
rel = "conflicts" if self.conflicts(ids[0], sid) else ("is a known-good pairing" if self.compatible(ids[0], sid) else "has no declared relationship")
lines.append("SECONDARY %s (%s with %s)" % (s["name"], rel, p["name"]))
if s.get("as_secondary"):
lines.append(" behaves as: " + s["as_secondary"])
else:
lines.append(" contributes SURFACE + MOOD only: palette character (%s), lighting, texture, atmosphere, motifs and decorative vocabulary. It does NOT override the primary's geometry, stroke, or level-of-detail rules." % s["palette"]["guidance"].split(".")[0])
pc, sc = p.get("checks", {}), s.get("checks", {})
forb = set(pc.get("forbid_elements", []))
if forb and (sc.get("min_filters") or sc.get("min_gradients")):
lines.append(" primary forbids <%s>: emulate the secondary's glow/gradient/soft effects with hard-edged stacked shapes at stepped opacities instead." % ", ".join(sorted(forb)))
if pc.get("max_colors"):
lines.append(" primary caps the palette at %d colours: fit the secondary's hues INTO that budget." % pc["max_colors"])
if self.conflicts(ids[0], sid):
lines.append(" CONFLICT: import only the secondary's colour mood and motifs; drop its rendering techniques where they break the primary's rules.")
lines.append("Sanity test: cover the palette - the drawing style must still read as %s; cover the drawing - the mood must still read as %s." % (p["name"], ", ".join(self.reg[i]["name"] for i in ids[1:])))
return lines
def validate_cmd(self, res):
if res["mode"] == "multi":
return "validate_svg.py <file>.svg --style <that file's style id> (run once per output file)"
if not res["styles"]:
return "validate_svg.py <file>.svg"
return "validate_svg.py <file>.svg --style %s" % res["styles"][0]["id"]
FILLER = (r"please|can you|could you|i want|i need|i d like|give me|make me|make|create|generate|draw|design|produce|build|"
r"an?|the|svg|vector|illustration|artwork|art|style|styled|version|versions|in|of|for|but|with|and|plus|different|same|"
r"styles|aesthetic|this|that|these|those|image|picture|photo|it|my|attached|uploaded|look|looks|like|convert|restyle|"
r"turn|transform|into|to|as|on|from|or|vs|versus|\d+|two|three|four|five|six|seven|eight|nine|ten|several|multiple|various|each|every|some")
def subject_hint(self, text, style_ids, mods, asset=None):
"""Best-effort subject phrase: the request minus style phrases, modifier words,
asset nouns and filler. Works on the normalised (lowercase) text."""
t = " " + text + " "
phrases = set()
seen = set()
stack = list(style_ids)
while stack:
sid = stack.pop()
if sid in seen or sid not in self.reg:
continue
seen.add(sid)
st = self.reg[sid]
phrases.update([st["name"], sid] + st.get("aliases", []) + st.get("weak_aliases", []))
if st.get("extends"):
stack.append(st["extends"])
spans = []
for ph in phrases:
for m in phrase_re(ph).finditer(t):
spans.append(m.span())
spans.sort()
merged = []
for sp in spans:
if merged and sp[0] <= merged[-1][1]:
merged[-1] = (merged[-1][0], max(merged[-1][1], sp[1]))
else:
merged.append(sp)
out, pos = [], 0
for a0, b0 in merged:
out.append(t[pos:a0])
out.append(" ")
pos = b0
out.append(t[pos:])
t = "".join(out)
mod_ids = {m["id"] for m in mods}
for m in self.mods:
if m["id"] in mod_ids:
for tr in m["triggers"]:
t = phrase_re(tr).sub(" ", t)
words = [w for ws in ASSET_TYPES.values() for w in ws]
for w in sorted(words, key=len, reverse=True):
t = phrase_re(w).sub(" ", t)
t = re.sub(r"(?<![a-z0-9])(" + self.FILLER + r")(?![a-z0-9])", " ", t)
t = re.sub(r"[\+\.,:;'\"]+", " ", t)
t = re.sub(r"\s+", " ", t).strip()
return t or None
def file_names(self, res):
base = slug(res["subject_hint"] or "artwork")[:40] or "artwork"
if res["mode"] == "multi":
ids = res.get("multi_styles", [])
return [base + ".svg"] + ["%s-%s.svg" % (base, i) for i in ids]
if res["styles"]:
return ["%s-%s.svg" % (base, "-".join(s["id"] for s in res["styles"]))]
return [base + ".svg"]
# ------------------------------------------------------------------
def menu(self):
fams = OrderedDict()
for sid, s in self.reg.items():
fams.setdefault(s["family"], []).append(s)
titles = {
"core-modern": "Clean & modern", "pixel-retro": "Pixel & retro games", "digital-glossy": "Digital, glossy & 3D",
"craft": "Handmade & illustrative", "comics": "Comics & manga", "east-asian": "East Asian traditions",
"decorative-eras": "Historic & decorative", "print": "Print & street", "technical": "Technical & data",
"genre": "Cinematic & genre",
}
out = ["Pick a style (or combine, e.g. \"cyberpunk + minimalist\"; or say \"5 styles\" for a comparison set):", ""]
for fam, items in fams.items():
out.append("%s: %s" % (titles.get(fam, fam.title()), " · ".join(s["name"] for s in items)))
return "\n".join(out)
def brief(self, sid, depth=0):
s = self.reg[sid]
out = []
parent = s.get("extends")
if parent and parent in self.reg and depth < 3:
out.append(self.brief(parent, depth + 1))
out.append("")
h = "#" * 2 if depth == 0 else "#" * 2
out.append("%s Style brief: %s (%s)%s" % (h, s["name"], s["id"], " [extends %s: parent rules apply, these specialise them]" % parent if parent else ""))
out.append(s["description"])
def bullets(title, items):
out.append("**%s**" % title)
for i in items:
out.append("- " + i)
bullets("Visual characteristics", s["visual_characteristics"])
out.append("**Palette**: %s" % s["palette"]["guidance"] + (" Swatches: " + " ".join(s["palette"].get("swatches", [])) if s["palette"].get("swatches") else ""))
for key, label in (("geometry", "Geometry"), ("stroke", "Stroke"), ("texture", "Texture"), ("composition", "Composition"), ("typography", "Typography"), ("lighting_depth", "Lighting & depth")):
out.append("**%s**: %s" % (label, s[key]))
bullets("SVG techniques", s["svg_techniques"])
bullets("Avoid", s["avoid"])
if s.get("checks"):
out.append("**Automated checks** (validate_svg.py --style %s): %s" % (sid, ", ".join("%s=%s" % kv for kv in s["checks"].items())))
out.append("**Deep reference**: styles/references/%s" % s["reference"])
return "\n".join(out)
def format_resolution(self, res, with_brief):
L = []
L.append("# Routing result")
L.append("- mode: **%s**%s asset: **%s** source: **%s**" % (res["mode"], " (n=%d)" % res["count"] if res.get("count") else "", res["asset"], res["source"]))
if res["subject_hint"]:
L.append("- subject (best guess): %s" % res["subject_hint"])
if res["needs_style"]:
L.append("- style: **NOT SPECIFIED** -> ask the user; do not pick one yourself.\n")
L.append(self.menu())
return "\n".join(L)
if res.get("confirm_style"):
L.append("- style: **WEAK MATCH** (only the generic word '%s') -> one-line check with the user: use %s, or pick from the menu (`--menu`)?" % (res["styles"][0]["matched"], self.reg[res["styles"][0]["id"]]["name"]))
if res["ambiguous"]:
L.append("- style: **AMBIGUOUS** -> ask which one the user means (offer these, plus the menu on request):")
for c in res["candidates"]:
L.append(" - %s (%s) via: %s" % (self.reg[c["id"]]["name"], c["id"], ", ".join(c["keywords"])))
return "\n".join(L)
if res["mode"] == "multi":
L.append("- variants: " + ", ".join(res["multi_styles"]))
L.append("- files: " + ", ".join(res["file_names"]))
else:
L.append("- styles: " + ", ".join("%s **%s**" % (s["role"], s["id"]) + (" (via '%s')" % s["matched"]) for s in res["styles"]))
L.append("- files: " + ", ".join(res["file_names"]))
if res["modifiers"]:
L.append("- modifiers:")
for m in res["modifiers"]:
L.append(" - **%s** (\"%s\"): %s" % (m["id"], m["phrase"], m["effect"]))
if res.get("blend_plan"):
L.append("- blend plan:")
for b in res["blend_plan"]:
L.append(" " + ("" if b.startswith(" ") else "- ") + b.strip() if not b.startswith(" ") else " " + b.strip())
L.append("- after drawing run: `%s`" % res["validate_cmd"])
if with_brief:
ids = res["multi_styles"] if res["mode"] == "multi" else [s["id"] for s in res["styles"]]
for sid in ids:
L.append("")
L.append(self.brief(sid))
return "\n".join(L)
# ------------------------------------------------------------------
def lint(self):
errs, warns = [], []
reg = self.reg
alias_owner = {}
for sid, s in reg.items():
for f in REQUIRED_FIELDS:
if f not in s or s[f] in (None, "", [], {}):
errs.append("%s: missing/empty field %r" % (sid, f))
if s.get("id") and s["id"] != slug(s["id"]):
errs.append("%s: id must be a lowercase slug" % sid)
pal = s.get("palette", {})
if not isinstance(pal, dict) or "guidance" not in pal:
errs.append("%s: palette must be an object with 'guidance' (and optional 'swatches')" % sid)
else:
for sw in pal.get("swatches", []):
if not re.fullmatch(r"#[0-9a-fA-F]{6}", sw):
errs.append("%s: swatch %r is not #rrggbb" % (sid, sw))
for rel in ("compatible_with", "conflicts_with", "extends"):
vals = s.get(rel, [])
vals = [vals] if isinstance(vals, str) else vals
for v in vals:
if v not in reg:
errs.append("%s: %s references unknown style %r" % (sid, rel, v))
if v == sid:
errs.append("%s: %s references itself" % (sid, rel))
both = set(s.get("compatible_with", [])) & set(s.get("conflicts_with", []))
if both:
errs.append("%s: styles both compatible and conflicting: %s" % (sid, ", ".join(sorted(both))))
ref = s.get("reference", "")
fname, _, anchor = ref.partition("#")
path = REFERENCE_DIR / fname
if not path.exists():
errs.append("%s: reference file %s does not exist" % (sid, fname))
elif anchor:
text = path.read_text(encoding="utf-8")
if not re.search(r"^#{1,4}\s.*\{#%s\}\s*$" % re.escape(anchor), text, re.M):
errs.append("%s: anchor {#%s} not found in %s" % (sid, anchor, fname))
for ph in [s.get("name", "")] + s.get("aliases", []):
n = norm(ph)
alias_owner.setdefault(n, sid) # shared aliases are intentional: they route to both styles (a blend)
if not all(isinstance(w, str) for w in s.get("weak_aliases", [])):
errs.append("%s: weak_aliases must be a list of strings" % sid)
for k in s.get("checks", {}):
if k not in KNOWN_CHECKS:
errs.append("%s: unknown check %r (see validate_svg.py run_style_checks)" % (sid, k))
if len(s.get("aliases", [])) < 2:
warns.append("%s: fewer than 2 aliases; routing may miss natural phrasings" % sid)
for sid, s in reg.items():
for o in s.get("compatible_with", []):
if o in reg and sid in reg[o].get("conflicts_with", []):
errs.append("%s says compatible with %s, but %s says it conflicts" % (sid, o, o))
for o in s.get("conflicts_with", []):
if o in reg and sid in reg[o].get("compatible_with", []):
errs.append("%s says it conflicts with %s, but %s says compatible" % (sid, o, o))
for m in self.mods:
if not m.get("triggers") or not m.get("effect"):
errs.append("modifier %s: needs triggers and effect" % m.get("id"))
return errs, warns
KNOWN_CHECKS = {
"forbid_elements", "require_elements_any", "max_colors", "min_colors", "forbid_path_curves", "integer_coords",
"require_shape_rendering", "max_filters", "min_filters", "max_gradients", "min_gradients", "min_elements",
"max_elements", "min_groups", "require_text", "forbid_text", "min_stroke_widths", "max_stroke_widths",
"require_stroke", "forbid_stroke", "require_linecap", "require_linejoin", "dark_base", "light_base", "allow_bleed",
}
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("prompt", nargs="?")
ap.add_argument("--json", action="store_true")
ap.add_argument("--no-brief", action="store_true", help="resolution only, without the style briefs")
ap.add_argument("--menu", action="store_true")
ap.add_argument("--list", action="store_true")
ap.add_argument("--show", metavar="ID", nargs="+")
ap.add_argument("--compat", nargs="+", metavar="ID")
ap.add_argument("--lint", action="store_true")
a = ap.parse_args()
r = Router()
if a.lint:
errs, warns = r.lint()
for w in warns:
print("warn ", w)
for e in errs:
print("ERROR", e)
print("%d styles, %d modifiers, %d errors, %d warnings" % (len(r.reg), len(r.mods), len(errs), len(warns)))
sys.exit(1 if errs else 0)
if a.menu:
print(r.menu())
return
if a.list:
for sid, s in r.reg.items():
print("%-22s %-28s %s" % (sid, s["name"], ", ".join(s.get("aliases", []))))
return
if a.show:
for sid in a.show:
if sid not in r.reg:
sys.exit("unknown style %r (try --list)" % sid)
print(r.brief(sid))
print()
return
if a.compat:
for sid in a.compat:
if sid not in r.reg:
sys.exit("unknown style %r" % sid)
print("\n".join(r.blend_plan(a.compat)))
return
if not a.prompt:
ap.print_help()
return
res = r.resolve(a.prompt)
if a.json:
print(json.dumps(res, indent=2))
else:
print(r.format_resolution(res, not a.no_brief))
if __name__ == "__main__":
main()
scripts/svgtools.py
#!/usr/bin/env python3
"""Shared helpers for SVG Style Studio scripts.
Pure-Python (lxml only). Provides:
- SVG parsing and namespace-free tag helpers
- affine transform parsing / composition
- path-data tokenising and conservative bounding boxes
- inherited style-property lookup (attributes, style="", simple <style> rules)
- colour normalisation
- style-registry loading
Nothing here renders pixels; see render_svg.py for that.
"""
from __future__ import annotations
import json
import math
import os
import re
import sys
from pathlib import Path
try:
from lxml import etree
except ImportError: # pragma: no cover
sys.exit("lxml is required: pip install lxml")
SKILL_DIR = Path(__file__).resolve().parent.parent
REGISTRY_DIR = SKILL_DIR / "styles" / "registry"
REFERENCE_DIR = SKILL_DIR / "styles" / "references"
SVG_NS = "http://www.w3.org/2000/svg"
XLINK_NS = "http://www.w3.org/1999/xlink"
SHAPE_TAGS = {"rect", "circle", "ellipse", "line", "polyline", "polygon", "path"}
GRAPHIC_TAGS = SHAPE_TAGS | {"text", "image", "use"}
# containers whose children are never painted directly
NON_RENDERED = {
"defs", "clipPath", "mask", "pattern", "symbol", "marker", "filter",
"linearGradient", "radialGradient", "style", "title", "desc", "metadata",
"script", "font", "cursor", "view",
}
INHERITED = {
"fill", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin",
"fill-opacity", "stroke-opacity", "visibility", "font-family", "font-size",
"shape-rendering", "fill-rule", "stroke-dasharray", "text-anchor",
}
# --------------------------------------------------------------------------
# parsing
# --------------------------------------------------------------------------
def local(tag) -> str:
if not isinstance(tag, str):
return ""
return tag.rsplit("}", 1)[-1] if "}" in tag else tag
def parse_svg(source):
"""Parse a path or SVG text. Returns (root, error_message_or_None)."""
parser = etree.XMLParser(remove_blank_text=False, resolve_entities=False, huge_tree=True)
try:
if isinstance(source, (str, Path)) and os.path.exists(str(source)):
tree = etree.parse(str(source), parser)
return tree.getroot(), None
data = source.encode("utf-8") if isinstance(source, str) else source
return etree.fromstring(data, parser), None
except etree.XMLSyntaxError as exc:
return None, str(exc)
except OSError as exc:
return None, str(exc)
def iter_elements(root):
for el in root.iter():
if isinstance(el.tag, str):
yield el
def get_href(el):
return el.get("{%s}href" % XLINK_NS) or el.get("href")
def ids_map(root):
out = {}
for el in iter_elements(root):
i = el.get("id")
if i is not None and i not in out:
out[i] = el
return out
# --------------------------------------------------------------------------
# numbers / lengths
# --------------------------------------------------------------------------
NUM_RE = re.compile(r"[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?")
def fnum(value, default=0.0):
"""Parse a leading number from an attribute (ignores px, pt, %, etc.)."""
if value is None:
return default
m = NUM_RE.search(str(value))
return float(m.group()) if m else default
def parse_viewbox(root):
vb = root.get("viewBox")
if not vb:
return None
nums = [float(x) for x in NUM_RE.findall(vb)]
if len(nums) != 4:
return None
return tuple(nums)
# --------------------------------------------------------------------------
# transforms
# --------------------------------------------------------------------------
IDENTITY = (1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
def mat_mul(m, n):
a1, b1, c1, d1, e1, f1 = m
a2, b2, c2, d2, e2, f2 = n
return (
a1 * a2 + c1 * b2,
b1 * a2 + d1 * b2,
a1 * c2 + c1 * d2,
b1 * c2 + d1 * d2,
a1 * e2 + c1 * f2 + e1,
b1 * e2 + d1 * f2 + f1,
)
def mat_apply(m, x, y):
a, b, c, d, e, f = m
return (a * x + c * y + e, b * x + d * y + f)
def parse_transform(s):
if not s:
return IDENTITY
m = IDENTITY
for name, args in re.findall(r"(\w+)\s*\(([^)]*)\)", s):
v = [float(x) for x in NUM_RE.findall(args)]
t = IDENTITY
if name == "translate" and v:
t = (1, 0, 0, 1, v[0], v[1] if len(v) > 1 else 0.0)
elif name == "scale" and v:
t = (v[0], 0, 0, v[1] if len(v) > 1 else v[0], 0, 0)
elif name == "rotate" and v:
a = math.radians(v[0])
ca, sa = math.cos(a), math.sin(a)
r = (ca, sa, -sa, ca, 0, 0)
if len(v) >= 3:
t = mat_mul(mat_mul((1, 0, 0, 1, v[1], v[2]), r), (1, 0, 0, 1, -v[1], -v[2]))
else:
t = r
elif name == "matrix" and len(v) == 6:
t = tuple(v)
elif name == "skewX" and v:
t = (1, 0, math.tan(math.radians(v[0])), 1, 0, 0)
elif name == "skewY" and v:
t = (1, math.tan(math.radians(v[0])), 0, 1, 0, 0)
m = mat_mul(m, t)
return m
# --------------------------------------------------------------------------
# path data
# --------------------------------------------------------------------------
_CMD_ARGS = {"M": 2, "L": 2, "H": 1, "V": 1, "C": 6, "S": 4, "Q": 4, "T": 2, "A": 7, "Z": 0}
class PathParseError(ValueError):
pass
def parse_path(d):
"""Parse path data into a list of (command, [absolute coords]) plus the raw
command letters used. Returns (segments, letters_used).
segments: list of tuples (cmd_upper, points) where points are absolute
(x, y) pairs (control points included; arcs give sampled points).
"""
i, n = 0, len(d)
segs = []
letters = []
cx = cy = 0.0
sx = sy = 0.0
cmd = None
def skip_sep():
nonlocal i
while i < n and d[i] in " ,\t\r\n":
i += 1
def read_num():
nonlocal i
skip_sep()
m = NUM_RE.match(d, i)
if not m:
raise PathParseError("expected number at %d in path data" % i)
i = m.end()
return float(m.group())
def read_flag():
nonlocal i
skip_sep()
if i < n and d[i] in "01":
v = int(d[i])
i += 1
return v
raise PathParseError("expected arc flag at %d" % i)
while True:
skip_sep()
if i >= n:
break
ch = d[i]
if ch.isalpha():
cmd = ch
i += 1
letters.append(cmd)
if cmd in "Zz":
segs.append(("Z", [(sx, sy)]))
cx, cy = sx, sy
continue
elif cmd is None:
raise PathParseError("path data must start with a command")
elif cmd in "Zz":
raise PathParseError("numbers after closepath")
up = cmd.upper()
rel = cmd.islower()
# parse one argument group for this command
if up == "M":
x, y = read_num(), read_num()
if rel:
x, y = cx + x, cy + y
cx, cy = x, y
sx, sy = x, y
segs.append(("M", [(x, y)]))
cmd = "l" if rel else "L" # implicit lineto
elif up == "L" or up == "T":
x, y = read_num(), read_num()
if rel:
x, y = cx + x, cy + y
cx, cy = x, y
segs.append((up, [(x, y)]))
elif up == "H":
x = read_num()
x = cx + x if rel else x
cx = x
segs.append(("H", [(cx, cy)]))
elif up == "V":
y = read_num()
y = cy + y if rel else y
cy = y
segs.append(("V", [(cx, cy)]))
elif up == "C":
v = [read_num() for _ in range(6)]
pts = [(v[0], v[1]), (v[2], v[3]), (v[4], v[5])]
if rel:
pts = [(cx + px, cy + py) for px, py in pts]
cx, cy = pts[-1]
segs.append(("C", pts))
elif up in "SQ":
v = [read_num() for _ in range(4)]
pts = [(v[0], v[1]), (v[2], v[3])]
if rel:
pts = [(cx + px, cy + py) for px, py in pts]
cx, cy = pts[-1]
segs.append((up, pts))
elif up == "A":
rx, ry, rot = read_num(), read_num(), read_num()
fa, fs = read_flag(), read_flag()
x, y = read_num(), read_num()
if rel:
x, y = cx + x, cy + y
pts = _arc_points(cx, cy, rx, ry, rot, fa, fs, x, y)
cx, cy = x, y
segs.append(("A", pts))
else:
raise PathParseError("unknown path command %r" % cmd)
return segs, letters
def inherit_text_attrs(root):
"""In-memory only (validation): copy inheritable text attributes from ancestors onto <text>
so bbox estimates honour group-level font-size / text-anchor. Never write this tree back."""
parents = {ch: p for p in root.iter() for ch in p if isinstance(ch.tag, str)}
for el in root.iter():
if not isinstance(el.tag, str) or local(el.tag) != "text":
continue
for attr in ("font-size", "text-anchor"):
if el.get(attr) is not None:
continue
cur = parents.get(el)
while cur is not None:
if cur.get(attr) is not None:
el.set(attr, cur.get(attr))
break
cur = parents.get(cur)
def _arc_points(x1, y1, rx, ry, phi_deg, fa, fs, x2, y2, samples=24):
"""Sample points along an SVG elliptical arc (endpoint parameterisation)."""
rx, ry = abs(rx), abs(ry)
if rx == 0 or ry == 0 or (x1 == x2 and y1 == y2):
return [(x2, y2)]
phi = math.radians(phi_deg)
cp, sp = math.cos(phi), math.sin(phi)
dx, dy = (x1 - x2) / 2.0, (y1 - y2) / 2.0
x1p = cp * dx + sp * dy
y1p = -sp * dx + cp * dy
lam = (x1p ** 2) / (rx ** 2) + (y1p ** 2) / (ry ** 2)
if lam > 1:
s = math.sqrt(lam)
rx, ry = rx * s, ry * s
num = rx ** 2 * ry ** 2 - rx ** 2 * y1p ** 2 - ry ** 2 * x1p ** 2
den = rx ** 2 * y1p ** 2 + ry ** 2 * x1p ** 2
co = math.sqrt(max(0.0, num / den)) if den else 0.0
if fa == fs:
co = -co
cxp = co * rx * y1p / ry
cyp = -co * ry * x1p / rx
cx = cp * cxp - sp * cyp + (x1 + x2) / 2.0
cy = sp * cxp + cp * cyp + (y1 + y2) / 2.0
def ang(ux, uy, vx, vy):
a = math.atan2(ux * vy - uy * vx, ux * vx + uy * vy)
return a
th1 = ang(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry)
dth = ang((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry)
if not fs and dth > 0:
dth -= 2 * math.pi
elif fs and dth < 0:
dth += 2 * math.pi
pts = []
for k in range(1, samples + 1):
t = th1 + dth * k / samples
px = cp * rx * math.cos(t) - sp * ry * math.sin(t) + cx
py = sp * rx * math.cos(t) + cp * ry * math.sin(t) + cy
pts.append((px, py))
return pts
def path_bbox(d):
"""Conservative bbox (control points included) of path data, or None."""
segs, _ = parse_path(d)
xs, ys = [], []
for _, pts in segs:
for x, y in pts:
xs.append(x)
ys.append(y)
if not xs:
return None
return (min(xs), min(ys), max(xs), max(ys))
# --------------------------------------------------------------------------
# styles / inherited properties
# --------------------------------------------------------------------------
_CSS_RULE = re.compile(r"([^{}]+)\{([^{}]*)\}")
def collect_css(root):
"""Parse simple <style> rules: .class, tag, #id (no combinators)."""
rules = {"class": {}, "tag": {}, "id": {}}
for st in root.iter("{%s}style" % SVG_NS, "style"):
text = st.text or ""
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S)
for sel, body in _CSS_RULE.findall(text):
props = parse_style_attr(body)
for one in sel.split(","):
one = one.strip()
if re.fullmatch(r"\.[\w-]+", one):
rules["class"].setdefault(one[1:], {}).update(props)
elif re.fullmatch(r"#[\w-]+", one):
rules["id"].setdefault(one[1:], {}).update(props)
elif re.fullmatch(r"[a-zA-Z][\w-]*", one):
rules["tag"].setdefault(one, {}).update(props)
return rules
def parse_style_attr(s):
out = {}
for part in (s or "").split(";"):
if ":" in part:
k, v = part.split(":", 1)
out[k.strip()] = v.strip()
return out
class StyleResolver:
def __init__(self, root):
self.root = root
self.css = collect_css(root)
self._cache = {}
def own(self, el, prop):
"""Value declared on el itself (style attr > CSS > presentation attr)."""
inline = parse_style_attr(el.get("style", ""))
if prop in inline:
return inline[prop]
i = el.get("id")
if i and prop in self.css["id"].get(i, {}):
return self.css["id"][i][prop]
for c in (el.get("class") or "").split():
if prop in self.css["class"].get(c, {}):
return self.css["class"][c][prop]
t = local(el.tag)
if prop in self.css["tag"].get(t, {}):
return self.css["tag"][t][prop]
return el.get(prop)
def get(self, el, prop, default=None):
node = el
while node is not None and isinstance(node.tag, str):
v = self.own(node, prop)
if v is not None and v != "inherit":
return v
if prop not in INHERITED:
break
node = node.getparent()
return default
def opacity_chain(self, el):
"""Product of opacity along the ancestor chain (opacity is not inherited
but compounds visually)."""
prod = 1.0
node = el
while node is not None and isinstance(node.tag, str):
v = self.own(node, "opacity")
if v is not None:
try:
prod *= float(v.rstrip("%")) / (100.0 if v.endswith("%") else 1.0)
except ValueError:
pass
node = node.getparent()
return prod
def displayed(self, el):
node = el
while node is not None and isinstance(node.tag, str):
if (self.own(node, "display") or "").strip() == "none":
return False
node = node.getparent()
return True
# --------------------------------------------------------------------------
# colours
# --------------------------------------------------------------------------
try: # PIL knows all CSS colour names
from PIL import ImageColor as _IC
except Exception: # pragma: no cover
_IC = None
def norm_color(value):
"""Return '#rrggbb' or None for none/url()/currentColor/unparseable."""
if value is None:
return None
v = value.strip().lower()
if v in ("", "none", "transparent", "currentcolor", "inherit") or v.startswith("url("):
return None
if re.fullmatch(r"#[0-9a-f]{3}", v):
return "#" + "".join(ch * 2 for ch in v[1:])
if re.fullmatch(r"#[0-9a-f]{6}", v):
return v
if re.fullmatch(r"#[0-9a-f]{8}", v):
return v[:7]
m = re.fullmatch(r"rgba?\(([^)]*)\)", v)
if m:
parts = [p.strip() for p in re.split(r"[,\s/]+", m.group(1)) if p.strip()]
try:
rgb = []
for p in parts[:3]:
rgb.append(round(float(p[:-1]) * 2.55) if p.endswith("%") else round(float(p)))
return "#%02x%02x%02x" % tuple(max(0, min(255, c)) for c in rgb)
except ValueError:
return None
if _IC is not None:
try:
r, g, b = _IC.getrgb(v)[:3]
return "#%02x%02x%02x" % (r, g, b)
except ValueError:
return None
return None
def luminance(hexcolor):
r, g, b = (int(hexcolor[i:i + 2], 16) / 255.0 for i in (1, 3, 5))
def lin(c):
return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)
def hex_to_hsl(hexcolor):
import colorsys
r, g, b = (int(hexcolor[i:i + 2], 16) / 255.0 for i in (1, 3, 5))
h, l, s = colorsys.rgb_to_hls(r, g, b)
return h * 360.0, s, l
# --------------------------------------------------------------------------
# element geometry (bbox in the root user space)
# --------------------------------------------------------------------------
def local_bbox(el, ids=None, _depth=0):
"""bbox of a single graphic element in its own coordinate system
(element's own transform NOT applied). Returns None if unknown."""
t = local(el.tag)
try:
if t == "rect":
x, y = fnum(el.get("x")), fnum(el.get("y"))
w, h = fnum(el.get("width")), fnum(el.get("height"))
return (x, y, x + w, y + h)
if t == "circle":
cx, cy, r = fnum(el.get("cx")), fnum(el.get("cy")), fnum(el.get("r"))
return (cx - r, cy - r, cx + r, cy + r)
if t == "ellipse":
cx, cy = fnum(el.get("cx")), fnum(el.get("cy"))
rx, ry = fnum(el.get("rx")), fnum(el.get("ry"))
return (cx - rx, cy - ry, cx + rx, cy + ry)
if t == "line":
x1, y1, x2, y2 = (fnum(el.get(k)) for k in ("x1", "y1", "x2", "y2"))
return (min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2))
if t in ("polyline", "polygon"):
nums = [float(x) for x in NUM_RE.findall(el.get("points", ""))]
pts = list(zip(nums[0::2], nums[1::2]))
if not pts:
return None
xs, ys = [p[0] for p in pts], [p[1] for p in pts]
return (min(xs), min(ys), max(xs), max(ys))
if t == "path":
return path_bbox(el.get("d", ""))
if t == "image":
x, y = fnum(el.get("x")), fnum(el.get("y"))
return (x, y, x + fnum(el.get("width")), y + fnum(el.get("height")))
if t == "text":
size = fnum(el.get("font-size"), 16.0)
txt = "".join(el.itertext()).strip()
x, y = fnum(el.get("x")), fnum(el.get("y"))
w = len(txt) * size * 0.58
if fnum(el.get("textLength"), 0) > 0:
w = fnum(el.get("textLength"))
anchor = el.get("text-anchor", "start")
if anchor == "middle":
x -= w / 2
elif anchor == "end":
x -= w
return (x, y - size * 0.8, x + w, y + size * 0.25)
if t == "use" and ids is not None and _depth < 6:
href = get_href(el)
if href and href.startswith("#") and href[1:] in ids:
tgt = ids[href[1:]]
inner = subtree_bbox(tgt, ids, IDENTITY, _depth + 1, include_self_transform=True)
if inner:
tx, ty = fnum(el.get("x")), fnum(el.get("y"))
return (inner[0] + tx, inner[1] + ty, inner[2] + tx, inner[3] + ty)
except PathParseError:
return None
return None
def transform_bbox(m, bb):
x0, y0, x1, y1 = bb
pts = [mat_apply(m, x, y) for x, y in ((x0, y0), (x1, y0), (x1, y1), (x0, y1))]
xs, ys = [p[0] for p in pts], [p[1] for p in pts]
return (min(xs), min(ys), max(xs), max(ys))
def union(a, b):
if a is None:
return b
if b is None:
return a
return (min(a[0], b[0]), min(a[1], b[1]), max(a[2], b[2]), max(a[3], b[3]))
def subtree_bbox(el, ids, parent_m=IDENTITY, _depth=0, include_self_transform=True):
"""Union bbox of everything painted under el (skips non-rendered containers)."""
t = local(el.tag)
if t in NON_RENDERED and _depth > 0:
return None
m = parent_m
if include_self_transform:
m = mat_mul(parent_m, parse_transform(el.get("transform")))
if t in GRAPHIC_TAGS:
bb = local_bbox(el, ids, _depth)
return transform_bbox(m, bb) if bb else None
out = None
for ch in el:
if isinstance(ch.tag, str):
out = union(out, subtree_bbox(ch, ids, m, _depth + 1, True))
return out
_CLIP_RE = re.compile(r"url\(\s*['\"]?#([^)'\"\s]+)")
def clip_bbox(el, ids, parents):
"""Root-space bbox that ancestors' (and el's own) clip-path attributes confine el to.
Approximation: a clipPath's extent is the union of its children's bboxes, placed with the
matrix of the element that references it (clipPathUnits=objectBoundingBox is ignored).
Returns None when el is unclipped.
"""
chain, cur = [], el
while cur is not None:
chain.append(cur)
cur = parents.get(cur)
chain.reverse() # root ... el
m, out = IDENTITY, None
for node in chain:
m = mat_mul(m, parse_transform(node.get("transform")))
cp = node.get("clip-path")
mt = _CLIP_RE.search(cp) if cp else None
if not mt:
continue
cpe = (ids or {}).get(mt.group(1))
if cpe is None or local(cpe.tag) != "clipPath":
continue
box = None
for ch in cpe:
if isinstance(ch.tag, str):
box = union(box, subtree_bbox(ch, ids, m, 1, True))
if box is None:
continue
out = box if out is None else (max(out[0], box[0]), max(out[1], box[1]), min(out[2], box[2]), min(out[3], box[3]))
return out
def walk_painted(root, ids=None):
"""Yield (element, matrix_to_root_space) for every painted graphic element
in document order, skipping defs/clipPath/etc."""
def rec(el, m):
t = local(el.tag)
if t in NON_RENDERED and el is not root:
return
cur = mat_mul(m, parse_transform(el.get("transform"))) if el is not root else m
if t in GRAPHIC_TAGS:
yield el, cur
return
for ch in el:
if isinstance(ch.tag, str):
yield from rec(ch, cur)
yield from rec(root, IDENTITY)
# --------------------------------------------------------------------------
# registry
# --------------------------------------------------------------------------
def load_registry(directory=None):
"""Load every styles/registry/*.json. Each file is a JSON list of style
objects or {"styles": [...]}. Returns dict id -> style (with '_file')."""
directory = Path(directory) if directory else REGISTRY_DIR
styles = {}
for f in sorted(directory.glob("*.json")):
try:
data = json.loads(f.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
raise SystemExit("Invalid JSON in %s: %s" % (f, exc))
items = data["styles"] if isinstance(data, dict) and "styles" in data else data
if not isinstance(items, list):
raise SystemExit("%s must contain a list of styles" % f)
for s in items:
s["_file"] = f.name
if s.get("id") in styles:
raise SystemExit("Duplicate style id %r (in %s and %s)" % (s.get("id"), styles[s["id"]]["_file"], f.name))
styles[s["id"]] = s
return styles
def slug(text):
return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
scripts/text_to_path.py
#!/usr/bin/env python3
"""text_to_path.py - outline text as SVG <path> data, so logos/posters don't depend on installed fonts.
SVG fonts are never embedded; a <text> element renders in whatever font the viewer has.
For logos, posters and anything handed off for print, outline the lettering with this tool.
Needs fontTools (pip install fonttools; add brotli for .woff2). Any .ttf/.otf/.woff/.woff2 works.
CLI
text_to_path.py FONT "TEXT" [--size 96] [--x 0] [--y 0] [--anchor start|middle|end]
[--tracking 0] [--width W] [--fill "#111"] [--id wordmark]
-> prints one <path> element (paste into your SVG). --width fits the text to W units by
adjusting the tracking (like textLength="W" lengthAdjust="spacing").
--d-only prints just the path data. --glyphs prints a <g aria-label> with one <path> per glyph
(recommended for long titles: small paths, letters individually editable).
Python
sys.path.insert(0, S); from text_to_path import text_path, text_group
d, width = text_path("Anton-Regular.ttf", "HIGHLANDS", size=120, x=400, y=1000, anchor="middle")
"""
from __future__ import annotations
import argparse
import sys
from functools import lru_cache
try:
from fontTools.pens.svgPathPen import SVGPathPen
from fontTools.pens.transformPen import TransformPen
from fontTools.ttLib import TTFont
except ImportError: # pragma: no cover
sys.exit("text_to_path.py needs fontTools: pip install fonttools brotli")
@lru_cache(maxsize=16)
def _font(path):
f = TTFont(path)
return f, f.getGlyphSet(), f.getBestCmap(), f["head"].unitsPerEm
def _kern_pairs(font):
"""Old-style 'kern' table pairs (GPOS kerning is ignored; tracking usually dominates in display type)."""
pairs = {}
if "kern" in font:
for sub in font["kern"].kernTables:
pairs.update(getattr(sub, "kernTable", {}) or {})
return pairs
def measure(font_path, text, size, tracking=0.0):
font, gs, cmap, upm = _font(font_path)
scale = size / upm
kern = _kern_pairs(font)
names = [cmap.get(ord(ch), ".notdef") for ch in text]
w = 0.0
for i, gn in enumerate(names):
w += gs[gn].width * scale
if i + 1 < len(names):
w += kern.get((gn, names[i + 1]), 0) * scale + tracking
return w
def text_path(font_path, text, size=96, x=0.0, y=0.0, anchor="start", tracking=0.0, width=None, precision=1):
"""Return (path_d, advance_width). y is the baseline, like SVG <text>."""
font, gs, cmap, upm = _font(font_path)
scale = size / upm
if width is not None and len(text) > 1:
tracking = tracking + (width - measure(font_path, text, size, tracking)) / (len(text) - 1)
total = measure(font_path, text, size, tracking)
x0 = x - (total / 2 if anchor == "middle" else total if anchor == "end" else 0)
kern = _kern_pairs(font)
names = [cmap.get(ord(ch), ".notdef") for ch in text]
parts, cx = [], x0
for i, gn in enumerate(names):
pen = SVGPathPen(gs, ntos=lambda v: ("%.*f" % (precision, v)).rstrip("0").rstrip("."))
# font units are y-up; SVG is y-down
gs[gn].draw(TransformPen(pen, (scale, 0, 0, -scale, cx, y)))
d = pen.getCommands()
if d:
parts.append(d)
cx += gs[gn].width * scale
if i + 1 < len(names):
cx += kern.get((gn, names[i + 1]), 0) * scale + tracking
return " ".join(parts), total
def text_glyphs(font_path, text, size=96, x=0.0, y=0.0, anchor="start", tracking=0.0, width=None, precision=1):
"""Like text_path but one path string per glyph (keeps each <path> small and letters individually editable)."""
out, x_cursor = [], None
font, gs, cmap, upm = _font(font_path)
scale = size / upm
if width is not None and len(text) > 1:
tracking = tracking + (width - measure(font_path, text, size, tracking)) / (len(text) - 1)
total = measure(font_path, text, size, tracking)
x_cursor = x - (total / 2 if anchor == "middle" else total if anchor == "end" else 0)
kern = _kern_pairs(font)
names = [cmap.get(ord(ch), ".notdef") for ch in text]
for i, gn in enumerate(names):
d, _ = text_path(font_path, text[i], size, x_cursor, y, "start", 0, None, precision)
if d:
out.append(d)
x_cursor += gs[gn].width * scale
if i + 1 < len(names):
x_cursor += kern.get((gn, names[i + 1]), 0) * scale + tracking
return out, total
def text_group(font_path, text, size=96, x=0.0, y=0.0, anchor="start", tracking=0.0, width=None, attrs=""):
"""<g aria-label="TEXT" ...> with one <path> per glyph - the recommended form for logos and titles."""
glyphs, _ = text_glyphs(font_path, text, size, x, y, anchor, tracking, width)
label = text.replace("&", "&").replace('"', """).replace("<", "<")
return '<g aria-label="%s" %s>%s</g>' % (label, attrs, "".join('<path d="%s"/>' % d for d in glyphs))
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("font")
ap.add_argument("text")
ap.add_argument("--size", type=float, default=96)
ap.add_argument("--x", type=float, default=0)
ap.add_argument("--y", type=float, default=0)
ap.add_argument("--anchor", choices=["start", "middle", "end"], default="start")
ap.add_argument("--tracking", type=float, default=0)
ap.add_argument("--width", type=float)
ap.add_argument("--fill", default="#111111")
ap.add_argument("--id")
ap.add_argument("--d-only", action="store_true")
ap.add_argument("--glyphs", action="store_true")
a = ap.parse_args()
if a.glyphs:
idattr = 'id="%s" ' % a.id if a.id else ""
print(text_group(a.font, a.text, a.size, a.x, a.y, a.anchor, a.tracking, a.width, attrs='%sfill="%s"' % (idattr, a.fill)))
return
d, w = text_path(a.font, a.text, a.size, a.x, a.y, a.anchor, a.tracking, a.width)
if a.d_only:
print(d)
else:
idattr = ' id="%s"' % a.id if a.id else ""
print('<path%s aria-label="%s" fill="%s" d="%s"/>' % (idattr, a.text.replace('"', """), a.fill, d))
print("<!-- advance width: %.1f -->" % w, file=sys.stderr)
if __name__ == "__main__":
main()
scripts/validate_svg.py
#!/usr/bin/env python3
"""validate_svg.py - static QC for SVG Style Studio.
Usage:
validate_svg.py FILE.svg [FILE2.svg ...] [--style STYLE_ID ...] [--json] [--strict]
Checks (maps to the QC pipeline in SKILL.md):
syntax, root/xmlns, viewBox, broken/duplicate ids, unused defs, embedded
raster, gradients/masks/clipPaths/filters sanity, invisible objects,
content clipped / off-canvas / cramped, z-order (hidden shapes, obscured
text, background not first), balance, complexity, duplicate geometry,
precision bloat, and optional per-style conformance from the registry's
"checks" block.
Exit code: 0 = no errors, 1 = errors (or warnings with --strict).
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from collections import Counter, defaultdict
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from svgtools import ( # noqa: E402
NON_RENDERED, SHAPE_TAGS, StyleResolver, fnum, get_href, ids_map,
iter_elements, load_registry, local, mat_mul, norm_color, parse_path,
parse_svg, parse_viewbox, parse_transform, PathParseError, transform_bbox,
walk_painted, clip_bbox, inherit_text_attrs, local_bbox, IDENTITY, NUM_RE, union,
)
LEVELS = {"error": 0, "warn": 1, "info": 2}
class Report:
def __init__(self, path):
self.path = str(path)
self.findings = []
self.stats = {}
def add(self, level, code, msg, el=None):
where = None
if el is not None:
where = local(el.tag)
if el.get("id"):
where += "#" + el.get("id")
if getattr(el, "sourceline", None):
where += " (line %d)" % el.sourceline
self.findings.append({"level": level, "code": code, "message": msg, "where": where})
@property
def errors(self):
return [f for f in self.findings if f["level"] == "error"]
@property
def warnings(self):
return [f for f in self.findings if f["level"] == "warn"]
URL_REF = re.compile(r"url\(\s*['\"]?#([^)'\" ]+)['\"]?\s*\)")
def validate(path, style_ids=None, registry=None):
rep = Report(path)
p = Path(path)
if not p.exists():
rep.add("error", "missing-file", "file not found")
return rep
raw = p.read_text(encoding="utf-8", errors="replace")
rep.stats["bytes"] = len(raw.encode("utf-8"))
# 1. syntax ------------------------------------------------------------
root, err = parse_svg(str(p))
if root is None:
rep.add("error", "xml-syntax", "not well-formed XML: %s" % err)
return rep
if local(root.tag) != "svg":
rep.add("error", "root", "root element is <%s>, expected <svg>" % local(root.tag))
return rep
if root.nsmap.get(None) != "http://www.w3.org/2000/svg" and not root.tag.startswith("{http://www.w3.org/2000/svg}"):
rep.add("error", "xmlns", 'missing xmlns="http://www.w3.org/2000/svg" on <svg>')
ids = ids_map(root)
res = StyleResolver(root)
elements = list(iter_elements(root))
rep.stats["elements"] = len(elements)
# 3. viewBox -----------------------------------------------------------
vb = parse_viewbox(root)
if vb is None:
rep.add("error", "viewbox", "missing or invalid viewBox (needed for scaling)")
vb = (0.0, 0.0, fnum(root.get("width"), 100), fnum(root.get("height"), 100))
elif vb[2] <= 0 or vb[3] <= 0:
rep.add("error", "viewbox", "viewBox has non-positive size")
W, H = vb[2], vb[3]
rep.stats["viewBox"] = list(vb)
for attr in ("width", "height"):
v = root.get(attr)
if v and re.fullmatch(r"\s*[\d.]+(px)?\s*", v):
rep.add("info", "fixed-size", "<svg %s=%r> is fixed-size; drop it or use 100%% so the file is responsive" % (attr, v))
ar = W / H if H else 1
if ar > 6 or ar < 1 / 6:
rep.add("warn", "aspect", "extreme aspect ratio %.2f" % ar)
if root.find("{http://www.w3.org/2000/svg}title") is None and root.find("title") is None:
rep.add("warn", "title", "no <title>; add one for accessibility")
# 2. references / ids ---------------------------------------------------
id_counts = Counter(e.get("id") for e in elements if e.get("id"))
for i, c in id_counts.items():
if c > 1:
rep.add("error", "duplicate-id", "id %r used %d times" % (i, c))
referenced = set()
for el in elements:
blobs = []
for k, v in el.attrib.items():
blobs.append(v)
if local(k) == "href" and v.startswith("#"):
referenced.add(v[1:])
if v[1:] not in ids:
rep.add("error", "broken-href", "href points to missing id %r" % v[1:], el)
elif local(k) == "href" and re.match(r"https?:", v):
rep.add("warn", "external-ref", "external resource %r will not load offline" % v[:60], el)
if local(el.tag) == "style" and el.text:
blobs.append(el.text)
if "@import" in el.text:
rep.add("warn", "css-import", "@import in <style> is not portable", el)
for blob in blobs:
for ref in URL_REF.findall(blob):
referenced.add(ref)
if ref not in ids:
rep.add("error", "broken-url-ref", "url(#%s) refers to a missing id" % ref, el)
for k in el.attrib:
if k.lower().startswith("on"):
rep.add("error", "event-handler", "inline event handler %r is not allowed" % k, el)
for el in elements:
t = local(el.tag)
if t == "script":
rep.add("error", "script", "<script> in artwork; SVG art should be static", el)
if t == "foreignObject":
rep.add("warn", "foreignobject", "<foreignObject> is poorly supported outside browsers", el)
if t == "image":
h = get_href(el) or ""
if h.startswith("data:image") and not h.startswith("data:image/svg"):
rep.add("error", "embedded-raster", "embedded raster image; artwork must be real vector", el)
elif h and not h.startswith("#"):
rep.add("warn", "image-ref", "<image> reference to %r; prefer native vector shapes" % h[:50], el)
# unused ids inside <defs>
for el in elements:
if local(el.tag) in NON_RENDERED and local(el.tag) not in ("title", "desc", "style", "metadata", "defs", "script") and el.get("id"):
if el.get("id") not in referenced:
# allow <symbol>/<marker> etc. only if unused -> warn
rep.add("info", "unused-def", "definition %r is never referenced" % el.get("id"), el)
# 7. gradients / masks / clipPaths / filters ---------------------------
for el in elements:
t = local(el.tag)
if t in ("linearGradient", "radialGradient"):
stops = [c for c in el if local(c.tag) == "stop"]
href = get_href(el)
if not stops and not (href and href.lstrip("#") in ids):
rep.add("error", "gradient-stops", "%s has no <stop> children" % t, el)
offs = [fnum(s.get("offset"), 0) / (100 if (s.get("offset") or "").endswith("%") else 1) for s in stops]
if offs and any(b < a - 1e-9 for a, b in zip(offs, offs[1:])):
rep.add("warn", "gradient-order", "gradient stops are not in ascending offset order", el)
if t == "radialGradient" and el.get("r") is not None and fnum(el.get("r")) <= 0:
rep.add("error", "gradient-radius", "radialGradient r <= 0 renders nothing", el)
elif t in ("clipPath", "mask"):
kids = [c for c in el if isinstance(c.tag, str)]
if not kids:
rep.add("error", "empty-" + t.lower(), "<%s> is empty; anything using it disappears" % t, el)
elif t == "filter":
prims = [c for c in el if local(c.tag).startswith("fe")]
if not prims:
rep.add("error", "empty-filter", "<filter> has no primitives; targets may vanish", el)
results = set()
for pr in prims:
for attr in ("in", "in2"):
v = pr.get(attr)
if v and v not in ("SourceGraphic", "SourceAlpha", "BackgroundImage", "BackgroundAlpha", "FillPaint", "StrokePaint") and v not in results:
rep.add("error", "filter-input", "filter primitive %s=%r has no earlier result with that name" % (attr, v), pr)
if pr.get("result"):
results.add(pr.get("result"))
if local(pr.tag) == "feGaussianBlur":
sd = fnum(pr.get("stdDeviation"), 0)
if sd > 0.05 * max(W, H) and not el.get("width"):
rep.add("warn", "filter-region", "large blur (%.1f) with default filter region may clip the glow; set x/y/width/height on <filter>" % sd, el)
n_filt = sum(1 for e in elements if local(e.tag) == "filter")
n_filt_use = sum(1 for e in elements if "filter" in (e.get("filter") or "") + (e.get("style") or ""))
rep.stats["filters"] = n_filt
rep.stats["filter_uses"] = n_filt_use
if n_filt_use > 12:
rep.add("warn", "filter-heavy", "%d elements use filters; group them or replace with vector techniques" % n_filt_use)
# painted elements ------------------------------------------------------
inherit_text_attrs(root)
painted = list(walk_painted(root, ids))
parents = {ch: p for p in root.iter() for ch in p if isinstance(ch.tag, str)}
rep.stats["painted"] = len(painted)
if not painted:
rep.add("error", "empty", "no painted graphic elements")
return rep
# 6. invisible objects, 4. clipped ------------------------------------
boxes = [] # (el, bbox in root space, opaque)
for el, m in painted:
t = local(el.tag)
disp = res.displayed(el)
vis = (res.get(el, "visibility", "visible") or "visible").strip()
op = res.opacity_chain(el)
if not disp or vis in ("hidden", "collapse"):
rep.add("warn", "invisible", "element is hidden (display:none / visibility:hidden)", el)
continue
if op <= 0.001:
rep.add("warn", "invisible", "opacity 0 makes this element invisible", el)
continue
fill = res.get(el, "fill", "black")
stroke = res.get(el, "stroke", "none")
fo = fnum(res.get(el, "fill-opacity", "1"), 1)
so = fnum(res.get(el, "stroke-opacity", "1"), 1)
sw = fnum(res.get(el, "stroke-width", "1"), 1)
has_fill = (fill or "none").strip() != "none" and fo > 0 and t != "line"
has_stroke = (stroke or "none").strip() != "none" and so > 0 and sw > 0
if t in SHAPE_TAGS and not has_fill and not has_stroke:
# polylines/lines default fill (black) is irrelevant when stroke none
rep.add("warn", "invisible", "no fill and no stroke: nothing is painted", el)
continue
bb = local_bbox(el, ids)
if t in SHAPE_TAGS:
zero = False
if t == "rect" and (fnum(el.get("width")) <= 0 or fnum(el.get("height")) <= 0):
zero = True
if t == "circle" and fnum(el.get("r")) <= 0:
zero = True
if t == "ellipse" and (fnum(el.get("rx")) <= 0 or fnum(el.get("ry")) <= 0):
zero = True
if zero:
rep.add("warn", "zero-size", "zero-size shape paints nothing", el)
continue
if t == "path":
try:
parse_path(el.get("d", ""))
except PathParseError as exc:
rep.add("error", "path-syntax", "invalid path data: %s" % exc, el)
continue
if not el.get("d", "").strip():
rep.add("warn", "empty-path", "empty path data", el)
continue
if bb is None:
continue
pad = sw / 2 if has_stroke else 0
bbr = transform_bbox(m, (bb[0] - pad, bb[1] - pad, bb[2] + pad, bb[3] + pad))
cb = clip_bbox(el, ids, parents)
if cb is not None:
bbr = (max(bbr[0], cb[0]), max(bbr[1], cb[1]), min(bbr[2], cb[2]), min(bbr[3], cb[3]))
if bbr[2] <= bbr[0] or bbr[3] <= bbr[1]:
continue
opaque = bool(has_fill and fo >= 0.95 and op >= 0.95 and norm_color(fill) is not None)
boxes.append((el, bbr, opaque, has_fill))
vx0, vy0, vx1, vy1 = vb[0], vb[1], vb[0] + W, vb[1] + H
canvas_area = W * H
def area(b):
return max(0.0, b[2] - b[0]) * max(0.0, b[3] - b[1])
bg_like = set()
for el, bb, opq, hf in boxes:
if area(bb) >= 0.85 * canvas_area:
bg_like.add(id(el))
allow_bleed = False
style_specs = [registry[s] for s in (style_ids or []) if registry and s in registry]
for s in style_specs:
if s.get("checks", {}).get("allow_bleed"):
allow_bleed = True
content = None
off_canvas = 0
for el, bb, opq, hf in boxes:
ix0, iy0, ix1, iy1 = max(bb[0], vx0), max(bb[1], vy0), min(bb[2], vx1), min(bb[3], vy1)
if ix1 <= ix0 or iy1 <= iy0:
off_canvas += 1
rep.add("warn", "off-canvas", "element lies completely outside the viewBox", el)
continue
if id(el) in bg_like:
continue
content = union(content, bb)
rep.stats["off_canvas"] = off_canvas
if content:
m_l, m_t = (content[0] - vx0) / W, (content[1] - vy0) / H
m_r, m_b = (vx1 - content[2]) / W, (vy1 - content[3]) / H
rep.stats["content_margins_pct"] = [round(100 * v, 1) for v in (m_l, m_t, m_r, m_b)]
fill_ratio = area(content) / canvas_area if canvas_area else 0
rep.stats["content_fill_pct"] = round(100 * fill_ratio, 1)
if min(m_l, m_t, m_r, m_b) < -0.002:
if not allow_bleed:
rep.add("warn", "clipped", "content extends past the viewBox on at least one side (margins %% L/T/R/B: %s); important content may be clipped" % [round(100 * v, 1) for v in (m_l, m_t, m_r, m_b)])
elif min(m_l, m_t, m_r, m_b) < 0.015 and not allow_bleed:
rep.add("info", "tight-margin", "content is within 1.5%% of the edge (margins %% L/T/R/B: %s)" % [round(100 * v, 1) for v in (m_l, m_t, m_r, m_b)])
if fill_ratio < 0.30:
rep.add("warn", "small-subject", "subject bounding box fills only %.0f%% of the canvas; scale up or tighten the viewBox" % (100 * fill_ratio))
# visual weight centroid (area weighted, capped so huge shapes don't dominate)
tot = cx = cy = 0.0
for el, bb, opq, hf in boxes:
if id(el) in bg_like or not hf:
continue
a = min(area(bb), 0.25 * canvas_area)
if a <= 0:
continue
tot += a
cx += a * (bb[0] + bb[2]) / 2
cy += a * (bb[1] + bb[3]) / 2
if tot:
ox = (cx / tot - (vx0 + W / 2)) / W
oy = (cy / tot - (vy0 + H / 2)) / H
rep.stats["weight_offset_pct"] = [round(100 * ox, 1), round(100 * oy, 1)]
if abs(ox) > 0.18 or abs(oy) > 0.18:
rep.add("info", "balance", "visual weight sits %.0f%%/%.0f%% from centre (x/y); fine for deliberate asymmetry, otherwise rebalance" % (100 * ox, 100 * oy))
# 5. z-order ------------------------------------------------------------
order = {id(el): i for i, (el, *_rest) in enumerate(boxes)}
if boxes:
big = [i for i, (el, bb, *_r) in enumerate(boxes) if id(el) in bg_like]
if big and big[0] != 0:
rep.add("warn", "z-background", "a canvas-sized shape is painted after smaller shapes and may cover them", boxes[big[0]][0])
for i in big[1:]:
el, bb, opq, hf = boxes[i]
if opq and i > 0:
rep.add("warn", "z-cover", "opaque canvas-sized shape painted late (element %d of %d) covers earlier art" % (i + 1, len(boxes)), el)
break
if len(boxes) <= 1500:
for i, (el, bb, opq, hf) in enumerate(boxes):
if area(bb) <= 0:
continue
for j in range(i + 1, len(boxes)):
el2, bb2, opq2, hf2 = boxes[j]
if not opq2 or local(el2.tag) not in ("rect", "circle", "ellipse"):
continue
if parse_transform(el2.get("transform")) != IDENTITY and local(el2.tag) == "rect":
continue
if local(el2.tag) != "rect":
# circles/ellipses cover only their inscribed box (~70%)
cx0 = bb2[0] + (bb2[2] - bb2[0]) * 0.146
cx1 = bb2[2] - (bb2[2] - bb2[0]) * 0.146
cy0 = bb2[1] + (bb2[3] - bb2[1]) * 0.146
cy1 = bb2[3] - (bb2[3] - bb2[1]) * 0.146
cover = (cx0, cy0, cx1, cy1)
else:
cover = bb2
if bb[0] >= cover[0] - 1e-6 and bb[1] >= cover[1] - 1e-6 and bb[2] <= cover[2] + 1e-6 and bb[3] <= cover[3] + 1e-6:
lvl = "warn" if local(el.tag) == "text" else "info"
what = "text is hidden behind a later opaque shape" if local(el.tag) == "text" else "fully hidden behind a later opaque shape (wasted geometry or wrong z-order)"
if id(el) not in bg_like and area(bb) > 0.0005 * canvas_area:
rep.add(lvl, "z-hidden", what, el)
break
# text sanity
for el, m in painted:
if local(el.tag) == "text":
ff = (res.get(el, "font-family", "") or "").lower()
if not ff:
rep.add("info", "font-missing", "text has no font-family; renderers will fall back unpredictably", el)
elif not re.search(r"(serif|sans-serif|monospace|cursive|fantasy|system-ui)", ff):
rep.add("info", "font-fallback", "font-family %r lacks a generic fallback; fonts are not embedded (outline text to paths for logos)" % ff[:60], el)
# complexity / hygiene ------------------------------------------------------
d_lens = []
long_decimals = 0
geo = defaultdict(list)
for el in elements:
if local(el.tag) == "path":
d = el.get("d", "")
d_lens.append(len(d))
if len(d) > 6000:
rep.add("warn", "huge-path", "path data is %d chars; split into meaningful sub-shapes or simplify" % len(d), el)
long_decimals += len(re.findall(r"\d+\.\d{4,}", d))
geo[("path", d.strip())].append(el)
elif local(el.tag) in ("polygon", "polyline"):
geo[(local(el.tag), el.get("points", "").strip())].append(el)
rep.stats["path_chars"] = sum(d_lens)
if long_decimals > 20:
rep.add("info", "precision", "%d numbers carry 4+ decimals; run optimize_svg.py to trim precision" % long_decimals)
for (t, data), els in geo.items():
if len(els) >= 3 and len(data) > 30:
rep.add("info", "duplicate-geometry", "%d <%s> elements share identical geometry; define once and <use> it" % (len(els), t), els[1])
if rep.stats["bytes"] > 1_000_000:
rep.add("warn", "size", "file is %.0f KB; consider simplifying" % (rep.stats["bytes"] / 1024))
elif rep.stats["bytes"] > 400_000:
rep.add("info", "size", "file is %.0f KB" % (rep.stats["bytes"] / 1024))
groups = [e for e in elements if local(e.tag) == "g"]
empties = [g for g in groups if len([c for c in g if isinstance(c.tag, str)]) == 0]
for g in empties[:5]:
rep.add("info", "empty-group", "empty <g>", g)
rep.stats["groups"] = len(groups)
named = [g for g in groups if g.get("id") or g.get("class") or g.get("data-name")]
rep.stats["named_groups"] = len(named)
if len(painted) > 25 and len(named) < 3:
rep.add("info", "structure", "%d shapes but only %d named groups; group semantically (id=\"background\", \"subject\", ...) for editability" % (len(painted), len(named)))
# style conformance ------------------------------------------------------------
colors = Counter()
stroke_widths = Counter()
curve_paths = 0
non_int = 0
for el, m in painted:
c = norm_color(res.get(el, "fill", None) if local(el.tag) != "text" else res.get(el, "fill", None))
if c:
colors[c] += 1
c2 = norm_color(res.get(el, "stroke", None))
if c2:
colors[c2] += 1
stroke_widths[round(fnum(res.get(el, "stroke-width", "1"), 1), 2)] += 1
if local(el.tag) == "path":
try:
_, letters = parse_path(el.get("d", ""))
if any(l.upper() in "CSQTA" for l in letters):
curve_paths += 1
except PathParseError:
pass
if local(el.tag) == "rect":
for k in ("x", "y", "width", "height"):
v = el.get(k)
if v and abs(fnum(v) - round(fnum(v))) > 1e-6:
non_int += 1
break
stop_colors = [norm_color(s.get("stop-color") or parse_style(s).get("stop-color")) for s in elements if local(s.tag) == "stop"]
for c in stop_colors:
if c:
colors[c] += 1
rep.stats["distinct_colors"] = len(colors)
rep.stats["distinct_stroke_widths"] = len(stroke_widths)
rep.stats["gradients"] = sum(1 for e in elements if local(e.tag) in ("linearGradient", "radialGradient"))
rep.stats["patterns"] = sum(1 for e in elements if local(e.tag) == "pattern")
rep.stats["text_elements"] = sum(1 for e in elements if local(e.tag) == "text")
# outlined lettering (text_to_path.py) counts as typography: <g|path aria-label="...">
rep.stats["outlined_text"] = sum(1 for e in root.iter() if isinstance(e.tag, str) and e is not root and e.get("aria-label") and local(e.tag) in ("g", "path"))
for spec in style_specs:
run_style_checks(rep, spec, elements, painted, colors, stroke_widths, curve_paths, non_int, res, root)
for sid in (style_ids or []):
if not registry or sid not in registry:
rep.add("warn", "unknown-style", "style %r not found in registry; conformance checks skipped" % sid)
return rep
def parse_style(el):
from svgtools import parse_style_attr
return parse_style_attr(el.get("style", ""))
def run_style_checks(rep, spec, elements, painted, colors, stroke_widths, curve_paths, non_int, res, root):
chk = spec.get("checks") or {}
sid = spec["id"]
tags = Counter(local(e.tag) for e in elements)
def w(code, msg):
rep.add("warn", "style:%s:%s" % (sid, code), "[%s] %s" % (spec["name"], msg))
for t in chk.get("forbid_elements", []):
if tags.get(t):
w("forbid-" + t, "<%s> used %d times but this style avoids it" % (t, tags[t]))
req_any = chk.get("require_elements_any")
if req_any and not any(tags.get(t) for t in req_any):
w("require-any", "expected at least one of: %s" % ", ".join(req_any))
if chk.get("max_colors") and len(colors) > chk["max_colors"]:
w("max-colors", "%d distinct colours; this style keeps a palette of <= %d" % (len(colors), chk["max_colors"]))
if chk.get("min_colors") and len(colors) < chk["min_colors"]:
w("min-colors", "only %d distinct colours; expected >= %d" % (len(colors), chk["min_colors"]))
if chk.get("forbid_path_curves") and curve_paths:
w("curves", "%d paths use curves/arcs; this style is straight-edged" % curve_paths)
if chk.get("integer_coords") and non_int:
w("grid", "%d <rect> elements have non-integer coordinates; snap to the grid" % non_int)
sr = chk.get("require_shape_rendering")
if sr:
n_ok = sum(1 for e, _m in painted if (res.get(e, "shape-rendering") or "") == sr)
if n_ok < max(1, int(0.9 * len(painted))):
w("shape-rendering", 'set shape-rendering="%s" on <svg> or the pixel group (%d/%d elements have it)' % (sr, n_ok, len(painted)))
if chk.get("max_filters") is not None and rep.stats.get("filter_uses", 0) > chk["max_filters"]:
w("max-filters", "%d filtered elements; limit is %d for this style" % (rep.stats["filter_uses"], chk["max_filters"]))
if chk.get("min_filters") is not None and rep.stats.get("filters", 0) < chk["min_filters"]:
w("min-filters", "expected at least %d filter(s) (e.g. glow) for this style" % chk["min_filters"])
if chk.get("max_gradients") is not None and rep.stats["gradients"] > chk["max_gradients"]:
w("max-gradients", "%d gradients; limit is %d for this style" % (rep.stats["gradients"], chk["max_gradients"]))
if chk.get("min_gradients") is not None and rep.stats["gradients"] < chk["min_gradients"]:
w("min-gradients", "expected at least %d gradient(s)" % chk["min_gradients"])
if chk.get("min_elements") and rep.stats["painted"] < chk["min_elements"]:
w("min-elements", "only %d painted elements; this style expects richer detail (>= %d)" % (rep.stats["painted"], chk["min_elements"]))
if chk.get("max_elements") and rep.stats["painted"] > chk["max_elements"]:
w("max-elements", "%d painted elements; this style stays simple (<= %d)" % (rep.stats["painted"], chk["max_elements"]))
if chk.get("min_groups") and rep.stats["groups"] < chk["min_groups"]:
w("min-groups", "only %d groups; layer the artwork (>= %d)" % (rep.stats["groups"], chk["min_groups"]))
if chk.get("require_text") and not (rep.stats["text_elements"] or rep.stats.get("outlined_text")):
w("text", "this style/format expects typography (<text>)")
if chk.get("forbid_text") and (rep.stats["text_elements"] or rep.stats.get("outlined_text")):
w("text", "this style avoids text")
if chk.get("min_stroke_widths") and len(stroke_widths) < chk["min_stroke_widths"]:
w("line-hierarchy", "%d distinct stroke widths; expected >= %d for line hierarchy" % (len(stroke_widths), chk["min_stroke_widths"]))
if chk.get("max_stroke_widths") and len(stroke_widths) > chk["max_stroke_widths"]:
w("stroke-consistency", "%d distinct stroke widths; expected <= %d for consistent weight" % (len(stroke_widths), chk["max_stroke_widths"]))
if chk.get("require_stroke") and not stroke_widths:
w("stroke", "no strokes found; this style relies on outlines")
if chk.get("forbid_stroke") and stroke_widths:
w("stroke", "strokes used; this style is fill-only")
if chk.get("require_linecap"):
caps = {res.get(e, "stroke-linecap") for e, _m in painted if res.get(e, "stroke") not in (None, "none")}
if caps and caps != {chk["require_linecap"]}:
w("linecap", 'stroke-linecap should be "%s" (found %s)' % (chk["require_linecap"], sorted(c or "butt" for c in caps)))
if chk.get("require_linejoin"):
joins = {res.get(e, "stroke-linejoin") for e, _m in painted if res.get(e, "stroke") not in (None, "none")}
if joins and joins != {chk["require_linejoin"]}:
w("linejoin", 'stroke-linejoin should be "%s" (found %s)' % (chk["require_linejoin"], sorted(j or "miter" for j in joins)))
if chk.get("dark_base") or chk.get("light_base"):
from svgtools import luminance
base = base_color(painted, res, root)
if base:
L = luminance(base)
if chk.get("dark_base") and L > 0.12:
w("dark-base", "the base/background colour %s is light; this style sits on a dark base" % base)
if chk.get("light_base") and L < 0.30:
w("light-base", "the base/background colour %s is dark; this style sits on a light base" % base)
def base_color(painted, res, root):
"""Colour of the first painted element that looks like a background (or simply
the first painted element); gradients resolve to their first stop."""
ids = ids_map(root)
for el, _m in painted:
f = res.get(el, "fill", None)
if f is None or f.strip() == "none":
continue
c = norm_color(f)
if c:
return c
m = URL_REF.search(f)
if m and m.group(1) in ids:
g = ids[m.group(1)]
for st in g.iter():
if isinstance(st.tag, str) and local(st.tag) == "stop":
return norm_color(st.get("stop-color") or parse_style(st).get("stop-color"))
return None
return None
def format_text(rep, verbose_info=True):
lines = []
e, wn = len(rep.errors), len(rep.warnings)
status = "FAIL" if e else ("WARN" if wn else "OK")
lines.append("%s %s (%d errors, %d warnings)" % (status, rep.path, e, wn))
s = rep.stats
keys = ["viewBox", "bytes", "painted", "groups", "named_groups", "gradients", "patterns", "filters", "distinct_colors", "distinct_stroke_widths", "content_margins_pct", "content_fill_pct", "weight_offset_pct"]
bits = ["%s=%s" % (k, s[k]) for k in keys if k in s]
lines.append(" stats: " + ", ".join(bits))
for f in sorted(rep.findings, key=lambda f: LEVELS[f["level"]]):
if f["level"] == "info" and not verbose_info:
continue
loc = " @ " + f["where"] if f["where"] else ""
lines.append(" %-5s %-28s %s%s" % (f["level"].upper(), f["code"], f["message"], loc))
return "\n".join(lines)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("files", nargs="+")
ap.add_argument("--style", action="append", default=[], help="style id(s) to check conformance against (repeatable)")
ap.add_argument("--json", action="store_true")
ap.add_argument("--strict", action="store_true", help="treat warnings as failures")
ap.add_argument("--quiet", action="store_true", help="hide info-level notes")
args = ap.parse_args()
registry = None
if args.style:
registry = load_registry()
reports = [validate(f, args.style, registry) for f in args.files]
if args.json:
print(json.dumps([{"file": r.path, "ok": not r.errors, "stats": r.stats, "findings": r.findings} for r in reports], indent=2))
else:
print("\n\n".join(format_text(r, not args.quiet) for r in reports))
bad = any(r.errors or (args.strict and r.warnings) for r in reports)
sys.exit(1 if bad else 0)
if __name__ == "__main__":
main()
SKILL.md
---
name: create-svg
description: SVG Style Studio (/create-svg) - art director + vector illustrator that makes polished, editable SVGs (illustrations, logos, icons, posters, infographics, stickers) in 44 styles - pixel art, 8/16-bit, cyberpunk, Y2K, Frutiger Aero, glassmorphism, clay, isometric, low poly, paper cutout, doodle, comic, manga, sumi-e, ukiyo-e, art nouveau, art deco, bauhaus, brutalist, vintage poster, screen print, risograph, blueprint, technical/scientific, medieval, graffiti, sticker, children's, editorial, infographic, cinematic, fantasy, sci-fi, horror, retro futurism, gothic, luxury, monochrome, geometric, botanical. Use for any request for SVG/vector art, style blends ("cyberpunk + minimalist", "ukiyo-e but darker"), one subject in several styles ("in 5 styles"), or converting an SVG/image to a style. Routes the style, applies its design rules, then validates, optimizes, renders and inspects before delivering.
---
# SVG Style Studio · `/create-svg`
**Invocation.** `/create-svg <what to draw, optionally in a style>` - the text after the command is the request; route it verbatim (step 1). Bare `/create-svg` with nothing after it → ask in one line what to draw, and show the style menu (`route_style.py --menu`). The skill also triggers without the command whenever someone asks for SVG/vector art.
You are an **SVG art director + vector illustrator**, not a code generator. Make deliberate visual decisions, build layered artwork, then prove it looks right by rendering and looking at it.
Everything below is relative to this skill's folder. `S` = its `scripts/` directory
(e.g. `S=.claude/skills/create-svg/scripts` in a project, `~/.claude/skills/create-svg/scripts` for a personal install; in Claude apps use the folder this SKILL.md was loaded from).
Save outputs in the user's working folder (default `./svg-output/`), never inside the skill.
## Workflow
### 1. Route the request
```bash
python3 $S/route_style.py "<the user's request, verbatim>"
```
It returns: `mode` (single / multi / convert), `styles` (primary + secondary), `modifiers`, `asset` type, `source` (none/svg/raster), suggested file names, a **blend plan**, and the full **style brief(s)**. Read them - they are your art direction.
- `needs_style` → **ask the user to choose**; show `python3 $S/route_style.py --menu`. Never pick a style yourself for a single artwork.
- `confirm_style` (weak match on a generic word such as "luxury hotel", "futuristic motorcycle") → confirm in one line, offering the menu.
- `ambiguous` (e.g. "Japanese style") → ask which of the listed candidates.
- Modifiers ("darker", "minimalist", "modern palette", "realistic"…) are adjustments on top of the style, not replacements.
- Unsure what a style means? `python3 $S/route_style.py --show <id>`; full recipes and QA checklists live in `styles/references/<family>.md#<id>`.
### 2. Plan before drawing
Write the design brief (template in `reference/composition.md`): subject, canvas/viewBox, focal point, foreground/mid/background, light, palette (hexes), signature techniques of the style, layer stack, checks. Icons, logos, posters and infographics have their own rules there too.
### 3. Build the SVG
Follow `reference/svg-craft.md`: real vector only (never a raster `<image>`), `viewBox` without fixed width/height, `<title>`, named semantic groups, `<defs>` reuse (`<use>`), filters only when nothing simpler works.
- Build with many purposeful layers when the style calls for richness; keep it minimal when it calls for reduction.
- Lettering in logos/posters: outline it with `$S/text_to_path.py FONT "TEXT" --glyphs` (any .ttf/.otf/.woff2 you have) instead of relying on installed fonts.
- Style-specific helpers: **pixel styles → author ASCII and run `$S/pixel_grid.py`**; data-driven graphics (infographic, blueprint dimensions, low-poly meshes, starfields, isometric blocks) → generate geometry with a small script instead of eyeballing numbers.
### 4. Validate → optimise → render → look
```bash
python3 $S/validate_svg.py art.svg --style <primary-id>
python3 $S/optimize_svg.py art.svg --in-place # --precision 0 for pixel grids
python3 $S/validate_svg.py art.svg --style <primary-id>
python3 $S/render_svg.py art.svg -o /tmp/preview.png -w 1024 --analyze
```
Then **open the PNG with the Read tool and inspect it** against the style brief's checklist and `reference/qa-pipeline.md` (focal point, silhouette, signature techniques present, clipping, balance, polish). Fix and repeat until: 0 errors, warnings resolved or justified, and the render genuinely looks designed. If rendering is unavailable, say the visual check was not performed.
### 5. Deliver
Give the file path(s) plus 2-4 lines: style decisions made (and modifiers applied), anything approximated, and one suggested next step. Do not paste the SVG source unless asked. For multi-style: also the comparison sheet.
## Modes (details in `reference/modes.md`)
- **Blend** ("cyberpunk + minimalist", "pixel art with a horror aesthetic"): primary owns structure (geometry, strokes, composition, detail, hard limits); secondary contributes palette mood, lighting, texture, motifs. Emulate forbidden techniques (e.g. glow in pixel art = stepped rings). Max 3 styles. Validate against the primary.
- **Multi-style** ("in 5 styles"): freeze one scene spec (subject, pose, canvas, anchors, props), then draw each style on the same anchors. Files: `fox.svg` (optional base) + `fox-<style-id>.svg`. Finish with `render_svg.py fox-*.svg --sheet fox-styles.png` and check the set is comparable. Style list not given → pick contrasting families and say which.
- **Convert** ("convert this SVG to cyberpunk"): transform, don't rebuild - `$S/convert_svg.py inspect | recolor | strokes | inject`. Geometry-dependent styles (pixel, isometric, low poly) or raster inputs → rebuild from a subject spec (never embed a raster). Report what was preserved vs changed.
## Style library (44)
Ask with `route_style.py --menu`; list with `--list`. Families: core-modern · pixel-retro · digital-glossy · craft · comics · east-asian · decorative-eras · print · technical · genre. Registry: `styles/registry/*.json`; add styles by following `README.md` (`scripts/new_style.py`).
## Ground rules
- Styles are **visual languages** (geometry, line, texture, light, composition, typography), never just colour presets. Ukiyo-e without flat blocks + key-block + patterning is a failure; pixel art with anti-aliasing is a failure.
- Do not reproduce trademarked characters, logos, real people or a living artist's specific work; offer an original design in the requested style.
- Never invent statistics for infographics; label placeholder data.
- Say honestly what you could not verify or approximate.
styles/modifiers.json
{
"_doc": "Modifier vocabulary. 'triggers' are matched (after normalisation) anywhere outside a recognised style phrase. 'effect' is shown verbatim to Claude in the routing brief: it must be an actionable art-direction instruction that ADJUSTS the base style without abandoning its signature rules. Add entries freely.",
"modifiers": [
{"id": "darker", "triggers": ["darker", "dark", "dark version", "moodier", "gloomier", "grimmer", "nocturnal", "at night", "night time", "nighttime"],
"effect": "Push the palette 25-35% darker: deepen the base, lower midtone lightness, shift hues slightly cooler, concentrate light into one or two motivated sources, add a soft vignette. Keep the style's own line/shape rules unchanged."},
{"id": "lighter", "triggers": ["lighter", "brighter", "airier", "sunnier", "daytime", "high key"],
"effect": "Lift the palette 20-30%: light base, reduce shadow density, widen highlights, use warmer or cleaner hues. Keep contrast sufficient for the subject to stay legible."},
{"id": "muted", "triggers": ["muted", "desaturated", "dusty", "faded", "washed out", "subdued", "pastel"],
"effect": "Cut saturation ~35-50%, narrow contrast slightly, prefer greyed and chalky versions of the style's hues. (For 'pastel', raise lightness too.)"},
{"id": "vibrant", "triggers": ["vibrant", "saturated", "punchy", "colorful", "colourful", "vivid", "loud"],
"effect": "Raise saturation and hue separation: purer accents, fewer greys, complementary pairs next to each other. Keep one dominant colour so it does not turn into confetti."},
{"id": "minimalist", "triggers": ["minimalist", "minimalistic", "minimal", "simplified", "simple", "stripped back", "reduced"],
"effect": "Cut detail ~60%: keep only silhouette-defining shapes, 3-4 colours, generous negative space (>=40% of canvas). Preserve ONE signature technique of the base style (e.g. a single neon edge, one halftone area) so it is still recognisably that style."},
{"id": "detailed", "triggers": ["detailed", "intricate", "ornate", "elaborate", "highly detailed", "complex", "rich detail"],
"effect": "Raise detail: add secondary forms, layered depth, pattern fills, small props and edge detail. Keep a clear focal point and a strong silhouette so the added detail does not flatten the hierarchy."},
{"id": "realistic", "triggers": ["realistic", "more realistic", "lifelike", "naturalistic", "true to life", "photoreal", "semi realistic"],
"effect": "Use natural proportions and anatomy, motivated single-source lighting with cast shadows, subtle tonal modelling and material cues while keeping the base style's rendering vocabulary (its inks, edges, textures). Never embed a raster; build the realism from layered gradients and many small shapes."},
{"id": "stylized", "triggers": ["stylized", "stylised", "exaggerated", "cartoony", "cartoonish", "caricature"],
"effect": "Exaggerate proportions and shapes, simplify anatomy into strong geometric primitives, push silhouette and gesture."},
{"id": "cute", "triggers": ["cute", "kawaii", "adorable", "chibi", "wholesome", "cuddly"],
"effect": "Large head, small body, big low-set eyes, rounded terminals, soft palette, small blush accents, no sharp corners."},
{"id": "playful", "triggers": ["playful", "whimsical", "quirky", "fun", "cheerful", "friendly"],
"effect": "Bouncy asymmetry, slightly off-grid rotations, saturated friendly accents, small decorative confetti/sparkle elements, round terminals."},
{"id": "serious", "triggers": ["serious", "sober", "corporate", "professional", "formal", "authoritative"],
"effect": "Restrained palette, symmetrical or grid-locked layout, consistent stroke weight, no novelty decoration, clean typography."},
{"id": "luxurious", "triggers": ["luxurious", "luxury feel", "elegant", "sophisticated", "refined", "high end", "premium feel", "opulent"],
"effect": "Dark or ivory base, restrained metallic accents (gradient gold/champagne), hairline strokes, generous margins, fine symmetrical ornament."},
{"id": "gritty", "triggers": ["gritty", "grungy", "rough", "distressed", "worn", "weathered", "battered", "dirty"],
"effect": "Add wear: irregular edges (feTurbulence/feDisplacementMap on ONE group, or hand-nudged points), speckle/scratch layers at low opacity, uneven ink density, colour registration offsets."},
{"id": "clean", "triggers": ["clean", "crisp", "sharp", "polished", "pristine", "precise"],
"effect": "Remove texture and imperfections; exact geometry, consistent stroke weights, aligned edges, tidy spacing."},
{"id": "warm", "triggers": ["warmer", "warm palette", "warm tones", "golden hour", "sunset colors", "sunset colours", "cozy"],
"effect": "Shift palette toward amber/coral/rust; warm lights vs cool-tinted shadows; keep neutrals slightly creamy."},
{"id": "cool", "triggers": ["cooler", "cool palette", "cool tones", "icy", "frosty", "wintry"],
"effect": "Shift palette toward teal/blue/violet; cool lights vs slightly warm accent for contrast."},
{"id": "modern-palette", "triggers": ["modern palette", "modern colors", "modern colours", "contemporary palette", "contemporary colors", "updated palette", "fresh palette"],
"effect": "Keep the base style's forms, line and ornament exactly, but replace its historical palette with a contemporary one: clean off-white or deep ink base, 1-2 saturated accents (e.g. electric blue, coral, mint), softened neutrals. No sepia/parchment tones."},
{"id": "vintage-palette", "triggers": ["vintage palette", "retro palette", "retro colors", "retro colours", "aged paper", "sepia"],
"effect": "Warm off-white paper base, desaturated primaries, ink-black or dark brown key colour, slight yellowing toward edges."},
{"id": "neon-palette", "triggers": ["neon palette", "neon colors", "neon colours", "glowing", "glow"],
"effect": "Add a neon accent set (hot magenta, cyan, electric violet, acid yellow) on a dark ground; use controlled glow on 2-4 hero edges only."},
{"id": "monochrome-palette", "triggers": ["black and white", "monochromatic", "single colour", "single color", "one color", "one colour", "two tone", "two color", "two colour", "duotone"],
"effect": "Restrict to one hue (or two inks for duotone) plus paper; express form purely through value, line and pattern."},
{"id": "high-contrast", "triggers": ["high contrast", "bold contrast", "stark"],
"effect": "Compress midtones, separate values hard, use the darkest dark and lightest light in the palette, keep shapes graphic."},
{"id": "soft", "triggers": ["soft", "softer", "gentle", "dreamy", "hazy", "ethereal", "pillowy"],
"effect": "Rounder corners, lower contrast edges, gradients instead of hard shadows, atmospheric haze layers at low opacity."},
{"id": "bold", "triggers": ["bolder", "chunky", "heavy", "thick lines", "thick outlines", "bold lines"],
"effect": "Increase stroke weight ~50-100%, simplify inner detail, enlarge shapes, tighten composition."},
{"id": "thin-line", "triggers": ["thin lines", "thin line", "delicate", "fine line", "hairline", "linework only", "line art"],
"effect": "Reduce stroke to hairline (about 1/3 of the base weight), remove or lighten fills, rely on line hierarchy and negative space."},
{"id": "symmetrical", "triggers": ["symmetrical", "symmetric", "symmetry", "mirrored", "centered", "centred"],
"effect": "Build one half and mirror it with <use transform=\"matrix(-1 0 0 1 W 0)\"> around the axis; align all key features to the axis."},
{"id": "dynamic", "triggers": ["dynamic", "action shot", "in motion", "energetic", "dramatic angle", "dramatic"],
"effect": "Diagonal composition, foreshortened forms, speed lines or motion smear, strong value contrast at the focal point."},
{"id": "calm", "triggers": ["calm", "peaceful", "serene", "tranquil", "quiet", "zen"],
"effect": "Horizontal structure, generous negative space, low contrast, single soft focal point, slow curves."},
{"id": "spooky", "triggers": ["spooky", "creepy", "eerie", "unsettling", "haunted", "sinister"],
"effect": "Cold sickly palette with one warm point of light, elongated or distorted proportions, deep vignette, fog layers, off-balance framing."},
{"id": "futuristic", "triggers": ["high tech", "high-tech", "techy", "cyber"],
"effect": "Add HUD-like linework, thin technical strokes, glowing accent edges, grid or circuit motifs, cool-biased palette."},
{"id": "hand-made", "triggers": ["hand made", "handmade", "hand crafted", "handcrafted", "organic feel", "imperfect", "wobbly"],
"effect": "Introduce controlled imperfection: slightly irregular line weight, wobble in paths (hand-nudged points), slight mis-registration between fill and outline."},
{"id": "geometric-construct", "triggers": ["geometric", "constructed from circles", "made of triangles", "made of shapes", "built from shapes"],
"effect": "Construct the subject from a small set of primitives (circles, triangles, arcs) on a hidden grid."},
{"id": "transparent-bg", "triggers": ["transparent background", "no background", "without background", "isolated"],
"effect": "Omit the background rect entirely so the artwork sits on transparency; keep a soft shadow only if the style needs grounding."},
{"id": "square", "triggers": ["square format", "square canvas", "1 1", "1:1"],
"effect": "Use a square viewBox (e.g. 0 0 1024 1024)."},
{"id": "wide", "triggers": ["wide format", "landscape format", "banner format", "16 9", "16:9", "widescreen"],
"effect": "Use a wide viewBox (e.g. 0 0 1600 900); compose along the horizontal with the subject on a third."},
{"id": "tall", "triggers": ["portrait format", "vertical format", "poster format", "9 16", "9:16", "tall format"],
"effect": "Use a tall viewBox (e.g. 0 0 900 1400 or 0 0 900 1600); stack focal image, title and supporting text vertically."}
]
}
styles/references/comics.md
# Comics & manga
Reference for: Comic Book, Manga.
---
## Comic Book {#comic-book}
**Layer stack**: `paper` → `flat-colour` → `halftone-shadows` → `ink-line` → `spotted-blacks` → `speed-lines/FX` → `lettering/captions` → `panel-border`.
**Design moves**
- Ink before colour: decide where the blacks go (they anchor the composition), then flat colour, then halftone for shadow.
- Three line weights: contour 4-6, interior 2-3, detail 1. Foreground thick, background thin.
- Mis-registration: offset the colour layer by (1.5, 1) relative to ink.
**Recipe - Ben-Day halftone at 3 densities**
```svg
<defs>
<pattern id="ht-l" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="4" cy="4" r="1.2" fill="#d81b60"/></pattern>
<pattern id="ht-m" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="4" cy="4" r="2.0" fill="#d81b60"/></pattern>
<pattern id="ht-d" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="4" cy="4" r="2.9" fill="#d81b60"/></pattern>
</defs>
<path d="…shadow region…" fill="url(#ht-m)"/>
```
**Recipe - impact burst + outlined SFX**
```svg
<polygon id="burst" points="…alternating radii 90/55, 16 points…" fill="#ffd60a" stroke="#111" stroke-width="5" stroke-linejoin="miter"/>
<text x="256" y="270" text-anchor="middle" font-family="Impact, 'Arial Black', sans-serif" font-size="72"
fill="#e63946" stroke="#111" stroke-width="6" paint-order="stroke" transform="rotate(-8 256 256)">POW!</text>
```
**QA**: [ ] ≥ 2 stroke widths [ ] ≥ 1 halftone `<pattern>` [ ] blacks are *shapes* (not just thick lines) [ ] no gradients [ ] dynamic angle/cropping
---
## Manga {#manga}
**Layer stack**: `paper` → `screentone regions` → `solid-black shadows` → `ink-lines (tapered)` → `effect lines` → `speech/SFX` → `panel frame`.
**Design moves**
- Contours are *tapered filled paths* (thick on shadow side, hairline at highlights), not uniform strokes. Simulate with 2 layers if needed: a 3-unit stroke + a thinner paper-coloured stroke offset on the lit side.
- Hair as flowing clumps with highlight gaps left as paper.
- Focus/speed lines: many `<line>`s radiating from a point, varying length and weight, clipped to the panel.
- Colour pages: keep cel colours flat with a single cool shadow tone.
**Recipe - screentone set**
```svg
<pattern id="tone-20" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="2" cy="2" r=".55" fill="#111"/></pattern>
<pattern id="tone-50" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="2" cy="2" r="1.1" fill="#111"/></pattern>
<pattern id="tone-70" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><circle cx="2" cy="2" r="1.6" fill="#111"/></pattern>
```
**Recipe - radial focus lines**
```svg
<g stroke="#111" stroke-linecap="round" clip-path="url(#panel)">
<line x1="256" y1="256" x2="256" y2="-20" stroke-width="2"/> <!-- repeat with rotate(i*7.5 256 256), vary length + width -->
</g>
```
**QA**: [ ] ≥ 2 stroke widths [ ] ≥ 1 screentone pattern [ ] ≤ 10 colours (mostly B/W) [ ] no gradient shading [ ] eyes/face have the strongest contrast
styles/references/core-modern.md
# Core / Modern styles
Reference for: Minimal / Modern, Flat Design, Geometric, Monochrome, Luxury / Premium.
Registry entries hold the rules; this file holds **build order, SVG recipes and QA checklists**.
---
## Minimal / Modern {#minimal-modern}
**Layer stack**: `background` → `subject-mass` (1-3 shapes) → `accent` (1 small shape) → optional `shadow-hint`.
**Design moves**
1. Squint test first: reduce the subject to a silhouette of 2-4 primitives. If it does not read, add a *defining* shape - never texture.
2. Pick the grid unit U (8 or 16). Every coordinate is a multiple of U/2.
3. One accent colour on the single thing the eye should land on.
**Recipe - palette as variables (easy recolouring)**
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<title>Minimal fox mark</title>
<style>
:root { --bg:#f5f3ee; --ink:#1a1a1f; --accent:#ff5a36; }
</style>
<rect id="background" width="512" height="512" fill="var(--bg)"/>
<g id="subject">
<path d="M136 328 L256 152 L376 328 Q256 400 136 328Z" fill="var(--ink)"/>
<circle id="accent" cx="256" cy="288" r="24" fill="var(--accent)"/>
</g>
</svg>
```
> CSS variables are honoured by browsers and most editors, but some converters ignore them. For files that must survive Illustrator/Figma round-trips, inline the hex values and keep the palette in a comment.
**QA checklist**
- [ ] Reads at 32×32 px
- [ ] ≥ 40 % of the canvas is empty
- [ ] ≤ 4 colours, ≤ 2 stroke widths, no filters
- [ ] Optical centring: circles/triangles nudged 2-3 % so they *look* centred
---
## Flat Design {#flat-design}
**Layer stack**: `bg` → `far-shapes` → `mid-scene` → `subject` (base shapes, then `shade` shapes) → `props` → `long-shadow`.
**Design moves**
- Every object = base fill + one darker "shade" shape + optionally one lighter "hilite" shape. No blends.
- Keep saturation equal across hues so nothing "pops" by accident.
- Long shadow: a polygon projected at 45°, `fill-opacity .12-.2`, clipped to the object's ground.
**Recipe - shade shape via clip-path (define once, reuse)**
```svg
<defs>
<clipPath id="clip-body"><rect x="120" y="160" width="200" height="220" rx="48"/></clipPath>
</defs>
<rect x="120" y="160" width="200" height="220" rx="48" fill="#ef476f"/>
<!-- shade: same shape, offset circle clipped inside it -->
<circle cx="330" cy="330" r="190" fill="#c9345a" clip-path="url(#clip-body)"/>
```
**QA checklist**
- [ ] Zero gradients, zero filters
- [ ] Each object has exactly one shade step (consistent light direction)
- [ ] Palette ≤ 14 colours, grouped as hue families
---
## Geometric {#geometric}
**Layer stack**: `grid-comment` → `background` → `structure` (large primitives) → `rhythm` (repeats) → `accents`.
**Design moves**
- Declare the construction in a comment: `<!-- grid: 12 units, module 40, golden-ratio radii 40/65/105 -->`.
- Use even-odd fills for concentric cut-outs; use `<use>` + rotate for radial repetition.
- Alternate warm/cool at every boundary so overlapping primitives never merge.
**Recipe - radial repetition**
```svg
<defs><path id="petal" d="M0 0 C 20 -40 20 -80 0 -110 C -20 -80 -20 -40 0 0Z"/></defs>
<g transform="translate(256 256)" fill="#e63946">
<use href="#petal" transform="rotate(0)"/>
<use href="#petal" transform="rotate(45)"/>
<use href="#petal" transform="rotate(90)"/>
<!-- ... every 45deg -->
</g>
```
**QA checklist**
- [ ] Every angle is a multiple of 15°
- [ ] Shared tangent points actually touch (zoom to 800 %)
- [ ] Repeats are `<use>`, not copy-pasted paths
---
## Monochrome {#monochrome}
**Layer stack**: `paper` → `light-masses` → `mid-masses` → `dark-masses` → `line/hatch` → `spot-black`.
**Design moves**
- Plan a value ladder first (5-7 steps) and *assign every region a step* before drawing.
- Line weight and hatch density are the variables that replace colour.
- Keep the darkest darks connected so the shadow reads as one shape.
**Recipe - hatch density ramp**
```svg
<defs>
<pattern id="hatch-l" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="8" stroke="#3a4550" stroke-width="1"/></pattern>
<pattern id="hatch-d" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="4" stroke="#101418" stroke-width="1.4"/></pattern>
</defs>
```
**QA checklist**
- [ ] Only one hue family (check `convert_svg.py inspect` palette)
- [ ] Squint: three clear value masses
- [ ] Silhouette still reads in pure black
---
## Luxury / Premium {#luxury-premium}
**Layer stack**: `base` → `hairline-frame` → `central-mark` → `metallic-accents` → `typography`.
**Design moves**
- 70 %+ empty. The mark is small and perfectly aligned; margins ≥ 15 %.
- Gold lives in *lines and small masses*. Define ONE gradient in `userSpaceOnUse` so every gold element shares the same light sweep.
**Recipe - reusable gold**
```svg
<defs>
<linearGradient id="gold" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="512" y2="512">
<stop offset="0" stop-color="#8f6a25"/>
<stop offset=".35" stop-color="#f1d99a"/>
<stop offset=".6" stop-color="#b8893b"/>
<stop offset="1" stop-color="#e9cf8b"/>
</linearGradient>
</defs>
<rect x="56" y="56" width="400" height="400" fill="none" stroke="url(#gold)" stroke-width="1.5"/>
<rect x="68" y="68" width="376" height="376" fill="none" stroke="url(#gold)" stroke-width=".75"/>
```
**QA checklist**
- [ ] Gold is a gradient, never a flat yellow
- [ ] Hairlines ≥ 0.75 at 512 px (they must survive small sizes)
- [ ] Text converted to paths for logos (fonts are not embedded)
styles/references/craft.md
# Handmade & illustrative styles
Reference for: Paper Cutout, Hand Drawn / Doodle, Sticker, Children's Illustration, Editorial Illustration, Organic / Botanical.
---
## Paper Cutout {#paper-cutout}
**Layer stack**: `paper-texture (top overlay)` ← `layer-5 (subject)` ← `layer-4` … ← `layer-1 (sky)`. Each layer is a `<g id="layer-N" filter="url(#shadow)">`.
**Design moves**
- 4-6 sheets. Adjacent sheets differ in value by ≥ 15 %.
- Hand-cut edge: author paths with slightly irregular Q-curves; avoid perfect circles; add a few straight scissor facets.
- Same shadow everywhere: `dx 1.5 / dy 2.5 / blur 2 / opacity .3`.
**Recipe**
```svg
<defs>
<filter id="shadow" x="-10%" y="-10%" width="125%" height="130%">
<feDropShadow dx="1.5" dy="2.5" stdDeviation="2" flood-color="#3a2a1a" flood-opacity=".3"/>
</filter>
<filter id="paper"><feTurbulence type="fractalNoise" baseFrequency=".9" numOctaves="2" result="n"/>
<feColorMatrix in="n" type="matrix" values="0 0 0 0 .3 0 0 0 0 .25 0 0 0 0 .2 0 0 0 .5 0"/></filter>
</defs>
<g id="layer-2" filter="url(#shadow)"><path d="M0 330 Q90 260 190 310 T400 300 T512 320 V512 H0Z" fill="#8ab17d"/></g>
<rect width="512" height="512" filter="url(#paper)" opacity=".06"/>
```
**QA**: [ ] no gradients [ ] no strokes [ ] one shadow filter reused [ ] ≥ 3 layer groups in back-to-front order
---
## Hand Drawn / Doodle {#hand-drawn-doodle}
**Layer stack**: `paper` → `fills` (offset by 2-4 units) → `hatching` → `ink-lines` → `doodle-fillers`.
**Design moves**
- Wobble comes from *authoring*, not effects: use uneven Q/T control points, let closed shapes overshoot their start point by 2-4 units.
- Fill offset: `<g id="fills" transform="translate(2.5 2)">` under the line layer = instant mis-registration charm.
- Vary `stroke-width` between paths (2-5). Ink is dark blue/brown, not `#000`.
**Recipe - wobbly circle**
```svg
<path d="M120 200 C 118 150, 170 118, 222 126 C 275 134, 300 178, 290 218 C 280 262, 226 288, 178 276 C 138 266, 122 236, 124 204"
fill="none" stroke="#1d2433" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
```
Optional ONE `feTurbulence`+`feDisplacementMap` (scale ≤ 2.5) on the entire line group for extra tremor.
**QA**: [ ] round caps/joins everywhere [ ] no perfect circles/straight edges [ ] fills visibly offset [ ] no gradients
---
## Sticker {#sticker}
**Layer stack**: `shadow` → `die-cut border (white)` → `artwork` → `gloss`.
**Recipe - die-cut border from the silhouette**
```svg
<defs>
<path id="cut" d="…merged silhouette path…"/>
<filter id="ds" x="-10%" y="-10%" width="125%" height="130%"><feDropShadow dx="2" dy="4" stdDeviation="3" flood-opacity=".25"/></filter>
</defs>
<use href="#cut" fill="#fff" stroke="#fff" stroke-width="14" stroke-linejoin="round" filter="url(#ds)"/>
<!-- artwork here, with 3-4 unit dark interior outlines -->
```
Merge islands (small gaps become bridges) so the border is ONE continuous shape. Transparent background - no canvas rect.
**QA**: [ ] border thickness uniform [ ] no notches [ ] shadow only on the border [ ] no background rect
---
## Children's Illustration {#childrens-illustration}
**Layer stack**: `sky/vignette` → `hills` → `props` → `character` → `soft-glow` → `grain (single overlay)`.
**Design moves**
- Head-to-body ≈ 1:1.5; eyes low and wide-set; cheeks as soft ellipses; no sharp corners.
- Outline colour is warm-dark (`#4a3b52`), 2-3 units, round caps - or no outlines with colour-on-colour details.
- Story details: tiny secondary characters or props reward a second look.
**Recipe - reusable face**
```svg
<g id="face"><ellipse cx="-16" cy="0" rx="5" ry="6" fill="#4a3b52"/><ellipse cx="16" cy="0" rx="5" ry="6" fill="#4a3b52"/>
<circle cx="-14" cy="-2" r="1.6" fill="#fff"/><circle cx="18" cy="-2" r="1.6" fill="#fff"/>
<ellipse cx="-26" cy="12" rx="7" ry="4.5" fill="#f4a3a8" opacity=".7"/><ellipse cx="26" cy="12" rx="7" ry="4.5" fill="#f4a3a8" opacity=".7"/>
<path d="M-6 14q6 6 12 0" fill="none" stroke="#4a3b52" stroke-width="2.4" stroke-linecap="round"/></g>
```
**QA**: [ ] warm palette, no greys [ ] rounded everything [ ] negative space kept for text [ ] ≤ 2 filters
---
## Editorial Illustration {#editorial-illustration}
**First decide the idea** (one sentence: "X is Y") - then draw only what serves it.
**Layer stack**: `background field` → `hero metaphor` (big shape) → `contrast element` (scale surprise) → `accent` → `texture overlays`.
**Recipe - halftone shading via pattern + gradient mask**
```svg
<pattern id="dots" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(30)"><circle cx="3" cy="3" r="1.6" fill="#1f3a4d"/></pattern>
<linearGradient id="fade" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#000"/></linearGradient>
<mask id="m"><rect width="512" height="512" fill="url(#fade)"/></mask>
<rect width="512" height="512" fill="url(#dots)" mask="url(#m)"/>
```
**QA**: [ ] concept readable at thumbnail [ ] 3-5 colours + 1 accent [ ] cropping/bleed deliberate [ ] no clip-art literalism
---
## Organic / Botanical {#organic-botanical}
**Layer stack**: `paper` → `stems` → `leaf-back` → `leaf-front` → `blooms` → `veins/details` → `caption`.
**Design moves**
- One leaf symbol, many variants: vary `rotate`, `scale`, and hue slightly with per-`<use>` `fill`.
- Stems are stroked Béziers (round caps); leaves attach at stem points.
- Petal ring = one petal rotated in equal steps.
**Recipe**
```svg
<defs>
<g id="leaf"><path d="M0 0 C 26-30 26-90 0-130 C -26-90 -26-30 0 0Z"/>
<path d="M0 0 V-120" fill="none" stroke="#1f3f28" stroke-width="1.2" opacity=".6"/></g>
</defs>
<use href="#leaf" fill="#5f9b58" transform="translate(256 400) rotate(-28) scale(.9)"/>
<use href="#leaf" fill="#7bb26a" transform="translate(256 400) rotate(18) scale(1.05)"/>
```
**QA**: [ ] no two leaves identical [ ] curves have smooth handles (no kinks) [ ] shading is tonal green, not black [ ] ≥ 30 elements
styles/references/decorative-eras.md
# Historic & decorative movements
Reference for: Art Nouveau, Art Deco, Bauhaus, Brutalist, Vintage Poster, Medieval Manuscript, Gothic.
---
## Art Nouveau {#art-nouveau}
**Layer stack**: `ground` → `halo/arch panel` → `border ornament` → `subject` (flat colour) → `pattern fills` → `contour lines` → `lettering panels`.
**Design moves**
- One dominant S-curve carries the eye through the piece (hair → stem → border).
- Contour lines taper: draw each as a filled path, or two strokes (dark 3 + lighter 1.2 offset).
- Frame = arch + inner offset arch; flowers overlap the frame to integrate it with the subject.
**Recipe - arched double frame**
```svg
<path d="M80 470 V210 A176 176 0 0 1 432 210 V470Z" fill="#efe2c0" stroke="#2c2a25" stroke-width="3"/>
<path d="M98 452 V212 A158 158 0 0 1 414 212 V452Z" fill="none" stroke="#c8a24a" stroke-width="1.5"/>
```
**Recipe - tendril (smooth handles)**
```svg
<path d="M256 470 C 210 400, 300 340, 250 280 S 200 170, 262 120" fill="none" stroke="#3f6b5e" stroke-width="4" stroke-linecap="round"/>
```
**QA**: [ ] no straight lines except architecture [ ] ≥ 2 stroke widths, round caps [ ] muted palette (no neon) [ ] frame integrated with subject
---
## Art Deco {#art-deco}
**Layer stack**: `black/emerald ground` → `sunburst rays` → `stepped frame` → `central motif` → `gold hairline sets` → `title lettering`.
**Design moves**
- Draw the **left half only**, mirror with `<use transform="matrix(-1 0 0 1 W 0)">` for perfect symmetry.
- Triple rules: three parallel gold lines, 1-2.5 wide with 3-4 gaps.
- Chamfer corners (45° cuts) instead of rounding.
**Recipe - sunburst + mirrored half**
```svg
<defs>
<path id="ray" d="M0 0 L-9 -230 L9 -230Z"/>
<linearGradient id="gold" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="512" y2="512">
<stop offset="0" stop-color="#8c6a1a"/><stop offset=".4" stop-color="#f3dd8b"/><stop offset=".7" stop-color="#c9a227"/><stop offset="1" stop-color="#f3dd8b"/></linearGradient>
<g id="half"> … left half art … </g>
</defs>
<g transform="translate(256 300)" fill="url(#gold)">
<use href="#ray" transform="rotate(-70)"/><use href="#ray" transform="rotate(-50)"/><!-- every 20deg -->
</g>
<use href="#half"/><use href="#half" transform="matrix(-1 0 0 1 512 0)"/>
```
**QA**: [ ] exact bilateral symmetry [ ] gold is a gradient [ ] miter joins, butt/square caps [ ] no rounded corners [ ] ≤ 6 colours
---
## Bauhaus {#bauhaus}
**Layer stack**: `off-white ground` → `large primary shape` → `overlapping secondary shapes` → `black bars/rules` → `rotated type`.
**Design moves**
- Grid unit U; big red circle, blue rectangle, yellow triangle, black bar - then move things until the *tension* is right (asymmetric, diagonal).
- Overlap by explicit intersection colours (black) not transparency.
**Recipe - half circle + intersection**
```svg
<path d="M90 300 A110 110 0 0 1 310 300Z" fill="#d62828"/>
<rect x="200" y="180" width="200" height="120" fill="#1d4e9f"/>
<clipPath id="i"><path d="M90 300 A110 110 0 0 1 310 300Z"/></clipPath>
<rect x="200" y="180" width="200" height="120" fill="#111" clip-path="url(#i)"/> <!-- intersection -->
<text x="60" y="430" font-family="Futura, 'Century Gothic', sans-serif" font-weight="700" font-size="40" transform="rotate(-90 60 430)">BAUHAUS</text>
```
**QA**: [ ] zero gradients/filters [ ] primaries + black + off-white only [ ] asymmetric balance [ ] angles multiples of 15°
---
## Brutalist {#brutalist}
**Recipe - block with hard shadow**
```svg
<g id="block">
<rect x="48" y="48" width="240" height="140" fill="#111"/> <!-- hard shadow, +8,+8 -->
<rect x="40" y="40" width="240" height="140" fill="#ffe600" stroke="#111" stroke-width="6" stroke-linejoin="miter"/>
<text x="56" y="120" font-family="'Courier New', monospace" font-weight="700" font-size="44">RAW.</text>
</g>
```
**Design moves**: heavy borders (4-8), hard offset shadows, monospace caps, visible grid, one or two shouting colours, deliberately awkward but structured.
**QA**: [ ] no gradients/blur/rounding [ ] borders ≥ 4 [ ] ≤ 6 colours [ ] type oversized
---
## Vintage Poster {#vintage-poster}
**Layer stack**: `paper + vignette` → `sky bands + sun rays` → `far → near scenery (flat inks)` → `overprint accents` → `title block` → `paper-texture overlay` → `border`.
**Design moves**
- Ink budget of 4-6. Distance = lighter and bluer. Sun = concentric circle + wedge rays clipped to the sky.
- Title in the lower or upper band (20-25 % of height); subtitle small, letter-spaced.
- Age it: warm vignette + speckle overlay, never blur.
**Recipe - paper wear overlay**
```svg
<defs>
<filter id="wear" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency=".8" numOctaves="2" seed="4"/>
<feColorMatrix type="matrix" values="0 0 0 0 .35 0 0 0 0 .25 0 0 0 0 .15 0 0 0 .8 -.28"/></filter>
<radialGradient id="age" cx=".5" cy=".5" r=".75"><stop offset=".6" stop-color="#7a5a2b" stop-opacity="0"/><stop offset="1" stop-color="#7a5a2b" stop-opacity=".35"/></radialGradient>
</defs>
<rect width="900" height="1300" fill="url(#age)"/><rect width="900" height="1300" filter="url(#wear)" opacity=".35"/>
```
**QA**: [ ] `<text>` title present (or outlined) [ ] ≤ 6 inks [ ] scenery flat (no gradient shading) [ ] margin + border frame [ ] warm paper base
---
## Medieval Manuscript {#medieval-manuscript}
**Layer stack**: `parchment` → `ruling lines` → `gold-leaf panel (initial + border ground)` → `historiated initial` → `vine border + drolleries` → `text block placeholder` → `stains/aging`.
**Design moves**
- Flattened perspective; important = large; opaque pigments with brown-black contour.
- Gold: 4-5 stop gradient + fine burnish pattern.
- Text block = ruled thin lines (do not fake unreadable text).
**Recipe - vine with leaf uses**
```svg
<path id="vine" d="M40 60 C 60 200, 20 320, 60 460" fill="none" stroke="#3b7a5c" stroke-width="3" stroke-linecap="round"/>
<g id="leaf"><path d="M0 0 C 12-10 26-6 30 8 C 16 14 4 10 0 0Z" fill="#3b7a5c"/></g>
<use href="#leaf" transform="translate(52 150) rotate(20)"/><use href="#leaf" transform="translate(30 250) rotate(160) scale(-1 1)"/>
```
**QA**: [ ] gold = gradient [ ] parchment darkens toward edges [ ] borders filled with vine/creature detail [ ] no modern perspective/shading
---
## Gothic {#gothic}
**Layer stack**: `night sky/vignette` → `stone architecture silhouette` → `light shafts` → `stained-glass window (jewel panes + lead lines)` → `foreground silhouette` → `fog bands`.
**Design moves**
- Pointed arch = two circular arcs meeting at the apex. Rose window = a petal rotated in equal steps + leading strokes over jewel fills.
- One motivated light (window/moon/candle), everything else falls into shadow.
**Recipe - lancet arch + rose window**
```svg
<path d="M150 470 V240 A110 110 0 0 1 256 130 A110 110 0 0 1 362 240 V470Z" fill="#231a33"/> <!-- approximate lancet -->
<g transform="translate(256 260)" fill="#1e3a6b" stroke="#0c0a12" stroke-width="3">
<path id="pane" d="M0-10 L22-60 A62 62 0 0 1-22-60Z"/>
<use href="#pane" transform="rotate(45)" fill="#6b1e2e"/><use href="#pane" transform="rotate(90)" fill="#d9a441"/> <!-- … every 45deg -->
</g>
```
**QA**: [ ] value-dark overall with one bright focal area [ ] vertical emphasis [ ] lead-line strokes consistent [ ] fog/vignette present [ ] no cheerful pastel palette
styles/references/digital-glossy.md
# Digital, glossy & 3D styles
Reference for: Cyberpunk Neon, Y2K, Frutiger Aero, Glassmorphism, Clay / Soft 3D, Isometric, Low Poly.
**Effect budget rule**: filters are expensive and can clip. Prefer gradients + stacked shapes; use a filter only when no vector construction gives the same result, define each filter ONCE, and apply it to *groups*.
---
## Cyberpunk Neon {#cyberpunk-neon}
**Layer stack**: `sky-gradient` → `far-skyline` (dark silhouettes) → `haze-band` → `mid-buildings` (windows, signs) → `neon-signs` (glow group) → `subject` (silhouette + rim) → `ground-reflection` → `rain/scanlines` → `vignette`.
**Design moves**
- 70 % dark / 25 % mid / 5 % pure neon. Two coloured lights from opposite sides (magenta vs cyan).
- Neon tube = 2 strokes on the same path: a bright thin core (`#fff` or pale tint, 1-2 units) over a saturated wider stroke (3-5 units); the *glow filter* goes on the parent `<g>`.
- Reflection: `<use>` the skyline flipped, masked by a fading gradient.
**Recipe - glow filter + neon tube**
```svg
<defs>
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#07040f"/><stop offset=".65" stop-color="#2a0f4d"/><stop offset="1" stop-color="#ff2a6d"/>
</linearGradient>
</defs>
<g id="neon-signs" filter="url(#glow)" fill="none" stroke-linecap="round">
<path id="tube" d="M120 300h160" stroke="#ff2a6d" stroke-width="5"/>
<use href="#tube" stroke="#ffd6e4" stroke-width="1.6"/>
</g>
```
**Scanlines / rain overlay**
```svg
<pattern id="scan" width="4" height="4" patternUnits="userSpaceOnUse"><rect width="4" height="1" fill="#000" fill-opacity=".18"/></pattern>
<rect width="100%" height="100%" fill="url(#scan)"/>
```
**QA checklist**
- [ ] Base is genuinely dark (validator `dark-base`)
- [ ] Glow is on ≤ 3 groups; filter region ≥ 160 % (no clipped halos)
- [ ] Two opposing light colours; one warm accent at most
- [ ] Render: bright neon occupies a *small* share of pixels
---
## Y2K {#y2k}
**Layer stack**: `gradient-field` → `orbit-rings` → `hero-chrome-object` → `gloss-highlights` → `sparkles` → `ui/cursor fragments`.
**Recipe - chrome gradient (fake horizon reflection)**
```svg
<linearGradient id="chrome" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffffff"/>
<stop offset=".35" stop-color="#9fb4d6"/>
<stop offset=".5" stop-color="#33456b"/> <!-- hard horizon line -->
<stop offset=".51" stop-color="#b9c8e6"/>
<stop offset="1" stop-color="#f4f8ff"/>
</linearGradient>
<!-- 4-point sparkle, define once -->
<path id="spark" d="M0-10 C1-3 3-1 10 0 C3 1 1 3 0 10 C-1 3 -3 1 -10 0 C-3-1 -1-3 0-10Z" fill="#fff"/>
```
**QA checklist**
- [ ] Chrome has a *hard* mid-stop (two stops 0.01 apart) - soft chrome reads as plastic
- [ ] ≥ 3 gradients, sparkles are `<use>`
- [ ] Highlights are crisp white, not grey
---
## Frutiger Aero {#frutiger-aero}
**Layer stack**: `sky-gradient` → `clouds/bokeh` → `landscape-strip (grass/water)` → `glass-object` → `bubbles` → `lens-flare`.
**Recipe - bubble**
```svg
<radialGradient id="bub" cx=".35" cy=".3" r=".8">
<stop offset="0" stop-color="#fff" stop-opacity=".9"/>
<stop offset=".45" stop-color="#bfe8ff" stop-opacity=".25"/>
<stop offset="1" stop-color="#5cc8ff" stop-opacity=".55"/>
</radialGradient>
<g id="bubble"><circle r="40" fill="url(#bub)" stroke="#fff" stroke-opacity=".6" stroke-width="1.2"/>
<path d="M-24-12a28 28 0 0 1 22-18" fill="none" stroke="#fff" stroke-opacity=".9" stroke-width="4" stroke-linecap="round"/></g>
```
**Glossy button**: rounded rect + vertical gradient + upper-half highlight clipped to the button with `white .6 → .05` gradient.
**QA checklist**
- [ ] High-key (validator `light-base`), airy
- [ ] ≥ 4 gradients, bubbles are `<use>` with varied scale
- [ ] Only one soft-shadow filter (ground)
---
## Glassmorphism {#glassmorphism}
SVG has no `backdrop-filter`; **fake it**: blurred copy of the background, clipped to each panel.
**Layer stack**: `bg-blobs` → per panel: `blurred-bg-copy (clipped)` → `tint fill` → `gradient border` → `content`.
**Recipe**
```svg
<defs>
<g id="bg"> <!-- blobs --> </g>
<filter id="blur" x="-10%" y="-10%" width="120%" height="120%"><feGaussianBlur stdDeviation="14"/></filter>
<clipPath id="p1"><rect x="90" y="140" width="330" height="220" rx="28"/></clipPath>
<linearGradient id="edge" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#fff" stop-opacity=".7"/><stop offset="1" stop-color="#fff" stop-opacity=".05"/></linearGradient>
</defs>
<use href="#bg"/>
<g clip-path="url(#p1)"><use href="#bg" filter="url(#blur)"/></g>
<rect x="90" y="140" width="330" height="220" rx="28" fill="#fff" fill-opacity=".14" stroke="url(#edge)" stroke-width="1.5"/>
```
**QA checklist**
- [ ] Each panel visibly blurs what is behind it (compare render to a version without the clipped copy)
- [ ] ≤ 3 panel layers; text/icons ≥ 4.5:1 contrast
- [ ] One shared blur filter
---
## Clay / Soft 3D {#clay-soft-3d}
**Layer stack**: `bg` → `contact-shadow (blurred ellipse)` → per object: `base` → `core-shadow crescent` → `highlight patch` → `bounce rim`.
**Recipe - inflated sphere**
```svg
<defs>
<radialGradient id="clay" cx=".32" cy=".28" r=".85">
<stop offset="0" stop-color="#ffd0c2"/><stop offset=".55" stop-color="#ffb4a2"/><stop offset="1" stop-color="#e58f8f"/>
</radialGradient>
<clipPath id="c1"><circle cx="256" cy="240" r="110"/></clipPath>
<filter id="soft" x="-30%" y="-30%" width="160%" height="160%"><feGaussianBlur stdDeviation="14"/></filter>
</defs>
<ellipse cx="256" cy="380" rx="120" ry="18" fill="#7a4a3a" opacity=".28" filter="url(#soft)"/>
<circle cx="256" cy="240" r="110" fill="url(#clay)"/>
<!-- core shadow, clipped -->
<circle cx="300" cy="290" r="120" fill="#c96f78" opacity=".35" clip-path="url(#c1)"/>
<ellipse cx="216" cy="190" rx="34" ry="20" fill="#fff" opacity=".35" transform="rotate(-30 216 190)"/>
```
**QA checklist**
- [ ] No outlines; shadows are tinted, never grey
- [ ] Light direction identical across all objects
- [ ] Only the contact shadow is blurred
---
## Isometric {#isometric}
**Projection**: `X = (x − y)·cos30°`, `Y = (x + y)·sin30° − z`. Use a generator script for anything beyond a few blocks.
**Layer stack**: `ground-plane` → back-to-front sorted objects (sort by `x+y+z`) → `props` → `shadows` (parallelograms under objects, painted before the object).
**Recipe - cube symbol + reuse**
```svg
<defs>
<g id="cube"> <!-- unit 40 -->
<polygon points="0,-20 34.64,0 0,20 -34.64,0" fill="var(--top)"/> <!-- top -->
<polygon points="-34.64,0 0,20 0,60 -34.64,40" fill="var(--left)"/> <!-- left -->
<polygon points="34.64,0 0,20 0,60 34.64,40" fill="var(--right)"/> <!-- right -->
</g>
</defs>
<use href="#cube" x="256" y="200" style="--top:#c9d6ea;--left:#8fa8d0;--right:#5c7ab0"/>
```
Flat art onto the floor: `transform="matrix(0.866 0.5 -0.866 0.5 0 0)"`.
**QA checklist**
- [ ] All edges at 30°/90°; three consistent face tones
- [ ] No gaps between faces (overlap 0.2 units or add same-colour hairline)
- [ ] Draw order back-to-front
---
## Low Poly {#low-poly}
**Workflow**: don't hand-place hundreds of triangles. Place ~15-40 *anchor* points along the silhouette/feature lines, add jittered interior points (denser near features), Delaunay-triangulate in code, colour each triangle from a lighting function, emit `<polygon>`.
**Recipe - facet colouring**
```python
# value = clamp(0.5 + 0.5 * dot(normal, light)) then lerp between shadow/light colour
```
Close seams: `stroke="<same fill>" stroke-width=".5" stroke-linejoin="round"`.
**QA checklist**
- [ ] Silhouette clean; no sliver triangles (min interior angle > 15°)
- [ ] Facet values follow form (light side/dark side clear)
- [ ] ≤ ~12 distinct fills after quantisation (use classes)
styles/references/east-asian.md
# East Asian traditions
Reference for: Japanese Ink / Sumi-e, Ukiyo-e.
> These are **visual languages, not palettes**. Recolouring a generic illustration with "Japanese colours" is a failure. Follow the construction below: it is what makes the result read as the tradition.
---
## Japanese Ink / Sumi-e {#sumi-e}
**Layer stack**: `paper` → `far mountains (pale wash)` → `mist bands` → `mid masses (grey wash)` → `subject strokes (black, dry brush)` → `accents (thin lines)` → `seal`.
**Design moves**
1. **Ma (negative space)** first: decide where nothing happens. The subject takes ≤ 40 %.
2. Draw with *brush shapes* - closed variable-width paths, not uniform strokes. A bamboo stalk = tall tapered shape with node gaps; a mountain = irregular soft mass.
3. Five ink tones: `#1a1a1a`, `#4a4a48`, `#8c8a84`, `#c9c5b8` + paper. Dark near / pale far.
4. One red seal, lower corner, opposite the main mass.
**Recipe - wet wash with halo + dry brush**
```svg
<defs>
<filter id="wet" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="6"/></filter>
<pattern id="bristle" width="200" height="6" patternUnits="userSpaceOnUse">
<rect width="200" height="6" fill="#fff"/>
<path d="M0 1h60M80 1h120 M0 3h140M160 3h40 M20 5h90M130 5h70" stroke="#000" stroke-width="1.2"/></pattern>
<mask id="dry"><rect width="512" height="512" fill="url(#bristle)"/></mask>
<linearGradient id="mist" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f4eddc" stop-opacity="0"/><stop offset="1" stop-color="#f4eddc" stop-opacity=".9"/></linearGradient>
</defs>
<path id="mountain" d="…soft irregular mass…" fill="#8c8a84"/>
<use href="#mountain" fill="#8c8a84" opacity=".5" filter="url(#wet)"/> <!-- wet halo -->
<path d="…tapered brush stroke…" fill="#1a1a1a" mask="url(#dry)"/> <!-- dry brush -->
<rect y="300" width="512" height="90" fill="url(#mist)"/> <!-- mist band -->
<g id="seal" transform="rotate(-4 440 450)"><rect x="420" y="430" width="40" height="40" rx="2" fill="#c1272d"/>
<path d="M430 442h20M440 442v20M432 456h16" stroke="#f4eddc" stroke-width="3" stroke-linecap="square"/></g>
```
**QA**: [ ] palette = paper + ink tones + one seal [ ] ≥ 60 % empty paper [ ] asymmetric [ ] brush shapes vary in width [ ] no outlines around washes
---
## Ukiyo-e {#ukiyo-e}
**Layer stack** (mirrors real print production):
1. `paper` (warm cream)
2. `colour-blocks` - one `<g>` per pigment block (blue, ochre, green, red, ...), **no outlines**
3. `bokashi` - gradient limited to sky/horizon via clip-path
4. `patterns` - waves (seigaiha), clouds, kimono motifs as `<pattern>`/`<use>`
5. `key-block` - the sumi outline layer, offset by (0.8, 0.6) for mis-registration
6. `texture` - wood-grain overlay (horizontal-stretched turbulence, ~6 %)
7. `cartouche + seal` - title box top corner, small red seal
**Design moves**
- Flat perspective: stack planes, overlap, crop the foreground element at one frame edge.
- Outline weights: 3 units exterior contours, 1-1.4 interior; round caps and joins; slight irregularity.
- Waves: curling crests with claw-like foam fingers; concentric-arc pattern for the water field.
- Limit to 8-12 colours from the pigment set: Prussian blue, pale indigo, vermilion, ochre, muted green, sumi, cream.
**Recipe - seigaiha (wave) pattern**
```svg
<pattern id="seigaiha" width="48" height="24" patternUnits="userSpaceOnUse">
<g fill="none" stroke="#1f3c6e" stroke-width="1.4">
<circle cx="0" cy="24" r="24"/><circle cx="0" cy="24" r="17"/><circle cx="0" cy="24" r="10"/>
<circle cx="48" cy="24" r="24"/><circle cx="48" cy="24" r="17"/><circle cx="48" cy="24" r="10"/>
<circle cx="24" cy="12" r="24"/><circle cx="24" cy="12" r="17"/><circle cx="24" cy="12" r="10"/>
</g>
</pattern>
```
**Recipe - wood-grain overlay**
```svg
<filter id="grain" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency=".002 .22" numOctaves="3" seed="7"/>
<feColorMatrix type="matrix" values="0 0 0 0 .25 0 0 0 0 .18 0 0 0 0 .1 0 0 0 .9 -.25"/>
</filter>
<rect width="512" height="512" filter="url(#grain)" opacity=".18"/>
```
**Recipe - bokashi sky (only here)**
```svg
<linearGradient id="bokashi" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#1f3c6e"/><stop offset=".7" stop-color="#6d8fb3"/><stop offset="1" stop-color="#efe3c8"/></linearGradient>
```
**QA**: [ ] colour blocks contain no strokes; outlines live in `#key-block` [ ] ≤ 12 colours, gradients only in the sky [ ] ≥ 1 `<pattern>` (water/cloud/cloth) [ ] cartouche + seal present [ ] no cast shadows/glow [ ] no vanishing-point perspective
styles/references/genre.md
# Cinematic & genre styles
Reference for: Cinematic Poster, Fantasy, Sci-Fi, Horror, Retro Futurism.
**Common approach** - these are *lighting-and-atmosphere* styles. Build the scene in value first (dark/mid/light masses), decide the single light source, then add colour grading, then detail. Depth comes from stacked layers separated by haze.
---
## Cinematic Poster {#cinematic-poster}
**Layer stack**: `graded sky/backdrop` → `far silhouettes` → `haze` → `hero silhouette + rim light` → `light shafts/flare` → `particles` → `vignette + grain` → `title treatment` → `billing block`.
**Design moves**
- One idea, one image. Pick the grading (teal/orange, mono + accent, night blue + warm key) and stay inside it.
- Title lower third, wide tracking; tagline above the hero or below the title; billing block ≈ 3 % height in condensed caps.
- Never use real film titles, actors, studio logos.
**Recipe - light shafts + vignette**
```svg
<defs>
<linearGradient id="shaft" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffcf99" stop-opacity=".55"/><stop offset="1" stop-color="#ffcf99" stop-opacity="0"/></linearGradient>
<radialGradient id="vig" cx=".5" cy=".45" r=".75"><stop offset=".55" stop-color="#000" stop-opacity="0"/><stop offset="1" stop-color="#000" stop-opacity=".75"/></radialGradient>
</defs>
<polygon points="440,0 520,0 640,900 300,900" fill="url(#shaft)"/>
<rect width="900" height="1350" fill="url(#vig)"/>
<text x="450" y="1120" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="96" letter-spacing="18" fill="#f5efe6">EMBERFALL</text>
```
**QA**: [ ] single focal light [ ] title/billing hierarchy (3 sizes) [ ] ≥ 3 gradients [ ] vignette last-but-text [ ] fictional title
---
## Fantasy {#fantasy}
**Layer stack**: `sky + aurora/sun` → `far mountains` → `mist` → `mid castle/ruins` → `mist` → `hero + creature` → `foreground framing (rocks/trees)` → `magic light + particles`.
**Design moves**
- Aerial perspective: each farther layer is lighter, cooler, lower contrast (mix toward the sky colour).
- Heroic scale: tiny figure, monumental structure. Light path leads to the focal point.
- Magic = radial gradient glow + 5-15 particle dots (single `<pattern>` or scripted `<circle>`s).
**Recipe - layered ranges with mist**
```svg
<path d="M0 520 L90 420 L170 490 L280 380 L380 480 L520 400 V640 H0Z" fill="#5b4b8a"/>
<rect y="470" width="900" height="120" fill="url(#mist)"/> <!-- mist: sky colour, opacity 0 → .8 → 0 -->
<path d="M0 600 L110 510 L220 590 L360 470 L500 600 L640 520 V760 H0Z" fill="#3b2f66"/>
```
**QA**: [ ] ≥ 4 depth layers with mist bands [ ] one motivated magic light [ ] silhouette readable [ ] no trademarked creatures/worlds
---
## Sci-Fi {#sci-fi}
**Layer stack**: `space gradient` → `starfield` → `nebula` → `planet (shaded + atmosphere)` → `station/ship (hard-surface)` → `engine glow` → `HUD/annotation (optional)`.
**Design moves**
- Scale cues: tiny ship vs huge planet/station; a window row or docking arm gives size.
- Hull = layered polygons with a linear gradient for metal; panel lines as thin darker paths; warm rim from the sun, cool fill from the planet.
- Starfield from a script: 80-200 circles, radius 0.4-1.6, opacity 0.3-1.
**Recipe - planet with atmosphere**
```svg
<defs>
<radialGradient id="pl" cx=".3" cy=".3" r=".9"><stop offset="0" stop-color="#7ec8d9"/><stop offset=".6" stop-color="#2b6f8f"/><stop offset="1" stop-color="#0a1f3a"/></radialGradient>
<radialGradient id="atm" cx=".5" cy=".5" r=".5"><stop offset=".92" stop-color="#7ee6ff" stop-opacity="0"/><stop offset=".97" stop-color="#7ee6ff" stop-opacity=".55"/><stop offset="1" stop-color="#7ee6ff" stop-opacity="0"/></radialGradient>
<clipPath id="pc"><circle cx="620" cy="520" r="300"/></clipPath>
</defs>
<circle cx="620" cy="520" r="300" fill="url(#pl)"/>
<g clip-path="url(#pc)"><path d="M300 470 Q520 430 940 500 V560 Q620 500 300 540Z" fill="#dff6ff" opacity=".25"/></g> <!-- cloud band -->
<circle cx="620" cy="520" r="318" fill="url(#atm)"/>
```
**QA**: [ ] one light direction [ ] scale cue present [ ] planets shaded (not flat circles) [ ] ≥ 2 gradients [ ] no copied franchise ships
---
## Horror {#horror}
**Layer stack**: `near-black base` → `light cone (radial/linear, low opacity)` → `what the light touches` → `silhouette forms` → `fog bands` → `vignette` → `grain`.
**Design moves**
- Start black and *add light*, not the reverse. Show less: half-hidden shapes, eyes, hands.
- Off-centre focal point, Dutch angle, extreme proportions.
- One violent accent only (arterial red or sick green). No gore - dread, not splatter.
**Recipe - fog + vignette**
```svg
<linearGradient id="fog" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#9bb0a4" stop-opacity="0"/><stop offset=".5" stop-color="#9bb0a4" stop-opacity=".35"/><stop offset="1" stop-color="#9bb0a4" stop-opacity="0"/></linearGradient>
<rect y="520" width="900" height="200" fill="url(#fog)"/>
<rect width="900" height="1200" fill="url(#vig)"/>
```
**QA**: [ ] dark base (validator `dark-base`) [ ] ≥ 70 % of pixels dark [ ] a single bright focal area [ ] asymmetric framing
---
## Retro Futurism {#retro-futurism}
Two moods - pick one. **Atomic age**: cream + teal + mustard, googie shapes. **Synthwave**: indigo → magenta → orange sky, striped sun, perspective grid.
**Layer stack (synthwave)**: `sky gradient` → `stars` → `striped sun` → `mountains` → `horizon glow` → `grid floor` → `hero (car/palm/rocket)` → `chrome title`.
**Recipe - striped sun (mask) + perspective grid**
```svg
<defs>
<linearGradient id="sun" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffd23f"/><stop offset=".5" stop-color="#ff8a1f"/><stop offset="1" stop-color="#ff2e93"/></linearGradient>
<mask id="stripes"><rect width="900" height="900" fill="#fff"/>
<rect y="470" width="900" height="6" fill="#000"/><rect y="500" width="900" height="10" fill="#000"/><rect y="535" width="900" height="14" fill="#000"/></mask>
<linearGradient id="gridfade" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset="1" stop-color="#fff"/></linearGradient>
<mask id="gm"><rect x="0" y="600" width="900" height="300" fill="url(#gridfade)"/></mask>
</defs>
<circle cx="450" cy="470" r="170" fill="url(#sun)" mask="url(#stripes)"/>
<g mask="url(#gm)" stroke="#22d3ee" stroke-width="2" fill="none">
<path d="M450 600 L-300 900M450 600 L-100 900M450 600 L150 900M450 600 L450 900M450 600 L750 900M450 600 L1000 900M450 600 L1200 900"/>
<path d="M0 640H900M0 690H900M0 760H900M0 850H900"/>
</g>
```
**QA**: [ ] grid converges to ONE vanishing point on the horizon [ ] sun stripes widen downward [ ] ≥ 2 gradients [ ] one palette family only
styles/references/pixel-retro.md
# Pixel & retro-game styles
Reference for: Pixel Art, Retro 8-bit, Retro 16-bit.
**Universal pixel workflow (all three)**
1. Decide the grid (e.g. 32×32) and the palette *before* drawing.
2. Author the sprite as ASCII → `scripts/pixel_grid.py` → merged `<path>` per colour (integer coordinates, `shape-rendering="crispEdges"`, `viewBox` = grid size).
3. Compose scenes by concatenating grids (or several `pixel_grid.py` outputs as `<g>` with integer `translate`).
4. Validate: `validate_svg.py file.svg --style pixel-art` → no curves, no gradients, integer coords, colour budget.
5. Render with `render_svg.py --analyze` and check `top32_share_pct ≈ 100` (no anti-aliasing leaks).
**Sprite file format** (see `pixel_grid.py --help`)
```text
@k=#1a1423 ; outline
@o=#e8702a ; fur
@w=#fdf0d5 ; cream
..kk......kk.. ; '.' = transparent
.kook....kook.
```
Use `--mirror` when only the left half is typed, `--outline "#1a1423"` for an automatic 1-cell outline, `--pad 2 --bg "#..."` for a framed tile.
---
## Pixel Art {#pixel-art}
**Layer stack**: `background` (flat or dithered) → `ground` → `shadow` (dithered ellipse) → `sprite` (outline, base, shade, highlight) → `fx`.
**Design moves**
- Silhouette first: fill the shape in one colour; it must be recognisable.
- Hue-shift ramps: shade → cooler/more purple, highlight → warmer/more yellow.
- Selective outline: outer outline dark, interior contours the darker tone of the neighbouring fill.
- Clusters, not noise. Avoid "jaggies" (single-pixel steps in an otherwise smooth curve) and "banding" (parallel 1-px ramps).
- Curves are staircases with run-lengths that step monotonically: 1-1-2-3-… or 3-2-1-1 (never 2-1-2).
**Recipe - Bayer 2×2 dither as pattern** (keeps integer geometry)
```svg
<defs>
<pattern id="dither" width="2" height="2" patternUnits="userSpaceOnUse">
<rect width="1" height="1" fill="#6b4a8e"/><rect x="1" y="1" width="1" height="1" fill="#6b4a8e"/>
</pattern>
</defs>
<rect x="0" y="20" width="32" height="6" fill="url(#dither)"/>
```
> Rects with integer coordinates are the **only** allowed primitives (plus `<path>` using M/L/H/V/Z with integer values). Patterns of rects are fine.
**Sizes** 16² icon · 32² character · 64² detailed sprite · 128×72 scene. Scale by integer factors only (viewBox stays in grid units, so browsers scale it exactly).
**QA checklist**
- [ ] `shape-rendering="crispEdges"` on root
- [ ] No `<circle>`, `<ellipse>`, curves, gradients, filters, strokes
- [ ] ≤ ~24 colours, one shared outline colour (not pure black)
- [ ] Rendered analysis: `top32_share_pct` ≥ 99
---
## Retro 8-bit {#retro-8bit}
*Extends Pixel Art - everything above applies, with tighter limits.*
**Layer stack**: `sky-band` → `parallax-far` → `ground-tiles` → `sprite` → `hud`.
**Design moves**
- Max **3 colours + transparent per sprite**, ≤ 16 in the scene, from ONE hardware-style palette.
- 8×8 tile logic: backgrounds are repeated tiles (brick, grass, water) - draw one tile, tile it with `<pattern>` of integer size.
- No dithering except a two-tone checker on water/brick tiles. Bold 1-px outlines.
**Palettes** (pick one)
| Look | Colours |
|---|---|
| NES-like | `#000000 #fcfcfc #bcbcbc #f83800 #0058f8 #00a800 #fca044 #503000` |
| Game Boy | `#0f380f #306230 #8bac0f #9bbc0f` |
| C64-like | `#000000 #ffffff #883932 #67b6bd #8b5429 #b8c76f #40318d` |
**Recipe - HUD text from cells**: draw each glyph in a 5×7 sprite with `pixel_grid.py`, place glyphs at 6-cell intervals.
**QA checklist**
- [ ] Palette comment lists the exact hardware-style colours used
- [ ] Every sprite ≤ 3 visible colours
- [ ] Scene ≤ 16 colours
---
## Retro 16-bit {#retro-16bit}
*Extends Pixel Art.*
**Layer stack**: `sky (dithered gradient bands)` → `far` → `mid` → `near` → `sprites` → `glow rings` → `ui-frame`.
**Design moves**
- Ramps of 4-5 per material; distant parallax layers are tinted toward the sky colour (atmospheric perspective).
- Sky gradient = 6-10 hard bands separated by a 2-row Bayer dither transition.
- Glow = concentric hard-edged rings at stepped colours (never blur).
- Add specular pixels on metal and eyes; rim-light one edge of the hero.
**Recipe - dithered band transition**
```svg
<defs>
<pattern id="d2" width="2" height="2" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="1" height="1" fill="#3b2a6b"/><rect x="1" y="1" width="1" height="1" fill="#3b2a6b"/></pattern>
</defs>
<rect width="128" height="12" fill="#1b1035"/>
<rect y="12" width="128" height="2" fill="url(#d2)"/>
<rect y="14" width="128" height="12" fill="#3b2a6b"/>
```
**QA checklist**
- [ ] ≤ ~32 colours, ramps hue-shifted
- [ ] Parallax layers separable (`<g id="far|mid|near">`)
- [ ] No blur/gradient - glows are stepped rings
styles/references/print.md
# Print & street styles
Reference for: Screen Print, Risograph, Graffiti / Street Art.
---
## Screen Print {#screen-print}
**Layer stack**: `paper` → `ink-1 (lightest)` → `ink-2` → `ink-3` → `ink-black (key)` → `overprints` → `speckle/halftone`.
**Design moves**
- Choose the ink set first (3-5) and *compute the overprint colours* (e.g. yellow over cyan → green) - paint those overlaps as explicit fills.
- Every ink is one `<g id="ink-…">`; offset the key layer by (0.5-1.5, 0.5-1) for mis-registration.
- Tone = halftone dots or line screens, not gradients.
**Recipe - overprint area**
```svg
<clipPath id="ov"><circle cx="220" cy="240" r="120"/></clipPath>
<circle cx="220" cy="240" r="120" fill="#f2b134"/> <!-- ink A -->
<circle cx="300" cy="240" r="120" fill="#2a4dab"/> <!-- ink B -->
<circle cx="300" cy="240" r="120" fill="#2a5f45" clip-path="url(#ov)"/> <!-- overprint (A×B) -->
```
**Recipe - ink dropout speckle** (apply to ONE ink group)
```svg
<filter id="speck"><feTurbulence type="fractalNoise" baseFrequency=".9" numOctaves="2" seed="2" result="n"/>
<feColorMatrix in="n" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -9 5.2" result="m"/>
<feComposite in="SourceGraphic" in2="m" operator="in"/></filter>
```
**QA**: [ ] ≤ 6 inks, no gradients [ ] overprints painted explicitly [ ] mis-registration visible but modest [ ] ≥ 3 ink groups
---
## Risograph {#risograph}
**Layer stack**: `paper (#f4efe4)` → `ink-1 (fluoro pink)` → `ink-2 (blue)` → `ink-3 optional` → `grain overlay`.
**Design moves**
- 2-3 inks max. The overprint of pink + blue makes the deep purple - use it deliberately for shadows.
- Gradients = *grain gradients* (dots that thin out), never smooth.
- Everything slightly off-register (1-3 units).
**Recipe - grain gradient fill** (dots that thin out; no smooth gradient)
```svg
<defs>
<pattern id="grain" width="3" height="3" patternUnits="userSpaceOnUse"><circle cx="1.5" cy="1.5" r=".8" fill="#ff48b0"/></pattern>
<linearGradient id="fadeg" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#000"/></linearGradient>
<mask id="grainmask"><rect width="512" height="512" fill="url(#fadeg)"/></mask>
</defs>
<rect x="60" y="60" width="392" height="392" fill="url(#grain)" mask="url(#grainmask)"/>
```
Overprint: `style="mix-blend-mode:multiply"` on each ink group, **plus** explicit overlap fills as fallback for renderers without blend support.
**QA**: [ ] ≤ 3-4 inks [ ] ≥ 1 grain/halftone pattern [ ] paper is off-white (never `#fff`) [ ] no smooth gradients
---
## Graffiti / Street Art {#graffiti-street-art}
**Layer stack**: `wall texture (brick/concrete)` → `grime` → `letter extrusion (dark offset)` → `letter fill (gradient)` → `outline black` → `inner highlight` → `drips` → `sparkles/arrows/crowns` → `overspray`.
**Design moves**
- Letters are **drawn paths** (bubbly, overlapping), not `<text>`. Build each with 4 stacked copies: extrusion, fill, black outline, white inner line.
- Drips: tapered vertical paths ending in a round blob; place under letter bottoms.
- Wall: brick `<pattern>` with 3 slightly different brick fills.
**Recipe - letter stack via `<use>`**
```svg
<defs><path id="L" d="…bubble letter path…"/></defs>
<use href="#L" transform="translate(6 8)" fill="#3a1f5a"/> <!-- extrusion -->
<use href="#L" fill="url(#spray)" stroke="#111" stroke-width="8" stroke-linejoin="round"/> <!-- fill + outline -->
<use href="#L" fill="none" stroke="#fff" stroke-width="2" transform="translate(-2 -2)" opacity=".8"/> <!-- highlight -->
<path d="M180 340 v46 q0 8 6 0 v-46" fill="#ff2e93"/> <!-- drip -->
```
**QA**: [ ] ≥ 2 stroke widths [ ] gradient/spray fade present [ ] wall texture present [ ] no real artist signatures/tags copied
styles/references/technical.md
# Technical & data styles
Reference for: Blueprint, Technical Illustration, Scientific Illustration, Infographic.
**Principle**: geometry is *computed*, not eyeballed. For anything with real dimensions or data, write a small Python/JS generator and emit the SVG; keep the generator in the output folder if the user may want to tweak numbers.
---
## Blueprint {#blueprint}
**Layer stack**: `blue ground` → `minor grid` → `major grid` → `border + title block` → `construction lines (hairline)` → `object linework (thick)` → `hidden/centre lines` → `dimensions + leaders + callouts` → `notes`.
**Line hierarchy** (units at 1000-wide canvas)
| Role | Width | Dash |
|---|---|---|
| Object outline | 2.5 | solid |
| Feature edge | 1.5 | solid |
| Hidden edge | 1.0 | `6 3` |
| Centre line | 0.8 | `12 3 2 3` |
| Dimension / leader | 0.6 | solid |
| Minor grid | 0.4 | solid |
**Recipe - grid + dimension marker**
```svg
<defs>
<pattern id="g-minor" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M10 0H0V10" fill="none" stroke="#2a5cb5" stroke-width=".4" opacity=".5"/></pattern>
<pattern id="g-major" width="50" height="50" patternUnits="userSpaceOnUse"><path d="M50 0H0V50" fill="none" stroke="#2a5cb5" stroke-width=".8" opacity=".7"/></pattern>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0 1L10 5L0 9Z" fill="#dbeaff"/></marker>
</defs>
<rect width="1000" height="700" fill="#0d3b8c"/><rect width="1000" height="700" fill="url(#g-minor)"/><rect width="1000" height="700" fill="url(#g-major)"/>
<g id="dim-width" stroke="#dbeaff" stroke-width=".6" fill="none">
<path d="M200 520H700" marker-start="url(#arr)" marker-end="url(#arr)"/>
<path d="M200 470V530M700 470V530"/>
<text x="450" y="514" fill="#dbeaff" stroke="none" text-anchor="middle" font-family="'Courier New', monospace" font-size="14">500</text>
</g>
```
**QA**: [ ] ≥ 3 distinct stroke widths [ ] grid `<pattern>` present [ ] every dimension has extension lines + arrows + value [ ] title block (name, scale, sheet) [ ] no gradients/shading [ ] colour = blue/white/cyan only
---
## Technical Illustration {#technical-illustration}
**Layer stack**: `paper` → `ground shadow (faint)` → `part fills (grey tones)` → `linework` → `section hatch` → `alignment lines (dashed)` → `arrows` → `callouts`.
**Design moves**
- Exploded view: parts separated along ONE axis with dashed alignment lines; keep them in assembly order.
- Signal colour marks *what moves* or *what matters* (orange); everything else is grey.
- Section hatch at 45° in cut faces via pattern + clip.
**Recipe - callout symbol**
```svg
<symbol id="callout" viewBox="-14 -14 28 28"><circle r="12" fill="#fff" stroke="#1a1a1a" stroke-width="1.5"/></symbol>
<g id="c1"><path d="M410 300 L470 250" stroke="#1a1a1a" stroke-width=".8"/>
<use href="#callout" x="470" y="250" width="28" height="28" transform="translate(-14 -14)"/>
<text x="470" y="255" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="700">1</text></g>
```
**QA**: [ ] consistent line weights [ ] every arrow means something (motion/assembly) [ ] numbered callouts sequential [ ] ≤ 2 signal colours
---
## Scientific Illustration {#scientific-illustration}
**Layer stack**: `plate paper + border` → `main specimen (washes)` → `contours + hatch` → `detail insets` → `labels + leader lines` → `caption + scale bar`.
**Design moves**
- Accuracy over flourish: research proportions; note uncertainty in the caption if approximating.
- Tone via gradient washes *inside* contours (opacity .6-.9), then hatch/stipple for form.
- Repeat units (scales, feathers, petals) via `<use>` with per-instance variation.
- Caption block in italic serif; scale bar with real units when a size is known.
**Recipe - stipple + hatch patterns**
```svg
<pattern id="stipple" width="6" height="6" patternUnits="userSpaceOnUse"><circle cx="1.2" cy="1.4" r=".6" fill="#3a2a1e"/><circle cx="4.4" cy="4.2" r=".5" fill="#3a2a1e"/></pattern>
<pattern id="hatch" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(35)"><path d="M0 0V4" stroke="#3a2a1e" stroke-width=".6"/></pattern>
```
**QA**: [ ] labelled parts + leader lines [ ] Latin/common name caption [ ] scale bar [ ] muted natural palette [ ] hairline weights survive at 1000 px
---
## Infographic {#infographic}
**Layer stack**: `background` → `header` → `hero-stat` → `modules (cards)` → `charts` → `legend` → `footer/source`.
**Design moves**
- Sketch the reading order in one sentence before drawing.
- Compute geometry from data. Bar length = `value / max * width`; donut arc = `2πr · value/total`; never eyeball proportions.
- Colour encodes meaning; greys for context. Check distinguishability without hue (vary lightness).
- Text: title 3-4× body; label size ≥ 12 units on a 1000-wide canvas; `text-anchor` for alignment.
**Recipe - generated bar chart (Python)**
```python
data = [("Q1", 42), ("Q2", 58), ("Q3", 71), ("Q4", 66)]
mx, W, H, bar = max(v for _, v in data), 520, 260, 90
out = []
for i, (k, v) in enumerate(data):
h = v / mx * H
out.append(f'<rect x="{i*(bar+20)}" y="{H-h:.1f}" width="{bar}" height="{h:.1f}" rx="6" fill="#2563eb"/>')
out.append(f'<text x="{i*(bar+20)+bar/2}" y="{H+22}" text-anchor="middle" font-size="14">{k}</text>')
```
**Donut arc path**: `M cx+r cy A r r 0 large 1 x y` with `large = 1 if share > .5` and `x,y = (cx + r·cos θ, cy + r·sin θ)`.
**QA**: [ ] ≥ 3 named groups (`header`, `chart-1`, `legend`, `footer`) [ ] numbers reconcile with source data [ ] no 3D/chartjunk [ ] `<title>`/`<desc>` present [ ] never invent statistics - use clearly labelled placeholder data if none supplied
styles/registry/comics.json
[
{
"id": "comic-book",
"name": "Comic Book",
"family": "comics",
"aliases": ["comic", "comics", "comic style", "comic book style", "western comic", "superhero comic", "pop art comic", "lichtenstein", "graphic novel", "cartoon inks", "marvel style", "ben-day dots"],
"keywords": ["halftone", "speech bubble", "action lines", "ben day", "inked", "panel", "pow"],
"description": "Bold inked contours with dynamic angles, flat saturated colours, Ben-Day halftone shading, spotted blacks and energetic action typography.",
"visual_characteristics": [
"thick-to-thin ink outlines with confident spotted blacks",
"flat CMYK-like colour (primary red, yellow, cyan, magenta) with halftone dot shading",
"dynamic foreshortening, diagonal composition, speed lines and impact bursts",
"onomatopoeia and caption boxes as graphic elements"
],
"palette": {"guidance": "Primary-leaning process colours: red #e63946, yellow #ffd60a, cyan #00b4d8, magenta #d81b60, plus ink black and paper cream. Shadows via halftone dots or darker flat tone, not gradients.", "swatches": ["#fff8e1", "#111111", "#e63946", "#ffd60a", "#00b4d8", "#d81b60", "#3a86ff"]},
"geometry": "Heroic proportions, angular shadows, dramatic perspective and cropping; bursts as jagged star polygons; panel borders as skewed rectangles with gutters.",
"stroke": "Variable line weight: heavy outer contour (4-6 units), medium interior (2-3), fine detail (1). Round joins on outer contour, miter on bursts; filled-black shadow shapes.",
"texture": "Ben-Day dots via <pattern> (circles on a rotated 45 degree grid, size varies by tone) used for skin tones and shadows; zip-a-tone diagonal hatching.",
"composition": "Dynamic Dutch angles, subject breaking the panel frame, speed lines converging on the focal point, high contrast between black masses and bright colour.",
"typography": "Bold hand-lettered caps: impact SFX text with thick outline and drop shadow (e.g. 'POW!'), caption boxes in yellow with black border.",
"lighting_depth": "Hard light: solid black cast shadows and rim highlights; depth via overlap and line weight (thick foreground, thin background).",
"svg_techniques": [
"halftone: <pattern patternTransform=\"rotate(45)\"> with a dot circle; vary dot radius by defining 3 patterns (light/mid/dark) and using them on clipped shape regions",
"ink layer separate from colour layer (colour slightly offset for print mis-registration)",
"burst = star <polygon> with alternating radii, thick outline, drop shadow polygon offset",
"text with paint-order=\"stroke\" for outlined lettering"
],
"avoid": ["soft gradients", "thin uniform outlines", "pastel palette", "static centred posing", "photoreal shading"],
"compatible_with": ["manga", "cyberpunk-neon", "sci-fi", "screen-print", "risograph", "vintage-poster", "monochrome", "sticker", "hand-drawn-doodle", "graffiti-street-art", "retro-futurism", "horror"],
"conflicts_with": ["minimal-modern", "luxury-premium", "clay-soft-3d", "frutiger-aero", "sumi-e", "glassmorphism"],
"best_for": ["illustration", "poster", "character", "scene", "sticker"],
"detail_level": "high",
"checks": {"require_stroke": true, "min_stroke_widths": 2, "require_elements_any": ["pattern"], "min_elements": 30},
"reference": "comics.md#comic-book"
},
{
"id": "manga",
"name": "Manga",
"family": "comics",
"aliases": ["manga style", "anime manga", "japanese comic", "shonen", "shojo", "seinen", "manga panel", "black and white manga", "screentone", "tankobon", "manga ink"],
"keywords": ["japanese", "japan", "screentone", "speed lines", "sparkle eyes", "manga panel", "kawaii ink", "inking"],
"description": "Crisp black-ink linework with G-pen line variation, screentone shading, expressive emotive framing, speed lines and emphasis effects; mostly black and white with optional single-colour accents.",
"visual_characteristics": [
"clean, precise contours with tapered strokes; hair as flowing clumps with highlight gaps",
"screentone (dot/gradient/hatch) instead of shading gradients",
"large expressive eyes / stylised faces; emotion effects (sweat drops, blush lines, sparkles)",
"speed lines, focus lines and dramatic panel framing"
],
"palette": {"guidance": "Ink black on white paper, with 2-3 screentone greys (dot patterns at different densities). Optional colour-page variant: flat cel colours with soft cool shadow tone.", "swatches": ["#ffffff", "#111111", "#4a4a4a", "#9a9a9a", "#d9d9d9", "#ff5d8f"]},
"geometry": "Slender proportions, angular jaw/hair points, dynamic gesture; background rendered with ruler-straight perspective lines; effects lines radiate from a focal point.",
"stroke": "Tapered variable-width filled paths for contours (thick at shadow side, thin at highlights); hairline for hatch; round caps; 3 weights (outer 3, inner 1.5, detail .7).",
"texture": "Screentones: <pattern> of dots at 45 degrees (three densities), gradient tones via dot radius ramp; hatch and cross-hatch; solid black fill for deep shadows.",
"composition": "Panel-based or splash page; emphasis by empty white margins; radial focus lines behind the subject; asymmetric framing with strong diagonal.",
"typography": "Vertical-feel SFX strokes as shapes, speech balloons with tails; keep text minimal or as shapes.",
"lighting_depth": "Graphic: black cast shadows plus tone; rim-light gaps in hair; atmospheric perspective by thinner distant lines.",
"svg_techniques": [
"tapered contour = closed path built from two offset curves (or stroke + separate thinner overlay)",
"3 screentone <pattern> defs (dot radius .6/1.1/1.6 at spacing 4, patternTransform rotate 45)",
"speed lines = many <line> in a radial <g> with clip to panel; vary length and weight",
"panel frame as thick rect stroke with skewed variants"
],
"avoid": ["gradient shading", "western comic halftone colours", "uniform-width strokes", "realistic proportions", "colour overload"],
"compatible_with": ["comic-book", "monochrome", "cyberpunk-neon", "sci-fi", "horror", "fantasy", "sticker", "hand-drawn-doodle", "sumi-e"],
"conflicts_with": ["clay-soft-3d", "frutiger-aero", "glassmorphism", "luxury-premium", "brutalist"],
"best_for": ["character", "illustration", "poster", "scene"],
"detail_level": "high",
"checks": {"require_stroke": true, "min_stroke_widths": 2, "require_elements_any": ["pattern"], "max_colors": 10, "max_filters": 0},
"reference": "comics.md#manga"
}
]
styles/registry/core-modern.json
[
{
"id": "minimal-modern",
"name": "Minimal / Modern",
"family": "core-modern",
"weak_aliases": ["contemporary"],
"aliases": ["minimal", "minimalist", "minimalism", "modern minimal", "clean modern", "simple modern", "swiss minimal"],
"keywords": ["clean lines", "negative space", "whitespace", "less is more"],
"description": "Reduction to the fewest shapes that still read instantly. The subject is a confident silhouette with one or two deliberate details; emptiness is an active design element.",
"visual_characteristics": [
"3-6 shapes carry the whole subject; every element earns its place",
"negative space >= 40% of the canvas; subject sits on a considered off-centre or optical-centre anchor",
"one dominant colour + one accent + neutral; no decorative texture",
"quiet precision: aligned edges, shared radii, shared angles"
],
"palette": {"guidance": "Off-white or deep ink base, one saturated accent, one soft neutral. Max 4 colours in total. Colour is used to guide the eye to a single focal point.", "swatches": ["#f5f3ee", "#1a1a1f", "#ff5a36", "#c9c6bd", "#2b59ff"]},
"geometry": "Primitives (circle, rect, rounded rect, arc, simple polygon) on a hidden 8- or 12-unit grid. Shared corner radius across the piece. Curves are true arcs or clean quadratic curves, never wobbly. Prefer 45/90 degree angles.",
"stroke": "Either fill-only or one uniform stroke weight (2-4% of canvas) with round caps and joins. Never mix more than two stroke weights.",
"texture": "None. No noise, no grain, no patterns.",
"composition": "Rule of thirds or true centre with generous margins (>= 10%). One focal point. Asymmetric balance through scale contrast (big shape vs tiny accent). Align to a baseline.",
"typography": "If text is needed: one geometric or grotesque sans, 1-2 sizes, generous tracking on small caps, aligned to the grid. Prefer converting to paths for logos.",
"lighting_depth": "Flat. Depth only by overlap and scale; optionally one soft cast shadow shape at low opacity, never a blur filter.",
"svg_techniques": [
"author on a 0 0 512 512 (or 1024) grid; snap coordinates to multiples of 8/16",
"use <circle>/<rect rx>/<path> arcs; build symmetry with <use> and a mirror transform",
"define palette as CSS custom properties or one <style> block for easy recolouring",
"use clip-path to trim shapes instead of drawing complicated paths"
],
"avoid": ["gradients as decoration", "drop shadows/glows via filters", "more than two stroke weights", "tiny details that vanish at 32px", "ornamental frames", "default 'blob' shapes"],
"compatible_with": ["cyberpunk-neon", "art-deco", "geometric", "monochrome", "luxury-premium", "blueprint", "flat-design", "retro-futurism", "sumi-e", "ukiyo-e", "art-nouveau", "sci-fi"],
"conflicts_with": ["medieval-manuscript", "gothic", "comic-book", "graffiti-street-art"],
"as_secondary": "acts as a REDUCTION MODIFIER: cut the primary's detail ~60%, keep 3-4 colours and >=40% negative space, but retain ONE signature technique of the primary (e.g. a single neon edge, one line of ornament).",
"best_for": ["logo", "icon", "illustration", "poster"],
"detail_level": "low",
"checks": {"max_elements": 70, "max_colors": 7, "max_filters": 0, "max_gradients": 1, "max_stroke_widths": 2},
"reference": "core-modern.md#minimal-modern"
},
{
"id": "flat-design",
"name": "Flat Design",
"family": "core-modern",
"aliases": ["flat", "flat illustration", "flat vector", "flat style", "flat art", "vector flat", "material flat", "corporate flat"],
"keywords": ["no gradients", "solid colors", "solid colours", "simple shapes", "vector illustration", "explainer illustration"],
"description": "Solid-colour shapes with no outlines, gradients or realistic shading. Depth comes from overlap, colour value steps and long or offset shadow shapes.",
"visual_characteristics": [
"solid fills only; shading is done with a second, darker or lighter flat shape",
"friendly simplified proportions; rounded rectangles and circles dominate",
"bright but harmonised palette of 5-8 colours with clear value steps",
"clear layering: background, mid shapes, character/object, small props"
],
"palette": {"guidance": "Curated 5-8 colour palette; each hue gets one light and one dark step. Keep saturation consistent; use one warm and one cool family.", "swatches": ["#ffd166", "#ef476f", "#06d6a0", "#118ab2", "#073b4c", "#fef6e4"]},
"geometry": "Simplified geometric anatomy: circles for heads, rounded rects for bodies. Consistent corner radius. Edges are hard; shapes tile without gaps. Minimal curves; clean bezier arcs.",
"stroke": "None by default (fill-only). If lines are needed for detail, use one weight, round caps, in a darker tone of the fill rather than black.",
"texture": "None. Optionally a very sparse dot or diagonal-line accent for one area.",
"composition": "Scene built in 3 depth planes. Central subject with supporting props at varied scale. Use overlapping to imply depth and keep a clear focal contrast.",
"typography": "Rounded or geometric sans, bold weights, high legibility. Short labels only.",
"lighting_depth": "Implied light from top-left: one darker flat 'shade' shape per object, occasionally a long flat shadow at 45 degrees (a parallelogram path). No blur, no gradients.",
"svg_techniques": [
"one <g> per object, palette via CSS variables",
"shadow shapes created by clipping a duplicate with clip-path (define once, <use> repeatedly)",
"long shadows as polygons with fill-opacity .12-.2",
"no filters at all"
],
"avoid": ["gradients", "outer glows", "textured brushes", "realistic proportions", "hairline strokes", "black outlines everywhere"],
"compatible_with": ["minimal-modern", "geometric", "editorial-illustration", "infographic", "sticker", "childrens-illustration", "paper-cutout", "isometric"],
"conflicts_with": ["glassmorphism", "clay-soft-3d", "frutiger-aero", "scientific-illustration"],
"best_for": ["illustration", "icon", "scene", "infographic", "character"],
"detail_level": "medium",
"checks": {"max_gradients": 0, "max_filters": 0, "max_colors": 14},
"reference": "core-modern.md#flat-design"
},
{
"id": "geometric",
"name": "Geometric",
"family": "core-modern",
"aliases": ["geometric art", "geometric style", "geometric abstraction", "shape based", "made of shapes", "constructivist shapes", "tangram"],
"keywords": ["triangles", "circles and squares", "geometry", "grid based", "modular"],
"description": "The subject is constructed from a small vocabulary of mathematical primitives on a visible or hidden construction grid; rhythm, symmetry and proportion do the storytelling.",
"visual_characteristics": [
"circles, triangles, squares, arcs and lines only - sometimes 1-2 special forms (quarter circles, lens shapes)",
"strict proportion system (golden ratio, root-2, or a modular grid)",
"repetition, rotation and mirroring create rhythm",
"hard edges, exact tangencies, no organic wobble"
],
"palette": {"guidance": "Limited palette (3-6) with strong value separation; alternate warm/cool between adjacent shapes so every boundary reads.", "swatches": ["#0f1b2d", "#e63946", "#f1c453", "#2a9d8f", "#f4f1de"]},
"geometry": "Every shape derives from a documented grid (state it in a comment). Arcs share centres; triangles are equilateral or right isosceles; angles are multiples of 15 degrees. Overlaps use even-odd cut-outs.",
"stroke": "Prefer fills. If lines are used they are constant-width, butt caps, miter joins for crispness.",
"texture": "None, or a repeated geometric pattern (dots, hatch) as its own tiled shape.",
"composition": "Centred or radially symmetric or strict modular grid. Balance through repetition and scale steps. Leave consistent gutters.",
"typography": "Constructed geometric caps drawn from the same primitives, or a geometric sans.",
"lighting_depth": "Flat. Depth via overlap and transparency (multiply-like overlaps by explicit intersection colours).",
"svg_techniques": [
"<pattern> or <use> loops for repetition; rotate <use> around a centre for radial symmetry",
"explicit intersection shapes via clip-path for overlap colours instead of mix-blend-mode",
"comment the grid unit at the top; compute coordinates from it",
"fill-rule=\"evenodd\" for concentric cut-outs"
],
"avoid": ["organic wobble", "unaligned edges", "arbitrary angles", "more than two special shapes", "soft shadows"],
"compatible_with": ["minimal-modern", "bauhaus", "art-deco", "flat-design", "monochrome", "low-poly", "retro-futurism", "isometric", "cyberpunk-neon", "screen-print", "risograph", "luxury-premium"],
"conflicts_with": ["hand-drawn-doodle", "organic-botanical", "sumi-e", "horror"],
"best_for": ["logo", "icon", "pattern", "poster", "illustration"],
"detail_level": "medium",
"checks": {"max_filters": 0, "min_elements": 8, "max_colors": 9},
"reference": "core-modern.md#geometric"
},
{
"id": "monochrome",
"name": "Monochrome",
"family": "core-modern",
"aliases": ["monochromatic", "black and white", "b&w", "greyscale", "grayscale", "single color", "single colour", "one color", "one colour", "duotone", "two tone"],
"keywords": ["black white", "ink only", "tonal", "value study"],
"description": "One hue (or pure black on white) carries everything. Form, depth and focus are built purely from value, line weight, pattern density and negative space.",
"visual_characteristics": [
"a 4-7 step value ladder from paper to darkest ink",
"contrast planned so the focal point holds the strongest value jump",
"line weight and hatching density replace colour as the variable",
"silhouette must work in pure black"
],
"palette": {"guidance": "Choose ONE hue (or neutral) and derive 4-7 tints/shades of it by lightness only; keep saturation constant. For duotone add a second ink and use it sparingly.", "swatches": ["#101418", "#3a4550", "#6f7d8a", "#aab6c0", "#dfe5ea", "#fbfcfd"]},
"geometry": "Any geometry that suits the subject, but built from large clear value masses first, then edge detail. Shapes have soft or hard edges consistently within the piece.",
"stroke": "Line weight variation is the main expressive tool: thick for silhouette/contours, thin for interior detail. Round caps, round joins.",
"texture": "Hatching, stippling or halftone patterns to create mid-values without introducing new colours.",
"composition": "Value-block first: 3 masses (dark, mid, light). Strongest contrast at focal point; darks connected into a readable shape; leave paper white for highlights.",
"typography": "Single family; weight contrast (light vs black) for hierarchy.",
"lighting_depth": "Strong single light source; cast shadows are the darkest value; atmospheric depth by lightening distant forms.",
"svg_techniques": [
"define value steps as fills (or one fill with varying fill-opacity over a paper base)",
"<pattern> for hatch / dots at 2-3 densities",
"group by value layer (highlights / mids / darks) for editability",
"stroke-width variety through separate <path> layers, not filters"
],
"avoid": ["a second hue creeping in", "pure grey mush (no value contrast)", "gradients that muddy the value ladder", "using opacity on multiple stacked shapes so values become unpredictable"],
"compatible_with": ["minimal-modern", "manga", "sumi-e", "screen-print", "geometric", "technical-illustration", "gothic", "horror", "blueprint", "art-deco", "luxury-premium", "hand-drawn-doodle", "comic-book"],
"conflicts_with": ["frutiger-aero", "y2k", "childrens-illustration"],
"as_secondary": "acts as a PALETTE CONSTRAINT: keep the primary's structure but recolour everything to a single hue's value ladder (or black/white). Lighting and texture of the primary are expressed through value only.",
"best_for": ["illustration", "logo", "poster", "icon"],
"detail_level": "medium",
"checks": {"max_colors": 9, "max_filters": 1},
"reference": "core-modern.md#monochrome"
},
{
"id": "luxury-premium",
"name": "Luxury / Premium",
"family": "core-modern",
"weak_aliases": ["luxury", "premium", "upscale", "opulent", "prestige", "high end", "high-end", "elegant luxury", "haute"],
"aliases": ["luxury style", "premium style", "luxury aesthetic", "luxury branding", "gold and black"],
"keywords": ["gold", "champagne", "jewelry", "boutique", "five star", "couture", "fine"],
"description": "Restraint plus precision: dark or ivory grounds, hairline geometry, metallic gradients used sparingly, and generous emptiness that signals confidence.",
"visual_characteristics": [
"very deep base (near-black, midnight, oxblood) or warm ivory",
"metallic accent (gold/champagne/rose gold) as thin lines and small masses, never large flat areas",
"symmetry, fine ornament, precise margins",
"small number of elements with impeccable alignment"
],
"palette": {"guidance": "Near-black or ivory base; metallic gradient accent (3-4 stops from deep gold to pale champagne); one muted secondary (emerald, oxblood or navy). No bright saturated colours.", "swatches": ["#0c0b0d", "#1a1424", "#b8893b", "#f1d99a", "#f7f1e3", "#0f3d33"]},
"geometry": "Symmetrical, centred, on a strict grid. Thin double rules, fine arcs, small diamond/lozenge ornaments. Monoline construction.",
"stroke": "Hairline (0.5-1.5% of canvas) with butt or round caps; double rules with a gap; metallic stroke via linearGradient.",
"texture": "Optional very subtle fine grain or micro-pattern at <= 5% opacity; or none.",
"composition": "Centred axis, tall margins, small mark with lots of air; type spaced widely. Key element sits slightly above the geometric centre (optical centre).",
"typography": "High-contrast serif (Didone/Garamond-like) or thin wide-tracked sans caps; small sizes, tracking +150..+300. Fall back to generic serif; outline text for logos.",
"lighting_depth": "Flat with metallic gradients that imply a light sweep (diagonal, 3-4 stops); no heavy shadows.",
"svg_techniques": [
"define a reusable <linearGradient id=\"gold\"> with userSpaceOnUse coordinates so the sweep is consistent across shapes",
"use stroke=\"url(#gold)\" for hairlines",
"mask a subtle diagonal sheen across the main mark",
"keep <text> letter-spacing generous and convert to paths for the final logo"
],
"avoid": ["yellow flat 'gold'", "busy ornament", "heavy drop shadows", "bevel/emboss effects", "bright saturated accents", "cramped margins"],
"compatible_with": ["art-deco", "minimal-modern", "monochrome", "geometric", "art-nouveau", "gothic", "vintage-poster", "cinematic-poster"],
"conflicts_with": ["childrens-illustration", "graffiti-street-art", "sticker", "y2k", "pixel-art"],
"as_secondary": "acts as a SURFACE FINISH: keep the primary's structure, but move to a near-black or ivory base, thin the strokes, and introduce metallic gold-gradient accents on 1-3 hero elements only.",
"best_for": ["logo", "poster", "icon", "pattern"],
"detail_level": "low",
"checks": {"min_gradients": 1, "max_stroke_widths": 3, "max_filters": 1, "max_colors": 12},
"reference": "core-modern.md#luxury-premium"
}
]
styles/registry/craft.json
[
{
"id": "paper-cutout",
"name": "Paper Cutout",
"family": "craft",
"aliases": ["paper cut", "paper cutout", "paper-cut", "papercut", "paper craft", "layered paper", "paper collage", "cut paper", "paper art", "construction paper", "matisse cutout"],
"keywords": ["layered", "collage", "scissor cut", "stacked paper", "shadow box"],
"description": "Scenes built from layered sheets of paper: flat coloured shapes with slightly irregular scissor-cut edges, each layer casting a small soft shadow onto the one beneath.",
"visual_characteristics": [
"3-7 clearly separated layers from back to front",
"flat matte colour per sheet (no gradients), occasional printed-paper pattern",
"small consistent drop shadow under each layer, creating physical depth",
"slightly wavy, hand-cut edges; simplified organic shapes"
],
"palette": {"guidance": "Craft-paper palette: 5-8 flat colours, warm neutrals + 2-3 saturated accents. Depth via value: farther layers lighter/cooler, nearer layers darker/warmer, or vice versa consistently.", "swatches": ["#f3e9d2", "#e9c46a", "#f4a261", "#e76f51", "#2a9d8f", "#264653", "#8ab17d"]},
"geometry": "Organic simplified silhouettes with gently irregular outlines (avoid perfect circles); scissor-cut nicks and small straight-cut facets; punched circles for holes. Layers interlock.",
"stroke": "None. Edges are the paper edge. Optional very thin lighter edge highlight on the top layer.",
"texture": "Optional fine paper fibre via feTurbulence at ~6% opacity on a single overlay; or subtle speckle pattern. No gradients within a sheet.",
"composition": "Landscape-like depth stacking (sky, far hills, mid hills, trees, foreground, subject). Subject contrast against the nearest background layer. Overlaps generous.",
"typography": "Cut-out letter blocks with slight rotation and offset shadows, chunky sans or rounded shapes.",
"lighting_depth": "Light from top-left: each layer gets a 2-4 unit soft shadow offset down-right (opacity .25-.35). Shadow grows slightly for higher layers.",
"svg_techniques": [
"define ONE drop-shadow filter (feDropShadow dx=1.5 dy=2.5 stdDeviation=2 opacity .3) and apply it to each layer group",
"one <g id=\"layer-N\"> per sheet in strict back-to-front order",
"irregular edges by hand-nudged path points or Q-curves with slight asymmetry",
"paper texture as a single <rect> overlay using feTurbulence + feColorMatrix at low opacity"
],
"avoid": ["gradients", "outlines", "perfect geometric primitives everywhere", "different shadow directions", "photoreal materials", "more than one blur filter definition"],
"compatible_with": ["flat-design", "childrens-illustration", "organic-botanical", "minimal-modern", "sticker", "editorial-illustration", "fantasy", "screen-print", "risograph"],
"conflicts_with": ["glassmorphism", "cyberpunk-neon", "blueprint", "pixel-art", "scientific-illustration"],
"best_for": ["scene", "illustration", "poster", "character"],
"detail_level": "medium",
"checks": {"min_filters": 1, "max_gradients": 2, "min_groups": 3, "forbid_stroke": true},
"reference": "craft.md#paper-cutout"
},
{
"id": "hand-drawn-doodle",
"name": "Hand Drawn / Doodle",
"family": "craft",
"aliases": ["hand drawn", "hand-drawn", "handdrawn", "doodle", "doodles", "doodle style", "sketchy", "sketch", "pen sketch", "scribble", "marker drawing", "ink doodle", "notebook doodle"],
"keywords": ["wobbly lines", "marker", "felt tip", "napkin sketch", "scribble fill"],
"description": "Loose, human line work: wobbly contours, uneven weights, offset colour fills that do not quite stay in the lines, and playful imperfection that feels drawn in a notebook.",
"visual_characteristics": [
"lines that overshoot and wobble slightly; double-stroked contours in places",
"colour fills offset from the outlines (mis-registration) and scribble/hatch fills",
"simplified naive proportions with character",
"sparse small doodle fillers (stars, dots, squiggles, arrows)"
],
"palette": {"guidance": "Marker/pen palette: black or dark-blue ink line, 3-5 highlighter-like fill colours at slight transparency, warm paper white or cream base.", "swatches": ["#fbf7ee", "#1d2433", "#ff6b6b", "#ffd93d", "#4d96ff", "#6bcB77", "#c77dff"]},
"geometry": "Nothing perfectly straight or circular: hand-nudged control points, closed shapes that do not quite close, slightly rotated boxes. Keep proportion errors intentional and small.",
"stroke": "Round caps and joins always; weight varies 2-5 units between paths (or use two overlapping strokes with slight offset). Ink line colour is dark, not pure black.",
"texture": "Scribble hatching fills built from many short zig-zag strokes; optional faint paper grain. Fills offset by 2-4 units from outlines.",
"composition": "Centred subject with loosely arranged doodle props; unequal spacing; a wobbly underline or frame; playful scale contrast.",
"typography": "Handwritten look: build letters from wobbly strokes (or use a cursive/fantasy generic fallback and mention it). Keep short.",
"lighting_depth": "None; maybe hatch shading on one side. Depth only by overlap.",
"svg_techniques": [
"produce wobble by authoring paths with irregular quadratic curves (Q/T) and slightly uneven control points; optionally displace with ONE subtle feTurbulence+feDisplacementMap (scale 1.5-2.5) on the whole line group",
"fills as separate <path> layers under the line layer, translated by (2,1.5) to mimic mis-registration",
"hatching as a <pattern> of slightly rotated short lines used sparingly",
"stroke-linecap/linejoin round; vary stroke-width per path"
],
"avoid": ["mathematically perfect shapes", "gradients", "uniform stroke weight everywhere", "sharp miter corners", "heavy filters"],
"compatible_with": ["childrens-illustration", "sticker", "editorial-illustration", "monochrome", "organic-botanical", "flat-design", "comic-book", "risograph", "screen-print"],
"conflicts_with": ["glassmorphism", "isometric", "low-poly", "pixel-art", "blueprint", "luxury-premium", "clay-soft-3d"],
"best_for": ["illustration", "character", "icon", "sticker", "scene"],
"detail_level": "medium",
"checks": {"require_stroke": true, "require_linecap": "round", "require_linejoin": "round", "max_gradients": 0, "max_filters": 1},
"reference": "craft.md#hand-drawn-doodle"
},
{
"id": "sticker",
"name": "Sticker",
"family": "craft",
"aliases": ["sticker style", "die cut", "die-cut", "die cut sticker", "vinyl sticker", "sticker design", "emoji style", "patch", "enamel pin", "badge sticker"],
"keywords": ["white border", "peel", "cut line", "kiss cut", "glossy sticker"],
"description": "A bold, self-contained design wrapped by a thick white die-cut border and a subtle shadow, so it reads as an adhesive object regardless of the background.",
"visual_characteristics": [
"thick uniform white outline (contour offset) around the whole silhouette",
"saturated, high-contrast colours; dark interior outlines",
"simple readable shapes, charming character focus",
"optional glossy highlight streak and a tiny peeled-corner"
],
"palette": {"guidance": "Saturated 4-7 colour palette on flat fills with darker outlines; the white border is pure white (#fff); a soft neutral drop shadow beneath. Avoid muddy midtones.", "swatches": ["#ffffff", "#1c1c28", "#ff5d73", "#ffc857", "#3ddc97", "#4da3ff", "#8b5cf6"]},
"geometry": "Rounded, friendly, simplified shapes; die-cut contour smooth with no tiny notches (merge islands so the border is one continuous shape).",
"stroke": "Interior outlines 3-4 units, round joins; the die-cut border = the merged silhouette path stroked 10-14 units white with round joins, drawn UNDER the art.",
"texture": "None or subtle halftone on one shadow area; optional tiny sparkle marks.",
"composition": "Single centred subject filling ~80% of the canvas; sticker slightly rotated (-3..-6 degrees) optional; transparent background so the border and shadow carry the object.",
"typography": "Chunky rounded or brush caps with outline; text also gets the white border via the merged silhouette.",
"lighting_depth": "Flat art with a soft shadow below-right; optional white curved gloss streak clipped to the subject at 25% opacity.",
"svg_techniques": [
"duplicate the merged silhouette as <path id=\"cut\"> in defs; <use> it first with fill=#fff stroke=#fff stroke-width=12 stroke-linejoin=round",
"one soft feDropShadow (or blurred offset copy) on the border layer only",
"artwork layers above the border with dark outlines",
"transparent background (no full-canvas rect)"
],
"avoid": ["thin white borders", "borders with jagged notches", "multiple different border thicknesses", "photographic gradients", "filling the canvas with a background rect"],
"compatible_with": ["flat-design", "hand-drawn-doodle", "childrens-illustration", "y2k", "clay-soft-3d", "comic-book", "manga", "graffiti-street-art", "pixel-art"],
"conflicts_with": ["luxury-premium", "blueprint", "scientific-illustration", "medieval-manuscript"],
"best_for": ["sticker", "character", "icon", "illustration"],
"detail_level": "medium",
"checks": {"require_stroke": true, "max_gradients": 4, "max_filters": 2},
"reference": "craft.md#sticker"
},
{
"id": "childrens-illustration",
"name": "Children's Illustration",
"family": "craft",
"aliases": ["childrens book", "children's book", "kids illustration", "storybook", "picture book", "nursery", "kid friendly", "cute storybook", "bedtime story", "preschool illustration"],
"keywords": ["whimsical", "storybook", "friendly animals", "crayon", "gouache", "cozy"],
"description": "Warm, friendly picture-book art: rounded characters with big expressive faces, soft painterly textures, cheerful palettes and simple storytelling compositions.",
"visual_characteristics": [
"round, soft shapes; oversized heads and eyes; warm expressions",
"gouache/crayon-like flat colour with soft grain and visible imperfection",
"cheerful but not garish palette; cozy scenes with small narrative details",
"generous negative space for text"
],
"palette": {"guidance": "Warm, slightly chalky mid-saturation colours: sunny yellow, tomato red, sky blue, leaf green, blush; cream paper base. Shadows are gentle tints, not greys.", "swatches": ["#fff6e5", "#ffd166", "#ef8354", "#7bb0a8", "#5c8dd6", "#a3c76a", "#e5989b", "#4a3b52"]},
"geometry": "Rounded, slightly asymmetrical shapes; circles for cheeks/bodies; short limbs; soft terminals. Small proportion exaggeration for charm.",
"stroke": "Optional soft outline in a warm dark (brown/plum) with round caps at 2-3 units, slightly varied; or no outline with colour-on-colour details.",
"texture": "Light crayon/gouache grain via one low-opacity feTurbulence overlay clipped to shapes; sparse dotted patterns on clothes and grass.",
"composition": "Story-first: one clear focal character on a gentle vignette scene (hill, house, moon); leading path or curved horizon; open area for title.",
"typography": "Rounded, hand-lettered feel; large friendly title; generous letter spacing.",
"lighting_depth": "Soft glow around light sources (moon/lantern), gentle cast shadows as flat tinted ellipses; no hard specular.",
"svg_techniques": [
"compose characters from <ellipse>/<circle> + soft Q-curve paths; reuse eyes, cheeks, paws through <use>",
"one grain filter (feTurbulence baseFrequency .8) applied to a single overlay rect masked by the artwork alpha",
"soft glows via radialGradient only",
"keep stroke colour warm-dark and consistent"
],
"avoid": ["sharp angular shapes", "dark grim palettes", "photoreal shading", "dense tiny details", "black outlines"],
"compatible_with": ["hand-drawn-doodle", "paper-cutout", "flat-design", "clay-soft-3d", "organic-botanical", "sticker", "fantasy", "retro-8bit", "frutiger-aero"],
"conflicts_with": ["horror", "gothic", "cyberpunk-neon", "brutalist", "scientific-illustration", "blueprint", "graffiti-street-art"],
"best_for": ["illustration", "character", "scene", "poster"],
"detail_level": "medium",
"checks": {"min_elements": 20, "max_filters": 2, "min_groups": 3},
"reference": "craft.md#childrens-illustration"
},
{
"id": "editorial-illustration",
"name": "Editorial Illustration",
"family": "craft",
"aliases": ["editorial", "editorial style", "magazine illustration", "conceptual illustration", "op-ed illustration", "new yorker style", "concept art editorial", "article illustration", "metaphor illustration"],
"keywords": ["concept", "metaphor", "newspaper", "magazine", "visual metaphor", "surreal collage"],
"description": "Concept-driven imagery: a single strong visual metaphor rendered with sophisticated shape language, restrained palette, textural flair and confident scale contrast.",
"visual_characteristics": [
"one idea, one metaphor, instantly readable at thumbnail size",
"unexpected scale relationships and juxtapositions (giant hand / tiny figure)",
"limited palette of 3-5 sophisticated colours with one accent",
"flat shapes plus purposeful texture (grain, halftone, brush edge)"
],
"palette": {"guidance": "Sophisticated, slightly desaturated 3-5 colours (deep teal, ochre, brick, ink, cream) plus one punchy accent; strong value structure; avoid rainbow palettes.", "swatches": ["#f1ead9", "#1f3a4d", "#c9553d", "#e0a93b", "#5d8a7b", "#111111"]},
"geometry": "Bold simplified shapes with stylised proportions; strong diagonals; cropping that lets shapes bleed off the canvas; occasional cut-out/collage overlaps.",
"stroke": "Sparingly; when used, expressive tapered lines (filled variable-width paths) rather than uniform strokes.",
"texture": "Grain and halftone on shadows and large flat areas; subtle paper texture overlay; brush-edge irregularities on key shapes.",
"composition": "Rule of thirds or dynamic asymmetry; deliberate negative space for headline; a clear hierarchy of 1 hero + 2 supporting shapes; cropping for drama.",
"typography": "Rarely embedded; if included, editorial serif/grotesk pairing at small scale.",
"lighting_depth": "Graphic light: flat shadow shapes and one gradient at most; depth via overlap and scale.",
"svg_techniques": [
"layered flat shapes with mix of clip-paths to create collage-like overlaps",
"halftone via <pattern> of circles masked by a gradient (tone-dependent dot size)",
"grain overlay as feTurbulence rect at 8% multiply-like opacity (single filter)",
"tapered lines as closed paths"
],
"avoid": ["clip-art literalism", "generic corporate people blobs", "many metaphors at once", "hairline detail", "rainbow palettes"],
"compatible_with": ["flat-design", "paper-cutout", "risograph", "screen-print", "minimal-modern", "monochrome", "geometric", "hand-drawn-doodle", "vintage-poster"],
"conflicts_with": ["pixel-art", "glassmorphism", "clay-soft-3d", "y2k"],
"best_for": ["illustration", "poster", "scene"],
"detail_level": "medium",
"checks": {"min_elements": 15, "max_filters": 2, "max_colors": 14},
"reference": "craft.md#editorial-illustration"
},
{
"id": "organic-botanical",
"name": "Organic / Botanical",
"family": "craft",
"weak_aliases": ["organic"],
"aliases": ["botanical", "botanical illustration", "organic style", "floral", "floral illustration", "plant illustration", "nature illustration", "foliage", "leaf pattern", "botanical print", "herbarium"],
"keywords": ["leaves", "flowers", "vines", "botany", "garden", "petals", "ferns", "wildflowers"],
"description": "Plants and natural forms rendered with graceful flowing curves, layered petals/leaves, delicate vein detail and harmonious natural palettes.",
"visual_characteristics": [
"asymmetric, rhythmic growth patterns; stems curve in S-shapes",
"layered leaves/petals with overlapping tone changes",
"fine vein and stamen detail; occasional bud/seed/insect accents",
"natural palette with a single flower accent"
],
"palette": {"guidance": "Greens with hue variation (yellow-green to blue-green), warm bloom accent (coral, ochre, lilac), cream or soft sage ground. Shade with darker green, never black.", "swatches": ["#f6f2e4", "#2f5d3a", "#5f9b58", "#a9c987", "#e58a7b", "#f2c14e", "#8c5a3c"]},
"geometry": "Curves-first: Bezier stems, teardrop and lanceolate leaf shapes, radial petal arrays (built with rotated <use>). Slight variation in each repeated leaf (scale, rotate, hue) so nothing looks copy-pasted.",
"stroke": "Fine 0.75-1.5 unit tapered veins/outlines in a dark green or sepia; round caps. For flat variants, fill-only with vein cut-outs.",
"texture": "Subtle watercolour wash via 2-3 stacked low-opacity fills and gradient edges; speckle; optional paper tone.",
"composition": "Curving diagonal growth (Hogarth curve) or symmetric wreath; specimens with generous margin; scientific-print layout with small captions optional; overlap for density at the base.",
"typography": "Small italic serif captions (Latin-like names), letterspaced; optional.",
"lighting_depth": "Soft: leaf undersides lighter, tone shifts at folds; light from one side with gradient shading on leaves.",
"svg_techniques": [
"define one leaf symbol (shape + vein group) and <use> it with varied transform, then override fill via CSS variable or currentColor",
"petal rings by rotating one <use> in 5-8 steps around a centre",
"gradient shading per leaf (linearGradient along the midrib) defined once via gradientTransform variations",
"stems as stroked paths with stroke-linecap round; veins as thin child paths"
],
"avoid": ["rigid symmetry for wild plants", "identical copy-paste leaves", "neon greens", "black outlines", "cartoon faces on plants"],
"compatible_with": ["art-nouveau", "scientific-illustration", "hand-drawn-doodle", "paper-cutout", "childrens-illustration", "medieval-manuscript", "sumi-e", "minimal-modern", "monochrome", "screen-print", "risograph", "flat-design"],
"conflicts_with": ["cyberpunk-neon", "isometric", "low-poly", "pixel-art", "brutalist"],
"best_for": ["illustration", "pattern", "poster", "icon"],
"detail_level": "high",
"checks": {"min_elements": 30, "max_filters": 1, "light_base": true},
"reference": "craft.md#organic-botanical"
}
]
styles/registry/decorative-eras.json
[
{
"id": "art-nouveau",
"name": "Art Nouveau",
"family": "decorative-eras",
"aliases": ["nouveau", "art nouveau style", "jugendstil", "mucha style", "mucha", "belle epoque poster", "whiplash curves", "liberty style", "secession style"],
"keywords": ["whiplash", "flowing hair", "floral frame", "organic ornament", "arched frame", "belle epoque"],
"description": "Sinuous whiplash curves and organic ornament: idealised figures or subjects framed by arched borders, botanical motifs, flat decorative colour and strong contour lines.",
"visual_characteristics": [
"continuous flowing S-curves; lines that morph from stem to hair to border",
"botanical ornament (lilies, irises, vines) integrated into the frame",
"flat muted decorative colour with strong dark contour lines",
"circular halo or arched panel behind the subject; ornamental typography that is part of the layout"
],
"palette": {"guidance": "Muted, earthy jewel tones: olive, sage, dusty rose, mustard, teal, terracotta on a cream or pale gold ground; contours in deep brown or teal-black. 6-9 colours, low-to-mid saturation.", "swatches": ["#efe2c0", "#c8a24a", "#7d8f5a", "#3f6b5e", "#c98b7a", "#8a4b3a", "#2c2a25"]},
"geometry": "Curves everywhere: parametric spirals, tapered tendrils, symmetrical or gently asymmetrical arches; mosaic-like patterned fills for robes/flowers; circles and arches as structural frames (Mucha's halo).",
"stroke": "Contour lines 2-3.5 units with tapered varying width (filled path or two stroke layers); round caps and joins; fine 0.8-unit inner detail lines.",
"texture": "Ornamental pattern fills (dot mosaics, small lozenges, scales) in <pattern>; optional faint paper grain.",
"composition": "Vertical poster format (2:3+); central figure/subject within an arched or circular halo; decorative border with botanical repetition; text panels top and bottom integrated in the ornament.",
"typography": "Hand-drawn stylised display letters with flowing curves (draw as paths or use a decorative-looking generic like fantasy/serif and note fonts are not embedded).",
"lighting_depth": "Flat with limited tonal gradients; depth via overlapping ornament and border layering.",
"svg_techniques": [
"author tendrils as cubic Bezier paths with smooth join continuity (mirror control handles); use stroke with linecap round then optionally duplicate with thinner lighter stroke for a highlight",
"arch frame via <path> with a semicircular top + inner offset path (double frame)",
"repeated flowers: one <symbol> with <use> variants rotated/scaled",
"pattern fills for robes: small <pattern> of 8x8 units"
],
"avoid": ["rigid straight lines", "neon or high-saturation colours", "photorealism", "isometric or geometric grid layouts", "cluttered without hierarchy"],
"compatible_with": ["organic-botanical", "ukiyo-e", "art-deco", "vintage-poster", "luxury-premium", "fantasy", "minimal-modern", "gothic", "medieval-manuscript", "screen-print", "editorial-illustration"],
"conflicts_with": ["pixel-art", "brutalist", "isometric", "low-poly", "y2k", "cyberpunk-neon"],
"best_for": ["poster", "illustration", "logo", "pattern"],
"detail_level": "high",
"checks": {"require_stroke": true, "min_stroke_widths": 2, "require_linecap": "round", "min_elements": 30, "max_filters": 0},
"reference": "decorative-eras.md#art-nouveau"
},
{
"id": "art-deco",
"name": "Art Deco",
"family": "decorative-eras",
"aliases": ["deco", "art deco style", "1920s deco", "gatsby style", "roaring twenties", "streamline moderne", "deco poster", "deco logo", "chrysler style"],
"keywords": ["sunburst", "fan motif", "chevron", "gold lines", "stepped", "gatsby", "geometric ornament", "symmetry"],
"description": "Glamorous geometric symmetry: radiating sunbursts, stepped forms, fans, chevrons and fine gold linework on deep colour, executed with precision and verticality.",
"visual_characteristics": [
"strict bilateral symmetry and a central axis",
"stepped/ziggurat silhouettes, radiating fans and sunbursts, concentric arcs",
"parallel gold line sets (triple rules) outlining forms",
"rich dark base (black, deep green, navy) with metallic gold and ivory"
],
"palette": {"guidance": "Black or deep emerald/navy base, metallic gold (#c9a227 -> #f3dd8b gradient), ivory, with one jewel accent (emerald, ruby, teal). 4-6 colours.", "swatches": ["#0d0d12", "#0f2e2a", "#c9a227", "#f3dd8b", "#f4ecd8", "#b3202f"]},
"geometry": "Built on a symmetry axis: sunburst rays (rotated <use> of a wedge), scalloped fans (concentric arcs), chevrons, stepped tiers, tall proportions and elongated forms. Corners chamfered rather than rounded.",
"stroke": "Parallel line sets of 1-2.5 units in gold with 3-4 unit gaps; butt or square caps and miter joins for crisp deco edges.",
"texture": "Fine line hatching, fan-scallop patterns via <pattern>; no grain.",
"composition": "Centred, vertical, symmetric; a hero motif inside an arch/frame; borders of repeated motifs; generous ornament at the top (crown/sunburst) and base (steps).",
"typography": "Geometric high-contrast caps with tall proportions (Broadway/Poiret-like): thin/thick strokes, wide tracking, small-caps subtitles; centre aligned.",
"lighting_depth": "Flat metallic: 3-4 stop gold gradients along a diagonal; no cast shadows; depth by stepped layering.",
"svg_techniques": [
"sunburst: define one <path id=\"ray\"> wedge, <use> it 24-36 times with rotate(i*step cx cy)",
"gold via linearGradient (userSpaceOnUse) shared by all strokes: stroke=\"url(#gold)\"",
"author the left half, mirror with <use transform=\"matrix(-1 0 0 1 W 0)\"> for exact symmetry",
"fan-scallop <pattern> of overlapping semicircle arcs for backgrounds"
],
"avoid": ["asymmetry", "organic wobble", "rounded bubbly forms", "flat yellow 'gold'", "too many colours", "unaligned lines"],
"compatible_with": ["luxury-premium", "geometric", "minimal-modern", "vintage-poster", "art-nouveau", "retro-futurism", "monochrome", "cinematic-poster", "screen-print", "sci-fi"],
"conflicts_with": ["hand-drawn-doodle", "childrens-illustration", "graffiti-street-art", "organic-botanical", "pixel-art", "clay-soft-3d"],
"best_for": ["logo", "poster", "pattern", "icon", "illustration"],
"detail_level": "medium",
"checks": {"min_gradients": 1, "max_filters": 0, "min_elements": 20, "require_elements_any": ["use", "pattern"]},
"reference": "decorative-eras.md#art-deco"
},
{
"id": "bauhaus",
"name": "Bauhaus",
"family": "decorative-eras",
"aliases": ["bauhaus style", "bauhaus poster", "bauhaus design", "de stijl bauhaus", "german modernism", "constructivist bauhaus", "primary shapes", "bauhaus geometry"],
"keywords": ["primary colors", "circle square triangle", "form follows function", "kandinsky", "asymmetric grid", "modernist"],
"description": "Form follows function: the primary triad of circle, square and triangle in red/yellow/blue plus black on off-white, arranged in dynamic asymmetric grids with diagonal type.",
"visual_characteristics": [
"circle/square/triangle vocabulary in primary red, yellow, blue, plus black and white",
"asymmetric but balanced composition on an underlying grid; diagonals",
"overlapping shapes with hard edges; thick bars and rules",
"sans-serif geometric type set on angles or vertical"
],
"palette": {"guidance": "Primary triad: red #d62828, yellow #f7b500, blue #1d4e9f, with black #111 and warm off-white #f1eadb. Optional grey. No gradients. 5-7 colours.", "swatches": ["#f1eadb", "#111111", "#d62828", "#f7b500", "#1d4e9f", "#8c8c8c"]},
"geometry": "Pure primitives: full circles, half-circles, quarter-circles, right triangles, bars; consistent unit module; 15/30/45/90 degree angles only.",
"stroke": "Prefer fills; rules/bars as rects 6-24 units; if strokes are used they are uniform, butt caps, miter joins.",
"texture": "None (occasionally a fine parallel-line field).",
"composition": "Dynamic asymmetry: heavy element off-centre balanced by small contrast elements; diagonal axis from top-left to bottom-right; text integrated as a structural bar.",
"typography": "Geometric sans (Futura-like) lowercase or uppercase, tight leading, rotated 90 or 45 degrees; bold and light weights only.",
"lighting_depth": "Flat. Overlaps use black multiply-like intersection shapes rather than transparency.",
"svg_techniques": [
"define unit U and compute all coordinates from it; comment the grid",
"half/quarter circles via <path> arcs; intersections via clip-path duplicates",
"text as <text> with transform=rotate(-90)",
"no filters, no gradients"
],
"avoid": ["gradients", "ornament", "curvy organic shapes", "pastel palettes", "centred symmetric layouts", "drop shadows"],
"compatible_with": ["geometric", "minimal-modern", "flat-design", "screen-print", "risograph", "editorial-illustration", "retro-futurism", "monochrome", "brutalist", "vintage-poster"],
"conflicts_with": ["art-nouveau", "fantasy", "horror", "organic-botanical", "childrens-illustration", "glassmorphism"],
"best_for": ["poster", "logo", "illustration", "pattern"],
"detail_level": "low",
"checks": {"max_gradients": 0, "max_filters": 0, "max_colors": 9, "max_stroke_widths": 3},
"reference": "decorative-eras.md#bauhaus"
},
{
"id": "brutalist",
"name": "Brutalist",
"family": "decorative-eras",
"aliases": ["brutalism", "brutalist design", "neo brutalism", "neo-brutalist", "brutalist web", "raw concrete style", "anti design", "swiss brutal", "ugly design"],
"keywords": ["raw", "concrete", "monospace", "harsh", "heavy borders", "hard shadow", "unrefined"],
"description": "Raw, confrontational graphic language: heavy black borders, hard offset shadows, harsh flat colour, monospace/grotesque type and deliberately unpolished, blocky composition.",
"visual_characteristics": [
"thick 4-8 unit black borders around every block",
"hard offset shadows (no blur) in solid black or a contrasting colour",
"harsh, unmixed colours (yellow, blue, pink, acid green) or concrete greys",
"visible grids, oversized type, intentionally awkward alignment"
],
"palette": {"guidance": "Concrete greys plus one or two shouting accents (safety yellow, electric blue, hot pink); black borders; pure white. Max 5-6 colours; no gradients.", "swatches": ["#f2f2f2", "#111111", "#ffe600", "#3d5afe", "#ff4fa3", "#8a8a8a"]},
"geometry": "Rectangles, thick rules, stacked blocks, oversized offsets; sharp 90-degree corners only; grid lines exposed; occasional rotated block by a few degrees.",
"stroke": "Very thick, uniform, black, square caps and miter joins.",
"texture": "None or raw hatch/crosshatch blocks; concrete speckle only as a flat pattern.",
"composition": "Blocky, crowded but structured; huge headline; boxes overlapping with offset hard shadows; no elegant margins.",
"typography": "Monospace or heavy grotesque, all caps, oversized, tight leading; underlines and strikethroughs as graphic elements.",
"lighting_depth": "Depth only via hard offset shadows (rect duplicated at +8,+8 in black).",
"svg_techniques": [
"each block = shadow rect (offset) + fill rect + border stroke; wrap as a reusable <symbol> or repeating pattern of the same construct",
"use stroke-linejoin miter, stroke-linecap square",
"text as <text> with monospace generic fallback; paint-order stroke for outlined heads"
],
"avoid": ["gradients", "blurred shadows", "rounded corners", "thin lines", "polished elegance", "pastel palettes"],
"compatible_with": ["bauhaus", "minimal-modern", "monochrome", "screen-print", "risograph", "comic-book", "geometric", "graffiti-street-art", "flat-design"],
"conflicts_with": ["luxury-premium", "art-nouveau", "frutiger-aero", "glassmorphism", "childrens-illustration", "organic-botanical", "clay-soft-3d"],
"best_for": ["poster", "illustration", "logo", "infographic"],
"detail_level": "low",
"checks": {"max_gradients": 0, "max_filters": 0, "max_colors": 8, "require_stroke": true},
"reference": "decorative-eras.md#brutalist"
},
{
"id": "vintage-poster",
"name": "Vintage Poster",
"family": "decorative-eras",
"aliases": ["vintage", "retro poster", "vintage travel poster", "travel poster", "wpa poster", "1950s poster", "mid century poster", "mid-century poster", "old poster", "classic poster", "national park poster", "retro travel"],
"keywords": ["travel poster", "wpa", "sunburst sky", "retro print", "stamp", "1930s", "1950s", "mid century"],
"description": "Screen-printed poster look of the 1930s-60s: flat layered scenery with limited inks, dramatic simplified light, worn paper and bold hand-lettered title blocks.",
"visual_characteristics": [
"layered, simplified scenery (sun/sky bands, mountains, water) in 4-6 inks",
"stylised sun/rays and strong silhouettes",
"aged paper tone with slight fading, edge wear and misregistration",
"title block in bold display lettering plus small tagline / location line"
],
"palette": {"guidance": "Warm cream paper, faded red/orange, mustard, teal/blue-green, deep navy, with muted saturation. 4-6 inks; overprint areas produce a darker mixed tone.", "swatches": ["#f2e6c9", "#e2572d", "#f0a83a", "#2f7d7a", "#1f3a5a", "#7a3b2e"]},
"geometry": "Flat shapes with confident, simplified silhouettes; gentle atmospheric bands; sun as concentric circle + ray wedges; vertical poster format with a title band.",
"stroke": "Minimal: shapes are print blocks; small keyline accents in the darkest ink; consistent 1.5-2 unit lines if used.",
"texture": "Paper grain + speckled ink dropout: a single feTurbulence-based overlay clipped to the poster with low opacity; subtle edge darkening via radial vignette.",
"composition": "Vertical layout: title at top or bottom band (20-25%), hero scene occupying the middle, small caption line; strong horizon line at a third; a border frame with margin.",
"typography": "Bold slab/condensed display with slight distress, hand-lettered feel; outline or drop shadow in second ink; small tracking-spaced serif subtitle.",
"lighting_depth": "Flat sun with radial glow bands; distant layers lighter and bluer; overprint tones create shadows.",
"svg_techniques": [
"layer inks as <g id=\"ink-1\"> etc. with fill-opacity .85-1 and mix via stacked overlaps; keep to 5-6 fill colours",
"sun rays as rotated wedge <use>s clipped to the sky region",
"paper texture: rect with feTurbulence + feColorMatrix (alpha low), plus a subtle radial gradient vignette",
"title text as <text> (generic serif/sans fallback) or outlined paths"
],
"avoid": ["gradients as primary shading", "clean modern sans", "photoreal detail", "neon colours", "too many inks"],
"compatible_with": ["screen-print", "risograph", "art-deco", "minimal-modern", "flat-design", "editorial-illustration", "cinematic-poster", "retro-futurism", "comic-book", "monochrome", "ukiyo-e"],
"conflicts_with": ["glassmorphism", "cyberpunk-neon", "pixel-art", "clay-soft-3d", "y2k", "frutiger-aero"],
"best_for": ["poster", "scene", "illustration"],
"detail_level": "medium",
"checks": {"require_text": true, "min_elements": 25, "max_filters": 2, "min_groups": 3, "light_base": true},
"reference": "decorative-eras.md#vintage-poster"
},
{
"id": "medieval-manuscript",
"name": "Medieval Manuscript",
"family": "decorative-eras",
"aliases": ["illuminated manuscript", "medieval", "medieval illustration", "illuminated", "manuscript", "book of hours", "medieval miniature", "bestiary", "gothic manuscript", "monastic illumination", "illuminated letter", "drolleries"],
"keywords": ["gold leaf", "parchment", "drollery", "illuminated capital", "vellum", "bestiary", "marginalia"],
"description": "Illuminated manuscript pages: parchment ground, flat tempera colours with gold-leaf highlights, dense decorative borders and drolleries, historiated initials and flattened medieval perspective.",
"visual_characteristics": [
"parchment base with darker aged edges and prick-marked ruling",
"flat opaque pigment (lapis blue, vermilion, verdigris green, ochre) with black-brown contours",
"gold-leaf backgrounds and highlights (gradient gold with tiny burnish lines)",
"dense vine/foliage borders with creatures (drolleries) and an ornate historiated initial"
],
"palette": {"guidance": "Parchment #f0e0b8, lapis #1f3f8f, vermilion #c8342b, verdigris #3b7a5c, ochre #d0a03a, ink #2a1c14, gold gradient #b98a2e -> #f5dc8a. Pigments are opaque and slightly chalky.", "swatches": ["#f0e0b8", "#1f3f8f", "#c8342b", "#3b7a5c", "#d0a03a", "#2a1c14", "#b98a2e"]},
"geometry": "Flattened perspective; stiff gestural figures; outlined shapes with hard edges; vine scrolls with curling leaf terminals; illuminated capital in a square frame; rounded gothic arches.",
"stroke": "Brown-black contour 1.5-2.5 units with round caps; hairline gold-leaf borders (1 unit); fine hatch for shading.",
"texture": "Parchment mottling (feTurbulence, low), stains at the corners, tiny gold dots, hairline ruled lines.",
"composition": "Single-page layout: illuminated initial at the top-left, text-block area as ruled lines, vine border wrapping two sides, small bas-de-page scene or creature.",
"typography": "Blackletter/textura suggested via a blackletter-like generic (fantasy/serif fallback) or as ruled pseudo-text lines; capitals as historiated initials drawn as shapes.",
"lighting_depth": "None; flat. Gold backgrounds provide the glow; depth by overlap and scale hierarchy (important = large).",
"svg_techniques": [
"gold via linearGradient with 4-5 stops plus a very fine burnish <pattern> at low opacity",
"vine borders as stroked Bezier paths with leaf <use>s along them",
"parchment via base fill + radialGradient edge darkening + subtle turbulence overlay",
"text-block placeholder as thin lines (not fake text)"
],
"avoid": ["modern perspective and realism", "gradients other than gold/parchment", "clean digital sans", "bright saturated neon", "empty borders"],
"compatible_with": ["gothic", "fantasy", "art-nouveau", "organic-botanical", "luxury-premium", "ukiyo-e", "hand-drawn-doodle", "horror"],
"conflicts_with": ["cyberpunk-neon", "y2k", "glassmorphism", "minimal-modern", "pixel-art", "clay-soft-3d"],
"best_for": ["illustration", "poster", "pattern", "scene"],
"detail_level": "high",
"checks": {"min_gradients": 1, "min_elements": 35, "require_elements_any": ["use", "pattern"], "light_base": true},
"reference": "decorative-eras.md#medieval-manuscript"
},
{
"id": "gothic",
"name": "Gothic",
"family": "decorative-eras",
"aliases": ["gothic style", "gothic art", "gothic architecture", "dark gothic", "victorian gothic", "gothic revival", "goth", "gothic horror aesthetic", "cathedral style", "tracery"],
"keywords": ["pointed arch", "tracery", "cathedral", "rose window", "gargoyle", "stained glass", "raven", "candlelight", "cemetery"],
"description": "Vertical, pointed and ornate: lancet arches, tracery, stained-glass geometry and moody candlelit darkness with high-contrast silhouettes.",
"visual_characteristics": [
"pointed arches, ribbed vaults, rose windows and tracery",
"dark palette with jewel-tone stained-glass accents and candle-gold highlights",
"dramatic verticality, silhouettes against moonlit or stained-glass light",
"decorative filigree, ravens, thorny vines and iron-work motifs"
],
"palette": {"guidance": "Near-black plum/charcoal base, stained-glass jewel accents (ruby, sapphire, emerald, amber), candle gold and bone-white highlights. 5-8 colours; overall value dark.", "swatches": ["#0c0a12", "#231a33", "#6b1e2e", "#1e3a6b", "#2f6b4a", "#d9a441", "#e8e2d0"]},
"geometry": "Pointed (ogee/lancet) arches from two intersecting circle arcs; radial rose windows built by rotating petal shapes; tall narrow proportions; filigree scrolls; strong symmetry.",
"stroke": "Leaded-glass lines (3-4 units dark) around jewel-tone panes; hairline gold filigree; miter or round joins consistently.",
"texture": "Stone mottling, candle flicker glow gradients, fog layers; optional subtle grain.",
"composition": "Centred symmetric vertical frame with a focal window or silhouette; light source behind the subject; darkness at edges (vignette).",
"typography": "Blackletter-style capitals (or generic fantasy/serif fallback) with letter-spaced small text.",
"lighting_depth": "Chiaroscuro: single warm or coloured light source, strong rim, deep vignette; stained-glass light shafts as translucent polygons.",
"svg_techniques": [
"pointed arch = path with two circular arcs (A commands) meeting at the apex",
"rose window: define one petal path, <use> with rotate steps; leading = strokes over gradient fills",
"light shafts: polygons with linearGradient fading opacity",
"vignette radialGradient overlay; fog bands as low-opacity gradient rects"
],
"avoid": ["bright cheerful palettes", "rounded cute shapes", "flat pastel fills", "modern sans typography", "horizontal calm compositions"],
"compatible_with": ["horror", "fantasy", "medieval-manuscript", "art-nouveau", "monochrome", "luxury-premium", "cinematic-poster", "manga", "comic-book"],
"conflicts_with": ["frutiger-aero", "childrens-illustration", "y2k", "flat-design", "clay-soft-3d", "minimal-modern"],
"best_for": ["poster", "scene", "illustration", "logo"],
"detail_level": "high",
"checks": {"min_elements": 25, "min_gradients": 1, "max_filters": 2},
"reference": "decorative-eras.md#gothic"
}
]
styles/registry/digital-glossy.json
[
{
"id": "cyberpunk-neon",
"name": "Cyberpunk Neon",
"family": "digital-glossy",
"aliases": ["cyberpunk", "cyber punk", "neon noir", "neon city", "neon futuristic city", "synthwave neon", "blade runner style", "night city", "dystopian neon", "neon"],
"keywords": ["neon", "futuristic city", "rain-soaked", "holographic", "megacity", "hacker", "dystopian", "glowing signs"],
"description": "High-tech, low-life night scenes: near-black bases, saturated magenta/cyan/violet light, controlled glow on hero edges, atmospheric haze and dense signage-like detail.",
"visual_characteristics": [
"dark base (near-black indigo) with strong colour contrast between a magenta/pink key and a cyan/teal fill light",
"neon strokes with a glow: bright core line + wider translucent halo",
"atmosphere: volumetric haze, rain streaks, light shafts, reflections on wet ground",
"dense layered city/tech detail: signs, wires, HUD marks, katakana-like glyphs (as shapes)"
],
"palette": {"guidance": "Base #06050f-#120a26. Key neon magenta (#ff2a6d), electric cyan (#05d9e8), violet (#7b2ff7), acid accent (#f9f871 or #ffb300) used sparingly. Colour temperature contrast between lights. Keep 70% dark / 25% mid / 5% pure neon.", "swatches": ["#07040f", "#150a2b", "#7b2ff7", "#ff2a6d", "#05d9e8", "#f9f871", "#1a1f4d"]},
"geometry": "Hard-edged, angular architecture with vertical emphasis; skewed and perspective-flavoured slabs; thin precise strokes; occasional circles for signs and moons. Silhouettes first, neon rims second.",
"stroke": "Hero strokes 2-6 units with round caps; each has a paired halo (same path, wider, lower opacity, blurred or stepped). Secondary detail in hairline 0.5-1.",
"texture": "Scanlines (repeated thin lines at 5-10% opacity), light grain, rain streaks, subtle chromatic offset by duplicating a shape in cyan/magenta with small x-shift.",
"composition": "Strong vanishing point or vertical canyon of buildings; subject silhouetted against a glowing backdrop (sun/sign/moon); foreground dark, mid lit by neon, background haze-lit. Diagonals and leading lines.",
"typography": "Condensed sans or monospace, wide tracking, neon-outlined; vertical signs; faux-katakana built from strokes. Keep <=2 short text elements.",
"lighting_depth": "Rim lighting and colour-cast: magenta from one side, cyan from the other. Glow = additive feel via lighter stroke core over a saturated halo. Haze gradients increase toward the horizon.",
"svg_techniques": [
"define 2 reusable filters: #glow-soft (feGaussianBlur stdDeviation 3-6 merged with SourceGraphic) and #glow-hard (stdDev 1-2); set filter region x/y/width/height to -30%/160% so glows are not clipped",
"apply filters to GROUPS of neon strokes, not each path",
"sky/haze via linearGradient stops (deep indigo -> magenta at horizon) and radialGradient for moon/sun glow",
"wet-ground reflection: <use> the skyline flipped (scale(1,-1)) with a gradient mask fading to transparent",
"scanlines/rain as a single <pattern> overlay at low opacity"
],
"avoid": ["glow on every element", "white or light base", "pastel colours", "muddy grey midtones", "a single flat neon colour without a complementary fill light", "filters on hundreds of small shapes"],
"compatible_with": ["minimal-modern", "sci-fi", "retro-futurism", "pixel-art", "retro-16bit", "y2k", "comic-book", "manga", "blueprint", "monochrome", "isometric", "low-poly", "horror", "geometric"],
"conflicts_with": ["organic-botanical", "childrens-illustration", "medieval-manuscript", "sumi-e", "vintage-poster", "scientific-illustration"],
"best_for": ["scene", "poster", "illustration", "character", "logo"],
"detail_level": "high",
"checks": {"dark_base": true, "min_filters": 1, "min_gradients": 1, "min_elements": 25, "min_groups": 3},
"reference": "digital-glossy.md#cyberpunk-neon"
},
{
"id": "y2k",
"name": "Y2K",
"family": "digital-glossy",
"aliases": ["early 2000s glossy internet aesthetic", "y2k aesthetic", "early 2000s", "2000s aesthetic", "millennium", "millennial tech", "cyber y2k", "chrome y2k", "bubble y2k", "early internet", "late 90s tech"],
"keywords": ["chrome", "glossy", "bubbly", "iridescent", "web 1.0", "web 2.0", "cd-rom", "tribal tattoo", "star sparkle", "holographic foil"],
"description": "Optimistic techno-future as imagined around 1999-2004: liquid chrome, translucent plastic, bubble shapes, metallic gradients, sparkles, tribal/atomic motifs and saturated candy colours.",
"visual_characteristics": [
"liquid chrome and glossy plastic forms with strong specular highlights",
"rounded pill/bubble shapes, lens flares and 4-point sparkle stars",
"iridescent / holographic gradient washes (blue-violet-pink-cyan)",
"cyber-tribal swooshes, atomic orbits, pixel/cursor/UI fragments as motifs"
],
"palette": {"guidance": "Silver/chrome greys with candy accents: aqua, electric blue, hot pink, lime, violet; iridescent gradients across pink-cyan-lavender. White highlights are crisp.", "swatches": ["#d7dde8", "#7f8fa6", "#30a9ff", "#ff5fd2", "#b8ff3d", "#8a5cff", "#ffffff"]},
"geometry": "Rounded, inflated forms (superellipses, pills, blobs), sharp-tipped stars and swooshes, concentric ring motifs. Consistent glossy 'top light' highlight shapes.",
"stroke": "Mostly no strokes; thin white inner strokes or 1-2 unit dark-blue outlines on chrome for edge definition; swooshes as tapered filled paths.",
"texture": "Gloss highlights, faint dot-matrix or grid backgrounds, sparkle overlays; optional subtle halftone.",
"composition": "Centred hero object floating on a bright gradient field; orbiting rings and sparkles; symmetrical or dynamic diagonal; UI-window or CD-disc motifs as framing.",
"typography": "Rounded or extended sans, chrome gradient fill with dark outline and highlight, italics; Bank-Gothic-like caps.",
"lighting_depth": "Glossy: bright specular streak along the top, dark reflection band near the bottom (chrome horizon line), soft rim of light. Depth via layered translucent shapes.",
"svg_techniques": [
"chrome via linearGradient with 6-8 stops that alternate light/dark (a fake horizon reflection)",
"gloss highlight = a clipped ellipse/path with a white->transparent linearGradient",
"iridescence via a multi-stop conic-like wash: layer 2-3 low-opacity linear gradients at different angles",
"sparkles = 4-point star <path> defined once, <use> at various sizes"
],
"avoid": ["flat matte fills", "muted earth tones", "hand-drawn wobble", "heavy black outlines", "dark grim atmospheres"],
"compatible_with": ["frutiger-aero", "cyberpunk-neon", "retro-futurism", "sticker", "sci-fi", "glassmorphism", "geometric"],
"conflicts_with": ["sumi-e", "medieval-manuscript", "gothic", "scientific-illustration", "brutalist"],
"best_for": ["logo", "poster", "icon", "illustration", "sticker"],
"detail_level": "medium",
"checks": {"min_gradients": 3, "min_elements": 12},
"reference": "digital-glossy.md#y2k"
},
{
"id": "frutiger-aero",
"name": "Frutiger Aero",
"family": "digital-glossy",
"aliases": ["aero", "windows vista aesthetic", "vista aero", "aqua", "web 2.0 glossy", "skeuomorphic nature tech", "glossy nature", "2000s nature aesthetic", "aero glass", "early 2000s glossy internet aesthetic", "glossy internet aesthetic"],
"keywords": ["bubbles", "blue sky", "green grass", "glossy", "water droplets", "lens flare", "aqua buttons", "clean tech nature"],
"description": "Mid-2000s techno-optimism: glossy translucent UI surfaces set against bright skies, water, bubbles, fresh grass and dolphins-and-orbs imagery; clean, luminous, friendly.",
"visual_characteristics": [
"sky-blue to white gradient fields with soft clouds and lens flares",
"glass and aqua buttons, bubbles, droplets with bright caustic highlights",
"nature + tech pairing: leaves, water, sunbeams next to glossy UI shapes",
"high key, airy, luminous - everything looks freshly rinsed"
],
"palette": {"guidance": "Aqua/sky blues, fresh leaf greens, white highlights; small warm accents (sun yellow, orange). High-key, saturated but clean. Transparent whites for glass.", "swatches": ["#d9f1ff", "#5cc8ff", "#1f8fe0", "#7ed957", "#2c9b3a", "#ffffff", "#ffd54a"]},
"geometry": "Soft rounded rectangles, perfect spheres, teardrops; smooth flowing swooshes; subtle perspective floor plane. Everything is inflated and smooth.",
"stroke": "Nearly none; edge defined by a thin semi-transparent white inner border and a soft darker rim gradient.",
"texture": "Smooth. Optional subtle noise-free bokeh circles and water caustic streaks.",
"composition": "Wide open sky, horizon low; hero glossy object/UI panel centred-left; bubbles and light rays fanning from a corner; foreground grass/water strip for grounding.",
"typography": "Humanist sans (Frutiger/Segoe-like), title with soft white shadow; light weights; sentence case.",
"lighting_depth": "Bright ambient with a strong sun flare; glass objects with top specular arc and bottom bounce light; soft blurred shadows on the ground plane.",
"svg_techniques": [
"bubble = radialGradient (transparent centre -> pale-blue edge) + crescent highlight path + tiny secondary highlight",
"glossy button = rounded rect with vertical linearGradient + clipped upper-half highlight with white 60%->10% gradient",
"lens flare = layered low-opacity circles and a thin cross-star; single radialGradient for the sun glow",
"one soft blur filter only for ground shadows"
],
"avoid": ["dark base", "flat colours only", "grunge textures", "harsh black outlines", "muted palettes"],
"compatible_with": ["y2k", "glassmorphism", "clay-soft-3d", "childrens-illustration", "organic-botanical", "sci-fi", "retro-futurism"],
"conflicts_with": ["horror", "gothic", "brutalist", "blueprint", "pixel-art", "sumi-e"],
"best_for": ["scene", "icon", "poster", "illustration"],
"detail_level": "medium",
"checks": {"min_gradients": 4, "light_base": true, "min_elements": 15},
"reference": "digital-glossy.md#frutiger-aero"
},
{
"id": "glassmorphism",
"name": "Glassmorphism",
"family": "digital-glossy",
"aliases": ["glass morphism", "glass ui", "frosted glass", "frosted", "glassy", "translucent ui", "acrylic ui", "blur glass", "glass card"],
"keywords": ["frosted panel", "translucent", "backdrop blur", "glass card", "soft gradient background"],
"description": "Frosted translucent panels floating above vivid blurred colour fields; thin bright borders, soft shadows and subtle inner light convey glass depth.",
"visual_characteristics": [
"vibrant blurred gradient blobs as the backdrop",
"translucent white/tinted panels with a 1px light border and soft inner glow",
"layered depth: panel over panel, each blurring what is behind",
"high polish, soft shadows, generous corner radii"
],
"palette": {"guidance": "Rich, saturated background blobs (violet, blue, pink, teal) on a deep or bright field; glass in white at 10-25% opacity, borders white at 30-50%. Text/icon in near-white or deep navy for contrast.", "swatches": ["#141433", "#6a4cff", "#ff5fa2", "#22d3ee", "#ffffff", "#0b1030"]},
"geometry": "Rounded rectangles (radius 5-8% of the panel), circles, soft blobs. Panels overlap at slight angles or offsets; consistent radius system.",
"stroke": "1-1.5 unit border with a linearGradient stroke (white 60% top-left -> transparent bottom-right) to fake edge light.",
"texture": "Optional very faint noise; otherwise clean.",
"composition": "2-3 glass panels layered over 2-4 colour blobs; hero panel holds the icon or content; off-centre arrangement with clear hierarchy; large soft shadow under the topmost panel.",
"typography": "Clean geometric sans, medium/semibold, white or navy; small labels at 60-70% opacity.",
"lighting_depth": "Fake backdrop blur: draw a blurred copy of the background shapes clipped to each panel; add inner top-left highlight, soft drop shadow beneath.",
"svg_techniques": [
"SVG has no backdrop-filter: for each panel <use> the background group with a feGaussianBlur filter (stdDeviation ~12) and clip-path = the panel shape, then overlay a translucent white fill",
"gradient stroke (userSpaceOnUse) for the light border",
"inner glow by a stroke-only duplicate clipped inside the panel with blur",
"reuse one blur filter id for all panels; define the background as <g id=\"bg\"> in defs"
],
"avoid": ["opaque flat panels", "black outlines", "heavy textures", "blur on the whole canvas (kills legibility)", "more than 3 panel layers"],
"compatible_with": ["minimal-modern", "frutiger-aero", "y2k", "cyberpunk-neon", "clay-soft-3d", "sci-fi", "isometric"],
"conflicts_with": ["pixel-art", "retro-8bit", "retro-16bit", "flat-design", "brutalist", "sumi-e", "medieval-manuscript"],
"best_for": ["icon", "illustration", "poster", "logo"],
"detail_level": "medium",
"checks": {"min_filters": 1, "min_gradients": 2, "require_elements_any": ["clipPath", "mask"], "min_elements": 12},
"reference": "digital-glossy.md#glassmorphism"
},
{
"id": "clay-soft-3d",
"name": "Clay / Soft 3D",
"family": "digital-glossy",
"aliases": ["clay", "claymorphism", "clay style", "soft 3d", "soft-3d", "3d clay", "plasticine", "inflated 3d", "soft render", "3d icon soft", "toy 3d"],
"keywords": ["pillowy", "puffy", "inflated", "matte plastic", "soft shadows", "rounded 3d"],
"description": "Chunky, rounded, pillowy forms that look like sculpted clay or matte vinyl: soft gradients, inner shadows and highlights, gentle ambient occlusion and pastel palettes.",
"visual_characteristics": [
"everything is inflated; no sharp corners, thick rounded volumes",
"matte shading with soft top-left highlight and inner bottom-right shadow",
"pastel or mid-saturation palette; colour bleed in shadows",
"soft contact shadows anchoring objects on a plain or gently graded ground"
],
"palette": {"guidance": "Soft mid-saturation hues (peach, lilac, mint, butter, sky) on a warm neutral ground; shadows are a darker, hue-shifted version of the object colour, never grey or black.", "swatches": ["#f6e7d8", "#ffb4a2", "#b8a1ff", "#9be7c4", "#ffe08a", "#7aa2ff", "#e5989b"]},
"geometry": "Spheres, capsules, rounded boxes, torus shapes with generous radii. Simplify anatomy into blobby primitives; slight squash-and-stretch for charm. Fillets everywhere.",
"stroke": "None. Forms are defined by shading only.",
"texture": "Very subtle fine grain optional at <=3%; otherwise perfectly smooth matte.",
"composition": "Single hero object/character centred with 2-4 smaller floating props; consistent light direction; big soft shadow ellipse below; clean background.",
"typography": "Chunky rounded sans built to feel inflated (heavy weight, round terminals); minimal use.",
"lighting_depth": "Soft key light from top-left: broad highlight patch near the top-left; core shadow along bottom-right with bounce light on the far edge; contact shadow blurred.",
"svg_techniques": [
"each volume = base fill + radialGradient (offset highlight centre at 30%/25%) + an inner-shadow crescent path clipped to the shape",
"rim/bounce light = thin lighter crescent along the shadow-side edge with low opacity",
"one shared blur filter for contact shadows; do not blur the forms themselves",
"build forms from <ellipse>/<rect rx> and clip highlights to them"
],
"avoid": ["outlines", "hard black shadows", "sharp corners", "grey shadows", "photoreal reflections", "too many tiny details"],
"compatible_with": ["frutiger-aero", "glassmorphism", "childrens-illustration", "sticker", "isometric", "minimal-modern", "sci-fi"],
"conflicts_with": ["pixel-art", "blueprint", "comic-book", "manga", "brutalist", "gothic", "sumi-e"],
"best_for": ["icon", "character", "illustration", "logo"],
"detail_level": "medium",
"checks": {"min_gradients": 4, "min_elements": 10},
"reference": "digital-glossy.md#clay-soft-3d"
},
{
"id": "isometric",
"name": "Isometric",
"family": "digital-glossy",
"aliases": ["isometric art", "isometric illustration", "iso", "isometric 3d", "isometric view", "isometric scene", "isometric city", "isometric room", "2.5d", "axonometric"],
"keywords": ["30 degree", "cube", "block", "diorama", "tiny world", "isometric grid"],
"description": "Parallel-projection pseudo-3D on a 30-degree grid: no vanishing point, three visible faces per block with consistent light values, tidy tiny-world dioramas.",
"visual_characteristics": [
"true isometric axes (30 degrees from horizontal); vertical edges stay vertical",
"three face tones per object: top lightest, left mid, right darkest",
"objects sit on a diamond ground plane with crisp edges",
"small, dense, tidy scenes with repeated modular units"
],
"palette": {"guidance": "Each material has a 3-tone face set (top / left / right) derived consistently; a soft base colour ground; 6-10 hues total; accents limited to props.", "swatches": ["#f4efe6", "#c9d6ea", "#8fa8d0", "#5c7ab0", "#ffb35c", "#e26a4a", "#7ec59b"]},
"geometry": "Author in 3D coordinates and project: x' = (x - y) * cos30, y' = (x + y) * sin30 - z. Build cubes from three parallelogram <polygon>s. Circles become ellipses with ratio ~1.73:1 (2:1 in pixel-iso).",
"stroke": "Usually none; optional 1-unit darker edge lines on top edges only (consistent). Round joins.",
"texture": "Flat faces; optional subtle pattern on floors (tiles) via a sheared <pattern>.",
"composition": "Diamond-shaped vignette of the world centred in the canvas with air around; depth by draw order (back-to-front, low-to-high); a single large hero structure with smaller props.",
"typography": "Rare; if used, text is projected with a skew transform to lie on a face.",
"lighting_depth": "Fixed light from top-left: face tones stay constant; cast shadows are parallelograms projected at the same angle on the ground, opacity 0.15-0.25.",
"svg_techniques": [
"write a tiny JS/Python generator or use helper symbols: define <symbol id=\"cube\"> faces once and <use> with translate() for repeated blocks",
"use a matrix transform for flat art: matrix(0.866 0.5 -0.866 0.5 0 0) to lay flat shapes on the ground plane",
"sort draw order by (x+y+z) back-to-front",
"keep coordinates on a unit grid so edges meet exactly (avoid gaps by overlapping 0.2 units and using shape-rendering geometricPrecision)"
],
"avoid": ["perspective convergence", "inconsistent face lighting", "angles other than 30/90", "gaps between adjacent faces", "mixed projection styles"],
"compatible_with": ["flat-design", "low-poly", "pixel-art", "clay-soft-3d", "cyberpunk-neon", "sci-fi", "fantasy", "blueprint", "geometric", "childrens-illustration"],
"conflicts_with": ["hand-drawn-doodle", "sumi-e", "ukiyo-e", "art-nouveau", "organic-botanical"],
"best_for": ["scene", "illustration", "icon", "infographic"],
"detail_level": "medium",
"checks": {"min_elements": 20, "max_filters": 1, "min_groups": 3},
"reference": "digital-glossy.md#isometric"
},
{
"id": "low-poly",
"name": "Low Poly",
"family": "digital-glossy",
"aliases": ["low-poly", "lowpoly", "polygonal", "faceted", "triangulated", "polygon art", "mesh art", "delaunay art", "crystal facets"],
"keywords": ["triangles mesh", "faceted look", "polygon mesh", "gem cut"],
"description": "Subjects and landscapes tessellated into flat-shaded triangles; the facets imply volume and light through value shifts while the outer silhouette stays clean and iconic.",
"visual_characteristics": [
"triangulated mesh with denser vertices at complex/contour areas and larger triangles in flat areas",
"flat colour per facet, sampled from a gradient of the form (light -> shadow)",
"clean silhouette; facet edges invisible (no strokes) or hairline in same tone",
"subtle sparkle from a few brighter facets"
],
"palette": {"guidance": "A 2-3 hue family per object with 6-10 value steps assigned per facet by facing direction to the light; background as a gentle gradient of the same family. Avoid random noise - value must follow form.", "swatches": ["#12233f", "#1f4068", "#2f6f9f", "#4fa3c7", "#9fd8e8", "#e8f6f8", "#ff8a5c"]},
"geometry": "Triangles only (occasionally quads). Vertex placement guided by form: rings around volumes, ridge lines on landscapes. Triangle sizes vary 3-5x. Avoid slivers.",
"stroke": "None. To hide anti-aliasing seams between facets add stroke of the same colour at 0.4-0.6 unit width (or shape-rendering=\"crispEdges\" for large facets).",
"texture": "None; the mesh is the texture.",
"composition": "Strong central silhouette with mesh density highest at focal features (eyes, peaks); gradient background mesh that blends into the subject.",
"typography": "Geometric sans, possibly also triangulated for a title; minimal.",
"lighting_depth": "Single light direction: facet value from the dot product of facet normal and light; ridge highlights and core shadows on opposite sides.",
"svg_techniques": [
"generate the mesh programmatically (Python/JS) from hand-placed anchor points + jittered Poisson-like fill, then colour each triangle from a value function; emit <polygon points>",
"use the same-colour hairline stroke (stroke-width .5, stroke-linejoin round) to close seams",
"group by object (id=\"body\", \"head\", \"bg\") and sort by depth",
"quantise to ~12 distinct fills and define them as classes to keep the file small"
],
"avoid": ["random, form-ignoring colour noise", "sliver triangles", "visible dark wireframe (unless intentional wireframe variant)", "curves", "uniform triangle size"],
"compatible_with": ["geometric", "flat-design", "isometric", "cyberpunk-neon", "sci-fi", "minimal-modern", "fantasy", "retro-futurism"],
"conflicts_with": ["hand-drawn-doodle", "sumi-e", "art-nouveau", "organic-botanical", "scientific-illustration"],
"best_for": ["illustration", "scene", "character", "logo"],
"detail_level": "high",
"checks": {"require_elements_any": ["polygon"], "min_elements": 30, "max_filters": 0},
"reference": "digital-glossy.md#low-poly"
}
]
styles/registry/east-asian.json
[
{
"id": "sumi-e",
"name": "Japanese Ink / Sumi-e",
"family": "east-asian",
"aliases": ["sumi-e", "sumi e", "sumie", "japanese ink", "ink wash", "ink painting", "chinese ink", "brush painting", "zen ink", "enso", "shuimo", "suibokuga", "east asian ink", "japanese brush", "ink brush"],
"keywords": ["japanese", "japan", "ink wash", "brush stroke", "enso", "zen", "bamboo ink", "mountain mist", "calligraphic", "single stroke"],
"description": "Monochrome ink painting: a few decisive brush strokes with tonal wash, generous empty paper (ma), and suggestion over description; energy (qi) lives in the stroke itself.",
"visual_characteristics": [
"black ink in 4-5 tones (dry, grey, wash, wet-black) on warm rice paper",
"each stroke is a single gesture: thick-to-thin, dry-brush breaks, ink pooling",
"vast negative space; the subject occupies <= 40% and fades into mist",
"one red seal (hanko) and optionally a short calligraphic line as the only colour accent"
],
"palette": {"guidance": "Paper #f3ecdc-ish, ink ramp from #111 to #d9d4c7 (4-5 tones), single vermilion seal #c1272d (<=1% of area). No other colours.", "swatches": ["#f4eddc", "#1a1a1a", "#4a4a48", "#8c8a84", "#c9c5b8", "#c1272d"]},
"geometry": "Organic brush-stroke shapes drawn as filled variable-width paths (not uniform strokes); bamboo nodes as short horizontal breaks; mountains as layered soft masses; asymmetric triangle balance.",
"stroke": "Brush = closed path with tapered ends and irregular width; dry-brush = the same shape masked with streaky texture; hair-thin lines for grass/needles using 0.6-1.2 unit strokes with round caps.",
"texture": "Rice-paper fibres (very faint feTurbulence), dry-brush bristle streaks (mask of thin horizontal lines with random gaps), wet-edge halo on washes (radial or blurred outer copy).",
"composition": "Asymmetric, high or low placement; diagonal from lower-left to upper-right; one main mass + 1-2 accents; empty area is 60%+; seal in the lower corner balancing the mass.",
"typography": "Brush calligraphy as shapes (or a single vertical line of Chinese/Japanese characters if the user supplies text); seal as a red square with knocked-out mark.",
"lighting_depth": "No cast light; depth by tone (dark near, pale far), overlap and mist bands (semi-transparent paper-coloured gradients).",
"svg_techniques": [
"washes: shapes with linearGradient/radialGradient in ink tones and one feGaussianBlur (stdDev 4-8) on a copy for the wet halo",
"dry brush: apply a <mask> containing a <pattern> of irregular thin lines to a stroke shape",
"mist = paper-coloured gradient bands with fill-opacity .5-.8 between layers",
"seal: <rect rx=2> vermilion + a knocked-out mark via evenodd or mask; slightly rotated"
],
"avoid": ["colour palettes beyond ink + one seal", "uniform vector strokes", "crowded compositions", "symmetrical layouts", "drop shadows", "outlines around washes"],
"compatible_with": ["minimal-modern", "monochrome", "ukiyo-e", "manga", "organic-botanical", "hand-drawn-doodle", "fantasy"],
"conflicts_with": ["cyberpunk-neon", "y2k", "frutiger-aero", "glassmorphism", "pixel-art", "clay-soft-3d", "isometric", "low-poly"],
"best_for": ["illustration", "scene", "poster", "logo"],
"detail_level": "low",
"checks": {"max_colors": 9, "require_elements_any": ["filter", "linearGradient", "radialGradient", "mask"], "max_elements": 140, "light_base": true},
"reference": "east-asian.md#sumi-e"
},
{
"id": "ukiyo-e",
"name": "Ukiyo-e",
"family": "east-asian",
"aliases": ["ukiyoe", "ukiyo e", "japanese woodblock", "woodblock print", "japanese woodcut", "edo print", "hokusai style", "hiroshige style", "nishiki-e", "japanese print", "traditional japanese print", "great wave style"],
"keywords": ["japanese", "japan", "woodblock", "edo", "seigaiha", "kimono pattern", "wave pattern", "bokashi", "japanese traditional", "mount fuji"],
"description": "Japanese woodblock print language: flat colour regions separated by a confident key-block outline, limited pigment palette, bokashi gradations in skies, decorative patterning and traditional composition with cropped foregrounds.",
"visual_characteristics": [
"flat colour blocks, each printed as a separate 'block' with slight registration offsets",
"organic, calligraphic key-block outlines of varying weight (thicker on outer contours)",
"limited pigment palette: indigo/Prussian blue, vermilion, ochre, sap green, warm cream paper, sumi black",
"decorative patterning for water (seigaiha, wave curls), clothing (kimono motifs), clouds (stylised bands), and grain-like wood texture in large areas",
"bokashi (graded wash) only at horizons/skies; everything else flat"
],
"palette": {"guidance": "Paper #efe3c8. Prussian blue #1f3c6e (dominant), pale indigo #6d8fb3, vermilion #c8372d, ochre #d9a441, muted green #6b8f5a, sumi #1c1a19. Colours are slightly desaturated and warm-toned; 8-12 colours total.", "swatches": ["#efe3c8", "#1f3c6e", "#6d8fb3", "#c8372d", "#d9a441", "#6b8f5a", "#1c1a19"]},
"geometry": "Flattened perspective (no vanishing point; stacked planes), stylised waves as curling claw-like crests, clouds as banded ribbons, figures simplified with elegant curved contours; strong diagonals and cropping by frame.",
"stroke": "Key-block outline in sumi: variable weight simulated by two stroke layers (3 units for exterior contour, 1-1.4 for interior detail); round caps, round joins; slight irregularity.",
"texture": "Wood-grain: faint horizontal streak pattern (feTurbulence stretched baseFrequency 0.002 0.2, opacity ~6%) over colour blocks; paper fibre; slight mis-registration offset (0.6-1.2 units) between colour and key-line.",
"composition": "Asymmetric traditional layout: large foreground element cropped at one edge (wave or branch), subject in the middle distance, Fuji/horizon high, decorative cartouche (title box) in a top corner with a red seal.",
"typography": "Vertical title cartouche: rectangle with light fill and dark border, with text as brush-like shapes (or a simple vertical text placeholder in a serif generic font); small red artist seal.",
"lighting_depth": "No cast shadows; depth by overlap, colour value (darker foreground), and bokashi mist bands.",
"svg_techniques": [
"colour blocks first (no outlines), then a separate <g id=\"key-block\"> of outline paths on top with slight translate(.8,.6) offset for mis-registration",
"seigaiha/wave pattern as a <pattern> of overlapping concentric arcs (3-4 rings) reused for water and sky bands",
"bokashi with a single vertical linearGradient limited to the sky region via clip-path",
"wood-grain overlay = one rect with feTurbulence stretched horizontally, low opacity, clipped to the picture area",
"seal = small vermilion rounded rect with knocked-out mark"
],
"avoid": ["smooth painterly shading", "cast shadows or glows", "more than 12 colours", "neon / modern saturation", "vanishing-point perspective", "just recolouring a generic drawing with 'Japanese' colours"],
"compatible_with": ["sumi-e", "art-nouveau", "minimal-modern", "manga", "screen-print", "risograph", "organic-botanical", "fantasy", "monochrome", "cyberpunk-neon", "geometric", "vintage-poster"],
"conflicts_with": ["glassmorphism", "clay-soft-3d", "y2k", "frutiger-aero", "pixel-art", "isometric", "low-poly"],
"best_for": ["illustration", "scene", "poster", "character"],
"detail_level": "high",
"checks": {"max_colors": 14, "max_gradients": 3, "max_filters": 2, "require_stroke": true, "require_elements_any": ["pattern", "use"], "min_elements": 35, "light_base": true},
"reference": "east-asian.md#ukiyo-e"
}
]
styles/registry/genre.json
[
{
"id": "cinematic-poster",
"name": "Cinematic Poster",
"family": "genre",
"aliases": ["movie poster", "film poster", "cinematic", "cinematic style", "blockbuster poster", "theatrical poster", "key art", "one sheet", "epic poster", "teaser poster", "hollywood poster"],
"keywords": ["title treatment", "billing block", "tagline", "epic", "teal and orange", "lens flare", "dramatic lighting"],
"description": "Epic key art: a single dramatic focal image with cinematic lighting and colour grading, strong silhouette, atmospheric depth and a well-structured title treatment with tagline and billing block.",
"visual_characteristics": [
"one iconic image; wide dramatic composition or heroic low-angle silhouette",
"colour-graded palette (teal/orange, monochrome red, cold blue) with a lit focal point",
"atmosphere: haze, light rays, particles, lens flares, vignette",
"professional typography: large title, tagline, credit block with condensed type"
],
"palette": {"guidance": "Pick ONE grading: teal & orange (complementary), monochrome + one accent, or night-blue with warm key. Keep shadows deep and highlights controlled; keep saturated colour only around the light source.", "swatches": ["#06131c", "#0e3b4a", "#1d6a7a", "#e8894a", "#ffcf99", "#f5efe6"]},
"geometry": "Strong vertical or wide-cinematic silhouette shapes; leading lines to the focal point; foreground silhouettes framing the scene; central or golden-ratio anchor.",
"stroke": "Little to none for art; type gets thin keylines; hairline rules in the billing block.",
"texture": "Film grain (single feTurbulence overlay), subtle dust/particles, bloom around lights, soft vignette.",
"composition": "Poster (2:3): top tagline, hero image dominating the middle, title lower third (large, wide tracking), billing block small at the bottom; strong value structure; depth planes with atmosphere.",
"typography": "Title in bold cinematic serif/sans, wide tracking or heavily condensed; tagline small caps; billing block in condensed sans at 3-4% height; use paths for the title if possible.",
"lighting_depth": "Volumetric: light rays as gradient polygons; rim light on silhouette; atmospheric perspective via layered haze; lens flare group at the light source.",
"svg_techniques": [
"sky/backdrop with 3-4 stop linearGradient (grading); radialGradient for light bloom",
"light shafts: polygons with linearGradient opacity fade, low fill-opacity",
"silhouette layers in near-black with subtle rim strokes in light colour",
"grain overlay + vignette overlay as last two layers"
],
"avoid": ["flat clip-art look", "rainbow colour palettes", "multiple competing focal points", "tiny illegible text", "cluttered layout", "real actor names / real film titles or logos"],
"compatible_with": ["fantasy", "sci-fi", "horror", "gothic", "cyberpunk-neon", "vintage-poster", "retro-futurism", "luxury-premium", "art-deco", "minimal-modern", "comic-book"],
"conflicts_with": ["childrens-illustration", "flat-design", "pixel-art", "hand-drawn-doodle", "sticker"],
"best_for": ["poster", "scene", "illustration"],
"detail_level": "high",
"checks": {"require_text": true, "min_gradients": 3, "min_elements": 25},
"reference": "genre.md#cinematic-poster"
},
{
"id": "fantasy",
"name": "Fantasy",
"family": "genre",
"aliases": ["fantasy art", "fantasy illustration", "high fantasy", "epic fantasy", "sword and sorcery", "storybook fantasy", "mythical", "magical", "rpg fantasy", "dungeons and dragons style", "d&d style", "tolkienesque"],
"keywords": ["dragon", "castle", "wizard", "elf", "magic", "enchanted", "sword", "kingdom", "mystical", "quest"],
"description": "Storied worlds of castles, dragons, magic and ancient landscapes: painterly depth, glowing magic light, grand scale and richly layered atmosphere.",
"visual_characteristics": [
"grand vistas with layered atmospheric depth (mist, mountains, ruins)",
"magical light sources (runes, orbs, dragon-fire, aurora) casting coloured glow",
"ornate detail: filigree, armour engraving, runic borders",
"heroic scale: tiny figures against monumental structures"
],
"palette": {"guidance": "Rich jewel or earthy palettes with a magical accent (arcane teal/violet, ember orange). Atmospheric perspective: far layers desaturated toward sky colour. Sunset golds for heroic, cool twilight for mystery.", "swatches": ["#120f24", "#2e2a5c", "#5b4b8a", "#8fb8d9", "#e8a13a", "#ffd98a", "#2d6a4f"]},
"geometry": "Organic terrain curves against strong architectural verticals (towers, spires); dragons and creatures with sweeping S-curve silhouettes; asymmetrical but weighted layout; ornate frames.",
"stroke": "Painterly/mostly fills; optional fine dark outline (1-1.5) on key characters; rune strokes glowing (round caps).",
"texture": "Soft mist layers, star/particle fields, subtle stone/bark textures via patterns; optional grain.",
"composition": "Depth stack: foreground framing (rocks/trees), mid-ground hero, background castle/mountains with haze; light source off-centre creating a path of light; leading lines toward the focal point.",
"typography": "Decorative serif or blackletter-like display for titles; runic glyphs as shapes; minimal.",
"lighting_depth": "Motivated glow: radial gradients around magic; god rays; rim light on silhouettes; aerial perspective bands of low-opacity gradient.",
"svg_techniques": [
"mountain ranges as layered paths with progressively lighter gradients and mist gradient bands between",
"glow via radialGradient (preferred) and one blur filter for hero magic only",
"stars/particles from a single <pattern> or <use> scatter",
"runes/ornaments as reusable <symbol>s"
],
"avoid": ["flat clip-art fills", "modern sans typography", "photoreal textures", "uniform lighting", "copying trademarked creatures or worlds"],
"compatible_with": ["gothic", "medieval-manuscript", "cinematic-poster", "art-nouveau", "ukiyo-e", "sumi-e", "childrens-illustration", "retro-16bit", "paper-cutout", "comic-book", "manga", "pixel-art", "low-poly", "isometric"],
"conflicts_with": ["brutalist", "minimal-modern", "blueprint", "infographic", "y2k"],
"best_for": ["scene", "illustration", "poster", "character"],
"detail_level": "high",
"checks": {"min_gradients": 3, "min_elements": 35, "min_groups": 4},
"reference": "genre.md#fantasy"
},
{
"id": "sci-fi",
"name": "Sci-Fi",
"family": "genre",
"weak_aliases": ["futuristic"],
"aliases": ["science fiction", "scifi", "sci fi", "space opera", "space art", "spaceship art", "hard sci-fi", "sci-fi illustration", "alien world", "starship", "space scene"],
"keywords": ["spaceship", "planet", "starfield", "alien", "hologram", "space station", "orbit", "nebula", "rocket", "interstellar"],
"description": "Futurist scenes of space, technology and alien worlds: clean engineered hard-surface forms, glowing instrument lights, vast scale and atmospheric nebulae or planetary vistas.",
"visual_characteristics": [
"hard-surface engineered forms with panel lines, vents, greebles in moderation",
"vast scale: planets, rings, stations dwarfing ships",
"glowing indicator lights and engine plumes; lens flares off stars",
"cool palette with a warm sun/engine counter-light"
],
"palette": {"guidance": "Deep space blue-black base; cool grey hulls with warm-lit edges; engine/energy accents (cyan, orange, magenta); planet atmospheres in teal/amber. Keep the brightest value at one focal light.", "swatches": ["#04060f", "#0e1a33", "#1f3a66", "#7ea6d9", "#dfe8f5", "#ff8a3d", "#39e6ff"]},
"geometry": "Modular hard-surface: bevelled shapes, straight panel seams, symmetrical hulls with asymmetric details; large circles for planets; orbits and trajectories as thin curves.",
"stroke": "Fine 0.5-1 panel lines in darker hull tone; thin light rim strokes on lit edges; glow strokes on engines.",
"texture": "Starfield (random small circles at varying opacities, or a <pattern>), subtle nebula clouds via layered gradients, panel line patterns.",
"composition": "Cinematic diagonal: ship or station in the foreground at an angle, planet or star large behind; strong scale contrast; a single light source direction; empty space for scale.",
"typography": "Clean sans or techno-condensed HUD text as small callouts (optional); vector mission patch style if requested.",
"lighting_depth": "Hard single light (sun) plus soft bounce from the planet; rim light on hulls; atmospheric glow on the planet limb (radial gradient); engine glow with small blur.",
"svg_techniques": [
"planet: circle with radialGradient shading + clipped cloud bands + limb-atmosphere ring (radialGradient stroke)",
"hull shapes as layered polygons with linearGradient for metal; panel lines as thin paths",
"starfield: 80-200 <circle>s generated by script or one <pattern> tile scaled; vary radius and opacity",
"glow through a single small filter on engine group"
],
"avoid": ["fantasy ornament", "flat sticker look unless requested", "uniform lighting", "scale without reference objects", "copying franchise ships/logos"],
"compatible_with": ["cyberpunk-neon", "retro-futurism", "blueprint", "isometric", "low-poly", "pixel-art", "retro-16bit", "comic-book", "manga", "cinematic-poster", "minimal-modern", "technical-illustration", "horror", "geometric"],
"conflicts_with": ["medieval-manuscript", "art-nouveau", "organic-botanical", "sumi-e", "childrens-illustration"],
"best_for": ["scene", "illustration", "poster", "icon"],
"detail_level": "high",
"checks": {"min_gradients": 2, "min_elements": 30},
"reference": "genre.md#sci-fi"
},
{
"id": "horror",
"name": "Horror",
"family": "genre",
"aliases": ["horror style", "horror art", "creepy", "spooky", "scary", "dread", "nightmare", "haunted", "macabre", "eerie", "horror movie style", "halloween dark", "supernatural horror"],
"keywords": ["haunted", "ghost", "skull", "blood", "fog", "moonlit", "graveyard", "monster", "dread", "creeping"],
"description": "Dread through restraint: deep shadows, sickly colour, distorted proportions, obscured details and a single unsettling focal point revealed by hard, unnatural light.",
"visual_characteristics": [
"dominant darkness (70%+ of the canvas) with a small area of hard cold or sickly light",
"distortion: elongated limbs, uneven eyes, off-balance framing",
"fog, vignette, and silhouettes hiding detail; reveal only the eyes/hands/shape",
"muted sickly palette with one visceral accent (blood red or toxic green)"
],
"palette": {"guidance": "Near-black green/blue/purple base, desaturated bone and grey-green mids, ONE violent accent (arterial red or sickly yellow-green). Highlights are small and cold.", "swatches": ["#050706", "#0e1512", "#1f2a26", "#4a5b52", "#c8cdb8", "#a4161a", "#9bb04a"]},
"geometry": "Angular, jagged edges; asymmetry; extreme aspect proportions; branches/claws as thin tapering forms; irregular silhouettes; slightly tilted horizons.",
"stroke": "Sparse: thin, uneven scratchy lines for detail; heavy black masses for shadow; jagged joins (miter) on claw-like shapes.",
"texture": "Grain, scratches, fog gradients, ink bleed; optionally a single displacement to roughen edges.",
"composition": "Off-centre focal point at the edge of the light; large negative darkness pressing in; low or Dutch angle; fog cutting mid-ground; something that half-hides in the dark.",
"typography": "Distressed condensed serif or scratchy hand-lettered look if used; small; low contrast against darkness.",
"lighting_depth": "Hard under-lighting or backlight; hard cast shadows; radial vignette; fog planes reduce contrast with depth.",
"svg_techniques": [
"start from a near-black base, add light as radialGradient cones/rects with low opacity, then draw only what the light touches",
"fog: horizontal linearGradient bands with opacity .1-.4 at multiple depths",
"roughened silhouettes: irregular hand-placed polygon points or one small feTurbulence displacement (scale <= 4) on the silhouette group",
"vignette overlay as last layer"
],
"avoid": ["gore and graphic violence", "bright cheerful colours", "even lighting", "centred symmetric framing", "cartoonish cuteness (unless requested)"],
"compatible_with": ["gothic", "pixel-art", "retro-8bit", "retro-16bit", "manga", "comic-book", "monochrome", "cinematic-poster", "medieval-manuscript", "fantasy", "sci-fi", "cyberpunk-neon", "screen-print"],
"conflicts_with": ["childrens-illustration", "frutiger-aero", "flat-design", "clay-soft-3d", "y2k"],
"best_for": ["scene", "illustration", "poster", "character"],
"detail_level": "medium",
"checks": {"dark_base": true, "min_gradients": 1, "min_elements": 20},
"reference": "genre.md#horror"
},
{
"id": "retro-futurism",
"name": "Retro Futurism",
"family": "genre",
"aliases": ["retrofuturism", "retro futuristic", "retro-futuristic", "atomic age", "space age", "googie", "jetsons style", "1950s future", "raygun gothic", "synthwave", "outrun", "vaporwave", "retro sci-fi", "mid century futurism"],
"keywords": ["sunset grid", "chrome", "rocket", "atomic", "boomerang", "starburst", "retro grid", "80s future", "jetpack"],
"description": "Yesterday's tomorrow: atomic-age optimism (googie shapes, rockets, starbursts) or 80s synthwave (sunset grids, chrome type, palm silhouettes) rendered with bold flat shapes, gradients and confident geometry.",
"visual_characteristics": [
"streamlined forms, fins, boomerangs, orbital rings, starbursts",
"sunset gradients (yellow -> orange -> magenta -> purple) with a striped setting sun",
"perspective grid floor, chrome lettering, neon line accents",
"optimistic, clean, poster-like composition"
],
"palette": {"guidance": "Two options: (a) atomic age - teal, cream, mustard, tomato red, navy on aged white; (b) synthwave - deep purple/indigo base with hot pink, orange, cyan, yellow sun gradient. Pick one and stay consistent.", "swatches": ["#1a0b3b", "#5b1a8f", "#ff2e93", "#ff8a1f", "#ffd23f", "#22d3ee", "#f6efe2"]},
"geometry": "Smooth streamlined curves, perfect circles and ellipses, angular fins, orbits; horizon grid with converging lines; striped sun (horizontal cut bands widening downward).",
"stroke": "Clean monoline 2-4 units with round caps for atomic motifs; neon strokes for grid lines; occasional double line.",
"texture": "Subtle grain or halftone; chrome banding through gradient stops; starfield dots.",
"composition": "Symmetric or centred hero (rocket/sun/car) on a horizon; grid floor to a vanishing point; big title in chrome letters; balanced geometry with plenty of air.",
"typography": "Italic extended or chrome-gradient display type; atomic-age script for retro accents; keep short.",
"lighting_depth": "Sun glow through radialGradient; chrome reflection via multi-stop gradient with a hard horizon band; grid lines fade with distance (opacity gradient).",
"svg_techniques": [
"striped sun: circle filled with a vertical gradient, masked by horizontal bars that grow thicker toward the bottom",
"perspective grid: converging lines from a vanishing point + horizontal lines spaced by geometric progression; opacity fade via mask gradient",
"chrome text: gradient with hard mid-stop; outline stroke + offset shadow",
"atomic motifs: reusable starburst <symbol> and orbit ellipses with rotation"
],
"avoid": ["muddy realism", "dark grim cyberpunk grit", "hand-drawn wobble", "inconsistent grid perspective", "too many hues"],
"compatible_with": ["cyberpunk-neon", "y2k", "vintage-poster", "art-deco", "sci-fi", "minimal-modern", "geometric", "screen-print", "cinematic-poster", "low-poly", "retro-16bit", "comic-book", "bauhaus"],
"conflicts_with": ["gothic", "medieval-manuscript", "sumi-e", "organic-botanical", "scientific-illustration"],
"best_for": ["poster", "scene", "illustration", "logo"],
"detail_level": "medium",
"checks": {"min_gradients": 2, "min_elements": 20},
"reference": "genre.md#retro-futurism"
}
]
styles/registry/pixel-retro.json
[
{
"id": "pixel-art",
"name": "Pixel Art",
"family": "pixel-retro",
"aliases": ["pixel", "pixelart", "pixelated", "pixel style", "sprite", "sprite art", "pixel sprite", "low res pixel", "bitmap sprite"],
"keywords": ["pixels", "sprite sheet", "tile map", "low resolution", "low res"],
"description": "Artwork drawn on a strict integer grid with hard edges, deliberate colour clusters and no anti-aliasing. Every pixel is a decision; form comes from clusters, dithering and readable silhouettes.",
"visual_characteristics": [
"single square grid (e.g. 16x16, 32x32, 64x64); every shape is an integer-aligned block",
"limited palette (typically 8-24 colours) organised in hue-shifted ramps",
"1-pixel dark outline or selective outlining; clean staircase lines (2:1 or 1:1 slopes)",
"clusters instead of noise; dithering only where it adds form"
],
"palette": {"guidance": "Hue-shifted ramps of 3-5 colours per material: shadows shift toward cool/purple, highlights toward warm/yellow. Keep a shared dark outline colour that is NOT pure black. 8-24 colours total.", "swatches": ["#1a1423", "#3d2c54", "#6b4a8e", "#e8702a", "#f7b267", "#fdf0d5", "#3e7cb1", "#7ec8a4"]},
"geometry": "Rectangles on integer coordinates only. Diagonals as clean 1:1 or 2:1 staircases; circles as classic pixel-circle arrangements. No curves, no sub-pixel offsets, no rotation except multiples of 90 degrees.",
"stroke": "No SVG strokes. Outlines are pixels (cells) of an outline colour. Selective outlining: darker tone of the fill on the lit side.",
"texture": "Ordered (Bayer 2x2/4x4) or hand-placed dithering for gradients and material texture, kept on the grid.",
"composition": "Design at the target grid size first (e.g. 32x32), strong silhouette, 1-2 px padding. Even pixel size everywhere (no mixed-resolution pixels).",
"typography": "Bitmap-style letters built from the same grid (5x7 or 3x5 cells). Do not use <text> with smooth fonts.",
"lighting_depth": "Two-to-three-step cel shading with a fixed light direction (top-left). Ground shadow as a dithered or flat ellipse of cells.",
"svg_techniques": [
"set shape-rendering=\"crispEdges\" on the root <svg>",
"author sprites as ASCII with scripts/pixel_grid.py -> one merged <path> per colour, integer coordinates",
"viewBox equals the grid size (e.g. 0 0 32 32) so it scales by integer factors",
"group per material (id=\"fur\", \"outline\", \"eyes\") for editability"
],
"avoid": ["<circle>/<ellipse>/curves", "gradients, blur or filters", "anti-aliased diagonals", "mixed pixel sizes", "sub-pixel positions", "pure black outlines on everything", "more than ~24 colours"],
"compatible_with": ["retro-8bit", "retro-16bit", "horror", "sci-fi", "fantasy", "cyberpunk-neon", "monochrome", "isometric", "retro-futurism", "childrens-illustration"],
"conflicts_with": ["glassmorphism", "frutiger-aero", "clay-soft-3d", "scientific-illustration", "sumi-e", "art-nouveau"],
"best_for": ["icon", "character", "scene", "illustration"],
"detail_level": "medium",
"checks": {
"forbid_elements": ["circle", "ellipse", "filter", "linearGradient", "radialGradient", "mask"],
"forbid_path_curves": true, "integer_coords": true, "require_shape_rendering": "crispEdges",
"max_colors": 28, "max_filters": 0, "forbid_stroke": true
},
"reference": "pixel-retro.md#pixel-art"
},
{
"id": "retro-8bit",
"name": "Retro 8-bit",
"family": "pixel-retro",
"extends": "pixel-art",
"aliases": ["8 bit", "8-bit", "8bit", "nes style", "nes", "famicom", "gameboy", "game boy", "atari", "commodore 64", "c64", "early console", "arcade 8-bit", "chiptune visual"],
"keywords": ["nes palette", "sprite 16x16", "four colors per sprite", "8-bit game"],
"description": "The NES / Game Boy / C64 era: tiny grids, tight colour budgets per sprite, chunky silhouettes and a fixed hardware-style palette.",
"visual_characteristics": [
"grid 16x16 or 32x32 sprites; scenes on 8x8 tiles up to 256x240 logical resolution",
"max 3 colours + transparent per sprite; <= 16 colours in the whole scene",
"flat cel shading, almost no dithering; bold 1px outlines",
"HUD-like framing: score bars, borders and blocky lettering"
],
"palette": {"guidance": "Pick ONE hardware palette and stay inside it: NES-like (saturated reds, blues, greens, olive, cream) or Game Boy 4-green ramp or C64 muted tones. Never invent gradient colours.", "swatches": ["#000000", "#fcfcfc", "#bcbcbc", "#f83800", "#0058f8", "#00a800", "#fca044", "#503000"]},
"geometry": "8x8 tile logic: shapes snap to multiples of a base cell. Chunky, symmetrical sprites; very simple readable silhouettes; hard-coded tile repetition for backgrounds (brick, grass, water).",
"stroke": "Pixel outlines only. 1-cell outline in the darkest palette colour.",
"texture": "None or two-tone checker tiles (brick/water) repeated on the tile grid.",
"composition": "Side-view or top-down with flat horizontal ground band; sprite large relative to the screen; sky as a flat colour or two-tone band; HUD strip at top.",
"typography": "8x8 bitmap font drawn from cells (uppercase, no anti-aliasing), e.g. 'SCORE 000100'.",
"lighting_depth": "No lighting model; depth via parallax bands (2-3 flat layers) and overlap.",
"svg_techniques": [
"everything from pixel_grid.py paths; scene tiles built with <pattern> of an integer-sized tile",
"root shape-rendering=\"crispEdges\"; viewBox equal to logical resolution (e.g. 0 0 128 112)",
"keep the palette in one comment and reuse only those hexes"
],
"avoid": ["more than 16 colours", "smooth gradients", "16-bit style shading and dithering", "anti-aliased text", "curved SVG primitives"],
"compatible_with": ["pixel-art", "horror", "fantasy", "sci-fi", "childrens-illustration", "monochrome"],
"conflicts_with": ["retro-16bit", "glassmorphism", "frutiger-aero", "clay-soft-3d"],
"best_for": ["character", "scene", "icon", "poster"],
"detail_level": "low",
"checks": {
"forbid_elements": ["circle", "ellipse", "filter", "linearGradient", "radialGradient", "mask"],
"forbid_path_curves": true, "integer_coords": true, "require_shape_rendering": "crispEdges",
"max_colors": 16, "max_filters": 0, "forbid_stroke": true
},
"reference": "pixel-retro.md#retro-8bit"
},
{
"id": "retro-16bit",
"name": "Retro 16-bit",
"family": "pixel-retro",
"extends": "pixel-art",
"aliases": ["16 bit", "16-bit", "16bit", "snes style", "snes", "super nintendo", "sega genesis", "mega drive", "genesis style", "16-bit rpg", "jrpg sprite", "16 bit era"],
"keywords": ["parallax background", "16-bit game", "rpg battle", "sprite shading", "dithered sky"],
"description": "The SNES / Mega Drive era: richer palettes, multi-step shading, dithered skies, detailed sprites on 32-64px grids and layered parallax backgrounds.",
"visual_characteristics": [
"sprites 32x32 to 64x64 with 3-5 step shading ramps and selective outlining",
"palettes of 16-32 colours per scene with carefully hue-shifted ramps",
"dithered gradients for skies, glows and shadows",
"layered parallax backgrounds (far mountains, mid trees, near foreground)"
],
"palette": {"guidance": "Ramp-based: each material has 4-5 steps with hue shift (cool shadows, warm highlights). Scene is unified by an atmospheric tint on distant layers. 16-32 colours total.", "swatches": ["#1b1035", "#3b2a6b", "#6e4fa5", "#c266d3", "#ff9d5c", "#ffe9a8", "#2b6a8f", "#5fd0a0", "#e04a4a"]},
"geometry": "Integer cells, staircase curves (anti-aliased-looking edges are made with intermediate-tone pixels only if the tone is a real palette colour), pixel circles, chunky proportions but more anatomy than 8-bit.",
"stroke": "Pixel outlines, coloured (selective) rather than black; interior lines in darker fill tone.",
"texture": "Bayer/hand dithering for skies, water and stone; specular pixel highlights on metal and eyes.",
"composition": "Cinematic side-on or 3/4 top-down; foreground / mid / background parallax planes; strong horizon; focal character in the lower third with glow behind.",
"typography": "Pixel font with 1px drop shadow, 8x8 or 8x12; menu frames with double borders and corner ornaments.",
"lighting_depth": "Single light direction plus rim light; atmospheric perspective by recolouring far layers toward the sky colour; glow through concentric hard-edged rings.",
"svg_techniques": [
"pixel_grid.py for sprites; backgrounds as one <path> per colour per layer",
"dither as <pattern> tiles of 2x2 or 4x4 integer size",
"crispEdges on root; keep layers as <g id=\"far\"> <g id=\"mid\"> <g id=\"near\">",
"emulate glow with stepped concentric pixel rings, never blur"
],
"avoid": ["true gradients or blur", "more than ~32 colours", "curves and circles as SVG primitives", "mixed pixel sizes", "modern flat UI look"],
"compatible_with": ["pixel-art", "fantasy", "sci-fi", "horror", "cyberpunk-neon", "retro-futurism", "isometric"],
"conflicts_with": ["retro-8bit", "glassmorphism", "frutiger-aero", "minimal-modern"],
"best_for": ["scene", "character", "poster", "illustration"],
"detail_level": "high",
"checks": {
"forbid_elements": ["circle", "ellipse", "filter", "linearGradient", "radialGradient", "mask"],
"forbid_path_curves": true, "integer_coords": true, "require_shape_rendering": "crispEdges",
"max_colors": 36, "max_filters": 0, "forbid_stroke": true
},
"reference": "pixel-retro.md#retro-16bit"
}
]
styles/registry/print.json
[
{
"id": "screen-print",
"name": "Screen Print",
"family": "print",
"aliases": ["screenprint", "screen printed", "silkscreen", "serigraph", "gig poster", "serigraphy", "screen print poster", "spot colour print", "spot color print", "limited ink print"],
"keywords": ["overprint", "ink layers", "gig poster", "registration", "spot color", "flat inks", "print texture"],
"description": "Layered spot-colour printing: 2-6 flat inks that overprint into new colours, bold shapes, halftone tone areas and characteristic mis-registration and ink texture.",
"visual_characteristics": [
"a strict ink budget (2-6 spot colours) with visible overprint intersections",
"bold, high-contrast graphic shapes with knock-outs",
"halftone or line-screen for tone",
"slight mis-registration, ink speckle and paper showing through"
],
"palette": {"guidance": "Choose 3-5 inks (e.g. tomato red, cobalt, mustard, black) on cream/kraft paper. Overprint areas (multiply) are pre-computed as darker mixed fills. No gradients.", "swatches": ["#f1e7d3", "#e8432e", "#2a4dab", "#f2b134", "#141414", "#5e2a54"]},
"geometry": "Simplified poster shapes, thick contours or knock-outs, strong silhouettes; geometric reduction; text and image built from the same blocky shapes.",
"stroke": "Prefer shape-based; if lines are used they are thick and blunt (butt/round) and belong to one ink layer.",
"texture": "Halftone dots (<pattern>), speckled ink dropout (single feTurbulence mask on one ink layer), slight paper grain.",
"composition": "Poster: dominant central image, bold title; asymmetric balance; margins consistent; overprint moments used deliberately at overlaps.",
"typography": "Bold display type, often integrated with the image; each letter in a single ink or knocked out of a shape.",
"lighting_depth": "Graphic light through ink values and overprints; no gradients; depth by layered opacity multiplication simulated with explicit mixed colours.",
"svg_techniques": [
"one <g id=\"ink-red\"> etc. per ink layer; layer offsets of 0.5-1.5 units for mis-registration",
"explicit overprint shapes: duplicate the overlap area via clip-path and fill with the mixed colour (or use mix-blend-mode: multiply with a fallback)",
"halftone patterns via <pattern> sized to the print screen; different dot radii in different areas",
"speckle mask via feTurbulence thresholded by feComponentTransfer (only on one layer)"
],
"avoid": ["gradients", "more than 6 inks", "photographic shading", "perfect alignment everywhere", "blur/glow"],
"compatible_with": ["risograph", "vintage-poster", "comic-book", "editorial-illustration", "minimal-modern", "geometric", "bauhaus", "brutalist", "monochrome", "graffiti-street-art", "ukiyo-e", "paper-cutout", "art-deco"],
"conflicts_with": ["glassmorphism", "clay-soft-3d", "frutiger-aero", "y2k", "scientific-illustration"],
"best_for": ["poster", "illustration", "logo", "scene"],
"detail_level": "medium",
"checks": {"max_colors": 9, "max_gradients": 0, "max_filters": 2, "min_groups": 3},
"reference": "print.md#screen-print"
},
{
"id": "risograph",
"name": "Risograph",
"family": "print",
"aliases": ["riso", "riso print", "risograph print", "riso style", "risograph style", "duplicator print", "riso texture", "zine print", "two color riso"],
"keywords": ["fluorescent pink", "riso blue", "grainy ink", "zine", "halftone grain", "misregistered"],
"description": "Grainy two-to-three-ink duplicator prints: fluorescent inks with overprint, coarse halftone/grain shading, gentle mis-registration and matte uncoated paper.",
"visual_characteristics": [
"2-3 vivid inks (fluorescent pink, riso blue, sunflower yellow, teal) overprinting",
"coarse grain and dot gradients instead of smooth gradients",
"off-register layers with visible overlap colours",
"matte cream paper; slightly soft, imperfect edges"
],
"palette": {"guidance": "Riso inks: fluorescent pink #ff48b0, blue #0078bf, yellow #ffe800, teal #00a95c, orange #ff6c2f, purple #765ba7; paper #f4efe4. Use 2-3 inks; overprint = multiply mix.", "swatches": ["#f4efe4", "#ff48b0", "#0078bf", "#ffe800", "#00a95c", "#765ba7"]},
"geometry": "Bold simple shapes, rounded and slightly irregular; grain fills; chunky lettering; strong silhouettes; layout often playful and off-grid.",
"stroke": "Few strokes; where used, thick and round in one ink layer.",
"texture": "Grain gradients: shapes filled with a dot/noise pattern whose density varies (via feTurbulence thresholded with feComponentTransfer, or a <pattern> at two densities); paper fibre.",
"composition": "Layered overlapping shapes; strong colour blocking; offset duplicates for misregistration; abundant paper margin; playful asymmetry.",
"typography": "Chunky friendly display type; overprinted letters offset by 2-3 units in the second ink.",
"lighting_depth": "Depth via overprint colour mixing and grain density; no smooth gradients.",
"svg_techniques": [
"one <g> per ink with mix-blend-mode: multiply (SVG2/CSS) AND a fallback of explicit overlap fill; offset each ink group by (1..3,1..2)",
"grain gradient: define filter grain (feTurbulence type=fractalNoise baseFrequency .9 -> feColorMatrix to alpha -> feComposite in SourceGraphic) applied to selected shapes only",
"halftone <pattern> with 2 dot sizes for graded areas",
"paper background off-white; no pure white"
],
"avoid": ["smooth digital gradients", "more than 3-4 inks", "glossy highlights", "perfect registration", "dark black-dominant palettes"],
"compatible_with": ["screen-print", "editorial-illustration", "flat-design", "paper-cutout", "hand-drawn-doodle", "childrens-illustration", "geometric", "vintage-poster", "bauhaus", "organic-botanical", "comic-book"],
"conflicts_with": ["glassmorphism", "cyberpunk-neon", "clay-soft-3d", "frutiger-aero", "scientific-illustration"],
"best_for": ["poster", "illustration", "pattern", "scene"],
"detail_level": "medium",
"checks": {"max_colors": 9, "max_gradients": 0, "require_elements_any": ["pattern", "filter"], "min_groups": 2},
"reference": "print.md#risograph"
},
{
"id": "graffiti-street-art",
"name": "Graffiti / Street Art",
"family": "print",
"aliases": ["graffiti", "street art", "graffiti style", "spray paint", "spray-paint", "urban art", "wildstyle", "tag art", "stencil art", "banksy style", "mural street art", "bubble letters"],
"keywords": ["spray", "drips", "tag", "stencil", "bubble letters", "wall mural", "brick wall", "wildstyle"],
"description": "Urban wall art: bold outlined lettering or characters, spray gradients and drips, stencil cutouts, arrows, stars and layered tags on textured brick or concrete.",
"visual_characteristics": [
"chunky outlined lettering (bubble/wildstyle) with 3D drop-extrusions and highlights",
"spray-paint softness: fades, halos, drips and overspray",
"stencil layering: hard-edged shapes with bridges",
"gritty wall textures: brick, concrete, posters and tags"
],
"palette": {"guidance": "High-saturation spray colours (hot pink, cyan, acid yellow, orange) with black and white outlines; wall in warm concrete/brick tones. Fills often have a light-to-dark fade.", "swatches": ["#2b2b30", "#7a6a5e", "#ff2e93", "#00d1ff", "#ffe600", "#ff7a00", "#ffffff"]},
"geometry": "Rounded, inflated letterforms with overlapping strokes; arrows and crowns; stencil bridges; dynamic diagonals; extrusion shapes.",
"stroke": "Layered outlines: inner white highlight line, thick black outline (6-10 units), outer offset shadow; round joins. Drips = tapered vertical paths.",
"texture": "Brick or concrete via <pattern>; spray via blurred gradient dots and speckle; grime overlay at low opacity.",
"composition": "Piece dominates the wall; slight tilt; supporting tags/characters around; some elements overlapping the frame; energetic and asymmetrical.",
"typography": "Draw letters as custom paths (bubbly, overlapping); avoid live text. Small tag signatures in a scrawl.",
"lighting_depth": "Fake 3D extrusion behind letters; highlight streaks on top-left; overspray glow around edges.",
"svg_techniques": [
"letters as stacked paths: extrusion (offset copy in darker tone), fill with linearGradient, black outline stroke, white inner highlight",
"drips as thin tapered paths with circular end blobs",
"spray fade via radialGradient with soft opacity stops; a single blur filter on overspray layer only",
"brick wall as <pattern> with mortar lines; slight colour variation via 3 brick fills"
],
"avoid": ["clean corporate typography", "thin lines", "flat white backgrounds", "over-polished geometry", "copying real artists' works or signatures"],
"compatible_with": ["comic-book", "sticker", "brutalist", "screen-print", "cyberpunk-neon", "hand-drawn-doodle", "y2k", "pixel-art", "risograph"],
"conflicts_with": ["luxury-premium", "art-nouveau", "minimal-modern", "scientific-illustration", "medieval-manuscript", "childrens-illustration"],
"best_for": ["poster", "illustration", "sticker", "character"],
"detail_level": "high",
"checks": {"require_stroke": true, "min_stroke_widths": 2, "min_elements": 25, "min_gradients": 1},
"reference": "print.md#graffiti-street-art"
}
]
styles/registry/technical.json
[
{
"id": "blueprint",
"name": "Blueprint",
"family": "technical",
"aliases": ["technical schematic", "blueprints", "blueprint style", "technical blueprint", "cyanotype plan", "engineering drawing", "schematic", "architectural plan", "construction drawing", "draft drawing", "cad drawing", "technical drawing", "engineering schematic"],
"keywords": ["schematic", "dimensions", "grid paper", "callouts", "plan view", "measurements", "construction lines", "annotation"],
"description": "White/cyan linework on cyanotype blue: a technical grid, precise construction geometry, dimension lines, callouts and a title block, organised like an engineering sheet.",
"visual_characteristics": [
"deep blue base (#0b3d91-#104a9c) with a two-level grid (minor and major lines)",
"line hierarchy: thick object outline, medium hidden/section lines, hairline construction/dimension lines",
"dimension arrows, leader lines, numbered callouts, centre lines (dash-dot), section marks",
"title block with drawing number, scale, notes; small revision table"
],
"palette": {"guidance": "Base cyanotype blue (#0d3b8c) with a slightly lighter grid (#2a5cb5 at 40-60% opacity), linework in white (#f2f7ff) and pale cyan (#9fd4ff). No other hues; accent only for a highlighted dimension (pale yellow) if needed.", "swatches": ["#0d3b8c", "#164a9f", "#2a5cb5", "#9fd4ff", "#dbeaff", "#f7fbff"]},
"geometry": "Orthographic views (top/front/side) or plan; exact circles, arcs and tangents; centre marks; constructions drawn (bisectors, arcs) faintly; everything aligned to the grid.",
"stroke": "Line hierarchy: 2.5 (outline), 1.5 (edges), 1 (hidden dashed 6 3), 0.6 (dimension/construction), 0.4 (grid). Butt caps, miter joins. Dash-dot (12 3 2 3) for centre lines.",
"texture": "Paper grid only; optional faint fold creases; no shading.",
"composition": "Sheet layout: outer border, inner drawing area, title block bottom-right, main view centred, secondary views arranged around it; notes in small caps at left; scale bar.",
"typography": "Monospace or single-stroke technical lettering (uppercase), 3 sizes; dimension text centred on dimension lines with a gap.",
"lighting_depth": "None. Depth by projection lines between views and line hierarchy.",
"svg_techniques": [
"grid = two <pattern>s (10 and 50 unit) filled into a rect",
"dimension line as reusable <marker> arrowheads + <line> + <text> group defined via <symbol>",
"dashed styles through stroke-dasharray classes (hidden, centre)",
"title block as a table of <rect>/<line>/<text>; keep text as <text> monospace with generic fallback"
],
"avoid": ["shading and gradients", "colour beyond blue/white/cyan", "organic decoration", "misaligned annotations", "inconsistent line weights", "drop shadows"],
"compatible_with": ["technical-illustration", "sci-fi", "retro-futurism", "cyberpunk-neon", "minimal-modern", "monochrome", "isometric", "geometric", "infographic"],
"conflicts_with": ["hand-drawn-doodle", "childrens-illustration", "frutiger-aero", "clay-soft-3d", "art-nouveau", "graffiti-street-art"],
"best_for": ["illustration", "infographic", "poster", "icon"],
"detail_level": "high",
"checks": {"dark_base": true, "require_stroke": true, "min_stroke_widths": 3, "require_elements_any": ["pattern"], "require_text": true, "max_gradients": 0, "max_filters": 0},
"reference": "technical.md#blueprint"
},
{
"id": "technical-illustration",
"name": "Technical Illustration",
"family": "technical",
"aliases": ["technical illustration", "technical schematic", "tech illustration", "exploded view", "cutaway", "cutaway diagram", "instruction manual art", "ikea style", "patent drawing", "manual illustration", "line drawing technical", "engineering illustration", "technical diagram"],
"keywords": ["exploded", "cross-section", "cutaway", "assembly", "patent", "manual", "parts diagram", "callout numbers"],
"description": "Clear, explanatory line-and-tone drawings: precise linework with consistent weight hierarchy, cutaways/exploded views, numbered callouts and restrained tone to explain how something works.",
"visual_characteristics": [
"accurate proportions and parallel/axonometric views",
"clean contour + interior line hierarchy; hidden edges dashed when needed",
"light spot tones (greys or 1-2 colours) to separate parts; sparse shading",
"callout numbers with leader lines; arrows for motion/assembly; exploded parts aligned on axes"
],
"palette": {"guidance": "White paper, near-black linework, a mid/light grey tone for parts, and 1-2 signal colours (safety orange, blue) to highlight moving or key parts. Low saturation elsewhere.", "swatches": ["#ffffff", "#1a1a1a", "#c8ccd0", "#8a9199", "#ff6a1f", "#1f6fff"]},
"geometry": "Isometric/axonometric or orthographic; exact circles as ellipses in projection; consistent draft angles; parts separated along exploded axes with dashed alignment lines.",
"stroke": "Weights: 2 (silhouette), 1.2 (feature edges), 0.6 (detail/hatching), dashed 0.8 (hidden/alignment). Round joins for organic parts, miter for machined parts. Consistent across the sheet.",
"texture": "Cross-hatching and fine line shading on curved parts; stipple for cast metal; parallel section hatch at 45 degrees in cutaways.",
"composition": "Hero assembly centred, exploded parts fan along an axis, numbered callouts placed radially outside the drawing; legend or parts list at side.",
"typography": "Single-stroke sans, small; callout numbers in circles; consistent size.",
"lighting_depth": "Minimal: tone by direction to a single light (top-left), no cast shadows except a faint ground shadow; depth by overlap and line weight.",
"svg_techniques": [
"define reusable part symbols and <use> them in the exploded layout",
"dashed alignment lines via stroke-dasharray class; arrows via <marker>",
"section hatch as <pattern rotate(45)> clipped to cut faces",
"callouts as a group: circle + text + leader line, reused via <symbol>"
],
"avoid": ["decorative gradients", "wobbly lines", "inconsistent line weights", "unlabelled arrows", "colour overload", "glows/blurs"],
"compatible_with": ["blueprint", "scientific-illustration", "isometric", "minimal-modern", "monochrome", "infographic", "sci-fi", "retro-futurism", "flat-design"],
"conflicts_with": ["hand-drawn-doodle", "graffiti-street-art", "art-nouveau", "cyberpunk-neon", "y2k", "childrens-illustration"],
"best_for": ["illustration", "infographic", "icon"],
"detail_level": "high",
"checks": {"require_stroke": true, "min_stroke_widths": 2, "require_text": true, "max_filters": 0, "max_gradients": 2, "light_base": true},
"reference": "technical.md#technical-illustration"
},
{
"id": "scientific-illustration",
"name": "Scientific Illustration",
"family": "technical",
"aliases": ["scientific", "scientific drawing", "natural history illustration", "naturalist illustration", "field guide", "field guide illustration", "anatomical illustration", "anatomy drawing", "specimen plate", "biological illustration", "zoological plate", "encyclopedia plate", "audubon style"],
"keywords": ["specimen", "plate", "labels", "latin name", "anatomy", "field guide", "cross section", "taxonomy"],
"description": "Accurate, information-rich natural-history plates: naturalistic form with subtle tonal modelling, fine linework, labelled parts, scale bars and a calm specimen-plate layout.",
"visual_characteristics": [
"naturalistic proportions and careful anatomy",
"fine contour + hatching/stipple/soft washes for form",
"multiple views (whole, detail, cross-section) arranged on a plate",
"labels with leader lines, Latin names, scale bar; cream plate background"
],
"palette": {"guidance": "Cream plate (#f5efe0), sepia/brown-black ink (#3a2a1e), naturalistic muted washes for specimens (olive, ochre, umber, slate), one careful accent for the key feature.", "swatches": ["#f5efe0", "#3a2a1e", "#8a6d3b", "#6d7f4f", "#a86b4a", "#5f7a8a", "#d9c9a0"]},
"geometry": "Organic forms with accurate structure, symmetric where the species is symmetric; overlapping scales/feathers/leaves as repeated units with variation; plate border rule.",
"stroke": "Thin (0.5-1.2) ink contours with varied weight; cross-hatch and stipple for tone; leader lines 0.5; round caps.",
"texture": "Stipple (small circles via <pattern>), hatching for shadows, watercolour wash gradients within contours, faint paper tone.",
"composition": "Plate layout: main specimen large and slightly off-centre, detail insets, caption block at bottom, inset scale bar, thin double border.",
"typography": "Italic serif for Latin names, small caps for headings, tiny sans for callouts.",
"lighting_depth": "Soft single light, gentle form shadows through hatch/wash; no cast shadows; delicate highlight left as paper.",
"svg_techniques": [
"washes via gradient fills inside contour shapes with 0.6-0.9 opacity; do not blur",
"hatching by clipped <pattern> of fine lines or explicit line groups following form",
"repeated units (scales, feathers) via <use> with per-instance transform and tone variation",
"labels as <text> with leader <path>s"
],
"avoid": ["cartoon proportions", "heavy outlines", "saturated colours", "flat clip-art fills", "unlabelled plates", "inaccurate anatomy"],
"compatible_with": ["organic-botanical", "technical-illustration", "medieval-manuscript", "monochrome", "infographic", "vintage-poster", "art-nouveau"],
"conflicts_with": ["cyberpunk-neon", "pixel-art", "y2k", "flat-design", "glassmorphism", "comic-book", "clay-soft-3d", "low-poly"],
"best_for": ["illustration", "infographic", "poster"],
"detail_level": "high",
"checks": {"require_stroke": true, "require_text": true, "min_elements": 30, "light_base": true, "max_filters": 1},
"reference": "technical.md#scientific-illustration"
},
{
"id": "infographic",
"name": "Infographic",
"family": "technical",
"aliases": ["infographics", "info graphic", "data visualization", "data viz", "dataviz", "explainer graphic", "statistics graphic", "process diagram", "timeline graphic", "information design", "visual explainer", "chart graphic"],
"keywords": ["chart", "data", "statistics", "timeline", "steps", "comparison", "process", "flow", "kpi"],
"description": "Information design first: a strict hierarchy, consistent icon and chart grammar, aligned modules, and colour used to encode meaning rather than decorate.",
"visual_characteristics": [
"clear reading order: title -> key number/insight -> supporting modules -> source/footer",
"consistent icon style and chart styling (one stroke weight, one corner radius)",
"colour encodes categories or emphasis; neutral greys for context",
"generous alignment, spacing and grouping (proximity, containers, dividers)"
],
"palette": {"guidance": "Neutral base (white/off-white/dark), 1 primary accent + 3-4 categorical colours that are distinguishable (also by lightness for colour-blind safety), grey for secondary data.", "swatches": ["#ffffff", "#111827", "#2563eb", "#f59e0b", "#10b981", "#ef4444", "#9ca3af"]},
"geometry": "Grid-locked modules; bars, lines, rings, dots as data marks with exact proportions; rounded rects for cards; consistent icon grid (e.g. 24 units with 2 unit stroke).",
"stroke": "Uniform 2 unit stroke for icons and chart axes (round caps/joins); hairline 1 for gridlines; no other weights.",
"texture": "None. Subtle background tints for grouping.",
"composition": "12-column mental grid; strong left-aligned or centred axis; hero stat large; charts scaled to data (compute bar lengths, angles from real numbers); footer with source.",
"typography": "Two sizes ratio 1:1.6+ (title/labels), one family (sans), tabular-friendly numerals; hero numbers 3-4x body; keep labels short and legible at intended size (>= 12 units at 1000 wide).",
"lighting_depth": "Flat. Optional 1-level card shadow at very low opacity; avoid 3D charts.",
"svg_techniques": [
"compute all geometry from data in code (Python/JS) instead of eyeballing; emit <rect>/<path> with exact values",
"icons as <symbol> in defs with a consistent 24 grid; <use> for each",
"text as <text> with text-anchor for alignment; add <title> for accessibility",
"group per module: id=\"header\", \"chart-1\", \"legend\", \"footer\""
],
"avoid": ["chartjunk", "3D pie/bar charts", "decorative gradients", "unaligned labels", "more than ~6 colours", "illegible small text", "invented data presented as real"],
"compatible_with": ["flat-design", "minimal-modern", "blueprint", "technical-illustration", "isometric", "editorial-illustration", "monochrome", "geometric", "bauhaus", "brutalist"],
"conflicts_with": ["horror", "gothic", "medieval-manuscript", "graffiti-street-art", "sumi-e", "art-nouveau"],
"best_for": ["infographic", "poster", "illustration"],
"detail_level": "medium",
"checks": {"require_text": true, "min_groups": 3, "max_filters": 0, "max_gradients": 2, "max_colors": 12},
"reference": "technical.md#infographic"
}
]