@roxyapi/ui 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/AGENTS.md +15 -10
  2. package/README.md +18 -13
  3. package/dist/cdn/components/compatibility-card.js.map +1 -1
  4. package/dist/cdn/components/dasha-timeline.js +8 -8
  5. package/dist/cdn/components/dasha-timeline.js.map +2 -2
  6. package/dist/cdn/components/divisional-chart.js +35 -23
  7. package/dist/cdn/components/divisional-chart.js.map +4 -4
  8. package/dist/cdn/components/guna-milan.js.map +1 -1
  9. package/dist/cdn/components/kp-chart.js +306 -0
  10. package/dist/cdn/components/kp-chart.js.map +7 -0
  11. package/dist/cdn/components/kp-planets-table.js.map +1 -1
  12. package/dist/cdn/components/kp-ruling-planets.js +269 -0
  13. package/dist/cdn/components/kp-ruling-planets.js.map +7 -0
  14. package/dist/cdn/components/location-search.js +7 -5
  15. package/dist/cdn/components/location-search.js.map +3 -3
  16. package/dist/cdn/components/moon-phase.js.map +1 -1
  17. package/dist/cdn/components/nakshatra-card.js +229 -0
  18. package/dist/cdn/components/nakshatra-card.js.map +7 -0
  19. package/dist/cdn/components/natal-chart.js +228 -115
  20. package/dist/cdn/components/natal-chart.js.map +4 -4
  21. package/dist/cdn/components/numerology-card.js +3 -3
  22. package/dist/cdn/components/numerology-card.js.map +2 -2
  23. package/dist/cdn/components/panchang-table.js.map +1 -1
  24. package/dist/cdn/components/shadbala-table.js.map +1 -1
  25. package/dist/cdn/components/synastry-chart.js +3 -3
  26. package/dist/cdn/components/synastry-chart.js.map +2 -2
  27. package/dist/cdn/components/transits-table.js.map +1 -1
  28. package/dist/cdn/components/vedic-kundli.js +34 -22
  29. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  30. package/dist/cdn/components/vedic-planets-table.js +231 -0
  31. package/dist/cdn/components/vedic-planets-table.js.map +7 -0
  32. package/dist/cdn/components/western-planets-table.js +220 -0
  33. package/dist/cdn/components/western-planets-table.js.map +7 -0
  34. package/dist/cdn/roxy-ui.js +1078 -331
  35. package/dist/cdn/roxy-ui.js.map +4 -4
  36. package/dist/components/compatibility-card.js.map +1 -1
  37. package/dist/components/dasha-timeline.d.ts.map +1 -1
  38. package/dist/components/dasha-timeline.js.map +2 -2
  39. package/dist/components/divisional-chart.d.ts +5 -3
  40. package/dist/components/divisional-chart.d.ts.map +1 -1
  41. package/dist/components/divisional-chart.js +159 -38
  42. package/dist/components/divisional-chart.js.map +3 -3
  43. package/dist/components/guna-milan.js.map +1 -1
  44. package/dist/components/kp-chart.d.ts +26 -0
  45. package/dist/components/kp-chart.d.ts.map +1 -0
  46. package/dist/components/kp-chart.js +382 -0
  47. package/dist/components/kp-chart.js.map +7 -0
  48. package/dist/components/kp-planets-table.js.map +1 -1
  49. package/dist/components/kp-ruling-planets.d.ts +20 -0
  50. package/dist/components/kp-ruling-planets.d.ts.map +1 -0
  51. package/dist/components/kp-ruling-planets.js +275 -0
  52. package/dist/components/kp-ruling-planets.js.map +7 -0
  53. package/dist/components/location-search.d.ts.map +1 -1
  54. package/dist/components/location-search.js +9 -2
  55. package/dist/components/location-search.js.map +2 -2
  56. package/dist/components/moon-phase.js.map +1 -1
  57. package/dist/components/nakshatra-card.d.ts +18 -0
  58. package/dist/components/nakshatra-card.d.ts.map +1 -0
  59. package/dist/components/nakshatra-card.js +231 -0
  60. package/dist/components/nakshatra-card.js.map +7 -0
  61. package/dist/components/natal-chart.d.ts +28 -0
  62. package/dist/components/natal-chart.d.ts.map +1 -1
  63. package/dist/components/natal-chart.js +401 -104
  64. package/dist/components/natal-chart.js.map +2 -2
  65. package/dist/components/numerology-card.d.ts.map +1 -1
  66. package/dist/components/numerology-card.js.map +2 -2
  67. package/dist/components/panchang-table.js.map +1 -1
  68. package/dist/components/shadbala-table.js.map +1 -1
  69. package/dist/components/synastry-chart.js.map +2 -2
  70. package/dist/components/transits-table.js.map +1 -1
  71. package/dist/components/vedic-kundli.d.ts +7 -3
  72. package/dist/components/vedic-kundli.d.ts.map +1 -1
  73. package/dist/components/vedic-kundli.js +209 -87
  74. package/dist/components/vedic-kundli.js.map +3 -3
  75. package/dist/components/vedic-planets-table.d.ts +21 -0
  76. package/dist/components/vedic-planets-table.d.ts.map +1 -0
  77. package/dist/components/vedic-planets-table.js +355 -0
  78. package/dist/components/vedic-planets-table.js.map +7 -0
  79. package/dist/components/western-planets-table.d.ts +21 -0
  80. package/dist/components/western-planets-table.d.ts.map +1 -0
  81. package/dist/components/western-planets-table.js +350 -0
  82. package/dist/components/western-planets-table.js.map +7 -0
  83. package/dist/index.cjs +2042 -695
  84. package/dist/index.cjs.map +4 -4
  85. package/dist/index.d.ts +5 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +2029 -682
  88. package/dist/index.js.map +4 -4
  89. package/dist/manifest.d.ts.map +1 -1
  90. package/dist/manifest.json +23 -18
  91. package/dist/styles/tokens.css +4 -0
  92. package/dist/types/types.gen.d.ts +343 -49
  93. package/dist/types/types.gen.d.ts.map +1 -1
  94. package/dist/utils/degree.d.ts +12 -0
  95. package/dist/utils/degree.d.ts.map +1 -1
  96. package/dist/utils/format.d.ts +1 -1
  97. package/dist/utils/kundli-render.d.ts +85 -12
  98. package/dist/utils/kundli-render.d.ts.map +1 -1
  99. package/dist/version.d.ts +1 -1
  100. package/package.json +1 -1
  101. package/src/components/dasha-timeline.ts +1 -7
  102. package/src/components/divisional-chart.ts +27 -41
  103. package/src/components/kp-chart.ts +313 -0
  104. package/src/components/kp-ruling-planets.ts +196 -0
  105. package/src/components/location-search.ts +16 -2
  106. package/src/components/nakshatra-card.ts +149 -0
  107. package/src/components/natal-chart.ts +408 -119
  108. package/src/components/numerology-card.ts +1 -5
  109. package/src/components/vedic-kundli.ts +30 -40
  110. package/src/components/vedic-planets-table.ts +184 -0
  111. package/src/components/western-planets-table.ts +180 -0
  112. package/src/index.ts +5 -0
  113. package/src/manifest.ts +146 -84
  114. package/src/styles/tokens.css +4 -0
  115. package/src/types/types.gen.ts +343 -49
  116. package/src/utils/degree.ts +21 -0
  117. package/src/utils/format.ts +1 -1
  118. package/src/utils/kundli-render.ts +234 -29
  119. package/src/version.ts +1 -1
package/dist/index.cjs CHANGED
@@ -43,9 +43,12 @@ __export(src_exports, {
43
43
  RoxyGunaMilan: () => RoxyGunaMilan,
44
44
  RoxyHexagram: () => RoxyHexagram,
45
45
  RoxyHoroscopeCard: () => RoxyHoroscopeCard,
46
+ RoxyKpChart: () => RoxyKpChart,
46
47
  RoxyKpPlanetsTable: () => RoxyKpPlanetsTable,
48
+ RoxyKpRulingPlanets: () => RoxyKpRulingPlanets,
47
49
  RoxyLocationSearch: () => RoxyLocationSearch,
48
50
  RoxyMoonPhase: () => RoxyMoonPhase,
51
+ RoxyNakshatraCard: () => RoxyNakshatraCard,
49
52
  RoxyNatalChart: () => RoxyNatalChart,
50
53
  RoxyNumerologyCard: () => RoxyNumerologyCard,
51
54
  RoxyPanchangTable: () => RoxyPanchangTable,
@@ -55,6 +58,8 @@ __export(src_exports, {
55
58
  RoxyTarotSpread: () => RoxyTarotSpread,
56
59
  RoxyTransitsTable: () => RoxyTransitsTable,
57
60
  RoxyVedicKundli: () => RoxyVedicKundli,
61
+ RoxyVedicPlanetsTable: () => RoxyVedicPlanetsTable,
62
+ RoxyWesternPlanetsTable: () => RoxyWesternPlanetsTable,
58
63
  RoxyYogaList: () => RoxyYogaList
59
64
  });
60
65
  module.exports = __toCommonJS(src_exports);
@@ -149,6 +154,15 @@ var SIGNS_ORDER = [
149
154
  var RASHI_KEYS = SIGNS_ORDER.map(
150
155
  (s) => s.toLowerCase()
151
156
  );
157
+ var ASPECT_SYMBOL = {
158
+ conjunction: "\u260C",
159
+ opposition: "\u260D",
160
+ trine: "\u25B3",
161
+ square: "\u25A1",
162
+ sextile: "\u2731",
163
+ quincunx: "\u22BB",
164
+ semisextile: "\u22BC"
165
+ };
152
166
  var TRIGRAM_GLYPH = {
153
167
  heaven: "\u2630",
154
168
  lake: "\u2631",
@@ -1622,9 +1636,88 @@ var import_decorators7 = require("lit/decorators.js");
1622
1636
 
1623
1637
  // packages/ui/src/utils/kundli-render.ts
1624
1638
  var import_lit8 = require("lit");
1639
+
1640
+ // packages/ui/src/utils/degree.ts
1641
+ function normalizeLongitude(lon) {
1642
+ const wrapped = lon % 360;
1643
+ return wrapped < 0 ? wrapped + 360 : wrapped;
1644
+ }
1645
+ function longitudeToSignPosition(longitude) {
1646
+ const lon = normalizeLongitude(longitude);
1647
+ const signIndex = Math.floor(lon / 30) % 12;
1648
+ const within = lon % 30;
1649
+ const degree = Math.floor(within);
1650
+ const minuteFloat = (within - degree) * 60;
1651
+ const minute = Math.floor(minuteFloat);
1652
+ const second = Math.round((minuteFloat - minute) * 60);
1653
+ return {
1654
+ sign: SIGNS_ORDER[signIndex] ?? "Aries",
1655
+ signIndex,
1656
+ degree,
1657
+ minute,
1658
+ second
1659
+ };
1660
+ }
1661
+ function formatSignPosition(longitude) {
1662
+ const { sign, degree, minute } = longitudeToSignPosition(longitude);
1663
+ return `${degree}\xB0 ${sign} ${String(minute).padStart(2, "0")}'`;
1664
+ }
1665
+ function oppositePoint(longitude) {
1666
+ return normalizeLongitude(longitude + 180);
1667
+ }
1668
+ function arcMidpoint(start, end) {
1669
+ const s = normalizeLongitude(start);
1670
+ let span = normalizeLongitude(end) - s;
1671
+ if (span < 0) span += 360;
1672
+ return normalizeLongitude(s + span / 2);
1673
+ }
1674
+ function polarToCartesian(cx, cy, radius, angleDeg) {
1675
+ const angleRad = angleDeg * Math.PI / 180;
1676
+ return {
1677
+ x: cx + radius * Math.cos(angleRad),
1678
+ y: cy + radius * Math.sin(angleRad)
1679
+ };
1680
+ }
1681
+
1682
+ // packages/ui/src/utils/kundli-render.ts
1625
1683
  var RASHI_TO_SIGN = Object.fromEntries(
1626
1684
  SIGNS_ORDER.map((s) => [s.toLowerCase(), s])
1627
1685
  );
1686
+ var RETRO_MARK = "\u02B3";
1687
+ function grahaLabel(p) {
1688
+ const abbr = PLANET_ABBR[capitalize(p.graha)] ?? p.graha.slice(0, 2);
1689
+ const retro = p.isRetrograde ? RETRO_MARK : "";
1690
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
1691
+ return `${abbr}${retro}`;
1692
+ }
1693
+ const { degree } = longitudeToSignPosition(p.longitude);
1694
+ return `${abbr} ${degree}\xB0${retro}`;
1695
+ }
1696
+ function grahaTitle(p) {
1697
+ const parts = [capitalize(p.graha)];
1698
+ if (typeof p.longitude === "number" && Number.isFinite(p.longitude)) {
1699
+ const sp = longitudeToSignPosition(p.longitude);
1700
+ parts.push(
1701
+ `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}' ${sp.sign}`
1702
+ );
1703
+ }
1704
+ if (p.nakshatra?.name) {
1705
+ const pada = p.nakshatra.pada ? ` pada ${p.nakshatra.pada}` : "";
1706
+ parts.push(`${p.nakshatra.name}${pada}`);
1707
+ }
1708
+ if (p.awastha) parts.push(p.awastha);
1709
+ if (p.isRetrograde) parts.push("retrograde");
1710
+ return parts.join(" \xB7 ");
1711
+ }
1712
+ function renderPlanetStack(planets, cx, baseY, lineHeight) {
1713
+ const startY = baseY - (planets.length - 1) * lineHeight / 2;
1714
+ return planets.map((p, j) => {
1715
+ const yPos = startY + j * lineHeight;
1716
+ return import_lit8.svg`<text class="planet-text" x=${cx} y=${yPos} text-anchor="middle" dominant-baseline="central">${grahaLabel(
1717
+ p
1718
+ )}<title>${grahaTitle(p)}</title></text>`;
1719
+ });
1720
+ }
1628
1721
  var SOUTH_HOUSE_CENTERS = {
1629
1722
  1: { x: 150, y: 58 },
1630
1723
  2: { x: 205, y: 52 },
@@ -1667,12 +1760,52 @@ var NORTH_HOUSE_CENTERS = {
1667
1760
  11: { x: 200, y: 80 },
1668
1761
  12: { x: 200, y: 220 }
1669
1762
  };
1763
+ var EAST_HOUSE_CENTERS = {
1764
+ 1: { x: 150, y: 80 },
1765
+ // inner diamond, top
1766
+ 2: { x: 220, y: 33 },
1767
+ // top-right corner, upper triangle
1768
+ 3: { x: 267, y: 80 },
1769
+ // top-right corner, right triangle
1770
+ 4: { x: 220, y: 150 },
1771
+ // inner diamond, right
1772
+ 5: { x: 267, y: 220 },
1773
+ // bottom-right corner, right triangle
1774
+ 6: { x: 220, y: 267 },
1775
+ // bottom-right corner, lower triangle
1776
+ 7: { x: 150, y: 220 },
1777
+ // inner diamond, bottom
1778
+ 8: { x: 80, y: 267 },
1779
+ // bottom-left corner, lower triangle
1780
+ 9: { x: 33, y: 220 },
1781
+ // bottom-left corner, left triangle
1782
+ 10: { x: 80, y: 150 },
1783
+ // inner diamond, left
1784
+ 11: { x: 33, y: 80 },
1785
+ // top-left corner, left triangle
1786
+ 12: { x: 80, y: 33 }
1787
+ // top-left corner, upper triangle
1788
+ };
1789
+ var EAST_SIGN_POSITIONS = {
1790
+ 1: { x: 150, y: 55 },
1791
+ 2: { x: 235, y: 24 },
1792
+ 3: { x: 276, y: 62 },
1793
+ 4: { x: 242, y: 150 },
1794
+ 5: { x: 276, y: 238 },
1795
+ 6: { x: 235, y: 276 },
1796
+ 7: { x: 150, y: 245 },
1797
+ 8: { x: 65, y: 276 },
1798
+ 9: { x: 24, y: 238 },
1799
+ 10: { x: 58, y: 150 },
1800
+ 11: { x: 24, y: 62 },
1801
+ 12: { x: 65, y: 24 }
1802
+ };
1670
1803
  function renderSouthHouseGroup(h) {
1671
1804
  const center = SOUTH_HOUSE_CENTERS[h.number];
1672
1805
  const signPos = SOUTH_SIGN_POSITIONS[h.number];
1673
1806
  if (!center || !signPos) return import_lit8.nothing;
1674
1807
  const signAbbr = SIGN_ABBR[h.sign] ?? "";
1675
- const planets = h.planets;
1808
+ const baseY = h.isLagna ? center.y + 8 : center.y;
1676
1809
  return import_lit8.svg`
1677
1810
  <g>
1678
1811
  ${h.isLagna ? import_lit8.svg`<rect
@@ -1682,14 +1815,7 @@ function renderSouthHouseGroup(h) {
1682
1815
  />` : import_lit8.nothing}
1683
1816
  ${signAbbr ? import_lit8.svg`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : import_lit8.nothing}
1684
1817
  ${h.isLagna ? import_lit8.svg`<text class="lagna-marker" x=${center.x} y=${center.y - 18} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : import_lit8.nothing}
1685
- ${planets.map((planet, j) => {
1686
- const abbr = PLANET_ABBR[capitalize(planet)] ?? planet.slice(0, 2);
1687
- const lineHeight = 13;
1688
- const baseY = h.isLagna ? center.y + 8 : center.y;
1689
- const startY = baseY - (planets.length - 1) * lineHeight / 2;
1690
- const yPos = startY + j * lineHeight;
1691
- return import_lit8.svg`<text class="planet-text" x=${center.x} y=${yPos} text-anchor="middle" dominant-baseline="central">${abbr}</text>`;
1692
- })}
1818
+ ${renderPlanetStack(h.planets, center.x, baseY, 13)}
1693
1819
  </g>
1694
1820
  `;
1695
1821
  }
@@ -1708,19 +1834,12 @@ function renderNorthHouseGroup(h) {
1708
1834
  const center = NORTH_HOUSE_CENTERS[h.number];
1709
1835
  if (!center) return import_lit8.nothing;
1710
1836
  const signAbbr = SIGN_ABBR[h.sign] ?? "";
1711
- const planets = h.planets;
1712
1837
  return import_lit8.svg`
1713
1838
  <g>
1714
1839
  ${h.isLagna ? import_lit8.svg`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="22" />` : import_lit8.nothing}
1715
1840
  ${signAbbr ? import_lit8.svg`<text class="sign-text" x=${center.x} y=${center.y - 10} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : import_lit8.nothing}
1716
1841
  <text class="house-num" x=${center.x} y=${center.y + 2} text-anchor="middle" dominant-baseline="central">${h.number}</text>
1717
- ${planets.map((planet, j) => {
1718
- const abbr = PLANET_ABBR[capitalize(planet)] ?? planet.slice(0, 2);
1719
- const lineHeight = 11;
1720
- const startY = center.y + 14 - (planets.length - 1) * lineHeight / 2;
1721
- const yPos = startY + j * lineHeight;
1722
- return import_lit8.svg`<text class="planet-text" x=${center.x} y=${yPos} text-anchor="middle" dominant-baseline="central">${abbr}</text>`;
1723
- })}
1842
+ ${renderPlanetStack(h.planets, center.x, center.y + 14, 11)}
1724
1843
  </g>
1725
1844
  `;
1726
1845
  }
@@ -1738,6 +1857,58 @@ function renderSouthFrame() {
1738
1857
  <line class="line" x1="10" y1="150" x2="80" y2="80" stroke-width="1" />
1739
1858
  `;
1740
1859
  }
1860
+ function renderEastFrame() {
1861
+ return import_lit8.svg`
1862
+ <rect class="line" x="10" y="10" width="280" height="280" stroke-width="1.5" fill="none" />
1863
+ <line class="line" x1="10" y1="10" x2="290" y2="290" stroke-width="1" />
1864
+ <line class="line" x1="290" y1="10" x2="10" y2="290" stroke-width="1" />
1865
+ <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1" fill="none" />
1866
+ `;
1867
+ }
1868
+ function renderEastHouseGroup(h) {
1869
+ const center = EAST_HOUSE_CENTERS[h.number];
1870
+ const signPos = EAST_SIGN_POSITIONS[h.number];
1871
+ if (!center || !signPos) return import_lit8.nothing;
1872
+ const signAbbr = SIGN_ABBR[h.sign] ?? "";
1873
+ return import_lit8.svg`
1874
+ <g>
1875
+ ${h.isLagna ? import_lit8.svg`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="20" />` : import_lit8.nothing}
1876
+ ${signAbbr ? import_lit8.svg`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : import_lit8.nothing}
1877
+ ${h.isLagna ? import_lit8.svg`<text class="lagna-marker" x=${center.x} y=${center.y - 14} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : import_lit8.nothing}
1878
+ ${renderPlanetStack(h.planets, center.x, center.y + 2, 11)}
1879
+ </g>
1880
+ `;
1881
+ }
1882
+ function buildHousesFromMeta(meta) {
1883
+ const byRashi = /* @__PURE__ */ new Map();
1884
+ let lagnaKey = "";
1885
+ for (const [name, pos] of Object.entries(meta)) {
1886
+ const rashiKey = (pos?.rashi ?? "").toLowerCase();
1887
+ if (name === "Lagna" || pos?.graha === "Lagna") {
1888
+ lagnaKey = rashiKey;
1889
+ continue;
1890
+ }
1891
+ if (!rashiKey) continue;
1892
+ const list = byRashi.get(rashiKey) ?? [];
1893
+ list.push({
1894
+ graha: pos.graha ?? name,
1895
+ longitude: pos.longitude,
1896
+ nakshatra: pos.nakshatra,
1897
+ isRetrograde: pos.isRetrograde,
1898
+ awastha: pos.awastha
1899
+ });
1900
+ byRashi.set(rashiKey, list);
1901
+ }
1902
+ return SIGNS_ORDER.map((sign, i) => {
1903
+ const key = sign.toLowerCase();
1904
+ return {
1905
+ number: i + 1,
1906
+ sign,
1907
+ planets: byRashi.get(key) ?? [],
1908
+ isLagna: lagnaKey === key
1909
+ };
1910
+ });
1911
+ }
1741
1912
 
1742
1913
  // packages/ui/src/components/divisional-chart.ts
1743
1914
  var RoxyDivisionalChart = class extends import_lit9.LitElement {
@@ -1747,31 +1918,17 @@ var RoxyDivisionalChart = class extends import_lit9.LitElement {
1747
1918
  this.chartStyle = "south";
1748
1919
  }
1749
1920
  buildHouses() {
1750
- if (!this.data) return [];
1751
- const chart = this.data.chart;
1752
- const meta = this.data.chart.meta ?? {};
1753
- const lagnaSign = meta.Lagna?.rashi ?? "";
1754
- const houses = [];
1755
- for (let i = 0; i < 12; i++) {
1756
- const key = RASHI_KEYS[i];
1757
- const bucket = chart[key];
1758
- const planets = (bucket?.signs ?? []).map((p) => p.graha).filter(Boolean);
1759
- const sign = RASHI_TO_SIGN[key] ?? "";
1760
- houses.push({
1761
- number: i + 1,
1762
- sign,
1763
- planets,
1764
- isLagna: lagnaSign ? lagnaSign.toLowerCase() === sign.toLowerCase() : false
1765
- });
1766
- }
1767
- return houses;
1921
+ if (!this.data?.chart?.meta) return [];
1922
+ return buildHousesFromMeta(this.data.chart.meta);
1768
1923
  }
1769
1924
  render() {
1770
1925
  if (!this.data)
1771
1926
  return import_lit9.html`<div class="roxy-empty" role="status">No divisional chart data</div>`;
1772
1927
  const { division, vargottama } = this.data;
1773
1928
  const houses = this.buildHouses();
1774
- const isNorth = this.chartStyle === "north";
1929
+ const style = this.chartStyle;
1930
+ const frame = style === "north" ? renderNorthFrame() : style === "east" ? renderEastFrame() : renderSouthFrame();
1931
+ const houseGroup = style === "north" ? renderNorthHouseGroup : style === "east" ? renderEastHouseGroup : renderSouthHouseGroup;
1775
1932
  return import_lit9.html`<div class="wrap">
1776
1933
  <div class="header">
1777
1934
  <h2 class="title">
@@ -1787,8 +1944,8 @@ var RoxyDivisionalChart = class extends import_lit9.LitElement {
1787
1944
  aria-label="D${division.number} ${division.name} divisional chart with twelve sign houses"
1788
1945
  >
1789
1946
  <title>D${division.number} ${division.name}</title>
1790
- ${isNorth ? renderNorthFrame() : renderSouthFrame()}
1791
- ${isNorth ? houses.map((h) => renderNorthHouseGroup(h)) : houses.map((h) => renderSouthHouseGroup(h))}
1947
+ ${frame}
1948
+ ${houses.map((h) => houseGroup(h))}
1792
1949
  </svg>
1793
1950
 
1794
1951
  ${vargottama && vargottama.length > 0 ? import_lit9.html`<div class="vargottama-row" role="list" aria-label="Vargottama planets">
@@ -3161,26 +3318,300 @@ RoxyHoroscopeCard = __decorateClass([
3161
3318
  (0, import_decorators12.customElement)("roxy-horoscope-card")
3162
3319
  ], RoxyHoroscopeCard);
3163
3320
 
3164
- // packages/ui/src/components/kp-planets-table.ts
3321
+ // packages/ui/src/components/kp-chart.ts
3165
3322
  var import_lit15 = require("lit");
3166
3323
  var import_decorators13 = require("lit/decorators.js");
3167
- var RoxyKpPlanetsTable = class extends import_lit15.LitElement {
3324
+ var RoxyKpChart = class extends import_lit15.LitElement {
3325
+ constructor() {
3326
+ super(...arguments);
3327
+ this.data = null;
3328
+ this.activeTab = "planets";
3329
+ }
3330
+ /** Merge the 7 planets and the two nodes into one ordered body list. */
3331
+ bodies() {
3332
+ const d = this.data;
3333
+ if (!d) return [];
3334
+ const rows = (d.planets ?? []).map((p) => ({
3335
+ name: p.planet,
3336
+ sign: p.sign,
3337
+ house: p.house,
3338
+ nakshatra: p.nakshatra,
3339
+ starLord: p.starLord,
3340
+ subLord: p.subLord,
3341
+ subSubLord: p.subSubLord,
3342
+ kpNumber: p.kpNumber,
3343
+ retrograde: p.retrograde
3344
+ }));
3345
+ const nodes = d.nodes;
3346
+ for (const [name, node] of [
3347
+ ["Rahu", nodes?.rahu],
3348
+ ["Ketu", nodes?.ketu]
3349
+ ]) {
3350
+ if (node) {
3351
+ rows.push({
3352
+ name,
3353
+ sign: node.sign,
3354
+ house: node.house,
3355
+ nakshatra: node.nakshatra,
3356
+ starLord: node.starLord,
3357
+ subLord: node.subLord,
3358
+ subSubLord: node.subSubLord,
3359
+ retrograde: true
3360
+ });
3361
+ }
3362
+ }
3363
+ return rows;
3364
+ }
3365
+ onTabKeyDown(e) {
3366
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
3367
+ e.preventDefault();
3368
+ this.activeTab = this.activeTab === "planets" ? "cusps" : "planets";
3369
+ const next = this.activeTab;
3370
+ requestAnimationFrame(() => {
3371
+ this.shadowRoot?.querySelector(`#tab-${next}`)?.focus();
3372
+ });
3373
+ }
3374
+ render() {
3375
+ if (!this.data)
3376
+ return import_lit15.html`<div class="roxy-empty" role="status">No KP chart data</div>`;
3377
+ const d = this.data;
3378
+ const asc = d.ascendant;
3379
+ return import_lit15.html`<div class="wrap" aria-label="KP chart" tabindex="0">
3380
+ <header class="head">
3381
+ <h2 class="title">KP chart</h2>
3382
+ ${asc ? import_lit15.html`<div class="asc">
3383
+ Ascendant: <strong>${asc.sign ?? ""}</strong>
3384
+ ${asc.nakshatra ? import_lit15.html`· ${asc.nakshatra}` : import_lit15.nothing}
3385
+ ${asc.subLord ? import_lit15.html`· sub lord ${asc.subLord}` : import_lit15.nothing}
3386
+ ${typeof asc.kpNumber === "number" ? import_lit15.html`· KP ${asc.kpNumber}` : import_lit15.nothing}
3387
+ </div>` : import_lit15.nothing}
3388
+ ${typeof d.meta?.ayanamsa === "number" ? import_lit15.html`<div class="ayan">
3389
+ ${d.meta.ayanamsaType ?? "Ayanamsa"}: ${formatNumber(d.meta.ayanamsa, 4)}°
3390
+ ${d.meta.houseSystem ? import_lit15.html`· ${d.meta.houseSystem} houses` : import_lit15.nothing}
3391
+ </div>` : import_lit15.nothing}
3392
+ </header>
3393
+
3394
+ <div
3395
+ class="tablist"
3396
+ role="tablist"
3397
+ aria-label="KP chart views"
3398
+ @keydown=${this.onTabKeyDown}
3399
+ >
3400
+ ${["planets", "cusps"].map(
3401
+ (t) => import_lit15.html`<button
3402
+ class="tab"
3403
+ role="tab"
3404
+ id="tab-${t}"
3405
+ aria-selected=${this.activeTab === t ? "true" : "false"}
3406
+ aria-controls="panel-${t}"
3407
+ tabindex=${this.activeTab === t ? "0" : "-1"}
3408
+ @click=${() => {
3409
+ this.activeTab = t;
3410
+ }}
3411
+ >
3412
+ ${t === "planets" ? "Planets" : "Cusps"}
3413
+ </button>`
3414
+ )}
3415
+ </div>
3416
+
3417
+ <div id="panel-${this.activeTab}" role="tabpanel" aria-labelledby="tab-${this.activeTab}">
3418
+ ${this.activeTab === "planets" ? this.renderPlanets() : this.renderCusps()}
3419
+ </div>
3420
+ </div>`;
3421
+ }
3422
+ renderPlanets() {
3423
+ const bodies = this.bodies();
3424
+ if (!bodies.length)
3425
+ return import_lit15.html`<p class="roxy-empty" role="status">No planets</p>`;
3426
+ return import_lit15.html`<table role="table" aria-label="KP planets and nodes">
3427
+ <thead>
3428
+ <tr>
3429
+ <th scope="col">Body</th>
3430
+ <th scope="col">Sign</th>
3431
+ <th scope="col">House</th>
3432
+ <th scope="col">Nakshatra</th>
3433
+ <th scope="col">Star lord</th>
3434
+ <th scope="col">Sub lord</th>
3435
+ <th scope="col">Sub sub lord</th>
3436
+ <th scope="col">KP no.</th>
3437
+ </tr>
3438
+ </thead>
3439
+ <tbody>
3440
+ ${bodies.map(
3441
+ (b) => import_lit15.html`<tr>
3442
+ <td class="body">
3443
+ ${b.name}${b.retrograde ? import_lit15.html`<span class="retro">R</span>` : import_lit15.nothing}
3444
+ </td>
3445
+ <td>${b.sign ?? ""}</td>
3446
+ <td class="num">${typeof b.house === "number" ? b.house : ""}</td>
3447
+ <td>${b.nakshatra ?? ""}</td>
3448
+ <td>${b.starLord ?? ""}</td>
3449
+ <td>${b.subLord ?? ""}</td>
3450
+ <td>${b.subSubLord ?? ""}</td>
3451
+ <td class="num">${typeof b.kpNumber === "number" ? b.kpNumber : ""}</td>
3452
+ </tr>`
3453
+ )}
3454
+ </tbody>
3455
+ </table>`;
3456
+ }
3457
+ renderCusps() {
3458
+ const cusps = this.data?.cusps ?? [];
3459
+ if (!cusps.length)
3460
+ return import_lit15.html`<p class="roxy-empty" role="status">No cusps</p>`;
3461
+ return import_lit15.html`<table role="table" aria-label="KP Placidus cusps">
3462
+ <thead>
3463
+ <tr>
3464
+ <th scope="col">House</th>
3465
+ <th scope="col">Sign</th>
3466
+ <th scope="col">Sign lord</th>
3467
+ <th scope="col">Nakshatra</th>
3468
+ <th scope="col">Star lord</th>
3469
+ <th scope="col">Sub lord</th>
3470
+ <th scope="col">Sub sub lord</th>
3471
+ <th scope="col">KP no.</th>
3472
+ </tr>
3473
+ </thead>
3474
+ <tbody>
3475
+ ${cusps.map(
3476
+ (c) => import_lit15.html`<tr>
3477
+ <td class="body num">${c.house}</td>
3478
+ <td>${c.sign ?? ""}</td>
3479
+ <td>${c.signLord ?? ""}</td>
3480
+ <td>${c.nakshatra ?? ""}</td>
3481
+ <td>${c.starLord ?? ""}</td>
3482
+ <td>${c.subLord ?? ""}</td>
3483
+ <td>${c.subSubLord ?? ""}</td>
3484
+ <td class="num">${typeof c.kpNumber === "number" ? c.kpNumber : ""}</td>
3485
+ </tr>`
3486
+ )}
3487
+ </tbody>
3488
+ </table>`;
3489
+ }
3490
+ };
3491
+ RoxyKpChart.styles = [
3492
+ baseStyles,
3493
+ import_lit15.css`
3494
+ .wrap {
3495
+ border: 1px solid var(--roxy-border, #e4e4e7);
3496
+ border-radius: var(--roxy-radius-md, 8px);
3497
+ background: var(--roxy-bg, #fff);
3498
+ overflow: auto;
3499
+ box-shadow: var(--roxy-shadow-sm);
3500
+ }
3501
+ .head {
3502
+ padding: var(--roxy-space-md, 1rem);
3503
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
3504
+ display: grid;
3505
+ gap: var(--roxy-space-xs, 0.25rem);
3506
+ }
3507
+ .title {
3508
+ margin: 0;
3509
+ font-size: var(--roxy-text-lg, 1.125rem);
3510
+ font-weight: var(--roxy-weight-bold, 600);
3511
+ }
3512
+ .asc,
3513
+ .ayan {
3514
+ color: var(--roxy-muted, #71717a);
3515
+ font-size: var(--roxy-text-sm, 0.875rem);
3516
+ }
3517
+ .asc strong {
3518
+ color: var(--roxy-fg, #0a0a0a);
3519
+ }
3520
+ .tablist {
3521
+ display: flex;
3522
+ gap: 2px;
3523
+ padding: 0 var(--roxy-space-md, 1rem);
3524
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
3525
+ }
3526
+ .tab {
3527
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
3528
+ font-size: var(--roxy-text-sm, 0.875rem);
3529
+ background: none;
3530
+ border: none;
3531
+ border-bottom: 2px solid transparent;
3532
+ margin-bottom: -2px;
3533
+ cursor: pointer;
3534
+ color: var(--roxy-muted, #71717a);
3535
+ font-family: inherit;
3536
+ }
3537
+ .tab[aria-selected='true'] {
3538
+ color: var(--roxy-accent-fg, #b45309);
3539
+ border-bottom-color: var(--roxy-accent, #f59e0b);
3540
+ font-weight: var(--roxy-weight-bold, 600);
3541
+ }
3542
+ .tab:hover:not([aria-selected='true']) {
3543
+ color: var(--roxy-fg, #0a0a0a);
3544
+ }
3545
+ table {
3546
+ width: 100%;
3547
+ border-collapse: collapse;
3548
+ font-size: var(--roxy-text-sm, 0.875rem);
3549
+ min-width: 620px;
3550
+ }
3551
+ thead {
3552
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
3553
+ }
3554
+ th,
3555
+ td {
3556
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
3557
+ text-align: left;
3558
+ white-space: nowrap;
3559
+ }
3560
+ th {
3561
+ color: var(--roxy-muted, #71717a);
3562
+ font-weight: var(--roxy-weight-bold, 600);
3563
+ text-transform: uppercase;
3564
+ font-size: var(--roxy-text-xs, 0.75rem);
3565
+ letter-spacing: 0.04em;
3566
+ }
3567
+ tbody tr {
3568
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
3569
+ }
3570
+ td.body {
3571
+ font-weight: var(--roxy-weight-bold, 600);
3572
+ color: var(--roxy-fg, #0a0a0a);
3573
+ }
3574
+ .retro {
3575
+ color: var(--roxy-warning-fg, #9a3412);
3576
+ font-size: var(--roxy-text-xs, 0.75rem);
3577
+ font-weight: var(--roxy-weight-bold, 600);
3578
+ margin-left: 4px;
3579
+ }
3580
+ .num {
3581
+ font-variant-numeric: tabular-nums;
3582
+ }
3583
+ `
3584
+ ];
3585
+ __decorateClass([
3586
+ (0, import_decorators13.property)({ attribute: false })
3587
+ ], RoxyKpChart.prototype, "data", 2);
3588
+ __decorateClass([
3589
+ (0, import_decorators13.state)()
3590
+ ], RoxyKpChart.prototype, "activeTab", 2);
3591
+ RoxyKpChart = __decorateClass([
3592
+ (0, import_decorators13.customElement)("roxy-kp-chart")
3593
+ ], RoxyKpChart);
3594
+
3595
+ // packages/ui/src/components/kp-planets-table.ts
3596
+ var import_lit16 = require("lit");
3597
+ var import_decorators14 = require("lit/decorators.js");
3598
+ var RoxyKpPlanetsTable = class extends import_lit16.LitElement {
3168
3599
  constructor() {
3169
3600
  super(...arguments);
3170
3601
  this.data = null;
3171
3602
  }
3172
3603
  render() {
3173
3604
  if (!this.data)
3174
- return import_lit15.html`<div class="roxy-empty" role="status">No KP data</div>`;
3605
+ return import_lit16.html`<div class="roxy-empty" role="status">No KP data</div>`;
3175
3606
  const planets = this.data.planets ?? [];
3176
- return import_lit15.html`<div
3607
+ return import_lit16.html`<div
3177
3608
  class="wrap"
3178
3609
  aria-label="KP planets table"
3179
3610
  tabindex="0"
3180
3611
  >
3181
3612
  <header class="head">
3182
3613
  <h2 class="title">KP planets</h2>
3183
- ${typeof this.data.ayanamsa === "number" ? import_lit15.html`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : import_lit15.nothing}
3614
+ ${typeof this.data.ayanamsa === "number" ? import_lit16.html`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : import_lit16.nothing}
3184
3615
  </header>
3185
3616
  <table role="table">
3186
3617
  <thead>
@@ -3197,10 +3628,10 @@ var RoxyKpPlanetsTable = class extends import_lit15.LitElement {
3197
3628
  </thead>
3198
3629
  <tbody>
3199
3630
  ${planets.map(
3200
- (p) => import_lit15.html`<tr>
3631
+ (p) => import_lit16.html`<tr>
3201
3632
  <td class="planet">
3202
3633
  ${p.planet}
3203
- ${p.retrograde ? import_lit15.html`<span class="retro">R</span>` : import_lit15.nothing}
3634
+ ${p.retrograde ? import_lit16.html`<span class="retro">R</span>` : import_lit16.nothing}
3204
3635
  </td>
3205
3636
  <td>${p.sign ?? ""}</td>
3206
3637
  <td>${p.signLord ?? ""}</td>
@@ -3218,7 +3649,7 @@ var RoxyKpPlanetsTable = class extends import_lit15.LitElement {
3218
3649
  };
3219
3650
  RoxyKpPlanetsTable.styles = [
3220
3651
  baseStyles,
3221
- import_lit15.css`
3652
+ import_lit16.css`
3222
3653
  .wrap {
3223
3654
  border: 1px solid var(--roxy-border, #e4e4e7);
3224
3655
  border-radius: var(--roxy-radius-md, 8px);
@@ -3280,62 +3711,235 @@ RoxyKpPlanetsTable.styles = [
3280
3711
  `
3281
3712
  ];
3282
3713
  __decorateClass([
3283
- (0, import_decorators13.property)({ attribute: false })
3714
+ (0, import_decorators14.property)({ attribute: false })
3284
3715
  ], RoxyKpPlanetsTable.prototype, "data", 2);
3285
3716
  RoxyKpPlanetsTable = __decorateClass([
3286
- (0, import_decorators13.customElement)("roxy-kp-planets-table")
3717
+ (0, import_decorators14.customElement)("roxy-kp-planets-table")
3287
3718
  ], RoxyKpPlanetsTable);
3288
3719
 
3289
- // packages/ui/src/components/location-search.ts
3290
- var import_lit16 = require("lit");
3291
- var import_decorators14 = require("lit/decorators.js");
3292
-
3293
- // packages/ui/src/utils/debounce.ts
3294
- function debounce(fn, wait) {
3295
- let timer;
3296
- const debounced = ((...args) => {
3297
- if (timer) clearTimeout(timer);
3298
- timer = setTimeout(() => {
3299
- timer = void 0;
3300
- fn(...args);
3301
- }, wait);
3302
- });
3303
- debounced.cancel = () => {
3304
- if (timer) {
3305
- clearTimeout(timer);
3306
- timer = void 0;
3307
- }
3308
- };
3309
- return debounced;
3310
- }
3311
-
3312
- // packages/ui/src/components/location-search.ts
3313
- var RoxyLocationSearch = class extends import_lit16.LitElement {
3720
+ // packages/ui/src/components/kp-ruling-planets.ts
3721
+ var import_lit17 = require("lit");
3722
+ var import_decorators15 = require("lit/decorators.js");
3723
+ var RoxyKpRulingPlanets = class extends import_lit17.LitElement {
3314
3724
  constructor() {
3315
3725
  super(...arguments);
3316
- this.endpoint = "https://roxyapi.com/api/v2/location/search";
3317
- this.placeholder = "Search city";
3318
- this.defaultValue = "";
3319
- this.query = "";
3320
- this.results = [];
3321
- this.isOpen = false;
3322
- this.isLoading = false;
3323
- this.highlight = -1;
3324
- this.secretKeyWarned = false;
3325
- this.debouncedFetch = debounce((q) => {
3326
- void this.fetchResults(q);
3327
- }, 300);
3328
- this.onInput = (e) => {
3329
- const value = e.target.value;
3330
- this.query = value;
3331
- if (value.length < 2) {
3332
- this.results = [];
3333
- this.isOpen = false;
3334
- this.highlight = -1;
3335
- return;
3336
- }
3337
- this.debouncedFetch(value);
3338
- };
3726
+ this.data = null;
3727
+ }
3728
+ render() {
3729
+ if (!this.data)
3730
+ return import_lit17.html`<div class="roxy-empty" role="status">No ruling planets data</div>`;
3731
+ const d = this.data;
3732
+ const significators = d.significators ?? [];
3733
+ return import_lit17.html`<div class="wrap" aria-label="KP ruling planets">
3734
+ <header>
3735
+ <h2 class="title">KP ruling planets</h2>
3736
+ ${d.dayLord ? import_lit17.html`<div class="day-lord">Day lord: <strong>${d.dayLord}</strong></div>` : import_lit17.nothing}
3737
+ </header>
3738
+
3739
+ <div class="groups">
3740
+ <div class="group">
3741
+ <h3>Moon</h3>
3742
+ <dl>
3743
+ <dt>Sign lord</dt><dd>${d.moonSignLord ?? ""}</dd>
3744
+ <dt>Star lord</dt><dd>${d.moonStarLord ?? ""}</dd>
3745
+ <dt>Sub lord</dt><dd>${d.moonSublord ?? ""}</dd>
3746
+ <dt>Sub-sub lord</dt><dd>${d.moonSubSublord ?? ""}</dd>
3747
+ </dl>
3748
+ </div>
3749
+ <div class="group">
3750
+ <h3>Lagna</h3>
3751
+ <dl>
3752
+ <dt>Sign lord</dt><dd>${d.lagnaSignLord ?? ""}</dd>
3753
+ <dt>Star lord</dt><dd>${d.lagnaStarLord ?? ""}</dd>
3754
+ <dt>Sub lord</dt><dd>${d.lagnaSublord ?? ""}</dd>
3755
+ <dt>Sub-sub lord</dt><dd>${d.lagnaSubSublord ?? ""}</dd>
3756
+ </dl>
3757
+ </div>
3758
+ </div>
3759
+
3760
+ ${d.rulingPlanets?.length ? import_lit17.html`<div class="rp-list" role="list" aria-label="Ruling planets by strength">
3761
+ <span class="rp-label">Ruling planets</span>
3762
+ ${d.rulingPlanets.map(
3763
+ (p, i) => import_lit17.html`<span class="rp" role="listitem"><span class="rank">${i + 1}</span> ${p}</span>`
3764
+ )}
3765
+ </div>` : import_lit17.nothing}
3766
+
3767
+ ${significators.length ? import_lit17.html`<table aria-label="House significators">
3768
+ <thead>
3769
+ <tr>
3770
+ <th scope="col">Planet</th>
3771
+ <th scope="col">Signifies houses</th>
3772
+ </tr>
3773
+ </thead>
3774
+ <tbody>
3775
+ ${significators.map(
3776
+ (s) => import_lit17.html`<tr>
3777
+ <td>${s.planet}</td>
3778
+ <td>${(s.signifies ?? []).join(", ")}</td>
3779
+ </tr>`
3780
+ )}
3781
+ </tbody>
3782
+ </table>` : import_lit17.nothing}
3783
+ </div>`;
3784
+ }
3785
+ };
3786
+ RoxyKpRulingPlanets.styles = [
3787
+ baseStyles,
3788
+ import_lit17.css`
3789
+ .wrap {
3790
+ border: 1px solid var(--roxy-border, #e4e4e7);
3791
+ border-radius: var(--roxy-radius-md, 8px);
3792
+ background: var(--roxy-bg, #fff);
3793
+ padding: var(--roxy-space-md, 1rem);
3794
+ display: grid;
3795
+ gap: var(--roxy-space-md, 1rem);
3796
+ box-shadow: var(--roxy-shadow-sm);
3797
+ }
3798
+ .title {
3799
+ margin: 0;
3800
+ font-size: var(--roxy-text-lg, 1.125rem);
3801
+ font-weight: var(--roxy-weight-bold, 600);
3802
+ }
3803
+ .day-lord {
3804
+ color: var(--roxy-muted, #71717a);
3805
+ font-size: var(--roxy-text-sm, 0.875rem);
3806
+ }
3807
+ .day-lord strong {
3808
+ color: var(--roxy-fg, #0a0a0a);
3809
+ }
3810
+ .groups {
3811
+ display: grid;
3812
+ grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
3813
+ gap: var(--roxy-space-md, 1rem);
3814
+ }
3815
+ .group h3 {
3816
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
3817
+ font-size: var(--roxy-text-xs, 0.75rem);
3818
+ font-weight: var(--roxy-weight-bold, 600);
3819
+ color: var(--roxy-muted, #71717a);
3820
+ text-transform: uppercase;
3821
+ letter-spacing: 0.05em;
3822
+ }
3823
+ .group dl {
3824
+ margin: 0;
3825
+ display: grid;
3826
+ grid-template-columns: auto 1fr;
3827
+ gap: 2px var(--roxy-space-sm, 0.5rem);
3828
+ font-size: var(--roxy-text-sm, 0.875rem);
3829
+ }
3830
+ .group dt {
3831
+ color: var(--roxy-muted, #71717a);
3832
+ }
3833
+ .group dd {
3834
+ margin: 0;
3835
+ color: var(--roxy-fg, #0a0a0a);
3836
+ font-weight: var(--roxy-weight-bold, 600);
3837
+ }
3838
+ .rp-list {
3839
+ display: flex;
3840
+ flex-wrap: wrap;
3841
+ gap: var(--roxy-space-xs, 0.25rem);
3842
+ align-items: center;
3843
+ }
3844
+ .rp-label {
3845
+ font-size: var(--roxy-text-xs, 0.75rem);
3846
+ color: var(--roxy-muted, #71717a);
3847
+ text-transform: uppercase;
3848
+ letter-spacing: 0.05em;
3849
+ margin-right: var(--roxy-space-xs, 0.25rem);
3850
+ }
3851
+ .rp {
3852
+ display: inline-flex;
3853
+ align-items: center;
3854
+ gap: 0.3em;
3855
+ font-size: var(--roxy-text-sm, 0.875rem);
3856
+ font-weight: var(--roxy-weight-bold, 600);
3857
+ padding: 0.15em 0.6em;
3858
+ border-radius: 999px;
3859
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 18%, transparent);
3860
+ color: var(--roxy-fg, #0a0a0a);
3861
+ }
3862
+ .rp .rank {
3863
+ font-size: var(--roxy-text-xs, 0.75rem);
3864
+ color: var(--roxy-accent-fg, #b45309);
3865
+ }
3866
+ table {
3867
+ width: 100%;
3868
+ border-collapse: collapse;
3869
+ font-size: var(--roxy-text-sm, 0.875rem);
3870
+ }
3871
+ th,
3872
+ td {
3873
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-sm, 0.5rem);
3874
+ text-align: left;
3875
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
3876
+ }
3877
+ th {
3878
+ color: var(--roxy-muted, #71717a);
3879
+ font-weight: var(--roxy-weight-bold, 600);
3880
+ text-transform: uppercase;
3881
+ font-size: var(--roxy-text-xs, 0.75rem);
3882
+ letter-spacing: 0.04em;
3883
+ }
3884
+ `
3885
+ ];
3886
+ __decorateClass([
3887
+ (0, import_decorators15.property)({ attribute: false })
3888
+ ], RoxyKpRulingPlanets.prototype, "data", 2);
3889
+ RoxyKpRulingPlanets = __decorateClass([
3890
+ (0, import_decorators15.customElement)("roxy-kp-ruling-planets")
3891
+ ], RoxyKpRulingPlanets);
3892
+
3893
+ // packages/ui/src/components/location-search.ts
3894
+ var import_lit18 = require("lit");
3895
+ var import_decorators16 = require("lit/decorators.js");
3896
+
3897
+ // packages/ui/src/utils/debounce.ts
3898
+ function debounce(fn, wait) {
3899
+ let timer;
3900
+ const debounced = ((...args) => {
3901
+ if (timer) clearTimeout(timer);
3902
+ timer = setTimeout(() => {
3903
+ timer = void 0;
3904
+ fn(...args);
3905
+ }, wait);
3906
+ });
3907
+ debounced.cancel = () => {
3908
+ if (timer) {
3909
+ clearTimeout(timer);
3910
+ timer = void 0;
3911
+ }
3912
+ };
3913
+ return debounced;
3914
+ }
3915
+
3916
+ // packages/ui/src/components/location-search.ts
3917
+ var RoxyLocationSearch = class extends import_lit18.LitElement {
3918
+ constructor() {
3919
+ super(...arguments);
3920
+ this.endpoint = "https://roxyapi.com/api/v2/location/search";
3921
+ this.placeholder = "Search city";
3922
+ this.defaultValue = "";
3923
+ this.query = "";
3924
+ this.results = [];
3925
+ this.isOpen = false;
3926
+ this.isLoading = false;
3927
+ this.highlight = -1;
3928
+ this.secretKeyWarned = false;
3929
+ this.debouncedFetch = debounce((q) => {
3930
+ void this.fetchResults(q);
3931
+ }, 300);
3932
+ this.onInput = (e) => {
3933
+ const value = e.target.value;
3934
+ this.query = value;
3935
+ if (value.length < 2) {
3936
+ this.results = [];
3937
+ this.isOpen = false;
3938
+ this.highlight = -1;
3939
+ return;
3940
+ }
3941
+ this.debouncedFetch(value);
3942
+ };
3339
3943
  this.onKeyDown = (e) => {
3340
3944
  if (!this.isOpen || this.results.length === 0) {
3341
3945
  if (e.key === "ArrowDown" && this.query.length >= 2) {
@@ -3407,8 +4011,13 @@ var RoxyLocationSearch = class extends import_lit16.LitElement {
3407
4011
  const headers = {
3408
4012
  Accept: "application/json"
3409
4013
  };
3410
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
3411
- if (this.publishableKey) headers["X-API-Key"] = this.publishableKey;
4014
+ if (this.apiKey && this.publishableKey) {
4015
+ console.warn(
4016
+ "[roxy-location-search] both api-key and publishable-key set; using publishable-key. Remove api-key from your widget markup."
4017
+ );
4018
+ }
4019
+ const key = this.publishableKey ?? this.apiKey;
4020
+ if (key) headers["X-API-Key"] = key;
3412
4021
  const res = await fetch(url, { headers, signal: controller.signal });
3413
4022
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
3414
4023
  const json = await res.json();
@@ -3440,12 +4049,13 @@ var RoxyLocationSearch = class extends import_lit16.LitElement {
3440
4049
  );
3441
4050
  }
3442
4051
  render() {
3443
- return import_lit16.html`<div class="field">
4052
+ return import_lit18.html`<div class="field">
3444
4053
  <input
3445
4054
  type="text"
3446
4055
  role="combobox"
3447
4056
  aria-expanded=${this.isOpen ? "true" : "false"}
3448
4057
  aria-controls="roxy-location-listbox"
4058
+ aria-activedescendant=${this.isOpen && this.highlight >= 0 ? `roxy-location-option-${this.highlight}` : ""}
3449
4059
  aria-autocomplete="list"
3450
4060
  autocomplete="off"
3451
4061
  placeholder=${this.placeholder}
@@ -3456,18 +4066,19 @@ var RoxyLocationSearch = class extends import_lit16.LitElement {
3456
4066
  if (this.results.length > 0) this.isOpen = true;
3457
4067
  }}
3458
4068
  />
3459
- ${this.isLoading ? import_lit16.html`<span class="spinner" role="status" aria-label="Loading"></span>` : import_lit16.nothing}
3460
- ${this.isOpen ? import_lit16.html`<ul
4069
+ ${this.isLoading ? import_lit18.html`<span class="spinner" role="status" aria-label="Loading"></span>` : import_lit18.nothing}
4070
+ ${this.isOpen ? import_lit18.html`<ul
3461
4071
  id="roxy-location-listbox"
3462
4072
  class="results"
3463
4073
  role="listbox"
3464
4074
  >
3465
- ${this.results.length === 0 ? import_lit16.html`<li class="empty" role="status">No cities found</li>` : this.results.map(
3466
- (city, idx) => import_lit16.html`<li role="presentation">
4075
+ ${this.results.length === 0 ? import_lit18.html`<li class="empty" role="status">No cities found</li>` : this.results.map(
4076
+ (city, idx) => import_lit18.html`<li role="presentation">
3467
4077
  <button
3468
4078
  type="button"
3469
4079
  class="option"
3470
4080
  role="option"
4081
+ id=${`roxy-location-option-${idx}`}
3471
4082
  aria-selected=${this.highlight === idx ? "true" : "false"}
3472
4083
  @click=${() => this.select(city)}
3473
4084
  @mouseenter=${() => {
@@ -3476,7 +4087,7 @@ var RoxyLocationSearch = class extends import_lit16.LitElement {
3476
4087
  >
3477
4088
  <span class="city">${city.city}</span>
3478
4089
  <span class="where"
3479
- >${city.province ? import_lit16.html`${city.province}, ` : ""}${city.country}</span
4090
+ >${city.province ? import_lit18.html`${city.province}, ` : ""}${city.country}</span
3480
4091
  >
3481
4092
  <span class="tz"
3482
4093
  >UTC${city.utcOffset >= 0 ? "+" : ""}${city.utcOffset}</span
@@ -3484,13 +4095,13 @@ var RoxyLocationSearch = class extends import_lit16.LitElement {
3484
4095
  </button>
3485
4096
  </li>`
3486
4097
  )}
3487
- </ul>` : import_lit16.nothing}
4098
+ </ul>` : import_lit18.nothing}
3488
4099
  </div>`;
3489
4100
  }
3490
4101
  };
3491
4102
  RoxyLocationSearch.styles = [
3492
4103
  baseStyles,
3493
- import_lit16.css`
4104
+ import_lit18.css`
3494
4105
  :host {
3495
4106
  display: block;
3496
4107
  position: relative;
@@ -3594,43 +4205,43 @@ RoxyLocationSearch.styles = [
3594
4205
  `
3595
4206
  ];
3596
4207
  __decorateClass([
3597
- (0, import_decorators14.property)({ type: String, attribute: "api-key" })
4208
+ (0, import_decorators16.property)({ type: String, attribute: "api-key" })
3598
4209
  ], RoxyLocationSearch.prototype, "apiKey", 2);
3599
4210
  __decorateClass([
3600
- (0, import_decorators14.property)({ type: String, attribute: "publishable-key" })
4211
+ (0, import_decorators16.property)({ type: String, attribute: "publishable-key" })
3601
4212
  ], RoxyLocationSearch.prototype, "publishableKey", 2);
3602
4213
  __decorateClass([
3603
- (0, import_decorators14.property)({ type: String })
4214
+ (0, import_decorators16.property)({ type: String })
3604
4215
  ], RoxyLocationSearch.prototype, "endpoint", 2);
3605
4216
  __decorateClass([
3606
- (0, import_decorators14.property)({ type: String })
4217
+ (0, import_decorators16.property)({ type: String })
3607
4218
  ], RoxyLocationSearch.prototype, "placeholder", 2);
3608
4219
  __decorateClass([
3609
- (0, import_decorators14.property)({ type: String, attribute: "default-value" })
4220
+ (0, import_decorators16.property)({ type: String, attribute: "default-value" })
3610
4221
  ], RoxyLocationSearch.prototype, "defaultValue", 2);
3611
4222
  __decorateClass([
3612
- (0, import_decorators14.state)()
4223
+ (0, import_decorators16.state)()
3613
4224
  ], RoxyLocationSearch.prototype, "query", 2);
3614
4225
  __decorateClass([
3615
- (0, import_decorators14.state)()
4226
+ (0, import_decorators16.state)()
3616
4227
  ], RoxyLocationSearch.prototype, "results", 2);
3617
4228
  __decorateClass([
3618
- (0, import_decorators14.state)()
4229
+ (0, import_decorators16.state)()
3619
4230
  ], RoxyLocationSearch.prototype, "isOpen", 2);
3620
4231
  __decorateClass([
3621
- (0, import_decorators14.state)()
4232
+ (0, import_decorators16.state)()
3622
4233
  ], RoxyLocationSearch.prototype, "isLoading", 2);
3623
4234
  __decorateClass([
3624
- (0, import_decorators14.state)()
4235
+ (0, import_decorators16.state)()
3625
4236
  ], RoxyLocationSearch.prototype, "highlight", 2);
3626
4237
  RoxyLocationSearch = __decorateClass([
3627
- (0, import_decorators14.customElement)("roxy-location-search")
4238
+ (0, import_decorators16.customElement)("roxy-location-search")
3628
4239
  ], RoxyLocationSearch);
3629
4240
 
3630
4241
  // packages/ui/src/components/moon-phase.ts
3631
- var import_lit17 = require("lit");
3632
- var import_decorators15 = require("lit/decorators.js");
3633
- var RoxyMoonPhase = class extends import_lit17.LitElement {
4242
+ var import_lit19 = require("lit");
4243
+ var import_decorators17 = require("lit/decorators.js");
4244
+ var RoxyMoonPhase = class extends import_lit19.LitElement {
3634
4245
  constructor() {
3635
4246
  super(...arguments);
3636
4247
  this.data = null;
@@ -3639,12 +4250,12 @@ var RoxyMoonPhase = class extends import_lit17.LitElement {
3639
4250
  render() {
3640
4251
  const d = this.data;
3641
4252
  if (!d)
3642
- return import_lit17.html`<div class="roxy-empty" role="status">No moon phase data</div>`;
4253
+ return import_lit19.html`<div class="roxy-empty" role="status">No moon phase data</div>`;
3643
4254
  const list = "phases" in d ? d.phases : "calendar" in d ? d.calendar : [];
3644
4255
  if (this.mode !== "current" && list.length > 0) {
3645
4256
  const month = "month" in d ? d.month : void 0;
3646
4257
  const year = "year" in d ? d.year : void 0;
3647
- return import_lit17.html`<article
4258
+ return import_lit19.html`<article
3648
4259
  class="card"
3649
4260
  aria-label="Moon phase calendar"
3650
4261
  >
@@ -3654,46 +4265,46 @@ var RoxyMoonPhase = class extends import_lit17.LitElement {
3654
4265
  </div>
3655
4266
  </article>`;
3656
4267
  }
3657
- if (!("phase" in d)) return import_lit17.nothing;
4268
+ if (!("phase" in d)) return import_lit19.nothing;
3658
4269
  return this.renderSingle(d);
3659
4270
  }
3660
4271
  renderSingle(d) {
3661
4272
  const emoji = phaseEmoji(d.phase);
3662
- return import_lit17.html`<article class="card" aria-label="Current moon phase">
4273
+ return import_lit19.html`<article class="card" aria-label="Current moon phase">
3663
4274
  <div class="hero">
3664
4275
  <span class="emoji" aria-hidden="true">${emoji}</span>
3665
4276
  <div>
3666
4277
  <h2 class="label">${d.phase ?? "Moon"}</h2>
3667
- ${d.date ? import_lit17.html`<div class="date">${d.date}</div>` : import_lit17.nothing}
4278
+ ${d.date ? import_lit19.html`<div class="date">${d.date}</div>` : import_lit19.nothing}
3668
4279
  </div>
3669
4280
  </div>
3670
4281
  <div class="stats">
3671
- ${typeof d.illumination === "number" ? import_lit17.html`<div>
4282
+ ${typeof d.illumination === "number" ? import_lit19.html`<div>
3672
4283
  <span>Illumination</span>
3673
4284
  <strong>${formatIllumination(d.illumination)}</strong>
3674
- </div>` : import_lit17.nothing}
3675
- ${typeof d.age === "number" ? import_lit17.html`<div>
4285
+ </div>` : import_lit19.nothing}
4286
+ ${typeof d.age === "number" ? import_lit19.html`<div>
3676
4287
  <span>Age</span>
3677
4288
  <strong>${formatNumber(d.age, 1)} days</strong>
3678
- </div>` : import_lit17.nothing}
3679
- ${d.sign ? import_lit17.html`<div>
4289
+ </div>` : import_lit19.nothing}
4290
+ ${d.sign ? import_lit19.html`<div>
3680
4291
  <span>Sign</span>
3681
4292
  <strong>${d.sign}</strong>
3682
- </div>` : import_lit17.nothing}
3683
- ${typeof d.distance === "number" ? import_lit17.html`<div>
4293
+ </div>` : import_lit19.nothing}
4294
+ ${typeof d.distance === "number" ? import_lit19.html`<div>
3684
4295
  <span>Distance</span>
3685
4296
  <strong>${(d.distance / 1e3).toFixed(0)}k km</strong>
3686
- </div>` : import_lit17.nothing}
4297
+ </div>` : import_lit19.nothing}
3687
4298
  </div>
3688
- ${d.meaning?.description ? import_lit17.html`<p class="meaning">${d.meaning.description}</p>` : import_lit17.nothing}
3689
- ${d.meaning?.keywords?.length ? import_lit17.html`<div class="keywords">
3690
- ${d.meaning.keywords.map((k) => import_lit17.html`<span>${k}</span>`)}
3691
- </div>` : import_lit17.nothing}
4299
+ ${d.meaning?.description ? import_lit19.html`<p class="meaning">${d.meaning.description}</p>` : import_lit19.nothing}
4300
+ ${d.meaning?.keywords?.length ? import_lit19.html`<div class="keywords">
4301
+ ${d.meaning.keywords.map((k) => import_lit19.html`<span>${k}</span>`)}
4302
+ </div>` : import_lit19.nothing}
3692
4303
  </article>`;
3693
4304
  }
3694
4305
  renderListItem(p) {
3695
4306
  const emoji = phaseEmoji(p.phase);
3696
- return import_lit17.html`<div class="list-item" role="listitem">
4307
+ return import_lit19.html`<div class="list-item" role="listitem">
3697
4308
  <span aria-hidden="true">${emoji}</span>
3698
4309
  <span>${p.phase}</span>
3699
4310
  <span>${p.date ?? ""}</span>
@@ -3702,7 +4313,7 @@ var RoxyMoonPhase = class extends import_lit17.LitElement {
3702
4313
  };
3703
4314
  RoxyMoonPhase.styles = [
3704
4315
  baseStyles,
3705
- import_lit17.css`
4316
+ import_lit19.css`
3706
4317
  .card {
3707
4318
  background: var(--roxy-bg, #fff);
3708
4319
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -3787,13 +4398,13 @@ RoxyMoonPhase.styles = [
3787
4398
  `
3788
4399
  ];
3789
4400
  __decorateClass([
3790
- (0, import_decorators15.property)({ attribute: false })
4401
+ (0, import_decorators17.property)({ attribute: false })
3791
4402
  ], RoxyMoonPhase.prototype, "data", 2);
3792
4403
  __decorateClass([
3793
- (0, import_decorators15.property)({ type: String, reflect: true })
4404
+ (0, import_decorators17.property)({ type: String, reflect: true })
3794
4405
  ], RoxyMoonPhase.prototype, "mode", 2);
3795
4406
  RoxyMoonPhase = __decorateClass([
3796
- (0, import_decorators15.customElement)("roxy-moon-phase")
4407
+ (0, import_decorators17.customElement)("roxy-moon-phase")
3797
4408
  ], RoxyMoonPhase);
3798
4409
  function phaseEmoji(phase) {
3799
4410
  if (!phase) return "\u{1F319}";
@@ -3804,117 +4415,320 @@ function formatIllumination(v) {
3804
4415
  return `${Math.round(pct)}%`;
3805
4416
  }
3806
4417
 
3807
- // packages/ui/src/components/natal-chart.ts
3808
- var import_lit18 = require("lit");
3809
- var import_decorators16 = require("lit/decorators.js");
3810
-
3811
- // packages/ui/src/utils/degree.ts
3812
- function polarToCartesian(cx, cy, radius, angleDeg) {
3813
- const angleRad = angleDeg * Math.PI / 180;
3814
- return {
3815
- x: cx + radius * Math.cos(angleRad),
3816
- y: cy + radius * Math.sin(angleRad)
3817
- };
3818
- }
3819
-
3820
- // packages/ui/src/components/natal-chart.ts
3821
- var SIZE = 420;
3822
- var CENTER = SIZE / 2;
3823
- var OUTER_R = 164;
3824
- var SIGN_R = 146;
3825
- var HOUSE_R = 120;
3826
- var PLANET_R = 96;
3827
- var ANGLE_TICK_R = 178;
3828
- var ANGLE_LABEL_R = 196;
3829
- var RoxyNatalChart = class extends import_lit18.LitElement {
4418
+ // packages/ui/src/components/nakshatra-card.ts
4419
+ var import_lit20 = require("lit");
4420
+ var import_decorators18 = require("lit/decorators.js");
4421
+ var RoxyNakshatraCard = class extends import_lit20.LitElement {
3830
4422
  constructor() {
3831
4423
  super(...arguments);
3832
4424
  this.data = null;
3833
- this.houseSystem = "placidus";
3834
- }
3835
- getPlanets() {
3836
- return this.data?.planets ?? [];
3837
- }
3838
- getAscendant() {
3839
- return this.data?.ascendant?.longitude ?? 0;
3840
- }
3841
- getMidheaven() {
3842
- const m = this.data?.midheaven?.longitude;
3843
- return typeof m === "number" ? m : null;
3844
- }
3845
- toAngle(lon) {
3846
- return 180 + this.getAscendant() - lon;
3847
4425
  }
3848
4426
  render() {
3849
4427
  if (!this.data)
3850
- return import_lit18.html`<div class="roxy-empty" role="status">No chart data</div>`;
3851
- const planets = this.getPlanets();
3852
- const aspects = this.data.aspects ?? [];
3853
- return import_lit18.html`<div class="wrap">
3854
- <header>
3855
- <h2 class="title">Natal chart</h2>
3856
- ${this.data.birthDetails ? import_lit18.html`<div class="meta">
3857
- ${[this.data.birthDetails.date, this.data.birthDetails.time].filter(Boolean).join(" \xB7 ")}
3858
- </div>` : import_lit18.nothing}
4428
+ return import_lit20.html`<div class="roxy-empty" role="status">No nakshatra data</div>`;
4429
+ const n = this.data;
4430
+ const remedies = n.remedies;
4431
+ return import_lit20.html`<article class="wrap" aria-label=${`Nakshatra ${n.name}`}>
4432
+ <header class="head">
4433
+ <h2 class="name">${n.name}</h2>
4434
+ ${typeof n.number === "number" ? import_lit20.html`<span class="number">Nakshatra ${n.number} of 27</span>` : import_lit20.nothing}
4435
+ ${n.range ? import_lit20.html`<span class="range">${n.range}</span>` : import_lit20.nothing}
3859
4436
  </header>
3860
- <svg
3861
- viewBox="0 0 ${SIZE} ${SIZE}"
3862
- role="img"
3863
- aria-label="Natal chart wheel with twelve houses, planets, and aspects"
3864
- >
3865
- <title>Natal chart wheel</title>
3866
- <desc>
3867
- Twelve zodiac sign segments around a circular wheel. Planet glyphs are
3868
- placed at their ecliptic longitudes. Aspect lines connect related planets.
3869
- </desc>
3870
- <circle
3871
- class="wheel-line"
3872
- cx=${CENTER}
3873
- cy=${CENTER}
3874
- r=${OUTER_R}
3875
- stroke-width="1.5"
3876
- />
3877
- <circle
3878
- class="wheel-line"
3879
- cx=${CENTER}
3880
- cy=${CENTER}
3881
- r=${HOUSE_R}
3882
- stroke-width="1"
3883
- />
3884
- <circle
3885
- class="wheel-line"
3886
- cx=${CENTER}
3887
- cy=${CENTER}
3888
- r=${PLANET_R - 16}
3889
- stroke-width="0.5"
3890
- />
3891
- ${this.renderSpokes()} ${this.renderSigns()} ${this.renderHouseNumbers()}
3892
- ${this.renderAspects(planets, aspects)} ${this.renderPlanets(planets)}
3893
- ${this.renderAngles()}
3894
- </svg>
3895
- <div class="legend">
3896
- <span>${planets.length} planets</span>
3897
- <span>${aspects.length} aspects</span>
3898
- <span><span class="legend-swatch" style="background: var(--roxy-success)"></span>harmonious</span>
3899
- <span><span class="legend-swatch" style="background: var(--roxy-danger)"></span>challenging</span>
3900
- </div>
3901
- ${this.renderDetails()}
3902
- ${this.renderInterpretations()}
3903
- </div>`;
3904
- }
3905
- renderAngles() {
3906
- const asc = this.getAscendant();
3907
- const mc = this.getMidheaven();
3908
- const items = [this.renderAngleMark(asc, "ASC")];
3909
- if (mc !== null) items.push(this.renderAngleMark(mc, "MC"));
3910
- return items;
4437
+
4438
+ <dl class="facts">
4439
+ ${n.lord ? import_lit20.html`<div class="fact"><dt>Lord</dt><dd>${n.lord}</dd></div>` : import_lit20.nothing}
4440
+ ${n.deity ? import_lit20.html`<div class="fact"><dt>Deity</dt><dd>${n.deity}</dd></div>` : import_lit20.nothing}
4441
+ ${n.symbol ? import_lit20.html`<div class="fact"><dt>Symbol</dt><dd>${n.symbol}</dd></div>` : import_lit20.nothing}
4442
+ </dl>
4443
+
4444
+ ${n.characteristics ? import_lit20.html`<div class="section">
4445
+ <h3>Characteristics</h3>
4446
+ <p>${n.characteristics}</p>
4447
+ </div>` : import_lit20.nothing}
4448
+
4449
+ ${remedies ? import_lit20.html`<div class="section">
4450
+ <h3>Remedies</h3>
4451
+ <div class="remedies">
4452
+ ${remedies.mantras ? import_lit20.html`<div class="remedy"><strong>Mantras:</strong> ${remedies.mantras}</div>` : import_lit20.nothing}
4453
+ ${remedies.gemstones ? import_lit20.html`<div class="remedy"><strong>Gemstones:</strong> ${remedies.gemstones}</div>` : import_lit20.nothing}
4454
+ ${remedies.rituals ? import_lit20.html`<div class="remedy"><strong>Rituals:</strong> ${remedies.rituals}</div>` : import_lit20.nothing}
4455
+ </div>
4456
+ </div>` : import_lit20.nothing}
4457
+ </article>`;
3911
4458
  }
3912
- renderAngleMark(longitude, label) {
3913
- const angle = this.toAngle(longitude);
4459
+ };
4460
+ RoxyNakshatraCard.styles = [
4461
+ baseStyles,
4462
+ import_lit20.css`
4463
+ .wrap {
4464
+ border: 1px solid var(--roxy-border, #e4e4e7);
4465
+ border-radius: var(--roxy-radius-md, 8px);
4466
+ background: var(--roxy-bg, #fff);
4467
+ padding: var(--roxy-space-md, 1rem);
4468
+ display: grid;
4469
+ gap: var(--roxy-space-md, 1rem);
4470
+ box-shadow: var(--roxy-shadow-sm);
4471
+ }
4472
+ .head {
4473
+ display: flex;
4474
+ align-items: baseline;
4475
+ gap: var(--roxy-space-sm, 0.5rem);
4476
+ flex-wrap: wrap;
4477
+ }
4478
+ .name {
4479
+ margin: 0;
4480
+ font-size: var(--roxy-text-lg, 1.125rem);
4481
+ font-weight: var(--roxy-weight-bold, 600);
4482
+ }
4483
+ .number {
4484
+ color: var(--roxy-accent-fg, #b45309);
4485
+ font-size: var(--roxy-text-sm, 0.875rem);
4486
+ font-weight: var(--roxy-weight-bold, 600);
4487
+ }
4488
+ .range {
4489
+ color: var(--roxy-muted, #71717a);
4490
+ font-size: var(--roxy-text-sm, 0.875rem);
4491
+ }
4492
+ .facts {
4493
+ display: grid;
4494
+ grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
4495
+ gap: var(--roxy-space-sm, 0.5rem);
4496
+ }
4497
+ .fact {
4498
+ display: grid;
4499
+ gap: 2px;
4500
+ }
4501
+ .fact dt {
4502
+ color: var(--roxy-muted, #71717a);
4503
+ font-size: var(--roxy-text-xs, 0.75rem);
4504
+ text-transform: uppercase;
4505
+ letter-spacing: 0.05em;
4506
+ }
4507
+ .fact dd {
4508
+ margin: 0;
4509
+ color: var(--roxy-fg, #0a0a0a);
4510
+ font-size: var(--roxy-text-sm, 0.875rem);
4511
+ }
4512
+ .section h3 {
4513
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
4514
+ font-size: var(--roxy-text-sm, 0.875rem);
4515
+ font-weight: var(--roxy-weight-bold, 600);
4516
+ color: var(--roxy-muted, #71717a);
4517
+ text-transform: uppercase;
4518
+ letter-spacing: 0.05em;
4519
+ }
4520
+ .section p {
4521
+ margin: 0;
4522
+ font-size: var(--roxy-text-sm, 0.875rem);
4523
+ color: var(--roxy-fg, #0a0a0a);
4524
+ line-height: 1.5;
4525
+ }
4526
+ .remedies {
4527
+ display: grid;
4528
+ gap: var(--roxy-space-xs, 0.25rem);
4529
+ }
4530
+ .remedy {
4531
+ font-size: var(--roxy-text-sm, 0.875rem);
4532
+ color: var(--roxy-fg, #0a0a0a);
4533
+ }
4534
+ .remedy strong {
4535
+ color: var(--roxy-muted, #71717a);
4536
+ font-weight: var(--roxy-weight-bold, 600);
4537
+ }
4538
+ `
4539
+ ];
4540
+ __decorateClass([
4541
+ (0, import_decorators18.property)({ attribute: false })
4542
+ ], RoxyNakshatraCard.prototype, "data", 2);
4543
+ RoxyNakshatraCard = __decorateClass([
4544
+ (0, import_decorators18.customElement)("roxy-nakshatra-card")
4545
+ ], RoxyNakshatraCard);
4546
+
4547
+ // packages/ui/src/components/natal-chart.ts
4548
+ var import_lit21 = require("lit");
4549
+ var import_decorators19 = require("lit/decorators.js");
4550
+ var SIZE = 420;
4551
+ var CENTER = SIZE / 2;
4552
+ var OUTER_R = 164;
4553
+ var SIGN_R = 146;
4554
+ var HOUSE_R = 120;
4555
+ var PLANET_R = 96;
4556
+ var ANGLE_TICK_R = 178;
4557
+ var ANGLE_LABEL_R = 196;
4558
+ var RoxyNatalChart = class extends import_lit21.LitElement {
4559
+ constructor() {
4560
+ super(...arguments);
4561
+ this.data = null;
4562
+ this.houseSystem = "placidus";
4563
+ this.view = "wheel";
4564
+ }
4565
+ getPlanets() {
4566
+ return this.data?.planets ?? [];
4567
+ }
4568
+ getAscendant() {
4569
+ return this.data?.ascendant?.longitude ?? 0;
4570
+ }
4571
+ getMidheaven() {
4572
+ const m = this.data?.midheaven?.longitude;
4573
+ return typeof m === "number" ? m : null;
4574
+ }
4575
+ toAngle(lon) {
4576
+ return 180 + this.getAscendant() - lon;
4577
+ }
4578
+ render() {
4579
+ if (!this.data)
4580
+ return import_lit21.html`<div class="roxy-empty" role="status">No chart data</div>`;
4581
+ const planets = this.getPlanets();
4582
+ const aspects = this.data.aspects ?? [];
4583
+ const view = this.view;
4584
+ return import_lit21.html`<div class="wrap">
4585
+ <header>
4586
+ <h2 class="title">Natal chart</h2>
4587
+ ${this.data.birthDetails ? import_lit21.html`<div class="meta">
4588
+ ${[this.data.birthDetails.date, this.data.birthDetails.time].filter(Boolean).join(" \xB7 ")}
4589
+ </div>` : import_lit21.nothing}
4590
+ </header>
4591
+ <div
4592
+ class="tablist"
4593
+ role="tablist"
4594
+ aria-label="Natal chart views"
4595
+ @keydown=${this.onTabKeyDown}
4596
+ >
4597
+ ${["wheel", "grid"].map(
4598
+ (t) => import_lit21.html`<button
4599
+ class="tab"
4600
+ role="tab"
4601
+ id="tab-${t}"
4602
+ aria-selected=${view === t ? "true" : "false"}
4603
+ aria-controls="panel-${t}"
4604
+ tabindex=${view === t ? "0" : "-1"}
4605
+ @click=${() => {
4606
+ this.view = t;
4607
+ }}
4608
+ >
4609
+ ${t === "wheel" ? "Wheel" : "Aspect grid"}
4610
+ </button>`
4611
+ )}
4612
+ </div>
4613
+ <div id="panel-${view}" role="tabpanel" aria-labelledby="tab-${view}">
4614
+ ${view === "wheel" ? this.renderWheel(planets, aspects) : this.renderAspectGrid(planets, aspects)}
4615
+ </div>
4616
+ <div class="legend">
4617
+ <span>${planets.length} planets</span>
4618
+ <span>${aspects.length} aspects</span>
4619
+ ${this.data.houseSystem ? import_lit21.html`<span>${this.data.houseSystem} houses</span>` : import_lit21.nothing}
4620
+ <span><span class="legend-swatch" style="background: var(--roxy-success)"></span>harmonious</span>
4621
+ <span><span class="legend-swatch" style="background: var(--roxy-danger)"></span>challenging</span>
4622
+ </div>
4623
+ ${this.renderDetails()}
4624
+ ${this.renderInterpretations()}
4625
+ </div>`;
4626
+ }
4627
+ onTabKeyDown(e) {
4628
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
4629
+ e.preventDefault();
4630
+ this.view = this.view === "wheel" ? "grid" : "wheel";
4631
+ const next = this.view;
4632
+ requestAnimationFrame(() => {
4633
+ this.shadowRoot?.querySelector(`#tab-${next}`)?.focus();
4634
+ });
4635
+ }
4636
+ renderWheel(planets, aspects) {
4637
+ return import_lit21.html`<svg
4638
+ viewBox="0 0 ${SIZE} ${SIZE}"
4639
+ role="img"
4640
+ aria-label="Natal chart wheel with twelve houses, planets, and aspects"
4641
+ >
4642
+ <title>Natal chart wheel</title>
4643
+ <desc>
4644
+ Twelve zodiac sign segments around a circular wheel. Planet glyphs are
4645
+ placed at their ecliptic longitudes. Aspect lines connect related planets.
4646
+ </desc>
4647
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${OUTER_R} stroke-width="1.5" />
4648
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${SIGN_R - 14} stroke-width="0.8" />
4649
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${HOUSE_R} stroke-width="1" />
4650
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${PLANET_R - 16} stroke-width="0.5" />
4651
+ ${this.renderTicks()} ${this.renderSpokes()} ${this.renderSigns()}
4652
+ ${this.renderHouseNumbers()} ${this.renderCuspDegrees()}
4653
+ ${this.renderAspects(planets, aspects)} ${this.renderPlanets(planets)}
4654
+ ${this.renderAngles()}
4655
+ </svg>`;
4656
+ }
4657
+ /**
4658
+ * Planet-by-planet aspect grid: the lower-triangular matrix astrologers read
4659
+ * alongside the wheel. Each filled cell shows the aspect glyph colored by
4660
+ * nature, with the exact orb in the SVG-free `<title>` tooltip.
4661
+ */
4662
+ renderAspectGrid(planets, aspects) {
4663
+ const names = planets.map((p) => capitalize(p.name));
4664
+ const byPair = /* @__PURE__ */ new Map();
4665
+ for (const a of aspects) {
4666
+ const k = [capitalize(a.planet1), capitalize(a.planet2)].sort().join("|");
4667
+ byPair.set(k, a);
4668
+ }
4669
+ if (names.length === 0)
4670
+ return import_lit21.html`<p class="roxy-empty" role="status">No planets to grid</p>`;
4671
+ return import_lit21.html`<div class="grid-scroll">
4672
+ <table class="aspect-grid" aria-label="Planet by planet aspect grid">
4673
+ <thead>
4674
+ <tr>
4675
+ <th></th>
4676
+ ${names.slice(0, -1).map((n) => {
4677
+ const g = PLANET_GLYPH[n] ?? n.slice(0, 2);
4678
+ return import_lit21.html`<th scope="col" title=${n}>${g}</th>`;
4679
+ })}
4680
+ </tr>
4681
+ </thead>
4682
+ <tbody>
4683
+ ${names.slice(1).map((rowName, ri) => {
4684
+ const rowGlyph = PLANET_GLYPH[rowName] ?? rowName.slice(0, 2);
4685
+ return import_lit21.html`<tr>
4686
+ <th scope="row" title=${rowName}>${rowGlyph}</th>
4687
+ ${names.slice(0, ri + 1).map((colName) => {
4688
+ const a = byPair.get([rowName, colName].sort().join("|"));
4689
+ if (!a) return import_lit21.html`<td class="empty"></td>`;
4690
+ const name = normalizeAspect(a);
4691
+ const sym = ASPECT_SYMBOL[name] ?? ASPECT_SYMBOL[name.replace(/-/g, "")] ?? name.slice(0, 3);
4692
+ const cls = ASPECT_CLASS[name] ?? "aspect-other";
4693
+ const orb = formatNumber(a.orb, 1);
4694
+ return import_lit21.html`<td class=${`cell ${cls}`} title=${`${rowName} ${name} ${colName}${orb ? ` (orb ${orb}\xB0)` : ""}`}>
4695
+ <span class="asp">${sym}</span>
4696
+ </td>`;
4697
+ })}
4698
+ ${names.slice(ri + 1, -1).map(() => import_lit21.html`<td class="empty"></td>`)}
4699
+ </tr>`;
4700
+ })}
4701
+ </tbody>
4702
+ </table>
4703
+ </div>`;
4704
+ }
4705
+ renderAngles() {
4706
+ const asc = this.getAscendant();
4707
+ const mc = this.getMidheaven();
4708
+ const items = [
4709
+ this.renderAngleMark(asc, "ASC"),
4710
+ this.renderAngleMark(oppositePoint(asc), "DSC")
4711
+ ];
4712
+ if (mc !== null) {
4713
+ items.push(this.renderAngleMark(mc, "MC"));
4714
+ items.push(this.renderAngleMark(oppositePoint(mc), "IC"));
4715
+ }
4716
+ const pof = this.data?.partOfFortune?.longitude;
4717
+ if (typeof pof === "number") {
4718
+ items.push(this.renderAngleMark(normalizeLongitude(pof), "PoF"));
4719
+ }
4720
+ const vertex = this.data?.vertex?.longitude;
4721
+ if (typeof vertex === "number") {
4722
+ items.push(this.renderAngleMark(normalizeLongitude(vertex), "Vtx"));
4723
+ }
4724
+ return items;
4725
+ }
4726
+ renderAngleMark(longitude, label) {
4727
+ const angle = this.toAngle(longitude);
3914
4728
  const tickInner = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
3915
4729
  const tickOuter = polarToCartesian(CENTER, CENTER, ANGLE_TICK_R, angle);
3916
4730
  const labelPos = polarToCartesian(CENTER, CENTER, ANGLE_LABEL_R, angle);
3917
- return import_lit18.svg`
4731
+ return import_lit21.svg`
3918
4732
  <g>
3919
4733
  <line class="angle-tick" x1=${tickInner.x} y1=${tickInner.y} x2=${tickOuter.x} y2=${tickOuter.y} />
3920
4734
  <text class="angle-marker" x=${labelPos.x} y=${labelPos.y} text-anchor="middle" dominant-baseline="central">${label}</text>
@@ -3922,105 +4736,192 @@ var RoxyNatalChart = class extends import_lit18.LitElement {
3922
4736
  `;
3923
4737
  }
3924
4738
  renderSpokes() {
3925
- return Array.from({ length: 12 }, (_, i) => {
3926
- const angle = this.toAngle(i * 30);
4739
+ const houses = this.data?.houses ?? [];
4740
+ const cuspLongitudes = houses.length === 12 ? houses.map((h) => h.longitude) : Array.from({ length: 12 }, (_, i) => this.getAscendant() + i * 30);
4741
+ return cuspLongitudes.map((lon) => {
4742
+ const angle = this.toAngle(lon);
3927
4743
  const start = polarToCartesian(CENTER, CENTER, HOUSE_R, angle);
3928
4744
  const end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
3929
- return import_lit18.svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.8" />`;
4745
+ return import_lit21.svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.8" />`;
3930
4746
  });
3931
4747
  }
3932
4748
  renderSigns() {
3933
4749
  return SIGNS_ORDER.map((sign, i) => {
3934
4750
  const angle = this.toAngle(i * 30 + 15);
3935
4751
  const pos = polarToCartesian(CENTER, CENTER, SIGN_R, angle);
3936
- return import_lit18.svg`<text class="sign-glyph" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[sign]}</text>`;
4752
+ return import_lit21.svg`<text class="sign-glyph" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[sign]}</text>`;
3937
4753
  });
3938
4754
  }
3939
4755
  renderHouseNumbers() {
4756
+ const houses = this.data?.houses ?? [];
4757
+ if (houses.length === 12) {
4758
+ return houses.map((house, i) => {
4759
+ const next = houses[(i + 1) % 12];
4760
+ const mid = arcMidpoint(
4761
+ house.longitude,
4762
+ next ? next.longitude : house.longitude + 30
4763
+ );
4764
+ const pos = polarToCartesian(
4765
+ CENTER,
4766
+ CENTER,
4767
+ HOUSE_R - 12,
4768
+ this.toAngle(mid)
4769
+ );
4770
+ return import_lit21.svg`<text class="house-num" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${house.number}</text>`;
4771
+ });
4772
+ }
3940
4773
  const ascSignIndex = Math.floor(this.getAscendant() / 30);
3941
4774
  return Array.from({ length: 12 }, (_, i) => {
3942
4775
  const angle = this.toAngle(i * 30 + 15);
3943
4776
  const pos = polarToCartesian(CENTER, CENTER, HOUSE_R - 12, angle);
3944
4777
  const houseNum = (i - ascSignIndex + 12) % 12 + 1;
3945
- return import_lit18.svg`<text class="house-num" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${houseNum}</text>`;
4778
+ return import_lit21.svg`<text class="house-num" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${houseNum}</text>`;
4779
+ });
4780
+ }
4781
+ /**
4782
+ * Degree ticks on the outer zodiac band: a short mark every 5 degrees and a
4783
+ * longer one on each 30-degree sign cusp, so the wheel reads like a
4784
+ * reference-grade chart rather than a bare ring of glyphs.
4785
+ */
4786
+ renderTicks() {
4787
+ const ticks = [];
4788
+ for (let deg = 0; deg < 360; deg += 5) {
4789
+ const angle = this.toAngle(deg);
4790
+ const isMajor = deg % 30 === 0;
4791
+ const inner = isMajor ? SIGN_R - 14 : OUTER_R - 5;
4792
+ const a = polarToCartesian(CENTER, CENTER, inner, angle);
4793
+ const b = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
4794
+ ticks.push(
4795
+ import_lit21.svg`<line class=${isMajor ? "tick tick-major" : "tick"} x1=${a.x} y1=${a.y} x2=${b.x} y2=${b.y} stroke-width=${isMajor ? 1 : 0.5} />`
4796
+ );
4797
+ }
4798
+ return ticks;
4799
+ }
4800
+ /**
4801
+ * Degree-and-minute label printed next to each house cusp on the wheel, so
4802
+ * the exact cusp position is readable without leaving the chart.
4803
+ */
4804
+ renderCuspDegrees() {
4805
+ const houses = this.data?.houses ?? [];
4806
+ if (houses.length !== 12) return import_lit21.nothing;
4807
+ return houses.map((house) => {
4808
+ const angle = this.toAngle(house.longitude);
4809
+ const pos = polarToCartesian(CENTER, CENTER, HOUSE_R + 9, angle);
4810
+ const sp = longitudeToSignPosition(house.longitude);
4811
+ return import_lit21.svg`<text class="cusp-deg" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${sp.degree}°${String(sp.minute).padStart(2, "0")}'</text>`;
3946
4812
  });
3947
4813
  }
3948
4814
  renderPlanets(planets) {
3949
4815
  return planets.map((p) => {
3950
- if (!Number.isFinite(p.longitude)) return import_lit18.nothing;
4816
+ if (!Number.isFinite(p.longitude)) return import_lit21.nothing;
3951
4817
  const angle = this.toAngle(p.longitude);
3952
- const pos = polarToCartesian(CENTER, CENTER, PLANET_R, angle);
4818
+ const glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, angle);
4819
+ const degPos = polarToCartesian(CENTER, CENTER, PLANET_R - 13, angle);
3953
4820
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
3954
- const retro = p.isRetrograde ? " R" : "";
3955
- const display = retro ? `${glyph}\u1D3F` : glyph;
3956
- return import_lit18.svg`<text class="planet-glyph" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}${retro}</title>${display}</text>`;
4821
+ const sp = longitudeToSignPosition(p.longitude);
4822
+ const retro = p.isRetrograde === true;
4823
+ const degLabel = `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}'`;
4824
+ return import_lit21.svg`<g>
4825
+ <text class="planet-glyph" x=${glyphPos.x} y=${glyphPos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}${retro ? " retrograde" : ""} - ${degLabel} ${p.sign ?? ""}</title>${glyph}</text>
4826
+ <text class="planet-deg" x=${degPos.x} y=${degPos.y} text-anchor="middle" dominant-baseline="central">${degLabel}${retro ? import_lit21.svg`<tspan class="retro"> ℞</tspan>` : import_lit21.nothing}</text>
4827
+ </g>`;
3957
4828
  });
3958
4829
  }
3959
4830
  renderDetails() {
3960
4831
  const summary = this.data?.summary;
3961
4832
  const ai = this.data?.aspectsInterpretation;
3962
- if (!summary && !ai) return import_lit18.nothing;
4833
+ if (!summary && !ai) return import_lit21.nothing;
3963
4834
  const retrogrades = summary?.retrogradePlanets ?? [];
3964
- const elementDist = summary?.elementDistribution ?? {};
3965
- const modalityDist = summary?.modalityDistribution ?? {};
3966
- const elementMax = Math.max(1, ...Object.values(elementDist));
3967
- const modalityMax = Math.max(1, ...Object.values(modalityDist));
3968
- return import_lit18.html`<div class="details">
3969
- ${summary?.dominantElement || summary?.dominantModality ? import_lit18.html`<div class="pill-row">
3970
- ${summary.dominantElement ? import_lit18.html`<span class="pill">Dominant element: ${summary.dominantElement}</span>` : import_lit18.nothing}
3971
- ${summary.dominantModality ? import_lit18.html`<span class="pill">Dominant modality: ${summary.dominantModality}</span>` : import_lit18.nothing}
3972
- </div>` : import_lit18.nothing}
3973
- ${ai ? import_lit18.html`<div class="pill-row">
4835
+ return import_lit21.html`<div class="details">
4836
+ ${summary?.dominantElement || summary?.dominantModality ? import_lit21.html`<div class="pill-row">
4837
+ ${summary.dominantElement ? import_lit21.html`<span class="pill">Dominant element: ${summary.dominantElement}</span>` : import_lit21.nothing}
4838
+ ${summary.dominantModality ? import_lit21.html`<span class="pill">Dominant modality: ${summary.dominantModality}</span>` : import_lit21.nothing}
4839
+ </div>` : import_lit21.nothing}
4840
+ ${ai ? import_lit21.html`<div class="pill-row">
3974
4841
  <span class="pill pill--success">Harmonious ${ai.harmonious}</span>
3975
4842
  <span class="pill pill--danger">Challenging ${ai.challenging}</span>
3976
4843
  <span class="pill pill--muted">Neutral ${ai.neutral}</span>
3977
- </div>` : import_lit18.nothing}
3978
- ${retrogrades.length > 0 ? import_lit18.html`<div class="pill-row">
4844
+ </div>` : import_lit21.nothing}
4845
+ ${retrogrades.length > 0 ? import_lit21.html`<div class="pill-row">
3979
4846
  ${retrogrades.map((p) => {
3980
4847
  const glyph = PLANET_GLYPH[p] ?? p.slice(0, 2);
3981
- return import_lit18.html`<span class="pill pill--muted">${glyph} ${p} R</span>`;
4848
+ return import_lit21.html`<span class="pill pill--muted">${glyph} ${p} R</span>`;
3982
4849
  })}
3983
- </div>` : import_lit18.nothing}
3984
- ${ai?.summary ? import_lit18.html`<p class="summary">${ai.summary}</p>` : import_lit18.nothing}
3985
- ${Object.keys(elementDist).length > 0 || Object.keys(modalityDist).length > 0 ? import_lit18.html`<div class="dist-grid">
3986
- ${Object.keys(elementDist).length > 0 ? import_lit18.html`<div class="dist-section">
3987
- <h3>Elements</h3>
3988
- ${Object.entries(elementDist).map(
3989
- ([label, count]) => import_lit18.html`<div class="dist-row">
3990
- <span>${label}</span>
3991
- <div class="dist-bar"><span style="width: ${Math.round(count / elementMax * 100)}%"></span></div>
3992
- <span>${count}</span>
3993
- </div>`
3994
- )}
3995
- </div>` : import_lit18.nothing}
3996
- ${Object.keys(modalityDist).length > 0 ? import_lit18.html`<div class="dist-section">
3997
- <h3>Modalities</h3>
3998
- ${Object.entries(modalityDist).map(
3999
- ([label, count]) => import_lit18.html`<div class="dist-row">
4000
- <span>${label}</span>
4001
- <div class="dist-bar"><span style="width: ${Math.round(count / modalityMax * 100)}%"></span></div>
4002
- <span>${count}</span>
4003
- </div>`
4004
- )}
4005
- </div>` : import_lit18.nothing}
4006
- </div>` : import_lit18.nothing}
4850
+ </div>` : import_lit21.nothing}
4851
+ ${ai?.summary ? import_lit21.html`<p class="summary">${ai.summary}</p>` : import_lit21.nothing}
4852
+ ${this.renderElementModalityGrid()}
4007
4853
  </div>`;
4008
4854
  }
4855
+ /**
4856
+ * Element by modality grid: the 4x3 cross-tab astrologers read for chart
4857
+ * balance. Each planet is placed by its sign into one cell (Fire/Earth/Air/
4858
+ * Water row, Cardinal/Fixed/Mutable column). Derived purely from the planet
4859
+ * signs, with row, column, and grand totals.
4860
+ */
4861
+ renderElementModalityGrid() {
4862
+ const planets = this.getPlanets();
4863
+ if (planets.length === 0) return import_lit21.nothing;
4864
+ const ELEMENTS = ["Fire", "Earth", "Air", "Water"];
4865
+ const MODALITIES = ["Cardinal", "Fixed", "Mutable"];
4866
+ const order = SIGNS_ORDER;
4867
+ const cells = {};
4868
+ for (const el of ELEMENTS)
4869
+ cells[el] = { Cardinal: [], Fixed: [], Mutable: [] };
4870
+ for (const p of planets) {
4871
+ const idx = order.indexOf(capitalize(p.sign ?? ""));
4872
+ if (idx < 0) continue;
4873
+ const el = ELEMENTS[idx % 4];
4874
+ const mod = MODALITIES[idx % 3];
4875
+ const glyph = PLANET_GLYPH[capitalize(p.name)] ?? capitalize(p.name).slice(0, 2);
4876
+ cells[el]?.[mod]?.push(glyph);
4877
+ }
4878
+ return import_lit21.html`<table class="em-grid" aria-label="Element and modality distribution">
4879
+ <thead>
4880
+ <tr>
4881
+ <th></th>
4882
+ ${MODALITIES.map((m) => import_lit21.html`<th scope="col">${m.slice(0, 3)}</th>`)}
4883
+ <th scope="col">Total</th>
4884
+ </tr>
4885
+ </thead>
4886
+ <tbody>
4887
+ ${ELEMENTS.map((el) => {
4888
+ const rowTotal = MODALITIES.reduce(
4889
+ (s, m) => s + (cells[el]?.[m]?.length ?? 0),
4890
+ 0
4891
+ );
4892
+ return import_lit21.html`<tr>
4893
+ <th scope="row">${el}</th>
4894
+ ${MODALITIES.map(
4895
+ (m) => import_lit21.html`<td>${(cells[el]?.[m] ?? []).join(" ")}</td>`
4896
+ )}
4897
+ <td class="em-total">${rowTotal}</td>
4898
+ </tr>`;
4899
+ })}
4900
+ <tr>
4901
+ <th scope="row">Total</th>
4902
+ ${MODALITIES.map(
4903
+ (m) => import_lit21.html`<td class="em-total">${ELEMENTS.reduce((s, el) => s + (cells[el]?.[m]?.length ?? 0), 0)}</td>`
4904
+ )}
4905
+ <td class="em-total">${planets.length}</td>
4906
+ </tr>
4907
+ </tbody>
4908
+ </table>`;
4909
+ }
4009
4910
  renderInterpretations() {
4010
4911
  const planets = this.getPlanets().filter((p) => p.interpretation);
4011
- if (planets.length === 0) return import_lit18.nothing;
4012
- return import_lit18.html`<section class="interpretations">
4912
+ if (planets.length === 0) return import_lit21.nothing;
4913
+ return import_lit21.html`<section class="interpretations">
4013
4914
  <h3>Planet readings</h3>
4014
4915
  ${planets.map((p, idx) => {
4015
4916
  const interp = p.interpretation;
4016
4917
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
4017
4918
  const deg = formatNumber(p.degree ?? 0, 1);
4018
- return import_lit18.html`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
4919
+ return import_lit21.html`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
4019
4920
  <summary>${glyph} ${p.name} <small>${p.sign ?? ""} ${deg}</small></summary>
4020
4921
  <div class="interp-body">
4021
- ${interp.summary ? import_lit18.html`<p class="interp-summary">${interp.summary}</p>` : import_lit18.nothing}
4022
- ${interp.detailed ? import_lit18.html`<p class="interp-detail">${interp.detailed}</p>` : import_lit18.nothing}
4023
- ${interp.keywords?.length ? import_lit18.html`<div class="interp-keywords">${interp.keywords.map((k) => import_lit18.html`<span class="kw">${k}</span>`)}</div>` : import_lit18.nothing}
4922
+ ${interp.summary ? import_lit21.html`<p class="interp-summary">${interp.summary}</p>` : import_lit21.nothing}
4923
+ ${interp.detailed ? import_lit21.html`<p class="interp-detail">${interp.detailed}</p>` : import_lit21.nothing}
4924
+ ${interp.keywords?.length ? import_lit21.html`<div class="interp-keywords">${interp.keywords.map((k) => import_lit21.html`<span class="kw">${k}</span>`)}</div>` : import_lit21.nothing}
4024
4925
  </div>
4025
4926
  </details>`;
4026
4927
  })}
@@ -4036,7 +4937,7 @@ var RoxyNatalChart = class extends import_lit18.LitElement {
4036
4937
  return aspects.map((a) => {
4037
4938
  const l1 = planetMap.get(capitalize(a.planet1));
4038
4939
  const l2 = planetMap.get(capitalize(a.planet2));
4039
- if (l1 === void 0 || l2 === void 0) return import_lit18.nothing;
4940
+ if (l1 === void 0 || l2 === void 0) return import_lit21.nothing;
4040
4941
  const p1 = polarToCartesian(
4041
4942
  CENTER,
4042
4943
  CENTER,
@@ -4052,13 +4953,13 @@ var RoxyNatalChart = class extends import_lit18.LitElement {
4052
4953
  const aspectName = normalizeAspect(a);
4053
4954
  const aspectClass = ASPECT_CLASS[aspectName] ?? "aspect-other";
4054
4955
  const orbLabel = formatNumber(a.orb, 1);
4055
- return import_lit18.svg`<line class=${`aspect ${aspectClass}`} x1=${p1.x} y1=${p1.y} x2=${p2.x} y2=${p2.y}><title>${a.planet1} ${aspectName || ""} ${a.planet2}${orbLabel ? ` (orb ${orbLabel}\xB0)` : ""}</title></line>`;
4956
+ return import_lit21.svg`<line class=${`aspect ${aspectClass}`} x1=${p1.x} y1=${p1.y} x2=${p2.x} y2=${p2.y}><title>${a.planet1} ${aspectName || ""} ${a.planet2}${orbLabel ? ` (orb ${orbLabel}\xB0)` : ""}</title></line>`;
4056
4957
  });
4057
4958
  }
4058
4959
  };
4059
4960
  RoxyNatalChart.styles = [
4060
4961
  baseStyles,
4061
- import_lit18.css`
4962
+ import_lit21.css`
4062
4963
  .wrap {
4063
4964
  width: 100%;
4064
4965
  display: grid;
@@ -4103,12 +5004,35 @@ RoxyNatalChart.styles = [
4103
5004
  font-family: var(--roxy-font-sans);
4104
5005
  }
4105
5006
 
5007
+ .planet-deg {
5008
+ fill: var(--roxy-fg, #0a0a0a);
5009
+ font-size: 7px;
5010
+ font-family: var(--roxy-font-sans);
5011
+ }
5012
+
5013
+ .planet-deg .retro {
5014
+ fill: var(--roxy-danger, #dc2626);
5015
+ }
5016
+
4106
5017
  .house-num {
4107
5018
  fill: var(--roxy-muted, #71717a);
4108
5019
  font-size: 9px;
4109
5020
  font-family: var(--roxy-font-sans);
4110
5021
  }
4111
5022
 
5023
+ .cusp-deg {
5024
+ fill: var(--roxy-muted, #71717a);
5025
+ font-size: 6px;
5026
+ font-family: var(--roxy-font-sans);
5027
+ }
5028
+
5029
+ .tick {
5030
+ stroke: var(--roxy-border, #e4e4e7);
5031
+ }
5032
+ .tick-major {
5033
+ stroke: var(--roxy-secondary, #475569);
5034
+ }
5035
+
4112
5036
  .aspect {
4113
5037
  stroke-width: 0.8;
4114
5038
  fill: none;
@@ -4158,6 +5082,78 @@ RoxyNatalChart.styles = [
4158
5082
  vertical-align: middle;
4159
5083
  }
4160
5084
 
5085
+ .tablist {
5086
+ display: flex;
5087
+ gap: 2px;
5088
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
5089
+ }
5090
+ .tab {
5091
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
5092
+ font-size: var(--roxy-text-sm, 0.875rem);
5093
+ background: none;
5094
+ border: none;
5095
+ border-bottom: 2px solid transparent;
5096
+ margin-bottom: -2px;
5097
+ cursor: pointer;
5098
+ color: var(--roxy-muted, #71717a);
5099
+ font-family: inherit;
5100
+ transition: color var(--roxy-motion-duration, 200ms) var(--roxy-motion-easing, ease);
5101
+ }
5102
+ .tab[aria-selected='true'] {
5103
+ color: var(--roxy-accent-fg, #b45309);
5104
+ border-bottom-color: var(--roxy-accent, #f59e0b);
5105
+ font-weight: var(--roxy-weight-bold, 600);
5106
+ }
5107
+ .tab:hover:not([aria-selected='true']) {
5108
+ color: var(--roxy-fg, #0a0a0a);
5109
+ }
5110
+
5111
+ .grid-scroll {
5112
+ overflow-x: auto;
5113
+ -webkit-overflow-scrolling: touch;
5114
+ }
5115
+ table.aspect-grid {
5116
+ border-collapse: collapse;
5117
+ font-size: var(--roxy-text-xs, 0.75rem);
5118
+ margin: 0 auto;
5119
+ }
5120
+ table.aspect-grid th,
5121
+ table.aspect-grid td {
5122
+ width: 1.6rem;
5123
+ height: 1.6rem;
5124
+ text-align: center;
5125
+ border: 1px solid var(--roxy-border, #e4e4e7);
5126
+ padding: 0;
5127
+ }
5128
+ table.aspect-grid th {
5129
+ color: var(--roxy-secondary, #475569);
5130
+ font-weight: var(--roxy-weight-bold, 600);
5131
+ }
5132
+ table.aspect-grid td.cell {
5133
+ cursor: default;
5134
+ }
5135
+ table.aspect-grid td.empty {
5136
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 18%, transparent);
5137
+ }
5138
+ table.aspect-grid td .asp {
5139
+ font-size: 0.95em;
5140
+ line-height: 1;
5141
+ }
5142
+ table.aspect-grid td.aspect-trine .asp,
5143
+ table.aspect-grid td.aspect-sextile .asp {
5144
+ color: var(--roxy-success, #16a34a);
5145
+ }
5146
+ table.aspect-grid td.aspect-square .asp,
5147
+ table.aspect-grid td.aspect-opposition .asp {
5148
+ color: var(--roxy-danger, #dc2626);
5149
+ }
5150
+ table.aspect-grid td.aspect-conjunction .asp {
5151
+ color: var(--roxy-accent-fg, #b45309);
5152
+ }
5153
+ table.aspect-grid td.aspect-other .asp {
5154
+ color: var(--roxy-muted, #71717a);
5155
+ }
5156
+
4161
5157
  .details {
4162
5158
  margin-top: var(--roxy-space-md, 1rem);
4163
5159
  }
@@ -4198,48 +5194,37 @@ RoxyNatalChart.styles = [
4198
5194
  margin: var(--roxy-space-md, 1rem) 0;
4199
5195
  }
4200
5196
 
4201
- .dist-grid {
4202
- display: grid;
4203
- grid-template-columns: 1fr 1fr;
4204
- gap: var(--roxy-space-md, 1rem);
5197
+ .em-grid {
5198
+ border-collapse: collapse;
5199
+ font-size: var(--roxy-text-xs, 0.75rem);
5200
+ width: 100%;
4205
5201
  }
4206
-
4207
- @container (max-width: 639px) {
4208
- .dist-grid {
4209
- grid-template-columns: 1fr;
4210
- }
5202
+ .em-grid th,
5203
+ .em-grid td {
5204
+ border: 1px solid var(--roxy-border, #e4e4e7);
5205
+ padding: 3px 5px;
5206
+ text-align: center;
5207
+ vertical-align: middle;
4211
5208
  }
4212
-
4213
- .dist-section h3 {
4214
- font-size: var(--roxy-text-xs, 0.75rem);
4215
- font-weight: var(--roxy-weight-bold, 600);
5209
+ .em-grid th {
4216
5210
  color: var(--roxy-muted, #71717a);
4217
- margin: 0 0 var(--roxy-space-xs, 0.25rem);
5211
+ font-weight: var(--roxy-weight-bold, 600);
4218
5212
  text-transform: uppercase;
4219
- letter-spacing: 0.05em;
5213
+ letter-spacing: 0.04em;
4220
5214
  }
4221
-
4222
- .dist-row {
4223
- display: grid;
4224
- grid-template-columns: 4rem 1fr 1.5rem;
4225
- align-items: center;
4226
- gap: var(--roxy-space-xs, 0.25rem);
4227
- font-size: var(--roxy-text-xs, 0.75rem);
4228
- color: var(--roxy-fg, #0f172a);
4229
- margin-bottom: 4px;
5215
+ .em-grid th[scope='row'] {
5216
+ text-align: left;
4230
5217
  }
4231
-
4232
- .dist-bar {
4233
- background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 20%, transparent);
4234
- height: 6px;
4235
- border-radius: 3px;
5218
+ .em-grid td {
5219
+ color: var(--roxy-accent, #f59e0b);
5220
+ font-size: 0.95em;
5221
+ line-height: 1.4;
5222
+ min-width: 1.4rem;
4236
5223
  }
4237
-
4238
- .dist-bar > span {
4239
- display: block;
4240
- height: 100%;
4241
- background: var(--roxy-accent, #f59e0b);
4242
- border-radius: 3px;
5224
+ .em-grid .em-total {
5225
+ color: var(--roxy-fg, #0a0a0a);
5226
+ font-weight: var(--roxy-weight-bold, 600);
5227
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 25%, transparent);
4243
5228
  }
4244
5229
 
4245
5230
  .interpretations {
@@ -4290,19 +5275,22 @@ RoxyNatalChart.styles = [
4290
5275
  `
4291
5276
  ];
4292
5277
  __decorateClass([
4293
- (0, import_decorators16.property)({ attribute: false })
5278
+ (0, import_decorators19.property)({ attribute: false })
4294
5279
  ], RoxyNatalChart.prototype, "data", 2);
4295
5280
  __decorateClass([
4296
- (0, import_decorators16.property)({ type: String, attribute: "house-system", reflect: true })
5281
+ (0, import_decorators19.property)({ type: String, attribute: "house-system", reflect: true })
4297
5282
  ], RoxyNatalChart.prototype, "houseSystem", 2);
5283
+ __decorateClass([
5284
+ (0, import_decorators19.state)()
5285
+ ], RoxyNatalChart.prototype, "view", 2);
4298
5286
  RoxyNatalChart = __decorateClass([
4299
- (0, import_decorators16.customElement)("roxy-natal-chart")
5287
+ (0, import_decorators19.customElement)("roxy-natal-chart")
4300
5288
  ], RoxyNatalChart);
4301
5289
 
4302
5290
  // packages/ui/src/components/numerology-card.ts
4303
- var import_lit19 = require("lit");
4304
- var import_decorators17 = require("lit/decorators.js");
4305
- var RoxyNumerologyCard = class extends import_lit19.LitElement {
5291
+ var import_lit22 = require("lit");
5292
+ var import_decorators20 = require("lit/decorators.js");
5293
+ var RoxyNumerologyCard = class extends import_lit22.LitElement {
4306
5294
  constructor() {
4307
5295
  super(...arguments);
4308
5296
  this.data = null;
@@ -4311,7 +5299,7 @@ var RoxyNumerologyCard = class extends import_lit19.LitElement {
4311
5299
  render() {
4312
5300
  const d = this.data;
4313
5301
  if (!d)
4314
- return import_lit19.html`<div class="roxy-empty" role="status">No numerology data</div>`;
5302
+ return import_lit22.html`<div class="roxy-empty" role="status">No numerology data</div>`;
4315
5303
  const headerLabel = LABELS[this.type] ?? this.type;
4316
5304
  if ("coreNumbers" in d) return this.renderChart(d, headerLabel);
4317
5305
  if ("personalYear" in d) return this.renderPersonalYear(d, headerLabel);
@@ -4322,61 +5310,61 @@ var RoxyNumerologyCard = class extends import_lit19.LitElement {
4322
5310
  }
4323
5311
  renderNumberCard(d, headerLabel) {
4324
5312
  const keywords = d.meaning?.keywords ?? [];
4325
- return import_lit19.html`<article class="card" aria-label=${headerLabel}>
5313
+ return import_lit22.html`<article class="card" aria-label=${headerLabel}>
4326
5314
  <div class="hero">
4327
- ${typeof d.number === "number" ? import_lit19.html`<div class="numeral">${d.number}</div>` : import_lit19.nothing}
5315
+ ${typeof d.number === "number" ? import_lit22.html`<div class="numeral">${d.number}</div>` : import_lit22.nothing}
4328
5316
  <div>
4329
5317
  <p class="label">${headerLabel}</p>
4330
- ${d.meaning?.title ? import_lit19.html`<h2 class="title">${d.meaning.title}</h2>` : import_lit19.nothing}
5318
+ ${d.meaning?.title ? import_lit22.html`<h2 class="title">${d.meaning.title}</h2>` : import_lit22.nothing}
4331
5319
  </div>
4332
5320
  </div>
4333
- ${d.meaning?.description ? import_lit19.html`<p class="meaning">${d.meaning.description}</p>` : import_lit19.nothing}
4334
- ${d.calculation ? import_lit19.html`<pre class="calc">${d.calculation}</pre>` : import_lit19.nothing}
4335
- ${keywords.length > 0 ? import_lit19.html`<div class="chips">
4336
- ${keywords.map((k) => import_lit19.html`<span>${k}</span>`)}
4337
- </div>` : import_lit19.nothing}
4338
- ${d.hasKarmicDebt && d.karmicDebtNumber ? import_lit19.html`<div class="karmic">
5321
+ ${d.meaning?.description ? import_lit22.html`<p class="meaning">${d.meaning.description}</p>` : import_lit22.nothing}
5322
+ ${d.calculation ? import_lit22.html`<pre class="calc">${d.calculation}</pre>` : import_lit22.nothing}
5323
+ ${keywords.length > 0 ? import_lit22.html`<div class="chips">
5324
+ ${keywords.map((k) => import_lit22.html`<span>${k}</span>`)}
5325
+ </div>` : import_lit22.nothing}
5326
+ ${d.hasKarmicDebt && d.karmicDebtNumber ? import_lit22.html`<div class="karmic">
4339
5327
  Karmic debt ${d.karmicDebtNumber}.
4340
5328
  ${karmicDebtText(d.karmicDebtMeaning)}
4341
- </div>` : import_lit19.nothing}
5329
+ </div>` : import_lit22.nothing}
4342
5330
  </article>`;
4343
5331
  }
4344
5332
  renderPersonalYear(d, headerLabel) {
4345
- return import_lit19.html`<article class="card" aria-label=${headerLabel}>
5333
+ return import_lit22.html`<article class="card" aria-label=${headerLabel}>
4346
5334
  <div class="hero">
4347
- ${typeof d.personalYear === "number" ? import_lit19.html`<div class="numeral">${d.personalYear}</div>` : import_lit19.nothing}
5335
+ ${typeof d.personalYear === "number" ? import_lit22.html`<div class="numeral">${d.personalYear}</div>` : import_lit22.nothing}
4348
5336
  <div>
4349
5337
  <p class="label">${headerLabel}</p>
4350
- ${d.theme ? import_lit19.html`<h2 class="title">${d.theme}</h2>` : import_lit19.nothing}
5338
+ ${d.theme ? import_lit22.html`<h2 class="title">${d.theme}</h2>` : import_lit22.nothing}
4351
5339
  </div>
4352
5340
  </div>
4353
- ${d.forecast ? import_lit19.html`<p class="meaning">${d.forecast}</p>` : import_lit19.nothing}
4354
- ${d.advice ? import_lit19.html`<p>${d.advice}</p>` : import_lit19.nothing}
5341
+ ${d.forecast ? import_lit22.html`<p class="meaning">${d.forecast}</p>` : import_lit22.nothing}
5342
+ ${d.advice ? import_lit22.html`<p>${d.advice}</p>` : import_lit22.nothing}
4355
5343
  </article>`;
4356
5344
  }
4357
5345
  renderChart(d, headerLabel) {
4358
5346
  const cores = Object.entries(d.coreNumbers).filter(
4359
5347
  ([, v]) => v !== null && v !== void 0
4360
5348
  );
4361
- return import_lit19.html`<article class="card" aria-label=${headerLabel}>
5349
+ return import_lit22.html`<article class="card" aria-label=${headerLabel}>
4362
5350
  <div>
4363
5351
  <p class="label">${headerLabel}</p>
4364
- ${d.profile?.name ? import_lit19.html`<h2 class="title">${d.profile.name}</h2>` : import_lit19.nothing}
5352
+ ${d.profile?.name ? import_lit22.html`<h2 class="title">${d.profile.name}</h2>` : import_lit22.nothing}
4365
5353
  </div>
4366
- ${cores.length > 0 ? import_lit19.html`<div class="cores">
5354
+ ${cores.length > 0 ? import_lit22.html`<div class="cores">
4367
5355
  ${cores.map(
4368
- ([k, v]) => import_lit19.html`<div class="item">
5356
+ ([k, v]) => import_lit22.html`<div class="item">
4369
5357
  <span>${humanize(k)}</span>
4370
5358
  <strong>${v.number ?? ""}</strong>
4371
5359
  </div>`
4372
5360
  )}
4373
- </div>` : import_lit19.nothing}
5361
+ </div>` : import_lit22.nothing}
4374
5362
  </article>`;
4375
5363
  }
4376
5364
  };
4377
5365
  RoxyNumerologyCard.styles = [
4378
5366
  baseStyles,
4379
- import_lit19.css`
5367
+ import_lit22.css`
4380
5368
  .card {
4381
5369
  background: var(--roxy-bg, #fff);
4382
5370
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -4475,13 +5463,13 @@ RoxyNumerologyCard.styles = [
4475
5463
  `
4476
5464
  ];
4477
5465
  __decorateClass([
4478
- (0, import_decorators17.property)({ attribute: false })
5466
+ (0, import_decorators20.property)({ attribute: false })
4479
5467
  ], RoxyNumerologyCard.prototype, "data", 2);
4480
5468
  __decorateClass([
4481
- (0, import_decorators17.property)({ type: String, reflect: true })
5469
+ (0, import_decorators20.property)({ type: String, reflect: true })
4482
5470
  ], RoxyNumerologyCard.prototype, "type", 2);
4483
5471
  RoxyNumerologyCard = __decorateClass([
4484
- (0, import_decorators17.customElement)("roxy-numerology-card")
5472
+ (0, import_decorators20.customElement)("roxy-numerology-card")
4485
5473
  ], RoxyNumerologyCard);
4486
5474
  var LABELS = {
4487
5475
  "life-path": "Life Path",
@@ -4495,9 +5483,9 @@ function karmicDebtText(value) {
4495
5483
  }
4496
5484
 
4497
5485
  // packages/ui/src/components/panchang-table.ts
4498
- var import_lit20 = require("lit");
4499
- var import_decorators18 = require("lit/decorators.js");
4500
- var RoxyPanchangTable = class extends import_lit20.LitElement {
5486
+ var import_lit23 = require("lit");
5487
+ var import_decorators21 = require("lit/decorators.js");
5488
+ var RoxyPanchangTable = class extends import_lit23.LitElement {
4501
5489
  constructor() {
4502
5490
  super(...arguments);
4503
5491
  this.data = null;
@@ -4506,7 +5494,7 @@ var RoxyPanchangTable = class extends import_lit20.LitElement {
4506
5494
  render() {
4507
5495
  const d = this.data;
4508
5496
  if (!d)
4509
- return import_lit20.html`<div class="roxy-empty" role="status">No panchang data</div>`;
5497
+ return import_lit23.html`<div class="roxy-empty" role="status">No panchang data</div>`;
4510
5498
  const detailed = "sunrise" in d ? d : null;
4511
5499
  const fivefold = [
4512
5500
  ["Tithi", this.formatPart(d.tithi)],
@@ -4529,7 +5517,7 @@ var RoxyPanchangTable = class extends import_lit20.LitElement {
4529
5517
  ["Yamaganda", detailed.yamaganda],
4530
5518
  ["Gulika", detailed.gulika]
4531
5519
  ] : [];
4532
- return import_lit20.html`<div class="wrap" aria-label="Panchang">
5520
+ return import_lit23.html`<div class="wrap" aria-label="Panchang">
4533
5521
  <header class="head">
4534
5522
  <h2 class="title">Panchang</h2>
4535
5523
  <span class="date">${detailed ? formatDate(detailed.date) : ""}</span>
@@ -4537,35 +5525,35 @@ var RoxyPanchangTable = class extends import_lit20.LitElement {
4537
5525
  <table>
4538
5526
  <tbody>
4539
5527
  ${fivefold.map(
4540
- ([k, v]) => import_lit20.html`<tr>
5528
+ ([k, v]) => import_lit23.html`<tr>
4541
5529
  <th>${k}</th>
4542
5530
  <td>${v}</td>
4543
5531
  </tr>`
4544
5532
  )}
4545
- ${detailed?.sunrise ? import_lit20.html`<tr>
5533
+ ${detailed?.sunrise ? import_lit23.html`<tr>
4546
5534
  <th>Sunrise</th>
4547
5535
  <td>${formatTime(detailed.sunrise)}</td>
4548
- </tr>` : import_lit20.nothing}
4549
- ${detailed?.sunset ? import_lit20.html`<tr>
5536
+ </tr>` : import_lit23.nothing}
5537
+ ${detailed?.sunset ? import_lit23.html`<tr>
4550
5538
  <th>Sunset</th>
4551
5539
  <td>${formatTime(detailed.sunset)}</td>
4552
- </tr>` : import_lit20.nothing}
4553
- ${detailed?.moonrise ? import_lit20.html`<tr>
5540
+ </tr>` : import_lit23.nothing}
5541
+ ${detailed?.moonrise ? import_lit23.html`<tr>
4554
5542
  <th>Moonrise</th>
4555
5543
  <td>${formatTime(detailed.moonrise)}</td>
4556
- </tr>` : import_lit20.nothing}
4557
- ${detailed?.moonset ? import_lit20.html`<tr>
5544
+ </tr>` : import_lit23.nothing}
5545
+ ${detailed?.moonset ? import_lit23.html`<tr>
4558
5546
  <th>Moonset</th>
4559
5547
  <td>${formatTime(detailed.moonset)}</td>
4560
- </tr>` : import_lit20.nothing}
5548
+ </tr>` : import_lit23.nothing}
4561
5549
  </tbody>
4562
5550
  </table>
4563
- ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? import_lit20.html`
5551
+ ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? import_lit23.html`
4564
5552
  <div class="section">Auspicious muhurtas</div>
4565
5553
  <table>
4566
5554
  <tbody>
4567
5555
  ${muhurtas.filter(([, v]) => !!v).map(
4568
- ([k, v]) => import_lit20.html`<tr>
5556
+ ([k, v]) => import_lit23.html`<tr>
4569
5557
  <th>${k}</th>
4570
5558
  <td>${formatTimeRange(v)}</td>
4571
5559
  </tr>`
@@ -4576,14 +5564,14 @@ var RoxyPanchangTable = class extends import_lit20.LitElement {
4576
5564
  <table>
4577
5565
  <tbody>
4578
5566
  ${inauspicious.filter(([, v]) => !!v).map(
4579
- ([k, v]) => import_lit20.html`<tr>
5567
+ ([k, v]) => import_lit23.html`<tr>
4580
5568
  <th>${k}</th>
4581
5569
  <td>${formatTimeRange(v)}</td>
4582
5570
  </tr>`
4583
5571
  )}
4584
5572
  </tbody>
4585
5573
  </table>
4586
- ` : import_lit20.nothing}
5574
+ ` : import_lit23.nothing}
4587
5575
  </div>`;
4588
5576
  }
4589
5577
  formatPart(v) {
@@ -4603,7 +5591,7 @@ var RoxyPanchangTable = class extends import_lit20.LitElement {
4603
5591
  };
4604
5592
  RoxyPanchangTable.styles = [
4605
5593
  baseStyles,
4606
- import_lit20.css`
5594
+ import_lit23.css`
4607
5595
  .wrap {
4608
5596
  border: 1px solid var(--roxy-border, #e4e4e7);
4609
5597
  border-radius: var(--roxy-radius-md, 8px);
@@ -4664,18 +5652,18 @@ RoxyPanchangTable.styles = [
4664
5652
  `
4665
5653
  ];
4666
5654
  __decorateClass([
4667
- (0, import_decorators18.property)({ attribute: false })
5655
+ (0, import_decorators21.property)({ attribute: false })
4668
5656
  ], RoxyPanchangTable.prototype, "data", 2);
4669
5657
  __decorateClass([
4670
- (0, import_decorators18.property)({ type: String, reflect: true })
5658
+ (0, import_decorators21.property)({ type: String, reflect: true })
4671
5659
  ], RoxyPanchangTable.prototype, "detail", 2);
4672
5660
  RoxyPanchangTable = __decorateClass([
4673
- (0, import_decorators18.customElement)("roxy-panchang-table")
5661
+ (0, import_decorators21.customElement)("roxy-panchang-table")
4674
5662
  ], RoxyPanchangTable);
4675
5663
 
4676
5664
  // packages/ui/src/components/shadbala-table.ts
4677
- var import_lit21 = require("lit");
4678
- var import_decorators19 = require("lit/decorators.js");
5665
+ var import_lit24 = require("lit");
5666
+ var import_decorators22 = require("lit/decorators.js");
4679
5667
  var BALA_COMPONENTS = [
4680
5668
  { key: "sthanaBala", label: "Sthana", color: "var(--roxy-info, #0284c7)" },
4681
5669
  { key: "digBala", label: "Dig", color: "var(--roxy-success, #16a34a)" },
@@ -4688,19 +5676,19 @@ var BALA_COMPONENTS = [
4688
5676
  },
4689
5677
  { key: "drikBala", label: "Drik", color: "var(--roxy-danger, #dc2626)" }
4690
5678
  ];
4691
- var RoxyShadbalaTable = class extends import_lit21.LitElement {
5679
+ var RoxyShadbalaTable = class extends import_lit24.LitElement {
4692
5680
  constructor() {
4693
5681
  super(...arguments);
4694
5682
  this.data = null;
4695
5683
  }
4696
5684
  render() {
4697
5685
  if (!this.data?.planets?.length) {
4698
- return import_lit21.html`<div class="roxy-empty" role="status">No shadbala data</div>`;
5686
+ return import_lit24.html`<div class="roxy-empty" role="status">No shadbala data</div>`;
4699
5687
  }
4700
5688
  const sorted = [...this.data.planets].sort(
4701
5689
  (a, b) => a.relativeRank - b.relativeRank
4702
5690
  );
4703
- return import_lit21.html`<div class="wrap" aria-label="Shadbala planetary strength">
5691
+ return import_lit24.html`<div class="wrap" aria-label="Shadbala planetary strength">
4704
5692
  <div class="head">
4705
5693
  <h2 class="title">Shadbala</h2>
4706
5694
  <p class="subtitle">${sorted.length} planets ranked by strength</p>
@@ -4712,7 +5700,7 @@ var RoxyShadbalaTable = class extends import_lit21.LitElement {
4712
5700
 
4713
5701
  <div class="legend" aria-label="Strength component legend">
4714
5702
  ${BALA_COMPONENTS.map(
4715
- (b) => import_lit21.html`<div class="legend-row">
5703
+ (b) => import_lit24.html`<div class="legend-row">
4716
5704
  <span
4717
5705
  class="legend-swatch"
4718
5706
  style="background: ${b.color}"
@@ -4732,7 +5720,7 @@ var RoxyShadbalaTable = class extends import_lit21.LitElement {
4732
5720
  const badgeClass = isAdequate ? "adequacy-badge--adequate" : "adequacy-badge--weak";
4733
5721
  const badgeLabel = isAdequate ? "adequate" : "weak";
4734
5722
  const rupasStr = formatNumber(p.totalRupas, 2) && formatNumber(p.minRequired, 2) ? `${formatNumber(p.totalRupas, 2)} / ${formatNumber(p.minRequired, 2)} R` : "";
4735
- return import_lit21.html`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
5723
+ return import_lit24.html`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
4736
5724
  <div class="planet-label">
4737
5725
  <span class="glyph" aria-hidden="true">${glyph}</span>
4738
5726
  ${p.planet}
@@ -4742,18 +5730,18 @@ var RoxyShadbalaTable = class extends import_lit21.LitElement {
4742
5730
  <div class="bar" role="img" aria-label="Strength components for ${p.planet}">
4743
5731
  ${total > 0 ? BALA_COMPONENTS.map((b, i) => {
4744
5732
  const v = values[i];
4745
- if (v <= 0) return import_lit21.nothing;
5733
+ if (v <= 0) return import_lit24.nothing;
4746
5734
  const grow = v / total * 100;
4747
- return import_lit21.html`<div
5735
+ return import_lit24.html`<div
4748
5736
  class="bar-segment"
4749
5737
  style="flex-grow: ${grow}; background: ${b.color};"
4750
5738
  title="${b.label}: ${formatNumber(v, 1)}"
4751
5739
  ></div>`;
4752
- }) : import_lit21.nothing}
5740
+ }) : import_lit24.nothing}
4753
5741
  </div>
4754
5742
  </div>
4755
5743
  <div class="pills">
4756
- ${rupasStr ? import_lit21.html`<span class="rupas-label">${rupasStr}</span>` : import_lit21.nothing}
5744
+ ${rupasStr ? import_lit24.html`<span class="rupas-label">${rupasStr}</span>` : import_lit24.nothing}
4757
5745
  <span class="${`adequacy-badge ${badgeClass}`}">${badgeLabel}</span>
4758
5746
  </div>
4759
5747
  </div>`;
@@ -4761,7 +5749,7 @@ var RoxyShadbalaTable = class extends import_lit21.LitElement {
4761
5749
  };
4762
5750
  RoxyShadbalaTable.styles = [
4763
5751
  baseStyles,
4764
- import_lit21.css`
5752
+ import_lit24.css`
4765
5753
  .wrap {
4766
5754
  display: grid;
4767
5755
  gap: var(--roxy-space-md, 1rem);
@@ -4910,29 +5898,29 @@ RoxyShadbalaTable.styles = [
4910
5898
  `
4911
5899
  ];
4912
5900
  __decorateClass([
4913
- (0, import_decorators19.property)({ attribute: false })
5901
+ (0, import_decorators22.property)({ attribute: false })
4914
5902
  ], RoxyShadbalaTable.prototype, "data", 2);
4915
5903
  RoxyShadbalaTable = __decorateClass([
4916
- (0, import_decorators19.customElement)("roxy-shadbala-table")
5904
+ (0, import_decorators22.customElement)("roxy-shadbala-table")
4917
5905
  ], RoxyShadbalaTable);
4918
5906
 
4919
5907
  // packages/ui/src/components/synastry-chart.ts
4920
- var import_lit22 = require("lit");
4921
- var import_decorators20 = require("lit/decorators.js");
5908
+ var import_lit25 = require("lit");
5909
+ var import_decorators23 = require("lit/decorators.js");
4922
5910
  var SIZE2 = 360;
4923
5911
  var CENTER2 = SIZE2 / 2;
4924
5912
  var OUTER_R2 = 170;
4925
5913
  var SIGN_R2 = 154;
4926
5914
  var P1_R = 124;
4927
5915
  var P2_R = 96;
4928
- var RoxySynastryChart = class extends import_lit22.LitElement {
5916
+ var RoxySynastryChart = class extends import_lit25.LitElement {
4929
5917
  constructor() {
4930
5918
  super(...arguments);
4931
5919
  this.data = null;
4932
5920
  }
4933
5921
  render() {
4934
5922
  if (!this.data)
4935
- return import_lit22.html`<div class="roxy-empty" role="status">No synastry data</div>`;
5923
+ return import_lit25.html`<div class="roxy-empty" role="status">No synastry data</div>`;
4936
5924
  const { person1, person2, compatibilityScore, analysis } = this.data;
4937
5925
  const interAspects = this.data.interAspects ?? [];
4938
5926
  const p1Planets = person1?.planets ?? [];
@@ -4943,15 +5931,15 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
4943
5931
  const challenges = analysis?.challenges ?? [];
4944
5932
  const hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;
4945
5933
  if (!hasPlanets) {
4946
- return import_lit22.html`<div
5934
+ return import_lit25.html`<div
4947
5935
  class="wrap"
4948
5936
  aria-label="Synastry compatibility chart"
4949
5937
  >
4950
5938
  <div class="head">
4951
5939
  <h2 class="title">Synastry</h2>
4952
- ${typeof score === "number" ? import_lit22.html`<span class="score" aria-label=${`Score ${score} of 100`}
5940
+ ${typeof score === "number" ? import_lit25.html`<span class="score" aria-label=${`Score ${score} of 100`}
4953
5941
  >${score} / 100</span
4954
- >` : import_lit22.nothing}
5942
+ >` : import_lit25.nothing}
4955
5943
  </div>
4956
5944
  <div class="missing-planets" role="status">
4957
5945
  Synastry response missing planet positions. Pass
@@ -4959,33 +5947,33 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
4959
5947
  <code>person2.planets</code> arrays from the natal-chart endpoint, or
4960
5948
  use the <code>&lt;roxy-data&gt;</code> fallback.
4961
5949
  </div>
4962
- ${summaryText ? import_lit22.html`<p class="summary">${summaryText}</p>` : import_lit22.nothing}
4963
- ${interAspects.length > 0 ? this.renderAspects(interAspects) : import_lit22.nothing}
4964
- ${strengths.length > 0 || challenges.length > 0 ? import_lit22.html`<div class="lists">
4965
- ${strengths.length ? import_lit22.html`<div>
5950
+ ${summaryText ? import_lit25.html`<p class="summary">${summaryText}</p>` : import_lit25.nothing}
5951
+ ${interAspects.length > 0 ? this.renderAspects(interAspects) : import_lit25.nothing}
5952
+ ${strengths.length > 0 || challenges.length > 0 ? import_lit25.html`<div class="lists">
5953
+ ${strengths.length ? import_lit25.html`<div>
4966
5954
  <h3>Strengths</h3>
4967
5955
  <ul>
4968
- ${strengths.map((s) => import_lit22.html`<li>${s}</li>`)}
5956
+ ${strengths.map((s) => import_lit25.html`<li>${s}</li>`)}
4969
5957
  </ul>
4970
- </div>` : import_lit22.nothing}
4971
- ${challenges.length ? import_lit22.html`<div>
5958
+ </div>` : import_lit25.nothing}
5959
+ ${challenges.length ? import_lit25.html`<div>
4972
5960
  <h3>Challenges</h3>
4973
5961
  <ul>
4974
- ${challenges.map((s) => import_lit22.html`<li>${s}</li>`)}
5962
+ ${challenges.map((s) => import_lit25.html`<li>${s}</li>`)}
4975
5963
  </ul>
4976
- </div>` : import_lit22.nothing}
4977
- </div>` : import_lit22.nothing}
5964
+ </div>` : import_lit25.nothing}
5965
+ </div>` : import_lit25.nothing}
4978
5966
  </div>`;
4979
5967
  }
4980
- return import_lit22.html`<div
5968
+ return import_lit25.html`<div
4981
5969
  class="wrap"
4982
5970
  aria-label="Synastry compatibility chart"
4983
5971
  >
4984
5972
  <div class="head">
4985
5973
  <h2 class="title">Synastry</h2>
4986
- ${typeof score === "number" ? import_lit22.html`<span class="score" aria-label=${`Score ${score} of 100`}
5974
+ ${typeof score === "number" ? import_lit25.html`<span class="score" aria-label=${`Score ${score} of 100`}
4987
5975
  >${score} / 100</span
4988
- >` : import_lit22.nothing}
5976
+ >` : import_lit25.nothing}
4989
5977
  </div>
4990
5978
  <svg
4991
5979
  viewBox="0 0 ${SIZE2} ${SIZE2}"
@@ -5024,22 +6012,22 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5024
6012
  <span><span class="swatch" style="background: var(--roxy-success)"></span>harmonious</span>
5025
6013
  <span><span class="swatch" style="background: var(--roxy-danger)"></span>challenging</span>
5026
6014
  </div>
5027
- ${summaryText ? import_lit22.html`<p class="summary">${summaryText}</p>` : import_lit22.nothing}
5028
- ${interAspects.length > 0 ? this.renderAspects(interAspects) : import_lit22.nothing}
5029
- ${strengths.length > 0 || challenges.length > 0 ? import_lit22.html`<div class="lists">
5030
- ${strengths.length ? import_lit22.html`<div>
6015
+ ${summaryText ? import_lit25.html`<p class="summary">${summaryText}</p>` : import_lit25.nothing}
6016
+ ${interAspects.length > 0 ? this.renderAspects(interAspects) : import_lit25.nothing}
6017
+ ${strengths.length > 0 || challenges.length > 0 ? import_lit25.html`<div class="lists">
6018
+ ${strengths.length ? import_lit25.html`<div>
5031
6019
  <h3>Strengths</h3>
5032
6020
  <ul>
5033
- ${strengths.map((s) => import_lit22.html`<li>${s}</li>`)}
6021
+ ${strengths.map((s) => import_lit25.html`<li>${s}</li>`)}
5034
6022
  </ul>
5035
- </div>` : import_lit22.nothing}
5036
- ${challenges.length ? import_lit22.html`<div>
6023
+ </div>` : import_lit25.nothing}
6024
+ ${challenges.length ? import_lit25.html`<div>
5037
6025
  <h3>Challenges</h3>
5038
6026
  <ul>
5039
- ${challenges.map((s) => import_lit22.html`<li>${s}</li>`)}
6027
+ ${challenges.map((s) => import_lit25.html`<li>${s}</li>`)}
5040
6028
  </ul>
5041
- </div>` : import_lit22.nothing}
5042
- </div>` : import_lit22.nothing}
6029
+ </div>` : import_lit25.nothing}
6030
+ </div>` : import_lit25.nothing}
5043
6031
  </div>`;
5044
6032
  }
5045
6033
  toAngle(longitude) {
@@ -5050,19 +6038,19 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5050
6038
  const angle = this.toAngle(i * 30);
5051
6039
  const start = polarToCartesian(CENTER2, CENTER2, P2_R - 14, angle);
5052
6040
  const end = polarToCartesian(CENTER2, CENTER2, OUTER_R2, angle);
5053
- return import_lit22.svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.6" />`;
6041
+ return import_lit25.svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.6" />`;
5054
6042
  });
5055
6043
  }
5056
6044
  renderSigns() {
5057
6045
  return SIGNS_ORDER.map((s, i) => {
5058
6046
  const angle = this.toAngle(i * 30 + 15);
5059
6047
  const pos = polarToCartesian(CENTER2, CENTER2, SIGN_R2, angle);
5060
- return import_lit22.svg`<text class="sign" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[s]}</text>`;
6048
+ return import_lit25.svg`<text class="sign" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[s]}</text>`;
5061
6049
  });
5062
6050
  }
5063
6051
  renderRing(planets, radius, cls) {
5064
6052
  return planets.map((p) => {
5065
- if (!Number.isFinite(p.longitude)) return import_lit22.nothing;
6053
+ if (!Number.isFinite(p.longitude)) return import_lit25.nothing;
5066
6054
  const pos = polarToCartesian(
5067
6055
  CENTER2,
5068
6056
  CENTER2,
@@ -5070,7 +6058,7 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5070
6058
  this.toAngle(p.longitude)
5071
6059
  );
5072
6060
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
5073
- return import_lit22.svg`<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}</title>${glyph}</text>`;
6061
+ return import_lit25.svg`<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}</title>${glyph}</text>`;
5074
6062
  });
5075
6063
  }
5076
6064
  renderInterAspectLines(p1, p2, aspects) {
@@ -5085,17 +6073,17 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5085
6073
  return aspects.map((a) => {
5086
6074
  const l1 = longitudeOf(p1, a.planet1);
5087
6075
  const l2 = longitudeOf(p2, a.planet2);
5088
- if (l1 === void 0 || l2 === void 0) return import_lit22.nothing;
6076
+ if (l1 === void 0 || l2 === void 0) return import_lit25.nothing;
5089
6077
  const out = polarToCartesian(CENTER2, CENTER2, P1_R - 12, this.toAngle(l1));
5090
6078
  const inn = polarToCartesian(CENTER2, CENTER2, P2_R + 8, this.toAngle(l2));
5091
6079
  const aspectName = normalizeAspect(a);
5092
6080
  const cls = ASPECT_CLASS[aspectName] ?? "aspect-other";
5093
6081
  const orbLabel = formatNumber(a.orb, 1);
5094
- return import_lit22.svg`<line class=${`aspect ${cls}`} x1=${out.x} y1=${out.y} x2=${inn.x} y2=${inn.y}><title>${a.planet1} ${aspectName} ${a.planet2}${orbLabel ? ` (orb ${orbLabel}\xB0)` : ""}</title></line>`;
6082
+ return import_lit25.svg`<line class=${`aspect ${cls}`} x1=${out.x} y1=${out.y} x2=${inn.x} y2=${inn.y}><title>${a.planet1} ${aspectName} ${a.planet2}${orbLabel ? ` (orb ${orbLabel}\xB0)` : ""}</title></line>`;
5095
6083
  });
5096
6084
  }
5097
6085
  renderAspects(aspects) {
5098
- return import_lit22.html`<table>
6086
+ return import_lit25.html`<table>
5099
6087
  <thead>
5100
6088
  <tr>
5101
6089
  <th>Planet 1</th>
@@ -5107,7 +6095,7 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5107
6095
  </thead>
5108
6096
  <tbody>
5109
6097
  ${aspects.slice(0, 12).map(
5110
- (a) => import_lit22.html`<tr>
6098
+ (a) => import_lit25.html`<tr>
5111
6099
  <td>${a.planet1}</td>
5112
6100
  <td>${a.planet2}</td>
5113
6101
  <td>${normalizeAspect(a) || ""}</td>
@@ -5121,7 +6109,7 @@ var RoxySynastryChart = class extends import_lit22.LitElement {
5121
6109
  };
5122
6110
  RoxySynastryChart.styles = [
5123
6111
  baseStyles,
5124
- import_lit22.css`
6112
+ import_lit25.css`
5125
6113
  .wrap {
5126
6114
  display: grid;
5127
6115
  gap: var(--roxy-space-md, 1rem);
@@ -5276,10 +6264,10 @@ RoxySynastryChart.styles = [
5276
6264
  `
5277
6265
  ];
5278
6266
  __decorateClass([
5279
- (0, import_decorators20.property)({ attribute: false })
6267
+ (0, import_decorators23.property)({ attribute: false })
5280
6268
  ], RoxySynastryChart.prototype, "data", 2);
5281
6269
  RoxySynastryChart = __decorateClass([
5282
- (0, import_decorators20.customElement)("roxy-synastry-chart")
6270
+ (0, import_decorators23.customElement)("roxy-synastry-chart")
5283
6271
  ], RoxySynastryChart);
5284
6272
  function formatStrength(s) {
5285
6273
  if (typeof s === "number") return Math.round(s).toString();
@@ -5287,9 +6275,9 @@ function formatStrength(s) {
5287
6275
  }
5288
6276
 
5289
6277
  // packages/ui/src/components/tarot-card.ts
5290
- var import_lit23 = require("lit");
5291
- var import_decorators21 = require("lit/decorators.js");
5292
- var RoxyTarotCard = class extends import_lit23.LitElement {
6278
+ var import_lit26 = require("lit");
6279
+ var import_decorators24 = require("lit/decorators.js");
6280
+ var RoxyTarotCard = class extends import_lit26.LitElement {
5293
6281
  constructor() {
5294
6282
  super(...arguments);
5295
6283
  this.data = null;
@@ -5301,7 +6289,7 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5301
6289
  render() {
5302
6290
  const d = this.data;
5303
6291
  if (!d)
5304
- return import_lit23.html`<div class="roxy-empty" role="status">No tarot data</div>`;
6292
+ return import_lit26.html`<div class="roxy-empty" role="status">No tarot data</div>`;
5305
6293
  if ("card" in d) return this.renderDailyCard(d);
5306
6294
  return this.renderFullCard(d);
5307
6295
  }
@@ -5309,9 +6297,9 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5309
6297
  const card = d.card;
5310
6298
  const isReversed = this.flipped !== Boolean(card.reversed);
5311
6299
  const keywords = card.keywords ?? [];
5312
- return import_lit23.html`<article class="card" aria-label=${card.name ?? "Tarot card"}>
6300
+ return import_lit26.html`<article class="card" aria-label=${card.name ?? "Tarot card"}>
5313
6301
  <div class="image-wrap">
5314
- ${card.imageUrl ? import_lit23.html`<img
6302
+ ${card.imageUrl ? import_lit26.html`<img
5315
6303
  class=${`image ${isReversed ? "reversed" : ""}`}
5316
6304
  src=${card.imageUrl}
5317
6305
  alt=${card.name ?? "Tarot card"}
@@ -5323,7 +6311,7 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5323
6311
  this.toggleFlip();
5324
6312
  }
5325
6313
  }}
5326
- />` : import_lit23.html`<div
6314
+ />` : import_lit26.html`<div
5327
6315
  class=${`image ${isReversed ? "reversed" : ""}`}
5328
6316
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
5329
6317
  >
@@ -5332,15 +6320,15 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5332
6320
  </div>
5333
6321
  <div>
5334
6322
  <div class="meta">
5335
- ${card.arcana ? import_lit23.html`${card.arcana} arcana` : import_lit23.nothing}
5336
- ${isReversed ? import_lit23.html` · reversed` : import_lit23.nothing}
6323
+ ${card.arcana ? import_lit26.html`${card.arcana} arcana` : import_lit26.nothing}
6324
+ ${isReversed ? import_lit26.html` · reversed` : import_lit26.nothing}
5337
6325
  </div>
5338
6326
  <h2 class="title">${card.name ?? "Tarot card"}</h2>
5339
- ${d.dailyMessage ? import_lit23.html`<p class="message">${d.dailyMessage}</p>` : import_lit23.nothing}
5340
- ${card.meaning ? import_lit23.html`<p>${card.meaning}</p>` : import_lit23.nothing}
5341
- ${keywords.length > 0 ? import_lit23.html`<div class="chips">
5342
- ${keywords.map((k) => import_lit23.html`<span>${k}</span>`)}
5343
- </div>` : import_lit23.nothing}
6327
+ ${d.dailyMessage ? import_lit26.html`<p class="message">${d.dailyMessage}</p>` : import_lit26.nothing}
6328
+ ${card.meaning ? import_lit26.html`<p>${card.meaning}</p>` : import_lit26.nothing}
6329
+ ${keywords.length > 0 ? import_lit26.html`<div class="chips">
6330
+ ${keywords.map((k) => import_lit26.html`<span>${k}</span>`)}
6331
+ </div>` : import_lit26.nothing}
5344
6332
  <button
5345
6333
  class="flip"
5346
6334
  type="button"
@@ -5356,9 +6344,9 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5356
6344
  const isReversed = this.flipped;
5357
6345
  const orientedMeaning = isReversed ? d.reversed : d.upright;
5358
6346
  const keywords = isReversed ? d.keywords?.reversed ?? [] : d.keywords?.upright ?? [];
5359
- return import_lit23.html`<article class="card" aria-label=${d.name ?? "Tarot card"}>
6347
+ return import_lit26.html`<article class="card" aria-label=${d.name ?? "Tarot card"}>
5360
6348
  <div class="image-wrap">
5361
- ${d.imageUrl ? import_lit23.html`<img
6349
+ ${d.imageUrl ? import_lit26.html`<img
5362
6350
  class=${`image ${isReversed ? "reversed" : ""}`}
5363
6351
  src=${d.imageUrl}
5364
6352
  alt=${d.name ?? "Tarot card"}
@@ -5370,7 +6358,7 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5370
6358
  this.toggleFlip();
5371
6359
  }
5372
6360
  }}
5373
- />` : import_lit23.html`<div
6361
+ />` : import_lit26.html`<div
5374
6362
  class=${`image ${isReversed ? "reversed" : ""}`}
5375
6363
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
5376
6364
  >
@@ -5379,15 +6367,15 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5379
6367
  </div>
5380
6368
  <div>
5381
6369
  <div class="meta">
5382
- ${d.arcana ? import_lit23.html`${d.arcana} arcana` : import_lit23.nothing}
5383
- ${d.number !== void 0 && d.number !== null ? import_lit23.html` · ${d.number}` : import_lit23.nothing}
5384
- ${isReversed ? import_lit23.html` · reversed` : import_lit23.nothing}
6370
+ ${d.arcana ? import_lit26.html`${d.arcana} arcana` : import_lit26.nothing}
6371
+ ${d.number !== void 0 && d.number !== null ? import_lit26.html` · ${d.number}` : import_lit26.nothing}
6372
+ ${isReversed ? import_lit26.html` · reversed` : import_lit26.nothing}
5385
6373
  </div>
5386
6374
  <h2 class="title">${d.name ?? "Tarot card"}</h2>
5387
- ${orientedMeaning?.description ? import_lit23.html`<p>${orientedMeaning.description}</p>` : import_lit23.nothing}
5388
- ${keywords.length > 0 ? import_lit23.html`<div class="chips">
5389
- ${keywords.map((k) => import_lit23.html`<span>${k}</span>`)}
5390
- </div>` : import_lit23.nothing}
6375
+ ${orientedMeaning?.description ? import_lit26.html`<p>${orientedMeaning.description}</p>` : import_lit26.nothing}
6376
+ ${keywords.length > 0 ? import_lit26.html`<div class="chips">
6377
+ ${keywords.map((k) => import_lit26.html`<span>${k}</span>`)}
6378
+ </div>` : import_lit26.nothing}
5391
6379
  <button
5392
6380
  class="flip"
5393
6381
  type="button"
@@ -5402,7 +6390,7 @@ var RoxyTarotCard = class extends import_lit23.LitElement {
5402
6390
  };
5403
6391
  RoxyTarotCard.styles = [
5404
6392
  baseStyles,
5405
- import_lit23.css`
6393
+ import_lit26.css`
5406
6394
  .card {
5407
6395
  background: var(--roxy-bg, #fff);
5408
6396
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -5495,19 +6483,19 @@ RoxyTarotCard.styles = [
5495
6483
  `
5496
6484
  ];
5497
6485
  __decorateClass([
5498
- (0, import_decorators21.property)({ attribute: false })
6486
+ (0, import_decorators24.property)({ attribute: false })
5499
6487
  ], RoxyTarotCard.prototype, "data", 2);
5500
6488
  __decorateClass([
5501
- (0, import_decorators21.state)()
6489
+ (0, import_decorators24.state)()
5502
6490
  ], RoxyTarotCard.prototype, "flipped", 2);
5503
6491
  RoxyTarotCard = __decorateClass([
5504
- (0, import_decorators21.customElement)("roxy-tarot-card")
6492
+ (0, import_decorators24.customElement)("roxy-tarot-card")
5505
6493
  ], RoxyTarotCard);
5506
6494
 
5507
6495
  // packages/ui/src/components/tarot-spread.ts
5508
- var import_lit24 = require("lit");
5509
- var import_decorators22 = require("lit/decorators.js");
5510
- var RoxyTarotSpread = class extends import_lit24.LitElement {
6496
+ var import_lit27 = require("lit");
6497
+ var import_decorators25 = require("lit/decorators.js");
6498
+ var RoxyTarotSpread = class extends import_lit27.LitElement {
5511
6499
  constructor() {
5512
6500
  super(...arguments);
5513
6501
  this.data = null;
@@ -5516,7 +6504,7 @@ var RoxyTarotSpread = class extends import_lit24.LitElement {
5516
6504
  render() {
5517
6505
  const d = this.data;
5518
6506
  if (!d)
5519
- return import_lit24.html`<div class="roxy-empty" role="status">No tarot spread</div>`;
6507
+ return import_lit27.html`<div class="roxy-empty" role="status">No tarot spread</div>`;
5520
6508
  const isYesNo = "answer" in d;
5521
6509
  const isDrawn = "cards" in d && !("spread" in d);
5522
6510
  const positions = isDrawn ? [] : "positions" in d ? d.positions ?? [] : [];
@@ -5528,60 +6516,60 @@ var RoxyTarotSpread = class extends import_lit24.LitElement {
5528
6516
  const summary = "summary" in d ? d.summary : void 0;
5529
6517
  const yesNoInterp = isYesNo ? d.interpretation : void 0;
5530
6518
  const answerClass = answer ? answer.toLowerCase().replace(/[^a-z]/g, "") : "";
5531
- return import_lit24.html`<article class="wrap" aria-label="Tarot spread">
6519
+ return import_lit27.html`<article class="wrap" aria-label="Tarot spread">
5532
6520
  <header class="head">
5533
6521
  <h2 class="title">${spreadLabel}</h2>
5534
- ${question ? import_lit24.html`<span class="question">"${question}"</span>` : import_lit24.nothing}
6522
+ ${question ? import_lit27.html`<span class="question">"${question}"</span>` : import_lit27.nothing}
5535
6523
  </header>
5536
- ${isYesNo ? import_lit24.html`<div>
6524
+ ${isYesNo ? import_lit27.html`<div>
5537
6525
  <span class=${`answer ${answerClass}`}>${answer}</span>
5538
- ${strength ? import_lit24.html`<small> · ${strength}</small>` : import_lit24.nothing}
5539
- </div>` : import_lit24.nothing}
5540
- ${positions.length > 0 ? import_lit24.html`<div class="grid">
6526
+ ${strength ? import_lit27.html`<small> · ${strength}</small>` : import_lit27.nothing}
6527
+ </div>` : import_lit27.nothing}
6528
+ ${positions.length > 0 ? import_lit27.html`<div class="grid">
5541
6529
  ${positions.map(
5542
- (p) => import_lit24.html`<div class="card">
6530
+ (p) => import_lit27.html`<div class="card">
5543
6531
  <p class="label">${p.name ?? ""}</p>
5544
6532
  <div class="image">
5545
- ${p.card?.imageUrl ? import_lit24.html`<img
6533
+ ${p.card?.imageUrl ? import_lit27.html`<img
5546
6534
  src=${p.card.imageUrl}
5547
6535
  alt=${p.card.name ?? "tarot card"}
5548
6536
  class=${p.card.reversed ? "reversed" : ""}
5549
- />` : import_lit24.html`${p.card?.name ?? "?"}`}
6537
+ />` : import_lit27.html`${p.card?.name ?? "?"}`}
5550
6538
  </div>
5551
6539
  <p class="name">
5552
6540
  ${p.card?.name ?? ""}
5553
- ${p.card?.reversed ? import_lit24.html`<small>(reversed)</small>` : import_lit24.nothing}
6541
+ ${p.card?.reversed ? import_lit27.html`<small>(reversed)</small>` : import_lit27.nothing}
5554
6542
  </p>
5555
- ${p.interpretation ? import_lit24.html`<p class="interp">${p.interpretation}</p>` : import_lit24.nothing}
6543
+ ${p.interpretation ? import_lit27.html`<p class="interp">${p.interpretation}</p>` : import_lit27.nothing}
5556
6544
  </div>`
5557
6545
  )}
5558
- </div>` : import_lit24.nothing}
5559
- ${cards.length > 0 ? import_lit24.html`<div class="grid">
6546
+ </div>` : import_lit27.nothing}
6547
+ ${cards.length > 0 ? import_lit27.html`<div class="grid">
5560
6548
  ${cards.map(
5561
- (c) => import_lit24.html`<div class="card">
6549
+ (c) => import_lit27.html`<div class="card">
5562
6550
  <div class="image">
5563
- ${c.imageUrl ? import_lit24.html`<img
6551
+ ${c.imageUrl ? import_lit27.html`<img
5564
6552
  src=${c.imageUrl}
5565
6553
  alt=${c.name ?? "tarot card"}
5566
6554
  class=${c.reversed ? "reversed" : ""}
5567
- />` : import_lit24.html`${c.name ?? "?"}`}
6555
+ />` : import_lit27.html`${c.name ?? "?"}`}
5568
6556
  </div>
5569
6557
  <p class="name">
5570
6558
  ${c.name ?? ""}
5571
- ${c.reversed ? import_lit24.html`<small>(reversed)</small>` : import_lit24.nothing}
6559
+ ${c.reversed ? import_lit27.html`<small>(reversed)</small>` : import_lit27.nothing}
5572
6560
  </p>
5573
- ${c.meaning ? import_lit24.html`<p class="interp">${c.meaning}</p>` : import_lit24.nothing}
6561
+ ${c.meaning ? import_lit27.html`<p class="interp">${c.meaning}</p>` : import_lit27.nothing}
5574
6562
  </div>`
5575
6563
  )}
5576
- </div>` : import_lit24.nothing}
5577
- ${summary ? import_lit24.html`<p class="reading">${summary}</p>` : import_lit24.nothing}
5578
- ${yesNoInterp ? import_lit24.html`<p class="reading">${yesNoInterp}</p>` : import_lit24.nothing}
6564
+ </div>` : import_lit27.nothing}
6565
+ ${summary ? import_lit27.html`<p class="reading">${summary}</p>` : import_lit27.nothing}
6566
+ ${yesNoInterp ? import_lit27.html`<p class="reading">${yesNoInterp}</p>` : import_lit27.nothing}
5579
6567
  </article>`;
5580
6568
  }
5581
6569
  };
5582
6570
  RoxyTarotSpread.styles = [
5583
6571
  baseStyles,
5584
- import_lit24.css`
6572
+ import_lit27.css`
5585
6573
  .wrap {
5586
6574
  display: grid;
5587
6575
  gap: var(--roxy-space-md, 1rem);
@@ -5690,26 +6678,26 @@ RoxyTarotSpread.styles = [
5690
6678
  `
5691
6679
  ];
5692
6680
  __decorateClass([
5693
- (0, import_decorators22.property)({ attribute: false })
6681
+ (0, import_decorators25.property)({ attribute: false })
5694
6682
  ], RoxyTarotSpread.prototype, "data", 2);
5695
6683
  __decorateClass([
5696
- (0, import_decorators22.property)({ type: String, reflect: true })
6684
+ (0, import_decorators25.property)({ type: String, reflect: true })
5697
6685
  ], RoxyTarotSpread.prototype, "spread", 2);
5698
6686
  RoxyTarotSpread = __decorateClass([
5699
- (0, import_decorators22.customElement)("roxy-tarot-spread")
6687
+ (0, import_decorators25.customElement)("roxy-tarot-spread")
5700
6688
  ], RoxyTarotSpread);
5701
6689
 
5702
6690
  // packages/ui/src/components/transits-table.ts
5703
- var import_lit25 = require("lit");
5704
- var import_decorators23 = require("lit/decorators.js");
5705
- var RoxyTransitsTable = class extends import_lit25.LitElement {
6691
+ var import_lit28 = require("lit");
6692
+ var import_decorators26 = require("lit/decorators.js");
6693
+ var RoxyTransitsTable = class extends import_lit28.LitElement {
5706
6694
  constructor() {
5707
6695
  super(...arguments);
5708
6696
  this.data = null;
5709
6697
  }
5710
6698
  render() {
5711
6699
  if (!this.data?.transitPlanets?.length) {
5712
- return import_lit25.html`<div class="roxy-empty" role="status">No transits data</div>`;
6700
+ return import_lit28.html`<div class="roxy-empty" role="status">No transits data</div>`;
5713
6701
  }
5714
6702
  const {
5715
6703
  transitDate,
@@ -5719,13 +6707,13 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5719
6707
  summary
5720
6708
  } = this.data;
5721
6709
  const dateStr = [formatDate(transitDate), formatTime(transitTime)].filter(Boolean).join(" ");
5722
- return import_lit25.html`<div class="wrap" aria-label="Transit positions table">
6710
+ return import_lit28.html`<div class="wrap" aria-label="Transit positions table">
5723
6711
  <div class="head">
5724
6712
  <h2 class="title">Transits</h2>
5725
- ${dateStr ? import_lit25.html`<p class="subtitle">${dateStr}</p>` : import_lit25.nothing}
6713
+ ${dateStr ? import_lit28.html`<p class="subtitle">${dateStr}</p>` : import_lit28.nothing}
5726
6714
  </div>
5727
6715
 
5728
- ${summary ? this.renderSummaryPills(summary) : import_lit25.nothing}
6716
+ ${summary ? this.renderSummaryPills(summary) : import_lit28.nothing}
5729
6717
 
5730
6718
  <div>
5731
6719
  <p class="section-label">Planet positions</p>
@@ -5734,16 +6722,16 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5734
6722
  </div>
5735
6723
  </div>
5736
6724
 
5737
- ${transitAspects?.length ? import_lit25.html`<div>
6725
+ ${transitAspects?.length ? import_lit28.html`<div>
5738
6726
  <p class="section-label">Transit aspects</p>
5739
6727
  <div class="overflow-scroll">
5740
6728
  ${this.renderAspectsList(transitAspects)}
5741
6729
  </div>
5742
- </div>` : import_lit25.nothing}
6730
+ </div>` : import_lit28.nothing}
5743
6731
  </div>`;
5744
6732
  }
5745
6733
  renderSummaryPills(summary) {
5746
- return import_lit25.html`<div class="summary-pills" role="region" aria-label="Aspect summary">
6734
+ return import_lit28.html`<div class="summary-pills" role="region" aria-label="Aspect summary">
5747
6735
  <span class="pill pill--muted">
5748
6736
  Total: ${summary.totalAspects}
5749
6737
  </span>
@@ -5759,7 +6747,7 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5759
6747
  </div>`;
5760
6748
  }
5761
6749
  renderPlanetsTable(planets) {
5762
- return import_lit25.html`<table class="planets-table">
6750
+ return import_lit28.html`<table class="planets-table">
5763
6751
  <thead>
5764
6752
  <tr>
5765
6753
  <th scope="col">Planet</th>
@@ -5773,12 +6761,12 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5773
6761
  const pGlyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
5774
6762
  const sGlyph = SIGN_GLYPH[capitalize(p.sign)] ?? "";
5775
6763
  const speedArrow = p.speed >= 0 ? "\u2191" : "\u2193";
5776
- return import_lit25.html`<tr>
6764
+ return import_lit28.html`<tr>
5777
6765
  <td>
5778
6766
  <div class="planet-cell">
5779
6767
  <span class="glyph" aria-hidden="true">${pGlyph}</span>
5780
6768
  ${p.name}
5781
- ${p.isRetrograde ? import_lit25.html`<span class="retro-badge" aria-label="retrograde">R</span>` : import_lit25.nothing}
6769
+ ${p.isRetrograde ? import_lit28.html`<span class="retro-badge" aria-label="retrograde">R</span>` : import_lit28.nothing}
5782
6770
  </div>
5783
6771
  </td>
5784
6772
  <td>
@@ -5798,7 +6786,7 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5798
6786
  </table>`;
5799
6787
  }
5800
6788
  renderAspectsList(aspects) {
5801
- return import_lit25.html`<div role="list" aria-label="Transit aspects">
6789
+ return import_lit28.html`<div role="list" aria-label="Transit aspects">
5802
6790
  ${aspects.map((a, idx) => {
5803
6791
  const tGlyph = PLANET_GLYPH[capitalize(a.transitPlanet)] ?? "";
5804
6792
  const nGlyph = PLANET_GLYPH[capitalize(a.natalPlanet)] ?? "";
@@ -5806,7 +6794,7 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5806
6794
  const interp = a.interpretation;
5807
6795
  const type = (a.type ?? "").toLowerCase();
5808
6796
  const status = a.isApplying ? "Applying" : "Separating";
5809
- return import_lit25.html`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
6797
+ return import_lit28.html`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
5810
6798
  <summary>
5811
6799
  <span aria-hidden="true">${tGlyph}</span>
5812
6800
  ${a.transitPlanet}
@@ -5818,13 +6806,13 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5818
6806
  </span>
5819
6807
  </summary>
5820
6808
  <div class="interp-body">
5821
- ${interp?.summary ? import_lit25.html`<p>${interp.summary}</p>` : import_lit25.nothing}
5822
- ${interp?.impact ? import_lit25.html`<p><strong>Impact:</strong> ${interp.impact}</p>` : import_lit25.nothing}
5823
- ${interp?.timing ? import_lit25.html`<p><strong>Timing:</strong> ${interp.timing}</p>` : import_lit25.nothing}
5824
- ${interp?.guidance ? import_lit25.html`<p><strong>Guidance:</strong> ${interp.guidance}</p>` : import_lit25.nothing}
5825
- ${interp?.keywords?.length ? import_lit25.html`<div class="interp-keywords">
5826
- ${interp.keywords.map((k) => import_lit25.html`<span class="kw">${k}</span>`)}
5827
- </div>` : import_lit25.nothing}
6809
+ ${interp?.summary ? import_lit28.html`<p>${interp.summary}</p>` : import_lit28.nothing}
6810
+ ${interp?.impact ? import_lit28.html`<p><strong>Impact:</strong> ${interp.impact}</p>` : import_lit28.nothing}
6811
+ ${interp?.timing ? import_lit28.html`<p><strong>Timing:</strong> ${interp.timing}</p>` : import_lit28.nothing}
6812
+ ${interp?.guidance ? import_lit28.html`<p><strong>Guidance:</strong> ${interp.guidance}</p>` : import_lit28.nothing}
6813
+ ${interp?.keywords?.length ? import_lit28.html`<div class="interp-keywords">
6814
+ ${interp.keywords.map((k) => import_lit28.html`<span class="kw">${k}</span>`)}
6815
+ </div>` : import_lit28.nothing}
5828
6816
  </div>
5829
6817
  </details>`;
5830
6818
  })}
@@ -5833,7 +6821,7 @@ var RoxyTransitsTable = class extends import_lit25.LitElement {
5833
6821
  };
5834
6822
  RoxyTransitsTable.styles = [
5835
6823
  baseStyles,
5836
- import_lit25.css`
6824
+ import_lit28.css`
5837
6825
  .wrap {
5838
6826
  display: grid;
5839
6827
  gap: var(--roxy-space-md, 1rem);
@@ -6032,46 +7020,33 @@ RoxyTransitsTable.styles = [
6032
7020
  `
6033
7021
  ];
6034
7022
  __decorateClass([
6035
- (0, import_decorators23.property)({ attribute: false })
7023
+ (0, import_decorators26.property)({ attribute: false })
6036
7024
  ], RoxyTransitsTable.prototype, "data", 2);
6037
7025
  RoxyTransitsTable = __decorateClass([
6038
- (0, import_decorators23.customElement)("roxy-transits-table")
7026
+ (0, import_decorators26.customElement)("roxy-transits-table")
6039
7027
  ], RoxyTransitsTable);
6040
7028
 
6041
7029
  // packages/ui/src/components/vedic-kundli.ts
6042
- var import_lit26 = require("lit");
6043
- var import_decorators24 = require("lit/decorators.js");
6044
- var RoxyVedicKundli = class extends import_lit26.LitElement {
7030
+ var import_lit29 = require("lit");
7031
+ var import_decorators27 = require("lit/decorators.js");
7032
+ var RoxyVedicKundli = class extends import_lit29.LitElement {
6045
7033
  constructor() {
6046
7034
  super(...arguments);
6047
7035
  this.data = null;
6048
7036
  this.chartStyle = "south";
6049
7037
  }
6050
7038
  buildHouses() {
6051
- if (!this.data) return [];
6052
- const data = this.data;
6053
- const lagnaSign = this.data?.meta?.Lagna?.rashi ?? "";
6054
- const houses = [];
6055
- for (let i = 0; i < 12; i++) {
6056
- const key = RASHI_KEYS[i];
6057
- const bucket = data[key];
6058
- const planets = (bucket?.signs ?? []).map((p) => p.graha).filter(Boolean);
6059
- const sign = RASHI_TO_SIGN[key] ?? "";
6060
- houses.push({
6061
- number: i + 1,
6062
- sign,
6063
- planets,
6064
- isLagna: lagnaSign ? lagnaSign.toLowerCase() === sign.toLowerCase() : false
6065
- });
6066
- }
6067
- return houses;
7039
+ if (!this.data?.meta) return [];
7040
+ return buildHousesFromMeta(this.data.meta);
6068
7041
  }
6069
7042
  render() {
6070
7043
  if (!this.data)
6071
- return import_lit26.html`<div class="roxy-empty" role="status">No kundli data</div>`;
7044
+ return import_lit29.html`<div class="roxy-empty" role="status">No kundli data</div>`;
6072
7045
  const houses = this.buildHouses();
6073
- const isNorth = this.chartStyle === "north";
6074
- return import_lit26.html`<div class="wrap">
7046
+ const style = this.chartStyle;
7047
+ const frame = style === "north" ? renderNorthFrame() : style === "east" ? renderEastFrame() : renderSouthFrame();
7048
+ const houseGroup = style === "north" ? renderNorthHouseGroup : style === "east" ? renderEastHouseGroup : renderSouthHouseGroup;
7049
+ return import_lit29.html`<div class="wrap">
6075
7050
  <h2 class="title">Vedic kundli</h2>
6076
7051
  <svg
6077
7052
  viewBox="0 0 300 300"
@@ -6079,15 +7054,15 @@ var RoxyVedicKundli = class extends import_lit26.LitElement {
6079
7054
  aria-label="Vedic birth chart with twelve sign houses"
6080
7055
  >
6081
7056
  <title>Vedic kundli</title>
6082
- ${isNorth ? renderNorthFrame() : renderSouthFrame()}
6083
- ${isNorth ? houses.map((h) => renderNorthHouseGroup(h)) : houses.map((h) => renderSouthHouseGroup(h))}
7057
+ ${frame}
7058
+ ${houses.map((h) => houseGroup(h))}
6084
7059
  </svg>
6085
7060
  </div>`;
6086
7061
  }
6087
7062
  };
6088
7063
  RoxyVedicKundli.styles = [
6089
7064
  baseStyles,
6090
- import_lit26.css`
7065
+ import_lit29.css`
6091
7066
  .wrap {
6092
7067
  display: grid;
6093
7068
  gap: var(--roxy-space-md, 1rem);
@@ -6095,64 +7070,381 @@ RoxyVedicKundli.styles = [
6095
7070
  .title {
6096
7071
  font-size: var(--roxy-text-lg, 1.125rem);
6097
7072
  font-weight: var(--roxy-weight-bold, 600);
6098
- margin: 0;
7073
+ margin: 0;
7074
+ }
7075
+ svg {
7076
+ display: block;
7077
+ width: 100%;
7078
+ max-width: 360px;
7079
+ margin: 0 auto;
7080
+ }
7081
+ .line {
7082
+ fill: transparent;
7083
+ stroke: var(--roxy-border, #e4e4e7);
7084
+ }
7085
+ .sign-text {
7086
+ fill: var(--roxy-muted, #71717a);
7087
+ font-size: 9px;
7088
+ font-weight: 500;
7089
+ font-family: var(--roxy-font-sans);
7090
+ }
7091
+ .planet-text {
7092
+ fill: var(--roxy-fg, #0a0a0a);
7093
+ font-size: 10px;
7094
+ font-weight: 600;
7095
+ font-family: var(--roxy-font-sans);
7096
+ }
7097
+ .house-num {
7098
+ fill: var(--roxy-muted, #71717a);
7099
+ font-size: 9px;
7100
+ font-weight: 400;
7101
+ font-family: var(--roxy-font-sans);
7102
+ }
7103
+ .lagna-marker {
7104
+ fill: var(--roxy-accent-fg, #b45309);
7105
+ font-size: 8px;
7106
+ font-weight: 700;
7107
+ font-family: var(--roxy-font-sans);
7108
+ letter-spacing: 0.05em;
7109
+ }
7110
+ .lagna-bg {
7111
+ fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
7112
+ stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
7113
+ stroke-width: 0.8;
7114
+ }
7115
+ `
7116
+ ];
7117
+ __decorateClass([
7118
+ (0, import_decorators27.property)({ attribute: false })
7119
+ ], RoxyVedicKundli.prototype, "data", 2);
7120
+ __decorateClass([
7121
+ (0, import_decorators27.property)({ type: String, reflect: true, attribute: "chart-style" })
7122
+ ], RoxyVedicKundli.prototype, "chartStyle", 2);
7123
+ RoxyVedicKundli = __decorateClass([
7124
+ (0, import_decorators27.customElement)("roxy-vedic-kundli")
7125
+ ], RoxyVedicKundli);
7126
+
7127
+ // packages/ui/src/components/vedic-planets-table.ts
7128
+ var import_lit30 = require("lit");
7129
+ var import_decorators28 = require("lit/decorators.js");
7130
+ var GRAHA_ORDER = [
7131
+ "Lagna",
7132
+ "Sun",
7133
+ "Moon",
7134
+ "Mars",
7135
+ "Mercury",
7136
+ "Jupiter",
7137
+ "Venus",
7138
+ "Saturn",
7139
+ "Rahu",
7140
+ "Ketu"
7141
+ ];
7142
+ var RoxyVedicPlanetsTable = class extends import_lit30.LitElement {
7143
+ constructor() {
7144
+ super(...arguments);
7145
+ this.data = null;
7146
+ }
7147
+ /** Ordered [name, entry] pairs: GRAHA_ORDER first, then any extras. */
7148
+ orderedRows() {
7149
+ const meta = this.data?.meta ?? {};
7150
+ const seen = /* @__PURE__ */ new Set();
7151
+ const rows = [];
7152
+ for (const name of GRAHA_ORDER) {
7153
+ const entry = meta[name];
7154
+ if (entry) {
7155
+ rows.push([name, entry]);
7156
+ seen.add(name);
7157
+ }
7158
+ }
7159
+ for (const [name, entry] of Object.entries(meta)) {
7160
+ if (!seen.has(name)) rows.push([name, entry]);
7161
+ }
7162
+ return rows;
7163
+ }
7164
+ render() {
7165
+ if (!this.data?.meta)
7166
+ return import_lit30.html`<div class="roxy-empty" role="status">No chart data</div>`;
7167
+ const rows = this.orderedRows();
7168
+ return import_lit30.html`<div class="wrap" aria-label="Vedic planetary positions" tabindex="0">
7169
+ <header class="head">
7170
+ <h2 class="title">Planetary positions</h2>
7171
+ </header>
7172
+ <table role="table">
7173
+ <thead>
7174
+ <tr>
7175
+ <th scope="col">Graha</th>
7176
+ <th scope="col">Rashi</th>
7177
+ <th scope="col">Degree</th>
7178
+ <th scope="col">Nakshatra</th>
7179
+ <th scope="col">Pada</th>
7180
+ <th scope="col">Nak. lord</th>
7181
+ <th scope="col">House</th>
7182
+ <th scope="col">Avastha</th>
7183
+ <th scope="col">Retro</th>
7184
+ </tr>
7185
+ </thead>
7186
+ <tbody>
7187
+ ${rows.map(([name, p]) => {
7188
+ const isLagna = (p.graha ?? name) === "Lagna";
7189
+ const glyph = PLANET_GLYPH[capitalize(p.graha ?? name)] ?? "";
7190
+ const signGlyph = SIGN_GLYPH[capitalize(p.rashi ?? "")] ?? "";
7191
+ return import_lit30.html`<tr class=${isLagna ? "lagna" : ""}>
7192
+ <td class="graha">
7193
+ ${glyph ? import_lit30.html`<span class="glyph">${glyph}</span>` : import_lit30.nothing}${p.graha ?? name}
7194
+ </td>
7195
+ <td>
7196
+ ${signGlyph ? import_lit30.html`<span class="glyph">${signGlyph}</span>` : import_lit30.nothing}${p.rashi ?? ""}
7197
+ </td>
7198
+ <td class="num">
7199
+ ${typeof p.longitude === "number" ? formatSignPosition(p.longitude) : ""}
7200
+ </td>
7201
+ <td>${p.nakshatra?.name ?? ""}</td>
7202
+ <td class="num">${p.nakshatra?.pada ?? ""}</td>
7203
+ <td>${p.nakshatra?.lord ?? ""}</td>
7204
+ <td class="num">${typeof p.house === "number" ? p.house : ""}</td>
7205
+ <td>${p.awastha ?? ""}</td>
7206
+ <td>${p.isRetrograde ? import_lit30.html`<span class="retro">R</span>` : import_lit30.nothing}</td>
7207
+ </tr>`;
7208
+ })}
7209
+ </tbody>
7210
+ </table>
7211
+ </div>`;
7212
+ }
7213
+ };
7214
+ RoxyVedicPlanetsTable.styles = [
7215
+ baseStyles,
7216
+ import_lit30.css`
7217
+ .wrap {
7218
+ border: 1px solid var(--roxy-border, #e4e4e7);
7219
+ border-radius: var(--roxy-radius-md, 8px);
7220
+ background: var(--roxy-bg, #fff);
7221
+ overflow: auto;
7222
+ box-shadow: var(--roxy-shadow-sm);
7223
+ }
7224
+ .head {
7225
+ padding: var(--roxy-space-md, 1rem);
7226
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
7227
+ }
7228
+ .title {
7229
+ margin: 0;
7230
+ font-size: var(--roxy-text-lg, 1.125rem);
7231
+ font-weight: var(--roxy-weight-bold, 600);
7232
+ }
7233
+ table {
7234
+ width: 100%;
7235
+ border-collapse: collapse;
7236
+ font-size: var(--roxy-text-sm, 0.875rem);
7237
+ min-width: 620px;
7238
+ }
7239
+ thead {
7240
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
7241
+ }
7242
+ th,
7243
+ td {
7244
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
7245
+ text-align: left;
7246
+ white-space: nowrap;
7247
+ }
7248
+ th {
7249
+ color: var(--roxy-muted, #71717a);
7250
+ font-weight: var(--roxy-weight-bold, 600);
7251
+ text-transform: uppercase;
7252
+ font-size: var(--roxy-text-xs, 0.75rem);
7253
+ letter-spacing: 0.04em;
7254
+ }
7255
+ tbody tr {
7256
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
7257
+ }
7258
+ tbody tr.lagna {
7259
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 10%, transparent);
7260
+ }
7261
+ td.graha {
7262
+ font-weight: var(--roxy-weight-bold, 600);
7263
+ color: var(--roxy-fg, #0a0a0a);
7264
+ }
7265
+ .glyph {
7266
+ margin-right: 0.4em;
7267
+ color: var(--roxy-muted, #71717a);
7268
+ }
7269
+ /* On the tinted Lagna row the muted glyph drops below the WCAG AA
7270
+ contrast floor, so use the accent foreground there instead. */
7271
+ tbody tr.lagna .glyph {
7272
+ color: var(--roxy-accent-fg, #b45309);
7273
+ }
7274
+ .retro {
7275
+ color: var(--roxy-warning-fg, #9a3412);
7276
+ font-size: var(--roxy-text-xs, 0.75rem);
7277
+ font-weight: var(--roxy-weight-bold, 600);
7278
+ }
7279
+ .num {
7280
+ font-variant-numeric: tabular-nums;
7281
+ }
7282
+ `
7283
+ ];
7284
+ __decorateClass([
7285
+ (0, import_decorators28.property)({ attribute: false })
7286
+ ], RoxyVedicPlanetsTable.prototype, "data", 2);
7287
+ RoxyVedicPlanetsTable = __decorateClass([
7288
+ (0, import_decorators28.customElement)("roxy-vedic-planets-table")
7289
+ ], RoxyVedicPlanetsTable);
7290
+
7291
+ // packages/ui/src/components/western-planets-table.ts
7292
+ var import_lit31 = require("lit");
7293
+ var import_decorators29 = require("lit/decorators.js");
7294
+ var RoxyWesternPlanetsTable = class extends import_lit31.LitElement {
7295
+ constructor() {
7296
+ super(...arguments);
7297
+ this.data = null;
7298
+ }
7299
+ /** Build the ordered row list: the planets array, then the four chart points. */
7300
+ rows() {
7301
+ const d = this.data;
7302
+ if (!d) return [];
7303
+ const rows = (d.planets ?? []).map((p) => ({
7304
+ name: p.name,
7305
+ sign: p.sign,
7306
+ longitude: p.longitude,
7307
+ house: p.house,
7308
+ speed: p.speed,
7309
+ isRetrograde: p.isRetrograde
7310
+ }));
7311
+ for (const [name, point] of [
7312
+ ["Ascendant", d.ascendant],
7313
+ ["Midheaven", d.midheaven],
7314
+ ["Part of Fortune", d.partOfFortune],
7315
+ ["Vertex", d.vertex]
7316
+ ]) {
7317
+ if (point) {
7318
+ rows.push({
7319
+ name,
7320
+ sign: point.sign,
7321
+ longitude: point.longitude,
7322
+ isPoint: true
7323
+ });
7324
+ }
7325
+ }
7326
+ return rows;
7327
+ }
7328
+ render() {
7329
+ if (!this.data?.planets)
7330
+ return import_lit31.html`<div class="roxy-empty" role="status">No chart data</div>`;
7331
+ const rows = this.rows();
7332
+ return import_lit31.html`<div class="wrap" aria-label="Western planetary positions" tabindex="0">
7333
+ <header class="head">
7334
+ <h2 class="title">Planetary positions</h2>
7335
+ </header>
7336
+ <table role="table">
7337
+ <thead>
7338
+ <tr>
7339
+ <th scope="col">Body</th>
7340
+ <th scope="col">Sign</th>
7341
+ <th scope="col">Degree</th>
7342
+ <th scope="col">House</th>
7343
+ <th scope="col">Motion</th>
7344
+ </tr>
7345
+ </thead>
7346
+ <tbody>
7347
+ ${rows.map((r) => {
7348
+ const glyph = PLANET_GLYPH[capitalize(r.name)] ?? "";
7349
+ const signGlyph = SIGN_GLYPH[capitalize(r.sign ?? "")] ?? "";
7350
+ const speed = typeof r.speed === "number" ? formatNumber(r.speed, 3) : "";
7351
+ return import_lit31.html`<tr class=${r.isPoint ? "point" : ""}>
7352
+ <td class="body">
7353
+ ${glyph ? import_lit31.html`<span class="glyph">${glyph}</span>` : import_lit31.nothing}${r.name}
7354
+ </td>
7355
+ <td>
7356
+ ${signGlyph ? import_lit31.html`<span class="glyph">${signGlyph}</span>` : import_lit31.nothing}${r.sign ?? ""}
7357
+ </td>
7358
+ <td class="num">
7359
+ ${typeof r.longitude === "number" ? formatSignPosition(r.longitude) : ""}
7360
+ </td>
7361
+ <td class="num">${typeof r.house === "number" ? r.house : ""}</td>
7362
+ <td class="num">
7363
+ ${speed ? import_lit31.html`${speed}°/day` : import_lit31.nothing}
7364
+ ${r.isRetrograde ? import_lit31.html`<span class="retro"> ℞</span>` : import_lit31.nothing}
7365
+ </td>
7366
+ </tr>`;
7367
+ })}
7368
+ </tbody>
7369
+ </table>
7370
+ </div>`;
7371
+ }
7372
+ };
7373
+ RoxyWesternPlanetsTable.styles = [
7374
+ baseStyles,
7375
+ import_lit31.css`
7376
+ .wrap {
7377
+ border: 1px solid var(--roxy-border, #e4e4e7);
7378
+ border-radius: var(--roxy-radius-md, 8px);
7379
+ background: var(--roxy-bg, #fff);
7380
+ overflow: auto;
7381
+ box-shadow: var(--roxy-shadow-sm);
7382
+ }
7383
+ .head {
7384
+ padding: var(--roxy-space-md, 1rem);
7385
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
7386
+ }
7387
+ .title {
7388
+ margin: 0;
7389
+ font-size: var(--roxy-text-lg, 1.125rem);
7390
+ font-weight: var(--roxy-weight-bold, 600);
6099
7391
  }
6100
- svg {
6101
- display: block;
7392
+ table {
6102
7393
  width: 100%;
6103
- max-width: 360px;
6104
- margin: 0 auto;
7394
+ border-collapse: collapse;
7395
+ font-size: var(--roxy-text-sm, 0.875rem);
7396
+ min-width: 460px;
6105
7397
  }
6106
- .line {
6107
- fill: transparent;
6108
- stroke: var(--roxy-border, #e4e4e7);
7398
+ thead {
7399
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
6109
7400
  }
6110
- .sign-text {
6111
- fill: var(--roxy-muted, #71717a);
6112
- font-size: 9px;
6113
- font-weight: 500;
6114
- font-family: var(--roxy-font-sans);
7401
+ th,
7402
+ td {
7403
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
7404
+ text-align: left;
7405
+ white-space: nowrap;
6115
7406
  }
6116
- .planet-text {
6117
- fill: var(--roxy-fg, #0a0a0a);
6118
- font-size: 11px;
6119
- font-weight: 600;
6120
- font-family: var(--roxy-font-sans);
7407
+ th {
7408
+ color: var(--roxy-muted, #71717a);
7409
+ font-weight: var(--roxy-weight-bold, 600);
7410
+ text-transform: uppercase;
7411
+ font-size: var(--roxy-text-xs, 0.75rem);
7412
+ letter-spacing: 0.04em;
6121
7413
  }
6122
- .house-num {
6123
- fill: var(--roxy-muted, #71717a);
6124
- font-size: 9px;
6125
- font-weight: 400;
6126
- font-family: var(--roxy-font-sans);
7414
+ tbody tr {
7415
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
6127
7416
  }
6128
- .lagna-marker {
6129
- fill: var(--roxy-accent-fg, #b45309);
6130
- font-size: 8px;
6131
- font-weight: 700;
6132
- font-family: var(--roxy-font-sans);
6133
- letter-spacing: 0.05em;
7417
+ tbody tr.point {
7418
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 8%, transparent);
6134
7419
  }
6135
- .lagna-bg {
6136
- fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
6137
- stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
6138
- stroke-width: 0.8;
7420
+ td.body {
7421
+ font-weight: var(--roxy-weight-bold, 600);
7422
+ color: var(--roxy-fg, #0a0a0a);
7423
+ }
7424
+ .glyph {
7425
+ margin-right: 0.4em;
7426
+ color: var(--roxy-muted, #71717a);
7427
+ }
7428
+ .retro {
7429
+ color: var(--roxy-danger, #dc2626);
7430
+ font-weight: var(--roxy-weight-bold, 600);
7431
+ }
7432
+ .num {
7433
+ font-variant-numeric: tabular-nums;
6139
7434
  }
6140
7435
  `
6141
7436
  ];
6142
7437
  __decorateClass([
6143
- (0, import_decorators24.property)({ attribute: false })
6144
- ], RoxyVedicKundli.prototype, "data", 2);
6145
- __decorateClass([
6146
- (0, import_decorators24.property)({ type: String, reflect: true, attribute: "chart-style" })
6147
- ], RoxyVedicKundli.prototype, "chartStyle", 2);
6148
- RoxyVedicKundli = __decorateClass([
6149
- (0, import_decorators24.customElement)("roxy-vedic-kundli")
6150
- ], RoxyVedicKundli);
7438
+ (0, import_decorators29.property)({ attribute: false })
7439
+ ], RoxyWesternPlanetsTable.prototype, "data", 2);
7440
+ RoxyWesternPlanetsTable = __decorateClass([
7441
+ (0, import_decorators29.customElement)("roxy-western-planets-table")
7442
+ ], RoxyWesternPlanetsTable);
6151
7443
 
6152
7444
  // packages/ui/src/components/yoga-list.ts
6153
- var import_lit27 = require("lit");
6154
- var import_decorators25 = require("lit/decorators.js");
6155
- var RoxyYogaList = class extends import_lit27.LitElement {
7445
+ var import_lit32 = require("lit");
7446
+ var import_decorators30 = require("lit/decorators.js");
7447
+ var RoxyYogaList = class extends import_lit32.LitElement {
6156
7448
  constructor() {
6157
7449
  super(...arguments);
6158
7450
  this.data = null;
@@ -6163,29 +7455,29 @@ var RoxyYogaList = class extends import_lit27.LitElement {
6163
7455
  }
6164
7456
  renderQualityChip(quality) {
6165
7457
  const cls = `quality-chip quality-${quality}`;
6166
- return import_lit27.html`<span class=${cls}>${quality}</span>`;
7458
+ return import_lit32.html`<span class=${cls}>${quality}</span>`;
6167
7459
  }
6168
7460
  renderDetailCard(yoga) {
6169
- return import_lit27.html`<div class="detail-card">
7461
+ return import_lit32.html`<div class="detail-card">
6170
7462
  <p class="detail-name">
6171
7463
  ${yoga.name}
6172
- ${yoga.quality ? this.renderQualityChip(yoga.quality) : import_lit27.nothing}
7464
+ ${yoga.quality ? this.renderQualityChip(yoga.quality) : import_lit32.nothing}
6173
7465
  </p>
6174
- ${yoga.description ? import_lit27.html`<p class="description">${yoga.description}</p>` : import_lit27.nothing}
6175
- ${yoga.result ? import_lit27.html`<details>
7466
+ ${yoga.description ? import_lit32.html`<p class="description">${yoga.description}</p>` : import_lit32.nothing}
7467
+ ${yoga.result ? import_lit32.html`<details>
6176
7468
  <summary>Effects</summary>
6177
7469
  <div class="result-body">${yoga.result}</div>
6178
- </details>` : import_lit27.nothing}
7470
+ </details>` : import_lit32.nothing}
6179
7471
  </div>`;
6180
7472
  }
6181
7473
  render() {
6182
7474
  if (!this.data)
6183
- return import_lit27.html`<div class="roxy-empty" role="status">No yoga data</div>`;
7475
+ return import_lit32.html`<div class="roxy-empty" role="status">No yoga data</div>`;
6184
7476
  const d = this.data;
6185
7477
  const lc = this.filter.toLowerCase();
6186
7478
  if ("description" in d && typeof d.description === "string") {
6187
7479
  const yoga = d;
6188
- return import_lit27.html`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
7480
+ return import_lit32.html`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
6189
7481
  }
6190
7482
  if ("yogas" in d && Array.isArray(d.yogas)) {
6191
7483
  const allYogas = d.yogas;
@@ -6194,10 +7486,10 @@ var RoxyYogaList = class extends import_lit27.LitElement {
6194
7486
  const detailYogas = allYogas;
6195
7487
  const filtered2 = lc ? detailYogas.filter((y) => y.name.toLowerCase().includes(lc)) : detailYogas;
6196
7488
  const total2 = d.total;
6197
- return import_lit27.html`<div class="wrap">
7489
+ return import_lit32.html`<div class="wrap">
6198
7490
  <div class="head">
6199
7491
  <h2 class="title">Yoga catalog</h2>
6200
- ${total2 !== void 0 ? import_lit27.html`<span class="count">${total2} total</span>` : import_lit27.nothing}
7492
+ ${total2 !== void 0 ? import_lit32.html`<span class="count">${total2} total</span>` : import_lit32.nothing}
6201
7493
  </div>
6202
7494
  <div class="search-wrap">
6203
7495
  <input
@@ -6215,17 +7507,17 @@ var RoxyYogaList = class extends import_lit27.LitElement {
6215
7507
  aria-live="polite"
6216
7508
  aria-label="Yoga results"
6217
7509
  >
6218
- ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : import_lit27.html`<p class="no-results">No yogas match your search.</p>`}
7510
+ ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : import_lit32.html`<p class="no-results">No yogas match your search.</p>`}
6219
7511
  </div>
6220
7512
  </div>`;
6221
7513
  }
6222
7514
  const catalogYogas = allYogas;
6223
7515
  const filtered = lc ? catalogYogas.filter((y) => y.name.toLowerCase().includes(lc)) : catalogYogas;
6224
7516
  const total = d.total;
6225
- return import_lit27.html`<div class="wrap">
7517
+ return import_lit32.html`<div class="wrap">
6226
7518
  <div class="head">
6227
7519
  <h2 class="title">Yoga catalog</h2>
6228
- ${total !== void 0 ? import_lit27.html`<span class="count">${total} total</span>` : import_lit27.nothing}
7520
+ ${total !== void 0 ? import_lit32.html`<span class="count">${total} total</span>` : import_lit32.nothing}
6229
7521
  </div>
6230
7522
  <div class="search-wrap">
6231
7523
  <input
@@ -6244,20 +7536,20 @@ var RoxyYogaList = class extends import_lit27.LitElement {
6244
7536
  aria-label="Yoga results"
6245
7537
  >
6246
7538
  ${filtered.length > 0 ? filtered.map(
6247
- (y) => import_lit27.html`<div class="yoga-chip">
7539
+ (y) => import_lit32.html`<div class="yoga-chip">
6248
7540
  ${y.name}
6249
7541
  <span class="yoga-id">${y.id}</span>
6250
7542
  </div>`
6251
- ) : import_lit27.html`<p class="no-results">No yogas match your search.</p>`}
7543
+ ) : import_lit32.html`<p class="no-results">No yogas match your search.</p>`}
6252
7544
  </div>
6253
7545
  </div>`;
6254
7546
  }
6255
- return import_lit27.html`<div class="roxy-empty" role="status">No yoga data</div>`;
7547
+ return import_lit32.html`<div class="roxy-empty" role="status">No yoga data</div>`;
6256
7548
  }
6257
7549
  };
6258
7550
  RoxyYogaList.styles = [
6259
7551
  baseStyles,
6260
- import_lit27.css`
7552
+ import_lit32.css`
6261
7553
  .wrap {
6262
7554
  display: grid;
6263
7555
  gap: var(--roxy-space-md, 1rem);
@@ -6408,13 +7700,13 @@ RoxyYogaList.styles = [
6408
7700
  `
6409
7701
  ];
6410
7702
  __decorateClass([
6411
- (0, import_decorators25.property)({ attribute: false })
7703
+ (0, import_decorators30.property)({ attribute: false })
6412
7704
  ], RoxyYogaList.prototype, "data", 2);
6413
7705
  __decorateClass([
6414
- (0, import_decorators25.state)()
7706
+ (0, import_decorators30.state)()
6415
7707
  ], RoxyYogaList.prototype, "filter", 2);
6416
7708
  RoxyYogaList = __decorateClass([
6417
- (0, import_decorators25.customElement)("roxy-yoga-list")
7709
+ (0, import_decorators30.customElement)("roxy-yoga-list")
6418
7710
  ], RoxyYogaList);
6419
7711
 
6420
7712
  // packages/ui/src/manifest.ts
@@ -6430,17 +7722,6 @@ var ROXY_COMPONENTS = [
6430
7722
  docsSummary: "Natal chart wheel with planet glyphs and aspect lines",
6431
7723
  topic: "Astrology"
6432
7724
  },
6433
- {
6434
- pascal: "RoxyHoroscopeCard",
6435
- tag: "roxy-horoscope-card",
6436
- slug: "horoscope-card",
6437
- heading: "Daily horoscope",
6438
- description: "Daily, weekly, or monthly horoscope card for /astrology/horoscope/...",
6439
- docsLabel: "Western",
6440
- endpointLabel: "GET /astrology/horoscope/{sign}/{daily,weekly,monthly}",
6441
- docsSummary: "Daily, weekly, or monthly horoscope card",
6442
- topic: "Astrology"
6443
- },
6444
7725
  {
6445
7726
  pascal: "RoxySynastryChart",
6446
7727
  tag: "roxy-synastry-chart",
@@ -6453,14 +7734,25 @@ var ROXY_COMPONENTS = [
6453
7734
  topic: "Astrology"
6454
7735
  },
6455
7736
  {
6456
- pascal: "RoxyCompatibilityCard",
6457
- tag: "roxy-compatibility-card",
6458
- slug: "compatibility-card",
6459
- heading: "Compatibility score",
6460
- description: "Cross-domain compatibility score card",
6461
- docsLabel: "Cross",
6462
- endpointLabel: "POST /astrology/compatibility-score, /numerology/compatibility, /biorhythm/compatibility",
6463
- docsSummary: "Score card with category breakdown",
7737
+ pascal: "RoxyWesternPlanetsTable",
7738
+ tag: "roxy-western-planets-table",
7739
+ slug: "western-planets-table",
7740
+ heading: "Western planets",
7741
+ description: "Western planetary positions table with sign, degree, house, and motion plus the chart angles",
7742
+ docsLabel: "Western",
7743
+ endpointLabel: "POST /astrology/natal-chart",
7744
+ docsSummary: "Sign, degree, house, motion columns plus ASC, MC, PoF, Vertex",
7745
+ topic: "Astrology"
7746
+ },
7747
+ {
7748
+ pascal: "RoxyTransitsTable",
7749
+ tag: "roxy-transits-table",
7750
+ slug: "transits-table",
7751
+ heading: "Transits",
7752
+ description: "Live planet positions plus aspects to a natal chart",
7753
+ docsLabel: "Western",
7754
+ endpointLabel: "POST /astrology/transits",
7755
+ docsSummary: "Transit planet positions plus optional aspects to a natal chart",
6464
7756
  topic: "Astrology"
6465
7757
  },
6466
7758
  {
@@ -6474,59 +7766,70 @@ var ROXY_COMPONENTS = [
6474
7766
  docsSummary: "Moon phase card and calendar",
6475
7767
  topic: "Astrology"
6476
7768
  },
7769
+ {
7770
+ pascal: "RoxyHoroscopeCard",
7771
+ tag: "roxy-horoscope-card",
7772
+ slug: "horoscope-card",
7773
+ heading: "Daily horoscope",
7774
+ description: "Daily, weekly, or monthly horoscope card for /astrology/horoscope/...",
7775
+ docsLabel: "Western",
7776
+ endpointLabel: "GET /astrology/horoscope/{sign}/{daily,weekly,monthly}",
7777
+ docsSummary: "Daily, weekly, or monthly horoscope card",
7778
+ topic: "Astrology"
7779
+ },
7780
+ {
7781
+ pascal: "RoxyCompatibilityCard",
7782
+ tag: "roxy-compatibility-card",
7783
+ slug: "compatibility-card",
7784
+ heading: "Compatibility score",
7785
+ description: "Cross-domain compatibility score card",
7786
+ docsLabel: "Cross",
7787
+ endpointLabel: "POST /astrology/compatibility-score, /numerology/compatibility, /biorhythm/compatibility",
7788
+ docsSummary: "Score card with category breakdown",
7789
+ topic: "Astrology"
7790
+ },
6477
7791
  {
6478
7792
  pascal: "RoxyVedicKundli",
6479
7793
  tag: "roxy-vedic-kundli",
6480
7794
  slug: "vedic-kundli",
6481
7795
  heading: "Vedic kundli",
6482
- description: "South or North Indian Vedic kundli for /vedic-astrology/birth-chart",
7796
+ description: "South, North, or East Indian Vedic kundli for /vedic-astrology/birth-chart with per-planet degree and nakshatra detail",
6483
7797
  docsLabel: "Vedic",
6484
7798
  endpointLabel: "POST /vedic-astrology/birth-chart",
6485
- docsSummary: "South or North Indian kundli",
6486
- topic: "Vedic"
6487
- },
6488
- {
6489
- pascal: "RoxyPanchangTable",
6490
- tag: "roxy-panchang-table",
6491
- slug: "panchang-table",
6492
- heading: "Panchang",
6493
- description: "Panchang muhurta table with auspicious and inauspicious periods",
6494
- docsLabel: "Vedic",
6495
- endpointLabel: "POST /vedic-astrology/panchang/{basic,detailed}",
6496
- docsSummary: "15+ muhurtas in detailed mode",
7799
+ docsSummary: "South, North, or East Indian kundli with degree detail",
6497
7800
  topic: "Vedic"
6498
7801
  },
6499
7802
  {
6500
- pascal: "RoxyDashaTimeline",
6501
- tag: "roxy-dasha-timeline",
6502
- slug: "dasha-timeline",
6503
- heading: "Vimshottari dasha",
6504
- description: "Vimshottari dasha timeline with active mahadasha highlighted",
7803
+ pascal: "RoxyDivisionalChart",
7804
+ tag: "roxy-divisional-chart",
7805
+ slug: "divisional-chart",
7806
+ heading: "Divisional chart",
7807
+ description: "D2 to D60 varga chart wheel with Vargottama markers",
6505
7808
  docsLabel: "Vedic",
6506
- endpointLabel: "POST /vedic-astrology/dasha/{current,major,sub/...}",
6507
- docsSummary: "Vimshottari mahadasha + antardasha + pratyantardasha",
7809
+ endpointLabel: "POST /vedic-astrology/divisional-chart",
7810
+ docsSummary: "Generic divisional varga wheel from D2 Hora to D60 Shashtiamsa",
6508
7811
  topic: "Vedic"
6509
7812
  },
6510
7813
  {
6511
- pascal: "RoxyDoshaCard",
6512
- tag: "roxy-dosha-card",
6513
- slug: "dosha-card",
6514
- heading: "Manglik dosha",
6515
- description: "Manglik, Kaal Sarp, or Sade Sati presence card",
6516
- docsLabel: "Vedic",
6517
- endpointLabel: "POST /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}",
6518
- docsSummary: "Presence, severity, remedies, scoped effects",
7814
+ pascal: "RoxyKpChart",
7815
+ tag: "roxy-kp-chart",
7816
+ slug: "kp-chart",
7817
+ heading: "KP chart",
7818
+ description: "Full KP chart with Ascendant, Placidus cusps, and planets in tabbed stellar-hierarchy tables",
7819
+ docsLabel: "Vedic (KP)",
7820
+ endpointLabel: "POST /vedic-astrology/kp/chart",
7821
+ docsSummary: "Ascendant, cusps, and planets with KP stellar hierarchy",
6519
7822
  topic: "Vedic"
6520
7823
  },
6521
7824
  {
6522
- pascal: "RoxyGunaMilan",
6523
- tag: "roxy-guna-milan",
6524
- slug: "guna-milan",
6525
- heading: "Guna milan",
6526
- description: "36-point Ashtakoota matrimonial compatibility breakdown",
7825
+ pascal: "RoxyVedicPlanetsTable",
7826
+ tag: "roxy-vedic-planets-table",
7827
+ slug: "vedic-planets-table",
7828
+ heading: "Vedic planets",
7829
+ description: "Vedic planetary positions table with degree, nakshatra, pada, nakshatra lord, bhava, and avastha",
6527
7830
  docsLabel: "Vedic",
6528
- endpointLabel: "POST /vedic-astrology/compatibility",
6529
- docsSummary: "36-point Ashtakoota with eight sub-scores",
7831
+ endpointLabel: "POST /vedic-astrology/birth-chart",
7832
+ docsSummary: "Degree, nakshatra, pada, lord, bhava, avastha columns",
6530
7833
  topic: "Vedic"
6531
7834
  },
6532
7835
  {
@@ -6541,25 +7844,14 @@ var ROXY_COMPONENTS = [
6541
7844
  topic: "Vedic"
6542
7845
  },
6543
7846
  {
6544
- pascal: "RoxyTransitsTable",
6545
- tag: "roxy-transits-table",
6546
- slug: "transits-table",
6547
- heading: "Transits",
6548
- description: "Live planet positions plus aspects to a natal chart",
6549
- docsLabel: "Western",
6550
- endpointLabel: "POST /astrology/transits",
6551
- docsSummary: "Transit planet positions plus optional aspects to a natal chart",
6552
- topic: "Astrology"
6553
- },
6554
- {
6555
- pascal: "RoxyDivisionalChart",
6556
- tag: "roxy-divisional-chart",
6557
- slug: "divisional-chart",
6558
- heading: "Divisional chart",
6559
- description: "D2 to D60 varga chart wheel with Vargottama markers",
6560
- docsLabel: "Vedic",
6561
- endpointLabel: "POST /vedic-astrology/divisional-chart",
6562
- docsSummary: "Generic divisional varga wheel from D2 Hora to D60 Shashtiamsa",
7847
+ pascal: "RoxyKpRulingPlanets",
7848
+ tag: "roxy-kp-ruling-planets",
7849
+ slug: "kp-ruling-planets",
7850
+ heading: "KP ruling planets",
7851
+ description: "KP ruling planets with day lord, Moon and Lagna stellar hierarchies, and house significators",
7852
+ docsLabel: "Vedic (KP)",
7853
+ endpointLabel: "POST /vedic-astrology/kp/ruling-planets",
7854
+ docsSummary: "Day lord, Moon/Lagna hierarchies, ruling planets, significators",
6563
7855
  topic: "Vedic"
6564
7856
  },
6565
7857
  {
@@ -6585,14 +7877,36 @@ var ROXY_COMPONENTS = [
6585
7877
  topic: "Vedic"
6586
7878
  },
6587
7879
  {
6588
- pascal: "RoxyYogaList",
6589
- tag: "roxy-yoga-list",
6590
- slug: "yoga-list",
6591
- heading: "Yoga catalog",
6592
- description: "Yoga reference cards from the catalog with optional detail mode",
7880
+ pascal: "RoxyDashaTimeline",
7881
+ tag: "roxy-dasha-timeline",
7882
+ slug: "dasha-timeline",
7883
+ heading: "Vimshottari dasha",
7884
+ description: "Vimshottari dasha timeline with active mahadasha highlighted",
6593
7885
  docsLabel: "Vedic",
6594
- endpointLabel: "GET /vedic-astrology/yoga, /yoga/{id}",
6595
- docsSummary: "Filterable yoga cards from the 300 plus yoga catalog",
7886
+ endpointLabel: "POST /vedic-astrology/dasha/{current,major,sub/...}",
7887
+ docsSummary: "Vimshottari mahadasha + antardasha + pratyantardasha",
7888
+ topic: "Vedic"
7889
+ },
7890
+ {
7891
+ pascal: "RoxyGunaMilan",
7892
+ tag: "roxy-guna-milan",
7893
+ slug: "guna-milan",
7894
+ heading: "Guna milan",
7895
+ description: "36-point Ashtakoota matrimonial compatibility breakdown",
7896
+ docsLabel: "Vedic",
7897
+ endpointLabel: "POST /vedic-astrology/compatibility",
7898
+ docsSummary: "36-point Ashtakoota with eight sub-scores",
7899
+ topic: "Vedic"
7900
+ },
7901
+ {
7902
+ pascal: "RoxyPanchangTable",
7903
+ tag: "roxy-panchang-table",
7904
+ slug: "panchang-table",
7905
+ heading: "Panchang",
7906
+ description: "Panchang muhurta table with auspicious and inauspicious periods",
7907
+ docsLabel: "Vedic",
7908
+ endpointLabel: "POST /vedic-astrology/panchang/{basic,detailed}",
7909
+ docsSummary: "15+ muhurtas in detailed mode",
6596
7910
  topic: "Vedic"
6597
7911
  },
6598
7912
  {
@@ -6606,6 +7920,39 @@ var ROXY_COMPONENTS = [
6606
7920
  docsSummary: "Day and night Choghadiya muhurta tiles colored by effect",
6607
7921
  topic: "Vedic"
6608
7922
  },
7923
+ {
7924
+ pascal: "RoxyYogaList",
7925
+ tag: "roxy-yoga-list",
7926
+ slug: "yoga-list",
7927
+ heading: "Yoga catalog",
7928
+ description: "Yoga reference cards from the catalog with optional detail mode",
7929
+ docsLabel: "Vedic",
7930
+ endpointLabel: "GET /vedic-astrology/yoga, /yoga/{id}",
7931
+ docsSummary: "Filterable yoga cards from the 300 plus yoga catalog",
7932
+ topic: "Vedic"
7933
+ },
7934
+ {
7935
+ pascal: "RoxyNakshatraCard",
7936
+ tag: "roxy-nakshatra-card",
7937
+ slug: "nakshatra-card",
7938
+ heading: "Nakshatra",
7939
+ description: "Nakshatra reference card with lord, deity, symbol, characteristics, and remedies",
7940
+ docsLabel: "Vedic",
7941
+ endpointLabel: "GET /vedic-astrology/nakshatras/{id}",
7942
+ docsSummary: "Lord, deity, symbol, characteristics, remedies",
7943
+ topic: "Vedic"
7944
+ },
7945
+ {
7946
+ pascal: "RoxyDoshaCard",
7947
+ tag: "roxy-dosha-card",
7948
+ slug: "dosha-card",
7949
+ heading: "Manglik dosha",
7950
+ description: "Manglik, Kaal Sarp, or Sade Sati presence card",
7951
+ docsLabel: "Vedic",
7952
+ endpointLabel: "POST /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}",
7953
+ docsSummary: "Presence, severity, remedies, scoped effects",
7954
+ topic: "Vedic"
7955
+ },
6609
7956
  {
6610
7957
  pascal: "RoxyNumerologyCard",
6611
7958
  tag: "roxy-numerology-card",
@@ -6700,7 +8047,7 @@ var ROXY_COMPONENTS = [
6700
8047
  ];
6701
8048
 
6702
8049
  // packages/ui/src/version.ts
6703
- var ROXY_UI_VERSION = "0.2.2";
8050
+ var ROXY_UI_VERSION = "0.3.0";
6704
8051
 
6705
8052
  // packages/ui/src/index.ts
6706
8053
  var ROXY_UI_COMPONENTS = ROXY_COMPONENTS.map((c) => c.slug);