@roxyapi/ui 0.3.1 → 0.4.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 (165) hide show
  1. package/AGENTS.md +34 -7
  2. package/README.md +145 -26
  3. package/dist/cdn/components/ashtakavarga-grid.js +74 -19
  4. package/dist/cdn/components/ashtakavarga-grid.js.map +2 -2
  5. package/dist/cdn/components/biorhythm-chart.js +18 -4
  6. package/dist/cdn/components/biorhythm-chart.js.map +2 -2
  7. package/dist/cdn/components/choghadiya-grid.js +47 -12
  8. package/dist/cdn/components/choghadiya-grid.js.map +3 -3
  9. package/dist/cdn/components/compatibility-card.js +21 -7
  10. package/dist/cdn/components/compatibility-card.js.map +2 -2
  11. package/dist/cdn/components/dasha-timeline.js +113 -28
  12. package/dist/cdn/components/dasha-timeline.js.map +3 -3
  13. package/dist/cdn/components/data.js +27 -13
  14. package/dist/cdn/components/data.js.map +2 -2
  15. package/dist/cdn/components/divisional-chart.js +225 -118
  16. package/dist/cdn/components/divisional-chart.js.map +4 -4
  17. package/dist/cdn/components/dosha-card.js +18 -4
  18. package/dist/cdn/components/dosha-card.js.map +2 -2
  19. package/dist/cdn/components/endpoint-form.js +25 -11
  20. package/dist/cdn/components/endpoint-form.js.map +2 -2
  21. package/dist/cdn/components/guna-milan.js +20 -6
  22. package/dist/cdn/components/guna-milan.js.map +2 -2
  23. package/dist/cdn/components/hexagram.js +22 -8
  24. package/dist/cdn/components/hexagram.js.map +2 -2
  25. package/dist/cdn/components/horoscope-card.js +20 -6
  26. package/dist/cdn/components/horoscope-card.js.map +2 -2
  27. package/dist/cdn/components/kp-chart.js +19 -5
  28. package/dist/cdn/components/kp-chart.js.map +2 -2
  29. package/dist/cdn/components/kp-planets-table.js +17 -3
  30. package/dist/cdn/components/kp-planets-table.js.map +2 -2
  31. package/dist/cdn/components/kp-ruling-planets.js +17 -3
  32. package/dist/cdn/components/kp-ruling-planets.js.map +2 -2
  33. package/dist/cdn/components/location-search.js +18 -4
  34. package/dist/cdn/components/location-search.js.map +2 -2
  35. package/dist/cdn/components/moon-phase.js +27 -13
  36. package/dist/cdn/components/moon-phase.js.map +2 -2
  37. package/dist/cdn/components/nakshatra-card.js +16 -2
  38. package/dist/cdn/components/nakshatra-card.js.map +2 -2
  39. package/dist/cdn/components/natal-chart.js +79 -40
  40. package/dist/cdn/components/natal-chart.js.map +3 -3
  41. package/dist/cdn/components/numerology-card.js +18 -4
  42. package/dist/cdn/components/numerology-card.js.map +2 -2
  43. package/dist/cdn/components/panchang-table.js +53 -25
  44. package/dist/cdn/components/panchang-table.js.map +3 -3
  45. package/dist/cdn/components/shadbala-table.js +24 -10
  46. package/dist/cdn/components/shadbala-table.js.map +2 -2
  47. package/dist/cdn/components/synastry-chart.js +96 -48
  48. package/dist/cdn/components/synastry-chart.js.map +3 -3
  49. package/dist/cdn/components/tarot-card.js +17 -3
  50. package/dist/cdn/components/tarot-card.js.map +2 -2
  51. package/dist/cdn/components/tarot-spread.js +39 -25
  52. package/dist/cdn/components/tarot-spread.js.map +2 -2
  53. package/dist/cdn/components/transits-table.js +18 -4
  54. package/dist/cdn/components/transits-table.js.map +2 -2
  55. package/dist/cdn/components/vedic-kundli.js +215 -105
  56. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  57. package/dist/cdn/components/vedic-planets-table.js +22 -8
  58. package/dist/cdn/components/vedic-planets-table.js.map +2 -2
  59. package/dist/cdn/components/western-planets-table.js +18 -4
  60. package/dist/cdn/components/western-planets-table.js.map +2 -2
  61. package/dist/cdn/components/yoga-list.js +17 -3
  62. package/dist/cdn/components/yoga-list.js.map +2 -2
  63. package/dist/cdn/roxy-ui.js +1082 -816
  64. package/dist/cdn/roxy-ui.js.map +4 -4
  65. package/dist/components/ashtakavarga-grid.d.ts +13 -1
  66. package/dist/components/ashtakavarga-grid.d.ts.map +1 -1
  67. package/dist/components/ashtakavarga-grid.js +86 -11
  68. package/dist/components/ashtakavarga-grid.js.map +2 -2
  69. package/dist/components/biorhythm-chart.js +14 -0
  70. package/dist/components/biorhythm-chart.js.map +2 -2
  71. package/dist/components/choghadiya-grid.d.ts +6 -0
  72. package/dist/components/choghadiya-grid.d.ts.map +1 -1
  73. package/dist/components/choghadiya-grid.js +50 -2
  74. package/dist/components/choghadiya-grid.js.map +2 -2
  75. package/dist/components/compatibility-card.js +14 -0
  76. package/dist/components/compatibility-card.js.map +2 -2
  77. package/dist/components/dasha-timeline.d.ts +10 -0
  78. package/dist/components/dasha-timeline.d.ts.map +1 -1
  79. package/dist/components/dasha-timeline.js +135 -4
  80. package/dist/components/dasha-timeline.js.map +2 -2
  81. package/dist/components/data.js +14 -0
  82. package/dist/components/data.js.map +2 -2
  83. package/dist/components/divisional-chart.d.ts +9 -6
  84. package/dist/components/divisional-chart.d.ts.map +1 -1
  85. package/dist/components/divisional-chart.js +546 -251
  86. package/dist/components/divisional-chart.js.map +4 -4
  87. package/dist/components/dosha-card.js +14 -0
  88. package/dist/components/dosha-card.js.map +2 -2
  89. package/dist/components/endpoint-form.js +14 -0
  90. package/dist/components/endpoint-form.js.map +2 -2
  91. package/dist/components/guna-milan.js +14 -0
  92. package/dist/components/guna-milan.js.map +2 -2
  93. package/dist/components/hexagram.js +14 -0
  94. package/dist/components/hexagram.js.map +2 -2
  95. package/dist/components/horoscope-card.js +14 -0
  96. package/dist/components/horoscope-card.js.map +2 -2
  97. package/dist/components/kp-chart.js +14 -0
  98. package/dist/components/kp-chart.js.map +2 -2
  99. package/dist/components/kp-planets-table.js +14 -0
  100. package/dist/components/kp-planets-table.js.map +2 -2
  101. package/dist/components/kp-ruling-planets.js +14 -0
  102. package/dist/components/kp-ruling-planets.js.map +2 -2
  103. package/dist/components/location-search.js +14 -0
  104. package/dist/components/location-search.js.map +2 -2
  105. package/dist/components/moon-phase.js +14 -0
  106. package/dist/components/moon-phase.js.map +2 -2
  107. package/dist/components/nakshatra-card.js +14 -0
  108. package/dist/components/nakshatra-card.js.map +2 -2
  109. package/dist/components/natal-chart.d.ts.map +1 -1
  110. package/dist/components/natal-chart.js +76 -6
  111. package/dist/components/natal-chart.js.map +2 -2
  112. package/dist/components/numerology-card.js +14 -0
  113. package/dist/components/numerology-card.js.map +2 -2
  114. package/dist/components/panchang-table.d.ts +1 -0
  115. package/dist/components/panchang-table.d.ts.map +1 -1
  116. package/dist/components/panchang-table.js +37 -1
  117. package/dist/components/panchang-table.js.map +2 -2
  118. package/dist/components/shadbala-table.js +14 -0
  119. package/dist/components/shadbala-table.js.map +2 -2
  120. package/dist/components/synastry-chart.d.ts +6 -0
  121. package/dist/components/synastry-chart.d.ts.map +1 -1
  122. package/dist/components/synastry-chart.js +106 -7
  123. package/dist/components/synastry-chart.js.map +2 -2
  124. package/dist/components/tarot-card.js +14 -0
  125. package/dist/components/tarot-card.js.map +2 -2
  126. package/dist/components/tarot-spread.js +14 -0
  127. package/dist/components/tarot-spread.js.map +2 -2
  128. package/dist/components/transits-table.js +14 -0
  129. package/dist/components/transits-table.js.map +2 -2
  130. package/dist/components/vedic-kundli.d.ts +14 -9
  131. package/dist/components/vedic-kundli.d.ts.map +1 -1
  132. package/dist/components/vedic-kundli.js +537 -245
  133. package/dist/components/vedic-kundli.js.map +4 -4
  134. package/dist/components/vedic-planets-table.js +14 -0
  135. package/dist/components/vedic-planets-table.js.map +2 -2
  136. package/dist/components/western-planets-table.js +14 -0
  137. package/dist/components/western-planets-table.js.map +2 -2
  138. package/dist/components/yoga-list.js +14 -0
  139. package/dist/components/yoga-list.js.map +2 -2
  140. package/dist/index.cjs +1397 -797
  141. package/dist/index.cjs.map +4 -4
  142. package/dist/index.js +1278 -678
  143. package/dist/index.js.map +4 -4
  144. package/dist/manifest.json +23 -23
  145. package/dist/styles/tokens.css +8 -23
  146. package/dist/utils/base-styles.d.ts.map +1 -1
  147. package/dist/utils/kundli-render.d.ts +43 -104
  148. package/dist/utils/kundli-render.d.ts.map +1 -1
  149. package/dist/utils/kundli-styles.d.ts +13 -0
  150. package/dist/utils/kundli-styles.d.ts.map +1 -0
  151. package/dist/version.d.ts +1 -1
  152. package/package.json +1 -1
  153. package/src/components/ashtakavarga-grid.ts +73 -11
  154. package/src/components/choghadiya-grid.ts +37 -2
  155. package/src/components/dasha-timeline.ts +135 -4
  156. package/src/components/divisional-chart.ts +40 -97
  157. package/src/components/natal-chart.ts +89 -6
  158. package/src/components/panchang-table.ts +34 -1
  159. package/src/components/synastry-chart.ts +84 -8
  160. package/src/components/vedic-kundli.ts +35 -95
  161. package/src/styles/tokens.css +8 -23
  162. package/src/utils/base-styles.ts +14 -0
  163. package/src/utils/kundli-render.ts +609 -270
  164. package/src/utils/kundli-styles.ts +124 -0
  165. package/src/version.ts +1 -1
@@ -10,7 +10,7 @@ var __decorateClass = (decorators, target, key, kind) => {
10
10
  };
11
11
 
12
12
  // packages/ui/src/components/vedic-kundli.ts
13
- import { css as css2, html, LitElement } from "lit";
13
+ import { html as html2, LitElement } from "lit";
14
14
  import { customElement, property } from "lit/decorators.js";
15
15
 
16
16
  // packages/ui/src/utils/base-styles.ts
@@ -97,10 +97,24 @@ var baseStyles = css`
97
97
  outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
98
98
  outline-offset: 2px;
99
99
  }
100
+
101
+ /* Force the text-style variant on every Unicode glyph in the component.
102
+ * macOS and iOS substitute coloured emoji glyphs for the planetary and
103
+ * gender Unicode code points (Mars, Venus, Mercury, etc.) when the
104
+ * system colour-emoji font wins font selection. The text-style variant
105
+ * keeps glyphs monochrome so they inherit the surrounding fill colour
106
+ * and match the brand palette consistently across platforms.
107
+ *
108
+ * font-variant-emoji is part of CSS Fonts 4 (Safari 17+, Chrome 134+,
109
+ * Firefox 139+). On older browsers the rule is silently ignored.
110
+ */
111
+ :host {
112
+ font-variant-emoji: text;
113
+ }
100
114
  `;
101
115
 
102
116
  // packages/ui/src/utils/kundli-render.ts
103
- import { nothing, svg } from "lit";
117
+ import { html, nothing, svg } from "lit";
104
118
 
105
119
  // packages/ui/src/tokens/index.ts
106
120
  var PLANET_ABBR = {
@@ -180,25 +194,45 @@ function capitalize(s) {
180
194
  }
181
195
 
182
196
  // packages/ui/src/utils/kundli-render.ts
197
+ var VIEW_BOX = 400;
198
+ var MARGIN = 20;
199
+ var INNER = VIEW_BOX - 2 * MARGIN;
200
+ var CENTRE = VIEW_BOX / 2;
183
201
  var RASHI_TO_SIGN = Object.fromEntries(
184
202
  SIGNS_ORDER.map((s) => [s.toLowerCase(), s])
185
203
  );
204
+ var CHART_STYLES = [
205
+ { id: "north", label: "North" },
206
+ { id: "south", label: "South" },
207
+ { id: "east", label: "East" }
208
+ ];
186
209
  var RETRO_MARK = "\u02B3";
187
- function grahaLabel(p) {
210
+ function isDivisionalPlacement(p, cellSign) {
211
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
212
+ return false;
213
+ }
214
+ return longitudeToSignPosition(p.longitude).sign.toLowerCase() !== cellSign.toLowerCase();
215
+ }
216
+ function grahaLabel(p, cellSign) {
188
217
  const abbr = PLANET_ABBR[capitalize(p.graha)] ?? p.graha.slice(0, 2);
189
218
  const retro = p.isRetrograde ? RETRO_MARK : "";
190
- if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
219
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude) || isDivisionalPlacement(p, cellSign)) {
191
220
  return `${abbr}${retro}`;
192
221
  }
193
222
  const { degree } = longitudeToSignPosition(p.longitude);
194
223
  return `${abbr} ${degree}\xB0${retro}`;
195
224
  }
196
- function grahaTitle(p) {
225
+ function grahaTitle(p, cellSign) {
197
226
  const parts = [capitalize(p.graha)];
227
+ const divisional = isDivisionalPlacement(p, cellSign);
228
+ if (divisional) {
229
+ parts.push(`in ${cellSign}`);
230
+ }
198
231
  if (typeof p.longitude === "number" && Number.isFinite(p.longitude)) {
199
232
  const sp = longitudeToSignPosition(p.longitude);
233
+ const minute = String(sp.minute).padStart(2, "0");
200
234
  parts.push(
201
- `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}' ${sp.sign}`
235
+ divisional ? `D1: ${sp.degree}\xB0${minute}' ${sp.sign}` : `${sp.degree}\xB0${minute}' ${sp.sign}`
202
236
  );
203
237
  }
204
238
  if (p.nakshatra?.name) {
@@ -209,293 +243,551 @@ function grahaTitle(p) {
209
243
  if (p.isRetrograde) parts.push("retrograde");
210
244
  return parts.join(" \xB7 ");
211
245
  }
212
- function renderPlanetStack(planets, cx, baseY, lineHeight) {
246
+ function renderPlanetStack(planets, cellSign, cx, baseY, lineHeight) {
213
247
  const startY = baseY - (planets.length - 1) * lineHeight / 2;
214
248
  return planets.map((p, j) => {
215
249
  const yPos = startY + j * lineHeight;
216
250
  return svg`<text class="planet-text" x=${cx} y=${yPos} text-anchor="middle" dominant-baseline="central">${grahaLabel(
217
- p
218
- )}<title>${grahaTitle(p)}</title></text>`;
251
+ p,
252
+ cellSign
253
+ )}<title>${grahaTitle(p, cellSign)}</title></text>`;
219
254
  });
220
255
  }
221
- var SOUTH_HOUSE_CENTERS = {
222
- 1: { x: 150, y: 58 },
223
- 2: { x: 205, y: 52 },
224
- 3: { x: 253, y: 112 },
225
- 4: { x: 243, y: 150 },
226
- 5: { x: 253, y: 188 },
227
- 6: { x: 205, y: 248 },
228
- 7: { x: 150, y: 242 },
229
- 8: { x: 95, y: 248 },
230
- 9: { x: 47, y: 188 },
231
- 10: { x: 57, y: 150 },
232
- 11: { x: 47, y: 112 },
233
- 12: { x: 95, y: 52 }
234
- };
235
- var SOUTH_SIGN_POSITIONS = {
236
- 1: { x: 150, y: 35 },
237
- 2: { x: 222, y: 40 },
238
- 3: { x: 265, y: 100 },
239
- 4: { x: 265, y: 150 },
240
- 5: { x: 265, y: 200 },
241
- 6: { x: 222, y: 260 },
242
- 7: { x: 150, y: 265 },
243
- 8: { x: 78, y: 260 },
244
- 9: { x: 35, y: 200 },
245
- 10: { x: 35, y: 150 },
246
- 11: { x: 35, y: 100 },
247
- 12: { x: 78, y: 40 }
248
- };
249
- var NORTH_HOUSE_CENTERS = {
250
- 1: { x: 150, y: 60 },
251
- 2: { x: 225, y: 100 },
252
- 3: { x: 255, y: 150 },
253
- 4: { x: 225, y: 200 },
254
- 5: { x: 150, y: 240 },
255
- 6: { x: 75, y: 200 },
256
- 7: { x: 45, y: 150 },
257
- 8: { x: 75, y: 100 },
258
- 9: { x: 100, y: 80 },
259
- 10: { x: 150, y: 108 },
260
- 11: { x: 200, y: 80 },
261
- 12: { x: 200, y: 220 }
262
- };
263
- var EAST_HOUSE_CENTERS = {
264
- 1: { x: 150, y: 80 },
265
- // inner diamond, top
266
- 2: { x: 220, y: 33 },
267
- // top-right corner, upper triangle
268
- 3: { x: 267, y: 80 },
269
- // top-right corner, right triangle
270
- 4: { x: 220, y: 150 },
271
- // inner diamond, right
272
- 5: { x: 267, y: 220 },
273
- // bottom-right corner, right triangle
274
- 6: { x: 220, y: 267 },
275
- // bottom-right corner, lower triangle
276
- 7: { x: 150, y: 220 },
277
- // inner diamond, bottom
278
- 8: { x: 80, y: 267 },
279
- // bottom-left corner, lower triangle
280
- 9: { x: 33, y: 220 },
281
- // bottom-left corner, left triangle
282
- 10: { x: 80, y: 150 },
283
- // inner diamond, left
284
- 11: { x: 33, y: 80 },
285
- // top-left corner, left triangle
286
- 12: { x: 80, y: 33 }
287
- // top-left corner, upper triangle
288
- };
289
- var EAST_SIGN_POSITIONS = {
290
- 1: { x: 150, y: 55 },
291
- 2: { x: 235, y: 24 },
292
- 3: { x: 276, y: 62 },
293
- 4: { x: 242, y: 150 },
294
- 5: { x: 276, y: 238 },
295
- 6: { x: 235, y: 276 },
296
- 7: { x: 150, y: 245 },
297
- 8: { x: 65, y: 276 },
298
- 9: { x: 24, y: 238 },
299
- 10: { x: 58, y: 150 },
300
- 11: { x: 24, y: 62 },
301
- 12: { x: 65, y: 24 }
256
+ function toKundliViewModel(meta, divisionLabel) {
257
+ const placements = {};
258
+ for (const sign of SIGNS_ORDER) placements[sign.toLowerCase()] = [];
259
+ let lagnaSign = "";
260
+ for (const [name, pos] of Object.entries(meta ?? {})) {
261
+ const rashiKey = (pos?.rashi ?? "").toLowerCase();
262
+ if (name === "Lagna" || pos?.graha === "Lagna") {
263
+ lagnaSign = RASHI_TO_SIGN[rashiKey] ?? "";
264
+ continue;
265
+ }
266
+ if (!rashiKey || !(rashiKey in placements)) continue;
267
+ placements[rashiKey]?.push({
268
+ graha: pos.graha ?? name,
269
+ longitude: pos.longitude,
270
+ nakshatra: pos.nakshatra,
271
+ isRetrograde: pos.isRetrograde,
272
+ awastha: pos.awastha
273
+ });
274
+ }
275
+ return { lagnaSign, placements, divisionLabel };
276
+ }
277
+ var SOUTH_CELL = INNER / 4;
278
+ var SOUTH_CELL_GRID = {
279
+ Pisces: { col: 0, row: 0 },
280
+ Aries: { col: 1, row: 0 },
281
+ Taurus: { col: 2, row: 0 },
282
+ Gemini: { col: 3, row: 0 },
283
+ Cancer: { col: 3, row: 1 },
284
+ Leo: { col: 3, row: 2 },
285
+ Virgo: { col: 3, row: 3 },
286
+ Libra: { col: 2, row: 3 },
287
+ Scorpio: { col: 1, row: 3 },
288
+ Sagittarius: { col: 0, row: 3 },
289
+ Capricorn: { col: 0, row: 2 },
290
+ Aquarius: { col: 0, row: 1 }
302
291
  };
303
- function renderSouthHouseGroup(h) {
304
- const center = SOUTH_HOUSE_CENTERS[h.number];
305
- const signPos = SOUTH_SIGN_POSITIONS[h.number];
306
- if (!center || !signPos) return nothing;
307
- const signAbbr = SIGN_ABBR[h.sign] ?? "";
308
- const baseY = h.isLagna ? center.y + 8 : center.y;
292
+ function southCellRect(sign) {
293
+ const g = SOUTH_CELL_GRID[sign] ?? { col: 0, row: 0 };
294
+ return {
295
+ x: MARGIN + g.col * SOUTH_CELL,
296
+ y: MARGIN + g.row * SOUTH_CELL,
297
+ w: SOUTH_CELL,
298
+ h: SOUTH_CELL
299
+ };
300
+ }
301
+ function renderSouthFrame(divisionLabel) {
302
+ const a = MARGIN;
303
+ const b = MARGIN + SOUTH_CELL;
304
+ const c = MARGIN + 2 * SOUTH_CELL;
305
+ const d = MARGIN + 3 * SOUTH_CELL;
306
+ const e = VIEW_BOX - MARGIN;
307
+ return svg`
308
+ <rect class="line" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
309
+ <line class="line" x1=${a} y1=${b} x2=${e} y2=${b} stroke-width="1" />
310
+ <line class="line" x1=${a} y1=${d} x2=${e} y2=${d} stroke-width="1" />
311
+ <line class="line" x1=${b} y1=${a} x2=${b} y2=${e} stroke-width="1" />
312
+ <line class="line" x1=${d} y1=${a} x2=${d} y2=${e} stroke-width="1" />
313
+ <line class="line" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width="1" />
314
+ <line class="line" x1=${d} y1=${c} x2=${e} y2=${c} stroke-width="1" />
315
+ <line class="line" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width="1" />
316
+ <line class="line" x1=${c} y1=${d} x2=${c} y2=${e} stroke-width="1" />
317
+ ${divisionLabel ? svg`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing}
318
+ `;
319
+ }
320
+ function houseNumberInSign(sign, lagnaSign) {
321
+ const lagnaIdx = SIGNS_ORDER.findIndex((s) => s === lagnaSign);
322
+ const signIdx = SIGNS_ORDER.findIndex((s) => s === sign);
323
+ if (lagnaIdx === -1 || signIdx === -1) return 0;
324
+ return (signIdx - lagnaIdx + 12) % 12 + 1;
325
+ }
326
+ function renderSouthCell(sign, planets, isLagna, houseNum) {
327
+ const r = southCellRect(sign);
328
+ const cx = r.x + r.w / 2;
329
+ const cy = r.y + r.h / 2;
330
+ const signAbbr = SIGN_ABBR[sign] ?? sign.slice(0, 2);
331
+ const slashInset = 14;
309
332
  return svg`
310
- <g>
311
- ${h.isLagna ? svg`<rect
312
- class="lagna-bg"
313
- x=${center.x - 30} y=${center.y - 28}
314
- width="60" height="56" rx="6"
315
- />` : nothing}
316
- ${signAbbr ? svg`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing}
317
- ${h.isLagna ? svg`<text class="lagna-marker" x=${center.x} y=${center.y - 18} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : nothing}
318
- ${renderPlanetStack(h.planets, center.x, baseY, 13)}
333
+ <g class=${isLagna ? "cell lagna" : "cell"}>
334
+ ${isLagna ? svg`
335
+ <rect class="lagna-bg" x=${r.x} y=${r.y} width=${r.w} height=${r.h} />
336
+ <line class="lagna-slash" x1=${r.x + r.w - slashInset} y1=${r.y + slashInset} x2=${r.x + slashInset} y2=${r.y + r.h - slashInset} stroke-width="1.2" />
337
+ ` : nothing}
338
+ <text class="sign-text" x=${r.x + 6} y=${r.y + 12} text-anchor="start" dominant-baseline="central">${signAbbr}</text>
339
+ ${houseNum > 0 ? svg`<text class="house-num" x=${r.x + r.w - 6} y=${r.y + 12} text-anchor="end" dominant-baseline="central">${houseNum}</text>` : nothing}
340
+ ${planets.length ? renderPlanetStack(planets, sign, cx, cy + 4, 14) : nothing}
319
341
  </g>
320
342
  `;
321
343
  }
322
- function renderNorthFrame() {
344
+ function renderSouthSvg(vm) {
345
+ const lagnaKey = vm.lagnaSign.toLowerCase();
346
+ return svg`
347
+ ${renderSouthFrame(vm.divisionLabel)}
348
+ ${SIGNS_ORDER.map(
349
+ (sign) => renderSouthCell(
350
+ sign,
351
+ vm.placements[sign.toLowerCase()] ?? [],
352
+ sign.toLowerCase() === lagnaKey,
353
+ houseNumberInSign(sign, vm.lagnaSign)
354
+ )
355
+ )}
356
+ `;
357
+ }
358
+ var NORTH_VERTICES = {
359
+ tl: { x: MARGIN, y: MARGIN },
360
+ tr: { x: VIEW_BOX - MARGIN, y: MARGIN },
361
+ br: { x: VIEW_BOX - MARGIN, y: VIEW_BOX - MARGIN },
362
+ bl: { x: MARGIN, y: VIEW_BOX - MARGIN },
363
+ top: { x: CENTRE, y: MARGIN },
364
+ right: { x: VIEW_BOX - MARGIN, y: CENTRE },
365
+ bottom: { x: CENTRE, y: VIEW_BOX - MARGIN },
366
+ left: { x: MARGIN, y: CENTRE },
367
+ tlMid: { x: CENTRE - INNER / 4, y: CENTRE - INNER / 4 },
368
+ trMid: { x: CENTRE + INNER / 4, y: CENTRE - INNER / 4 },
369
+ brMid: { x: CENTRE + INNER / 4, y: CENTRE + INNER / 4 },
370
+ blMid: { x: CENTRE - INNER / 4, y: CENTRE + INNER / 4 }
371
+ };
372
+ function centroidOf(pts) {
373
+ const x = pts.reduce((s, p) => s + p.x, 0) / pts.length;
374
+ const y = pts.reduce((s, p) => s + p.y, 0) / pts.length;
375
+ return { x, y };
376
+ }
377
+ var NORTH_HOUSE_CENTERS = {
378
+ 1: { x: CENTRE, y: NORTH_VERTICES.tlMid.y },
379
+ 2: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.top, NORTH_VERTICES.tlMid]),
380
+ 3: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.left, NORTH_VERTICES.tlMid]),
381
+ 4: { x: NORTH_VERTICES.tlMid.x, y: CENTRE },
382
+ 5: centroidOf([NORTH_VERTICES.bl, NORTH_VERTICES.left, NORTH_VERTICES.blMid]),
383
+ 6: centroidOf([
384
+ NORTH_VERTICES.bl,
385
+ NORTH_VERTICES.bottom,
386
+ NORTH_VERTICES.blMid
387
+ ]),
388
+ 7: { x: CENTRE, y: NORTH_VERTICES.blMid.y },
389
+ 8: centroidOf([
390
+ NORTH_VERTICES.br,
391
+ NORTH_VERTICES.bottom,
392
+ NORTH_VERTICES.brMid
393
+ ]),
394
+ 9: centroidOf([
395
+ NORTH_VERTICES.br,
396
+ NORTH_VERTICES.right,
397
+ NORTH_VERTICES.brMid
398
+ ]),
399
+ 10: { x: NORTH_VERTICES.brMid.x, y: CENTRE },
400
+ 11: centroidOf([
401
+ NORTH_VERTICES.tr,
402
+ NORTH_VERTICES.right,
403
+ NORTH_VERTICES.trMid
404
+ ]),
405
+ 12: centroidOf([NORTH_VERTICES.tr, NORTH_VERTICES.top, NORTH_VERTICES.trMid])
406
+ };
407
+ function rashiInHouse(houseNum, lagnaSign) {
408
+ const lagnaIdx = SIGNS_ORDER.findIndex((s) => s === lagnaSign);
409
+ if (lagnaIdx === -1) return houseNum;
410
+ return (lagnaIdx + houseNum - 1) % 12 + 1;
411
+ }
412
+ function renderNorthFrame(divisionLabel) {
413
+ const { tl, tr, br, bl, top, right, bottom, left } = NORTH_VERTICES;
323
414
  return svg`
324
- <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1.5" />
325
- <line class="line" x1="150" y1="10" x2="150" y2="290" stroke-width="1" />
326
- <line class="line" x1="10" y1="150" x2="290" y2="150" stroke-width="1" />
327
- <line class="line" x1="150" y1="10" x2="10" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
328
- <line class="line" x1="150" y1="10" x2="290" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
329
- <line class="line" x1="150" y1="290" x2="10" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
330
- <line class="line" x1="150" y1="290" x2="290" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
415
+ <rect class="line" x=${tl.x} y=${tl.y} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
416
+ <polygon class="line" points="${top.x},${top.y} ${right.x},${right.y} ${bottom.x},${bottom.y} ${left.x},${left.y}" stroke-width="1" fill="none" />
417
+ <line class="line" x1=${tl.x} y1=${tl.y} x2=${br.x} y2=${br.y} stroke-width="1" />
418
+ <line class="line" x1=${tr.x} y1=${tr.y} x2=${bl.x} y2=${bl.y} stroke-width="1" />
419
+ ${divisionLabel ? svg`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing}
331
420
  `;
332
421
  }
333
- function renderNorthHouseGroup(h) {
334
- const center = NORTH_HOUSE_CENTERS[h.number];
335
- if (!center) return nothing;
336
- const signAbbr = SIGN_ABBR[h.sign] ?? "";
422
+ function renderNorthCell(houseNum, rashiNum, sign, planets, isLagna) {
423
+ const c = NORTH_HOUSE_CENTERS[houseNum];
424
+ if (!c) return svg``;
425
+ const rashiOffsetY = Math.min(14, Math.abs(c.y - CENTRE) * 0.45 + 6);
426
+ const ascOffsetY = rashiOffsetY + 12;
337
427
  return svg`
338
- <g>
339
- ${h.isLagna ? svg`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="22" />` : nothing}
340
- ${signAbbr ? svg`<text class="sign-text" x=${center.x} y=${center.y - 10} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing}
341
- <text class="house-num" x=${center.x} y=${center.y + 2} text-anchor="middle" dominant-baseline="central">${h.number}</text>
342
- ${renderPlanetStack(h.planets, center.x, center.y + 14, 11)}
428
+ <g class=${isLagna ? "cell lagna" : "cell"}>
429
+ <text class="rashi-num" x=${c.x} y=${c.y - rashiOffsetY} text-anchor="middle" dominant-baseline="central">${rashiNum}</text>
430
+ ${isLagna ? svg`<text class="lagna-marker" x=${c.x} y=${c.y - ascOffsetY} text-anchor="middle" dominant-baseline="central">Asc</text>` : nothing}
431
+ ${planets.length ? renderPlanetStack(planets, sign, c.x, c.y + 8, 12) : nothing}
343
432
  </g>
344
433
  `;
345
434
  }
346
- function renderSouthFrame() {
435
+ function renderNorthSvg(vm) {
436
+ const lagnaSign = vm.lagnaSign || "Aries";
347
437
  return svg`
348
- <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1.5" />
349
- <polygon class="line" points="220,80 220,220 80,220 80,80" stroke-width="1" fill="none" />
350
- <line class="line" x1="150" y1="10" x2="80" y2="80" stroke-width="1" />
351
- <line class="line" x1="150" y1="10" x2="220" y2="80" stroke-width="1" />
352
- <line class="line" x1="290" y1="150" x2="220" y2="80" stroke-width="1" />
353
- <line class="line" x1="290" y1="150" x2="220" y2="220" stroke-width="1" />
354
- <line class="line" x1="150" y1="290" x2="220" y2="220" stroke-width="1" />
355
- <line class="line" x1="150" y1="290" x2="80" y2="220" stroke-width="1" />
356
- <line class="line" x1="10" y1="150" x2="80" y2="220" stroke-width="1" />
357
- <line class="line" x1="10" y1="150" x2="80" y2="80" stroke-width="1" />
438
+ ${renderNorthFrame(vm.divisionLabel)}
439
+ ${Array.from({ length: 12 }, (_, i) => {
440
+ const houseNum = i + 1;
441
+ const rashiNum = rashiInHouse(houseNum, lagnaSign);
442
+ const sign = SIGNS_ORDER[rashiNum - 1] ?? "Aries";
443
+ return renderNorthCell(
444
+ houseNum,
445
+ rashiNum,
446
+ sign,
447
+ vm.placements[sign.toLowerCase()] ?? [],
448
+ houseNum === 1
449
+ );
450
+ })}
358
451
  `;
359
452
  }
360
- function renderEastFrame() {
453
+ var EAST_CELL = INNER / 3;
454
+ function eastCells() {
455
+ const a = MARGIN;
456
+ const b = MARGIN + EAST_CELL;
457
+ const c = MARGIN + 2 * EAST_CELL;
458
+ const d = VIEW_BOX - MARGIN;
459
+ const aries = [
460
+ { x: b, y: a },
461
+ { x: c, y: a },
462
+ { x: c, y: b },
463
+ { x: b, y: b }
464
+ ];
465
+ const cancer = [
466
+ { x: a, y: b },
467
+ { x: b, y: b },
468
+ { x: b, y: c },
469
+ { x: a, y: c }
470
+ ];
471
+ const libra = [
472
+ { x: b, y: c },
473
+ { x: c, y: c },
474
+ { x: c, y: d },
475
+ { x: b, y: d }
476
+ ];
477
+ const capricorn = [
478
+ { x: c, y: b },
479
+ { x: d, y: b },
480
+ { x: d, y: c },
481
+ { x: c, y: c }
482
+ ];
483
+ const taurus = [
484
+ { x: a, y: a },
485
+ { x: b, y: a },
486
+ { x: b, y: b }
487
+ ];
488
+ const gemini = [
489
+ { x: a, y: a },
490
+ { x: b, y: b },
491
+ { x: a, y: b }
492
+ ];
493
+ const leo = [
494
+ { x: a, y: c },
495
+ { x: b, y: c },
496
+ { x: a, y: d }
497
+ ];
498
+ const virgo = [
499
+ { x: b, y: c },
500
+ { x: b, y: d },
501
+ { x: a, y: d }
502
+ ];
503
+ const scorpio = [
504
+ { x: c, y: c },
505
+ { x: c, y: d },
506
+ { x: d, y: d }
507
+ ];
508
+ const sagittarius = [
509
+ { x: c, y: c },
510
+ { x: d, y: d },
511
+ { x: d, y: c }
512
+ ];
513
+ const aquarius = [
514
+ { x: d, y: a },
515
+ { x: d, y: b },
516
+ { x: c, y: b }
517
+ ];
518
+ const pisces = [
519
+ { x: c, y: a },
520
+ { x: d, y: a },
521
+ { x: c, y: b }
522
+ ];
523
+ const polys = {
524
+ Aries: aries,
525
+ Taurus: taurus,
526
+ Gemini: gemini,
527
+ Cancer: cancer,
528
+ Leo: leo,
529
+ Virgo: virgo,
530
+ Libra: libra,
531
+ Scorpio: scorpio,
532
+ Sagittarius: sagittarius,
533
+ Capricorn: capricorn,
534
+ Aquarius: aquarius,
535
+ Pisces: pisces
536
+ };
537
+ const out = {};
538
+ for (const [sign, points] of Object.entries(polys)) {
539
+ out[sign] = { points: [...points], centroid: centroidOf(points) };
540
+ }
541
+ return out;
542
+ }
543
+ var EAST_CELLS = eastCells();
544
+ function renderEastFrame(divisionLabel) {
545
+ const a = MARGIN;
546
+ const b = MARGIN + EAST_CELL;
547
+ const c = MARGIN + 2 * EAST_CELL;
548
+ const d = VIEW_BOX - MARGIN;
361
549
  return svg`
362
- <rect class="line" x="10" y="10" width="280" height="280" stroke-width="1.5" fill="none" />
363
- <line class="line" x1="10" y1="10" x2="290" y2="290" stroke-width="1" />
364
- <line class="line" x1="290" y1="10" x2="10" y2="290" stroke-width="1" />
365
- <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1" fill="none" />
550
+ <rect class="line" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
551
+ <line class="line" x1=${a} y1=${b} x2=${b} y2=${b} stroke-width="1" />
552
+ <line class="line" x1=${c} y1=${b} x2=${d} y2=${b} stroke-width="1" />
553
+ <line class="line" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width="1" />
554
+ <line class="line" x1=${c} y1=${c} x2=${d} y2=${c} stroke-width="1" />
555
+ <line class="line" x1=${b} y1=${a} x2=${b} y2=${b} stroke-width="1" />
556
+ <line class="line" x1=${b} y1=${c} x2=${b} y2=${d} stroke-width="1" />
557
+ <line class="line" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width="1" />
558
+ <line class="line" x1=${c} y1=${c} x2=${c} y2=${d} stroke-width="1" />
559
+ <line class="line" x1=${a} y1=${a} x2=${b} y2=${b} stroke-width="1" />
560
+ <line class="line" x1=${d} y1=${a} x2=${c} y2=${b} stroke-width="1" />
561
+ <line class="line" x1=${d} y1=${d} x2=${c} y2=${c} stroke-width="1" />
562
+ <line class="line" x1=${a} y1=${d} x2=${b} y2=${c} stroke-width="1" />
563
+ ${divisionLabel ? svg`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing}
366
564
  `;
367
565
  }
368
- function renderEastHouseGroup(h) {
369
- const center = EAST_HOUSE_CENTERS[h.number];
370
- const signPos = EAST_SIGN_POSITIONS[h.number];
371
- if (!center || !signPos) return nothing;
372
- const signAbbr = SIGN_ABBR[h.sign] ?? "";
566
+ function renderEastCell(sign, planets, isLagna, houseNum) {
567
+ const cell = EAST_CELLS[sign];
568
+ if (!cell) return svg``;
569
+ const { centroid: cen, points } = cell;
570
+ const signAbbr = SIGN_ABBR[sign] ?? sign.slice(0, 2);
571
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
373
572
  return svg`
374
- <g>
375
- ${h.isLagna ? svg`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="20" />` : nothing}
376
- ${signAbbr ? svg`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing}
377
- ${h.isLagna ? svg`<text class="lagna-marker" x=${center.x} y=${center.y - 14} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : nothing}
378
- ${renderPlanetStack(h.planets, center.x, center.y + 2, 11)}
573
+ <g class=${isLagna ? "cell lagna" : "cell"}>
574
+ ${isLagna ? svg`<polygon class="lagna-bg" points=${polyPoints} />` : nothing}
575
+ <text class="sign-text" x=${cen.x} y=${cen.y - 16} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>
576
+ ${houseNum > 0 ? svg`<text class="house-num" x=${cen.x + 18} y=${cen.y - 16} text-anchor="start" dominant-baseline="central">${houseNum}</text>` : nothing}
577
+ ${isLagna ? svg`<text class="lagna-marker" x=${cen.x} y=${cen.y - 30} text-anchor="middle" dominant-baseline="central">Asc</text>` : nothing}
578
+ ${planets.length ? renderPlanetStack(planets, sign, cen.x, cen.y + 4, 12) : nothing}
379
579
  </g>
380
580
  `;
381
581
  }
382
- function buildHousesFromMeta(meta) {
383
- const byRashi = /* @__PURE__ */ new Map();
384
- let lagnaKey = "";
385
- for (const [name, pos] of Object.entries(meta)) {
386
- const rashiKey = (pos?.rashi ?? "").toLowerCase();
387
- if (name === "Lagna" || pos?.graha === "Lagna") {
388
- lagnaKey = rashiKey;
389
- continue;
390
- }
391
- if (!rashiKey) continue;
392
- const list = byRashi.get(rashiKey) ?? [];
393
- list.push({
394
- graha: pos.graha ?? name,
395
- longitude: pos.longitude,
396
- nakshatra: pos.nakshatra,
397
- isRetrograde: pos.isRetrograde,
398
- awastha: pos.awastha
399
- });
400
- byRashi.set(rashiKey, list);
401
- }
402
- return SIGNS_ORDER.map((sign, i) => {
403
- const key = sign.toLowerCase();
404
- return {
405
- number: i + 1,
582
+ function renderEastSvg(vm) {
583
+ const lagnaKey = vm.lagnaSign.toLowerCase();
584
+ return svg`
585
+ ${renderEastFrame(vm.divisionLabel)}
586
+ ${SIGNS_ORDER.map(
587
+ (sign) => renderEastCell(
406
588
  sign,
407
- planets: byRashi.get(key) ?? [],
408
- isLagna: lagnaKey === key
409
- };
410
- });
589
+ vm.placements[sign.toLowerCase()] ?? [],
590
+ sign.toLowerCase() === lagnaKey,
591
+ houseNumberInSign(sign, vm.lagnaSign)
592
+ )
593
+ )}
594
+ `;
595
+ }
596
+ function renderKundliSvg(vm, style) {
597
+ switch (style) {
598
+ case "north":
599
+ return renderNorthSvg(vm);
600
+ case "east":
601
+ return renderEastSvg(vm);
602
+ default:
603
+ return renderSouthSvg(vm);
604
+ }
605
+ }
606
+ function renderKundliStyleTablist(active, setStyle) {
607
+ const onKeyDown = (e) => {
608
+ const idx = CHART_STYLES.findIndex((s) => s.id === active);
609
+ if (e.key === "ArrowRight") {
610
+ e.preventDefault();
611
+ const next = CHART_STYLES[(idx + 1) % CHART_STYLES.length];
612
+ if (next) setStyle(next.id);
613
+ } else if (e.key === "ArrowLeft") {
614
+ e.preventDefault();
615
+ const next = CHART_STYLES[(idx - 1 + CHART_STYLES.length) % CHART_STYLES.length];
616
+ if (next) setStyle(next.id);
617
+ }
618
+ };
619
+ return html`<div
620
+ class="kundli-tablist"
621
+ role="tablist"
622
+ aria-label="Kundli style"
623
+ @keydown=${onKeyDown}
624
+ >
625
+ ${CHART_STYLES.map(
626
+ (s) => html`<button
627
+ type="button"
628
+ class="kundli-tab"
629
+ role="tab"
630
+ id="kundli-tab-${s.id}"
631
+ aria-selected=${active === s.id ? "true" : "false"}
632
+ tabindex=${active === s.id ? "0" : "-1"}
633
+ @click=${() => setStyle(s.id)}
634
+ >
635
+ ${s.label}
636
+ </button>`
637
+ )}
638
+ </div>`;
411
639
  }
412
640
 
641
+ // packages/ui/src/utils/kundli-styles.ts
642
+ import { css as css2 } from "lit";
643
+ var kundliStyles = css2`
644
+ .wrap {
645
+ display: grid;
646
+ gap: var(--roxy-space-md, 1rem);
647
+ }
648
+ .header {
649
+ display: flex;
650
+ flex-wrap: wrap;
651
+ align-items: center;
652
+ justify-content: space-between;
653
+ gap: var(--roxy-space-sm, 0.5rem);
654
+ }
655
+ .title {
656
+ font-size: var(--roxy-text-lg, 1.125rem);
657
+ font-weight: var(--roxy-weight-bold, 600);
658
+ margin: 0;
659
+ }
660
+ .kundli-tablist {
661
+ display: inline-flex;
662
+ gap: 2px;
663
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
664
+ }
665
+ .kundli-tab {
666
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
667
+ font-size: var(--roxy-text-sm, 0.875rem);
668
+ background: none;
669
+ border: none;
670
+ border-bottom: 2px solid transparent;
671
+ margin-bottom: -2px;
672
+ cursor: pointer;
673
+ color: var(--roxy-muted, #71717a);
674
+ font-family: inherit;
675
+ transition: color var(--roxy-motion-duration, 200ms)
676
+ var(--roxy-motion-easing, ease);
677
+ }
678
+ .kundli-tab[aria-selected='true'] {
679
+ color: var(--roxy-accent-fg, #b45309);
680
+ border-bottom-color: var(--roxy-accent, #f59e0b);
681
+ font-weight: var(--roxy-weight-bold, 600);
682
+ }
683
+ .kundli-tab:hover:not([aria-selected='true']) {
684
+ color: var(--roxy-fg, #0a0a0a);
685
+ }
686
+ .kundli-tab:focus-visible {
687
+ outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
688
+ outline-offset: 2px;
689
+ border-radius: 4px;
690
+ }
691
+ svg {
692
+ display: block;
693
+ width: 100%;
694
+ max-width: 560px;
695
+ aspect-ratio: 1 / 1;
696
+ height: auto;
697
+ margin: 0 auto;
698
+ }
699
+ .line {
700
+ fill: transparent;
701
+ stroke: var(--roxy-border, #d4d4d8);
702
+ }
703
+ .sign-text {
704
+ fill: var(--roxy-muted, #71717a);
705
+ font-size: 11px;
706
+ font-weight: 500;
707
+ font-family: var(--roxy-font-sans);
708
+ text-transform: uppercase;
709
+ letter-spacing: 0.04em;
710
+ }
711
+ .rashi-num {
712
+ fill: var(--roxy-muted, #71717a);
713
+ font-size: 12px;
714
+ font-weight: 500;
715
+ font-family: var(--roxy-font-sans);
716
+ }
717
+ .house-num {
718
+ fill: var(--roxy-accent-fg, #b45309);
719
+ font-size: 11px;
720
+ font-weight: 600;
721
+ font-family: var(--roxy-font-sans);
722
+ opacity: 0.85;
723
+ }
724
+ .planet-text {
725
+ fill: var(--roxy-fg, #0a0a0a);
726
+ font-size: 13px;
727
+ font-weight: 600;
728
+ font-family: var(--roxy-font-sans);
729
+ }
730
+ .centre-label {
731
+ fill: var(--roxy-muted, #71717a);
732
+ font-size: 14px;
733
+ font-weight: 600;
734
+ font-family: var(--roxy-font-sans);
735
+ letter-spacing: 0.02em;
736
+ }
737
+ .lagna-marker {
738
+ fill: var(--roxy-accent-fg, #b45309);
739
+ font-size: 10px;
740
+ font-weight: 700;
741
+ font-family: var(--roxy-font-sans);
742
+ letter-spacing: 0.08em;
743
+ text-transform: uppercase;
744
+ }
745
+ .lagna-bg {
746
+ fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 14%, transparent);
747
+ }
748
+ .lagna-slash {
749
+ stroke: var(--roxy-accent, #f59e0b);
750
+ stroke-linecap: round;
751
+ opacity: 0.7;
752
+ }
753
+ `;
754
+
413
755
  // packages/ui/src/components/vedic-kundli.ts
414
756
  var RoxyVedicKundli = class extends LitElement {
415
757
  constructor() {
416
758
  super(...arguments);
417
759
  this.data = null;
418
- this.chartStyle = "south";
760
+ this.chartStyle = "north";
761
+ this.setStyle = (next) => {
762
+ this.chartStyle = next;
763
+ };
419
764
  }
420
- buildHouses() {
421
- if (!this.data?.meta) return [];
422
- return buildHousesFromMeta(this.data.meta);
765
+ viewModel() {
766
+ if (!this.data?.meta) return null;
767
+ return toKundliViewModel(this.data.meta, "D1 Rashi");
423
768
  }
424
769
  render() {
425
- if (!this.data)
426
- return html`<div class="roxy-empty" role="status">No kundli data</div>`;
427
- const houses = this.buildHouses();
428
- const style = this.chartStyle;
429
- const frame = style === "north" ? renderNorthFrame() : style === "east" ? renderEastFrame() : renderSouthFrame();
430
- const houseGroup = style === "north" ? renderNorthHouseGroup : style === "east" ? renderEastHouseGroup : renderSouthHouseGroup;
431
- return html`<div class="wrap">
432
- <h2 class="title">Vedic kundli</h2>
770
+ const vm = this.viewModel();
771
+ if (!vm)
772
+ return html2`<div class="roxy-empty" role="status">No kundli data</div>`;
773
+ return html2`<div class="wrap">
774
+ <div class="header">
775
+ <h2 class="title">Vedic kundli</h2>
776
+ ${renderKundliStyleTablist(this.chartStyle, this.setStyle)}
777
+ </div>
433
778
  <svg
434
- viewBox="0 0 300 300"
779
+ viewBox="0 0 400 400"
780
+ preserveAspectRatio="xMidYMid meet"
435
781
  role="img"
436
782
  aria-label="Vedic birth chart with twelve sign houses"
437
783
  >
438
784
  <title>Vedic kundli</title>
439
- ${frame}
440
- ${houses.map((h) => houseGroup(h))}
785
+ ${renderKundliSvg(vm, this.chartStyle)}
441
786
  </svg>
442
787
  </div>`;
443
788
  }
444
789
  };
445
- RoxyVedicKundli.styles = [
446
- baseStyles,
447
- css2`
448
- .wrap {
449
- display: grid;
450
- gap: var(--roxy-space-md, 1rem);
451
- }
452
- .title {
453
- font-size: var(--roxy-text-lg, 1.125rem);
454
- font-weight: var(--roxy-weight-bold, 600);
455
- margin: 0;
456
- }
457
- svg {
458
- display: block;
459
- width: 100%;
460
- max-width: 360px;
461
- margin: 0 auto;
462
- }
463
- .line {
464
- fill: transparent;
465
- stroke: var(--roxy-border, #e4e4e7);
466
- }
467
- .sign-text {
468
- fill: var(--roxy-muted, #71717a);
469
- font-size: 9px;
470
- font-weight: 500;
471
- font-family: var(--roxy-font-sans);
472
- }
473
- .planet-text {
474
- fill: var(--roxy-fg, #0a0a0a);
475
- font-size: 10px;
476
- font-weight: 600;
477
- font-family: var(--roxy-font-sans);
478
- }
479
- .house-num {
480
- fill: var(--roxy-muted, #71717a);
481
- font-size: 9px;
482
- font-weight: 400;
483
- font-family: var(--roxy-font-sans);
484
- }
485
- .lagna-marker {
486
- fill: var(--roxy-accent-fg, #b45309);
487
- font-size: 8px;
488
- font-weight: 700;
489
- font-family: var(--roxy-font-sans);
490
- letter-spacing: 0.05em;
491
- }
492
- .lagna-bg {
493
- fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
494
- stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
495
- stroke-width: 0.8;
496
- }
497
- `
498
- ];
790
+ RoxyVedicKundli.styles = [baseStyles, kundliStyles];
499
791
  __decorateClass([
500
792
  property({ attribute: false })
501
793
  ], RoxyVedicKundli.prototype, "data", 2);