PICK SKY: natal wheel icon + palette sprint — inline SVG zodiac icons, per-planet alchemical colors
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful

- Replace Unicode glyph text elements with inline SVG <path> icons loaded from icons/zodiac-signs/*.svg files
- NatusWheel.preload() fetches + caches all 12 zodiac SVG paths before first draw; auto-detects static base URL from <script src>
- Per-element (fire/stone/air/water) colored circles behind zodiac icons; icon fill colors use element palette vars
- PLANET_ELEMENTS map (au/ag/hg/cu/fe/sn/pb/u/np/pu) drives per-planet circle + label CSS modifier classes
- Planet circles: ternary fill + senary stroke per alchemical metal palette; labels: senary fill + stroke halo
- Zodiac icon scale = 85% of circle diameter (15% inset so icons breathe inside circles)
- Zodiac + house segment bg opacity 0.5; all ring/segment borders --terUser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-15 18:20:39 -04:00
parent 9beb21bffe
commit 71ef3dcb7f
15 changed files with 153 additions and 41 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M433.7 355.4C442.7 350.9 453.3 350.9 462.3 355.4L590.3 419.4C606.1 427.3 612.5 446.5 604.6 462.3C596.7 478.1 577.5 484.5 561.7 476.6L448 419.8L334.3 476.6C325.3 481.1 314.7 481.1 305.7 476.6L192 419.8L78.3 476.6C62.5 484.5 43.3 478.1 35.4 462.3C27.5 446.5 33.9 427.3 49.7 419.4L177.7 355.4C186.7 350.9 197.3 350.9 206.3 355.4L320 412.2L433.7 355.4zM437.1 161.9C445.3 158.9 454.4 159.4 462.3 163.4L590.3 227.4C606.1 235.3 612.5 254.5 604.6 270.3C596.7 286.1 577.5 292.5 561.7 284.6L448 227.8L334.3 284.6C325.3 289.1 314.7 289.1 305.7 284.6L192 227.8L78.3 284.6C62.5 292.5 43.3 286.1 35.4 270.3C27.5 254.5 33.9 235.3 49.7 227.4L177.7 163.4L181.1 161.9C189.3 158.9 198.4 159.4 206.3 163.4L320 220.2L433.7 163.4L437.1 161.9z"/></svg>

After

Width:  |  Height:  |  Size: 801 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M465.6 64C526.9 64 576 114.3 576 175C576 204.4 564.4 232.9 543.4 253.8L534.6 262.6C522.1 275.1 501.8 275.1 489.4 262.6C477 250.1 476.9 229.8 489.4 217.4L498.2 208.6C507 199.8 512 187.6 512 175C512 149.1 490.9 128 465.6 128C443.1 128 423.9 144.1 419.9 166.2L351.5 549.6C351.5 549.7 351.4 549.9 351.4 550C351.2 550.8 351.1 551.6 350.9 552.3C350.8 552.6 350.8 552.8 350.7 553C350.5 553.6 350.3 554.3 350 554.9C349.8 555.4 349.6 555.9 349.4 556.4C349.3 556.7 349.1 557 349 557.3C347.4 560.8 345.2 563.8 342.6 566.5C340.6 568.5 338.4 570.2 336 571.6C335.8 571.7 335.7 571.8 335.5 571.9C334.8 572.3 334.1 572.6 333.4 572.9C333.1 573 332.8 573.2 332.5 573.3C332 573.5 331.5 573.7 331 573.9C330.4 574.1 329.8 574.4 329.1 574.6C328.9 574.7 328.6 574.8 328.4 574.8C327.6 575 326.8 575.2 326.1 575.3C325.9 575.3 325.8 575.4 325.7 575.4C325.5 575.4 325.3 575.4 325.1 575.5C324.4 575.6 323.8 575.7 323.1 575.7C322.7 575.7 322.3 575.8 321.9 575.8C321.3 575.8 320.7 575.9 320.1 575.9C319.5 575.9 318.9 575.9 318.3 575.8C317.9 575.8 317.5 575.7 317.1 575.7C316.4 575.6 315.7 575.6 315.1 575.5C314.9 575.5 314.7 575.5 314.5 575.4C314.3 575.4 314.2 575.3 314 575.3C313.2 575.1 312.4 575 311.7 574.8C311.4 574.7 311.2 574.7 310.9 574.6C310.3 574.4 309.6 574.2 309 573.9C308.5 573.7 308 573.5 307.5 573.3C307.2 573.2 306.9 573 306.6 572.9C305.9 572.6 305.2 572.2 304.5 571.9C304.3 571.8 304.2 571.7 304 571.6C301.6 570.2 299.3 568.5 297.4 566.5C294.8 563.9 292.6 560.8 291 557.3C290.9 557.1 290.8 557 290.8 556.8L290.3 555.7C290.2 555.4 290.1 555.2 290 554.9C289.8 554.3 289.5 553.7 289.3 553C289.2 552.8 289.1 552.5 289.1 552.3C288.9 551.5 288.7 550.7 288.6 550C288.6 549.9 288.5 549.7 288.5 549.6L220 166.2C216 144.1 196.8 128 174.3 128C149 128 127.9 149.1 127.9 175C127.9 187.6 132.9 199.8 141.7 208.6L150.5 217.4C163 229.9 163 250.2 150.5 262.6C138 275 117.7 275.1 105.3 262.6L96.5 253.8C75.6 232.9 64 204.3 64 175C64 114.3 113.1 64 174.4 64C227.8 64 273.6 102.3 283 155L320 362L357 155C366.4 102.4 412.2 64 465.7 64z"/></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M472 216C529.4 216 576 262.6 576 320C576 461.4 461.4 576 320 576C265.7 576 215.2 559 173.7 530.1C159.2 520 155.7 500 165.8 485.5C175.9 471 195.9 467.5 210.4 477.6C241.5 499.3 279.3 512 320.1 512C388 512 447.6 476.7 481.8 423.5C478.6 423.8 475.4 424 472.1 424C414.7 424 368.1 377.4 368.1 320C368.1 262.6 414.7 216 472.1 216zM320 64C374.3 64 424.8 81 466.3 109.9C480.8 120 484.3 140 474.2 154.5C464.1 169 444.1 172.5 429.6 162.4C398.5 140.7 360.7 128 319.9 128C252 128 192.4 163.2 158.2 216.4C161.4 216.1 164.6 216 167.9 216C225.3 216 271.9 262.6 271.9 320C271.9 377.4 225.4 424 168 424C110.6 424 64 377.4 64 320C64 318.1 64 316.2 64.1 314.4C67.1 175.6 180.5 64 320 64zM168 280C145.9 280 128 297.9 128 320C128 342.1 145.9 360 168 360C190.1 360 208 342.1 208 320C208 297.9 190.1 280 168 280zM472 280C449.9 280 432 297.9 432 320C432 342.1 449.9 360 472 360C494.1 360 512 342.1 512 320C512 297.9 494.1 280 472 280z"/></svg>

After

Width:  |  Height:  |  Size: 990 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M272 96C351.5 96 416 160.5 416 240L416 305.2C434.8 294.3 456.7 288 480 288C550.7 288 608 345.3 608 416C608 486.7 550.7 544 480 544C444.2 544 411.8 529.3 388.6 505.6C359.8 548.1 311.2 576 256 576C238.3 576 224 561.7 224 544C224 526.3 238.3 512 256 512C309 512 352 469 352 416L352 240C352 195.8 316.2 160 272 160C227.8 160 192 195.8 192 240L192 448C192 465.7 177.7 480 160 480C142.3 480 128 465.7 128 448L128 224C128 188.7 99.3 160 64 160C46.3 160 32 145.7 32 128C32 110.3 46.3 96 64 96C104.6 96 140.8 115 164.2 144.5C190.6 114.7 229.1 96 272 96zM480 352C444.7 352 416 380.7 416 416C416 451.3 444.7 480 480 480C515.3 480 544 451.3 544 416C544 380.7 515.3 352 480 352z"/></svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M491.4 72C504.6 60.3 524.7 61.5 536.5 74.7C548.2 87.9 547 108.1 533.8 119.9C531.1 122.3 510.7 139.7 475.5 156.7C467.3 160.7 458.3 164.6 448.5 168.3L448.5 471.5C458.3 475.3 467.3 479.1 475.5 483.1C493.7 491.9 507.9 500.8 517.7 507.6C520.7 509.7 523.2 511.7 525.4 513.3C526.6 514.2 527.7 514.9 528.6 515.7C528.8 515.8 528.9 515.9 529.1 516.1C530.4 517.2 531.5 518.1 532.3 518.7C532.7 519 533 519.3 533.2 519.5C533.3 519.6 533.5 519.7 533.5 519.8C533.5 519.8 533.6 519.9 533.6 519.9L533.7 519.9L533.7 519.9L533.7 519.9C537 522.8 539.6 526.3 541.4 530.1C546.8 541.4 545.2 555.2 536.4 565.1C536.1 565.5 535.7 565.8 535.3 566.2C523.4 578.4 504.1 579.3 491.3 567.9C491 567.6 489.8 566.6 487.8 565.1C487.4 564.8 486.9 564.4 486.3 564C484.4 562.6 482.1 560.9 479.2 558.9C477 557.4 474.5 555.8 471.8 554.1C465.2 550 457 545.3 447.4 540.6C440 537 431.8 533.4 422.7 530.1C409.1 525 393.7 520.5 376.6 517.2C370.9 516.1 365 515.1 358.9 514.4C346.8 512.8 333.9 511.9 320.3 511.9C266.1 511.9 222.9 526.3 193.3 540.7C189.9 542.3 186.8 544 183.8 545.6C178.7 548.4 174.1 551 170 553.5C166.7 555.6 163.7 557.5 161.1 559.2C159.4 560.4 157.9 561.5 156.5 562.5C154.6 563.9 153 565 151.9 565.9C150.5 567 149.7 567.7 149.4 567.9C136.2 579.6 116.1 578.4 104.3 565.1C102.1 562.6 100.3 559.9 99.1 557C97.9 554.1 97 551.1 96.6 548.1C95.2 537.9 98.8 527.3 107 519.9C107.2 519.7 107.6 519.4 108.2 518.9C108.3 518.8 108.4 518.7 108.5 518.6L111.7 516C113.4 514.7 115.4 513.2 117.9 511.4C119.5 510.2 121.2 508.9 123.1 507.6C132.9 500.8 147.1 491.9 165.3 483.1C173.5 479.1 182.5 475.2 192.3 471.5L192.3 168.3C182.7 164.6 173.7 160.7 165.5 156.8C130.3 139.7 110 122.3 107.3 119.9C94.1 108.2 92.9 88 104.6 74.7C116.3 61.5 136.5 60.3 149.7 72C151.1 73.2 166.7 86.2 193.5 99.2C223.1 113.6 266.3 128 320.5 128C374.7 128 417.9 113.6 447.5 99.2C474.3 86.2 489.9 73.2 491.3 72zM384.5 186.3C364.8 189.8 343.5 192 320.5 192C297.5 192 276.2 189.8 256.5 186.3L256.5 453.7C269.1 451.4 282.4 449.7 296.4 448.8C304.2 448.3 312.2 448 320.4 448C343.4 448 364.8 450.2 384.4 453.7L384.4 186.3z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M324 64C396.9 64 456 123.1 456 196L456 201.4L455.9 202.8L432.1 472.8C432.5 494.5 450.3 512 472.1 512C494.2 512 512.1 494.1 512.1 472L512.1 432C512.1 414.3 526.4 400 544.1 400C561.8 400 576.1 414.3 576.1 432L576.1 472C576.1 529.4 529.5 576 472.1 576C414.7 576 368 529.4 368 472L368 470.6L368.1 469.2L392 198.6L392 196C392 158.4 361.6 128 324 128C286.4 128 256 158.4 256 196L256 200C256 203.7 256.3 207.3 256.8 211L286.5 404.4C287.4 410.5 287.9 416.6 287.9 422.8L287.9 432C287.9 493.9 237.8 544 175.9 544C114 544 64 493.9 64 432C64 370.1 114.1 320 176 320C187.7 320 199 321.8 209.7 325.1L193.6 220.7C192.5 213.9 192 206.9 192 200L192 196C192 123.1 251.1 64 324 64zM176 384C149.5 384 128 405.5 128 432C128 458.5 149.5 480 176 480C202.5 480 224 458.5 224 432C224 405.5 202.5 384 176 384z"/></svg>

After

Width:  |  Height:  |  Size: 864 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M544 448C561.7 448 576 462.3 576 480C576 497.7 561.7 512 544 512L96 512C78.3 512 64 497.7 64 480C64 462.3 78.3 448 96 448L544 448zM320 96C417.2 96 496 174.8 496 272C496 288.6 493.6 304.7 489.3 320L544 320C561.7 320 576 334.3 576 352C576 369.7 561.7 384 544 384L439.8 384C428 384 417.1 377.5 411.6 367.1C406.1 356.7 406.7 344 413.2 334.2C425.1 316.4 432 295.1 432 272C432 210.1 381.9 160 320 160C258.1 160 208 210.1 208 272C208 295.1 214.9 316.4 226.8 334.2C233.4 344 234 356.7 228.4 367.1C222.8 377.5 212.1 384 200.2 384L96 384C78.3 384 64 369.7 64 352C64 334.3 78.3 320 96 320L150.8 320C146.5 304.7 144 288.6 144 272C144 174.8 222.8 96 320 96z"/></svg>

After

Width:  |  Height:  |  Size: 725 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M136.4 74.4C148.3 61.4 168.6 60.5 181.6 72.4C184.5 75.1 209.9 99.1 235.2 140.2C257.5 176.4 280.2 226.8 286.3 288L353.6 288C359.7 226.9 382.4 176.4 404.7 140.2C430 99.1 455.4 75.1 458.3 72.4C471.3 60.5 491.6 61.3 503.5 74.4C515.4 87.4 514.5 107.7 501.5 119.6C500.1 120.9 479.5 140.7 459.1 173.8C441.2 202.9 423.7 241.8 417.9 288L511.9 288L515.2 288.2C531.3 289.8 543.9 303.5 543.9 320C543.9 336.5 531.3 350.2 515.2 351.8L511.9 352L417.9 352C423.7 398.2 441.2 437.1 459.1 466.2C479.5 499.3 500.1 519.1 501.5 520.4C514.5 532.3 515.4 552.6 503.5 565.6C491.6 578.6 471.3 579.5 458.3 567.6C455.4 564.9 430 540.9 404.7 499.8C382.4 463.6 359.7 413.2 353.6 352L286.3 352C280.2 413.1 257.5 463.6 235.2 499.8C209.9 540.9 184.5 564.9 181.6 567.6C168.6 579.5 148.3 578.7 136.4 565.6C124.5 552.6 125.4 532.3 138.4 520.4C139.8 519.1 160.4 499.3 180.8 466.2C198.7 437.1 216.2 398.2 222 352L128 352C110.3 352 96 337.7 96 320C96 302.3 110.3 288 128 288L222 288C216.2 241.8 198.7 202.9 180.8 173.8C160.4 140.7 139.8 120.9 138.4 119.6C125.4 107.7 124.5 87.4 136.4 74.4z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M512 96C529.7 96 544 110.3 544 128L544 288C544 305.7 529.7 320 512 320C494.3 320 480 305.7 480 288L480 205.2L333.2 352L374.6 393.4C387.1 405.9 387.1 426.2 374.6 438.6C362.1 451 341.8 451.1 329.3 438.6L288 397.3L150.6 534.6C138.1 547.1 117.8 547.1 105.4 534.6C93 522.1 92.9 501.8 105.4 489.4L242.8 352L201.4 310.6C188.9 298.1 188.9 277.8 201.4 265.4C213.9 253 234.2 252.9 246.6 265.4L288 306.8L434.8 160L352 160C334.3 160 320 145.7 320 128C320 110.3 334.3 96 352 96L512 96z"/></svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M368 96C429.9 96 480 146.1 480 208L480 497.7C480 505.6 486.4 512 494.3 512C498.2 512 501.9 510.4 504.5 507.7L544.6 466.6L534.9 456.9C528 450 526 439.7 529.7 430.7C533.4 421.7 542.3 416 552 416L616 416L618.5 416.1C630.6 417.3 640 427.6 640 440L640 504L639.7 507.6C638.5 515.8 633 522.9 625.2 526.2C616.2 529.9 605.9 527.8 599 521L589.9 511.9L550.3 552.5C535.6 567.6 515.4 576.1 494.3 576.1C451 576.1 416 541 416 497.8L416 208C416 181.5 394.5 160 368 160C341.5 160 320 181.5 320 208L320 512C320 529.7 305.7 544 288 544C270.3 544 256 529.7 256 512L256 208C256 181.5 234.5 160 208 160C181.5 160 160 181.5 160 208L160 512C160 529.7 145.7 544 128 544C110.3 544 96 529.7 96 512L96 192C96 175.4 83.4 161.8 67.3 160.2L60.7 159.9C44.6 158.2 32 144.6 32 128C32 110.3 46.3 96 64 96C91 96 115.3 107.1 132.7 125C152.6 107 179 96 208 96C239.3 96 267.7 108.9 288 129.7C308.3 108.9 336.6 96 368 96z"/></svg>

After

Width:  |  Height:  |  Size: 962 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M464 48C481.7 48 496 62.3 496 80C496 138.5 467.4 190.3 423.5 222.3C476.7 256.4 512 316.1 512 384C512 490 426 576 320 576C214 576 128 490 128 384C128 316.1 163.3 256.4 216.5 222.3C172.6 190.3 144 138.5 144 80C144 62.3 158.3 48 176 48C193.7 48 208 62.3 208 80C208 141.9 258.1 192 320 192C381.9 192 432 141.9 432 80C432 62.3 446.3 48 464 48zM320 256C249.3 256 192 313.3 192 384C192 454.7 249.3 512 320 512C390.7 512 448 454.7 448 384C448 313.3 390.7 256 320 256z"/></svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M368 96C429.9 96 480 146.1 480 208L480 234.8C494.5 227.9 510.8 224 528 224C589.9 224 640 274.1 640 336C640 397.9 589.9 448 528 448L480 448L480 480C480 496.6 492.6 510.2 508.7 511.8L515.3 512.1C531.4 513.7 544 527.4 544 543.9C544 561.6 529.7 575.9 512 575.9C459 575.9 416 532.9 416 479.9L416 447.9L400 447.9C382.3 447.9 368 433.6 368 415.9C368 398.2 382.3 383.9 400 383.9L416 383.9L416 207.9C416 181.4 394.5 159.9 368 159.9C341.5 159.9 320 181.4 320 207.9L320 511.9C320 529.6 305.7 543.9 288 543.9C270.3 543.9 256 529.6 256 511.9L256 207.9C256 181.4 234.5 159.9 208 159.9C181.5 159.9 160 181.4 160 207.9L160 511.9C160 529.6 145.7 543.9 128 543.9C110.3 543.9 96 529.6 96 511.9L96 191.9C96 175.3 83.4 161.7 67.3 160.1L60.7 159.8C44.6 158.2 32 144.6 32 128C32 110.3 46.3 96 64 96C91 96 115.3 107.1 132.7 125C152.6 107 179 96 208 96C239.3 96 267.7 108.9 288 129.7C308.3 108.9 336.6 96 368 96zM528 288C501.5 288 480 309.5 480 336L480 384L528 384C554.5 384 576 362.5 576 336C576 309.5 554.5 288 528 288z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -27,18 +27,18 @@ const NatusWheel = (() => {
// ── Constants ────────────────────────────────────────────────────────────── // ── Constants ──────────────────────────────────────────────────────────────
const SIGNS = [ const SIGNS = [
{ name: 'Aries', symbol: '♈', element: 'Fire' }, { name: 'Aries', symbol: '♈', element: 'Fire', fa: 'aries' },
{ name: 'Taurus', symbol: '♉', element: 'Stone' }, { name: 'Taurus', symbol: '♉', element: 'Stone', fa: 'taurus' },
{ name: 'Gemini', symbol: '♊', element: 'Air' }, { name: 'Gemini', symbol: '♊', element: 'Air', fa: 'gemini' },
{ name: 'Cancer', symbol: '♋', element: 'Water' }, { name: 'Cancer', symbol: '♋', element: 'Water', fa: 'cancer' },
{ name: 'Leo', symbol: '♌', element: 'Fire' }, { name: 'Leo', symbol: '♌', element: 'Fire', fa: 'leo' },
{ name: 'Virgo', symbol: '♍', element: 'Stone' }, { name: 'Virgo', symbol: '♍', element: 'Stone', fa: 'virgo' },
{ name: 'Libra', symbol: '♎', element: 'Air' }, { name: 'Libra', symbol: '♎', element: 'Air', fa: 'libra' },
{ name: 'Scorpio', symbol: '♏', element: 'Water' }, { name: 'Scorpio', symbol: '♏', element: 'Water', fa: 'scorpio' },
{ name: 'Sagittarius', symbol: '♐', element: 'Fire' }, { name: 'Sagittarius', symbol: '♐', element: 'Fire', fa: 'sagittarius' },
{ name: 'Capricorn', symbol: '♑', element: 'Stone' }, { name: 'Capricorn', symbol: '♑', element: 'Stone', fa: 'capricorn' },
{ name: 'Aquarius', symbol: '♒', element: 'Air' }, { name: 'Aquarius', symbol: '♒', element: 'Air', fa: 'aquarius' },
{ name: 'Pisces', symbol: '♓', element: 'Water' }, { name: 'Pisces', symbol: '♓', element: 'Water', fa: 'pisces' },
]; ];
const PLANET_SYMBOLS = { const PLANET_SYMBOLS = {
@@ -46,6 +46,12 @@ const NatusWheel = (() => {
Jupiter: '♃', Saturn: '♄', Uranus: '♅', Neptune: '♆', Pluto: '♇', Jupiter: '♃', Saturn: '♄', Uranus: '♅', Neptune: '♆', Pluto: '♇',
}; };
// Alchemical element symbol → CSS modifier class suffix (matches rootvars palette)
const PLANET_ELEMENTS = {
Sun: 'au', Moon: 'ag', Mercury: 'hg', Venus: 'cu', Mars: 'fe',
Jupiter: 'sn', Saturn: 'pb', Uranus: 'u', Neptune: 'np', Pluto: 'pu',
};
// Aspect stroke colors remain in JS — they are data-driven, not stylistic. // Aspect stroke colors remain in JS — they are data-driven, not stylistic.
const ASPECT_COLORS = { const ASPECT_COLORS = {
Conjunction: 'var(--priYl, #f0e060)', Conjunction: 'var(--priYl, #f0e060)',
@@ -66,6 +72,9 @@ const NatusWheel = (() => {
let _svg = null; let _svg = null;
let _cx, _cy, _r; // centre + outer radius let _cx, _cy, _r; // centre + outer radius
// Cached SVG path strings keyed by sign name, populated by preload().
const _signPaths = {};
// Ring radii (fractions of _r, set in _layout) // Ring radii (fractions of _r, set in _layout)
let R = {}; let R = {};
@@ -170,16 +179,29 @@ const NatusWheel = (() => {
})) }))
.attr('class', `nw-sign--${sign.element.toLowerCase()}`); .attr('class', `nw-sign--${sign.element.toLowerCase()}`);
// Symbol at midpoint // Icon at midpoint
const midA = (sa + ea) / 2; const midA = (sa + ea) / 2;
sigGroup.append('text') const lx = _cx + R.labelR * Math.cos(midA);
.attr('x', _cx + R.labelR * Math.cos(midA)) const ly = _cy + R.labelR * Math.sin(midA);
.attr('y', _cy + R.labelR * Math.sin(midA)) const cr = _r * 0.065; // slightly larger than planet circles so icons breathe
.attr('text-anchor', 'middle') // scale the 640×640 icon viewBox down to 85% of the circle diameter
.attr('dominant-baseline', 'middle') const sf = (cr * 2 * 0.85) / 640;
.attr('font-size', `${_r * 0.095}px`)
.attr('class', 'nw-sign-label') // Colored circle behind icon
.text(sign.symbol); sigGroup.append('circle')
.attr('cx', lx)
.attr('cy', ly)
.attr('r', cr)
.attr('class', `nw-sign-icon-bg--${sign.element.toLowerCase()}`);
// Inline SVG path — translate origin to label centre, scale, re-centre icon
if (_signPaths[sign.name]) {
sigGroup.append('path')
.attr('d', _signPaths[sign.name])
.attr('transform',
`translate(${lx},${ly}) scale(${sf}) translate(-320,-320)`)
.attr('class', `nw-sign-icon nw-sign-icon--${sign.element.toLowerCase()}`);
}
}); });
} }
@@ -240,13 +262,15 @@ const NatusWheel = (() => {
Object.entries(data.planets).forEach(([name, pdata], idx) => { Object.entries(data.planets).forEach(([name, pdata], idx) => {
const finalA = _toAngle(pdata.degree, asc); const finalA = _toAngle(pdata.degree, asc);
const el = PLANET_ELEMENTS[name] || '';
// Circle behind symbol // Circle behind symbol
const circleBase = pdata.retrograde ? 'nw-planet-circle--rx' : 'nw-planet-circle';
const circle = planetGroup.append('circle') const circle = planetGroup.append('circle')
.attr('cx', _cx + R.planetR * Math.cos(ascAngle)) .attr('cx', _cx + R.planetR * Math.cos(ascAngle))
.attr('cy', _cy + R.planetR * Math.sin(ascAngle)) .attr('cy', _cy + R.planetR * Math.sin(ascAngle))
.attr('r', _r * 0.05) .attr('r', _r * 0.05)
.attr('class', pdata.retrograde ? 'nw-planet-circle--rx' : 'nw-planet-circle'); .attr('class', el ? `${circleBase} nw-planet--${el}` : circleBase);
// Symbol // Symbol
const label = planetGroup.append('text') const label = planetGroup.append('text')
@@ -256,7 +280,7 @@ const NatusWheel = (() => {
.attr('dominant-baseline', 'middle') .attr('dominant-baseline', 'middle')
.attr('dy', '0.1em') .attr('dy', '0.1em')
.attr('font-size', `${_r * 0.09}px`) .attr('font-size', `${_r * 0.09}px`)
.attr('class', 'nw-planet-label') .attr('class', el ? `nw-planet-label nw-planet-label--${el}` : 'nw-planet-label')
.text(PLANET_SYMBOLS[name] || name[0]); .text(PLANET_SYMBOLS[name] || name[0]);
// Retrograde indicator // Retrograde indicator
@@ -358,6 +382,37 @@ const NatusWheel = (() => {
// ── Public API ──────────────────────────────────────────────────────────── // ── Public API ────────────────────────────────────────────────────────────
/**
* Preload zodiac sign SVGs from `basePath` (default: same dir as this script).
* Returns a Promise that resolves when all 12 are cached in _signPaths.
* Safe to call multiple times; re-fetches every call so swapped files are picked up.
*
* To swap a sign icon: replace the corresponding .svg file in zodiac-signs/
* and call NatusWheel.preload() before the next draw().
*/
async function preload(basePath) {
const base = basePath ||
(() => {
const scripts = document.querySelectorAll('script[src]');
for (const s of scripts) {
if (s.src.includes('natus-wheel')) {
return s.src.replace(/natus-wheel\.js.*$/, 'icons/zodiac-signs/');
}
}
return '/static/apps/gameboard/icons/zodiac-signs/';
})();
await Promise.all(SIGNS.map(async sign => {
const url = base + sign.name.toLowerCase() + '.svg';
const resp = await window.fetch(url);
if (!resp.ok) { console.warn(`NatusWheel: failed to load ${url}`); return; }
const text = await resp.text();
const doc = new DOMParser().parseFromString(text, 'image/svg+xml');
const path = doc.querySelector('path');
if (path) _signPaths[sign.name] = path.getAttribute('d');
}));
}
function draw(svgEl, data) { function draw(svgEl, data) {
_svg = d3.select(svgEl); _svg = d3.select(svgEl);
_svg.selectAll('*').remove(); _svg.selectAll('*').remove();
@@ -393,5 +448,5 @@ const NatusWheel = (() => {
if (_svg) _svg.selectAll('*').remove(); if (_svg) _svg.selectAll('*').remove();
} }
return { draw, redraw, clear }; return { preload, draw, redraw, clear };
})(); })();

View File

@@ -359,35 +359,76 @@ html.natus-open .natus-modal-wrap {
.nw-outer-ring { .nw-outer-ring {
fill: none; fill: none;
stroke: rgba(var(--quaUser), 0.6); stroke: rgba(var(--terUser), 1);
stroke-width: 1px; stroke-width: 1.5px;
} }
.nw-inner-disc { .nw-inner-disc {
fill: rgba(var(--quaUser), 0.6); fill: rgba(var(--quaUser), 0.6);
stroke: rgba(var(--terUser), 1);
stroke-width: 0.75px;
} }
// Axes (ASC / DSC / MC / IC) // Axes (ASC / DSC / MC / IC)
.nw-axis-line { stroke: rgba(var(--secUser), 1); stroke-width: 1.5px; } .nw-axis-line { stroke: rgba(var(--secUser), 1); stroke-width: 1.5px; }
.nw-axis-label { fill: rgba(var(--secUser), 1); } .nw-axis-label { fill: rgba(var(--secUser), 1); }
// Sign ring — vars are RGB tuples ("R, G, B"), must be wrapped in rgba() // Sign ring — uniform --priMe bg at half opacity
.nw-sign--fire { fill: rgba(var(--priRd, 192, 64, 64), 0.75); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; } .nw-sign--fire,
.nw-sign--stone { fill: rgba(var(--priFs, 122, 96, 64), 0.75); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; } .nw-sign--stone,
.nw-sign--air { fill: rgba(var(--priCy, 64, 144, 176), 0.75); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; } .nw-sign--air,
.nw-sign--water { fill: rgba(var(--priId, 80, 80, 160), 0.75); stroke: rgba(var(--quaUser), 1); stroke-width: 0.5px; } .nw-sign--water {
.nw-sign-label { fill: rgba(var(--secUser), 1); } fill: rgba(var(--priMe), 0.25);
stroke: rgba(var(--terUser), 1);
stroke-width: 0.75px;
}
// House ring // Icon bg circles — element fill + matching border
.nw-house-cusp { stroke: rgba(var(--quaUser), 0.8); stroke-width: 0.8px; } .nw-sign-icon-bg--fire { fill: rgba(var(--terRd), 1); stroke: rgba(var(--priOr), 1); stroke-width: 1px; }
.nw-house-num { fill: rgba(var(--quiUser), 1); } .nw-sign-icon-bg--stone { fill: rgba(var(--priYl), 1); stroke: rgba(var(--priLm), 1); stroke-width: 1px; }
.nw-house-fill--even { fill: rgba(var(--quaUser), 0.45); } .nw-sign-icon-bg--air { fill: rgba(var(--terGn), 1); stroke: rgba(var(--priTk), 1); stroke-width: 1px; }
.nw-house-fill--odd { fill: rgba(var(--quiUser), 0.35); } .nw-sign-icon-bg--water { fill: rgba(var(--priCy), 1); stroke: rgba(var(--priBl), 1); stroke-width: 1px; }
// Planets // Inline SVG path icons — per-element colors
.nw-planet-circle { fill: rgba(var(--quiUser), 1); } .nw-sign-icon--fire { fill: rgba(var(--priOr), 1); }
.nw-planet-circle--rx { fill: rgba(var(--quiUser), 1); } .nw-sign-icon--stone { fill: rgba(var(--terLm), 1); }
.nw-planet-label { fill: rgba(var(--quaUser), 1); stroke: rgba(var(--quaUser), 0.6); stroke-width: 0.4px; paint-order: stroke fill; } .nw-sign-icon--air { fill: rgba(var(--priTk), 1); }
.nw-sign-icon--water { fill: rgba(var(--terBl), 1); }
// House ring — uniform --priFs bg
.nw-house-cusp { stroke: rgba(var(--terUser), 1); stroke-width: 1.2px; }
.nw-house-num { fill: rgba(var(--ninUser), 1); }
.nw-house-fill--even { fill: rgba(var(--priFs), 0.25); stroke: rgba(var(--terUser), 1); stroke-width: 0.75px; }
.nw-house-fill--odd { fill: rgba(var(--priFs), 0.25); stroke: rgba(var(--terUser), 1); stroke-width: 0.75px; }
// Planets — base geometry
.nw-planet-circle,
.nw-planet-circle--rx { stroke-width: 1px; }
.nw-planet-label { stroke-width: 0.4px; paint-order: stroke fill; }
// Per-planet circle: fill = ternary, border = senary
.nw-planet--au { fill: rgba(var(--terAu), 1); stroke: rgba(var(--sixAu), 1); } // Sun
.nw-planet--ag { fill: rgba(var(--terAg), 1); stroke: rgba(var(--sixAg), 1); } // Moon
.nw-planet--hg { fill: rgba(var(--terHg), 1); stroke: rgba(var(--sixHg), 1); } // Mercury
.nw-planet--cu { fill: rgba(var(--terCu), 1); stroke: rgba(var(--sixCu), 1); } // Venus
.nw-planet--fe { fill: rgba(var(--terFe), 1); stroke: rgba(var(--sixFe), 1); } // Mars
.nw-planet--sn { fill: rgba(var(--terSn), 1); stroke: rgba(var(--sixSn), 1); } // Jupiter
.nw-planet--pb { fill: rgba(var(--terPb), 1); stroke: rgba(var(--sixPb), 1); } // Saturn
.nw-planet--u { fill: rgba(var(--terU), 1); stroke: rgba(var(--sixU), 1); } // Uranus
.nw-planet--np { fill: rgba(var(--terNp), 1); stroke: rgba(var(--sixNp), 1); } // Neptune
.nw-planet--pu { fill: rgba(var(--terPu), 1); stroke: rgba(var(--sixPu), 1); } // Pluto
// Per-planet label: fill + stroke halo = senary
.nw-planet-label--au { fill: rgba(var(--sixAu), 1); stroke: rgba(var(--sixAu), 0.6); }
.nw-planet-label--ag { fill: rgba(var(--sixAg), 1); stroke: rgba(var(--sixAg), 0.6); }
.nw-planet-label--hg { fill: rgba(var(--sixHg), 1); stroke: rgba(var(--sixHg), 0.6); }
.nw-planet-label--cu { fill: rgba(var(--sixCu), 1); stroke: rgba(var(--sixCu), 0.6); }
.nw-planet-label--fe { fill: rgba(var(--sixFe), 1); stroke: rgba(var(--sixFe), 0.6); }
.nw-planet-label--sn { fill: rgba(var(--sixSn), 1); stroke: rgba(var(--sixSn), 0.6); }
.nw-planet-label--pb { fill: rgba(var(--sixPb), 1); stroke: rgba(var(--sixPb), 0.6); }
.nw-planet-label--u { fill: rgba(var(--sixU), 1); stroke: rgba(var(--sixU), 0.6); }
.nw-planet-label--np { fill: rgba(var(--sixNp), 1); stroke: rgba(var(--sixNp), 0.6); }
.nw-planet-label--pu { fill: rgba(var(--sixPu), 1); stroke: rgba(var(--sixPu), 0.6); }
.nw-rx { fill: rgba(var(--terUser), 1); } .nw-rx { fill: rgba(var(--terUser), 1); }
// Aspects // Aspects

View File

@@ -130,6 +130,10 @@
const PLACE_DELAY = 400; // ms — Nominatim polite rate const PLACE_DELAY = 400; // ms — Nominatim polite rate
const CHART_DELAY = 300; // ms — chart preview debounce const CHART_DELAY = 300; // ms — chart preview debounce
// Preload zodiac SVG icons eagerly — they'll be cached before any draw() call.
// To swap an icon, replace the .svg file in zodiac-signs/ and hard-refresh.
NatusWheel.preload();
// ── localStorage persistence ────────────────────────────────────────────── // ── localStorage persistence ──────────────────────────────────────────────
// Key scoped to room so multiple rooms don't clobber each other. // Key scoped to room so multiple rooms don't clobber each other.