Practical advice:
diff --git a/src/app/stars.css b/src/app/stars.css
new file mode 100644
index 0000000..403dc97
--- /dev/null
+++ b/src/app/stars.css
@@ -0,0 +1,76 @@
+/* Pre-generated star field — box-shadow technique */
+/* Adapted from https://codepen.io/sarazond/pen/LYGbwj */
+
+.stars-sky {
+ background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
+}
+
+.stars-layer {
+ position: absolute;
+ background: transparent;
+}
+
+.stars-layer::after {
+ content: "";
+ position: absolute;
+ top: 2000px;
+ background: transparent;
+}
+
+/* Layer 1: small stars (1px, slowest) */
+.stars-sm {
+ width: 1px;
+ height: 1px;
+ box-shadow: 1309px 228px #FFF, 51px 1518px #FFF, 563px 501px #FFF, 457px 285px #FFF, 1508px 209px #FFF, 1385px 1516px #FFF, 1827px 1116px #FFF, 178px 1209px #FFF, 864px 65px #FFF, 61px 191px #FFF, 447px 476px #FFF, 1034px 1232px #FFF, 54px 1149px #FFF, 407px 1466px #FFF, 1330px 1436px #FFF, 1116px 859px #FFF, 451px 919px #FFF, 1206px 569px #FFF, 1657px 1780px #FFF, 13px 1554px #FFF, 1650px 326px #FFF, 1429px 865px #FFF, 696px 569px #FFF, 318px 440px #FFF, 1960px 1563px #FFF, 689px 209px #FFF, 189px 778px #FFF, 198px 735px #FFF, 1735px 704px #FFF, 1236px 541px #FFF, 1652px 88px #FFF, 1494px 940px #FFF, 1098px 255px #FFF, 1992px 1888px #FFF, 775px 161px #FFF, 1130px 600px #FFF, 1698px 1287px #FFF, 1266px 1813px #FFF, 1764px 740px #FFF, 1182px 393px #FFF, 1442px 142px #FFF, 93px 1354px #FFF, 466px 1583px #FFF, 592px 163px #FFF, 1751px 476px #FFF, 1774px 206px #FFF, 778px 569px #FFF, 928px 1301px #FFF, 1708px 747px #FFF, 333px 758px #FFF, 727px 429px #FFF, 1372px 546px #FFF, 1437px 1918px #FFF, 1399px 1327px #FFF, 146px 1247px #FFF, 1300px 350px #FFF, 1093px 1493px #FFF, 501px 334px #FFF, 946px 777px #FFF, 552px 1895px #FFF, 1310px 1409px #FFF, 1140px 449px #FFF, 1402px 664px #FFF, 1726px 1573px #FFF, 1589px 114px #FFF, 469px 1683px #FFF, 65px 1648px #FFF, 646px 821px #FFF, 548px 135px #FFF, 432px 1870px #FFF, 1931px 1161px #FFF, 1794px 1470px #FFF, 644px 435px #FFF, 1342px 1022px #FFF, 810px 1811px #FFF, 1872px 1316px #FFF, 939px 292px #FFF, 542px 285px #FFF, 505px 1525px #FFF, 1149px 1103px #FFF, 538px 1529px #FFF, 1197px 877px #FFF, 1838px 1195px #FFF, 817px 741px #FFF, 449px 283px #FFF, 1043px 1010px #FFF, 186px 1547px #FFF, 96px 1763px #FFF, 224px 313px #FFF, 1285px 327px #FFF, 1622px 1393px #FFF, 864px 1221px #FFF, 130px 788px #FFF, 781px 1220px #FFF, 958px 1083px #FFF, 514px 1988px #FFF, 1133px 1762px #FFF, 1930px 23px #FFF, 1393px 1476px #FFF, 234px 1396px #FFF, 1812px 1099px #FFF, 1537px 546px #FFF, 1574px 1312px #FFF, 696px 228px #FFF, 601px 890px #FFF, 323px 929px #FFF, 6px 1953px #FFF, 1478px 1793px #FFF, 1473px 539px #FFF, 1990px 1025px #FFF, 1560px 365px #FFF, 1039px 1869px #FFF, 217px 1782px #FFF, 1280px 611px #FFF, 1723px 1308px #FFF, 1039px 1247px #FFF, 407px 313px #FFF, 765px 1561px #FFF, 330px 1104px #FFF, 1952px 1594px #FFF, 1889px 1086px #FFF, 1881px 1px #FFF, 1226px 663px #FFF, 1000px 39px #FFF, 229px 1902px #FFF, 743px 1799px #FFF, 1703px 1652px #FFF, 629px 490px #FFF, 118px 493px #FFF, 1798px 1161px #FFF, 1939px 161px #FFF, 175px 1498px #FFF, 995px 1671px #FFF, 141px 1557px #FFF, 1090px 1568px #FFF, 257px 262px #FFF, 1351px 973px #FFF, 1939px 1125px #FFF, 338px 542px #FFF, 1080px 1786px #FFF, 1242px 866px #FFF, 1975px 433px #FFF, 1902px 1104px #FFF, 1546px 1494px #FFF, 1412px 411px #FFF, 1460px 638px #FFF, 817px 1375px #FFF, 1330px 764px #FFF, 897px 1842px #FFF, 1059px 924px #FFF, 247px 507px #FFF, 460px 131px #FFF, 692px 43px #FFF, 1204px 1134px #FFF, 471px 1205px #FFF, 451px 14px #FFF, 145px 1449px #FFF, 1292px 120px #FFF, 468px 138px #FFF, 1854px 64px #FFF, 1760px 676px #FFF, 145px 1052px #FFF, 487px 570px #FFF, 1370px 994px #FFF, 438px 1104px #FFF, 270px 1481px #FFF, 1915px 1807px #FFF, 1169px 1180px #FFF, 968px 497px #FFF, 1606px 968px #FFF, 1653px 833px #FFF, 389px 193px #FFF, 198px 1349px #FFF, 882px 725px #FFF, 867px 841px #FFF, 956px 1769px #FFF, 1493px 110px #FFF, 1379px 1338px #FFF, 1323px 201px #FFF, 124px 824px #FFF, 1491px 694px #FFF, 1639px 1764px #FFF, 223px 509px #FFF, 392px 389px #FFF, 1098px 918px #FFF, 287px 864px #FFF, 375px 570px #FFF, 947px 511px #FFF, 1790px 1890px #FFF, 154px 907px #FFF, 1654px 1764px #FFF, 1753px 1127px #FFF, 200px 103px #FFF, 1335px 1107px #FFF, 1712px 30px #FFF, 1984px 191px #FFF, 1897px 1543px #FFF, 1738px 484px #FFF, 340px 832px #FFF, 994px 985px #FFF, 437px 1770px #FFF, 821px 1848px #FFF, 120px 337px #FFF, 776px 4px #FFF, 799px 543px #FFF, 1897px 1605px #FFF, 1607px 931px #FFF, 584px 866px #FFF, 1426px 1960px #FFF, 1496px 1604px #FFF, 1138px 1355px #FFF, 1471px 996px #FFF, 317px 388px #FFF, 607px 445px #FFF, 1983px 119px #FFF, 1186px 1506px #FFF, 1110px 124px #FFF, 1531px 642px #FFF, 117px 102px #FFF, 1196px 976px #FFF, 1029px 1882px #FFF, 1746px 1087px #FFF, 322px 116px #FFF, 1967px 1040px #FFF, 164px 1743px #FFF, 380px 140px #FFF, 1218px 139px #FFF, 1382px 1765px #FFF, 481px 826px #FFF, 245px 1928px #FFF, 1823px 1166px #FFF, 504px 1185px #FFF, 1217px 81px #FFF, 1268px 167px #FFF, 858px 1346px #FFF, 1195px 1157px #FFF, 1070px 647px #FFF, 1914px 534px #FFF, 418px 1371px #FFF, 1466px 643px #FFF, 488px 543px #FFF, 810px 268px #FFF, 1375px 1321px #FFF, 614px 936px #FFF, 647px 1902px #FFF, 1539px 1916px #FFF, 148px 19px #FFF, 938px 1272px #FFF, 1153px 204px #FFF, 150px 1101px #FFF, 436px 1036px #FFF, 543px 271px #FFF, 1911px 714px #FFF, 1804px 140px #FFF, 1800px 500px #FFF, 756px 583px #FFF, 323px 897px #FFF, 1707px 1112px #FFF, 1440px 619px #FFF, 1252px 1652px #FFF, 1339px 1083px #FFF, 16px 1367px #FFF, 1673px 1135px #FFF, 613px 1908px #FFF, 1358px 212px #FFF, 1922px 1798px #FFF, 275px 541px #FFF, 236px 1822px #FFF, 219px 1520px #FFF, 1133px 318px #FFF, 557px 577px #FFF, 1238px 431px #FFF, 1469px 702px #FFF, 416px 1407px #FFF, 1298px 1746px #FFF, 540px 1035px #FFF, 1000px 514px #FFF, 1854px 1859px #FFF, 1732px 104px #FFF, 189px 1299px #FFF, 867px 1698px #FFF, 566px 90px #FFF, 7px 683px #FFF, 1579px 267px #FFF, 1304px 536px #FFF, 330px 1518px #FFF, 904px 1129px #FFF, 1445px 875px #FFF, 1148px 19px #FFF, 229px 154px #FFF, 1935px 1808px #FFF, 1415px 1851px #FFF, 305px 1117px #FFF, 73px 1709px #FFF, 756px 1192px #FFF, 1131px 303px #FFF, 880px 261px #FFF, 85px 631px #FFF, 746px 1841px #FFF, 1910px 1630px #FFF;
+ animation: animStar 50s linear infinite;
+}
+.stars-sm::after {
+ width: 1px;
+ height: 1px;
+ box-shadow: 1309px 228px #FFF, 51px 1518px #FFF, 563px 501px #FFF, 457px 285px #FFF, 1508px 209px #FFF, 1385px 1516px #FFF, 1827px 1116px #FFF, 178px 1209px #FFF, 864px 65px #FFF, 61px 191px #FFF, 447px 476px #FFF, 1034px 1232px #FFF, 54px 1149px #FFF, 407px 1466px #FFF, 1330px 1436px #FFF, 1116px 859px #FFF, 451px 919px #FFF, 1206px 569px #FFF, 1657px 1780px #FFF, 13px 1554px #FFF, 1650px 326px #FFF, 1429px 865px #FFF, 696px 569px #FFF, 318px 440px #FFF, 1960px 1563px #FFF, 689px 209px #FFF, 189px 778px #FFF, 198px 735px #FFF, 1735px 704px #FFF, 1236px 541px #FFF, 1652px 88px #FFF, 1494px 940px #FFF, 1098px 255px #FFF, 1992px 1888px #FFF, 775px 161px #FFF, 1130px 600px #FFF, 1698px 1287px #FFF, 1266px 1813px #FFF, 1764px 740px #FFF, 1182px 393px #FFF, 1442px 142px #FFF, 93px 1354px #FFF, 466px 1583px #FFF, 592px 163px #FFF, 1751px 476px #FFF, 1774px 206px #FFF, 778px 569px #FFF, 928px 1301px #FFF, 1708px 747px #FFF, 333px 758px #FFF, 727px 429px #FFF, 1372px 546px #FFF, 1437px 1918px #FFF, 1399px 1327px #FFF, 146px 1247px #FFF, 1300px 350px #FFF, 1093px 1493px #FFF, 501px 334px #FFF, 946px 777px #FFF, 552px 1895px #FFF, 1310px 1409px #FFF, 1140px 449px #FFF, 1402px 664px #FFF, 1726px 1573px #FFF, 1589px 114px #FFF, 469px 1683px #FFF, 65px 1648px #FFF, 646px 821px #FFF, 548px 135px #FFF, 432px 1870px #FFF, 1931px 1161px #FFF, 1794px 1470px #FFF, 644px 435px #FFF, 1342px 1022px #FFF, 810px 1811px #FFF, 1872px 1316px #FFF, 939px 292px #FFF, 542px 285px #FFF, 505px 1525px #FFF, 1149px 1103px #FFF, 538px 1529px #FFF, 1197px 877px #FFF, 1838px 1195px #FFF, 817px 741px #FFF, 449px 283px #FFF, 1043px 1010px #FFF, 186px 1547px #FFF, 96px 1763px #FFF, 224px 313px #FFF, 1285px 327px #FFF, 1622px 1393px #FFF, 864px 1221px #FFF, 130px 788px #FFF, 781px 1220px #FFF, 958px 1083px #FFF, 514px 1988px #FFF, 1133px 1762px #FFF, 1930px 23px #FFF, 1393px 1476px #FFF, 234px 1396px #FFF, 1812px 1099px #FFF, 1537px 546px #FFF, 1574px 1312px #FFF, 696px 228px #FFF, 601px 890px #FFF, 323px 929px #FFF, 6px 1953px #FFF, 1478px 1793px #FFF, 1473px 539px #FFF, 1990px 1025px #FFF, 1560px 365px #FFF, 1039px 1869px #FFF, 217px 1782px #FFF, 1280px 611px #FFF, 1723px 1308px #FFF, 1039px 1247px #FFF, 407px 313px #FFF, 765px 1561px #FFF, 330px 1104px #FFF, 1952px 1594px #FFF, 1889px 1086px #FFF, 1881px 1px #FFF, 1226px 663px #FFF, 1000px 39px #FFF, 229px 1902px #FFF, 743px 1799px #FFF, 1703px 1652px #FFF, 629px 490px #FFF, 118px 493px #FFF, 1798px 1161px #FFF, 1939px 161px #FFF, 175px 1498px #FFF, 995px 1671px #FFF, 141px 1557px #FFF, 1090px 1568px #FFF, 257px 262px #FFF, 1351px 973px #FFF, 1939px 1125px #FFF, 338px 542px #FFF, 1080px 1786px #FFF, 1242px 866px #FFF, 1975px 433px #FFF, 1902px 1104px #FFF, 1546px 1494px #FFF, 1412px 411px #FFF, 1460px 638px #FFF, 817px 1375px #FFF, 1330px 764px #FFF, 897px 1842px #FFF, 1059px 924px #FFF, 247px 507px #FFF, 460px 131px #FFF, 692px 43px #FFF, 1204px 1134px #FFF, 471px 1205px #FFF, 451px 14px #FFF, 145px 1449px #FFF, 1292px 120px #FFF, 468px 138px #FFF, 1854px 64px #FFF, 1760px 676px #FFF, 145px 1052px #FFF, 487px 570px #FFF, 1370px 994px #FFF, 438px 1104px #FFF, 270px 1481px #FFF, 1915px 1807px #FFF, 1169px 1180px #FFF, 968px 497px #FFF, 1606px 968px #FFF, 1653px 833px #FFF, 389px 193px #FFF, 198px 1349px #FFF, 882px 725px #FFF, 867px 841px #FFF, 956px 1769px #FFF, 1493px 110px #FFF, 1379px 1338px #FFF, 1323px 201px #FFF, 124px 824px #FFF, 1491px 694px #FFF, 1639px 1764px #FFF, 223px 509px #FFF, 392px 389px #FFF, 1098px 918px #FFF, 287px 864px #FFF, 375px 570px #FFF, 947px 511px #FFF, 1790px 1890px #FFF, 154px 907px #FFF, 1654px 1764px #FFF, 1753px 1127px #FFF, 200px 103px #FFF, 1335px 1107px #FFF, 1712px 30px #FFF, 1984px 191px #FFF, 1897px 1543px #FFF, 1738px 484px #FFF, 340px 832px #FFF, 994px 985px #FFF, 437px 1770px #FFF, 821px 1848px #FFF, 120px 337px #FFF, 776px 4px #FFF, 799px 543px #FFF, 1897px 1605px #FFF, 1607px 931px #FFF, 584px 866px #FFF, 1426px 1960px #FFF, 1496px 1604px #FFF, 1138px 1355px #FFF, 1471px 996px #FFF, 317px 388px #FFF, 607px 445px #FFF, 1983px 119px #FFF, 1186px 1506px #FFF, 1110px 124px #FFF, 1531px 642px #FFF, 117px 102px #FFF, 1196px 976px #FFF, 1029px 1882px #FFF, 1746px 1087px #FFF, 322px 116px #FFF, 1967px 1040px #FFF, 164px 1743px #FFF, 380px 140px #FFF, 1218px 139px #FFF, 1382px 1765px #FFF, 481px 826px #FFF, 245px 1928px #FFF, 1823px 1166px #FFF, 504px 1185px #FFF, 1217px 81px #FFF, 1268px 167px #FFF, 858px 1346px #FFF, 1195px 1157px #FFF, 1070px 647px #FFF, 1914px 534px #FFF, 418px 1371px #FFF, 1466px 643px #FFF, 488px 543px #FFF, 810px 268px #FFF, 1375px 1321px #FFF, 614px 936px #FFF, 647px 1902px #FFF, 1539px 1916px #FFF, 148px 19px #FFF, 938px 1272px #FFF, 1153px 204px #FFF, 150px 1101px #FFF, 436px 1036px #FFF, 543px 271px #FFF, 1911px 714px #FFF, 1804px 140px #FFF, 1800px 500px #FFF, 756px 583px #FFF, 323px 897px #FFF, 1707px 1112px #FFF, 1440px 619px #FFF, 1252px 1652px #FFF, 1339px 1083px #FFF, 16px 1367px #FFF, 1673px 1135px #FFF, 613px 1908px #FFF, 1358px 212px #FFF, 1922px 1798px #FFF, 275px 541px #FFF, 236px 1822px #FFF, 219px 1520px #FFF, 1133px 318px #FFF, 557px 577px #FFF, 1238px 431px #FFF, 1469px 702px #FFF, 416px 1407px #FFF, 1298px 1746px #FFF, 540px 1035px #FFF, 1000px 514px #FFF, 1854px 1859px #FFF, 1732px 104px #FFF, 189px 1299px #FFF, 867px 1698px #FFF, 566px 90px #FFF, 7px 683px #FFF, 1579px 267px #FFF, 1304px 536px #FFF, 330px 1518px #FFF, 904px 1129px #FFF, 1445px 875px #FFF, 1148px 19px #FFF, 229px 154px #FFF, 1935px 1808px #FFF, 1415px 1851px #FFF, 305px 1117px #FFF, 73px 1709px #FFF, 756px 1192px #FFF, 1131px 303px #FFF, 880px 261px #FFF, 85px 631px #FFF, 746px 1841px #FFF, 1910px 1630px #FFF;
+}
+
+/* Layer 2: medium stars (2px, medium) */
+.stars-md {
+ width: 2px;
+ height: 2px;
+ box-shadow: 1991px 1762px #FFF, 81px 1840px #FFF, 732px 430px #FFF, 1396px 511px #FFF, 1365px 210px #FFF, 724px 1597px #FFF, 1146px 1810px #FFF, 1791px 832px #FFF, 1994px 1271px #FFF, 1534px 316px #FFF, 1895px 1904px #FFF, 484px 1770px #FFF, 332px 1999px #FFF, 1638px 1660px #FFF, 362px 1805px #FFF, 844px 50px #FFF, 367px 1508px #FFF, 1893px 680px #FFF, 1602px 1906px #FFF, 843px 1642px #FFF, 1371px 1769px #FFF, 1505px 1660px #FFF, 508px 546px #FFF, 326px 1612px #FFF, 1436px 221px #FFF, 783px 1786px #FFF, 79px 1758px #FFF, 963px 455px #FFF, 408px 1672px #FFF, 1880px 942px #FFF, 716px 625px #FFF, 1680px 1628px #FFF, 1784px 466px #FFF, 456px 48px #FFF, 1351px 395px #FFF, 816px 672px #FFF, 570px 1770px #FFF, 142px 1980px #FFF, 1583px 571px #FFF, 719px 1313px #FFF, 1043px 818px #FFF, 1391px 1727px #FFF, 1098px 678px #FFF, 1923px 56px #FFF, 236px 1796px #FFF, 1987px 534px #FFF, 365px 1189px #FFF, 1971px 543px #FFF, 78px 222px #FFF, 1221px 889px #FFF, 707px 1492px #FFF, 1610px 642px #FFF, 893px 1241px #FFF, 1047px 236px #FFF, 788px 1842px #FFF, 1180px 389px #FFF, 521px 90px #FFF, 1451px 893px #FFF, 3px 1064px #FFF, 1895px 1651px #FFF, 1102px 1406px #FFF, 1473px 1925px #FFF, 1519px 1509px #FFF, 1373px 403px #FFF, 745px 883px #FFF, 143px 1943px #FFF, 1360px 1885px #FFF, 676px 1276px #FFF, 642px 1358px #FFF, 1736px 255px #FFF, 1474px 1843px #FFF, 615px 1038px #FFF, 633px 1365px #FFF, 836px 668px #FFF, 824px 1427px #FFF, 605px 1135px #FFF, 260px 392px #FFF, 861px 1361px #FFF, 1925px 776px #FFF, 1387px 1532px #FFF, 1848px 356px #FFF, 1260px 1165px #FFF, 616px 831px #FFF, 1122px 1707px #FFF, 0px 622px #FFF, 587px 430px #FFF, 880px 1609px #FFF, 1187px 1242px #FFF, 1340px 659px #FFF, 952px 904px #FFF, 905px 1383px #FFF, 437px 1046px #FFF, 969px 1625px #FFF, 1845px 1965px #FFF, 1630px 1507px #FFF, 347px 1349px #FFF, 173px 581px #FFF, 1055px 1359px #FFF, 1296px 1268px #FFF, 686px 191px #FFF;
+ animation: animStar 100s linear infinite;
+}
+.stars-md::after {
+ width: 2px;
+ height: 2px;
+ box-shadow: 1991px 1762px #FFF, 81px 1840px #FFF, 732px 430px #FFF, 1396px 511px #FFF, 1365px 210px #FFF, 724px 1597px #FFF, 1146px 1810px #FFF, 1791px 832px #FFF, 1994px 1271px #FFF, 1534px 316px #FFF, 1895px 1904px #FFF, 484px 1770px #FFF, 332px 1999px #FFF, 1638px 1660px #FFF, 362px 1805px #FFF, 844px 50px #FFF, 367px 1508px #FFF, 1893px 680px #FFF, 1602px 1906px #FFF, 843px 1642px #FFF, 1371px 1769px #FFF, 1505px 1660px #FFF, 508px 546px #FFF, 326px 1612px #FFF, 1436px 221px #FFF, 783px 1786px #FFF, 79px 1758px #FFF, 963px 455px #FFF, 408px 1672px #FFF, 1880px 942px #FFF, 716px 625px #FFF, 1680px 1628px #FFF, 1784px 466px #FFF, 456px 48px #FFF, 1351px 395px #FFF, 816px 672px #FFF, 570px 1770px #FFF, 142px 1980px #FFF, 1583px 571px #FFF, 719px 1313px #FFF, 1043px 818px #FFF, 1391px 1727px #FFF, 1098px 678px #FFF, 1923px 56px #FFF, 236px 1796px #FFF, 1987px 534px #FFF, 365px 1189px #FFF, 1971px 543px #FFF, 78px 222px #FFF, 1221px 889px #FFF, 707px 1492px #FFF, 1610px 642px #FFF, 893px 1241px #FFF, 1047px 236px #FFF, 788px 1842px #FFF, 1180px 389px #FFF, 521px 90px #FFF, 1451px 893px #FFF, 3px 1064px #FFF, 1895px 1651px #FFF, 1102px 1406px #FFF, 1473px 1925px #FFF, 1519px 1509px #FFF, 1373px 403px #FFF, 745px 883px #FFF, 143px 1943px #FFF, 1360px 1885px #FFF, 676px 1276px #FFF, 642px 1358px #FFF, 1736px 255px #FFF, 1474px 1843px #FFF, 615px 1038px #FFF, 633px 1365px #FFF, 836px 668px #FFF, 824px 1427px #FFF, 605px 1135px #FFF, 260px 392px #FFF, 861px 1361px #FFF, 1925px 776px #FFF, 1387px 1532px #FFF, 1848px 356px #FFF, 1260px 1165px #FFF, 616px 831px #FFF, 1122px 1707px #FFF, 0px 622px #FFF, 587px 430px #FFF, 880px 1609px #FFF, 1187px 1242px #FFF, 1340px 659px #FFF, 952px 904px #FFF, 905px 1383px #FFF, 437px 1046px #FFF, 969px 1625px #FFF, 1845px 1965px #FFF, 1630px 1507px #FFF, 347px 1349px #FFF, 173px 581px #FFF, 1055px 1359px #FFF, 1296px 1268px #FFF, 686px 191px #FFF;
+}
+
+/* Layer 3: large stars (3px, fastest) */
+.stars-lg {
+ width: 3px;
+ height: 3px;
+ box-shadow: 1676px 1949px #FFF, 1538px 481px #FFF, 1377px 635px #FFF, 460px 1651px #FFF, 407px 301px #FFF, 50px 94px #FFF, 501px 973px #FFF, 1251px 1740px #FFF, 1573px 149px #FFF, 932px 848px #FFF, 1814px 1289px #FFF, 1178px 398px #FFF, 1471px 1426px #FFF, 786px 1012px #FFF, 818px 499px #FFF, 302px 1343px #FFF, 1408px 11px #FFF, 1828px 1537px #FFF, 1762px 1577px #FFF, 1813px 218px #FFF, 1594px 870px #FFF, 448px 360px #FFF, 1646px 1961px #FFF, 1425px 1060px #FFF, 951px 102px #FFF, 1141px 510px #FFF, 1878px 1737px #FFF, 248px 934px #FFF, 273px 1641px #FFF, 951px 1367px #FFF, 1087px 1144px #FFF, 1219px 649px #FFF, 1945px 1546px #FFF, 1825px 906px #FFF, 1254px 1669px #FFF, 1473px 1826px #FFF, 1033px 873px #FFF, 1701px 1857px #FFF, 1122px 913px #FFF, 1837px 325px #FFF, 1523px 1764px #FFF, 972px 921px #FFF, 530px 1539px #FFF, 506px 1720px #FFF, 1305px 567px #FFF, 1568px 1592px #FFF, 1067px 992px #FFF, 1283px 489px #FFF, 562px 900px #FFF, 158px 1461px #FFF;
+ animation: animStar 150s linear infinite;
+}
+.stars-lg::after {
+ width: 3px;
+ height: 3px;
+ box-shadow: 1676px 1949px #FFF, 1538px 481px #FFF, 1377px 635px #FFF, 460px 1651px #FFF, 407px 301px #FFF, 50px 94px #FFF, 501px 973px #FFF, 1251px 1740px #FFF, 1573px 149px #FFF, 932px 848px #FFF, 1814px 1289px #FFF, 1178px 398px #FFF, 1471px 1426px #FFF, 786px 1012px #FFF, 818px 499px #FFF, 302px 1343px #FFF, 1408px 11px #FFF, 1828px 1537px #FFF, 1762px 1577px #FFF, 1813px 218px #FFF, 1594px 870px #FFF, 448px 360px #FFF, 1646px 1961px #FFF, 1425px 1060px #FFF, 951px 102px #FFF, 1141px 510px #FFF, 1878px 1737px #FFF, 248px 934px #FFF, 273px 1641px #FFF, 951px 1367px #FFF, 1087px 1144px #FFF, 1219px 649px #FFF, 1945px 1546px #FFF, 1825px 906px #FFF, 1254px 1669px #FFF, 1473px 1826px #FFF, 1033px 873px #FFF, 1701px 1857px #FFF, 1122px 913px #FFF, 1837px 325px #FFF, 1523px 1764px #FFF, 972px 921px #FFF, 530px 1539px #FFF, 506px 1720px #FFF, 1305px 567px #FFF, 1568px 1592px #FFF, 1067px 992px #FFF, 1283px 489px #FFF, 562px 900px #FFF, 158px 1461px #FFF;
+}
+
+@keyframes animStar {
+ from { transform: translateY(0px); }
+ to { transform: translateY(-2000px); }
+}
+
+.stars-layer {
+ will-change: transform;
+}
+
+
+/* Zodiac constellation twinkle */
+@keyframes twinkle {
+ 0%, 100% { opacity: 0.3; }
+ 50% { opacity: 1; }
+}
+.animate-twinkle {
+ animation: twinkle 2s ease-in-out infinite;
+}
diff --git a/src/components/CompatibilityReport.tsx b/src/components/CompatibilityReport.tsx
index 07b140b..f79b486 100644
--- a/src/components/CompatibilityReport.tsx
+++ b/src/components/CompatibilityReport.tsx
@@ -6,6 +6,7 @@ import type { ClientAdminConfig } from "@/lib/config/admin";
import SmsLink from "./SmsLink";
import { ANIMAL_PROFILES } from "@/lib/compatibility/data";
import { encodeReport, buildShareUrl } from "@/lib/share/tokens";
+import { ZodiacSignIcon, CHINESE_ANIMAL_SYMBOL } from "./ZodiacSigns";
interface CompatibilityReportProps {
report: CompatibilityReport;
@@ -69,7 +70,10 @@ export default function CompatibilityReportComponent({
{/* Visitor Profile Cards */}
{/* Western Sign Card */}
-
+
+
+
+
Western Sign
@@ -86,7 +90,10 @@ export default function CompatibilityReportComponent({
{/* Chinese Sign Card */}
-
+
+
+ {CHINESE_ANIMAL_SYMBOL[report.visitor.chineseAnimal]}
+
Chinese Zodiac
@@ -182,6 +189,41 @@ export default function CompatibilityReportComponent({
{report.overall.summary}
+
+ {/* Structured Dos and Don'ts */}
+ {report.overall.dos && report.overall.dos.length > 0 && (
+
+
+
+ Do
+
+
+ {report.overall.dos.map((doItem, i) => (
+ -
+ +
+ {doItem}
+
+ ))}
+
+
+ {report.overall.donts && report.overall.donts.length > 0 && (
+
+
+ Don't
+
+
+ {report.overall.donts.map((dontItem, i) => (
+ -
+ −
+ {dontItem}
+
+ ))}
+
+
+ )}
+
+ )}
+
Practical advice:
diff --git a/src/components/StarryBackground.tsx b/src/components/StarryBackground.tsx
new file mode 100644
index 0000000..a802567
--- /dev/null
+++ b/src/components/StarryBackground.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+/**
+ * Pure CSS parallax star background — three layers of animated stars
+ * using the box-shadow technique. No JavaScript animation, no canvas.
+ * Adapted from https://codepen.io/sarazond/pen/LYGbwj
+ */
+export default function StarryBackground() {
+ return (
+
+ {/* Radial gradient sky */}
+
+ {/* Layer 1: small stars (1px), slowest parallax */}
+
+ {/* Layer 2: medium stars (2px), medium parallax */}
+
+ {/* Layer 3: large stars (3px), fastest parallax */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ZodiacSigns.tsx b/src/components/ZodiacSigns.tsx
new file mode 100644
index 0000000..16d3bf4
--- /dev/null
+++ b/src/components/ZodiacSigns.tsx
@@ -0,0 +1,124 @@
+import type { ZodiacSign, ChineseAnimal } from "@/lib/astrology";
+
+interface ZodiacSignIconProps {
+ sign: ZodiacSign;
+ size?: number;
+ className?: string;
+}
+
+/**
+ * Constellation-style SVG icons for Western zodiac signs.
+ * Simple line-art star patterns — recognizable but abstract.
+ */
+export function ZodiacSignIcon({ sign, size = 96, className = "" }: ZodiacSignIconProps) {
+ const paths: Record
= {
+ Aries: "M30,65 L48,10 L66,65 M48,38 L48,65 M20,65 L48,46 L76,65",
+ Taurus: "M48,10 L48,70 M30,25 L66,25 M20,50 L76,50 M30,70 L66,70",
+ Gemini: "M24,15 L48,45 L24,75 M72,15 L48,45 L72,75 M48,45 L48,45",
+ Cancer: "M24,60 C24,20 72,20 72,60 M48,30 L48,65",
+ Leo: "M48,10 L48,75 M30,25 L66,55 M66,25 L30,55 M36,70 L60,70",
+ Virgo: "M48,8 L48,30 L30,50 L30,72 M48,30 L66,50 L66,72",
+ Libra: "M48,15 L48,70 M30,40 L66,40",
+ Scorpio: "M48,10 L48,35 L30,55 L30,75 M48,35 L66,55 L66,75",
+ Sagittarius: "M48,72 L48,20 L34,8 M48,20 L62,8 M28,48 L68,48",
+ Capricorn: "M36,72 L36,25 C36,8 60,8 60,25 L60,45 L60,72 M36,25 L36,25",
+ Aquarius: "M20,55 L40,35 L56,55 L72,35 M28,25 L68,25",
+ Pisces: "M28,45 C28,20 68,20 68,45 M68,45 C68,70 28,70 28,45",
+ };
+
+ return (
+
+ );
+}
+
+/** Extract unique coordinate pairs from path data for star points */
+function getStarPoints(sign: ZodiacSign): [number, number][] {
+ const pathData: Record = {
+ Aries: "M30,65 L48,10 L66,65 M48,38 L48,65 M20,65 L48,46 L76,65",
+ Taurus: "M48,10 L48,70 M30,25 L66,25 M20,50 L76,50 M30,70 L66,70",
+ Gemini: "M24,15 L48,45 L24,75 M72,15 L48,45 L72,75",
+ Cancer: "M24,60 C24,20 72,20 72,60 M48,30 L48,65",
+ Leo: "M48,10 L48,75 M30,25 L66,55 M66,25 L30,55 M36,70 L60,70",
+ Virgo: "M48,8 L48,30 L30,50 L30,72 M48,30 L66,50 L66,72",
+ Libra: "M48,15 L48,70 M30,40 L66,40",
+ Scorpio: "M48,10 L48,35 L30,55 L30,75 M48,35 L66,55 L66,75",
+ Sagittarius: "M48,72 L48,20 L34,8 M48,20 L62,8 M28,48 L68,48",
+ Capricorn: "M36,72 L36,25 C36,8 60,8 60,25 L60,45 L60,72",
+ Aquarius: "M20,55 L40,35 L56,55 L72,35 M28,25 L68,25",
+ Pisces: "M28,45 C28,20 68,20 68,45 M68,45 C68,70 28,70 28,45",
+ };
+
+ const d = pathData[sign];
+ const coords = new Set();
+ const points: [number, number][] = [];
+
+ // Extract all coordinate pairs from SVG path commands
+ const coordRegex = /([0-9]+),([0-9]+)/g;
+ let match;
+ while ((match = coordRegex.exec(d)) !== null) {
+ const key = `${match[1]},${match[2]}`;
+ if (!coords.has(key)) {
+ coords.add(key);
+ points.push([parseInt(match[1]), parseInt(match[2])]);
+ }
+ }
+
+ return points;
+}
+
+// Chinese zodiac animal symbols (unicode)
+export const CHINESE_ANIMAL_SYMBOL: Record = {
+ Rat: "\uD83D\uDC00",
+ Ox: "\uD83D\uDC02",
+ Tiger: "\uD83D\uDC05",
+ Rabbit: "\uD83D\uDC07",
+ Dragon: "\uD83D\uDC09",
+ Snake: "\uD83D\uDC0D",
+ Horse: "\uD83D\uDC0E",
+ Goat: "\uD83D\uDC10",
+ Monkey: "\uD83D\uDC12",
+ Rooster: "\uD83D\uDC13",
+ Dog: "\uD83D\uDC15",
+ Pig: "\uD83D\uDC17",
+};
+
+// Simple twinkle animation keyframes (add to stars.css or globals)
+export const twinkleCSS = `
+@keyframes twinkle {
+ 0%, 100% { opacity: 0.3; }
+ 50% { opacity: 1; }
+}
+.animate-twinkle {
+ animation: twinkle 2s ease-in-out infinite;
+}
+`;
\ No newline at end of file
diff --git a/src/lib/compatibility/data.ts b/src/lib/compatibility/data.ts
index 2087207..d89088f 100644
--- a/src/lib/compatibility/data.ts
+++ b/src/lib/compatibility/data.ts
@@ -8,12 +8,16 @@ export interface WesternCompatibility {
rating: Rating;
summary: string;
advice: string; // friendship-specific
+ dos: string[]; // specific actionable advice
+ donts: string[]; // things to avoid
}
export interface ChineseCompatibility {
rating: Rating;
summary: string;
advice: string; // friendship-specific
+ dos: string[];
+ donts: string[];
elementInteraction: string; // Wu Xing note
isSixHarming: boolean;
}
@@ -47,6 +51,8 @@ export interface CompatibilityReport {
rating: Rating;
summary: string;
advice: string;
+ dos: string[];
+ donts: string[];
};
computedAt: string; // ISO date string
}
@@ -61,61 +67,85 @@ export const WESTERN_COMPATIBILITY: Record = {
rating: "Excellent",
summary: "Two Virgos understand each other instinctively. You share the same analytical mind, attention to detail, and quiet devotion. No one appreciates a Virgo's thoughtful gestures quite like another Virgo.",
advice: "You both notice everything — use that superpower to support each other, not to critique. Schedule regular low-key hangouts (coffee, a walk, tackling a project together) and save the big gestures for when they really count.",
+ dos: [],
+ donts: [],
},
Taurus: {
rating: "Excellent",
summary: "Virgo and Taurus are both Earth signs who value stability, loyalty, and the simple pleasures. You build trust slowly but deeply — once it's there, it's unshakable.",
advice: "Lean into shared sensory experiences: good food, nature, music. Taurus brings warmth and patience to Virgo's analytical world, while Virgo helps Taurus stay organized. A grounded, nourishing friendship.",
+ dos: [],
+ donts: [],
},
Capricorn: {
rating: "Excellent",
summary: "Earth sign to Earth sign, Virgo and Capricorn share a practical, goal-oriented wavelength. You respect each other's competence and don't waste time on drama.",
advice: "This is a friendship of mutual admiration and quiet reliability. Schedule check-ins around shared projects or goals. Don't forget to celebrate wins together — both of you tend to move straight to the next thing.",
+ dos: [],
+ donts: [],
},
Cancer: {
rating: "Good",
summary: "Virgo's practicality and Cancer's emotional depth complement each other beautifully. Virgo provides steady ground; Cancer brings warmth and care that Virgo sometimes forgets to give itself.",
advice: "Cancer, be patient when Virgo gets lost in details. Virgo, make space for Cancer's feelings without trying to 'fix' them. Home-cooked meals and one-on-one time are your sweet spot.",
+ dos: [],
+ donts: [],
},
Scorpio: {
rating: "Good",
summary: "Both Virgo and Scorpio value depth, loyalty, and truth. Scorpio's intensity fascinates Virgo's analytical mind, while Virgo's steadiness earns Scorpio's hard-won trust.",
advice: "This is a friendship of substance, not small talk. Respect each other's privacy and need for control. When Scorpio goes deep, Virgo can help organize those insights into action.",
+ dos: [],
+ donts: [],
},
Pisces: {
rating: "Good",
summary: "Virgo and Pisces are opposite signs — and opposites attract. Virgo brings structure to Pisces' dreams; Pisces reminds Virgo that not everything needs a spreadsheet. A beautifully balanced friendship.",
advice: "Virgo, resist the urge to 'improve' Pisces. Pisces, appreciate Virgo's help as love, not criticism. Creative collaborations — art, music, writing — bring out the best in both of you.",
+ dos: [],
+ donts: [],
},
Libra: {
rating: "Moderate",
summary: "Libra's charm and social grace intrigue Virgo, while Virgo's grounded nature gives Libra a welcome anchor. You approach life differently but can learn a lot from each other.",
advice: "Libra, don't mistake Virgo's quietness for disinterest. Virgo, join Libra's social world occasionally — it's good for you. Find shared activities that blend beauty (Libra) and function (Virgo).",
+ dos: [],
+ donts: [],
},
Leo: {
rating: "Moderate",
summary: "Leo's bold warmth and Virgo's quiet diligence are an unexpected but workable mix. Leo brings the spotlight; Virgo makes sure everything behind the scenes actually works.",
advice: "Virgo, give Leo genuine appreciation — they thrive on it and deserve it. Leo, notice and thank Virgo for the small things. You make a surprisingly effective team when you respect each other's styles.",
+ dos: [],
+ donts: [],
},
Gemini: {
rating: "Moderate",
summary: "Gemini's quick mind and Virgo's analytical depth share Mercury as their ruling planet, but express it very differently. Gemini skims broadly; Virgo dives deep. Together, you cover a lot of ground.",
advice: "Keep conversations varied and stimulating — you both love ideas. Gemini, slow down enough for Virgo to contribute fully. Virgo, enjoy Gemini's spontaneity without needing to plan everything.",
+ dos: [],
+ donts: [],
},
Aries: {
rating: "Challenging",
summary: "Aries charges forward; Virgo plans every step. Your approaches to life are nearly opposite, which can create friction — but also a fascinating complement if you both stay curious.",
advice: "Aries, give Virgo time to process before expecting a decision. Virgo, let Aries' enthusiasm inspire you rather than annoy you. Outdoor adventures where Aries leads and Virgo packs the snacks: your ideal middle ground.",
+ dos: [],
+ donts: [],
},
Sagittarius: {
rating: "Challenging",
summary: "Sagittarius wants freedom and big-picture truth; Virgo wants precision and practical results. You can frustrate each other, but also expand each other's worlds in ways no other sign can.",
advice: "Sagittarius, don't mistake Virgo's caution for pessimism. Virgo, don't mistake Sagittarius' broad strokes for carelessness. Travel together — Sagittarius picks the destination, Virgo handles the itinerary.",
+ dos: [],
+ donts: [],
},
Aquarius: {
rating: "Challenging",
summary: "Aquarius lives in the future; Virgo lives in the details of the present. Both are intellectual signs, but Aquarius thinks in systems and ideals while Virgo thinks in practical steps.",
advice: "Connect over shared causes or intellectual interests — you're both sharp and principled. Aquarius, bring Virgo into your vision without condescending. Virgo, appreciate Aquarius' idealism without dismissing it as impractical.",
+ dos: [],
+ donts: [],
},
};
diff --git a/src/lib/compatibility/engine.ts b/src/lib/compatibility/engine.ts
index e2926f2..8ff6895 100644
--- a/src/lib/compatibility/engine.ts
+++ b/src/lib/compatibility/engine.ts
@@ -16,10 +16,6 @@ import type {
export type { CompatibilityReport, VisitorProfile } from "./data";
export { WESTERN_COMPATIBILITY, CHINESE_COMPATIBILITY, ANIMAL_PROFILES } from "./data";
-/**
- * Compute the visitor's full profile from a birthday.
- * Accepts either a Date object or year/month/day numbers.
- */
export function computeVisitorProfile(
birthDate: Date,
visitorName?: string,
@@ -42,15 +38,12 @@ export function computeVisitorProfile(
};
}
-/**
- * Build a full compatibility report comparing a visitor to the admin (Virgo + Fire Dragon).
- */
export function buildCompatibilityReport(
visitor: VisitorProfile,
): CompatibilityReport {
const westernCompat = getWesternCompatibility(visitor.westernSign);
const chineseCompat = getChineseCompatibility(visitor.chineseAnimal, visitor.chineseElement);
- const overall = computeOverall(westernCompat.rating, chineseCompat.rating, visitor);
+ const overall = computeOverall(westernCompat, chineseCompat, visitor);
return {
visitor,
@@ -84,13 +77,15 @@ function getChineseCompatibility(
rating: data.rating,
summary: data.summary,
advice: data.advice,
+ dos: [],
+ donts: [],
elementInteraction: data.elementInteraction,
};
}
function computeOverall(
- westernRating: Rating,
- chineseRating: Rating,
+ westernCompat: WesternCompatibility,
+ chineseCompat: Omit,
visitor: VisitorProfile,
): CompatibilityReport["overall"] {
const ratingValues: Record = {
@@ -100,7 +95,7 @@ function computeOverall(
Challenging: 1,
};
- const avg = (ratingValues[westernRating] + ratingValues[chineseRating]) / 2;
+ const avg = (ratingValues[westernCompat.rating] + ratingValues[chineseCompat.rating]) / 2;
let overallRating: Rating;
if (avg >= 3.5) overallRating = "Excellent";
@@ -111,9 +106,132 @@ function computeOverall(
const animalProfile = ANIMAL_PROFILES[visitor.chineseAnimal];
const elementPersonality = animalProfile.elementModifiers[visitor.chineseElement];
+ // Generate contextual do's and don'ts from the compatibility context
+ const theDos = generateDos(westernCompat, chineseCompat, visitor);
+ const theDonts = generateDonts(westernCompat, chineseCompat, visitor);
+
return {
rating: overallRating,
- summary: `As a ${visitor.chineseElement} ${visitor.chineseAnimal} and ${visitor.westernSign}, your compatibility with a Fire Dragon Virgo is ${overallRating.toLowerCase()}. ${westernRating === "Excellent" && chineseRating === "Excellent" ? "You share a rare and powerful harmony across both zodiac systems — a friendship with extraordinary potential." : westernRating === "Excellent" || chineseRating === "Excellent" ? "You have standout compatibility in one tradition and a solid foundation overall." : overallRating === "Good" ? "You have a solid foundation with genuine warmth and mutual respect — a friendship worth investing in." : overallRating === "Moderate" ? "You're compatible enough to build something meaningful with awareness and effort. The differences make it interesting." : "You approach life from different angles, but that doesn't mean you can't connect. Awareness of your differences is the first step to bridging them."}`,
- advice: `As a ${visitor.chineseElement} ${visitor.chineseAnimal} (${visitor.chineseStemName}, ${visitor.chineseStemChinese} / ${visitor.chineseStemPinyin}), ${elementPersonality} As a ${visitor.westernSign} (${visitor.westernElement}, ${visitor.westernModality}, ruled by ${visitor.westernRulingPlanet}), ${visitor.westernSummary} In friendship, lean into what your signs do best — and stay aware of where friction might arise. The stars suggest a path, but you walk it together.`,
+ summary: `As a ${visitor.chineseElement} ${visitor.chineseAnimal} and ${visitor.westernSign}, your compatibility with a Fire Dragon Virgo is ${overallRating.toLowerCase()}. ${westernCompat.rating === "Excellent" && chineseCompat.rating === "Excellent" ? "You share a rare and powerful harmony across both zodiac systems — a friendship with extraordinary potential." : westernCompat.rating === "Excellent" || chineseCompat.rating === "Excellent" ? "You have standout compatibility in one tradition and a solid foundation overall." : overallRating === "Good" ? "You have a solid foundation with genuine warmth and mutual respect — a friendship worth investing in." : overallRating === "Moderate" ? "You're compatible enough to build something meaningful with awareness and effort. The differences make it interesting." : "You approach life from different angles, but that doesn't mean you can't connect. Awareness of your differences is the first step to bridging them."}`,
+ advice: `As a ${visitor.chineseElement} ${visitor.chineseAnimal} (${visitor.chineseStemName}), ${elementPersonality} As a ${visitor.westernSign} (${visitor.westernElement}, ${visitor.westernModality}, ruled by ${visitor.westernRulingPlanet}), ${visitor.westernSummary} In friendship, lean into what your signs do best — and stay aware of where friction might arise. The stars suggest a path, but you walk it together.`,
+ dos: theDos,
+ donts: theDonts,
};
+}
+
+/**
+ * Generate specific, actionable "Do" items based on the sign pairing.
+ * These are contextual to the specific visitor's signs and rating levels.
+ */
+function generateDos(
+ western: WesternCompatibility,
+ chinese: Omit,
+ visitor: VisitorProfile,
+): string[] {
+ const dos: string[] = [];
+
+ // Prioritize quality time based on Western element compatibility
+ const earthSigns: ZodiacSign[] = ["Taurus", "Virgo", "Capricorn"];
+ if (earthSigns.includes(visitor.westernSign)) {
+ dos.push(
+ `Plan low-key, quality-time hangouts — shared meals, nature walks, or working on a project together. You both thrive in calm, grounded settings.`
+ );
+ } else {
+ dos.push(
+ `Schedule regular one-on-one time. Virgo values consistency and shows love through presence. Don't let too much time pass between check-ins.`
+ );
+ }
+
+ // Chinese element interaction guidance
+ if (chinese.elementInteraction.includes("generating") || chinese.elementInteraction.includes("harmonious")) {
+ dos.push(
+ `Lean into the natural harmony between your elements. The ${visitor.chineseElement}-Fire dynamic is fundamentally supportive — trust the flow and don't overthink it.`
+ );
+ } else if (chinese.elementInteraction.includes("controlling") || chinese.elementInteraction.includes("tempers")) {
+ dos.push(
+ `Your ${visitor.chineseElement} element has a balancing effect on the Dragon's Fire. Don't suppress your natural caution — it's a gift that keeps the friendship steady.`
+ );
+ } else {
+ dos.push(
+ `Be aware of your elemental dynamics. The ${visitor.chineseElement}-Fire relationship shapes how energy flows between you — read each other's moods and adjust accordingly.`
+ );
+ }
+
+ // Rating-based general guidance
+ if (western.rating === "Excellent") {
+ dos.push(
+ `Appreciate how much you already understand each other. You don't need to explain yourself — this is a rare gift. Use that trust to be fully honest.`
+ );
+ } else if (western.rating === "Good") {
+ dos.push(
+ `Build on your complementary strengths. Virgo brings structure to your ${visitor.westernSign} nature, while you bring something Virgo genuinely needs. Name what you appreciate.`
+ );
+ }
+
+ // Six Harming awareness
+ const harming = isSixHarming(visitor.chineseAnimal, "Dragon");
+ if (harming) {
+ dos.push(
+ `Acknowledge the traditional tension between ${visitor.chineseAnimal} and Dragon openly. A little awareness goes a long way — naming the dynamic defuses it. Give each other extra grace when friction surfaces.`
+ );
+ }
+
+ // Always include a communication and generosity item
+ dos.push(`Be direct and kind in equal measure. Virgo respects honesty and notices the small things. Say what you mean — the subtext will be read either way.`);
+ dos.push(`Celebrate each other's wins genuinely. The Fire Dragon thrives on recognition; your support means more than you know.`);
+
+ return dos;
+}
+
+/**
+ * Generate "Don't" items — friction points to avoid based on the pairing.
+ */
+function generateDonts(
+ western: WesternCompatibility,
+ chinese: Omit,
+ visitor: VisitorProfile,
+): string[] {
+ const donts: string[] = [];
+
+ // Western element friction
+ if (western.rating === "Challenging") {
+ donts.push(
+ `Don't mistake Virgo's caution for judgment. When they hesitate or analyze, it's their way of caring — not criticism of your choices.`
+ );
+ }
+
+ // Fire intensity warning
+ if (visitor.chineseElement === "Fire") {
+ donts.push(
+ `Don't let double Fire energy burn too hot. Two strong wills need clear lanes. Agree on when to lead and when to follow.`
+ );
+ }
+
+ // Chinese compatibility friction
+ if (western.rating !== "Excellent" && chinese.rating !== "Excellent") {
+ donts.push(
+ `Don't force the friendship to look like other relationships. Your connection has its own rhythm. Let it be what it naturally is rather than what you think it should be.`
+ );
+ }
+
+ // Metal/Fire friction
+ if (visitor.chineseElement === "Metal" || chinese.elementInteraction.toLowerCase().includes("melt")) {
+ donts.push(
+ `Don't let the Dragon's intensity overwhelm your ${visitor.chineseElement} nature. Stand your ground when you need to — a true Dragon respects strength, not compliance.`
+ );
+ }
+
+ // Six Harming
+ if (isSixHarming(visitor.chineseAnimal, "Dragon")) {
+ donts.push(
+ `Don't take the Dragon's boldness personally. They're not trying to dominate — it's just how they move through the world. Let it roll off when you can.`
+ );
+ }
+
+ // General relationship wisdom
+ donts.push(
+ `Don't go too long without reaching out. Virgo won't chase — they'll simply assume you're busy. Initiative from you is always appreciated, never unwelcome.`
+ );
+
+ return donts;
}
\ No newline at end of file
diff --git a/src/lib/share/__tests__/tokens.test.ts b/src/lib/share/__tests__/tokens.test.ts
index 82c4987..c3123b1 100755
--- a/src/lib/share/__tests__/tokens.test.ts
+++ b/src/lib/share/__tests__/tokens.test.ts
@@ -27,6 +27,8 @@ const sampleReport: CompatibilityReport = {
rating: "Excellent",
summary: "Two Virgos understand each other.",
advice: "Be kind.",
+ dos: ["Spend time together.", "Appreciate each other."],
+ donts: ["Don't overthink."],
},
chinese: {
rating: "Excellent",
@@ -34,11 +36,15 @@ const sampleReport: CompatibilityReport = {
advice: "Share the spotlight.",
elementInteraction: "Fire with Fire.",
isSixHarming: false,
+ dos: ["Share the spotlight."],
+ donts: ["Don't compete."],
},
overall: {
rating: "Excellent",
summary: "Perfect match.",
advice: "Enjoy it.",
+ dos: ["Do be kind.", "Do communicate."],
+ donts: ["Don't overthink."],
},
computedAt: "2024-01-01T00:00:00.000Z",
};