@roxyapi/ui 0.2.3 → 0.3.1

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 +216 -38
  2. package/README.md +200 -24
  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 +5 -0
  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.js CHANGED
@@ -99,6 +99,15 @@ var SIGNS_ORDER = [
99
99
  var RASHI_KEYS = SIGNS_ORDER.map(
100
100
  (s) => s.toLowerCase()
101
101
  );
102
+ var ASPECT_SYMBOL = {
103
+ conjunction: "\u260C",
104
+ opposition: "\u260D",
105
+ trine: "\u25B3",
106
+ square: "\u25A1",
107
+ sextile: "\u2731",
108
+ quincunx: "\u22BB",
109
+ semisextile: "\u22BC"
110
+ };
102
111
  var TRIGRAM_GLYPH = {
103
112
  heaven: "\u2630",
104
113
  lake: "\u2631",
@@ -1572,9 +1581,88 @@ import { customElement as customElement7, property as property7 } from "lit/deco
1572
1581
 
1573
1582
  // packages/ui/src/utils/kundli-render.ts
1574
1583
  import { nothing as nothing7, svg as svg2 } from "lit";
1584
+
1585
+ // packages/ui/src/utils/degree.ts
1586
+ function normalizeLongitude(lon) {
1587
+ const wrapped = lon % 360;
1588
+ return wrapped < 0 ? wrapped + 360 : wrapped;
1589
+ }
1590
+ function longitudeToSignPosition(longitude) {
1591
+ const lon = normalizeLongitude(longitude);
1592
+ const signIndex = Math.floor(lon / 30) % 12;
1593
+ const within = lon % 30;
1594
+ const degree = Math.floor(within);
1595
+ const minuteFloat = (within - degree) * 60;
1596
+ const minute = Math.floor(minuteFloat);
1597
+ const second = Math.round((minuteFloat - minute) * 60);
1598
+ return {
1599
+ sign: SIGNS_ORDER[signIndex] ?? "Aries",
1600
+ signIndex,
1601
+ degree,
1602
+ minute,
1603
+ second
1604
+ };
1605
+ }
1606
+ function formatSignPosition(longitude) {
1607
+ const { sign, degree, minute } = longitudeToSignPosition(longitude);
1608
+ return `${degree}\xB0 ${sign} ${String(minute).padStart(2, "0")}'`;
1609
+ }
1610
+ function oppositePoint(longitude) {
1611
+ return normalizeLongitude(longitude + 180);
1612
+ }
1613
+ function arcMidpoint(start, end) {
1614
+ const s = normalizeLongitude(start);
1615
+ let span = normalizeLongitude(end) - s;
1616
+ if (span < 0) span += 360;
1617
+ return normalizeLongitude(s + span / 2);
1618
+ }
1619
+ function polarToCartesian(cx, cy, radius, angleDeg) {
1620
+ const angleRad = angleDeg * Math.PI / 180;
1621
+ return {
1622
+ x: cx + radius * Math.cos(angleRad),
1623
+ y: cy + radius * Math.sin(angleRad)
1624
+ };
1625
+ }
1626
+
1627
+ // packages/ui/src/utils/kundli-render.ts
1575
1628
  var RASHI_TO_SIGN = Object.fromEntries(
1576
1629
  SIGNS_ORDER.map((s) => [s.toLowerCase(), s])
1577
1630
  );
1631
+ var RETRO_MARK = "\u02B3";
1632
+ function grahaLabel(p) {
1633
+ const abbr = PLANET_ABBR[capitalize(p.graha)] ?? p.graha.slice(0, 2);
1634
+ const retro = p.isRetrograde ? RETRO_MARK : "";
1635
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
1636
+ return `${abbr}${retro}`;
1637
+ }
1638
+ const { degree } = longitudeToSignPosition(p.longitude);
1639
+ return `${abbr} ${degree}\xB0${retro}`;
1640
+ }
1641
+ function grahaTitle(p) {
1642
+ const parts = [capitalize(p.graha)];
1643
+ if (typeof p.longitude === "number" && Number.isFinite(p.longitude)) {
1644
+ const sp = longitudeToSignPosition(p.longitude);
1645
+ parts.push(
1646
+ `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}' ${sp.sign}`
1647
+ );
1648
+ }
1649
+ if (p.nakshatra?.name) {
1650
+ const pada = p.nakshatra.pada ? ` pada ${p.nakshatra.pada}` : "";
1651
+ parts.push(`${p.nakshatra.name}${pada}`);
1652
+ }
1653
+ if (p.awastha) parts.push(p.awastha);
1654
+ if (p.isRetrograde) parts.push("retrograde");
1655
+ return parts.join(" \xB7 ");
1656
+ }
1657
+ function renderPlanetStack(planets, cx, baseY, lineHeight) {
1658
+ const startY = baseY - (planets.length - 1) * lineHeight / 2;
1659
+ return planets.map((p, j) => {
1660
+ const yPos = startY + j * lineHeight;
1661
+ return svg2`<text class="planet-text" x=${cx} y=${yPos} text-anchor="middle" dominant-baseline="central">${grahaLabel(
1662
+ p
1663
+ )}<title>${grahaTitle(p)}</title></text>`;
1664
+ });
1665
+ }
1578
1666
  var SOUTH_HOUSE_CENTERS = {
1579
1667
  1: { x: 150, y: 58 },
1580
1668
  2: { x: 205, y: 52 },
@@ -1617,12 +1705,52 @@ var NORTH_HOUSE_CENTERS = {
1617
1705
  11: { x: 200, y: 80 },
1618
1706
  12: { x: 200, y: 220 }
1619
1707
  };
1708
+ var EAST_HOUSE_CENTERS = {
1709
+ 1: { x: 150, y: 80 },
1710
+ // inner diamond, top
1711
+ 2: { x: 220, y: 33 },
1712
+ // top-right corner, upper triangle
1713
+ 3: { x: 267, y: 80 },
1714
+ // top-right corner, right triangle
1715
+ 4: { x: 220, y: 150 },
1716
+ // inner diamond, right
1717
+ 5: { x: 267, y: 220 },
1718
+ // bottom-right corner, right triangle
1719
+ 6: { x: 220, y: 267 },
1720
+ // bottom-right corner, lower triangle
1721
+ 7: { x: 150, y: 220 },
1722
+ // inner diamond, bottom
1723
+ 8: { x: 80, y: 267 },
1724
+ // bottom-left corner, lower triangle
1725
+ 9: { x: 33, y: 220 },
1726
+ // bottom-left corner, left triangle
1727
+ 10: { x: 80, y: 150 },
1728
+ // inner diamond, left
1729
+ 11: { x: 33, y: 80 },
1730
+ // top-left corner, left triangle
1731
+ 12: { x: 80, y: 33 }
1732
+ // top-left corner, upper triangle
1733
+ };
1734
+ var EAST_SIGN_POSITIONS = {
1735
+ 1: { x: 150, y: 55 },
1736
+ 2: { x: 235, y: 24 },
1737
+ 3: { x: 276, y: 62 },
1738
+ 4: { x: 242, y: 150 },
1739
+ 5: { x: 276, y: 238 },
1740
+ 6: { x: 235, y: 276 },
1741
+ 7: { x: 150, y: 245 },
1742
+ 8: { x: 65, y: 276 },
1743
+ 9: { x: 24, y: 238 },
1744
+ 10: { x: 58, y: 150 },
1745
+ 11: { x: 24, y: 62 },
1746
+ 12: { x: 65, y: 24 }
1747
+ };
1620
1748
  function renderSouthHouseGroup(h) {
1621
1749
  const center = SOUTH_HOUSE_CENTERS[h.number];
1622
1750
  const signPos = SOUTH_SIGN_POSITIONS[h.number];
1623
1751
  if (!center || !signPos) return nothing7;
1624
1752
  const signAbbr = SIGN_ABBR[h.sign] ?? "";
1625
- const planets = h.planets;
1753
+ const baseY = h.isLagna ? center.y + 8 : center.y;
1626
1754
  return svg2`
1627
1755
  <g>
1628
1756
  ${h.isLagna ? svg2`<rect
@@ -1632,14 +1760,7 @@ function renderSouthHouseGroup(h) {
1632
1760
  />` : nothing7}
1633
1761
  ${signAbbr ? svg2`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing7}
1634
1762
  ${h.isLagna ? svg2`<text class="lagna-marker" x=${center.x} y=${center.y - 18} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : nothing7}
1635
- ${planets.map((planet, j) => {
1636
- const abbr = PLANET_ABBR[capitalize(planet)] ?? planet.slice(0, 2);
1637
- const lineHeight = 13;
1638
- const baseY = h.isLagna ? center.y + 8 : center.y;
1639
- const startY = baseY - (planets.length - 1) * lineHeight / 2;
1640
- const yPos = startY + j * lineHeight;
1641
- return svg2`<text class="planet-text" x=${center.x} y=${yPos} text-anchor="middle" dominant-baseline="central">${abbr}</text>`;
1642
- })}
1763
+ ${renderPlanetStack(h.planets, center.x, baseY, 13)}
1643
1764
  </g>
1644
1765
  `;
1645
1766
  }
@@ -1658,19 +1779,12 @@ function renderNorthHouseGroup(h) {
1658
1779
  const center = NORTH_HOUSE_CENTERS[h.number];
1659
1780
  if (!center) return nothing7;
1660
1781
  const signAbbr = SIGN_ABBR[h.sign] ?? "";
1661
- const planets = h.planets;
1662
1782
  return svg2`
1663
1783
  <g>
1664
1784
  ${h.isLagna ? svg2`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="22" />` : nothing7}
1665
1785
  ${signAbbr ? svg2`<text class="sign-text" x=${center.x} y=${center.y - 10} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing7}
1666
1786
  <text class="house-num" x=${center.x} y=${center.y + 2} text-anchor="middle" dominant-baseline="central">${h.number}</text>
1667
- ${planets.map((planet, j) => {
1668
- const abbr = PLANET_ABBR[capitalize(planet)] ?? planet.slice(0, 2);
1669
- const lineHeight = 11;
1670
- const startY = center.y + 14 - (planets.length - 1) * lineHeight / 2;
1671
- const yPos = startY + j * lineHeight;
1672
- return svg2`<text class="planet-text" x=${center.x} y=${yPos} text-anchor="middle" dominant-baseline="central">${abbr}</text>`;
1673
- })}
1787
+ ${renderPlanetStack(h.planets, center.x, center.y + 14, 11)}
1674
1788
  </g>
1675
1789
  `;
1676
1790
  }
@@ -1688,6 +1802,58 @@ function renderSouthFrame() {
1688
1802
  <line class="line" x1="10" y1="150" x2="80" y2="80" stroke-width="1" />
1689
1803
  `;
1690
1804
  }
1805
+ function renderEastFrame() {
1806
+ return svg2`
1807
+ <rect class="line" x="10" y="10" width="280" height="280" stroke-width="1.5" fill="none" />
1808
+ <line class="line" x1="10" y1="10" x2="290" y2="290" stroke-width="1" />
1809
+ <line class="line" x1="290" y1="10" x2="10" y2="290" stroke-width="1" />
1810
+ <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1" fill="none" />
1811
+ `;
1812
+ }
1813
+ function renderEastHouseGroup(h) {
1814
+ const center = EAST_HOUSE_CENTERS[h.number];
1815
+ const signPos = EAST_SIGN_POSITIONS[h.number];
1816
+ if (!center || !signPos) return nothing7;
1817
+ const signAbbr = SIGN_ABBR[h.sign] ?? "";
1818
+ return svg2`
1819
+ <g>
1820
+ ${h.isLagna ? svg2`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="20" />` : nothing7}
1821
+ ${signAbbr ? svg2`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing7}
1822
+ ${h.isLagna ? svg2`<text class="lagna-marker" x=${center.x} y=${center.y - 14} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : nothing7}
1823
+ ${renderPlanetStack(h.planets, center.x, center.y + 2, 11)}
1824
+ </g>
1825
+ `;
1826
+ }
1827
+ function buildHousesFromMeta(meta) {
1828
+ const byRashi = /* @__PURE__ */ new Map();
1829
+ let lagnaKey = "";
1830
+ for (const [name, pos] of Object.entries(meta)) {
1831
+ const rashiKey = (pos?.rashi ?? "").toLowerCase();
1832
+ if (name === "Lagna" || pos?.graha === "Lagna") {
1833
+ lagnaKey = rashiKey;
1834
+ continue;
1835
+ }
1836
+ if (!rashiKey) continue;
1837
+ const list = byRashi.get(rashiKey) ?? [];
1838
+ list.push({
1839
+ graha: pos.graha ?? name,
1840
+ longitude: pos.longitude,
1841
+ nakshatra: pos.nakshatra,
1842
+ isRetrograde: pos.isRetrograde,
1843
+ awastha: pos.awastha
1844
+ });
1845
+ byRashi.set(rashiKey, list);
1846
+ }
1847
+ return SIGNS_ORDER.map((sign, i) => {
1848
+ const key = sign.toLowerCase();
1849
+ return {
1850
+ number: i + 1,
1851
+ sign,
1852
+ planets: byRashi.get(key) ?? [],
1853
+ isLagna: lagnaKey === key
1854
+ };
1855
+ });
1856
+ }
1691
1857
 
1692
1858
  // packages/ui/src/components/divisional-chart.ts
1693
1859
  var RoxyDivisionalChart = class extends LitElement7 {
@@ -1697,31 +1863,17 @@ var RoxyDivisionalChart = class extends LitElement7 {
1697
1863
  this.chartStyle = "south";
1698
1864
  }
1699
1865
  buildHouses() {
1700
- if (!this.data) return [];
1701
- const chart = this.data.chart;
1702
- const meta = this.data.chart.meta ?? {};
1703
- const lagnaSign = meta.Lagna?.rashi ?? "";
1704
- const houses = [];
1705
- for (let i = 0; i < 12; i++) {
1706
- const key = RASHI_KEYS[i];
1707
- const bucket = chart[key];
1708
- const planets = (bucket?.signs ?? []).map((p) => p.graha).filter(Boolean);
1709
- const sign = RASHI_TO_SIGN[key] ?? "";
1710
- houses.push({
1711
- number: i + 1,
1712
- sign,
1713
- planets,
1714
- isLagna: lagnaSign ? lagnaSign.toLowerCase() === sign.toLowerCase() : false
1715
- });
1716
- }
1717
- return houses;
1866
+ if (!this.data?.chart?.meta) return [];
1867
+ return buildHousesFromMeta(this.data.chart.meta);
1718
1868
  }
1719
1869
  render() {
1720
1870
  if (!this.data)
1721
1871
  return html7`<div class="roxy-empty" role="status">No divisional chart data</div>`;
1722
1872
  const { division, vargottama } = this.data;
1723
1873
  const houses = this.buildHouses();
1724
- const isNorth = this.chartStyle === "north";
1874
+ const style = this.chartStyle;
1875
+ const frame = style === "north" ? renderNorthFrame() : style === "east" ? renderEastFrame() : renderSouthFrame();
1876
+ const houseGroup = style === "north" ? renderNorthHouseGroup : style === "east" ? renderEastHouseGroup : renderSouthHouseGroup;
1725
1877
  return html7`<div class="wrap">
1726
1878
  <div class="header">
1727
1879
  <h2 class="title">
@@ -1737,8 +1889,8 @@ var RoxyDivisionalChart = class extends LitElement7 {
1737
1889
  aria-label="D${division.number} ${division.name} divisional chart with twelve sign houses"
1738
1890
  >
1739
1891
  <title>D${division.number} ${division.name}</title>
1740
- ${isNorth ? renderNorthFrame() : renderSouthFrame()}
1741
- ${isNorth ? houses.map((h) => renderNorthHouseGroup(h)) : houses.map((h) => renderSouthHouseGroup(h))}
1892
+ ${frame}
1893
+ ${houses.map((h) => houseGroup(h))}
1742
1894
  </svg>
1743
1895
 
1744
1896
  ${vargottama && vargottama.length > 0 ? html7`<div class="vargottama-row" role="list" aria-label="Vargottama planets">
@@ -3111,26 +3263,300 @@ RoxyHoroscopeCard = __decorateClass([
3111
3263
  customElement12("roxy-horoscope-card")
3112
3264
  ], RoxyHoroscopeCard);
3113
3265
 
3114
- // packages/ui/src/components/kp-planets-table.ts
3266
+ // packages/ui/src/components/kp-chart.ts
3115
3267
  import { css as css14, html as html13, LitElement as LitElement13, nothing as nothing14 } from "lit";
3116
- import { customElement as customElement13, property as property13 } from "lit/decorators.js";
3117
- var RoxyKpPlanetsTable = class extends LitElement13 {
3268
+ import { customElement as customElement13, property as property13, state as state3 } from "lit/decorators.js";
3269
+ var RoxyKpChart = class extends LitElement13 {
3270
+ constructor() {
3271
+ super(...arguments);
3272
+ this.data = null;
3273
+ this.activeTab = "planets";
3274
+ }
3275
+ /** Merge the 7 planets and the two nodes into one ordered body list. */
3276
+ bodies() {
3277
+ const d = this.data;
3278
+ if (!d) return [];
3279
+ const rows = (d.planets ?? []).map((p) => ({
3280
+ name: p.planet,
3281
+ sign: p.sign,
3282
+ house: p.house,
3283
+ nakshatra: p.nakshatra,
3284
+ starLord: p.starLord,
3285
+ subLord: p.subLord,
3286
+ subSubLord: p.subSubLord,
3287
+ kpNumber: p.kpNumber,
3288
+ retrograde: p.retrograde
3289
+ }));
3290
+ const nodes = d.nodes;
3291
+ for (const [name, node] of [
3292
+ ["Rahu", nodes?.rahu],
3293
+ ["Ketu", nodes?.ketu]
3294
+ ]) {
3295
+ if (node) {
3296
+ rows.push({
3297
+ name,
3298
+ sign: node.sign,
3299
+ house: node.house,
3300
+ nakshatra: node.nakshatra,
3301
+ starLord: node.starLord,
3302
+ subLord: node.subLord,
3303
+ subSubLord: node.subSubLord,
3304
+ retrograde: true
3305
+ });
3306
+ }
3307
+ }
3308
+ return rows;
3309
+ }
3310
+ onTabKeyDown(e) {
3311
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
3312
+ e.preventDefault();
3313
+ this.activeTab = this.activeTab === "planets" ? "cusps" : "planets";
3314
+ const next = this.activeTab;
3315
+ requestAnimationFrame(() => {
3316
+ this.shadowRoot?.querySelector(`#tab-${next}`)?.focus();
3317
+ });
3318
+ }
3319
+ render() {
3320
+ if (!this.data)
3321
+ return html13`<div class="roxy-empty" role="status">No KP chart data</div>`;
3322
+ const d = this.data;
3323
+ const asc = d.ascendant;
3324
+ return html13`<div class="wrap" aria-label="KP chart" tabindex="0">
3325
+ <header class="head">
3326
+ <h2 class="title">KP chart</h2>
3327
+ ${asc ? html13`<div class="asc">
3328
+ Ascendant: <strong>${asc.sign ?? ""}</strong>
3329
+ ${asc.nakshatra ? html13`· ${asc.nakshatra}` : nothing14}
3330
+ ${asc.subLord ? html13`· sub lord ${asc.subLord}` : nothing14}
3331
+ ${typeof asc.kpNumber === "number" ? html13`· KP ${asc.kpNumber}` : nothing14}
3332
+ </div>` : nothing14}
3333
+ ${typeof d.meta?.ayanamsa === "number" ? html13`<div class="ayan">
3334
+ ${d.meta.ayanamsaType ?? "Ayanamsa"}: ${formatNumber(d.meta.ayanamsa, 4)}°
3335
+ ${d.meta.houseSystem ? html13`· ${d.meta.houseSystem} houses` : nothing14}
3336
+ </div>` : nothing14}
3337
+ </header>
3338
+
3339
+ <div
3340
+ class="tablist"
3341
+ role="tablist"
3342
+ aria-label="KP chart views"
3343
+ @keydown=${this.onTabKeyDown}
3344
+ >
3345
+ ${["planets", "cusps"].map(
3346
+ (t) => html13`<button
3347
+ class="tab"
3348
+ role="tab"
3349
+ id="tab-${t}"
3350
+ aria-selected=${this.activeTab === t ? "true" : "false"}
3351
+ aria-controls="panel-${t}"
3352
+ tabindex=${this.activeTab === t ? "0" : "-1"}
3353
+ @click=${() => {
3354
+ this.activeTab = t;
3355
+ }}
3356
+ >
3357
+ ${t === "planets" ? "Planets" : "Cusps"}
3358
+ </button>`
3359
+ )}
3360
+ </div>
3361
+
3362
+ <div id="panel-${this.activeTab}" role="tabpanel" aria-labelledby="tab-${this.activeTab}">
3363
+ ${this.activeTab === "planets" ? this.renderPlanets() : this.renderCusps()}
3364
+ </div>
3365
+ </div>`;
3366
+ }
3367
+ renderPlanets() {
3368
+ const bodies = this.bodies();
3369
+ if (!bodies.length)
3370
+ return html13`<p class="roxy-empty" role="status">No planets</p>`;
3371
+ return html13`<table role="table" aria-label="KP planets and nodes">
3372
+ <thead>
3373
+ <tr>
3374
+ <th scope="col">Body</th>
3375
+ <th scope="col">Sign</th>
3376
+ <th scope="col">House</th>
3377
+ <th scope="col">Nakshatra</th>
3378
+ <th scope="col">Star lord</th>
3379
+ <th scope="col">Sub lord</th>
3380
+ <th scope="col">Sub sub lord</th>
3381
+ <th scope="col">KP no.</th>
3382
+ </tr>
3383
+ </thead>
3384
+ <tbody>
3385
+ ${bodies.map(
3386
+ (b) => html13`<tr>
3387
+ <td class="body">
3388
+ ${b.name}${b.retrograde ? html13`<span class="retro">R</span>` : nothing14}
3389
+ </td>
3390
+ <td>${b.sign ?? ""}</td>
3391
+ <td class="num">${typeof b.house === "number" ? b.house : ""}</td>
3392
+ <td>${b.nakshatra ?? ""}</td>
3393
+ <td>${b.starLord ?? ""}</td>
3394
+ <td>${b.subLord ?? ""}</td>
3395
+ <td>${b.subSubLord ?? ""}</td>
3396
+ <td class="num">${typeof b.kpNumber === "number" ? b.kpNumber : ""}</td>
3397
+ </tr>`
3398
+ )}
3399
+ </tbody>
3400
+ </table>`;
3401
+ }
3402
+ renderCusps() {
3403
+ const cusps = this.data?.cusps ?? [];
3404
+ if (!cusps.length)
3405
+ return html13`<p class="roxy-empty" role="status">No cusps</p>`;
3406
+ return html13`<table role="table" aria-label="KP Placidus cusps">
3407
+ <thead>
3408
+ <tr>
3409
+ <th scope="col">House</th>
3410
+ <th scope="col">Sign</th>
3411
+ <th scope="col">Sign lord</th>
3412
+ <th scope="col">Nakshatra</th>
3413
+ <th scope="col">Star lord</th>
3414
+ <th scope="col">Sub lord</th>
3415
+ <th scope="col">Sub sub lord</th>
3416
+ <th scope="col">KP no.</th>
3417
+ </tr>
3418
+ </thead>
3419
+ <tbody>
3420
+ ${cusps.map(
3421
+ (c) => html13`<tr>
3422
+ <td class="body num">${c.house}</td>
3423
+ <td>${c.sign ?? ""}</td>
3424
+ <td>${c.signLord ?? ""}</td>
3425
+ <td>${c.nakshatra ?? ""}</td>
3426
+ <td>${c.starLord ?? ""}</td>
3427
+ <td>${c.subLord ?? ""}</td>
3428
+ <td>${c.subSubLord ?? ""}</td>
3429
+ <td class="num">${typeof c.kpNumber === "number" ? c.kpNumber : ""}</td>
3430
+ </tr>`
3431
+ )}
3432
+ </tbody>
3433
+ </table>`;
3434
+ }
3435
+ };
3436
+ RoxyKpChart.styles = [
3437
+ baseStyles,
3438
+ css14`
3439
+ .wrap {
3440
+ border: 1px solid var(--roxy-border, #e4e4e7);
3441
+ border-radius: var(--roxy-radius-md, 8px);
3442
+ background: var(--roxy-bg, #fff);
3443
+ overflow: auto;
3444
+ box-shadow: var(--roxy-shadow-sm);
3445
+ }
3446
+ .head {
3447
+ padding: var(--roxy-space-md, 1rem);
3448
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
3449
+ display: grid;
3450
+ gap: var(--roxy-space-xs, 0.25rem);
3451
+ }
3452
+ .title {
3453
+ margin: 0;
3454
+ font-size: var(--roxy-text-lg, 1.125rem);
3455
+ font-weight: var(--roxy-weight-bold, 600);
3456
+ }
3457
+ .asc,
3458
+ .ayan {
3459
+ color: var(--roxy-muted, #71717a);
3460
+ font-size: var(--roxy-text-sm, 0.875rem);
3461
+ }
3462
+ .asc strong {
3463
+ color: var(--roxy-fg, #0a0a0a);
3464
+ }
3465
+ .tablist {
3466
+ display: flex;
3467
+ gap: 2px;
3468
+ padding: 0 var(--roxy-space-md, 1rem);
3469
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
3470
+ }
3471
+ .tab {
3472
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
3473
+ font-size: var(--roxy-text-sm, 0.875rem);
3474
+ background: none;
3475
+ border: none;
3476
+ border-bottom: 2px solid transparent;
3477
+ margin-bottom: -2px;
3478
+ cursor: pointer;
3479
+ color: var(--roxy-muted, #71717a);
3480
+ font-family: inherit;
3481
+ }
3482
+ .tab[aria-selected='true'] {
3483
+ color: var(--roxy-accent-fg, #b45309);
3484
+ border-bottom-color: var(--roxy-accent, #f59e0b);
3485
+ font-weight: var(--roxy-weight-bold, 600);
3486
+ }
3487
+ .tab:hover:not([aria-selected='true']) {
3488
+ color: var(--roxy-fg, #0a0a0a);
3489
+ }
3490
+ table {
3491
+ width: 100%;
3492
+ border-collapse: collapse;
3493
+ font-size: var(--roxy-text-sm, 0.875rem);
3494
+ min-width: 620px;
3495
+ }
3496
+ thead {
3497
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
3498
+ }
3499
+ th,
3500
+ td {
3501
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
3502
+ text-align: left;
3503
+ white-space: nowrap;
3504
+ }
3505
+ th {
3506
+ color: var(--roxy-muted, #71717a);
3507
+ font-weight: var(--roxy-weight-bold, 600);
3508
+ text-transform: uppercase;
3509
+ font-size: var(--roxy-text-xs, 0.75rem);
3510
+ letter-spacing: 0.04em;
3511
+ }
3512
+ tbody tr {
3513
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
3514
+ }
3515
+ td.body {
3516
+ font-weight: var(--roxy-weight-bold, 600);
3517
+ color: var(--roxy-fg, #0a0a0a);
3518
+ }
3519
+ .retro {
3520
+ color: var(--roxy-warning-fg, #9a3412);
3521
+ font-size: var(--roxy-text-xs, 0.75rem);
3522
+ font-weight: var(--roxy-weight-bold, 600);
3523
+ margin-left: 4px;
3524
+ }
3525
+ .num {
3526
+ font-variant-numeric: tabular-nums;
3527
+ }
3528
+ `
3529
+ ];
3530
+ __decorateClass([
3531
+ property13({ attribute: false })
3532
+ ], RoxyKpChart.prototype, "data", 2);
3533
+ __decorateClass([
3534
+ state3()
3535
+ ], RoxyKpChart.prototype, "activeTab", 2);
3536
+ RoxyKpChart = __decorateClass([
3537
+ customElement13("roxy-kp-chart")
3538
+ ], RoxyKpChart);
3539
+
3540
+ // packages/ui/src/components/kp-planets-table.ts
3541
+ import { css as css15, html as html14, LitElement as LitElement14, nothing as nothing15 } from "lit";
3542
+ import { customElement as customElement14, property as property14 } from "lit/decorators.js";
3543
+ var RoxyKpPlanetsTable = class extends LitElement14 {
3118
3544
  constructor() {
3119
3545
  super(...arguments);
3120
3546
  this.data = null;
3121
3547
  }
3122
3548
  render() {
3123
3549
  if (!this.data)
3124
- return html13`<div class="roxy-empty" role="status">No KP data</div>`;
3550
+ return html14`<div class="roxy-empty" role="status">No KP data</div>`;
3125
3551
  const planets = this.data.planets ?? [];
3126
- return html13`<div
3552
+ return html14`<div
3127
3553
  class="wrap"
3128
3554
  aria-label="KP planets table"
3129
3555
  tabindex="0"
3130
3556
  >
3131
3557
  <header class="head">
3132
3558
  <h2 class="title">KP planets</h2>
3133
- ${typeof this.data.ayanamsa === "number" ? html13`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : nothing14}
3559
+ ${typeof this.data.ayanamsa === "number" ? html14`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : nothing15}
3134
3560
  </header>
3135
3561
  <table role="table">
3136
3562
  <thead>
@@ -3147,10 +3573,10 @@ var RoxyKpPlanetsTable = class extends LitElement13 {
3147
3573
  </thead>
3148
3574
  <tbody>
3149
3575
  ${planets.map(
3150
- (p) => html13`<tr>
3576
+ (p) => html14`<tr>
3151
3577
  <td class="planet">
3152
3578
  ${p.planet}
3153
- ${p.retrograde ? html13`<span class="retro">R</span>` : nothing14}
3579
+ ${p.retrograde ? html14`<span class="retro">R</span>` : nothing15}
3154
3580
  </td>
3155
3581
  <td>${p.sign ?? ""}</td>
3156
3582
  <td>${p.signLord ?? ""}</td>
@@ -3168,7 +3594,7 @@ var RoxyKpPlanetsTable = class extends LitElement13 {
3168
3594
  };
3169
3595
  RoxyKpPlanetsTable.styles = [
3170
3596
  baseStyles,
3171
- css14`
3597
+ css15`
3172
3598
  .wrap {
3173
3599
  border: 1px solid var(--roxy-border, #e4e4e7);
3174
3600
  border-radius: var(--roxy-radius-md, 8px);
@@ -3230,63 +3656,236 @@ RoxyKpPlanetsTable.styles = [
3230
3656
  `
3231
3657
  ];
3232
3658
  __decorateClass([
3233
- property13({ attribute: false })
3659
+ property14({ attribute: false })
3234
3660
  ], RoxyKpPlanetsTable.prototype, "data", 2);
3235
3661
  RoxyKpPlanetsTable = __decorateClass([
3236
- customElement13("roxy-kp-planets-table")
3662
+ customElement14("roxy-kp-planets-table")
3237
3663
  ], RoxyKpPlanetsTable);
3238
3664
 
3239
- // packages/ui/src/components/location-search.ts
3240
- import { css as css15, html as html14, LitElement as LitElement14, nothing as nothing15 } from "lit";
3241
- import { customElement as customElement14, property as property14, state as state3 } from "lit/decorators.js";
3242
-
3243
- // packages/ui/src/utils/debounce.ts
3244
- function debounce(fn, wait) {
3245
- let timer;
3246
- const debounced = ((...args) => {
3247
- if (timer) clearTimeout(timer);
3248
- timer = setTimeout(() => {
3249
- timer = void 0;
3250
- fn(...args);
3251
- }, wait);
3252
- });
3253
- debounced.cancel = () => {
3254
- if (timer) {
3255
- clearTimeout(timer);
3256
- timer = void 0;
3257
- }
3258
- };
3259
- return debounced;
3260
- }
3261
-
3262
- // packages/ui/src/components/location-search.ts
3263
- var RoxyLocationSearch = class extends LitElement14 {
3665
+ // packages/ui/src/components/kp-ruling-planets.ts
3666
+ import { css as css16, html as html15, LitElement as LitElement15, nothing as nothing16 } from "lit";
3667
+ import { customElement as customElement15, property as property15 } from "lit/decorators.js";
3668
+ var RoxyKpRulingPlanets = class extends LitElement15 {
3264
3669
  constructor() {
3265
3670
  super(...arguments);
3266
- this.endpoint = "https://roxyapi.com/api/v2/location/search";
3267
- this.placeholder = "Search city";
3268
- this.defaultValue = "";
3269
- this.query = "";
3270
- this.results = [];
3271
- this.isOpen = false;
3272
- this.isLoading = false;
3273
- this.highlight = -1;
3274
- this.secretKeyWarned = false;
3275
- this.debouncedFetch = debounce((q) => {
3276
- void this.fetchResults(q);
3277
- }, 300);
3278
- this.onInput = (e) => {
3279
- const value = e.target.value;
3280
- this.query = value;
3281
- if (value.length < 2) {
3282
- this.results = [];
3283
- this.isOpen = false;
3284
- this.highlight = -1;
3285
- return;
3286
- }
3287
- this.debouncedFetch(value);
3288
- };
3289
- this.onKeyDown = (e) => {
3671
+ this.data = null;
3672
+ }
3673
+ render() {
3674
+ if (!this.data)
3675
+ return html15`<div class="roxy-empty" role="status">No ruling planets data</div>`;
3676
+ const d = this.data;
3677
+ const significators = d.significators ?? [];
3678
+ return html15`<div class="wrap" aria-label="KP ruling planets">
3679
+ <header>
3680
+ <h2 class="title">KP ruling planets</h2>
3681
+ ${d.dayLord ? html15`<div class="day-lord">Day lord: <strong>${d.dayLord}</strong></div>` : nothing16}
3682
+ </header>
3683
+
3684
+ <div class="groups">
3685
+ <div class="group">
3686
+ <h3>Moon</h3>
3687
+ <dl>
3688
+ <dt>Sign lord</dt><dd>${d.moonSignLord ?? ""}</dd>
3689
+ <dt>Star lord</dt><dd>${d.moonStarLord ?? ""}</dd>
3690
+ <dt>Sub lord</dt><dd>${d.moonSublord ?? ""}</dd>
3691
+ <dt>Sub-sub lord</dt><dd>${d.moonSubSublord ?? ""}</dd>
3692
+ </dl>
3693
+ </div>
3694
+ <div class="group">
3695
+ <h3>Lagna</h3>
3696
+ <dl>
3697
+ <dt>Sign lord</dt><dd>${d.lagnaSignLord ?? ""}</dd>
3698
+ <dt>Star lord</dt><dd>${d.lagnaStarLord ?? ""}</dd>
3699
+ <dt>Sub lord</dt><dd>${d.lagnaSublord ?? ""}</dd>
3700
+ <dt>Sub-sub lord</dt><dd>${d.lagnaSubSublord ?? ""}</dd>
3701
+ </dl>
3702
+ </div>
3703
+ </div>
3704
+
3705
+ ${d.rulingPlanets?.length ? html15`<div class="rp-list" role="list" aria-label="Ruling planets by strength">
3706
+ <span class="rp-label">Ruling planets</span>
3707
+ ${d.rulingPlanets.map(
3708
+ (p, i) => html15`<span class="rp" role="listitem"><span class="rank">${i + 1}</span> ${p}</span>`
3709
+ )}
3710
+ </div>` : nothing16}
3711
+
3712
+ ${significators.length ? html15`<table aria-label="House significators">
3713
+ <thead>
3714
+ <tr>
3715
+ <th scope="col">Planet</th>
3716
+ <th scope="col">Signifies houses</th>
3717
+ </tr>
3718
+ </thead>
3719
+ <tbody>
3720
+ ${significators.map(
3721
+ (s) => html15`<tr>
3722
+ <td>${s.planet}</td>
3723
+ <td>${(s.signifies ?? []).join(", ")}</td>
3724
+ </tr>`
3725
+ )}
3726
+ </tbody>
3727
+ </table>` : nothing16}
3728
+ </div>`;
3729
+ }
3730
+ };
3731
+ RoxyKpRulingPlanets.styles = [
3732
+ baseStyles,
3733
+ css16`
3734
+ .wrap {
3735
+ border: 1px solid var(--roxy-border, #e4e4e7);
3736
+ border-radius: var(--roxy-radius-md, 8px);
3737
+ background: var(--roxy-bg, #fff);
3738
+ padding: var(--roxy-space-md, 1rem);
3739
+ display: grid;
3740
+ gap: var(--roxy-space-md, 1rem);
3741
+ box-shadow: var(--roxy-shadow-sm);
3742
+ }
3743
+ .title {
3744
+ margin: 0;
3745
+ font-size: var(--roxy-text-lg, 1.125rem);
3746
+ font-weight: var(--roxy-weight-bold, 600);
3747
+ }
3748
+ .day-lord {
3749
+ color: var(--roxy-muted, #71717a);
3750
+ font-size: var(--roxy-text-sm, 0.875rem);
3751
+ }
3752
+ .day-lord strong {
3753
+ color: var(--roxy-fg, #0a0a0a);
3754
+ }
3755
+ .groups {
3756
+ display: grid;
3757
+ grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
3758
+ gap: var(--roxy-space-md, 1rem);
3759
+ }
3760
+ .group h3 {
3761
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
3762
+ font-size: var(--roxy-text-xs, 0.75rem);
3763
+ font-weight: var(--roxy-weight-bold, 600);
3764
+ color: var(--roxy-muted, #71717a);
3765
+ text-transform: uppercase;
3766
+ letter-spacing: 0.05em;
3767
+ }
3768
+ .group dl {
3769
+ margin: 0;
3770
+ display: grid;
3771
+ grid-template-columns: auto 1fr;
3772
+ gap: 2px var(--roxy-space-sm, 0.5rem);
3773
+ font-size: var(--roxy-text-sm, 0.875rem);
3774
+ }
3775
+ .group dt {
3776
+ color: var(--roxy-muted, #71717a);
3777
+ }
3778
+ .group dd {
3779
+ margin: 0;
3780
+ color: var(--roxy-fg, #0a0a0a);
3781
+ font-weight: var(--roxy-weight-bold, 600);
3782
+ }
3783
+ .rp-list {
3784
+ display: flex;
3785
+ flex-wrap: wrap;
3786
+ gap: var(--roxy-space-xs, 0.25rem);
3787
+ align-items: center;
3788
+ }
3789
+ .rp-label {
3790
+ font-size: var(--roxy-text-xs, 0.75rem);
3791
+ color: var(--roxy-muted, #71717a);
3792
+ text-transform: uppercase;
3793
+ letter-spacing: 0.05em;
3794
+ margin-right: var(--roxy-space-xs, 0.25rem);
3795
+ }
3796
+ .rp {
3797
+ display: inline-flex;
3798
+ align-items: center;
3799
+ gap: 0.3em;
3800
+ font-size: var(--roxy-text-sm, 0.875rem);
3801
+ font-weight: var(--roxy-weight-bold, 600);
3802
+ padding: 0.15em 0.6em;
3803
+ border-radius: 999px;
3804
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 18%, transparent);
3805
+ color: var(--roxy-fg, #0a0a0a);
3806
+ }
3807
+ .rp .rank {
3808
+ font-size: var(--roxy-text-xs, 0.75rem);
3809
+ color: var(--roxy-accent-fg, #b45309);
3810
+ }
3811
+ table {
3812
+ width: 100%;
3813
+ border-collapse: collapse;
3814
+ font-size: var(--roxy-text-sm, 0.875rem);
3815
+ }
3816
+ th,
3817
+ td {
3818
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-sm, 0.5rem);
3819
+ text-align: left;
3820
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
3821
+ }
3822
+ th {
3823
+ color: var(--roxy-muted, #71717a);
3824
+ font-weight: var(--roxy-weight-bold, 600);
3825
+ text-transform: uppercase;
3826
+ font-size: var(--roxy-text-xs, 0.75rem);
3827
+ letter-spacing: 0.04em;
3828
+ }
3829
+ `
3830
+ ];
3831
+ __decorateClass([
3832
+ property15({ attribute: false })
3833
+ ], RoxyKpRulingPlanets.prototype, "data", 2);
3834
+ RoxyKpRulingPlanets = __decorateClass([
3835
+ customElement15("roxy-kp-ruling-planets")
3836
+ ], RoxyKpRulingPlanets);
3837
+
3838
+ // packages/ui/src/components/location-search.ts
3839
+ import { css as css17, html as html16, LitElement as LitElement16, nothing as nothing17 } from "lit";
3840
+ import { customElement as customElement16, property as property16, state as state4 } from "lit/decorators.js";
3841
+
3842
+ // packages/ui/src/utils/debounce.ts
3843
+ function debounce(fn, wait) {
3844
+ let timer;
3845
+ const debounced = ((...args) => {
3846
+ if (timer) clearTimeout(timer);
3847
+ timer = setTimeout(() => {
3848
+ timer = void 0;
3849
+ fn(...args);
3850
+ }, wait);
3851
+ });
3852
+ debounced.cancel = () => {
3853
+ if (timer) {
3854
+ clearTimeout(timer);
3855
+ timer = void 0;
3856
+ }
3857
+ };
3858
+ return debounced;
3859
+ }
3860
+
3861
+ // packages/ui/src/components/location-search.ts
3862
+ var RoxyLocationSearch = class extends LitElement16 {
3863
+ constructor() {
3864
+ super(...arguments);
3865
+ this.endpoint = "https://roxyapi.com/api/v2/location/search";
3866
+ this.placeholder = "Search city";
3867
+ this.defaultValue = "";
3868
+ this.query = "";
3869
+ this.results = [];
3870
+ this.isOpen = false;
3871
+ this.isLoading = false;
3872
+ this.highlight = -1;
3873
+ this.secretKeyWarned = false;
3874
+ this.debouncedFetch = debounce((q) => {
3875
+ void this.fetchResults(q);
3876
+ }, 300);
3877
+ this.onInput = (e) => {
3878
+ const value = e.target.value;
3879
+ this.query = value;
3880
+ if (value.length < 2) {
3881
+ this.results = [];
3882
+ this.isOpen = false;
3883
+ this.highlight = -1;
3884
+ return;
3885
+ }
3886
+ this.debouncedFetch(value);
3887
+ };
3888
+ this.onKeyDown = (e) => {
3290
3889
  if (!this.isOpen || this.results.length === 0) {
3291
3890
  if (e.key === "ArrowDown" && this.query.length >= 2) {
3292
3891
  void this.fetchResults(this.query);
@@ -3357,8 +3956,13 @@ var RoxyLocationSearch = class extends LitElement14 {
3357
3956
  const headers = {
3358
3957
  Accept: "application/json"
3359
3958
  };
3360
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
3361
- if (this.publishableKey) headers["X-API-Key"] = this.publishableKey;
3959
+ if (this.apiKey && this.publishableKey) {
3960
+ console.warn(
3961
+ "[roxy-location-search] both api-key and publishable-key set; using publishable-key. Remove api-key from your widget markup."
3962
+ );
3963
+ }
3964
+ const key = this.publishableKey ?? this.apiKey;
3965
+ if (key) headers["X-API-Key"] = key;
3362
3966
  const res = await fetch(url, { headers, signal: controller.signal });
3363
3967
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
3364
3968
  const json = await res.json();
@@ -3390,12 +3994,13 @@ var RoxyLocationSearch = class extends LitElement14 {
3390
3994
  );
3391
3995
  }
3392
3996
  render() {
3393
- return html14`<div class="field">
3997
+ return html16`<div class="field">
3394
3998
  <input
3395
3999
  type="text"
3396
4000
  role="combobox"
3397
4001
  aria-expanded=${this.isOpen ? "true" : "false"}
3398
4002
  aria-controls="roxy-location-listbox"
4003
+ aria-activedescendant=${this.isOpen && this.highlight >= 0 ? `roxy-location-option-${this.highlight}` : ""}
3399
4004
  aria-autocomplete="list"
3400
4005
  autocomplete="off"
3401
4006
  placeholder=${this.placeholder}
@@ -3406,18 +4011,19 @@ var RoxyLocationSearch = class extends LitElement14 {
3406
4011
  if (this.results.length > 0) this.isOpen = true;
3407
4012
  }}
3408
4013
  />
3409
- ${this.isLoading ? html14`<span class="spinner" role="status" aria-label="Loading"></span>` : nothing15}
3410
- ${this.isOpen ? html14`<ul
4014
+ ${this.isLoading ? html16`<span class="spinner" role="status" aria-label="Loading"></span>` : nothing17}
4015
+ ${this.isOpen ? html16`<ul
3411
4016
  id="roxy-location-listbox"
3412
4017
  class="results"
3413
4018
  role="listbox"
3414
4019
  >
3415
- ${this.results.length === 0 ? html14`<li class="empty" role="status">No cities found</li>` : this.results.map(
3416
- (city, idx) => html14`<li role="presentation">
4020
+ ${this.results.length === 0 ? html16`<li class="empty" role="status">No cities found</li>` : this.results.map(
4021
+ (city, idx) => html16`<li role="presentation">
3417
4022
  <button
3418
4023
  type="button"
3419
4024
  class="option"
3420
4025
  role="option"
4026
+ id=${`roxy-location-option-${idx}`}
3421
4027
  aria-selected=${this.highlight === idx ? "true" : "false"}
3422
4028
  @click=${() => this.select(city)}
3423
4029
  @mouseenter=${() => {
@@ -3426,7 +4032,7 @@ var RoxyLocationSearch = class extends LitElement14 {
3426
4032
  >
3427
4033
  <span class="city">${city.city}</span>
3428
4034
  <span class="where"
3429
- >${city.province ? html14`${city.province}, ` : ""}${city.country}</span
4035
+ >${city.province ? html16`${city.province}, ` : ""}${city.country}</span
3430
4036
  >
3431
4037
  <span class="tz"
3432
4038
  >UTC${city.utcOffset >= 0 ? "+" : ""}${city.utcOffset}</span
@@ -3434,13 +4040,13 @@ var RoxyLocationSearch = class extends LitElement14 {
3434
4040
  </button>
3435
4041
  </li>`
3436
4042
  )}
3437
- </ul>` : nothing15}
4043
+ </ul>` : nothing17}
3438
4044
  </div>`;
3439
4045
  }
3440
4046
  };
3441
4047
  RoxyLocationSearch.styles = [
3442
4048
  baseStyles,
3443
- css15`
4049
+ css17`
3444
4050
  :host {
3445
4051
  display: block;
3446
4052
  position: relative;
@@ -3544,43 +4150,43 @@ RoxyLocationSearch.styles = [
3544
4150
  `
3545
4151
  ];
3546
4152
  __decorateClass([
3547
- property14({ type: String, attribute: "api-key" })
4153
+ property16({ type: String, attribute: "api-key" })
3548
4154
  ], RoxyLocationSearch.prototype, "apiKey", 2);
3549
4155
  __decorateClass([
3550
- property14({ type: String, attribute: "publishable-key" })
4156
+ property16({ type: String, attribute: "publishable-key" })
3551
4157
  ], RoxyLocationSearch.prototype, "publishableKey", 2);
3552
4158
  __decorateClass([
3553
- property14({ type: String })
4159
+ property16({ type: String })
3554
4160
  ], RoxyLocationSearch.prototype, "endpoint", 2);
3555
4161
  __decorateClass([
3556
- property14({ type: String })
4162
+ property16({ type: String })
3557
4163
  ], RoxyLocationSearch.prototype, "placeholder", 2);
3558
4164
  __decorateClass([
3559
- property14({ type: String, attribute: "default-value" })
4165
+ property16({ type: String, attribute: "default-value" })
3560
4166
  ], RoxyLocationSearch.prototype, "defaultValue", 2);
3561
4167
  __decorateClass([
3562
- state3()
4168
+ state4()
3563
4169
  ], RoxyLocationSearch.prototype, "query", 2);
3564
4170
  __decorateClass([
3565
- state3()
4171
+ state4()
3566
4172
  ], RoxyLocationSearch.prototype, "results", 2);
3567
4173
  __decorateClass([
3568
- state3()
4174
+ state4()
3569
4175
  ], RoxyLocationSearch.prototype, "isOpen", 2);
3570
4176
  __decorateClass([
3571
- state3()
4177
+ state4()
3572
4178
  ], RoxyLocationSearch.prototype, "isLoading", 2);
3573
4179
  __decorateClass([
3574
- state3()
4180
+ state4()
3575
4181
  ], RoxyLocationSearch.prototype, "highlight", 2);
3576
4182
  RoxyLocationSearch = __decorateClass([
3577
- customElement14("roxy-location-search")
4183
+ customElement16("roxy-location-search")
3578
4184
  ], RoxyLocationSearch);
3579
4185
 
3580
4186
  // packages/ui/src/components/moon-phase.ts
3581
- import { css as css16, html as html15, LitElement as LitElement15, nothing as nothing16 } from "lit";
3582
- import { customElement as customElement15, property as property15 } from "lit/decorators.js";
3583
- var RoxyMoonPhase = class extends LitElement15 {
4187
+ import { css as css18, html as html17, LitElement as LitElement17, nothing as nothing18 } from "lit";
4188
+ import { customElement as customElement17, property as property17 } from "lit/decorators.js";
4189
+ var RoxyMoonPhase = class extends LitElement17 {
3584
4190
  constructor() {
3585
4191
  super(...arguments);
3586
4192
  this.data = null;
@@ -3589,12 +4195,12 @@ var RoxyMoonPhase = class extends LitElement15 {
3589
4195
  render() {
3590
4196
  const d = this.data;
3591
4197
  if (!d)
3592
- return html15`<div class="roxy-empty" role="status">No moon phase data</div>`;
4198
+ return html17`<div class="roxy-empty" role="status">No moon phase data</div>`;
3593
4199
  const list = "phases" in d ? d.phases : "calendar" in d ? d.calendar : [];
3594
4200
  if (this.mode !== "current" && list.length > 0) {
3595
4201
  const month = "month" in d ? d.month : void 0;
3596
4202
  const year = "year" in d ? d.year : void 0;
3597
- return html15`<article
4203
+ return html17`<article
3598
4204
  class="card"
3599
4205
  aria-label="Moon phase calendar"
3600
4206
  >
@@ -3604,46 +4210,46 @@ var RoxyMoonPhase = class extends LitElement15 {
3604
4210
  </div>
3605
4211
  </article>`;
3606
4212
  }
3607
- if (!("phase" in d)) return nothing16;
4213
+ if (!("phase" in d)) return nothing18;
3608
4214
  return this.renderSingle(d);
3609
4215
  }
3610
4216
  renderSingle(d) {
3611
4217
  const emoji = phaseEmoji(d.phase);
3612
- return html15`<article class="card" aria-label="Current moon phase">
4218
+ return html17`<article class="card" aria-label="Current moon phase">
3613
4219
  <div class="hero">
3614
4220
  <span class="emoji" aria-hidden="true">${emoji}</span>
3615
4221
  <div>
3616
4222
  <h2 class="label">${d.phase ?? "Moon"}</h2>
3617
- ${d.date ? html15`<div class="date">${d.date}</div>` : nothing16}
4223
+ ${d.date ? html17`<div class="date">${d.date}</div>` : nothing18}
3618
4224
  </div>
3619
4225
  </div>
3620
4226
  <div class="stats">
3621
- ${typeof d.illumination === "number" ? html15`<div>
4227
+ ${typeof d.illumination === "number" ? html17`<div>
3622
4228
  <span>Illumination</span>
3623
4229
  <strong>${formatIllumination(d.illumination)}</strong>
3624
- </div>` : nothing16}
3625
- ${typeof d.age === "number" ? html15`<div>
4230
+ </div>` : nothing18}
4231
+ ${typeof d.age === "number" ? html17`<div>
3626
4232
  <span>Age</span>
3627
4233
  <strong>${formatNumber(d.age, 1)} days</strong>
3628
- </div>` : nothing16}
3629
- ${d.sign ? html15`<div>
4234
+ </div>` : nothing18}
4235
+ ${d.sign ? html17`<div>
3630
4236
  <span>Sign</span>
3631
4237
  <strong>${d.sign}</strong>
3632
- </div>` : nothing16}
3633
- ${typeof d.distance === "number" ? html15`<div>
4238
+ </div>` : nothing18}
4239
+ ${typeof d.distance === "number" ? html17`<div>
3634
4240
  <span>Distance</span>
3635
4241
  <strong>${(d.distance / 1e3).toFixed(0)}k km</strong>
3636
- </div>` : nothing16}
4242
+ </div>` : nothing18}
3637
4243
  </div>
3638
- ${d.meaning?.description ? html15`<p class="meaning">${d.meaning.description}</p>` : nothing16}
3639
- ${d.meaning?.keywords?.length ? html15`<div class="keywords">
3640
- ${d.meaning.keywords.map((k) => html15`<span>${k}</span>`)}
3641
- </div>` : nothing16}
4244
+ ${d.meaning?.description ? html17`<p class="meaning">${d.meaning.description}</p>` : nothing18}
4245
+ ${d.meaning?.keywords?.length ? html17`<div class="keywords">
4246
+ ${d.meaning.keywords.map((k) => html17`<span>${k}</span>`)}
4247
+ </div>` : nothing18}
3642
4248
  </article>`;
3643
4249
  }
3644
4250
  renderListItem(p) {
3645
4251
  const emoji = phaseEmoji(p.phase);
3646
- return html15`<div class="list-item" role="listitem">
4252
+ return html17`<div class="list-item" role="listitem">
3647
4253
  <span aria-hidden="true">${emoji}</span>
3648
4254
  <span>${p.phase}</span>
3649
4255
  <span>${p.date ?? ""}</span>
@@ -3652,7 +4258,7 @@ var RoxyMoonPhase = class extends LitElement15 {
3652
4258
  };
3653
4259
  RoxyMoonPhase.styles = [
3654
4260
  baseStyles,
3655
- css16`
4261
+ css18`
3656
4262
  .card {
3657
4263
  background: var(--roxy-bg, #fff);
3658
4264
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -3737,13 +4343,13 @@ RoxyMoonPhase.styles = [
3737
4343
  `
3738
4344
  ];
3739
4345
  __decorateClass([
3740
- property15({ attribute: false })
4346
+ property17({ attribute: false })
3741
4347
  ], RoxyMoonPhase.prototype, "data", 2);
3742
4348
  __decorateClass([
3743
- property15({ type: String, reflect: true })
4349
+ property17({ type: String, reflect: true })
3744
4350
  ], RoxyMoonPhase.prototype, "mode", 2);
3745
4351
  RoxyMoonPhase = __decorateClass([
3746
- customElement15("roxy-moon-phase")
4352
+ customElement17("roxy-moon-phase")
3747
4353
  ], RoxyMoonPhase);
3748
4354
  function phaseEmoji(phase) {
3749
4355
  if (!phase) return "\u{1F319}";
@@ -3754,109 +4360,312 @@ function formatIllumination(v) {
3754
4360
  return `${Math.round(pct)}%`;
3755
4361
  }
3756
4362
 
3757
- // packages/ui/src/components/natal-chart.ts
3758
- import { css as css17, html as html16, LitElement as LitElement16, nothing as nothing17, svg as svg4 } from "lit";
3759
- import { customElement as customElement16, property as property16 } from "lit/decorators.js";
3760
-
3761
- // packages/ui/src/utils/degree.ts
3762
- function polarToCartesian(cx, cy, radius, angleDeg) {
3763
- const angleRad = angleDeg * Math.PI / 180;
3764
- return {
3765
- x: cx + radius * Math.cos(angleRad),
3766
- y: cy + radius * Math.sin(angleRad)
3767
- };
3768
- }
3769
-
3770
- // packages/ui/src/components/natal-chart.ts
3771
- var SIZE = 420;
3772
- var CENTER = SIZE / 2;
3773
- var OUTER_R = 164;
3774
- var SIGN_R = 146;
3775
- var HOUSE_R = 120;
3776
- var PLANET_R = 96;
3777
- var ANGLE_TICK_R = 178;
3778
- var ANGLE_LABEL_R = 196;
3779
- var RoxyNatalChart = class extends LitElement16 {
4363
+ // packages/ui/src/components/nakshatra-card.ts
4364
+ import { css as css19, html as html18, LitElement as LitElement18, nothing as nothing19 } from "lit";
4365
+ import { customElement as customElement18, property as property18 } from "lit/decorators.js";
4366
+ var RoxyNakshatraCard = class extends LitElement18 {
3780
4367
  constructor() {
3781
4368
  super(...arguments);
3782
4369
  this.data = null;
3783
- this.houseSystem = "placidus";
3784
- }
3785
- getPlanets() {
3786
- return this.data?.planets ?? [];
3787
- }
3788
- getAscendant() {
3789
- return this.data?.ascendant?.longitude ?? 0;
3790
- }
3791
- getMidheaven() {
3792
- const m = this.data?.midheaven?.longitude;
3793
- return typeof m === "number" ? m : null;
3794
- }
3795
- toAngle(lon) {
3796
- return 180 + this.getAscendant() - lon;
3797
4370
  }
3798
4371
  render() {
3799
4372
  if (!this.data)
3800
- return html16`<div class="roxy-empty" role="status">No chart data</div>`;
3801
- const planets = this.getPlanets();
3802
- const aspects = this.data.aspects ?? [];
3803
- return html16`<div class="wrap">
3804
- <header>
3805
- <h2 class="title">Natal chart</h2>
3806
- ${this.data.birthDetails ? html16`<div class="meta">
3807
- ${[this.data.birthDetails.date, this.data.birthDetails.time].filter(Boolean).join(" \xB7 ")}
3808
- </div>` : nothing17}
4373
+ return html18`<div class="roxy-empty" role="status">No nakshatra data</div>`;
4374
+ const n = this.data;
4375
+ const remedies = n.remedies;
4376
+ return html18`<article class="wrap" aria-label=${`Nakshatra ${n.name}`}>
4377
+ <header class="head">
4378
+ <h2 class="name">${n.name}</h2>
4379
+ ${typeof n.number === "number" ? html18`<span class="number">Nakshatra ${n.number} of 27</span>` : nothing19}
4380
+ ${n.range ? html18`<span class="range">${n.range}</span>` : nothing19}
3809
4381
  </header>
3810
- <svg
3811
- viewBox="0 0 ${SIZE} ${SIZE}"
3812
- role="img"
3813
- aria-label="Natal chart wheel with twelve houses, planets, and aspects"
3814
- >
3815
- <title>Natal chart wheel</title>
3816
- <desc>
3817
- Twelve zodiac sign segments around a circular wheel. Planet glyphs are
3818
- placed at their ecliptic longitudes. Aspect lines connect related planets.
3819
- </desc>
3820
- <circle
3821
- class="wheel-line"
3822
- cx=${CENTER}
3823
- cy=${CENTER}
3824
- r=${OUTER_R}
3825
- stroke-width="1.5"
3826
- />
3827
- <circle
3828
- class="wheel-line"
3829
- cx=${CENTER}
3830
- cy=${CENTER}
3831
- r=${HOUSE_R}
3832
- stroke-width="1"
3833
- />
3834
- <circle
3835
- class="wheel-line"
3836
- cx=${CENTER}
3837
- cy=${CENTER}
3838
- r=${PLANET_R - 16}
3839
- stroke-width="0.5"
3840
- />
3841
- ${this.renderSpokes()} ${this.renderSigns()} ${this.renderHouseNumbers()}
3842
- ${this.renderAspects(planets, aspects)} ${this.renderPlanets(planets)}
3843
- ${this.renderAngles()}
3844
- </svg>
3845
- <div class="legend">
3846
- <span>${planets.length} planets</span>
3847
- <span>${aspects.length} aspects</span>
3848
- <span><span class="legend-swatch" style="background: var(--roxy-success)"></span>harmonious</span>
3849
- <span><span class="legend-swatch" style="background: var(--roxy-danger)"></span>challenging</span>
3850
- </div>
3851
- ${this.renderDetails()}
3852
- ${this.renderInterpretations()}
3853
- </div>`;
4382
+
4383
+ <dl class="facts">
4384
+ ${n.lord ? html18`<div class="fact"><dt>Lord</dt><dd>${n.lord}</dd></div>` : nothing19}
4385
+ ${n.deity ? html18`<div class="fact"><dt>Deity</dt><dd>${n.deity}</dd></div>` : nothing19}
4386
+ ${n.symbol ? html18`<div class="fact"><dt>Symbol</dt><dd>${n.symbol}</dd></div>` : nothing19}
4387
+ </dl>
4388
+
4389
+ ${n.characteristics ? html18`<div class="section">
4390
+ <h3>Characteristics</h3>
4391
+ <p>${n.characteristics}</p>
4392
+ </div>` : nothing19}
4393
+
4394
+ ${remedies ? html18`<div class="section">
4395
+ <h3>Remedies</h3>
4396
+ <div class="remedies">
4397
+ ${remedies.mantras ? html18`<div class="remedy"><strong>Mantras:</strong> ${remedies.mantras}</div>` : nothing19}
4398
+ ${remedies.gemstones ? html18`<div class="remedy"><strong>Gemstones:</strong> ${remedies.gemstones}</div>` : nothing19}
4399
+ ${remedies.rituals ? html18`<div class="remedy"><strong>Rituals:</strong> ${remedies.rituals}</div>` : nothing19}
4400
+ </div>
4401
+ </div>` : nothing19}
4402
+ </article>`;
4403
+ }
4404
+ };
4405
+ RoxyNakshatraCard.styles = [
4406
+ baseStyles,
4407
+ css19`
4408
+ .wrap {
4409
+ border: 1px solid var(--roxy-border, #e4e4e7);
4410
+ border-radius: var(--roxy-radius-md, 8px);
4411
+ background: var(--roxy-bg, #fff);
4412
+ padding: var(--roxy-space-md, 1rem);
4413
+ display: grid;
4414
+ gap: var(--roxy-space-md, 1rem);
4415
+ box-shadow: var(--roxy-shadow-sm);
4416
+ }
4417
+ .head {
4418
+ display: flex;
4419
+ align-items: baseline;
4420
+ gap: var(--roxy-space-sm, 0.5rem);
4421
+ flex-wrap: wrap;
4422
+ }
4423
+ .name {
4424
+ margin: 0;
4425
+ font-size: var(--roxy-text-lg, 1.125rem);
4426
+ font-weight: var(--roxy-weight-bold, 600);
4427
+ }
4428
+ .number {
4429
+ color: var(--roxy-accent-fg, #b45309);
4430
+ font-size: var(--roxy-text-sm, 0.875rem);
4431
+ font-weight: var(--roxy-weight-bold, 600);
4432
+ }
4433
+ .range {
4434
+ color: var(--roxy-muted, #71717a);
4435
+ font-size: var(--roxy-text-sm, 0.875rem);
4436
+ }
4437
+ .facts {
4438
+ display: grid;
4439
+ grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
4440
+ gap: var(--roxy-space-sm, 0.5rem);
4441
+ }
4442
+ .fact {
4443
+ display: grid;
4444
+ gap: 2px;
4445
+ }
4446
+ .fact dt {
4447
+ color: var(--roxy-muted, #71717a);
4448
+ font-size: var(--roxy-text-xs, 0.75rem);
4449
+ text-transform: uppercase;
4450
+ letter-spacing: 0.05em;
4451
+ }
4452
+ .fact dd {
4453
+ margin: 0;
4454
+ color: var(--roxy-fg, #0a0a0a);
4455
+ font-size: var(--roxy-text-sm, 0.875rem);
4456
+ }
4457
+ .section h3 {
4458
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
4459
+ font-size: var(--roxy-text-sm, 0.875rem);
4460
+ font-weight: var(--roxy-weight-bold, 600);
4461
+ color: var(--roxy-muted, #71717a);
4462
+ text-transform: uppercase;
4463
+ letter-spacing: 0.05em;
4464
+ }
4465
+ .section p {
4466
+ margin: 0;
4467
+ font-size: var(--roxy-text-sm, 0.875rem);
4468
+ color: var(--roxy-fg, #0a0a0a);
4469
+ line-height: 1.5;
4470
+ }
4471
+ .remedies {
4472
+ display: grid;
4473
+ gap: var(--roxy-space-xs, 0.25rem);
4474
+ }
4475
+ .remedy {
4476
+ font-size: var(--roxy-text-sm, 0.875rem);
4477
+ color: var(--roxy-fg, #0a0a0a);
4478
+ }
4479
+ .remedy strong {
4480
+ color: var(--roxy-muted, #71717a);
4481
+ font-weight: var(--roxy-weight-bold, 600);
4482
+ }
4483
+ `
4484
+ ];
4485
+ __decorateClass([
4486
+ property18({ attribute: false })
4487
+ ], RoxyNakshatraCard.prototype, "data", 2);
4488
+ RoxyNakshatraCard = __decorateClass([
4489
+ customElement18("roxy-nakshatra-card")
4490
+ ], RoxyNakshatraCard);
4491
+
4492
+ // packages/ui/src/components/natal-chart.ts
4493
+ import { css as css20, html as html19, LitElement as LitElement19, nothing as nothing20, svg as svg4 } from "lit";
4494
+ import { customElement as customElement19, property as property19, state as state5 } from "lit/decorators.js";
4495
+ var SIZE = 420;
4496
+ var CENTER = SIZE / 2;
4497
+ var OUTER_R = 164;
4498
+ var SIGN_R = 146;
4499
+ var HOUSE_R = 120;
4500
+ var PLANET_R = 96;
4501
+ var ANGLE_TICK_R = 178;
4502
+ var ANGLE_LABEL_R = 196;
4503
+ var RoxyNatalChart = class extends LitElement19 {
4504
+ constructor() {
4505
+ super(...arguments);
4506
+ this.data = null;
4507
+ this.houseSystem = "placidus";
4508
+ this.view = "wheel";
4509
+ }
4510
+ getPlanets() {
4511
+ return this.data?.planets ?? [];
4512
+ }
4513
+ getAscendant() {
4514
+ return this.data?.ascendant?.longitude ?? 0;
4515
+ }
4516
+ getMidheaven() {
4517
+ const m = this.data?.midheaven?.longitude;
4518
+ return typeof m === "number" ? m : null;
4519
+ }
4520
+ toAngle(lon) {
4521
+ return 180 + this.getAscendant() - lon;
4522
+ }
4523
+ render() {
4524
+ if (!this.data)
4525
+ return html19`<div class="roxy-empty" role="status">No chart data</div>`;
4526
+ const planets = this.getPlanets();
4527
+ const aspects = this.data.aspects ?? [];
4528
+ const view = this.view;
4529
+ return html19`<div class="wrap">
4530
+ <header>
4531
+ <h2 class="title">Natal chart</h2>
4532
+ ${this.data.birthDetails ? html19`<div class="meta">
4533
+ ${[this.data.birthDetails.date, this.data.birthDetails.time].filter(Boolean).join(" \xB7 ")}
4534
+ </div>` : nothing20}
4535
+ </header>
4536
+ <div
4537
+ class="tablist"
4538
+ role="tablist"
4539
+ aria-label="Natal chart views"
4540
+ @keydown=${this.onTabKeyDown}
4541
+ >
4542
+ ${["wheel", "grid"].map(
4543
+ (t) => html19`<button
4544
+ class="tab"
4545
+ role="tab"
4546
+ id="tab-${t}"
4547
+ aria-selected=${view === t ? "true" : "false"}
4548
+ aria-controls="panel-${t}"
4549
+ tabindex=${view === t ? "0" : "-1"}
4550
+ @click=${() => {
4551
+ this.view = t;
4552
+ }}
4553
+ >
4554
+ ${t === "wheel" ? "Wheel" : "Aspect grid"}
4555
+ </button>`
4556
+ )}
4557
+ </div>
4558
+ <div id="panel-${view}" role="tabpanel" aria-labelledby="tab-${view}">
4559
+ ${view === "wheel" ? this.renderWheel(planets, aspects) : this.renderAspectGrid(planets, aspects)}
4560
+ </div>
4561
+ <div class="legend">
4562
+ <span>${planets.length} planets</span>
4563
+ <span>${aspects.length} aspects</span>
4564
+ ${this.data.houseSystem ? html19`<span>${this.data.houseSystem} houses</span>` : nothing20}
4565
+ <span><span class="legend-swatch" style="background: var(--roxy-success)"></span>harmonious</span>
4566
+ <span><span class="legend-swatch" style="background: var(--roxy-danger)"></span>challenging</span>
4567
+ </div>
4568
+ ${this.renderDetails()}
4569
+ ${this.renderInterpretations()}
4570
+ </div>`;
4571
+ }
4572
+ onTabKeyDown(e) {
4573
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
4574
+ e.preventDefault();
4575
+ this.view = this.view === "wheel" ? "grid" : "wheel";
4576
+ const next = this.view;
4577
+ requestAnimationFrame(() => {
4578
+ this.shadowRoot?.querySelector(`#tab-${next}`)?.focus();
4579
+ });
4580
+ }
4581
+ renderWheel(planets, aspects) {
4582
+ return html19`<svg
4583
+ viewBox="0 0 ${SIZE} ${SIZE}"
4584
+ role="img"
4585
+ aria-label="Natal chart wheel with twelve houses, planets, and aspects"
4586
+ >
4587
+ <title>Natal chart wheel</title>
4588
+ <desc>
4589
+ Twelve zodiac sign segments around a circular wheel. Planet glyphs are
4590
+ placed at their ecliptic longitudes. Aspect lines connect related planets.
4591
+ </desc>
4592
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${OUTER_R} stroke-width="1.5" />
4593
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${SIGN_R - 14} stroke-width="0.8" />
4594
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${HOUSE_R} stroke-width="1" />
4595
+ <circle class="wheel-line" cx=${CENTER} cy=${CENTER} r=${PLANET_R - 16} stroke-width="0.5" />
4596
+ ${this.renderTicks()} ${this.renderSpokes()} ${this.renderSigns()}
4597
+ ${this.renderHouseNumbers()} ${this.renderCuspDegrees()}
4598
+ ${this.renderAspects(planets, aspects)} ${this.renderPlanets(planets)}
4599
+ ${this.renderAngles()}
4600
+ </svg>`;
4601
+ }
4602
+ /**
4603
+ * Planet-by-planet aspect grid: the lower-triangular matrix astrologers read
4604
+ * alongside the wheel. Each filled cell shows the aspect glyph colored by
4605
+ * nature, with the exact orb in the SVG-free `<title>` tooltip.
4606
+ */
4607
+ renderAspectGrid(planets, aspects) {
4608
+ const names = planets.map((p) => capitalize(p.name));
4609
+ const byPair = /* @__PURE__ */ new Map();
4610
+ for (const a of aspects) {
4611
+ const k = [capitalize(a.planet1), capitalize(a.planet2)].sort().join("|");
4612
+ byPair.set(k, a);
4613
+ }
4614
+ if (names.length === 0)
4615
+ return html19`<p class="roxy-empty" role="status">No planets to grid</p>`;
4616
+ return html19`<div class="grid-scroll">
4617
+ <table class="aspect-grid" aria-label="Planet by planet aspect grid">
4618
+ <thead>
4619
+ <tr>
4620
+ <th></th>
4621
+ ${names.slice(0, -1).map((n) => {
4622
+ const g = PLANET_GLYPH[n] ?? n.slice(0, 2);
4623
+ return html19`<th scope="col" title=${n}>${g}</th>`;
4624
+ })}
4625
+ </tr>
4626
+ </thead>
4627
+ <tbody>
4628
+ ${names.slice(1).map((rowName, ri) => {
4629
+ const rowGlyph = PLANET_GLYPH[rowName] ?? rowName.slice(0, 2);
4630
+ return html19`<tr>
4631
+ <th scope="row" title=${rowName}>${rowGlyph}</th>
4632
+ ${names.slice(0, ri + 1).map((colName) => {
4633
+ const a = byPair.get([rowName, colName].sort().join("|"));
4634
+ if (!a) return html19`<td class="empty"></td>`;
4635
+ const name = normalizeAspect(a);
4636
+ const sym = ASPECT_SYMBOL[name] ?? ASPECT_SYMBOL[name.replace(/-/g, "")] ?? name.slice(0, 3);
4637
+ const cls = ASPECT_CLASS[name] ?? "aspect-other";
4638
+ const orb = formatNumber(a.orb, 1);
4639
+ return html19`<td class=${`cell ${cls}`} title=${`${rowName} ${name} ${colName}${orb ? ` (orb ${orb}\xB0)` : ""}`}>
4640
+ <span class="asp">${sym}</span>
4641
+ </td>`;
4642
+ })}
4643
+ ${names.slice(ri + 1, -1).map(() => html19`<td class="empty"></td>`)}
4644
+ </tr>`;
4645
+ })}
4646
+ </tbody>
4647
+ </table>
4648
+ </div>`;
3854
4649
  }
3855
4650
  renderAngles() {
3856
4651
  const asc = this.getAscendant();
3857
4652
  const mc = this.getMidheaven();
3858
- const items = [this.renderAngleMark(asc, "ASC")];
3859
- if (mc !== null) items.push(this.renderAngleMark(mc, "MC"));
4653
+ const items = [
4654
+ this.renderAngleMark(asc, "ASC"),
4655
+ this.renderAngleMark(oppositePoint(asc), "DSC")
4656
+ ];
4657
+ if (mc !== null) {
4658
+ items.push(this.renderAngleMark(mc, "MC"));
4659
+ items.push(this.renderAngleMark(oppositePoint(mc), "IC"));
4660
+ }
4661
+ const pof = this.data?.partOfFortune?.longitude;
4662
+ if (typeof pof === "number") {
4663
+ items.push(this.renderAngleMark(normalizeLongitude(pof), "PoF"));
4664
+ }
4665
+ const vertex = this.data?.vertex?.longitude;
4666
+ if (typeof vertex === "number") {
4667
+ items.push(this.renderAngleMark(normalizeLongitude(vertex), "Vtx"));
4668
+ }
3860
4669
  return items;
3861
4670
  }
3862
4671
  renderAngleMark(longitude, label) {
@@ -3872,8 +4681,10 @@ var RoxyNatalChart = class extends LitElement16 {
3872
4681
  `;
3873
4682
  }
3874
4683
  renderSpokes() {
3875
- return Array.from({ length: 12 }, (_, i) => {
3876
- const angle = this.toAngle(i * 30);
4684
+ const houses = this.data?.houses ?? [];
4685
+ const cuspLongitudes = houses.length === 12 ? houses.map((h) => h.longitude) : Array.from({ length: 12 }, (_, i) => this.getAscendant() + i * 30);
4686
+ return cuspLongitudes.map((lon) => {
4687
+ const angle = this.toAngle(lon);
3877
4688
  const start = polarToCartesian(CENTER, CENTER, HOUSE_R, angle);
3878
4689
  const end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
3879
4690
  return svg4`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.8" />`;
@@ -3887,6 +4698,23 @@ var RoxyNatalChart = class extends LitElement16 {
3887
4698
  });
3888
4699
  }
3889
4700
  renderHouseNumbers() {
4701
+ const houses = this.data?.houses ?? [];
4702
+ if (houses.length === 12) {
4703
+ return houses.map((house, i) => {
4704
+ const next = houses[(i + 1) % 12];
4705
+ const mid = arcMidpoint(
4706
+ house.longitude,
4707
+ next ? next.longitude : house.longitude + 30
4708
+ );
4709
+ const pos = polarToCartesian(
4710
+ CENTER,
4711
+ CENTER,
4712
+ HOUSE_R - 12,
4713
+ this.toAngle(mid)
4714
+ );
4715
+ return svg4`<text class="house-num" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${house.number}</text>`;
4716
+ });
4717
+ }
3890
4718
  const ascSignIndex = Math.floor(this.getAscendant() / 30);
3891
4719
  return Array.from({ length: 12 }, (_, i) => {
3892
4720
  const angle = this.toAngle(i * 30 + 15);
@@ -3895,82 +4723,150 @@ var RoxyNatalChart = class extends LitElement16 {
3895
4723
  return svg4`<text class="house-num" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${houseNum}</text>`;
3896
4724
  });
3897
4725
  }
4726
+ /**
4727
+ * Degree ticks on the outer zodiac band: a short mark every 5 degrees and a
4728
+ * longer one on each 30-degree sign cusp, so the wheel reads like a
4729
+ * reference-grade chart rather than a bare ring of glyphs.
4730
+ */
4731
+ renderTicks() {
4732
+ const ticks = [];
4733
+ for (let deg = 0; deg < 360; deg += 5) {
4734
+ const angle = this.toAngle(deg);
4735
+ const isMajor = deg % 30 === 0;
4736
+ const inner = isMajor ? SIGN_R - 14 : OUTER_R - 5;
4737
+ const a = polarToCartesian(CENTER, CENTER, inner, angle);
4738
+ const b = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
4739
+ ticks.push(
4740
+ svg4`<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} />`
4741
+ );
4742
+ }
4743
+ return ticks;
4744
+ }
4745
+ /**
4746
+ * Degree-and-minute label printed next to each house cusp on the wheel, so
4747
+ * the exact cusp position is readable without leaving the chart.
4748
+ */
4749
+ renderCuspDegrees() {
4750
+ const houses = this.data?.houses ?? [];
4751
+ if (houses.length !== 12) return nothing20;
4752
+ return houses.map((house) => {
4753
+ const angle = this.toAngle(house.longitude);
4754
+ const pos = polarToCartesian(CENTER, CENTER, HOUSE_R + 9, angle);
4755
+ const sp = longitudeToSignPosition(house.longitude);
4756
+ return svg4`<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>`;
4757
+ });
4758
+ }
3898
4759
  renderPlanets(planets) {
3899
4760
  return planets.map((p) => {
3900
- if (!Number.isFinite(p.longitude)) return nothing17;
4761
+ if (!Number.isFinite(p.longitude)) return nothing20;
3901
4762
  const angle = this.toAngle(p.longitude);
3902
- const pos = polarToCartesian(CENTER, CENTER, PLANET_R, angle);
4763
+ const glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, angle);
4764
+ const degPos = polarToCartesian(CENTER, CENTER, PLANET_R - 13, angle);
3903
4765
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
3904
- const retro = p.isRetrograde ? " R" : "";
3905
- const display = retro ? `${glyph}\u1D3F` : glyph;
3906
- return svg4`<text class="planet-glyph" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}${retro}</title>${display}</text>`;
4766
+ const sp = longitudeToSignPosition(p.longitude);
4767
+ const retro = p.isRetrograde === true;
4768
+ const degLabel = `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}'`;
4769
+ return svg4`<g>
4770
+ <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>
4771
+ <text class="planet-deg" x=${degPos.x} y=${degPos.y} text-anchor="middle" dominant-baseline="central">${degLabel}${retro ? svg4`<tspan class="retro"> ℞</tspan>` : nothing20}</text>
4772
+ </g>`;
3907
4773
  });
3908
4774
  }
3909
4775
  renderDetails() {
3910
4776
  const summary = this.data?.summary;
3911
4777
  const ai = this.data?.aspectsInterpretation;
3912
- if (!summary && !ai) return nothing17;
4778
+ if (!summary && !ai) return nothing20;
3913
4779
  const retrogrades = summary?.retrogradePlanets ?? [];
3914
- const elementDist = summary?.elementDistribution ?? {};
3915
- const modalityDist = summary?.modalityDistribution ?? {};
3916
- const elementMax = Math.max(1, ...Object.values(elementDist));
3917
- const modalityMax = Math.max(1, ...Object.values(modalityDist));
3918
- return html16`<div class="details">
3919
- ${summary?.dominantElement || summary?.dominantModality ? html16`<div class="pill-row">
3920
- ${summary.dominantElement ? html16`<span class="pill">Dominant element: ${summary.dominantElement}</span>` : nothing17}
3921
- ${summary.dominantModality ? html16`<span class="pill">Dominant modality: ${summary.dominantModality}</span>` : nothing17}
3922
- </div>` : nothing17}
3923
- ${ai ? html16`<div class="pill-row">
4780
+ return html19`<div class="details">
4781
+ ${summary?.dominantElement || summary?.dominantModality ? html19`<div class="pill-row">
4782
+ ${summary.dominantElement ? html19`<span class="pill">Dominant element: ${summary.dominantElement}</span>` : nothing20}
4783
+ ${summary.dominantModality ? html19`<span class="pill">Dominant modality: ${summary.dominantModality}</span>` : nothing20}
4784
+ </div>` : nothing20}
4785
+ ${ai ? html19`<div class="pill-row">
3924
4786
  <span class="pill pill--success">Harmonious ${ai.harmonious}</span>
3925
4787
  <span class="pill pill--danger">Challenging ${ai.challenging}</span>
3926
4788
  <span class="pill pill--muted">Neutral ${ai.neutral}</span>
3927
- </div>` : nothing17}
3928
- ${retrogrades.length > 0 ? html16`<div class="pill-row">
4789
+ </div>` : nothing20}
4790
+ ${retrogrades.length > 0 ? html19`<div class="pill-row">
3929
4791
  ${retrogrades.map((p) => {
3930
4792
  const glyph = PLANET_GLYPH[p] ?? p.slice(0, 2);
3931
- return html16`<span class="pill pill--muted">${glyph} ${p} R</span>`;
4793
+ return html19`<span class="pill pill--muted">${glyph} ${p} R</span>`;
3932
4794
  })}
3933
- </div>` : nothing17}
3934
- ${ai?.summary ? html16`<p class="summary">${ai.summary}</p>` : nothing17}
3935
- ${Object.keys(elementDist).length > 0 || Object.keys(modalityDist).length > 0 ? html16`<div class="dist-grid">
3936
- ${Object.keys(elementDist).length > 0 ? html16`<div class="dist-section">
3937
- <h3>Elements</h3>
3938
- ${Object.entries(elementDist).map(
3939
- ([label, count]) => html16`<div class="dist-row">
3940
- <span>${label}</span>
3941
- <div class="dist-bar"><span style="width: ${Math.round(count / elementMax * 100)}%"></span></div>
3942
- <span>${count}</span>
3943
- </div>`
3944
- )}
3945
- </div>` : nothing17}
3946
- ${Object.keys(modalityDist).length > 0 ? html16`<div class="dist-section">
3947
- <h3>Modalities</h3>
3948
- ${Object.entries(modalityDist).map(
3949
- ([label, count]) => html16`<div class="dist-row">
3950
- <span>${label}</span>
3951
- <div class="dist-bar"><span style="width: ${Math.round(count / modalityMax * 100)}%"></span></div>
3952
- <span>${count}</span>
3953
- </div>`
3954
- )}
3955
- </div>` : nothing17}
3956
- </div>` : nothing17}
4795
+ </div>` : nothing20}
4796
+ ${ai?.summary ? html19`<p class="summary">${ai.summary}</p>` : nothing20}
4797
+ ${this.renderElementModalityGrid()}
3957
4798
  </div>`;
3958
4799
  }
4800
+ /**
4801
+ * Element by modality grid: the 4x3 cross-tab astrologers read for chart
4802
+ * balance. Each planet is placed by its sign into one cell (Fire/Earth/Air/
4803
+ * Water row, Cardinal/Fixed/Mutable column). Derived purely from the planet
4804
+ * signs, with row, column, and grand totals.
4805
+ */
4806
+ renderElementModalityGrid() {
4807
+ const planets = this.getPlanets();
4808
+ if (planets.length === 0) return nothing20;
4809
+ const ELEMENTS = ["Fire", "Earth", "Air", "Water"];
4810
+ const MODALITIES = ["Cardinal", "Fixed", "Mutable"];
4811
+ const order = SIGNS_ORDER;
4812
+ const cells = {};
4813
+ for (const el of ELEMENTS)
4814
+ cells[el] = { Cardinal: [], Fixed: [], Mutable: [] };
4815
+ for (const p of planets) {
4816
+ const idx = order.indexOf(capitalize(p.sign ?? ""));
4817
+ if (idx < 0) continue;
4818
+ const el = ELEMENTS[idx % 4];
4819
+ const mod = MODALITIES[idx % 3];
4820
+ const glyph = PLANET_GLYPH[capitalize(p.name)] ?? capitalize(p.name).slice(0, 2);
4821
+ cells[el]?.[mod]?.push(glyph);
4822
+ }
4823
+ return html19`<table class="em-grid" aria-label="Element and modality distribution">
4824
+ <thead>
4825
+ <tr>
4826
+ <th></th>
4827
+ ${MODALITIES.map((m) => html19`<th scope="col">${m.slice(0, 3)}</th>`)}
4828
+ <th scope="col">Total</th>
4829
+ </tr>
4830
+ </thead>
4831
+ <tbody>
4832
+ ${ELEMENTS.map((el) => {
4833
+ const rowTotal = MODALITIES.reduce(
4834
+ (s, m) => s + (cells[el]?.[m]?.length ?? 0),
4835
+ 0
4836
+ );
4837
+ return html19`<tr>
4838
+ <th scope="row">${el}</th>
4839
+ ${MODALITIES.map(
4840
+ (m) => html19`<td>${(cells[el]?.[m] ?? []).join(" ")}</td>`
4841
+ )}
4842
+ <td class="em-total">${rowTotal}</td>
4843
+ </tr>`;
4844
+ })}
4845
+ <tr>
4846
+ <th scope="row">Total</th>
4847
+ ${MODALITIES.map(
4848
+ (m) => html19`<td class="em-total">${ELEMENTS.reduce((s, el) => s + (cells[el]?.[m]?.length ?? 0), 0)}</td>`
4849
+ )}
4850
+ <td class="em-total">${planets.length}</td>
4851
+ </tr>
4852
+ </tbody>
4853
+ </table>`;
4854
+ }
3959
4855
  renderInterpretations() {
3960
4856
  const planets = this.getPlanets().filter((p) => p.interpretation);
3961
- if (planets.length === 0) return nothing17;
3962
- return html16`<section class="interpretations">
4857
+ if (planets.length === 0) return nothing20;
4858
+ return html19`<section class="interpretations">
3963
4859
  <h3>Planet readings</h3>
3964
4860
  ${planets.map((p, idx) => {
3965
4861
  const interp = p.interpretation;
3966
4862
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
3967
4863
  const deg = formatNumber(p.degree ?? 0, 1);
3968
- return html16`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
4864
+ return html19`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
3969
4865
  <summary>${glyph} ${p.name} <small>${p.sign ?? ""} ${deg}</small></summary>
3970
4866
  <div class="interp-body">
3971
- ${interp.summary ? html16`<p class="interp-summary">${interp.summary}</p>` : nothing17}
3972
- ${interp.detailed ? html16`<p class="interp-detail">${interp.detailed}</p>` : nothing17}
3973
- ${interp.keywords?.length ? html16`<div class="interp-keywords">${interp.keywords.map((k) => html16`<span class="kw">${k}</span>`)}</div>` : nothing17}
4867
+ ${interp.summary ? html19`<p class="interp-summary">${interp.summary}</p>` : nothing20}
4868
+ ${interp.detailed ? html19`<p class="interp-detail">${interp.detailed}</p>` : nothing20}
4869
+ ${interp.keywords?.length ? html19`<div class="interp-keywords">${interp.keywords.map((k) => html19`<span class="kw">${k}</span>`)}</div>` : nothing20}
3974
4870
  </div>
3975
4871
  </details>`;
3976
4872
  })}
@@ -3986,7 +4882,7 @@ var RoxyNatalChart = class extends LitElement16 {
3986
4882
  return aspects.map((a) => {
3987
4883
  const l1 = planetMap.get(capitalize(a.planet1));
3988
4884
  const l2 = planetMap.get(capitalize(a.planet2));
3989
- if (l1 === void 0 || l2 === void 0) return nothing17;
4885
+ if (l1 === void 0 || l2 === void 0) return nothing20;
3990
4886
  const p1 = polarToCartesian(
3991
4887
  CENTER,
3992
4888
  CENTER,
@@ -4008,7 +4904,7 @@ var RoxyNatalChart = class extends LitElement16 {
4008
4904
  };
4009
4905
  RoxyNatalChart.styles = [
4010
4906
  baseStyles,
4011
- css17`
4907
+ css20`
4012
4908
  .wrap {
4013
4909
  width: 100%;
4014
4910
  display: grid;
@@ -4053,12 +4949,35 @@ RoxyNatalChart.styles = [
4053
4949
  font-family: var(--roxy-font-sans);
4054
4950
  }
4055
4951
 
4952
+ .planet-deg {
4953
+ fill: var(--roxy-fg, #0a0a0a);
4954
+ font-size: 7px;
4955
+ font-family: var(--roxy-font-sans);
4956
+ }
4957
+
4958
+ .planet-deg .retro {
4959
+ fill: var(--roxy-danger, #dc2626);
4960
+ }
4961
+
4056
4962
  .house-num {
4057
4963
  fill: var(--roxy-muted, #71717a);
4058
4964
  font-size: 9px;
4059
4965
  font-family: var(--roxy-font-sans);
4060
4966
  }
4061
4967
 
4968
+ .cusp-deg {
4969
+ fill: var(--roxy-muted, #71717a);
4970
+ font-size: 6px;
4971
+ font-family: var(--roxy-font-sans);
4972
+ }
4973
+
4974
+ .tick {
4975
+ stroke: var(--roxy-border, #e4e4e7);
4976
+ }
4977
+ .tick-major {
4978
+ stroke: var(--roxy-secondary, #475569);
4979
+ }
4980
+
4062
4981
  .aspect {
4063
4982
  stroke-width: 0.8;
4064
4983
  fill: none;
@@ -4108,6 +5027,78 @@ RoxyNatalChart.styles = [
4108
5027
  vertical-align: middle;
4109
5028
  }
4110
5029
 
5030
+ .tablist {
5031
+ display: flex;
5032
+ gap: 2px;
5033
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
5034
+ }
5035
+ .tab {
5036
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
5037
+ font-size: var(--roxy-text-sm, 0.875rem);
5038
+ background: none;
5039
+ border: none;
5040
+ border-bottom: 2px solid transparent;
5041
+ margin-bottom: -2px;
5042
+ cursor: pointer;
5043
+ color: var(--roxy-muted, #71717a);
5044
+ font-family: inherit;
5045
+ transition: color var(--roxy-motion-duration, 200ms) var(--roxy-motion-easing, ease);
5046
+ }
5047
+ .tab[aria-selected='true'] {
5048
+ color: var(--roxy-accent-fg, #b45309);
5049
+ border-bottom-color: var(--roxy-accent, #f59e0b);
5050
+ font-weight: var(--roxy-weight-bold, 600);
5051
+ }
5052
+ .tab:hover:not([aria-selected='true']) {
5053
+ color: var(--roxy-fg, #0a0a0a);
5054
+ }
5055
+
5056
+ .grid-scroll {
5057
+ overflow-x: auto;
5058
+ -webkit-overflow-scrolling: touch;
5059
+ }
5060
+ table.aspect-grid {
5061
+ border-collapse: collapse;
5062
+ font-size: var(--roxy-text-xs, 0.75rem);
5063
+ margin: 0 auto;
5064
+ }
5065
+ table.aspect-grid th,
5066
+ table.aspect-grid td {
5067
+ width: 1.6rem;
5068
+ height: 1.6rem;
5069
+ text-align: center;
5070
+ border: 1px solid var(--roxy-border, #e4e4e7);
5071
+ padding: 0;
5072
+ }
5073
+ table.aspect-grid th {
5074
+ color: var(--roxy-secondary, #475569);
5075
+ font-weight: var(--roxy-weight-bold, 600);
5076
+ }
5077
+ table.aspect-grid td.cell {
5078
+ cursor: default;
5079
+ }
5080
+ table.aspect-grid td.empty {
5081
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 18%, transparent);
5082
+ }
5083
+ table.aspect-grid td .asp {
5084
+ font-size: 0.95em;
5085
+ line-height: 1;
5086
+ }
5087
+ table.aspect-grid td.aspect-trine .asp,
5088
+ table.aspect-grid td.aspect-sextile .asp {
5089
+ color: var(--roxy-success, #16a34a);
5090
+ }
5091
+ table.aspect-grid td.aspect-square .asp,
5092
+ table.aspect-grid td.aspect-opposition .asp {
5093
+ color: var(--roxy-danger, #dc2626);
5094
+ }
5095
+ table.aspect-grid td.aspect-conjunction .asp {
5096
+ color: var(--roxy-accent-fg, #b45309);
5097
+ }
5098
+ table.aspect-grid td.aspect-other .asp {
5099
+ color: var(--roxy-muted, #71717a);
5100
+ }
5101
+
4111
5102
  .details {
4112
5103
  margin-top: var(--roxy-space-md, 1rem);
4113
5104
  }
@@ -4148,48 +5139,37 @@ RoxyNatalChart.styles = [
4148
5139
  margin: var(--roxy-space-md, 1rem) 0;
4149
5140
  }
4150
5141
 
4151
- .dist-grid {
4152
- display: grid;
4153
- grid-template-columns: 1fr 1fr;
4154
- gap: var(--roxy-space-md, 1rem);
5142
+ .em-grid {
5143
+ border-collapse: collapse;
5144
+ font-size: var(--roxy-text-xs, 0.75rem);
5145
+ width: 100%;
4155
5146
  }
4156
-
4157
- @container (max-width: 639px) {
4158
- .dist-grid {
4159
- grid-template-columns: 1fr;
4160
- }
5147
+ .em-grid th,
5148
+ .em-grid td {
5149
+ border: 1px solid var(--roxy-border, #e4e4e7);
5150
+ padding: 3px 5px;
5151
+ text-align: center;
5152
+ vertical-align: middle;
4161
5153
  }
4162
-
4163
- .dist-section h3 {
4164
- font-size: var(--roxy-text-xs, 0.75rem);
4165
- font-weight: var(--roxy-weight-bold, 600);
5154
+ .em-grid th {
4166
5155
  color: var(--roxy-muted, #71717a);
4167
- margin: 0 0 var(--roxy-space-xs, 0.25rem);
5156
+ font-weight: var(--roxy-weight-bold, 600);
4168
5157
  text-transform: uppercase;
4169
- letter-spacing: 0.05em;
4170
- }
4171
-
4172
- .dist-row {
4173
- display: grid;
4174
- grid-template-columns: 4rem 1fr 1.5rem;
4175
- align-items: center;
4176
- gap: var(--roxy-space-xs, 0.25rem);
4177
- font-size: var(--roxy-text-xs, 0.75rem);
4178
- color: var(--roxy-fg, #0f172a);
4179
- margin-bottom: 4px;
5158
+ letter-spacing: 0.04em;
4180
5159
  }
4181
-
4182
- .dist-bar {
4183
- background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 20%, transparent);
4184
- height: 6px;
4185
- border-radius: 3px;
5160
+ .em-grid th[scope='row'] {
5161
+ text-align: left;
4186
5162
  }
4187
-
4188
- .dist-bar > span {
4189
- display: block;
4190
- height: 100%;
4191
- background: var(--roxy-accent, #f59e0b);
4192
- border-radius: 3px;
5163
+ .em-grid td {
5164
+ color: var(--roxy-accent, #f59e0b);
5165
+ font-size: 0.95em;
5166
+ line-height: 1.4;
5167
+ min-width: 1.4rem;
5168
+ }
5169
+ .em-grid .em-total {
5170
+ color: var(--roxy-fg, #0a0a0a);
5171
+ font-weight: var(--roxy-weight-bold, 600);
5172
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 25%, transparent);
4193
5173
  }
4194
5174
 
4195
5175
  .interpretations {
@@ -4240,19 +5220,22 @@ RoxyNatalChart.styles = [
4240
5220
  `
4241
5221
  ];
4242
5222
  __decorateClass([
4243
- property16({ attribute: false })
5223
+ property19({ attribute: false })
4244
5224
  ], RoxyNatalChart.prototype, "data", 2);
4245
5225
  __decorateClass([
4246
- property16({ type: String, attribute: "house-system", reflect: true })
5226
+ property19({ type: String, attribute: "house-system", reflect: true })
4247
5227
  ], RoxyNatalChart.prototype, "houseSystem", 2);
5228
+ __decorateClass([
5229
+ state5()
5230
+ ], RoxyNatalChart.prototype, "view", 2);
4248
5231
  RoxyNatalChart = __decorateClass([
4249
- customElement16("roxy-natal-chart")
5232
+ customElement19("roxy-natal-chart")
4250
5233
  ], RoxyNatalChart);
4251
5234
 
4252
5235
  // packages/ui/src/components/numerology-card.ts
4253
- import { css as css18, html as html17, LitElement as LitElement17, nothing as nothing18 } from "lit";
4254
- import { customElement as customElement17, property as property17 } from "lit/decorators.js";
4255
- var RoxyNumerologyCard = class extends LitElement17 {
5236
+ import { css as css21, html as html20, LitElement as LitElement20, nothing as nothing21 } from "lit";
5237
+ import { customElement as customElement20, property as property20 } from "lit/decorators.js";
5238
+ var RoxyNumerologyCard = class extends LitElement20 {
4256
5239
  constructor() {
4257
5240
  super(...arguments);
4258
5241
  this.data = null;
@@ -4261,7 +5244,7 @@ var RoxyNumerologyCard = class extends LitElement17 {
4261
5244
  render() {
4262
5245
  const d = this.data;
4263
5246
  if (!d)
4264
- return html17`<div class="roxy-empty" role="status">No numerology data</div>`;
5247
+ return html20`<div class="roxy-empty" role="status">No numerology data</div>`;
4265
5248
  const headerLabel = LABELS[this.type] ?? this.type;
4266
5249
  if ("coreNumbers" in d) return this.renderChart(d, headerLabel);
4267
5250
  if ("personalYear" in d) return this.renderPersonalYear(d, headerLabel);
@@ -4272,61 +5255,61 @@ var RoxyNumerologyCard = class extends LitElement17 {
4272
5255
  }
4273
5256
  renderNumberCard(d, headerLabel) {
4274
5257
  const keywords = d.meaning?.keywords ?? [];
4275
- return html17`<article class="card" aria-label=${headerLabel}>
5258
+ return html20`<article class="card" aria-label=${headerLabel}>
4276
5259
  <div class="hero">
4277
- ${typeof d.number === "number" ? html17`<div class="numeral">${d.number}</div>` : nothing18}
5260
+ ${typeof d.number === "number" ? html20`<div class="numeral">${d.number}</div>` : nothing21}
4278
5261
  <div>
4279
5262
  <p class="label">${headerLabel}</p>
4280
- ${d.meaning?.title ? html17`<h2 class="title">${d.meaning.title}</h2>` : nothing18}
5263
+ ${d.meaning?.title ? html20`<h2 class="title">${d.meaning.title}</h2>` : nothing21}
4281
5264
  </div>
4282
5265
  </div>
4283
- ${d.meaning?.description ? html17`<p class="meaning">${d.meaning.description}</p>` : nothing18}
4284
- ${d.calculation ? html17`<pre class="calc">${d.calculation}</pre>` : nothing18}
4285
- ${keywords.length > 0 ? html17`<div class="chips">
4286
- ${keywords.map((k) => html17`<span>${k}</span>`)}
4287
- </div>` : nothing18}
4288
- ${d.hasKarmicDebt && d.karmicDebtNumber ? html17`<div class="karmic">
5266
+ ${d.meaning?.description ? html20`<p class="meaning">${d.meaning.description}</p>` : nothing21}
5267
+ ${d.calculation ? html20`<pre class="calc">${d.calculation}</pre>` : nothing21}
5268
+ ${keywords.length > 0 ? html20`<div class="chips">
5269
+ ${keywords.map((k) => html20`<span>${k}</span>`)}
5270
+ </div>` : nothing21}
5271
+ ${d.hasKarmicDebt && d.karmicDebtNumber ? html20`<div class="karmic">
4289
5272
  Karmic debt ${d.karmicDebtNumber}.
4290
5273
  ${karmicDebtText(d.karmicDebtMeaning)}
4291
- </div>` : nothing18}
5274
+ </div>` : nothing21}
4292
5275
  </article>`;
4293
5276
  }
4294
5277
  renderPersonalYear(d, headerLabel) {
4295
- return html17`<article class="card" aria-label=${headerLabel}>
5278
+ return html20`<article class="card" aria-label=${headerLabel}>
4296
5279
  <div class="hero">
4297
- ${typeof d.personalYear === "number" ? html17`<div class="numeral">${d.personalYear}</div>` : nothing18}
5280
+ ${typeof d.personalYear === "number" ? html20`<div class="numeral">${d.personalYear}</div>` : nothing21}
4298
5281
  <div>
4299
5282
  <p class="label">${headerLabel}</p>
4300
- ${d.theme ? html17`<h2 class="title">${d.theme}</h2>` : nothing18}
5283
+ ${d.theme ? html20`<h2 class="title">${d.theme}</h2>` : nothing21}
4301
5284
  </div>
4302
5285
  </div>
4303
- ${d.forecast ? html17`<p class="meaning">${d.forecast}</p>` : nothing18}
4304
- ${d.advice ? html17`<p>${d.advice}</p>` : nothing18}
5286
+ ${d.forecast ? html20`<p class="meaning">${d.forecast}</p>` : nothing21}
5287
+ ${d.advice ? html20`<p>${d.advice}</p>` : nothing21}
4305
5288
  </article>`;
4306
5289
  }
4307
5290
  renderChart(d, headerLabel) {
4308
5291
  const cores = Object.entries(d.coreNumbers).filter(
4309
5292
  ([, v]) => v !== null && v !== void 0
4310
5293
  );
4311
- return html17`<article class="card" aria-label=${headerLabel}>
5294
+ return html20`<article class="card" aria-label=${headerLabel}>
4312
5295
  <div>
4313
5296
  <p class="label">${headerLabel}</p>
4314
- ${d.profile?.name ? html17`<h2 class="title">${d.profile.name}</h2>` : nothing18}
5297
+ ${d.profile?.name ? html20`<h2 class="title">${d.profile.name}</h2>` : nothing21}
4315
5298
  </div>
4316
- ${cores.length > 0 ? html17`<div class="cores">
5299
+ ${cores.length > 0 ? html20`<div class="cores">
4317
5300
  ${cores.map(
4318
- ([k, v]) => html17`<div class="item">
5301
+ ([k, v]) => html20`<div class="item">
4319
5302
  <span>${humanize(k)}</span>
4320
5303
  <strong>${v.number ?? ""}</strong>
4321
5304
  </div>`
4322
5305
  )}
4323
- </div>` : nothing18}
5306
+ </div>` : nothing21}
4324
5307
  </article>`;
4325
5308
  }
4326
5309
  };
4327
5310
  RoxyNumerologyCard.styles = [
4328
5311
  baseStyles,
4329
- css18`
5312
+ css21`
4330
5313
  .card {
4331
5314
  background: var(--roxy-bg, #fff);
4332
5315
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -4425,13 +5408,13 @@ RoxyNumerologyCard.styles = [
4425
5408
  `
4426
5409
  ];
4427
5410
  __decorateClass([
4428
- property17({ attribute: false })
5411
+ property20({ attribute: false })
4429
5412
  ], RoxyNumerologyCard.prototype, "data", 2);
4430
5413
  __decorateClass([
4431
- property17({ type: String, reflect: true })
5414
+ property20({ type: String, reflect: true })
4432
5415
  ], RoxyNumerologyCard.prototype, "type", 2);
4433
5416
  RoxyNumerologyCard = __decorateClass([
4434
- customElement17("roxy-numerology-card")
5417
+ customElement20("roxy-numerology-card")
4435
5418
  ], RoxyNumerologyCard);
4436
5419
  var LABELS = {
4437
5420
  "life-path": "Life Path",
@@ -4445,9 +5428,9 @@ function karmicDebtText(value) {
4445
5428
  }
4446
5429
 
4447
5430
  // packages/ui/src/components/panchang-table.ts
4448
- import { css as css19, html as html18, LitElement as LitElement18, nothing as nothing19 } from "lit";
4449
- import { customElement as customElement18, property as property18 } from "lit/decorators.js";
4450
- var RoxyPanchangTable = class extends LitElement18 {
5431
+ import { css as css22, html as html21, LitElement as LitElement21, nothing as nothing22 } from "lit";
5432
+ import { customElement as customElement21, property as property21 } from "lit/decorators.js";
5433
+ var RoxyPanchangTable = class extends LitElement21 {
4451
5434
  constructor() {
4452
5435
  super(...arguments);
4453
5436
  this.data = null;
@@ -4456,7 +5439,7 @@ var RoxyPanchangTable = class extends LitElement18 {
4456
5439
  render() {
4457
5440
  const d = this.data;
4458
5441
  if (!d)
4459
- return html18`<div class="roxy-empty" role="status">No panchang data</div>`;
5442
+ return html21`<div class="roxy-empty" role="status">No panchang data</div>`;
4460
5443
  const detailed = "sunrise" in d ? d : null;
4461
5444
  const fivefold = [
4462
5445
  ["Tithi", this.formatPart(d.tithi)],
@@ -4479,7 +5462,7 @@ var RoxyPanchangTable = class extends LitElement18 {
4479
5462
  ["Yamaganda", detailed.yamaganda],
4480
5463
  ["Gulika", detailed.gulika]
4481
5464
  ] : [];
4482
- return html18`<div class="wrap" aria-label="Panchang">
5465
+ return html21`<div class="wrap" aria-label="Panchang">
4483
5466
  <header class="head">
4484
5467
  <h2 class="title">Panchang</h2>
4485
5468
  <span class="date">${detailed ? formatDate(detailed.date) : ""}</span>
@@ -4487,35 +5470,35 @@ var RoxyPanchangTable = class extends LitElement18 {
4487
5470
  <table>
4488
5471
  <tbody>
4489
5472
  ${fivefold.map(
4490
- ([k, v]) => html18`<tr>
5473
+ ([k, v]) => html21`<tr>
4491
5474
  <th>${k}</th>
4492
5475
  <td>${v}</td>
4493
5476
  </tr>`
4494
5477
  )}
4495
- ${detailed?.sunrise ? html18`<tr>
5478
+ ${detailed?.sunrise ? html21`<tr>
4496
5479
  <th>Sunrise</th>
4497
5480
  <td>${formatTime(detailed.sunrise)}</td>
4498
- </tr>` : nothing19}
4499
- ${detailed?.sunset ? html18`<tr>
5481
+ </tr>` : nothing22}
5482
+ ${detailed?.sunset ? html21`<tr>
4500
5483
  <th>Sunset</th>
4501
5484
  <td>${formatTime(detailed.sunset)}</td>
4502
- </tr>` : nothing19}
4503
- ${detailed?.moonrise ? html18`<tr>
5485
+ </tr>` : nothing22}
5486
+ ${detailed?.moonrise ? html21`<tr>
4504
5487
  <th>Moonrise</th>
4505
5488
  <td>${formatTime(detailed.moonrise)}</td>
4506
- </tr>` : nothing19}
4507
- ${detailed?.moonset ? html18`<tr>
5489
+ </tr>` : nothing22}
5490
+ ${detailed?.moonset ? html21`<tr>
4508
5491
  <th>Moonset</th>
4509
5492
  <td>${formatTime(detailed.moonset)}</td>
4510
- </tr>` : nothing19}
5493
+ </tr>` : nothing22}
4511
5494
  </tbody>
4512
5495
  </table>
4513
- ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? html18`
5496
+ ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? html21`
4514
5497
  <div class="section">Auspicious muhurtas</div>
4515
5498
  <table>
4516
5499
  <tbody>
4517
5500
  ${muhurtas.filter(([, v]) => !!v).map(
4518
- ([k, v]) => html18`<tr>
5501
+ ([k, v]) => html21`<tr>
4519
5502
  <th>${k}</th>
4520
5503
  <td>${formatTimeRange(v)}</td>
4521
5504
  </tr>`
@@ -4526,14 +5509,14 @@ var RoxyPanchangTable = class extends LitElement18 {
4526
5509
  <table>
4527
5510
  <tbody>
4528
5511
  ${inauspicious.filter(([, v]) => !!v).map(
4529
- ([k, v]) => html18`<tr>
5512
+ ([k, v]) => html21`<tr>
4530
5513
  <th>${k}</th>
4531
5514
  <td>${formatTimeRange(v)}</td>
4532
5515
  </tr>`
4533
5516
  )}
4534
5517
  </tbody>
4535
5518
  </table>
4536
- ` : nothing19}
5519
+ ` : nothing22}
4537
5520
  </div>`;
4538
5521
  }
4539
5522
  formatPart(v) {
@@ -4553,7 +5536,7 @@ var RoxyPanchangTable = class extends LitElement18 {
4553
5536
  };
4554
5537
  RoxyPanchangTable.styles = [
4555
5538
  baseStyles,
4556
- css19`
5539
+ css22`
4557
5540
  .wrap {
4558
5541
  border: 1px solid var(--roxy-border, #e4e4e7);
4559
5542
  border-radius: var(--roxy-radius-md, 8px);
@@ -4614,18 +5597,18 @@ RoxyPanchangTable.styles = [
4614
5597
  `
4615
5598
  ];
4616
5599
  __decorateClass([
4617
- property18({ attribute: false })
5600
+ property21({ attribute: false })
4618
5601
  ], RoxyPanchangTable.prototype, "data", 2);
4619
5602
  __decorateClass([
4620
- property18({ type: String, reflect: true })
5603
+ property21({ type: String, reflect: true })
4621
5604
  ], RoxyPanchangTable.prototype, "detail", 2);
4622
5605
  RoxyPanchangTable = __decorateClass([
4623
- customElement18("roxy-panchang-table")
5606
+ customElement21("roxy-panchang-table")
4624
5607
  ], RoxyPanchangTable);
4625
5608
 
4626
5609
  // packages/ui/src/components/shadbala-table.ts
4627
- import { css as css20, html as html19, LitElement as LitElement19, nothing as nothing20 } from "lit";
4628
- import { customElement as customElement19, property as property19 } from "lit/decorators.js";
5610
+ import { css as css23, html as html22, LitElement as LitElement22, nothing as nothing23 } from "lit";
5611
+ import { customElement as customElement22, property as property22 } from "lit/decorators.js";
4629
5612
  var BALA_COMPONENTS = [
4630
5613
  { key: "sthanaBala", label: "Sthana", color: "var(--roxy-info, #0284c7)" },
4631
5614
  { key: "digBala", label: "Dig", color: "var(--roxy-success, #16a34a)" },
@@ -4638,19 +5621,19 @@ var BALA_COMPONENTS = [
4638
5621
  },
4639
5622
  { key: "drikBala", label: "Drik", color: "var(--roxy-danger, #dc2626)" }
4640
5623
  ];
4641
- var RoxyShadbalaTable = class extends LitElement19 {
5624
+ var RoxyShadbalaTable = class extends LitElement22 {
4642
5625
  constructor() {
4643
5626
  super(...arguments);
4644
5627
  this.data = null;
4645
5628
  }
4646
5629
  render() {
4647
5630
  if (!this.data?.planets?.length) {
4648
- return html19`<div class="roxy-empty" role="status">No shadbala data</div>`;
5631
+ return html22`<div class="roxy-empty" role="status">No shadbala data</div>`;
4649
5632
  }
4650
5633
  const sorted = [...this.data.planets].sort(
4651
5634
  (a, b) => a.relativeRank - b.relativeRank
4652
5635
  );
4653
- return html19`<div class="wrap" aria-label="Shadbala planetary strength">
5636
+ return html22`<div class="wrap" aria-label="Shadbala planetary strength">
4654
5637
  <div class="head">
4655
5638
  <h2 class="title">Shadbala</h2>
4656
5639
  <p class="subtitle">${sorted.length} planets ranked by strength</p>
@@ -4662,7 +5645,7 @@ var RoxyShadbalaTable = class extends LitElement19 {
4662
5645
 
4663
5646
  <div class="legend" aria-label="Strength component legend">
4664
5647
  ${BALA_COMPONENTS.map(
4665
- (b) => html19`<div class="legend-row">
5648
+ (b) => html22`<div class="legend-row">
4666
5649
  <span
4667
5650
  class="legend-swatch"
4668
5651
  style="background: ${b.color}"
@@ -4682,7 +5665,7 @@ var RoxyShadbalaTable = class extends LitElement19 {
4682
5665
  const badgeClass = isAdequate ? "adequacy-badge--adequate" : "adequacy-badge--weak";
4683
5666
  const badgeLabel = isAdequate ? "adequate" : "weak";
4684
5667
  const rupasStr = formatNumber(p.totalRupas, 2) && formatNumber(p.minRequired, 2) ? `${formatNumber(p.totalRupas, 2)} / ${formatNumber(p.minRequired, 2)} R` : "";
4685
- return html19`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
5668
+ return html22`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
4686
5669
  <div class="planet-label">
4687
5670
  <span class="glyph" aria-hidden="true">${glyph}</span>
4688
5671
  ${p.planet}
@@ -4692,18 +5675,18 @@ var RoxyShadbalaTable = class extends LitElement19 {
4692
5675
  <div class="bar" role="img" aria-label="Strength components for ${p.planet}">
4693
5676
  ${total > 0 ? BALA_COMPONENTS.map((b, i) => {
4694
5677
  const v = values[i];
4695
- if (v <= 0) return nothing20;
5678
+ if (v <= 0) return nothing23;
4696
5679
  const grow = v / total * 100;
4697
- return html19`<div
5680
+ return html22`<div
4698
5681
  class="bar-segment"
4699
5682
  style="flex-grow: ${grow}; background: ${b.color};"
4700
5683
  title="${b.label}: ${formatNumber(v, 1)}"
4701
5684
  ></div>`;
4702
- }) : nothing20}
5685
+ }) : nothing23}
4703
5686
  </div>
4704
5687
  </div>
4705
5688
  <div class="pills">
4706
- ${rupasStr ? html19`<span class="rupas-label">${rupasStr}</span>` : nothing20}
5689
+ ${rupasStr ? html22`<span class="rupas-label">${rupasStr}</span>` : nothing23}
4707
5690
  <span class="${`adequacy-badge ${badgeClass}`}">${badgeLabel}</span>
4708
5691
  </div>
4709
5692
  </div>`;
@@ -4711,7 +5694,7 @@ var RoxyShadbalaTable = class extends LitElement19 {
4711
5694
  };
4712
5695
  RoxyShadbalaTable.styles = [
4713
5696
  baseStyles,
4714
- css20`
5697
+ css23`
4715
5698
  .wrap {
4716
5699
  display: grid;
4717
5700
  gap: var(--roxy-space-md, 1rem);
@@ -4860,29 +5843,29 @@ RoxyShadbalaTable.styles = [
4860
5843
  `
4861
5844
  ];
4862
5845
  __decorateClass([
4863
- property19({ attribute: false })
5846
+ property22({ attribute: false })
4864
5847
  ], RoxyShadbalaTable.prototype, "data", 2);
4865
5848
  RoxyShadbalaTable = __decorateClass([
4866
- customElement19("roxy-shadbala-table")
5849
+ customElement22("roxy-shadbala-table")
4867
5850
  ], RoxyShadbalaTable);
4868
5851
 
4869
5852
  // packages/ui/src/components/synastry-chart.ts
4870
- import { css as css21, html as html20, LitElement as LitElement20, nothing as nothing21, svg as svg5 } from "lit";
4871
- import { customElement as customElement20, property as property20 } from "lit/decorators.js";
5853
+ import { css as css24, html as html23, LitElement as LitElement23, nothing as nothing24, svg as svg5 } from "lit";
5854
+ import { customElement as customElement23, property as property23 } from "lit/decorators.js";
4872
5855
  var SIZE2 = 360;
4873
5856
  var CENTER2 = SIZE2 / 2;
4874
5857
  var OUTER_R2 = 170;
4875
5858
  var SIGN_R2 = 154;
4876
5859
  var P1_R = 124;
4877
5860
  var P2_R = 96;
4878
- var RoxySynastryChart = class extends LitElement20 {
5861
+ var RoxySynastryChart = class extends LitElement23 {
4879
5862
  constructor() {
4880
5863
  super(...arguments);
4881
5864
  this.data = null;
4882
5865
  }
4883
5866
  render() {
4884
5867
  if (!this.data)
4885
- return html20`<div class="roxy-empty" role="status">No synastry data</div>`;
5868
+ return html23`<div class="roxy-empty" role="status">No synastry data</div>`;
4886
5869
  const { person1, person2, compatibilityScore, analysis } = this.data;
4887
5870
  const interAspects = this.data.interAspects ?? [];
4888
5871
  const p1Planets = person1?.planets ?? [];
@@ -4893,15 +5876,15 @@ var RoxySynastryChart = class extends LitElement20 {
4893
5876
  const challenges = analysis?.challenges ?? [];
4894
5877
  const hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;
4895
5878
  if (!hasPlanets) {
4896
- return html20`<div
5879
+ return html23`<div
4897
5880
  class="wrap"
4898
5881
  aria-label="Synastry compatibility chart"
4899
5882
  >
4900
5883
  <div class="head">
4901
5884
  <h2 class="title">Synastry</h2>
4902
- ${typeof score === "number" ? html20`<span class="score" aria-label=${`Score ${score} of 100`}
5885
+ ${typeof score === "number" ? html23`<span class="score" aria-label=${`Score ${score} of 100`}
4903
5886
  >${score} / 100</span
4904
- >` : nothing21}
5887
+ >` : nothing24}
4905
5888
  </div>
4906
5889
  <div class="missing-planets" role="status">
4907
5890
  Synastry response missing planet positions. Pass
@@ -4909,33 +5892,33 @@ var RoxySynastryChart = class extends LitElement20 {
4909
5892
  <code>person2.planets</code> arrays from the natal-chart endpoint, or
4910
5893
  use the <code>&lt;roxy-data&gt;</code> fallback.
4911
5894
  </div>
4912
- ${summaryText ? html20`<p class="summary">${summaryText}</p>` : nothing21}
4913
- ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing21}
4914
- ${strengths.length > 0 || challenges.length > 0 ? html20`<div class="lists">
4915
- ${strengths.length ? html20`<div>
5895
+ ${summaryText ? html23`<p class="summary">${summaryText}</p>` : nothing24}
5896
+ ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing24}
5897
+ ${strengths.length > 0 || challenges.length > 0 ? html23`<div class="lists">
5898
+ ${strengths.length ? html23`<div>
4916
5899
  <h3>Strengths</h3>
4917
5900
  <ul>
4918
- ${strengths.map((s) => html20`<li>${s}</li>`)}
5901
+ ${strengths.map((s) => html23`<li>${s}</li>`)}
4919
5902
  </ul>
4920
- </div>` : nothing21}
4921
- ${challenges.length ? html20`<div>
5903
+ </div>` : nothing24}
5904
+ ${challenges.length ? html23`<div>
4922
5905
  <h3>Challenges</h3>
4923
5906
  <ul>
4924
- ${challenges.map((s) => html20`<li>${s}</li>`)}
5907
+ ${challenges.map((s) => html23`<li>${s}</li>`)}
4925
5908
  </ul>
4926
- </div>` : nothing21}
4927
- </div>` : nothing21}
5909
+ </div>` : nothing24}
5910
+ </div>` : nothing24}
4928
5911
  </div>`;
4929
5912
  }
4930
- return html20`<div
5913
+ return html23`<div
4931
5914
  class="wrap"
4932
5915
  aria-label="Synastry compatibility chart"
4933
5916
  >
4934
5917
  <div class="head">
4935
5918
  <h2 class="title">Synastry</h2>
4936
- ${typeof score === "number" ? html20`<span class="score" aria-label=${`Score ${score} of 100`}
5919
+ ${typeof score === "number" ? html23`<span class="score" aria-label=${`Score ${score} of 100`}
4937
5920
  >${score} / 100</span
4938
- >` : nothing21}
5921
+ >` : nothing24}
4939
5922
  </div>
4940
5923
  <svg
4941
5924
  viewBox="0 0 ${SIZE2} ${SIZE2}"
@@ -4974,22 +5957,22 @@ var RoxySynastryChart = class extends LitElement20 {
4974
5957
  <span><span class="swatch" style="background: var(--roxy-success)"></span>harmonious</span>
4975
5958
  <span><span class="swatch" style="background: var(--roxy-danger)"></span>challenging</span>
4976
5959
  </div>
4977
- ${summaryText ? html20`<p class="summary">${summaryText}</p>` : nothing21}
4978
- ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing21}
4979
- ${strengths.length > 0 || challenges.length > 0 ? html20`<div class="lists">
4980
- ${strengths.length ? html20`<div>
5960
+ ${summaryText ? html23`<p class="summary">${summaryText}</p>` : nothing24}
5961
+ ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing24}
5962
+ ${strengths.length > 0 || challenges.length > 0 ? html23`<div class="lists">
5963
+ ${strengths.length ? html23`<div>
4981
5964
  <h3>Strengths</h3>
4982
5965
  <ul>
4983
- ${strengths.map((s) => html20`<li>${s}</li>`)}
5966
+ ${strengths.map((s) => html23`<li>${s}</li>`)}
4984
5967
  </ul>
4985
- </div>` : nothing21}
4986
- ${challenges.length ? html20`<div>
5968
+ </div>` : nothing24}
5969
+ ${challenges.length ? html23`<div>
4987
5970
  <h3>Challenges</h3>
4988
5971
  <ul>
4989
- ${challenges.map((s) => html20`<li>${s}</li>`)}
5972
+ ${challenges.map((s) => html23`<li>${s}</li>`)}
4990
5973
  </ul>
4991
- </div>` : nothing21}
4992
- </div>` : nothing21}
5974
+ </div>` : nothing24}
5975
+ </div>` : nothing24}
4993
5976
  </div>`;
4994
5977
  }
4995
5978
  toAngle(longitude) {
@@ -5012,7 +5995,7 @@ var RoxySynastryChart = class extends LitElement20 {
5012
5995
  }
5013
5996
  renderRing(planets, radius, cls) {
5014
5997
  return planets.map((p) => {
5015
- if (!Number.isFinite(p.longitude)) return nothing21;
5998
+ if (!Number.isFinite(p.longitude)) return nothing24;
5016
5999
  const pos = polarToCartesian(
5017
6000
  CENTER2,
5018
6001
  CENTER2,
@@ -5035,7 +6018,7 @@ var RoxySynastryChart = class extends LitElement20 {
5035
6018
  return aspects.map((a) => {
5036
6019
  const l1 = longitudeOf(p1, a.planet1);
5037
6020
  const l2 = longitudeOf(p2, a.planet2);
5038
- if (l1 === void 0 || l2 === void 0) return nothing21;
6021
+ if (l1 === void 0 || l2 === void 0) return nothing24;
5039
6022
  const out = polarToCartesian(CENTER2, CENTER2, P1_R - 12, this.toAngle(l1));
5040
6023
  const inn = polarToCartesian(CENTER2, CENTER2, P2_R + 8, this.toAngle(l2));
5041
6024
  const aspectName = normalizeAspect(a);
@@ -5045,7 +6028,7 @@ var RoxySynastryChart = class extends LitElement20 {
5045
6028
  });
5046
6029
  }
5047
6030
  renderAspects(aspects) {
5048
- return html20`<table>
6031
+ return html23`<table>
5049
6032
  <thead>
5050
6033
  <tr>
5051
6034
  <th>Planet 1</th>
@@ -5057,7 +6040,7 @@ var RoxySynastryChart = class extends LitElement20 {
5057
6040
  </thead>
5058
6041
  <tbody>
5059
6042
  ${aspects.slice(0, 12).map(
5060
- (a) => html20`<tr>
6043
+ (a) => html23`<tr>
5061
6044
  <td>${a.planet1}</td>
5062
6045
  <td>${a.planet2}</td>
5063
6046
  <td>${normalizeAspect(a) || ""}</td>
@@ -5071,7 +6054,7 @@ var RoxySynastryChart = class extends LitElement20 {
5071
6054
  };
5072
6055
  RoxySynastryChart.styles = [
5073
6056
  baseStyles,
5074
- css21`
6057
+ css24`
5075
6058
  .wrap {
5076
6059
  display: grid;
5077
6060
  gap: var(--roxy-space-md, 1rem);
@@ -5226,10 +6209,10 @@ RoxySynastryChart.styles = [
5226
6209
  `
5227
6210
  ];
5228
6211
  __decorateClass([
5229
- property20({ attribute: false })
6212
+ property23({ attribute: false })
5230
6213
  ], RoxySynastryChart.prototype, "data", 2);
5231
6214
  RoxySynastryChart = __decorateClass([
5232
- customElement20("roxy-synastry-chart")
6215
+ customElement23("roxy-synastry-chart")
5233
6216
  ], RoxySynastryChart);
5234
6217
  function formatStrength(s) {
5235
6218
  if (typeof s === "number") return Math.round(s).toString();
@@ -5237,9 +6220,9 @@ function formatStrength(s) {
5237
6220
  }
5238
6221
 
5239
6222
  // packages/ui/src/components/tarot-card.ts
5240
- import { css as css22, html as html21, LitElement as LitElement21, nothing as nothing22 } from "lit";
5241
- import { customElement as customElement21, property as property21, state as state4 } from "lit/decorators.js";
5242
- var RoxyTarotCard = class extends LitElement21 {
6223
+ import { css as css25, html as html24, LitElement as LitElement24, nothing as nothing25 } from "lit";
6224
+ import { customElement as customElement24, property as property24, state as state6 } from "lit/decorators.js";
6225
+ var RoxyTarotCard = class extends LitElement24 {
5243
6226
  constructor() {
5244
6227
  super(...arguments);
5245
6228
  this.data = null;
@@ -5251,7 +6234,7 @@ var RoxyTarotCard = class extends LitElement21 {
5251
6234
  render() {
5252
6235
  const d = this.data;
5253
6236
  if (!d)
5254
- return html21`<div class="roxy-empty" role="status">No tarot data</div>`;
6237
+ return html24`<div class="roxy-empty" role="status">No tarot data</div>`;
5255
6238
  if ("card" in d) return this.renderDailyCard(d);
5256
6239
  return this.renderFullCard(d);
5257
6240
  }
@@ -5259,9 +6242,9 @@ var RoxyTarotCard = class extends LitElement21 {
5259
6242
  const card = d.card;
5260
6243
  const isReversed = this.flipped !== Boolean(card.reversed);
5261
6244
  const keywords = card.keywords ?? [];
5262
- return html21`<article class="card" aria-label=${card.name ?? "Tarot card"}>
6245
+ return html24`<article class="card" aria-label=${card.name ?? "Tarot card"}>
5263
6246
  <div class="image-wrap">
5264
- ${card.imageUrl ? html21`<img
6247
+ ${card.imageUrl ? html24`<img
5265
6248
  class=${`image ${isReversed ? "reversed" : ""}`}
5266
6249
  src=${card.imageUrl}
5267
6250
  alt=${card.name ?? "Tarot card"}
@@ -5273,7 +6256,7 @@ var RoxyTarotCard = class extends LitElement21 {
5273
6256
  this.toggleFlip();
5274
6257
  }
5275
6258
  }}
5276
- />` : html21`<div
6259
+ />` : html24`<div
5277
6260
  class=${`image ${isReversed ? "reversed" : ""}`}
5278
6261
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
5279
6262
  >
@@ -5282,15 +6265,15 @@ var RoxyTarotCard = class extends LitElement21 {
5282
6265
  </div>
5283
6266
  <div>
5284
6267
  <div class="meta">
5285
- ${card.arcana ? html21`${card.arcana} arcana` : nothing22}
5286
- ${isReversed ? html21` · reversed` : nothing22}
6268
+ ${card.arcana ? html24`${card.arcana} arcana` : nothing25}
6269
+ ${isReversed ? html24` · reversed` : nothing25}
5287
6270
  </div>
5288
6271
  <h2 class="title">${card.name ?? "Tarot card"}</h2>
5289
- ${d.dailyMessage ? html21`<p class="message">${d.dailyMessage}</p>` : nothing22}
5290
- ${card.meaning ? html21`<p>${card.meaning}</p>` : nothing22}
5291
- ${keywords.length > 0 ? html21`<div class="chips">
5292
- ${keywords.map((k) => html21`<span>${k}</span>`)}
5293
- </div>` : nothing22}
6272
+ ${d.dailyMessage ? html24`<p class="message">${d.dailyMessage}</p>` : nothing25}
6273
+ ${card.meaning ? html24`<p>${card.meaning}</p>` : nothing25}
6274
+ ${keywords.length > 0 ? html24`<div class="chips">
6275
+ ${keywords.map((k) => html24`<span>${k}</span>`)}
6276
+ </div>` : nothing25}
5294
6277
  <button
5295
6278
  class="flip"
5296
6279
  type="button"
@@ -5306,9 +6289,9 @@ var RoxyTarotCard = class extends LitElement21 {
5306
6289
  const isReversed = this.flipped;
5307
6290
  const orientedMeaning = isReversed ? d.reversed : d.upright;
5308
6291
  const keywords = isReversed ? d.keywords?.reversed ?? [] : d.keywords?.upright ?? [];
5309
- return html21`<article class="card" aria-label=${d.name ?? "Tarot card"}>
6292
+ return html24`<article class="card" aria-label=${d.name ?? "Tarot card"}>
5310
6293
  <div class="image-wrap">
5311
- ${d.imageUrl ? html21`<img
6294
+ ${d.imageUrl ? html24`<img
5312
6295
  class=${`image ${isReversed ? "reversed" : ""}`}
5313
6296
  src=${d.imageUrl}
5314
6297
  alt=${d.name ?? "Tarot card"}
@@ -5320,7 +6303,7 @@ var RoxyTarotCard = class extends LitElement21 {
5320
6303
  this.toggleFlip();
5321
6304
  }
5322
6305
  }}
5323
- />` : html21`<div
6306
+ />` : html24`<div
5324
6307
  class=${`image ${isReversed ? "reversed" : ""}`}
5325
6308
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
5326
6309
  >
@@ -5329,15 +6312,15 @@ var RoxyTarotCard = class extends LitElement21 {
5329
6312
  </div>
5330
6313
  <div>
5331
6314
  <div class="meta">
5332
- ${d.arcana ? html21`${d.arcana} arcana` : nothing22}
5333
- ${d.number !== void 0 && d.number !== null ? html21` · ${d.number}` : nothing22}
5334
- ${isReversed ? html21` · reversed` : nothing22}
6315
+ ${d.arcana ? html24`${d.arcana} arcana` : nothing25}
6316
+ ${d.number !== void 0 && d.number !== null ? html24` · ${d.number}` : nothing25}
6317
+ ${isReversed ? html24` · reversed` : nothing25}
5335
6318
  </div>
5336
6319
  <h2 class="title">${d.name ?? "Tarot card"}</h2>
5337
- ${orientedMeaning?.description ? html21`<p>${orientedMeaning.description}</p>` : nothing22}
5338
- ${keywords.length > 0 ? html21`<div class="chips">
5339
- ${keywords.map((k) => html21`<span>${k}</span>`)}
5340
- </div>` : nothing22}
6320
+ ${orientedMeaning?.description ? html24`<p>${orientedMeaning.description}</p>` : nothing25}
6321
+ ${keywords.length > 0 ? html24`<div class="chips">
6322
+ ${keywords.map((k) => html24`<span>${k}</span>`)}
6323
+ </div>` : nothing25}
5341
6324
  <button
5342
6325
  class="flip"
5343
6326
  type="button"
@@ -5352,7 +6335,7 @@ var RoxyTarotCard = class extends LitElement21 {
5352
6335
  };
5353
6336
  RoxyTarotCard.styles = [
5354
6337
  baseStyles,
5355
- css22`
6338
+ css25`
5356
6339
  .card {
5357
6340
  background: var(--roxy-bg, #fff);
5358
6341
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -5445,19 +6428,19 @@ RoxyTarotCard.styles = [
5445
6428
  `
5446
6429
  ];
5447
6430
  __decorateClass([
5448
- property21({ attribute: false })
6431
+ property24({ attribute: false })
5449
6432
  ], RoxyTarotCard.prototype, "data", 2);
5450
6433
  __decorateClass([
5451
- state4()
6434
+ state6()
5452
6435
  ], RoxyTarotCard.prototype, "flipped", 2);
5453
6436
  RoxyTarotCard = __decorateClass([
5454
- customElement21("roxy-tarot-card")
6437
+ customElement24("roxy-tarot-card")
5455
6438
  ], RoxyTarotCard);
5456
6439
 
5457
6440
  // packages/ui/src/components/tarot-spread.ts
5458
- import { css as css23, html as html22, LitElement as LitElement22, nothing as nothing23 } from "lit";
5459
- import { customElement as customElement22, property as property22 } from "lit/decorators.js";
5460
- var RoxyTarotSpread = class extends LitElement22 {
6441
+ import { css as css26, html as html25, LitElement as LitElement25, nothing as nothing26 } from "lit";
6442
+ import { customElement as customElement25, property as property25 } from "lit/decorators.js";
6443
+ var RoxyTarotSpread = class extends LitElement25 {
5461
6444
  constructor() {
5462
6445
  super(...arguments);
5463
6446
  this.data = null;
@@ -5466,7 +6449,7 @@ var RoxyTarotSpread = class extends LitElement22 {
5466
6449
  render() {
5467
6450
  const d = this.data;
5468
6451
  if (!d)
5469
- return html22`<div class="roxy-empty" role="status">No tarot spread</div>`;
6452
+ return html25`<div class="roxy-empty" role="status">No tarot spread</div>`;
5470
6453
  const isYesNo = "answer" in d;
5471
6454
  const isDrawn = "cards" in d && !("spread" in d);
5472
6455
  const positions = isDrawn ? [] : "positions" in d ? d.positions ?? [] : [];
@@ -5478,60 +6461,60 @@ var RoxyTarotSpread = class extends LitElement22 {
5478
6461
  const summary = "summary" in d ? d.summary : void 0;
5479
6462
  const yesNoInterp = isYesNo ? d.interpretation : void 0;
5480
6463
  const answerClass = answer ? answer.toLowerCase().replace(/[^a-z]/g, "") : "";
5481
- return html22`<article class="wrap" aria-label="Tarot spread">
6464
+ return html25`<article class="wrap" aria-label="Tarot spread">
5482
6465
  <header class="head">
5483
6466
  <h2 class="title">${spreadLabel}</h2>
5484
- ${question ? html22`<span class="question">"${question}"</span>` : nothing23}
6467
+ ${question ? html25`<span class="question">"${question}"</span>` : nothing26}
5485
6468
  </header>
5486
- ${isYesNo ? html22`<div>
6469
+ ${isYesNo ? html25`<div>
5487
6470
  <span class=${`answer ${answerClass}`}>${answer}</span>
5488
- ${strength ? html22`<small> · ${strength}</small>` : nothing23}
5489
- </div>` : nothing23}
5490
- ${positions.length > 0 ? html22`<div class="grid">
6471
+ ${strength ? html25`<small> · ${strength}</small>` : nothing26}
6472
+ </div>` : nothing26}
6473
+ ${positions.length > 0 ? html25`<div class="grid">
5491
6474
  ${positions.map(
5492
- (p) => html22`<div class="card">
6475
+ (p) => html25`<div class="card">
5493
6476
  <p class="label">${p.name ?? ""}</p>
5494
6477
  <div class="image">
5495
- ${p.card?.imageUrl ? html22`<img
6478
+ ${p.card?.imageUrl ? html25`<img
5496
6479
  src=${p.card.imageUrl}
5497
6480
  alt=${p.card.name ?? "tarot card"}
5498
6481
  class=${p.card.reversed ? "reversed" : ""}
5499
- />` : html22`${p.card?.name ?? "?"}`}
6482
+ />` : html25`${p.card?.name ?? "?"}`}
5500
6483
  </div>
5501
6484
  <p class="name">
5502
6485
  ${p.card?.name ?? ""}
5503
- ${p.card?.reversed ? html22`<small>(reversed)</small>` : nothing23}
6486
+ ${p.card?.reversed ? html25`<small>(reversed)</small>` : nothing26}
5504
6487
  </p>
5505
- ${p.interpretation ? html22`<p class="interp">${p.interpretation}</p>` : nothing23}
6488
+ ${p.interpretation ? html25`<p class="interp">${p.interpretation}</p>` : nothing26}
5506
6489
  </div>`
5507
6490
  )}
5508
- </div>` : nothing23}
5509
- ${cards.length > 0 ? html22`<div class="grid">
6491
+ </div>` : nothing26}
6492
+ ${cards.length > 0 ? html25`<div class="grid">
5510
6493
  ${cards.map(
5511
- (c) => html22`<div class="card">
6494
+ (c) => html25`<div class="card">
5512
6495
  <div class="image">
5513
- ${c.imageUrl ? html22`<img
6496
+ ${c.imageUrl ? html25`<img
5514
6497
  src=${c.imageUrl}
5515
6498
  alt=${c.name ?? "tarot card"}
5516
6499
  class=${c.reversed ? "reversed" : ""}
5517
- />` : html22`${c.name ?? "?"}`}
6500
+ />` : html25`${c.name ?? "?"}`}
5518
6501
  </div>
5519
6502
  <p class="name">
5520
6503
  ${c.name ?? ""}
5521
- ${c.reversed ? html22`<small>(reversed)</small>` : nothing23}
6504
+ ${c.reversed ? html25`<small>(reversed)</small>` : nothing26}
5522
6505
  </p>
5523
- ${c.meaning ? html22`<p class="interp">${c.meaning}</p>` : nothing23}
6506
+ ${c.meaning ? html25`<p class="interp">${c.meaning}</p>` : nothing26}
5524
6507
  </div>`
5525
6508
  )}
5526
- </div>` : nothing23}
5527
- ${summary ? html22`<p class="reading">${summary}</p>` : nothing23}
5528
- ${yesNoInterp ? html22`<p class="reading">${yesNoInterp}</p>` : nothing23}
6509
+ </div>` : nothing26}
6510
+ ${summary ? html25`<p class="reading">${summary}</p>` : nothing26}
6511
+ ${yesNoInterp ? html25`<p class="reading">${yesNoInterp}</p>` : nothing26}
5529
6512
  </article>`;
5530
6513
  }
5531
6514
  };
5532
6515
  RoxyTarotSpread.styles = [
5533
6516
  baseStyles,
5534
- css23`
6517
+ css26`
5535
6518
  .wrap {
5536
6519
  display: grid;
5537
6520
  gap: var(--roxy-space-md, 1rem);
@@ -5640,26 +6623,26 @@ RoxyTarotSpread.styles = [
5640
6623
  `
5641
6624
  ];
5642
6625
  __decorateClass([
5643
- property22({ attribute: false })
6626
+ property25({ attribute: false })
5644
6627
  ], RoxyTarotSpread.prototype, "data", 2);
5645
6628
  __decorateClass([
5646
- property22({ type: String, reflect: true })
6629
+ property25({ type: String, reflect: true })
5647
6630
  ], RoxyTarotSpread.prototype, "spread", 2);
5648
6631
  RoxyTarotSpread = __decorateClass([
5649
- customElement22("roxy-tarot-spread")
6632
+ customElement25("roxy-tarot-spread")
5650
6633
  ], RoxyTarotSpread);
5651
6634
 
5652
6635
  // packages/ui/src/components/transits-table.ts
5653
- import { css as css24, html as html23, LitElement as LitElement23, nothing as nothing24 } from "lit";
5654
- import { customElement as customElement23, property as property23 } from "lit/decorators.js";
5655
- var RoxyTransitsTable = class extends LitElement23 {
6636
+ import { css as css27, html as html26, LitElement as LitElement26, nothing as nothing27 } from "lit";
6637
+ import { customElement as customElement26, property as property26 } from "lit/decorators.js";
6638
+ var RoxyTransitsTable = class extends LitElement26 {
5656
6639
  constructor() {
5657
6640
  super(...arguments);
5658
6641
  this.data = null;
5659
6642
  }
5660
6643
  render() {
5661
6644
  if (!this.data?.transitPlanets?.length) {
5662
- return html23`<div class="roxy-empty" role="status">No transits data</div>`;
6645
+ return html26`<div class="roxy-empty" role="status">No transits data</div>`;
5663
6646
  }
5664
6647
  const {
5665
6648
  transitDate,
@@ -5669,13 +6652,13 @@ var RoxyTransitsTable = class extends LitElement23 {
5669
6652
  summary
5670
6653
  } = this.data;
5671
6654
  const dateStr = [formatDate(transitDate), formatTime(transitTime)].filter(Boolean).join(" ");
5672
- return html23`<div class="wrap" aria-label="Transit positions table">
6655
+ return html26`<div class="wrap" aria-label="Transit positions table">
5673
6656
  <div class="head">
5674
6657
  <h2 class="title">Transits</h2>
5675
- ${dateStr ? html23`<p class="subtitle">${dateStr}</p>` : nothing24}
6658
+ ${dateStr ? html26`<p class="subtitle">${dateStr}</p>` : nothing27}
5676
6659
  </div>
5677
6660
 
5678
- ${summary ? this.renderSummaryPills(summary) : nothing24}
6661
+ ${summary ? this.renderSummaryPills(summary) : nothing27}
5679
6662
 
5680
6663
  <div>
5681
6664
  <p class="section-label">Planet positions</p>
@@ -5684,16 +6667,16 @@ var RoxyTransitsTable = class extends LitElement23 {
5684
6667
  </div>
5685
6668
  </div>
5686
6669
 
5687
- ${transitAspects?.length ? html23`<div>
6670
+ ${transitAspects?.length ? html26`<div>
5688
6671
  <p class="section-label">Transit aspects</p>
5689
6672
  <div class="overflow-scroll">
5690
6673
  ${this.renderAspectsList(transitAspects)}
5691
6674
  </div>
5692
- </div>` : nothing24}
6675
+ </div>` : nothing27}
5693
6676
  </div>`;
5694
6677
  }
5695
6678
  renderSummaryPills(summary) {
5696
- return html23`<div class="summary-pills" role="region" aria-label="Aspect summary">
6679
+ return html26`<div class="summary-pills" role="region" aria-label="Aspect summary">
5697
6680
  <span class="pill pill--muted">
5698
6681
  Total: ${summary.totalAspects}
5699
6682
  </span>
@@ -5709,7 +6692,7 @@ var RoxyTransitsTable = class extends LitElement23 {
5709
6692
  </div>`;
5710
6693
  }
5711
6694
  renderPlanetsTable(planets) {
5712
- return html23`<table class="planets-table">
6695
+ return html26`<table class="planets-table">
5713
6696
  <thead>
5714
6697
  <tr>
5715
6698
  <th scope="col">Planet</th>
@@ -5723,12 +6706,12 @@ var RoxyTransitsTable = class extends LitElement23 {
5723
6706
  const pGlyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
5724
6707
  const sGlyph = SIGN_GLYPH[capitalize(p.sign)] ?? "";
5725
6708
  const speedArrow = p.speed >= 0 ? "\u2191" : "\u2193";
5726
- return html23`<tr>
6709
+ return html26`<tr>
5727
6710
  <td>
5728
6711
  <div class="planet-cell">
5729
6712
  <span class="glyph" aria-hidden="true">${pGlyph}</span>
5730
6713
  ${p.name}
5731
- ${p.isRetrograde ? html23`<span class="retro-badge" aria-label="retrograde">R</span>` : nothing24}
6714
+ ${p.isRetrograde ? html26`<span class="retro-badge" aria-label="retrograde">R</span>` : nothing27}
5732
6715
  </div>
5733
6716
  </td>
5734
6717
  <td>
@@ -5748,7 +6731,7 @@ var RoxyTransitsTable = class extends LitElement23 {
5748
6731
  </table>`;
5749
6732
  }
5750
6733
  renderAspectsList(aspects) {
5751
- return html23`<div role="list" aria-label="Transit aspects">
6734
+ return html26`<div role="list" aria-label="Transit aspects">
5752
6735
  ${aspects.map((a, idx) => {
5753
6736
  const tGlyph = PLANET_GLYPH[capitalize(a.transitPlanet)] ?? "";
5754
6737
  const nGlyph = PLANET_GLYPH[capitalize(a.natalPlanet)] ?? "";
@@ -5756,7 +6739,7 @@ var RoxyTransitsTable = class extends LitElement23 {
5756
6739
  const interp = a.interpretation;
5757
6740
  const type = (a.type ?? "").toLowerCase();
5758
6741
  const status = a.isApplying ? "Applying" : "Separating";
5759
- return html23`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
6742
+ return html26`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
5760
6743
  <summary>
5761
6744
  <span aria-hidden="true">${tGlyph}</span>
5762
6745
  ${a.transitPlanet}
@@ -5768,13 +6751,13 @@ var RoxyTransitsTable = class extends LitElement23 {
5768
6751
  </span>
5769
6752
  </summary>
5770
6753
  <div class="interp-body">
5771
- ${interp?.summary ? html23`<p>${interp.summary}</p>` : nothing24}
5772
- ${interp?.impact ? html23`<p><strong>Impact:</strong> ${interp.impact}</p>` : nothing24}
5773
- ${interp?.timing ? html23`<p><strong>Timing:</strong> ${interp.timing}</p>` : nothing24}
5774
- ${interp?.guidance ? html23`<p><strong>Guidance:</strong> ${interp.guidance}</p>` : nothing24}
5775
- ${interp?.keywords?.length ? html23`<div class="interp-keywords">
5776
- ${interp.keywords.map((k) => html23`<span class="kw">${k}</span>`)}
5777
- </div>` : nothing24}
6754
+ ${interp?.summary ? html26`<p>${interp.summary}</p>` : nothing27}
6755
+ ${interp?.impact ? html26`<p><strong>Impact:</strong> ${interp.impact}</p>` : nothing27}
6756
+ ${interp?.timing ? html26`<p><strong>Timing:</strong> ${interp.timing}</p>` : nothing27}
6757
+ ${interp?.guidance ? html26`<p><strong>Guidance:</strong> ${interp.guidance}</p>` : nothing27}
6758
+ ${interp?.keywords?.length ? html26`<div class="interp-keywords">
6759
+ ${interp.keywords.map((k) => html26`<span class="kw">${k}</span>`)}
6760
+ </div>` : nothing27}
5778
6761
  </div>
5779
6762
  </details>`;
5780
6763
  })}
@@ -5783,7 +6766,7 @@ var RoxyTransitsTable = class extends LitElement23 {
5783
6766
  };
5784
6767
  RoxyTransitsTable.styles = [
5785
6768
  baseStyles,
5786
- css24`
6769
+ css27`
5787
6770
  .wrap {
5788
6771
  display: grid;
5789
6772
  gap: var(--roxy-space-md, 1rem);
@@ -5982,46 +6965,33 @@ RoxyTransitsTable.styles = [
5982
6965
  `
5983
6966
  ];
5984
6967
  __decorateClass([
5985
- property23({ attribute: false })
6968
+ property26({ attribute: false })
5986
6969
  ], RoxyTransitsTable.prototype, "data", 2);
5987
6970
  RoxyTransitsTable = __decorateClass([
5988
- customElement23("roxy-transits-table")
6971
+ customElement26("roxy-transits-table")
5989
6972
  ], RoxyTransitsTable);
5990
6973
 
5991
6974
  // packages/ui/src/components/vedic-kundli.ts
5992
- import { css as css25, html as html24, LitElement as LitElement24 } from "lit";
5993
- import { customElement as customElement24, property as property24 } from "lit/decorators.js";
5994
- var RoxyVedicKundli = class extends LitElement24 {
6975
+ import { css as css28, html as html27, LitElement as LitElement27 } from "lit";
6976
+ import { customElement as customElement27, property as property27 } from "lit/decorators.js";
6977
+ var RoxyVedicKundli = class extends LitElement27 {
5995
6978
  constructor() {
5996
6979
  super(...arguments);
5997
6980
  this.data = null;
5998
6981
  this.chartStyle = "south";
5999
6982
  }
6000
6983
  buildHouses() {
6001
- if (!this.data) return [];
6002
- const data = this.data;
6003
- const lagnaSign = this.data?.meta?.Lagna?.rashi ?? "";
6004
- const houses = [];
6005
- for (let i = 0; i < 12; i++) {
6006
- const key = RASHI_KEYS[i];
6007
- const bucket = data[key];
6008
- const planets = (bucket?.signs ?? []).map((p) => p.graha).filter(Boolean);
6009
- const sign = RASHI_TO_SIGN[key] ?? "";
6010
- houses.push({
6011
- number: i + 1,
6012
- sign,
6013
- planets,
6014
- isLagna: lagnaSign ? lagnaSign.toLowerCase() === sign.toLowerCase() : false
6015
- });
6016
- }
6017
- return houses;
6984
+ if (!this.data?.meta) return [];
6985
+ return buildHousesFromMeta(this.data.meta);
6018
6986
  }
6019
6987
  render() {
6020
6988
  if (!this.data)
6021
- return html24`<div class="roxy-empty" role="status">No kundli data</div>`;
6989
+ return html27`<div class="roxy-empty" role="status">No kundli data</div>`;
6022
6990
  const houses = this.buildHouses();
6023
- const isNorth = this.chartStyle === "north";
6024
- return html24`<div class="wrap">
6991
+ const style = this.chartStyle;
6992
+ const frame = style === "north" ? renderNorthFrame() : style === "east" ? renderEastFrame() : renderSouthFrame();
6993
+ const houseGroup = style === "north" ? renderNorthHouseGroup : style === "east" ? renderEastHouseGroup : renderSouthHouseGroup;
6994
+ return html27`<div class="wrap">
6025
6995
  <h2 class="title">Vedic kundli</h2>
6026
6996
  <svg
6027
6997
  viewBox="0 0 300 300"
@@ -6029,15 +6999,15 @@ var RoxyVedicKundli = class extends LitElement24 {
6029
6999
  aria-label="Vedic birth chart with twelve sign houses"
6030
7000
  >
6031
7001
  <title>Vedic kundli</title>
6032
- ${isNorth ? renderNorthFrame() : renderSouthFrame()}
6033
- ${isNorth ? houses.map((h) => renderNorthHouseGroup(h)) : houses.map((h) => renderSouthHouseGroup(h))}
7002
+ ${frame}
7003
+ ${houses.map((h) => houseGroup(h))}
6034
7004
  </svg>
6035
7005
  </div>`;
6036
7006
  }
6037
7007
  };
6038
7008
  RoxyVedicKundli.styles = [
6039
7009
  baseStyles,
6040
- css25`
7010
+ css28`
6041
7011
  .wrap {
6042
7012
  display: grid;
6043
7013
  gap: var(--roxy-space-md, 1rem);
@@ -6045,64 +7015,381 @@ RoxyVedicKundli.styles = [
6045
7015
  .title {
6046
7016
  font-size: var(--roxy-text-lg, 1.125rem);
6047
7017
  font-weight: var(--roxy-weight-bold, 600);
6048
- margin: 0;
7018
+ margin: 0;
7019
+ }
7020
+ svg {
7021
+ display: block;
7022
+ width: 100%;
7023
+ max-width: 360px;
7024
+ margin: 0 auto;
7025
+ }
7026
+ .line {
7027
+ fill: transparent;
7028
+ stroke: var(--roxy-border, #e4e4e7);
7029
+ }
7030
+ .sign-text {
7031
+ fill: var(--roxy-muted, #71717a);
7032
+ font-size: 9px;
7033
+ font-weight: 500;
7034
+ font-family: var(--roxy-font-sans);
7035
+ }
7036
+ .planet-text {
7037
+ fill: var(--roxy-fg, #0a0a0a);
7038
+ font-size: 10px;
7039
+ font-weight: 600;
7040
+ font-family: var(--roxy-font-sans);
7041
+ }
7042
+ .house-num {
7043
+ fill: var(--roxy-muted, #71717a);
7044
+ font-size: 9px;
7045
+ font-weight: 400;
7046
+ font-family: var(--roxy-font-sans);
7047
+ }
7048
+ .lagna-marker {
7049
+ fill: var(--roxy-accent-fg, #b45309);
7050
+ font-size: 8px;
7051
+ font-weight: 700;
7052
+ font-family: var(--roxy-font-sans);
7053
+ letter-spacing: 0.05em;
7054
+ }
7055
+ .lagna-bg {
7056
+ fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
7057
+ stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
7058
+ stroke-width: 0.8;
7059
+ }
7060
+ `
7061
+ ];
7062
+ __decorateClass([
7063
+ property27({ attribute: false })
7064
+ ], RoxyVedicKundli.prototype, "data", 2);
7065
+ __decorateClass([
7066
+ property27({ type: String, reflect: true, attribute: "chart-style" })
7067
+ ], RoxyVedicKundli.prototype, "chartStyle", 2);
7068
+ RoxyVedicKundli = __decorateClass([
7069
+ customElement27("roxy-vedic-kundli")
7070
+ ], RoxyVedicKundli);
7071
+
7072
+ // packages/ui/src/components/vedic-planets-table.ts
7073
+ import { css as css29, html as html28, LitElement as LitElement28, nothing as nothing28 } from "lit";
7074
+ import { customElement as customElement28, property as property28 } from "lit/decorators.js";
7075
+ var GRAHA_ORDER = [
7076
+ "Lagna",
7077
+ "Sun",
7078
+ "Moon",
7079
+ "Mars",
7080
+ "Mercury",
7081
+ "Jupiter",
7082
+ "Venus",
7083
+ "Saturn",
7084
+ "Rahu",
7085
+ "Ketu"
7086
+ ];
7087
+ var RoxyVedicPlanetsTable = class extends LitElement28 {
7088
+ constructor() {
7089
+ super(...arguments);
7090
+ this.data = null;
7091
+ }
7092
+ /** Ordered [name, entry] pairs: GRAHA_ORDER first, then any extras. */
7093
+ orderedRows() {
7094
+ const meta = this.data?.meta ?? {};
7095
+ const seen = /* @__PURE__ */ new Set();
7096
+ const rows = [];
7097
+ for (const name of GRAHA_ORDER) {
7098
+ const entry = meta[name];
7099
+ if (entry) {
7100
+ rows.push([name, entry]);
7101
+ seen.add(name);
7102
+ }
7103
+ }
7104
+ for (const [name, entry] of Object.entries(meta)) {
7105
+ if (!seen.has(name)) rows.push([name, entry]);
7106
+ }
7107
+ return rows;
7108
+ }
7109
+ render() {
7110
+ if (!this.data?.meta)
7111
+ return html28`<div class="roxy-empty" role="status">No chart data</div>`;
7112
+ const rows = this.orderedRows();
7113
+ return html28`<div class="wrap" aria-label="Vedic planetary positions" tabindex="0">
7114
+ <header class="head">
7115
+ <h2 class="title">Planetary positions</h2>
7116
+ </header>
7117
+ <table role="table">
7118
+ <thead>
7119
+ <tr>
7120
+ <th scope="col">Graha</th>
7121
+ <th scope="col">Rashi</th>
7122
+ <th scope="col">Degree</th>
7123
+ <th scope="col">Nakshatra</th>
7124
+ <th scope="col">Pada</th>
7125
+ <th scope="col">Nak. lord</th>
7126
+ <th scope="col">House</th>
7127
+ <th scope="col">Avastha</th>
7128
+ <th scope="col">Retro</th>
7129
+ </tr>
7130
+ </thead>
7131
+ <tbody>
7132
+ ${rows.map(([name, p]) => {
7133
+ const isLagna = (p.graha ?? name) === "Lagna";
7134
+ const glyph = PLANET_GLYPH[capitalize(p.graha ?? name)] ?? "";
7135
+ const signGlyph = SIGN_GLYPH[capitalize(p.rashi ?? "")] ?? "";
7136
+ return html28`<tr class=${isLagna ? "lagna" : ""}>
7137
+ <td class="graha">
7138
+ ${glyph ? html28`<span class="glyph">${glyph}</span>` : nothing28}${p.graha ?? name}
7139
+ </td>
7140
+ <td>
7141
+ ${signGlyph ? html28`<span class="glyph">${signGlyph}</span>` : nothing28}${p.rashi ?? ""}
7142
+ </td>
7143
+ <td class="num">
7144
+ ${typeof p.longitude === "number" ? formatSignPosition(p.longitude) : ""}
7145
+ </td>
7146
+ <td>${p.nakshatra?.name ?? ""}</td>
7147
+ <td class="num">${p.nakshatra?.pada ?? ""}</td>
7148
+ <td>${p.nakshatra?.lord ?? ""}</td>
7149
+ <td class="num">${typeof p.house === "number" ? p.house : ""}</td>
7150
+ <td>${p.awastha ?? ""}</td>
7151
+ <td>${p.isRetrograde ? html28`<span class="retro">R</span>` : nothing28}</td>
7152
+ </tr>`;
7153
+ })}
7154
+ </tbody>
7155
+ </table>
7156
+ </div>`;
7157
+ }
7158
+ };
7159
+ RoxyVedicPlanetsTable.styles = [
7160
+ baseStyles,
7161
+ css29`
7162
+ .wrap {
7163
+ border: 1px solid var(--roxy-border, #e4e4e7);
7164
+ border-radius: var(--roxy-radius-md, 8px);
7165
+ background: var(--roxy-bg, #fff);
7166
+ overflow: auto;
7167
+ box-shadow: var(--roxy-shadow-sm);
7168
+ }
7169
+ .head {
7170
+ padding: var(--roxy-space-md, 1rem);
7171
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
7172
+ }
7173
+ .title {
7174
+ margin: 0;
7175
+ font-size: var(--roxy-text-lg, 1.125rem);
7176
+ font-weight: var(--roxy-weight-bold, 600);
7177
+ }
7178
+ table {
7179
+ width: 100%;
7180
+ border-collapse: collapse;
7181
+ font-size: var(--roxy-text-sm, 0.875rem);
7182
+ min-width: 620px;
7183
+ }
7184
+ thead {
7185
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
7186
+ }
7187
+ th,
7188
+ td {
7189
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
7190
+ text-align: left;
7191
+ white-space: nowrap;
7192
+ }
7193
+ th {
7194
+ color: var(--roxy-muted, #71717a);
7195
+ font-weight: var(--roxy-weight-bold, 600);
7196
+ text-transform: uppercase;
7197
+ font-size: var(--roxy-text-xs, 0.75rem);
7198
+ letter-spacing: 0.04em;
7199
+ }
7200
+ tbody tr {
7201
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
7202
+ }
7203
+ tbody tr.lagna {
7204
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 10%, transparent);
7205
+ }
7206
+ td.graha {
7207
+ font-weight: var(--roxy-weight-bold, 600);
7208
+ color: var(--roxy-fg, #0a0a0a);
7209
+ }
7210
+ .glyph {
7211
+ margin-right: 0.4em;
7212
+ color: var(--roxy-muted, #71717a);
7213
+ }
7214
+ /* On the tinted Lagna row the muted glyph drops below the WCAG AA
7215
+ contrast floor, so use the accent foreground there instead. */
7216
+ tbody tr.lagna .glyph {
7217
+ color: var(--roxy-accent-fg, #b45309);
7218
+ }
7219
+ .retro {
7220
+ color: var(--roxy-warning-fg, #9a3412);
7221
+ font-size: var(--roxy-text-xs, 0.75rem);
7222
+ font-weight: var(--roxy-weight-bold, 600);
7223
+ }
7224
+ .num {
7225
+ font-variant-numeric: tabular-nums;
7226
+ }
7227
+ `
7228
+ ];
7229
+ __decorateClass([
7230
+ property28({ attribute: false })
7231
+ ], RoxyVedicPlanetsTable.prototype, "data", 2);
7232
+ RoxyVedicPlanetsTable = __decorateClass([
7233
+ customElement28("roxy-vedic-planets-table")
7234
+ ], RoxyVedicPlanetsTable);
7235
+
7236
+ // packages/ui/src/components/western-planets-table.ts
7237
+ import { css as css30, html as html29, LitElement as LitElement29, nothing as nothing29 } from "lit";
7238
+ import { customElement as customElement29, property as property29 } from "lit/decorators.js";
7239
+ var RoxyWesternPlanetsTable = class extends LitElement29 {
7240
+ constructor() {
7241
+ super(...arguments);
7242
+ this.data = null;
7243
+ }
7244
+ /** Build the ordered row list: the planets array, then the four chart points. */
7245
+ rows() {
7246
+ const d = this.data;
7247
+ if (!d) return [];
7248
+ const rows = (d.planets ?? []).map((p) => ({
7249
+ name: p.name,
7250
+ sign: p.sign,
7251
+ longitude: p.longitude,
7252
+ house: p.house,
7253
+ speed: p.speed,
7254
+ isRetrograde: p.isRetrograde
7255
+ }));
7256
+ for (const [name, point] of [
7257
+ ["Ascendant", d.ascendant],
7258
+ ["Midheaven", d.midheaven],
7259
+ ["Part of Fortune", d.partOfFortune],
7260
+ ["Vertex", d.vertex]
7261
+ ]) {
7262
+ if (point) {
7263
+ rows.push({
7264
+ name,
7265
+ sign: point.sign,
7266
+ longitude: point.longitude,
7267
+ isPoint: true
7268
+ });
7269
+ }
7270
+ }
7271
+ return rows;
7272
+ }
7273
+ render() {
7274
+ if (!this.data?.planets)
7275
+ return html29`<div class="roxy-empty" role="status">No chart data</div>`;
7276
+ const rows = this.rows();
7277
+ return html29`<div class="wrap" aria-label="Western planetary positions" tabindex="0">
7278
+ <header class="head">
7279
+ <h2 class="title">Planetary positions</h2>
7280
+ </header>
7281
+ <table role="table">
7282
+ <thead>
7283
+ <tr>
7284
+ <th scope="col">Body</th>
7285
+ <th scope="col">Sign</th>
7286
+ <th scope="col">Degree</th>
7287
+ <th scope="col">House</th>
7288
+ <th scope="col">Motion</th>
7289
+ </tr>
7290
+ </thead>
7291
+ <tbody>
7292
+ ${rows.map((r) => {
7293
+ const glyph = PLANET_GLYPH[capitalize(r.name)] ?? "";
7294
+ const signGlyph = SIGN_GLYPH[capitalize(r.sign ?? "")] ?? "";
7295
+ const speed = typeof r.speed === "number" ? formatNumber(r.speed, 3) : "";
7296
+ return html29`<tr class=${r.isPoint ? "point" : ""}>
7297
+ <td class="body">
7298
+ ${glyph ? html29`<span class="glyph">${glyph}</span>` : nothing29}${r.name}
7299
+ </td>
7300
+ <td>
7301
+ ${signGlyph ? html29`<span class="glyph">${signGlyph}</span>` : nothing29}${r.sign ?? ""}
7302
+ </td>
7303
+ <td class="num">
7304
+ ${typeof r.longitude === "number" ? formatSignPosition(r.longitude) : ""}
7305
+ </td>
7306
+ <td class="num">${typeof r.house === "number" ? r.house : ""}</td>
7307
+ <td class="num">
7308
+ ${speed ? html29`${speed}°/day` : nothing29}
7309
+ ${r.isRetrograde ? html29`<span class="retro"> ℞</span>` : nothing29}
7310
+ </td>
7311
+ </tr>`;
7312
+ })}
7313
+ </tbody>
7314
+ </table>
7315
+ </div>`;
7316
+ }
7317
+ };
7318
+ RoxyWesternPlanetsTable.styles = [
7319
+ baseStyles,
7320
+ css30`
7321
+ .wrap {
7322
+ border: 1px solid var(--roxy-border, #e4e4e7);
7323
+ border-radius: var(--roxy-radius-md, 8px);
7324
+ background: var(--roxy-bg, #fff);
7325
+ overflow: auto;
7326
+ box-shadow: var(--roxy-shadow-sm);
7327
+ }
7328
+ .head {
7329
+ padding: var(--roxy-space-md, 1rem);
7330
+ border-bottom: 1px solid var(--roxy-border, #e4e4e7);
7331
+ }
7332
+ .title {
7333
+ margin: 0;
7334
+ font-size: var(--roxy-text-lg, 1.125rem);
7335
+ font-weight: var(--roxy-weight-bold, 600);
6049
7336
  }
6050
- svg {
6051
- display: block;
7337
+ table {
6052
7338
  width: 100%;
6053
- max-width: 360px;
6054
- margin: 0 auto;
7339
+ border-collapse: collapse;
7340
+ font-size: var(--roxy-text-sm, 0.875rem);
7341
+ min-width: 460px;
6055
7342
  }
6056
- .line {
6057
- fill: transparent;
6058
- stroke: var(--roxy-border, #e4e4e7);
7343
+ thead {
7344
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);
6059
7345
  }
6060
- .sign-text {
6061
- fill: var(--roxy-muted, #71717a);
6062
- font-size: 9px;
6063
- font-weight: 500;
6064
- font-family: var(--roxy-font-sans);
7346
+ th,
7347
+ td {
7348
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
7349
+ text-align: left;
7350
+ white-space: nowrap;
6065
7351
  }
6066
- .planet-text {
6067
- fill: var(--roxy-fg, #0a0a0a);
6068
- font-size: 11px;
6069
- font-weight: 600;
6070
- font-family: var(--roxy-font-sans);
7352
+ th {
7353
+ color: var(--roxy-muted, #71717a);
7354
+ font-weight: var(--roxy-weight-bold, 600);
7355
+ text-transform: uppercase;
7356
+ font-size: var(--roxy-text-xs, 0.75rem);
7357
+ letter-spacing: 0.04em;
6071
7358
  }
6072
- .house-num {
6073
- fill: var(--roxy-muted, #71717a);
6074
- font-size: 9px;
6075
- font-weight: 400;
6076
- font-family: var(--roxy-font-sans);
7359
+ tbody tr {
7360
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
6077
7361
  }
6078
- .lagna-marker {
6079
- fill: var(--roxy-accent-fg, #b45309);
6080
- font-size: 8px;
6081
- font-weight: 700;
6082
- font-family: var(--roxy-font-sans);
6083
- letter-spacing: 0.05em;
7362
+ tbody tr.point {
7363
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 8%, transparent);
6084
7364
  }
6085
- .lagna-bg {
6086
- fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
6087
- stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
6088
- stroke-width: 0.8;
7365
+ td.body {
7366
+ font-weight: var(--roxy-weight-bold, 600);
7367
+ color: var(--roxy-fg, #0a0a0a);
7368
+ }
7369
+ .glyph {
7370
+ margin-right: 0.4em;
7371
+ color: var(--roxy-muted, #71717a);
7372
+ }
7373
+ .retro {
7374
+ color: var(--roxy-danger, #dc2626);
7375
+ font-weight: var(--roxy-weight-bold, 600);
7376
+ }
7377
+ .num {
7378
+ font-variant-numeric: tabular-nums;
6089
7379
  }
6090
7380
  `
6091
7381
  ];
6092
7382
  __decorateClass([
6093
- property24({ attribute: false })
6094
- ], RoxyVedicKundli.prototype, "data", 2);
6095
- __decorateClass([
6096
- property24({ type: String, reflect: true, attribute: "chart-style" })
6097
- ], RoxyVedicKundli.prototype, "chartStyle", 2);
6098
- RoxyVedicKundli = __decorateClass([
6099
- customElement24("roxy-vedic-kundli")
6100
- ], RoxyVedicKundli);
7383
+ property29({ attribute: false })
7384
+ ], RoxyWesternPlanetsTable.prototype, "data", 2);
7385
+ RoxyWesternPlanetsTable = __decorateClass([
7386
+ customElement29("roxy-western-planets-table")
7387
+ ], RoxyWesternPlanetsTable);
6101
7388
 
6102
7389
  // packages/ui/src/components/yoga-list.ts
6103
- import { css as css26, html as html25, LitElement as LitElement25, nothing as nothing25 } from "lit";
6104
- import { customElement as customElement25, property as property25, state as state5 } from "lit/decorators.js";
6105
- var RoxyYogaList = class extends LitElement25 {
7390
+ import { css as css31, html as html30, LitElement as LitElement30, nothing as nothing30 } from "lit";
7391
+ import { customElement as customElement30, property as property30, state as state7 } from "lit/decorators.js";
7392
+ var RoxyYogaList = class extends LitElement30 {
6106
7393
  constructor() {
6107
7394
  super(...arguments);
6108
7395
  this.data = null;
@@ -6113,29 +7400,29 @@ var RoxyYogaList = class extends LitElement25 {
6113
7400
  }
6114
7401
  renderQualityChip(quality) {
6115
7402
  const cls = `quality-chip quality-${quality}`;
6116
- return html25`<span class=${cls}>${quality}</span>`;
7403
+ return html30`<span class=${cls}>${quality}</span>`;
6117
7404
  }
6118
7405
  renderDetailCard(yoga) {
6119
- return html25`<div class="detail-card">
7406
+ return html30`<div class="detail-card">
6120
7407
  <p class="detail-name">
6121
7408
  ${yoga.name}
6122
- ${yoga.quality ? this.renderQualityChip(yoga.quality) : nothing25}
7409
+ ${yoga.quality ? this.renderQualityChip(yoga.quality) : nothing30}
6123
7410
  </p>
6124
- ${yoga.description ? html25`<p class="description">${yoga.description}</p>` : nothing25}
6125
- ${yoga.result ? html25`<details>
7411
+ ${yoga.description ? html30`<p class="description">${yoga.description}</p>` : nothing30}
7412
+ ${yoga.result ? html30`<details>
6126
7413
  <summary>Effects</summary>
6127
7414
  <div class="result-body">${yoga.result}</div>
6128
- </details>` : nothing25}
7415
+ </details>` : nothing30}
6129
7416
  </div>`;
6130
7417
  }
6131
7418
  render() {
6132
7419
  if (!this.data)
6133
- return html25`<div class="roxy-empty" role="status">No yoga data</div>`;
7420
+ return html30`<div class="roxy-empty" role="status">No yoga data</div>`;
6134
7421
  const d = this.data;
6135
7422
  const lc = this.filter.toLowerCase();
6136
7423
  if ("description" in d && typeof d.description === "string") {
6137
7424
  const yoga = d;
6138
- return html25`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
7425
+ return html30`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
6139
7426
  }
6140
7427
  if ("yogas" in d && Array.isArray(d.yogas)) {
6141
7428
  const allYogas = d.yogas;
@@ -6144,10 +7431,10 @@ var RoxyYogaList = class extends LitElement25 {
6144
7431
  const detailYogas = allYogas;
6145
7432
  const filtered2 = lc ? detailYogas.filter((y) => y.name.toLowerCase().includes(lc)) : detailYogas;
6146
7433
  const total2 = d.total;
6147
- return html25`<div class="wrap">
7434
+ return html30`<div class="wrap">
6148
7435
  <div class="head">
6149
7436
  <h2 class="title">Yoga catalog</h2>
6150
- ${total2 !== void 0 ? html25`<span class="count">${total2} total</span>` : nothing25}
7437
+ ${total2 !== void 0 ? html30`<span class="count">${total2} total</span>` : nothing30}
6151
7438
  </div>
6152
7439
  <div class="search-wrap">
6153
7440
  <input
@@ -6165,17 +7452,17 @@ var RoxyYogaList = class extends LitElement25 {
6165
7452
  aria-live="polite"
6166
7453
  aria-label="Yoga results"
6167
7454
  >
6168
- ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : html25`<p class="no-results">No yogas match your search.</p>`}
7455
+ ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : html30`<p class="no-results">No yogas match your search.</p>`}
6169
7456
  </div>
6170
7457
  </div>`;
6171
7458
  }
6172
7459
  const catalogYogas = allYogas;
6173
7460
  const filtered = lc ? catalogYogas.filter((y) => y.name.toLowerCase().includes(lc)) : catalogYogas;
6174
7461
  const total = d.total;
6175
- return html25`<div class="wrap">
7462
+ return html30`<div class="wrap">
6176
7463
  <div class="head">
6177
7464
  <h2 class="title">Yoga catalog</h2>
6178
- ${total !== void 0 ? html25`<span class="count">${total} total</span>` : nothing25}
7465
+ ${total !== void 0 ? html30`<span class="count">${total} total</span>` : nothing30}
6179
7466
  </div>
6180
7467
  <div class="search-wrap">
6181
7468
  <input
@@ -6194,20 +7481,20 @@ var RoxyYogaList = class extends LitElement25 {
6194
7481
  aria-label="Yoga results"
6195
7482
  >
6196
7483
  ${filtered.length > 0 ? filtered.map(
6197
- (y) => html25`<div class="yoga-chip">
7484
+ (y) => html30`<div class="yoga-chip">
6198
7485
  ${y.name}
6199
7486
  <span class="yoga-id">${y.id}</span>
6200
7487
  </div>`
6201
- ) : html25`<p class="no-results">No yogas match your search.</p>`}
7488
+ ) : html30`<p class="no-results">No yogas match your search.</p>`}
6202
7489
  </div>
6203
7490
  </div>`;
6204
7491
  }
6205
- return html25`<div class="roxy-empty" role="status">No yoga data</div>`;
7492
+ return html30`<div class="roxy-empty" role="status">No yoga data</div>`;
6206
7493
  }
6207
7494
  };
6208
7495
  RoxyYogaList.styles = [
6209
7496
  baseStyles,
6210
- css26`
7497
+ css31`
6211
7498
  .wrap {
6212
7499
  display: grid;
6213
7500
  gap: var(--roxy-space-md, 1rem);
@@ -6358,13 +7645,13 @@ RoxyYogaList.styles = [
6358
7645
  `
6359
7646
  ];
6360
7647
  __decorateClass([
6361
- property25({ attribute: false })
7648
+ property30({ attribute: false })
6362
7649
  ], RoxyYogaList.prototype, "data", 2);
6363
7650
  __decorateClass([
6364
- state5()
7651
+ state7()
6365
7652
  ], RoxyYogaList.prototype, "filter", 2);
6366
7653
  RoxyYogaList = __decorateClass([
6367
- customElement25("roxy-yoga-list")
7654
+ customElement30("roxy-yoga-list")
6368
7655
  ], RoxyYogaList);
6369
7656
 
6370
7657
  // packages/ui/src/manifest.ts
@@ -6380,17 +7667,6 @@ var ROXY_COMPONENTS = [
6380
7667
  docsSummary: "Natal chart wheel with planet glyphs and aspect lines",
6381
7668
  topic: "Astrology"
6382
7669
  },
6383
- {
6384
- pascal: "RoxyHoroscopeCard",
6385
- tag: "roxy-horoscope-card",
6386
- slug: "horoscope-card",
6387
- heading: "Daily horoscope",
6388
- description: "Daily, weekly, or monthly horoscope card for /astrology/horoscope/...",
6389
- docsLabel: "Western",
6390
- endpointLabel: "GET /astrology/horoscope/{sign}/{daily,weekly,monthly}",
6391
- docsSummary: "Daily, weekly, or monthly horoscope card",
6392
- topic: "Astrology"
6393
- },
6394
7670
  {
6395
7671
  pascal: "RoxySynastryChart",
6396
7672
  tag: "roxy-synastry-chart",
@@ -6403,14 +7679,25 @@ var ROXY_COMPONENTS = [
6403
7679
  topic: "Astrology"
6404
7680
  },
6405
7681
  {
6406
- pascal: "RoxyCompatibilityCard",
6407
- tag: "roxy-compatibility-card",
6408
- slug: "compatibility-card",
6409
- heading: "Compatibility score",
6410
- description: "Cross-domain compatibility score card",
6411
- docsLabel: "Cross",
6412
- endpointLabel: "POST /astrology/compatibility-score, /numerology/compatibility, /biorhythm/compatibility",
6413
- docsSummary: "Score card with category breakdown",
7682
+ pascal: "RoxyWesternPlanetsTable",
7683
+ tag: "roxy-western-planets-table",
7684
+ slug: "western-planets-table",
7685
+ heading: "Western planets",
7686
+ description: "Western planetary positions table with sign, degree, house, and motion plus the chart angles",
7687
+ docsLabel: "Western",
7688
+ endpointLabel: "POST /astrology/natal-chart",
7689
+ docsSummary: "Sign, degree, house, motion columns plus ASC, MC, PoF, Vertex",
7690
+ topic: "Astrology"
7691
+ },
7692
+ {
7693
+ pascal: "RoxyTransitsTable",
7694
+ tag: "roxy-transits-table",
7695
+ slug: "transits-table",
7696
+ heading: "Transits",
7697
+ description: "Live planet positions plus aspects to a natal chart",
7698
+ docsLabel: "Western",
7699
+ endpointLabel: "POST /astrology/transits",
7700
+ docsSummary: "Transit planet positions plus optional aspects to a natal chart",
6414
7701
  topic: "Astrology"
6415
7702
  },
6416
7703
  {
@@ -6424,59 +7711,70 @@ var ROXY_COMPONENTS = [
6424
7711
  docsSummary: "Moon phase card and calendar",
6425
7712
  topic: "Astrology"
6426
7713
  },
7714
+ {
7715
+ pascal: "RoxyHoroscopeCard",
7716
+ tag: "roxy-horoscope-card",
7717
+ slug: "horoscope-card",
7718
+ heading: "Daily horoscope",
7719
+ description: "Daily, weekly, or monthly horoscope card for /astrology/horoscope/...",
7720
+ docsLabel: "Western",
7721
+ endpointLabel: "GET /astrology/horoscope/{sign}/{daily,weekly,monthly}",
7722
+ docsSummary: "Daily, weekly, or monthly horoscope card",
7723
+ topic: "Astrology"
7724
+ },
7725
+ {
7726
+ pascal: "RoxyCompatibilityCard",
7727
+ tag: "roxy-compatibility-card",
7728
+ slug: "compatibility-card",
7729
+ heading: "Compatibility score",
7730
+ description: "Cross-domain compatibility score card",
7731
+ docsLabel: "Cross",
7732
+ endpointLabel: "POST /astrology/compatibility-score, /numerology/compatibility, /biorhythm/compatibility",
7733
+ docsSummary: "Score card with category breakdown",
7734
+ topic: "Astrology"
7735
+ },
6427
7736
  {
6428
7737
  pascal: "RoxyVedicKundli",
6429
7738
  tag: "roxy-vedic-kundli",
6430
7739
  slug: "vedic-kundli",
6431
7740
  heading: "Vedic kundli",
6432
- description: "South or North Indian Vedic kundli for /vedic-astrology/birth-chart",
7741
+ description: "South, North, or East Indian Vedic kundli for /vedic-astrology/birth-chart with per-planet degree and nakshatra detail",
6433
7742
  docsLabel: "Vedic",
6434
7743
  endpointLabel: "POST /vedic-astrology/birth-chart",
6435
- docsSummary: "South or North Indian kundli",
6436
- topic: "Vedic"
6437
- },
6438
- {
6439
- pascal: "RoxyPanchangTable",
6440
- tag: "roxy-panchang-table",
6441
- slug: "panchang-table",
6442
- heading: "Panchang",
6443
- description: "Panchang muhurta table with auspicious and inauspicious periods",
6444
- docsLabel: "Vedic",
6445
- endpointLabel: "POST /vedic-astrology/panchang/{basic,detailed}",
6446
- docsSummary: "15+ muhurtas in detailed mode",
7744
+ docsSummary: "South, North, or East Indian kundli with degree detail",
6447
7745
  topic: "Vedic"
6448
7746
  },
6449
7747
  {
6450
- pascal: "RoxyDashaTimeline",
6451
- tag: "roxy-dasha-timeline",
6452
- slug: "dasha-timeline",
6453
- heading: "Vimshottari dasha",
6454
- description: "Vimshottari dasha timeline with active mahadasha highlighted",
7748
+ pascal: "RoxyDivisionalChart",
7749
+ tag: "roxy-divisional-chart",
7750
+ slug: "divisional-chart",
7751
+ heading: "Divisional chart",
7752
+ description: "D2 to D60 varga chart wheel with Vargottama markers",
6455
7753
  docsLabel: "Vedic",
6456
- endpointLabel: "POST /vedic-astrology/dasha/{current,major,sub/...}",
6457
- docsSummary: "Vimshottari mahadasha + antardasha + pratyantardasha",
7754
+ endpointLabel: "POST /vedic-astrology/divisional-chart",
7755
+ docsSummary: "Generic divisional varga wheel from D2 Hora to D60 Shashtiamsa",
6458
7756
  topic: "Vedic"
6459
7757
  },
6460
7758
  {
6461
- pascal: "RoxyDoshaCard",
6462
- tag: "roxy-dosha-card",
6463
- slug: "dosha-card",
6464
- heading: "Manglik dosha",
6465
- description: "Manglik, Kaal Sarp, or Sade Sati presence card",
6466
- docsLabel: "Vedic",
6467
- endpointLabel: "POST /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}",
6468
- docsSummary: "Presence, severity, remedies, scoped effects",
7759
+ pascal: "RoxyKpChart",
7760
+ tag: "roxy-kp-chart",
7761
+ slug: "kp-chart",
7762
+ heading: "KP chart",
7763
+ description: "Full KP chart with Ascendant, Placidus cusps, and planets in tabbed stellar-hierarchy tables",
7764
+ docsLabel: "Vedic (KP)",
7765
+ endpointLabel: "POST /vedic-astrology/kp/chart",
7766
+ docsSummary: "Ascendant, cusps, and planets with KP stellar hierarchy",
6469
7767
  topic: "Vedic"
6470
7768
  },
6471
7769
  {
6472
- pascal: "RoxyGunaMilan",
6473
- tag: "roxy-guna-milan",
6474
- slug: "guna-milan",
6475
- heading: "Guna milan",
6476
- description: "36-point Ashtakoota matrimonial compatibility breakdown",
7770
+ pascal: "RoxyVedicPlanetsTable",
7771
+ tag: "roxy-vedic-planets-table",
7772
+ slug: "vedic-planets-table",
7773
+ heading: "Vedic planets",
7774
+ description: "Vedic planetary positions table with degree, nakshatra, pada, nakshatra lord, bhava, and avastha",
6477
7775
  docsLabel: "Vedic",
6478
- endpointLabel: "POST /vedic-astrology/compatibility",
6479
- docsSummary: "36-point Ashtakoota with eight sub-scores",
7776
+ endpointLabel: "POST /vedic-astrology/birth-chart",
7777
+ docsSummary: "Degree, nakshatra, pada, lord, bhava, avastha columns",
6480
7778
  topic: "Vedic"
6481
7779
  },
6482
7780
  {
@@ -6491,25 +7789,14 @@ var ROXY_COMPONENTS = [
6491
7789
  topic: "Vedic"
6492
7790
  },
6493
7791
  {
6494
- pascal: "RoxyTransitsTable",
6495
- tag: "roxy-transits-table",
6496
- slug: "transits-table",
6497
- heading: "Transits",
6498
- description: "Live planet positions plus aspects to a natal chart",
6499
- docsLabel: "Western",
6500
- endpointLabel: "POST /astrology/transits",
6501
- docsSummary: "Transit planet positions plus optional aspects to a natal chart",
6502
- topic: "Astrology"
6503
- },
6504
- {
6505
- pascal: "RoxyDivisionalChart",
6506
- tag: "roxy-divisional-chart",
6507
- slug: "divisional-chart",
6508
- heading: "Divisional chart",
6509
- description: "D2 to D60 varga chart wheel with Vargottama markers",
6510
- docsLabel: "Vedic",
6511
- endpointLabel: "POST /vedic-astrology/divisional-chart",
6512
- docsSummary: "Generic divisional varga wheel from D2 Hora to D60 Shashtiamsa",
7792
+ pascal: "RoxyKpRulingPlanets",
7793
+ tag: "roxy-kp-ruling-planets",
7794
+ slug: "kp-ruling-planets",
7795
+ heading: "KP ruling planets",
7796
+ description: "KP ruling planets with day lord, Moon and Lagna stellar hierarchies, and house significators",
7797
+ docsLabel: "Vedic (KP)",
7798
+ endpointLabel: "POST /vedic-astrology/kp/ruling-planets",
7799
+ docsSummary: "Day lord, Moon/Lagna hierarchies, ruling planets, significators",
6513
7800
  topic: "Vedic"
6514
7801
  },
6515
7802
  {
@@ -6535,14 +7822,36 @@ var ROXY_COMPONENTS = [
6535
7822
  topic: "Vedic"
6536
7823
  },
6537
7824
  {
6538
- pascal: "RoxyYogaList",
6539
- tag: "roxy-yoga-list",
6540
- slug: "yoga-list",
6541
- heading: "Yoga catalog",
6542
- description: "Yoga reference cards from the catalog with optional detail mode",
7825
+ pascal: "RoxyDashaTimeline",
7826
+ tag: "roxy-dasha-timeline",
7827
+ slug: "dasha-timeline",
7828
+ heading: "Vimshottari dasha",
7829
+ description: "Vimshottari dasha timeline with active mahadasha highlighted",
6543
7830
  docsLabel: "Vedic",
6544
- endpointLabel: "GET /vedic-astrology/yoga, /yoga/{id}",
6545
- docsSummary: "Filterable yoga cards from the 300 plus yoga catalog",
7831
+ endpointLabel: "POST /vedic-astrology/dasha/{current,major,sub/...}",
7832
+ docsSummary: "Vimshottari mahadasha + antardasha + pratyantardasha",
7833
+ topic: "Vedic"
7834
+ },
7835
+ {
7836
+ pascal: "RoxyGunaMilan",
7837
+ tag: "roxy-guna-milan",
7838
+ slug: "guna-milan",
7839
+ heading: "Guna milan",
7840
+ description: "36-point Ashtakoota matrimonial compatibility breakdown",
7841
+ docsLabel: "Vedic",
7842
+ endpointLabel: "POST /vedic-astrology/compatibility",
7843
+ docsSummary: "36-point Ashtakoota with eight sub-scores",
7844
+ topic: "Vedic"
7845
+ },
7846
+ {
7847
+ pascal: "RoxyPanchangTable",
7848
+ tag: "roxy-panchang-table",
7849
+ slug: "panchang-table",
7850
+ heading: "Panchang",
7851
+ description: "Panchang muhurta table with auspicious and inauspicious periods",
7852
+ docsLabel: "Vedic",
7853
+ endpointLabel: "POST /vedic-astrology/panchang/{basic,detailed}",
7854
+ docsSummary: "15+ muhurtas in detailed mode",
6546
7855
  topic: "Vedic"
6547
7856
  },
6548
7857
  {
@@ -6556,6 +7865,39 @@ var ROXY_COMPONENTS = [
6556
7865
  docsSummary: "Day and night Choghadiya muhurta tiles colored by effect",
6557
7866
  topic: "Vedic"
6558
7867
  },
7868
+ {
7869
+ pascal: "RoxyYogaList",
7870
+ tag: "roxy-yoga-list",
7871
+ slug: "yoga-list",
7872
+ heading: "Yoga catalog",
7873
+ description: "Yoga reference cards from the catalog with optional detail mode",
7874
+ docsLabel: "Vedic",
7875
+ endpointLabel: "GET /vedic-astrology/yoga, /yoga/{id}",
7876
+ docsSummary: "Filterable yoga cards from the 300 plus yoga catalog",
7877
+ topic: "Vedic"
7878
+ },
7879
+ {
7880
+ pascal: "RoxyNakshatraCard",
7881
+ tag: "roxy-nakshatra-card",
7882
+ slug: "nakshatra-card",
7883
+ heading: "Nakshatra",
7884
+ description: "Nakshatra reference card with lord, deity, symbol, characteristics, and remedies",
7885
+ docsLabel: "Vedic",
7886
+ endpointLabel: "GET /vedic-astrology/nakshatras/{id}",
7887
+ docsSummary: "Lord, deity, symbol, characteristics, remedies",
7888
+ topic: "Vedic"
7889
+ },
7890
+ {
7891
+ pascal: "RoxyDoshaCard",
7892
+ tag: "roxy-dosha-card",
7893
+ slug: "dosha-card",
7894
+ heading: "Manglik dosha",
7895
+ description: "Manglik, Kaal Sarp, or Sade Sati presence card",
7896
+ docsLabel: "Vedic",
7897
+ endpointLabel: "POST /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}",
7898
+ docsSummary: "Presence, severity, remedies, scoped effects",
7899
+ topic: "Vedic"
7900
+ },
6559
7901
  {
6560
7902
  pascal: "RoxyNumerologyCard",
6561
7903
  tag: "roxy-numerology-card",
@@ -6650,7 +7992,7 @@ var ROXY_COMPONENTS = [
6650
7992
  ];
6651
7993
 
6652
7994
  // packages/ui/src/version.ts
6653
- var ROXY_UI_VERSION = "0.2.3";
7995
+ var ROXY_UI_VERSION = "0.3.1";
6654
7996
 
6655
7997
  // packages/ui/src/index.ts
6656
7998
  var ROXY_UI_COMPONENTS = ROXY_COMPONENTS.map((c) => c.slug);
@@ -6670,9 +8012,12 @@ export {
6670
8012
  RoxyGunaMilan,
6671
8013
  RoxyHexagram,
6672
8014
  RoxyHoroscopeCard,
8015
+ RoxyKpChart,
6673
8016
  RoxyKpPlanetsTable,
8017
+ RoxyKpRulingPlanets,
6674
8018
  RoxyLocationSearch,
6675
8019
  RoxyMoonPhase,
8020
+ RoxyNakshatraCard,
6676
8021
  RoxyNatalChart,
6677
8022
  RoxyNumerologyCard,
6678
8023
  RoxyPanchangTable,
@@ -6682,6 +8027,8 @@ export {
6682
8027
  RoxyTarotSpread,
6683
8028
  RoxyTransitsTable,
6684
8029
  RoxyVedicKundli,
8030
+ RoxyVedicPlanetsTable,
8031
+ RoxyWesternPlanetsTable,
6685
8032
  RoxyYogaList
6686
8033
  };
6687
8034
  //# sourceMappingURL=index.js.map