@roxyapi/ui 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/AGENTS.md +27 -1
  2. package/README.md +115 -14
  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
package/dist/index.js CHANGED
@@ -221,6 +221,20 @@ var baseStyles = css`
221
221
  outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
222
222
  outline-offset: 2px;
223
223
  }
224
+
225
+ /* Force the text-style variant on every Unicode glyph in the component.
226
+ * macOS and iOS substitute coloured emoji glyphs for the planetary and
227
+ * gender Unicode code points (Mars, Venus, Mercury, etc.) when the
228
+ * system colour-emoji font wins font selection. The text-style variant
229
+ * keeps glyphs monochrome so they inherit the surrounding fill colour
230
+ * and match the brand palette consistently across platforms.
231
+ *
232
+ * font-variant-emoji is part of CSS Fonts 4 (Safari 17+, Chrome 134+,
233
+ * Firefox 139+). On older browsers the rule is silently ignored.
234
+ */
235
+ :host {
236
+ font-variant-emoji: text;
237
+ }
224
238
  `;
225
239
 
226
240
  // packages/ui/src/components/ashtakavarga-grid.ts
@@ -299,7 +313,11 @@ var RoxyAshtakavargaGrid = class extends LitElement {
299
313
  btn?.focus();
300
314
  });
301
315
  }
302
- heatClass(count) {
316
+ /**
317
+ * Bhinna bindus per planet per sign run 0..8 (sum of 0/1 contributions
318
+ * from each of the 8 reference points). Bucket directly by raw count.
319
+ */
320
+ bhinnaHeat(count) {
303
321
  if (count <= 1) return "heat-1";
304
322
  if (count <= 2) return "heat-2";
305
323
  if (count <= 3) return "heat-3";
@@ -308,6 +326,22 @@ var RoxyAshtakavargaGrid = class extends LitElement {
308
326
  if (count <= 6) return "heat-6";
309
327
  return "heat-7";
310
328
  }
329
+ /**
330
+ * Sarva bindus per sign are the column total across all 7 planets, range
331
+ * roughly 0..56 with typical values 20..40. Bucketed per classical
332
+ * interpretation: 25 below par, 25..30 average, 30..40 strong, 40+ very
333
+ * strong. Bucket spans intentionally widen at the extremes so a single
334
+ * outlier sign reads as exceptional.
335
+ */
336
+ sarvaHeat(count) {
337
+ if (count <= 18) return "heat-1";
338
+ if (count <= 23) return "heat-2";
339
+ if (count <= 28) return "heat-3";
340
+ if (count <= 32) return "heat-4";
341
+ if (count <= 37) return "heat-5";
342
+ if (count <= 42) return "heat-6";
343
+ return "heat-7";
344
+ }
311
345
  renderSarva(signs) {
312
346
  const sav = this.data.sarvashtakavarga;
313
347
  if (!sav) return html`<p class="roxy-empty">No sarvashtakavarga data</p>`;
@@ -322,7 +356,7 @@ var RoxyAshtakavargaGrid = class extends LitElement {
322
356
  <tbody>
323
357
  ${signs.map((sign, i) => {
324
358
  const count = sav.bindus[i] ?? 0;
325
- const hc = this.heatClass(count);
359
+ const hc = this.sarvaHeat(count);
326
360
  return html`<tr>
327
361
  <td>
328
362
  <div class="planet-cell">
@@ -363,7 +397,7 @@ var RoxyAshtakavargaGrid = class extends LitElement {
363
397
  (row) => html`<tr>
364
398
  <td>${row.planet}</td>
365
399
  ${row.bindus.map((count) => {
366
- const hc = this.heatClass(count);
400
+ const hc = this.bhinnaHeat(count);
367
401
  return html`<td class="${`heat-cell ${hc}`}">${count}</td>`;
368
402
  })}
369
403
  <td>${row.total}</td>
@@ -510,27 +544,68 @@ RoxyAshtakavargaGrid.styles = [
510
544
  border-bottom: none;
511
545
  }
512
546
 
513
- /* Heat cells */
547
+ /* Heat cells. Single base hue (var --roxy-heat) mixed with
548
+ * transparent at increasing percentages produces seven readable
549
+ * tiers in both light and dark themes. Text colour stays
550
+ * var(--roxy-fg) so it inverts with the host theme without
551
+ * per-tier overrides. */
514
552
  .heat-cell {
515
553
  border-radius: var(--roxy-radius-sm, 4px);
516
554
  font-weight: var(--roxy-weight-bold, 600);
517
555
  min-width: 2rem;
518
556
  font-variant-numeric: tabular-nums;
557
+ color: var(--roxy-fg, currentColor);
519
558
  }
520
559
 
521
- .heat-1 { background: var(--roxy-heat-1, #f0fdf4); color: var(--roxy-fg, #0a0a0a); }
522
- .heat-2 { background: var(--roxy-heat-2, #d1fae5); color: var(--roxy-fg, #0a0a0a); }
523
- .heat-3 { background: var(--roxy-heat-3, #a7f3d0); color: var(--roxy-fg, #0a0a0a); }
524
- .heat-4 { background: var(--roxy-heat-4, #fde68a); color: var(--roxy-fg, #0a0a0a); }
525
- .heat-5 { background: var(--roxy-heat-5, #fdba74); color: var(--roxy-fg, #0a0a0a); }
526
- .heat-6 { background: var(--roxy-heat-6, #fb923c); color: var(--roxy-fg, #0a0a0a); }
527
- .heat-7 { background: var(--roxy-heat-7, #ef4444); color: var(--roxy-fg, #0a0a0a); }
560
+ .heat-1 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 6%, transparent); }
561
+ .heat-2 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 14%, transparent); }
562
+ .heat-3 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 26%, transparent); }
563
+ .heat-4 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 40%, transparent); }
564
+ .heat-5 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 55%, transparent); }
565
+ .heat-6 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 72%, transparent); }
566
+ .heat-7 { background: color-mix(in srgb, var(--roxy-heat, #ef4444) 90%, transparent); }
528
567
 
529
568
  /* Bhinna grid: planet header column narrower */
530
569
  .bhinna-table th:first-child,
531
570
  .bhinna-table td:first-child {
532
571
  min-width: 5rem;
533
572
  }
573
+
574
+ /* Tight cells below 480px so the 14-column bhinna grid stops
575
+ * overflowing the viewport. The wrapper keeps overflow-x:auto as
576
+ * a fallback for very long content. */
577
+ @container (max-width: 480px) {
578
+ .bhinna-table th,
579
+ .bhinna-table td {
580
+ padding: 0.3rem 0.35rem;
581
+ font-size: var(--roxy-text-xs, 0.75rem);
582
+ }
583
+ .bhinna-table th:first-child,
584
+ .bhinna-table td:first-child {
585
+ min-width: 3.5rem;
586
+ }
587
+ .heat-cell {
588
+ min-width: 1.5rem;
589
+ }
590
+ }
591
+ /* Visual cue that the bhinna table is scrollable below the breakpoint:
592
+ * a soft gradient at the right edge so users see there is more to scroll. */
593
+ .overflow-scroll {
594
+ mask-image: linear-gradient(
595
+ to right,
596
+ transparent 0,
597
+ black 0.5rem,
598
+ black calc(100% - 1rem),
599
+ transparent 100%
600
+ );
601
+ -webkit-mask-image: linear-gradient(
602
+ to right,
603
+ transparent 0,
604
+ black 0.5rem,
605
+ black calc(100% - 1rem),
606
+ transparent 100%
607
+ );
608
+ }
534
609
  `
535
610
  ];
536
611
  __decorateClass([
@@ -788,12 +863,31 @@ var RoxyChoghadiyaGrid = class extends LitElement3 {
788
863
  super(...arguments);
789
864
  this.data = null;
790
865
  }
866
+ /**
867
+ * True when the current wall-clock time falls inside this period. Both
868
+ * `start` and `end` are ISO 8601 with timezone, so the comparison is
869
+ * timezone-aware via the host's `Date` parsing.
870
+ */
871
+ isCurrent(period) {
872
+ const now = Date.now();
873
+ const start = Date.parse(period.start);
874
+ const end = Date.parse(period.end);
875
+ if (Number.isNaN(start) || Number.isNaN(end)) return false;
876
+ return now >= start && now < end;
877
+ }
791
878
  renderTile(period) {
792
879
  const effectClass = period.effect === "Good" ? "good" : period.effect === "Bad" ? "bad" : "neutral";
880
+ const current = this.isCurrent(period);
793
881
  const lordGlyph = PLANET_GLYPH[capitalize(period.lord)] ?? "";
794
882
  const timeRange = `${fmtTime(period.start)} - ${fmtTime(period.end)}`;
795
- return html3`<div class="cho-tile ${effectClass}" role="listitem">
796
- <span class="tile-name">${period.name}</span>
883
+ return html3`<div
884
+ class="cho-tile ${effectClass}${current ? " now" : ""}"
885
+ role="listitem"
886
+ aria-current=${current ? "time" : "false"}
887
+ >
888
+ <span class="tile-name">
889
+ ${period.name}${current ? html3`<span class="now-badge">Now</span>` : nothing3}
890
+ </span>
797
891
  <span class="tile-time" aria-label="Time range">${timeRange}</span>
798
892
  <span class="tile-lord">
799
893
  ${lordGlyph ? html3`<span aria-hidden="true">${lordGlyph}</span>` : nothing3}
@@ -893,6 +987,21 @@ RoxyChoghadiyaGrid.styles = [
893
987
  background: transparent;
894
988
  color: var(--roxy-fg, #0a0a0a);
895
989
  }
990
+ .cho-tile.now {
991
+ outline: 2px solid var(--roxy-accent, #f59e0b);
992
+ outline-offset: 1px;
993
+ box-shadow: 0 0 0 4px
994
+ color-mix(in srgb, var(--roxy-accent, #f59e0b) 18%, transparent);
995
+ }
996
+ .now-badge {
997
+ display: inline-block;
998
+ margin-left: 0.4em;
999
+ font-size: var(--roxy-text-xs, 0.75rem);
1000
+ font-weight: var(--roxy-weight-bold, 600);
1001
+ color: var(--roxy-accent-fg, #b45309);
1002
+ text-transform: uppercase;
1003
+ letter-spacing: 0.06em;
1004
+ }
896
1005
  .tile-name {
897
1006
  font-size: var(--roxy-text-base, 1rem);
898
1007
  font-weight: var(--roxy-weight-bold, 600);
@@ -1196,12 +1305,39 @@ var RoxyDashaTimeline = class extends LitElement5 {
1196
1305
  </div>` : nothing5}
1197
1306
  </header>
1198
1307
 
1308
+ ${this.renderBirthBalance(d)}
1199
1309
  ${this.period === "current" ? this.renderCurrent(d) : nothing5}
1200
1310
  ${periods.length > 0 ? html5`<div class="timeline" role="list">
1201
1311
  ${periods.map((p) => this.renderBar(p, maxYears))}
1202
1312
  </div>` : nothing5}
1313
+ ${this.renderActiveInterpretation(periods)}
1203
1314
  </div>`;
1204
1315
  }
1316
+ renderBirthBalance(d) {
1317
+ if (!("birthDashaBalance" in d) || !d.birthDashaBalance) return nothing5;
1318
+ const b = d.birthDashaBalance;
1319
+ const lord = "nakshatraLord" in d && d.nakshatraLord ? d.nakshatraLord : "";
1320
+ const yrs = b.years ?? 0;
1321
+ const mo = b.months ?? 0;
1322
+ const da = b.days ?? 0;
1323
+ const parts = [];
1324
+ if (yrs) parts.push(`${yrs}y`);
1325
+ if (mo) parts.push(`${mo}m`);
1326
+ if (da) parts.push(`${da}d`);
1327
+ const remaining = parts.length ? parts.join(" ") : "0d";
1328
+ return html5`<p class="balance">
1329
+ Birth dasha balance: ${remaining} of
1330
+ ${lord ? html5`<strong>${lord}</strong>` : "the opening mahadasha"} remained at birth.
1331
+ </p>`;
1332
+ }
1333
+ renderActiveInterpretation(periods) {
1334
+ const active = periods.find((p) => this.isCurrent(p));
1335
+ if (!active?.interpretation) return nothing5;
1336
+ return html5`<details class="interp">
1337
+ <summary>${active.planet} mahadasha interpretation</summary>
1338
+ <p>${active.interpretation}</p>
1339
+ </details>`;
1340
+ }
1205
1341
  renderCurrent(d) {
1206
1342
  if (!("mahadasha" in d)) return nothing5;
1207
1343
  return html5`<div class="current">
@@ -1227,12 +1363,52 @@ var RoxyDashaTimeline = class extends LitElement5 {
1227
1363
  if ("antardashas" in d && d.antardashas?.length) return d.antardashas;
1228
1364
  return [];
1229
1365
  }
1366
+ /** True when the current wall-clock time falls between the period's start and end. */
1367
+ isCurrent(p) {
1368
+ if (!p.startDate || !p.endDate) return false;
1369
+ const now = Date.now();
1370
+ const start = Date.parse(p.startDate);
1371
+ const end = Date.parse(p.endDate);
1372
+ if (Number.isNaN(start) || Number.isNaN(end)) return false;
1373
+ return now >= start && now < end;
1374
+ }
1375
+ /**
1376
+ * Fractional progress (0..1) through a period at the current time. Used to
1377
+ * draw a vertical "now" marker inside the active bar. Returns -1 outside the
1378
+ * period so the caller can skip the marker.
1379
+ */
1380
+ progressIn(p) {
1381
+ if (!p.startDate || !p.endDate) return -1;
1382
+ const start = Date.parse(p.startDate);
1383
+ const end = Date.parse(p.endDate);
1384
+ const now = Date.now();
1385
+ if (Number.isNaN(start) || Number.isNaN(end) || now < start || now >= end || end <= start) {
1386
+ return -1;
1387
+ }
1388
+ return (now - start) / (end - start);
1389
+ }
1230
1390
  renderBar(p, max) {
1231
1391
  const years = p.durationYears;
1232
1392
  const width = max > 0 ? years / max * 100 : 0;
1233
- return html5`<div class="bar" role="listitem">
1234
- <span>${p.planet}</span>
1235
- <span class="bar-track"><span style="width: ${width}%"></span></span>
1393
+ const current = this.isCurrent(p);
1394
+ const progress = current ? this.progressIn(p) : -1;
1395
+ const trackClass = current ? "bar-track bar-now" : "bar-track";
1396
+ return html5`<div
1397
+ class=${current ? "bar now" : "bar"}
1398
+ role="listitem"
1399
+ aria-current=${current ? "time" : "false"}
1400
+ >
1401
+ <span>
1402
+ <strong>${p.planet}</strong>${current ? html5`<span class="now-badge">Now</span>` : nothing5}
1403
+ </span>
1404
+ <span class=${trackClass}>
1405
+ <span class="bar-fill" style="width: ${width}%"></span>
1406
+ ${progress >= 0 ? html5`<span
1407
+ class="bar-progress"
1408
+ style="left: ${progress * width}%"
1409
+ aria-hidden="true"
1410
+ ></span>` : nothing5}
1411
+ </span>
1236
1412
  <span class="dates">
1237
1413
  ${p.startDate ? formatYear(p.startDate) : ""}
1238
1414
  ${p.endDate ? html5`- ${formatYear(p.endDate)}` : ""}
@@ -1286,6 +1462,13 @@ RoxyDashaTimeline.styles = [
1286
1462
  color: var(--roxy-fg, #0a0a0a);
1287
1463
  }
1288
1464
 
1465
+ .balance {
1466
+ font-size: var(--roxy-text-sm, 0.875rem);
1467
+ color: var(--roxy-muted, #71717a);
1468
+ border-left: 2px solid var(--roxy-border, #e4e4e7);
1469
+ padding-left: var(--roxy-space-sm, 0.5rem);
1470
+ margin: 0;
1471
+ }
1289
1472
  .timeline {
1290
1473
  display: grid;
1291
1474
  gap: var(--roxy-space-xs, 0.25rem);
@@ -1297,26 +1480,69 @@ RoxyDashaTimeline.styles = [
1297
1480
  align-items: center;
1298
1481
  font-size: var(--roxy-text-sm, 0.875rem);
1299
1482
  }
1483
+ .bar.now strong {
1484
+ color: var(--roxy-accent-fg, #b45309);
1485
+ }
1486
+ .now-badge {
1487
+ display: inline-block;
1488
+ margin-left: 0.4em;
1489
+ font-size: var(--roxy-text-xs, 0.75rem);
1490
+ font-weight: var(--roxy-weight-bold, 600);
1491
+ color: var(--roxy-accent-fg, #b45309);
1492
+ text-transform: uppercase;
1493
+ letter-spacing: 0.06em;
1494
+ }
1300
1495
  .bar-track {
1496
+ position: relative;
1301
1497
  height: 14px;
1302
1498
  background: var(--roxy-border, #e4e4e7);
1303
1499
  border-radius: var(--roxy-radius-full, 9999px);
1304
1500
  overflow: hidden;
1305
1501
  }
1306
- .bar-track > span {
1502
+ .bar-fill {
1307
1503
  display: block;
1308
1504
  height: 100%;
1309
1505
  background: var(--roxy-accent, #f59e0b);
1506
+ opacity: 0.45;
1310
1507
  transition:
1311
1508
  width var(--roxy-motion-duration, 200ms)
1312
1509
  var(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));
1313
1510
  }
1511
+ .bar-now .bar-fill {
1512
+ opacity: 1;
1513
+ }
1514
+ .bar-progress {
1515
+ position: absolute;
1516
+ top: -2px;
1517
+ bottom: -2px;
1518
+ width: 2px;
1519
+ background: var(--roxy-accent-fg, #b45309);
1520
+ border-radius: 2px;
1521
+ box-shadow: 0 0 0 2px
1522
+ color-mix(in srgb, var(--roxy-accent, #f59e0b) 35%, transparent);
1523
+ }
1314
1524
  .dates {
1315
1525
  color: var(--roxy-muted, #71717a);
1316
1526
  font-size: var(--roxy-text-xs, 0.75rem);
1317
1527
  font-variant-numeric: tabular-nums;
1318
1528
  text-align: right;
1319
1529
  }
1530
+ details.interp {
1531
+ border: 1px solid var(--roxy-border, #e4e4e7);
1532
+ border-radius: var(--roxy-radius-md, 8px);
1533
+ padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
1534
+ background: var(--roxy-bg, #fff);
1535
+ }
1536
+ details.interp summary {
1537
+ cursor: pointer;
1538
+ font-size: var(--roxy-text-sm, 0.875rem);
1539
+ font-weight: var(--roxy-weight-bold, 600);
1540
+ }
1541
+ details.interp p {
1542
+ margin: var(--roxy-space-sm, 0.5rem) 0 0;
1543
+ font-size: var(--roxy-text-sm, 0.875rem);
1544
+ color: var(--roxy-muted, #71717a);
1545
+ }
1320
1546
  `
1321
1547
  ];
1322
1548
  __decorateClass([
@@ -1576,11 +1802,11 @@ RoxyData = __decorateClass([
1576
1802
  ], RoxyData);
1577
1803
 
1578
1804
  // packages/ui/src/components/divisional-chart.ts
1579
- import { css as css8, html as html7, LitElement as LitElement7, nothing as nothing8 } from "lit";
1805
+ import { css as css9, html as html8, LitElement as LitElement7, nothing as nothing8 } from "lit";
1580
1806
  import { customElement as customElement7, property as property7 } from "lit/decorators.js";
1581
1807
 
1582
1808
  // packages/ui/src/utils/kundli-render.ts
1583
- import { nothing as nothing7, svg as svg2 } from "lit";
1809
+ import { html as html7, nothing as nothing7, svg as svg2 } from "lit";
1584
1810
 
1585
1811
  // packages/ui/src/utils/degree.ts
1586
1812
  function normalizeLongitude(lon) {
@@ -1625,25 +1851,45 @@ function polarToCartesian(cx, cy, radius, angleDeg) {
1625
1851
  }
1626
1852
 
1627
1853
  // packages/ui/src/utils/kundli-render.ts
1854
+ var VIEW_BOX = 400;
1855
+ var MARGIN = 20;
1856
+ var INNER = VIEW_BOX - 2 * MARGIN;
1857
+ var CENTRE = VIEW_BOX / 2;
1628
1858
  var RASHI_TO_SIGN = Object.fromEntries(
1629
1859
  SIGNS_ORDER.map((s) => [s.toLowerCase(), s])
1630
1860
  );
1861
+ var CHART_STYLES = [
1862
+ { id: "north", label: "North" },
1863
+ { id: "south", label: "South" },
1864
+ { id: "east", label: "East" }
1865
+ ];
1631
1866
  var RETRO_MARK = "\u02B3";
1632
- function grahaLabel(p) {
1867
+ function isDivisionalPlacement(p, cellSign) {
1868
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
1869
+ return false;
1870
+ }
1871
+ return longitudeToSignPosition(p.longitude).sign.toLowerCase() !== cellSign.toLowerCase();
1872
+ }
1873
+ function grahaLabel(p, cellSign) {
1633
1874
  const abbr = PLANET_ABBR[capitalize(p.graha)] ?? p.graha.slice(0, 2);
1634
1875
  const retro = p.isRetrograde ? RETRO_MARK : "";
1635
- if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude)) {
1876
+ if (typeof p.longitude !== "number" || !Number.isFinite(p.longitude) || isDivisionalPlacement(p, cellSign)) {
1636
1877
  return `${abbr}${retro}`;
1637
1878
  }
1638
1879
  const { degree } = longitudeToSignPosition(p.longitude);
1639
1880
  return `${abbr} ${degree}\xB0${retro}`;
1640
1881
  }
1641
- function grahaTitle(p) {
1882
+ function grahaTitle(p, cellSign) {
1642
1883
  const parts = [capitalize(p.graha)];
1884
+ const divisional = isDivisionalPlacement(p, cellSign);
1885
+ if (divisional) {
1886
+ parts.push(`in ${cellSign}`);
1887
+ }
1643
1888
  if (typeof p.longitude === "number" && Number.isFinite(p.longitude)) {
1644
1889
  const sp = longitudeToSignPosition(p.longitude);
1890
+ const minute = String(sp.minute).padStart(2, "0");
1645
1891
  parts.push(
1646
- `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}' ${sp.sign}`
1892
+ divisional ? `D1: ${sp.degree}\xB0${minute}' ${sp.sign}` : `${sp.degree}\xB0${minute}' ${sp.sign}`
1647
1893
  );
1648
1894
  }
1649
1895
  if (p.nakshatra?.name) {
@@ -1654,249 +1900,562 @@ function grahaTitle(p) {
1654
1900
  if (p.isRetrograde) parts.push("retrograde");
1655
1901
  return parts.join(" \xB7 ");
1656
1902
  }
1657
- function renderPlanetStack(planets, cx, baseY, lineHeight) {
1903
+ function renderPlanetStack(planets, cellSign, cx, baseY, lineHeight) {
1658
1904
  const startY = baseY - (planets.length - 1) * lineHeight / 2;
1659
1905
  return planets.map((p, j) => {
1660
1906
  const yPos = startY + j * lineHeight;
1661
1907
  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>`;
1908
+ p,
1909
+ cellSign
1910
+ )}<title>${grahaTitle(p, cellSign)}</title></text>`;
1664
1911
  });
1665
1912
  }
1666
- var SOUTH_HOUSE_CENTERS = {
1667
- 1: { x: 150, y: 58 },
1668
- 2: { x: 205, y: 52 },
1669
- 3: { x: 253, y: 112 },
1670
- 4: { x: 243, y: 150 },
1671
- 5: { x: 253, y: 188 },
1672
- 6: { x: 205, y: 248 },
1673
- 7: { x: 150, y: 242 },
1674
- 8: { x: 95, y: 248 },
1675
- 9: { x: 47, y: 188 },
1676
- 10: { x: 57, y: 150 },
1677
- 11: { x: 47, y: 112 },
1678
- 12: { x: 95, y: 52 }
1679
- };
1680
- var SOUTH_SIGN_POSITIONS = {
1681
- 1: { x: 150, y: 35 },
1682
- 2: { x: 222, y: 40 },
1683
- 3: { x: 265, y: 100 },
1684
- 4: { x: 265, y: 150 },
1685
- 5: { x: 265, y: 200 },
1686
- 6: { x: 222, y: 260 },
1687
- 7: { x: 150, y: 265 },
1688
- 8: { x: 78, y: 260 },
1689
- 9: { x: 35, y: 200 },
1690
- 10: { x: 35, y: 150 },
1691
- 11: { x: 35, y: 100 },
1692
- 12: { x: 78, y: 40 }
1693
- };
1694
- var NORTH_HOUSE_CENTERS = {
1695
- 1: { x: 150, y: 60 },
1696
- 2: { x: 225, y: 100 },
1697
- 3: { x: 255, y: 150 },
1698
- 4: { x: 225, y: 200 },
1699
- 5: { x: 150, y: 240 },
1700
- 6: { x: 75, y: 200 },
1701
- 7: { x: 45, y: 150 },
1702
- 8: { x: 75, y: 100 },
1703
- 9: { x: 100, y: 80 },
1704
- 10: { x: 150, y: 108 },
1705
- 11: { x: 200, y: 80 },
1706
- 12: { x: 200, y: 220 }
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 }
1913
+ function toKundliViewModel(meta, divisionLabel) {
1914
+ const placements = {};
1915
+ for (const sign of SIGNS_ORDER) placements[sign.toLowerCase()] = [];
1916
+ let lagnaSign = "";
1917
+ for (const [name, pos] of Object.entries(meta ?? {})) {
1918
+ const rashiKey = (pos?.rashi ?? "").toLowerCase();
1919
+ if (name === "Lagna" || pos?.graha === "Lagna") {
1920
+ lagnaSign = RASHI_TO_SIGN[rashiKey] ?? "";
1921
+ continue;
1922
+ }
1923
+ if (!rashiKey || !(rashiKey in placements)) continue;
1924
+ placements[rashiKey]?.push({
1925
+ graha: pos.graha ?? name,
1926
+ longitude: pos.longitude,
1927
+ nakshatra: pos.nakshatra,
1928
+ isRetrograde: pos.isRetrograde,
1929
+ awastha: pos.awastha
1930
+ });
1931
+ }
1932
+ return { lagnaSign, placements, divisionLabel };
1933
+ }
1934
+ var SOUTH_CELL = INNER / 4;
1935
+ var SOUTH_CELL_GRID = {
1936
+ Pisces: { col: 0, row: 0 },
1937
+ Aries: { col: 1, row: 0 },
1938
+ Taurus: { col: 2, row: 0 },
1939
+ Gemini: { col: 3, row: 0 },
1940
+ Cancer: { col: 3, row: 1 },
1941
+ Leo: { col: 3, row: 2 },
1942
+ Virgo: { col: 3, row: 3 },
1943
+ Libra: { col: 2, row: 3 },
1944
+ Scorpio: { col: 1, row: 3 },
1945
+ Sagittarius: { col: 0, row: 3 },
1946
+ Capricorn: { col: 0, row: 2 },
1947
+ Aquarius: { col: 0, row: 1 }
1747
1948
  };
1748
- function renderSouthHouseGroup(h) {
1749
- const center = SOUTH_HOUSE_CENTERS[h.number];
1750
- const signPos = SOUTH_SIGN_POSITIONS[h.number];
1751
- if (!center || !signPos) return nothing7;
1752
- const signAbbr = SIGN_ABBR[h.sign] ?? "";
1753
- const baseY = h.isLagna ? center.y + 8 : center.y;
1949
+ function southCellRect(sign) {
1950
+ const g = SOUTH_CELL_GRID[sign] ?? { col: 0, row: 0 };
1951
+ return {
1952
+ x: MARGIN + g.col * SOUTH_CELL,
1953
+ y: MARGIN + g.row * SOUTH_CELL,
1954
+ w: SOUTH_CELL,
1955
+ h: SOUTH_CELL
1956
+ };
1957
+ }
1958
+ function renderSouthFrame(divisionLabel) {
1959
+ const a = MARGIN;
1960
+ const b = MARGIN + SOUTH_CELL;
1961
+ const c = MARGIN + 2 * SOUTH_CELL;
1962
+ const d = MARGIN + 3 * SOUTH_CELL;
1963
+ const e = VIEW_BOX - MARGIN;
1754
1964
  return svg2`
1755
- <g>
1756
- ${h.isLagna ? svg2`<rect
1757
- class="lagna-bg"
1758
- x=${center.x - 30} y=${center.y - 28}
1759
- width="60" height="56" rx="6"
1760
- />` : nothing7}
1761
- ${signAbbr ? svg2`<text class="sign-text" x=${signPos.x} y=${signPos.y} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing7}
1762
- ${h.isLagna ? svg2`<text class="lagna-marker" x=${center.x} y=${center.y - 18} text-anchor="middle" dominant-baseline="central">LAGNA</text>` : nothing7}
1763
- ${renderPlanetStack(h.planets, center.x, baseY, 13)}
1965
+ <rect class="line" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
1966
+ <line class="line" x1=${a} y1=${b} x2=${e} y2=${b} stroke-width="1" />
1967
+ <line class="line" x1=${a} y1=${d} x2=${e} y2=${d} stroke-width="1" />
1968
+ <line class="line" x1=${b} y1=${a} x2=${b} y2=${e} stroke-width="1" />
1969
+ <line class="line" x1=${d} y1=${a} x2=${d} y2=${e} stroke-width="1" />
1970
+ <line class="line" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width="1" />
1971
+ <line class="line" x1=${d} y1=${c} x2=${e} y2=${c} stroke-width="1" />
1972
+ <line class="line" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width="1" />
1973
+ <line class="line" x1=${c} y1=${d} x2=${c} y2=${e} stroke-width="1" />
1974
+ ${divisionLabel ? svg2`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing7}
1975
+ `;
1976
+ }
1977
+ function houseNumberInSign(sign, lagnaSign) {
1978
+ const lagnaIdx = SIGNS_ORDER.findIndex((s) => s === lagnaSign);
1979
+ const signIdx = SIGNS_ORDER.findIndex((s) => s === sign);
1980
+ if (lagnaIdx === -1 || signIdx === -1) return 0;
1981
+ return (signIdx - lagnaIdx + 12) % 12 + 1;
1982
+ }
1983
+ function renderSouthCell(sign, planets, isLagna, houseNum) {
1984
+ const r = southCellRect(sign);
1985
+ const cx = r.x + r.w / 2;
1986
+ const cy = r.y + r.h / 2;
1987
+ const signAbbr = SIGN_ABBR[sign] ?? sign.slice(0, 2);
1988
+ const slashInset = 14;
1989
+ return svg2`
1990
+ <g class=${isLagna ? "cell lagna" : "cell"}>
1991
+ ${isLagna ? svg2`
1992
+ <rect class="lagna-bg" x=${r.x} y=${r.y} width=${r.w} height=${r.h} />
1993
+ <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" />
1994
+ ` : nothing7}
1995
+ <text class="sign-text" x=${r.x + 6} y=${r.y + 12} text-anchor="start" dominant-baseline="central">${signAbbr}</text>
1996
+ ${houseNum > 0 ? svg2`<text class="house-num" x=${r.x + r.w - 6} y=${r.y + 12} text-anchor="end" dominant-baseline="central">${houseNum}</text>` : nothing7}
1997
+ ${planets.length ? renderPlanetStack(planets, sign, cx, cy + 4, 14) : nothing7}
1764
1998
  </g>
1765
1999
  `;
1766
2000
  }
1767
- function renderNorthFrame() {
2001
+ function renderSouthSvg(vm) {
2002
+ const lagnaKey = vm.lagnaSign.toLowerCase();
1768
2003
  return svg2`
1769
- <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1.5" />
1770
- <line class="line" x1="150" y1="10" x2="150" y2="290" stroke-width="1" />
1771
- <line class="line" x1="10" y1="150" x2="290" y2="150" stroke-width="1" />
1772
- <line class="line" x1="150" y1="10" x2="10" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
1773
- <line class="line" x1="150" y1="10" x2="290" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
1774
- <line class="line" x1="150" y1="290" x2="10" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
1775
- <line class="line" x1="150" y1="290" x2="290" y2="150" stroke-width="0.6" stroke-dasharray="3,3" />
2004
+ ${renderSouthFrame(vm.divisionLabel)}
2005
+ ${SIGNS_ORDER.map(
2006
+ (sign) => renderSouthCell(
2007
+ sign,
2008
+ vm.placements[sign.toLowerCase()] ?? [],
2009
+ sign.toLowerCase() === lagnaKey,
2010
+ houseNumberInSign(sign, vm.lagnaSign)
2011
+ )
2012
+ )}
1776
2013
  `;
1777
2014
  }
1778
- function renderNorthHouseGroup(h) {
1779
- const center = NORTH_HOUSE_CENTERS[h.number];
1780
- if (!center) return nothing7;
1781
- const signAbbr = SIGN_ABBR[h.sign] ?? "";
2015
+ var NORTH_VERTICES = {
2016
+ tl: { x: MARGIN, y: MARGIN },
2017
+ tr: { x: VIEW_BOX - MARGIN, y: MARGIN },
2018
+ br: { x: VIEW_BOX - MARGIN, y: VIEW_BOX - MARGIN },
2019
+ bl: { x: MARGIN, y: VIEW_BOX - MARGIN },
2020
+ top: { x: CENTRE, y: MARGIN },
2021
+ right: { x: VIEW_BOX - MARGIN, y: CENTRE },
2022
+ bottom: { x: CENTRE, y: VIEW_BOX - MARGIN },
2023
+ left: { x: MARGIN, y: CENTRE },
2024
+ tlMid: { x: CENTRE - INNER / 4, y: CENTRE - INNER / 4 },
2025
+ trMid: { x: CENTRE + INNER / 4, y: CENTRE - INNER / 4 },
2026
+ brMid: { x: CENTRE + INNER / 4, y: CENTRE + INNER / 4 },
2027
+ blMid: { x: CENTRE - INNER / 4, y: CENTRE + INNER / 4 }
2028
+ };
2029
+ function centroidOf(pts) {
2030
+ const x = pts.reduce((s, p) => s + p.x, 0) / pts.length;
2031
+ const y = pts.reduce((s, p) => s + p.y, 0) / pts.length;
2032
+ return { x, y };
2033
+ }
2034
+ var NORTH_HOUSE_CENTERS = {
2035
+ 1: { x: CENTRE, y: NORTH_VERTICES.tlMid.y },
2036
+ 2: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.top, NORTH_VERTICES.tlMid]),
2037
+ 3: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.left, NORTH_VERTICES.tlMid]),
2038
+ 4: { x: NORTH_VERTICES.tlMid.x, y: CENTRE },
2039
+ 5: centroidOf([NORTH_VERTICES.bl, NORTH_VERTICES.left, NORTH_VERTICES.blMid]),
2040
+ 6: centroidOf([
2041
+ NORTH_VERTICES.bl,
2042
+ NORTH_VERTICES.bottom,
2043
+ NORTH_VERTICES.blMid
2044
+ ]),
2045
+ 7: { x: CENTRE, y: NORTH_VERTICES.blMid.y },
2046
+ 8: centroidOf([
2047
+ NORTH_VERTICES.br,
2048
+ NORTH_VERTICES.bottom,
2049
+ NORTH_VERTICES.brMid
2050
+ ]),
2051
+ 9: centroidOf([
2052
+ NORTH_VERTICES.br,
2053
+ NORTH_VERTICES.right,
2054
+ NORTH_VERTICES.brMid
2055
+ ]),
2056
+ 10: { x: NORTH_VERTICES.brMid.x, y: CENTRE },
2057
+ 11: centroidOf([
2058
+ NORTH_VERTICES.tr,
2059
+ NORTH_VERTICES.right,
2060
+ NORTH_VERTICES.trMid
2061
+ ]),
2062
+ 12: centroidOf([NORTH_VERTICES.tr, NORTH_VERTICES.top, NORTH_VERTICES.trMid])
2063
+ };
2064
+ function rashiInHouse(houseNum, lagnaSign) {
2065
+ const lagnaIdx = SIGNS_ORDER.findIndex((s) => s === lagnaSign);
2066
+ if (lagnaIdx === -1) return houseNum;
2067
+ return (lagnaIdx + houseNum - 1) % 12 + 1;
2068
+ }
2069
+ function renderNorthFrame(divisionLabel) {
2070
+ const { tl, tr, br, bl, top, right, bottom, left } = NORTH_VERTICES;
1782
2071
  return svg2`
1783
- <g>
1784
- ${h.isLagna ? svg2`<circle class="lagna-bg" cx=${center.x} cy=${center.y} r="22" />` : nothing7}
1785
- ${signAbbr ? svg2`<text class="sign-text" x=${center.x} y=${center.y - 10} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>` : nothing7}
1786
- <text class="house-num" x=${center.x} y=${center.y + 2} text-anchor="middle" dominant-baseline="central">${h.number}</text>
1787
- ${renderPlanetStack(h.planets, center.x, center.y + 14, 11)}
2072
+ <rect class="line" x=${tl.x} y=${tl.y} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
2073
+ <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" />
2074
+ <line class="line" x1=${tl.x} y1=${tl.y} x2=${br.x} y2=${br.y} stroke-width="1" />
2075
+ <line class="line" x1=${tr.x} y1=${tr.y} x2=${bl.x} y2=${bl.y} stroke-width="1" />
2076
+ ${divisionLabel ? svg2`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing7}
2077
+ `;
2078
+ }
2079
+ function renderNorthCell(houseNum, rashiNum, sign, planets, isLagna) {
2080
+ const c = NORTH_HOUSE_CENTERS[houseNum];
2081
+ if (!c) return svg2``;
2082
+ const rashiOffsetY = Math.min(14, Math.abs(c.y - CENTRE) * 0.45 + 6);
2083
+ const ascOffsetY = rashiOffsetY + 12;
2084
+ return svg2`
2085
+ <g class=${isLagna ? "cell lagna" : "cell"}>
2086
+ <text class="rashi-num" x=${c.x} y=${c.y - rashiOffsetY} text-anchor="middle" dominant-baseline="central">${rashiNum}</text>
2087
+ ${isLagna ? svg2`<text class="lagna-marker" x=${c.x} y=${c.y - ascOffsetY} text-anchor="middle" dominant-baseline="central">Asc</text>` : nothing7}
2088
+ ${planets.length ? renderPlanetStack(planets, sign, c.x, c.y + 8, 12) : nothing7}
1788
2089
  </g>
1789
2090
  `;
1790
2091
  }
1791
- function renderSouthFrame() {
2092
+ function renderNorthSvg(vm) {
2093
+ const lagnaSign = vm.lagnaSign || "Aries";
1792
2094
  return svg2`
1793
- <polygon class="line" points="150,10 290,150 150,290 10,150" stroke-width="1.5" />
1794
- <polygon class="line" points="220,80 220,220 80,220 80,80" stroke-width="1" fill="none" />
1795
- <line class="line" x1="150" y1="10" x2="80" y2="80" stroke-width="1" />
1796
- <line class="line" x1="150" y1="10" x2="220" y2="80" stroke-width="1" />
1797
- <line class="line" x1="290" y1="150" x2="220" y2="80" stroke-width="1" />
1798
- <line class="line" x1="290" y1="150" x2="220" y2="220" stroke-width="1" />
1799
- <line class="line" x1="150" y1="290" x2="220" y2="220" stroke-width="1" />
1800
- <line class="line" x1="150" y1="290" x2="80" y2="220" stroke-width="1" />
1801
- <line class="line" x1="10" y1="150" x2="80" y2="220" stroke-width="1" />
1802
- <line class="line" x1="10" y1="150" x2="80" y2="80" stroke-width="1" />
2095
+ ${renderNorthFrame(vm.divisionLabel)}
2096
+ ${Array.from({ length: 12 }, (_, i) => {
2097
+ const houseNum = i + 1;
2098
+ const rashiNum = rashiInHouse(houseNum, lagnaSign);
2099
+ const sign = SIGNS_ORDER[rashiNum - 1] ?? "Aries";
2100
+ return renderNorthCell(
2101
+ houseNum,
2102
+ rashiNum,
2103
+ sign,
2104
+ vm.placements[sign.toLowerCase()] ?? [],
2105
+ houseNum === 1
2106
+ );
2107
+ })}
1803
2108
  `;
1804
2109
  }
1805
- function renderEastFrame() {
2110
+ var EAST_CELL = INNER / 3;
2111
+ function eastCells() {
2112
+ const a = MARGIN;
2113
+ const b = MARGIN + EAST_CELL;
2114
+ const c = MARGIN + 2 * EAST_CELL;
2115
+ const d = VIEW_BOX - MARGIN;
2116
+ const aries = [
2117
+ { x: b, y: a },
2118
+ { x: c, y: a },
2119
+ { x: c, y: b },
2120
+ { x: b, y: b }
2121
+ ];
2122
+ const cancer = [
2123
+ { x: a, y: b },
2124
+ { x: b, y: b },
2125
+ { x: b, y: c },
2126
+ { x: a, y: c }
2127
+ ];
2128
+ const libra = [
2129
+ { x: b, y: c },
2130
+ { x: c, y: c },
2131
+ { x: c, y: d },
2132
+ { x: b, y: d }
2133
+ ];
2134
+ const capricorn = [
2135
+ { x: c, y: b },
2136
+ { x: d, y: b },
2137
+ { x: d, y: c },
2138
+ { x: c, y: c }
2139
+ ];
2140
+ const taurus = [
2141
+ { x: a, y: a },
2142
+ { x: b, y: a },
2143
+ { x: b, y: b }
2144
+ ];
2145
+ const gemini = [
2146
+ { x: a, y: a },
2147
+ { x: b, y: b },
2148
+ { x: a, y: b }
2149
+ ];
2150
+ const leo = [
2151
+ { x: a, y: c },
2152
+ { x: b, y: c },
2153
+ { x: a, y: d }
2154
+ ];
2155
+ const virgo = [
2156
+ { x: b, y: c },
2157
+ { x: b, y: d },
2158
+ { x: a, y: d }
2159
+ ];
2160
+ const scorpio = [
2161
+ { x: c, y: c },
2162
+ { x: c, y: d },
2163
+ { x: d, y: d }
2164
+ ];
2165
+ const sagittarius = [
2166
+ { x: c, y: c },
2167
+ { x: d, y: d },
2168
+ { x: d, y: c }
2169
+ ];
2170
+ const aquarius = [
2171
+ { x: d, y: a },
2172
+ { x: d, y: b },
2173
+ { x: c, y: b }
2174
+ ];
2175
+ const pisces = [
2176
+ { x: c, y: a },
2177
+ { x: d, y: a },
2178
+ { x: c, y: b }
2179
+ ];
2180
+ const polys = {
2181
+ Aries: aries,
2182
+ Taurus: taurus,
2183
+ Gemini: gemini,
2184
+ Cancer: cancer,
2185
+ Leo: leo,
2186
+ Virgo: virgo,
2187
+ Libra: libra,
2188
+ Scorpio: scorpio,
2189
+ Sagittarius: sagittarius,
2190
+ Capricorn: capricorn,
2191
+ Aquarius: aquarius,
2192
+ Pisces: pisces
2193
+ };
2194
+ const out = {};
2195
+ for (const [sign, points] of Object.entries(polys)) {
2196
+ out[sign] = { points: [...points], centroid: centroidOf(points) };
2197
+ }
2198
+ return out;
2199
+ }
2200
+ var EAST_CELLS = eastCells();
2201
+ function renderEastFrame(divisionLabel) {
2202
+ const a = MARGIN;
2203
+ const b = MARGIN + EAST_CELL;
2204
+ const c = MARGIN + 2 * EAST_CELL;
2205
+ const d = VIEW_BOX - MARGIN;
1806
2206
  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" />
2207
+ <rect class="line" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width="1.5" fill="none" />
2208
+ <line class="line" x1=${a} y1=${b} x2=${b} y2=${b} stroke-width="1" />
2209
+ <line class="line" x1=${c} y1=${b} x2=${d} y2=${b} stroke-width="1" />
2210
+ <line class="line" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width="1" />
2211
+ <line class="line" x1=${c} y1=${c} x2=${d} y2=${c} stroke-width="1" />
2212
+ <line class="line" x1=${b} y1=${a} x2=${b} y2=${b} stroke-width="1" />
2213
+ <line class="line" x1=${b} y1=${c} x2=${b} y2=${d} stroke-width="1" />
2214
+ <line class="line" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width="1" />
2215
+ <line class="line" x1=${c} y1=${c} x2=${c} y2=${d} stroke-width="1" />
2216
+ <line class="line" x1=${a} y1=${a} x2=${b} y2=${b} stroke-width="1" />
2217
+ <line class="line" x1=${d} y1=${a} x2=${c} y2=${b} stroke-width="1" />
2218
+ <line class="line" x1=${d} y1=${d} x2=${c} y2=${c} stroke-width="1" />
2219
+ <line class="line" x1=${a} y1=${d} x2=${b} y2=${c} stroke-width="1" />
2220
+ ${divisionLabel ? svg2`<text class="centre-label" x=${CENTRE} y=${CENTRE} text-anchor="middle" dominant-baseline="central">${divisionLabel}</text>` : nothing7}
1811
2221
  `;
1812
2222
  }
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] ?? "";
2223
+ function renderEastCell(sign, planets, isLagna, houseNum) {
2224
+ const cell = EAST_CELLS[sign];
2225
+ if (!cell) return svg2``;
2226
+ const { centroid: cen, points } = cell;
2227
+ const signAbbr = SIGN_ABBR[sign] ?? sign.slice(0, 2);
2228
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
1818
2229
  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)}
2230
+ <g class=${isLagna ? "cell lagna" : "cell"}>
2231
+ ${isLagna ? svg2`<polygon class="lagna-bg" points=${polyPoints} />` : nothing7}
2232
+ <text class="sign-text" x=${cen.x} y=${cen.y - 16} text-anchor="middle" dominant-baseline="central">${signAbbr}</text>
2233
+ ${houseNum > 0 ? svg2`<text class="house-num" x=${cen.x + 18} y=${cen.y - 16} text-anchor="start" dominant-baseline="central">${houseNum}</text>` : nothing7}
2234
+ ${isLagna ? svg2`<text class="lagna-marker" x=${cen.x} y=${cen.y - 30} text-anchor="middle" dominant-baseline="central">Asc</text>` : nothing7}
2235
+ ${planets.length ? renderPlanetStack(planets, sign, cen.x, cen.y + 4, 12) : nothing7}
1824
2236
  </g>
1825
2237
  `;
1826
2238
  }
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,
2239
+ function renderEastSvg(vm) {
2240
+ const lagnaKey = vm.lagnaSign.toLowerCase();
2241
+ return svg2`
2242
+ ${renderEastFrame(vm.divisionLabel)}
2243
+ ${SIGNS_ORDER.map(
2244
+ (sign) => renderEastCell(
1851
2245
  sign,
1852
- planets: byRashi.get(key) ?? [],
1853
- isLagna: lagnaKey === key
1854
- };
1855
- });
2246
+ vm.placements[sign.toLowerCase()] ?? [],
2247
+ sign.toLowerCase() === lagnaKey,
2248
+ houseNumberInSign(sign, vm.lagnaSign)
2249
+ )
2250
+ )}
2251
+ `;
2252
+ }
2253
+ function renderKundliSvg(vm, style) {
2254
+ switch (style) {
2255
+ case "north":
2256
+ return renderNorthSvg(vm);
2257
+ case "east":
2258
+ return renderEastSvg(vm);
2259
+ default:
2260
+ return renderSouthSvg(vm);
2261
+ }
2262
+ }
2263
+ function renderKundliStyleTablist(active, setStyle) {
2264
+ const onKeyDown = (e) => {
2265
+ const idx = CHART_STYLES.findIndex((s) => s.id === active);
2266
+ if (e.key === "ArrowRight") {
2267
+ e.preventDefault();
2268
+ const next = CHART_STYLES[(idx + 1) % CHART_STYLES.length];
2269
+ if (next) setStyle(next.id);
2270
+ } else if (e.key === "ArrowLeft") {
2271
+ e.preventDefault();
2272
+ const next = CHART_STYLES[(idx - 1 + CHART_STYLES.length) % CHART_STYLES.length];
2273
+ if (next) setStyle(next.id);
2274
+ }
2275
+ };
2276
+ return html7`<div
2277
+ class="kundli-tablist"
2278
+ role="tablist"
2279
+ aria-label="Kundli style"
2280
+ @keydown=${onKeyDown}
2281
+ >
2282
+ ${CHART_STYLES.map(
2283
+ (s) => html7`<button
2284
+ type="button"
2285
+ class="kundli-tab"
2286
+ role="tab"
2287
+ id="kundli-tab-${s.id}"
2288
+ aria-selected=${active === s.id ? "true" : "false"}
2289
+ tabindex=${active === s.id ? "0" : "-1"}
2290
+ @click=${() => setStyle(s.id)}
2291
+ >
2292
+ ${s.label}
2293
+ </button>`
2294
+ )}
2295
+ </div>`;
1856
2296
  }
1857
2297
 
2298
+ // packages/ui/src/utils/kundli-styles.ts
2299
+ import { css as css8 } from "lit";
2300
+ var kundliStyles = css8`
2301
+ .wrap {
2302
+ display: grid;
2303
+ gap: var(--roxy-space-md, 1rem);
2304
+ }
2305
+ .header {
2306
+ display: flex;
2307
+ flex-wrap: wrap;
2308
+ align-items: center;
2309
+ justify-content: space-between;
2310
+ gap: var(--roxy-space-sm, 0.5rem);
2311
+ }
2312
+ .title {
2313
+ font-size: var(--roxy-text-lg, 1.125rem);
2314
+ font-weight: var(--roxy-weight-bold, 600);
2315
+ margin: 0;
2316
+ }
2317
+ .kundli-tablist {
2318
+ display: inline-flex;
2319
+ gap: 2px;
2320
+ border-bottom: 2px solid var(--roxy-border, #e4e4e7);
2321
+ }
2322
+ .kundli-tab {
2323
+ padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
2324
+ font-size: var(--roxy-text-sm, 0.875rem);
2325
+ background: none;
2326
+ border: none;
2327
+ border-bottom: 2px solid transparent;
2328
+ margin-bottom: -2px;
2329
+ cursor: pointer;
2330
+ color: var(--roxy-muted, #71717a);
2331
+ font-family: inherit;
2332
+ transition: color var(--roxy-motion-duration, 200ms)
2333
+ var(--roxy-motion-easing, ease);
2334
+ }
2335
+ .kundli-tab[aria-selected='true'] {
2336
+ color: var(--roxy-accent-fg, #b45309);
2337
+ border-bottom-color: var(--roxy-accent, #f59e0b);
2338
+ font-weight: var(--roxy-weight-bold, 600);
2339
+ }
2340
+ .kundli-tab:hover:not([aria-selected='true']) {
2341
+ color: var(--roxy-fg, #0a0a0a);
2342
+ }
2343
+ .kundli-tab:focus-visible {
2344
+ outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
2345
+ outline-offset: 2px;
2346
+ border-radius: 4px;
2347
+ }
2348
+ svg {
2349
+ display: block;
2350
+ width: 100%;
2351
+ max-width: 560px;
2352
+ aspect-ratio: 1 / 1;
2353
+ height: auto;
2354
+ margin: 0 auto;
2355
+ }
2356
+ .line {
2357
+ fill: transparent;
2358
+ stroke: var(--roxy-border, #d4d4d8);
2359
+ }
2360
+ .sign-text {
2361
+ fill: var(--roxy-muted, #71717a);
2362
+ font-size: 11px;
2363
+ font-weight: 500;
2364
+ font-family: var(--roxy-font-sans);
2365
+ text-transform: uppercase;
2366
+ letter-spacing: 0.04em;
2367
+ }
2368
+ .rashi-num {
2369
+ fill: var(--roxy-muted, #71717a);
2370
+ font-size: 12px;
2371
+ font-weight: 500;
2372
+ font-family: var(--roxy-font-sans);
2373
+ }
2374
+ .house-num {
2375
+ fill: var(--roxy-accent-fg, #b45309);
2376
+ font-size: 11px;
2377
+ font-weight: 600;
2378
+ font-family: var(--roxy-font-sans);
2379
+ opacity: 0.85;
2380
+ }
2381
+ .planet-text {
2382
+ fill: var(--roxy-fg, #0a0a0a);
2383
+ font-size: 13px;
2384
+ font-weight: 600;
2385
+ font-family: var(--roxy-font-sans);
2386
+ }
2387
+ .centre-label {
2388
+ fill: var(--roxy-muted, #71717a);
2389
+ font-size: 14px;
2390
+ font-weight: 600;
2391
+ font-family: var(--roxy-font-sans);
2392
+ letter-spacing: 0.02em;
2393
+ }
2394
+ .lagna-marker {
2395
+ fill: var(--roxy-accent-fg, #b45309);
2396
+ font-size: 10px;
2397
+ font-weight: 700;
2398
+ font-family: var(--roxy-font-sans);
2399
+ letter-spacing: 0.08em;
2400
+ text-transform: uppercase;
2401
+ }
2402
+ .lagna-bg {
2403
+ fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 14%, transparent);
2404
+ }
2405
+ .lagna-slash {
2406
+ stroke: var(--roxy-accent, #f59e0b);
2407
+ stroke-linecap: round;
2408
+ opacity: 0.7;
2409
+ }
2410
+ `;
2411
+
1858
2412
  // packages/ui/src/components/divisional-chart.ts
1859
2413
  var RoxyDivisionalChart = class extends LitElement7 {
1860
2414
  constructor() {
1861
2415
  super(...arguments);
1862
2416
  this.data = null;
1863
- this.chartStyle = "south";
2417
+ this.chartStyle = "north";
2418
+ this.setStyle = (next) => {
2419
+ this.chartStyle = next;
2420
+ };
1864
2421
  }
1865
- buildHouses() {
1866
- if (!this.data?.chart?.meta) return [];
1867
- return buildHousesFromMeta(this.data.chart.meta);
2422
+ viewModel() {
2423
+ if (!this.data?.chart?.meta) return null;
2424
+ const { division } = this.data;
2425
+ const label = `D${division.number} ${division.name}`;
2426
+ return toKundliViewModel(this.data.chart.meta, label);
1868
2427
  }
1869
2428
  render() {
1870
- if (!this.data)
1871
- return html7`<div class="roxy-empty" role="status">No divisional chart data</div>`;
2429
+ const vm = this.viewModel();
2430
+ if (!this.data || !vm)
2431
+ return html8`<div class="roxy-empty" role="status">No divisional chart data</div>`;
1872
2432
  const { division, vargottama } = this.data;
1873
- const houses = this.buildHouses();
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;
1877
- return html7`<div class="wrap">
2433
+ return html8`<div class="wrap">
1878
2434
  <div class="header">
1879
- <h2 class="title">
1880
- D${division.number} ${division.name}
1881
- ${division.sanskritName && division.sanskritName !== division.name ? html7`<span class="division-meta"> · ${division.sanskritName}</span>` : nothing8}
1882
- </h2>
1883
- ${division.significance ? html7`<p class="significance">${division.significance}</p>` : nothing8}
2435
+ <div>
2436
+ <h2 class="title">
2437
+ D${division.number} ${division.name}
2438
+ ${division.sanskritName && division.sanskritName !== division.name ? html8`<span class="division-meta"> · ${division.sanskritName}</span>` : nothing8}
2439
+ </h2>
2440
+ ${division.significance ? html8`<p class="significance">${division.significance}</p>` : nothing8}
2441
+ </div>
2442
+ ${renderKundliStyleTablist(this.chartStyle, this.setStyle)}
1884
2443
  </div>
1885
2444
 
1886
2445
  <svg
1887
- viewBox="0 0 300 300"
2446
+ viewBox="0 0 400 400"
2447
+ preserveAspectRatio="xMidYMid meet"
1888
2448
  role="img"
1889
2449
  aria-label="D${division.number} ${division.name} divisional chart with twelve sign houses"
1890
2450
  >
1891
2451
  <title>D${division.number} ${division.name}</title>
1892
- ${frame}
1893
- ${houses.map((h) => houseGroup(h))}
2452
+ ${renderKundliSvg(vm, this.chartStyle)}
1894
2453
  </svg>
1895
2454
 
1896
- ${vargottama && vargottama.length > 0 ? html7`<div class="vargottama-row" role="list" aria-label="Vargottama planets">
2455
+ ${vargottama && vargottama.length > 0 ? html8`<div class="vargottama-row" role="list" aria-label="Vargottama planets">
1897
2456
  <span class="vargottama-label">Vargottama:</span>
1898
2457
  ${vargottama.map(
1899
- (planet) => html7`<span class="vargottama-pill" role="listitem">
2458
+ (planet) => html8`<span class="vargottama-pill" role="listitem">
1900
2459
  ${PLANET_GLYPH[planet] ?? ""} ${planet}
1901
2460
  </span>`
1902
2461
  )}
@@ -1906,20 +2465,8 @@ var RoxyDivisionalChart = class extends LitElement7 {
1906
2465
  };
1907
2466
  RoxyDivisionalChart.styles = [
1908
2467
  baseStyles,
1909
- css8`
1910
- .wrap {
1911
- display: grid;
1912
- gap: var(--roxy-space-md, 1rem);
1913
- }
1914
- .header {
1915
- display: grid;
1916
- gap: var(--roxy-space-xs, 0.25rem);
1917
- }
1918
- .title {
1919
- font-size: var(--roxy-text-lg, 1.125rem);
1920
- font-weight: var(--roxy-weight-bold, 600);
1921
- margin: 0;
1922
- }
2468
+ kundliStyles,
2469
+ css9`
1923
2470
  .division-meta {
1924
2471
  font-size: var(--roxy-text-sm, 0.875rem);
1925
2472
  color: var(--roxy-muted, #71717a);
@@ -1932,46 +2479,6 @@ RoxyDivisionalChart.styles = [
1932
2479
  padding-left: var(--roxy-space-sm, 0.5rem);
1933
2480
  margin: 0;
1934
2481
  }
1935
- svg {
1936
- display: block;
1937
- width: 100%;
1938
- max-width: 360px;
1939
- margin: 0 auto;
1940
- }
1941
- .line {
1942
- fill: transparent;
1943
- stroke: var(--roxy-border, #e4e4e7);
1944
- }
1945
- .sign-text {
1946
- fill: var(--roxy-muted, #71717a);
1947
- font-size: 9px;
1948
- font-weight: 500;
1949
- font-family: var(--roxy-font-sans);
1950
- }
1951
- .planet-text {
1952
- fill: var(--roxy-fg, #0a0a0a);
1953
- font-size: 11px;
1954
- font-weight: 600;
1955
- font-family: var(--roxy-font-sans);
1956
- }
1957
- .house-num {
1958
- fill: var(--roxy-muted, #71717a);
1959
- font-size: 9px;
1960
- font-weight: 400;
1961
- font-family: var(--roxy-font-sans);
1962
- }
1963
- .lagna-marker {
1964
- fill: var(--roxy-accent-fg, #b45309);
1965
- font-size: 8px;
1966
- font-weight: 700;
1967
- font-family: var(--roxy-font-sans);
1968
- letter-spacing: 0.05em;
1969
- }
1970
- .lagna-bg {
1971
- fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
1972
- stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
1973
- stroke-width: 0.8;
1974
- }
1975
2482
  .vargottama-row {
1976
2483
  display: flex;
1977
2484
  flex-wrap: wrap;
@@ -2009,7 +2516,7 @@ RoxyDivisionalChart = __decorateClass([
2009
2516
  ], RoxyDivisionalChart);
2010
2517
 
2011
2518
  // packages/ui/src/components/dosha-card.ts
2012
- import { css as css9, html as html8, LitElement as LitElement8, nothing as nothing9 } from "lit";
2519
+ import { css as css10, html as html9, LitElement as LitElement8, nothing as nothing9 } from "lit";
2013
2520
  import { customElement as customElement8, property as property8 } from "lit/decorators.js";
2014
2521
  var DOSHA_LABELS = {
2015
2522
  manglik: "Mangal Dosha",
@@ -2025,14 +2532,14 @@ var RoxyDoshaCard = class extends LitElement8 {
2025
2532
  render() {
2026
2533
  const d = this.data;
2027
2534
  if (!d)
2028
- return html8`<div class="roxy-empty" role="status">No dosha data</div>`;
2535
+ return html9`<div class="roxy-empty" role="status">No dosha data</div>`;
2029
2536
  const present = !!d.present;
2030
2537
  const label = DOSHA_LABELS[this.type] ?? this.type;
2031
2538
  const sevLower = (d.severity ?? "").toLowerCase();
2032
2539
  const tier = sevLower === "severe" ? 3 : sevLower === "moderate" ? 2 : sevLower === "mild" ? 1 : 0;
2033
2540
  const pct = tier * 33;
2034
2541
  const barColor = tier === 3 ? "var(--roxy-danger)" : tier === 2 ? "var(--roxy-warning)" : tier === 1 ? "var(--roxy-success)" : "transparent";
2035
- return html8`<article
2542
+ return html9`<article
2036
2543
  class="card"
2037
2544
  aria-label=${label}
2038
2545
  >
@@ -2042,7 +2549,7 @@ var RoxyDoshaCard = class extends LitElement8 {
2042
2549
  ${present ? "Present" : "Absent"}
2043
2550
  </span>
2044
2551
  </header>
2045
- ${d.severity ? html8`<div
2552
+ ${d.severity ? html9`<div
2046
2553
  class="severity-bar"
2047
2554
  role="meter"
2048
2555
  aria-valuemin="0"
@@ -2052,18 +2559,18 @@ var RoxyDoshaCard = class extends LitElement8 {
2052
2559
  >
2053
2560
  <span class="severity-fill" style="width: ${pct}%; background: ${barColor};"></span>
2054
2561
  </div>` : nothing9}
2055
- ${d.description ? html8`<p class="description">${d.description}</p>` : nothing9}
2562
+ ${d.description ? html9`<p class="description">${d.description}</p>` : nothing9}
2056
2563
  ${this.renderEffects(d)}
2057
- ${d.remedies && d.remedies.length > 0 ? html8`<div>
2564
+ ${d.remedies && d.remedies.length > 0 ? html9`<div>
2058
2565
  <h3>Remedies</h3>
2059
2566
  <ul>
2060
- ${d.remedies.map((r) => html8`<li>${r}</li>`)}
2567
+ ${d.remedies.map((r) => html9`<li>${r}</li>`)}
2061
2568
  </ul>
2062
2569
  </div>` : nothing9}
2063
- ${"exceptions" in d && d.exceptions && d.exceptions.length > 0 ? html8`<div>
2570
+ ${"exceptions" in d && d.exceptions && d.exceptions.length > 0 ? html9`<div>
2064
2571
  <h3>Exceptions</h3>
2065
2572
  <ul>
2066
- ${d.exceptions.map((r) => html8`<li>${r}</li>`)}
2573
+ ${d.exceptions.map((r) => html9`<li>${r}</li>`)}
2067
2574
  </ul>
2068
2575
  </div>` : nothing9}
2069
2576
  </article>`;
@@ -2074,9 +2581,9 @@ var RoxyDoshaCard = class extends LitElement8 {
2074
2581
  ([, v]) => typeof v === "string" && v.length > 0
2075
2582
  );
2076
2583
  if (entries.length === 0) return nothing9;
2077
- return html8`<div class="effects">
2584
+ return html9`<div class="effects">
2078
2585
  ${entries.map(
2079
- ([k, v]) => html8`<div>
2586
+ ([k, v]) => html9`<div>
2080
2587
  <h3>${k}</h3>
2081
2588
  <p>${v}</p>
2082
2589
  </div>`
@@ -2086,7 +2593,7 @@ var RoxyDoshaCard = class extends LitElement8 {
2086
2593
  };
2087
2594
  RoxyDoshaCard.styles = [
2088
2595
  baseStyles,
2089
- css9`
2596
+ css10`
2090
2597
  .card {
2091
2598
  background: var(--roxy-bg, #fff);
2092
2599
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -2187,7 +2694,7 @@ RoxyDoshaCard = __decorateClass([
2187
2694
  ], RoxyDoshaCard);
2188
2695
 
2189
2696
  // packages/ui/src/components/endpoint-form.ts
2190
- import { css as css10, html as html9, LitElement as LitElement9, nothing as nothing10 } from "lit";
2697
+ import { css as css11, html as html10, LitElement as LitElement9, nothing as nothing10 } from "lit";
2191
2698
  import { customElement as customElement9, property as property9, state as state2 } from "lit/decorators.js";
2192
2699
  var specCache = /* @__PURE__ */ new Map();
2193
2700
  async function loadSpec(url) {
@@ -2349,10 +2856,10 @@ var RoxyEndpointForm = class extends LitElement9 {
2349
2856
  }
2350
2857
  render() {
2351
2858
  if (!this.loaded) {
2352
- return html9`<form><div class="roxy-skeleton" style="height: 8rem"></div></form>`;
2859
+ return html10`<form><div class="roxy-skeleton" style="height: 8rem"></div></form>`;
2353
2860
  }
2354
2861
  if (this.specError) {
2355
- return html9`<div class="spec-error" role="alert">
2862
+ return html10`<div class="spec-error" role="alert">
2356
2863
  Schema load failed: ${this.specError}
2357
2864
  <button type="button" class="submit" @click=${this.retryLoadSchema}>Retry</button>
2358
2865
  </div>`;
@@ -2362,22 +2869,22 @@ var RoxyEndpointForm = class extends LitElement9 {
2362
2869
  return nothing10;
2363
2870
  }
2364
2871
  const inputId = `roxy-form-${f.name}`;
2365
- return html9`<div class="field">
2872
+ return html10`<div class="field">
2366
2873
  <label for=${inputId}>
2367
- ${humanize(f.name)}${f.required ? html9`<span class="req" aria-hidden="true">*</span>` : nothing10}
2874
+ ${humanize(f.name)}${f.required ? html10`<span class="req" aria-hidden="true">*</span>` : nothing10}
2368
2875
  </label>
2369
- ${f.enum ? html9`<select
2876
+ ${f.enum ? html10`<select
2370
2877
  id=${inputId}
2371
2878
  ?required=${f.required}
2372
2879
  @change=${(e) => this.setValue(f.name, e.target.value)}
2373
2880
  >
2374
2881
  <option value="">Choose</option>
2375
2882
  ${f.enum.map(
2376
- (opt) => html9`<option value=${opt} ?selected=${this.values[f.name] === opt}>
2883
+ (opt) => html10`<option value=${opt} ?selected=${this.values[f.name] === opt}>
2377
2884
  ${opt}
2378
2885
  </option>`
2379
2886
  )}
2380
- </select>` : html9`<input
2887
+ </select>` : html10`<input
2381
2888
  id=${inputId}
2382
2889
  type=${this.htmlType(f.type)}
2383
2890
  ?required=${f.required}
@@ -2390,12 +2897,12 @@ var RoxyEndpointForm = class extends LitElement9 {
2390
2897
  this.coerce(f.type, e.target.value)
2391
2898
  )}
2392
2899
  />`}
2393
- ${f.description ? html9`<small class="help">${f.description}</small>` : nothing10}
2900
+ ${f.description ? html10`<small class="help">${f.description}</small>` : nothing10}
2394
2901
  </div>`;
2395
2902
  };
2396
- return html9`<form @submit=${this.onSubmit}>
2903
+ return html10`<form @submit=${this.onSubmit}>
2397
2904
  <h2 class="title">${humanize(this.endpoint.split("/").pop() ?? "")}</h2>
2398
- ${this.hasLocation ? html9`<div class="location-block">
2905
+ ${this.hasLocation ? html10`<div class="location-block">
2399
2906
  <label>Birth location</label>
2400
2907
  <roxy-location-search
2401
2908
  @roxy-location-select=${this.onLocation}
@@ -2436,7 +2943,7 @@ var RoxyEndpointForm = class extends LitElement9 {
2436
2943
  };
2437
2944
  RoxyEndpointForm.styles = [
2438
2945
  baseStyles,
2439
- css10`
2946
+ css11`
2440
2947
  form {
2441
2948
  display: grid;
2442
2949
  gap: var(--roxy-space-md, 1rem);
@@ -2572,7 +3079,7 @@ RoxyEndpointForm = __decorateClass([
2572
3079
  ], RoxyEndpointForm);
2573
3080
 
2574
3081
  // packages/ui/src/components/guna-milan.ts
2575
- import { css as css11, html as html10, LitElement as LitElement10, nothing as nothing11 } from "lit";
3082
+ import { css as css12, html as html11, LitElement as LitElement10, nothing as nothing11 } from "lit";
2576
3083
  import { customElement as customElement10, property as property10 } from "lit/decorators.js";
2577
3084
  var RoxyGunaMilan = class extends LitElement10 {
2578
3085
  constructor() {
@@ -2582,7 +3089,7 @@ var RoxyGunaMilan = class extends LitElement10 {
2582
3089
  render() {
2583
3090
  const d = this.data;
2584
3091
  if (!d)
2585
- return html10`<div class="roxy-empty" role="status">No Guna Milan data</div>`;
3092
+ return html11`<div class="roxy-empty" role="status">No Guna Milan data</div>`;
2586
3093
  const breakdown = (d.breakdown ?? []).filter(
2587
3094
  (b) => b?.category !== void 0
2588
3095
  );
@@ -2593,18 +3100,18 @@ var RoxyGunaMilan = class extends LitElement10 {
2593
3100
  const fillColor = pct >= 70 ? "var(--roxy-success)" : pct >= 50 ? "var(--roxy-warning)" : "var(--roxy-danger)";
2594
3101
  const dashFill = pct * 2.827;
2595
3102
  const dashGap = (100 - pct) * 2.827;
2596
- return html10`<article class="card" aria-label="Guna Milan score">
3103
+ return html11`<article class="card" aria-label="Guna Milan score">
2597
3104
  <div class="score-header">
2598
3105
  <div class="score-info">
2599
3106
  <div class="score-bar">
2600
3107
  <div>
2601
3108
  <span class="total">${formatNumber(d.total, 1)}</span>
2602
3109
  <span class="over"> / ${d.maxScore}</span>
2603
- ${typeof d.percentage === "number" ? html10`<small style="margin-left: 0.5rem; color: var(--roxy-muted)">
3110
+ ${typeof d.percentage === "number" ? html11`<small style="margin-left: 0.5rem; color: var(--roxy-muted)">
2604
3111
  ${formatPercent(d.percentage, 1)}
2605
3112
  </small>` : nothing11}
2606
3113
  </div>
2607
- ${d.recommendation ? html10`<span class="recommendation">${d.recommendation}</span>` : nothing11}
3114
+ ${d.recommendation ? html11`<span class="recommendation">${d.recommendation}</span>` : nothing11}
2608
3115
  </div>
2609
3116
  </div>
2610
3117
  <div class="score-ring" role="meter" aria-label="Guna milan score" aria-valuemin="0" aria-valuemax="36" aria-valuenow="${score}">
@@ -2619,7 +3126,7 @@ var RoxyGunaMilan = class extends LitElement10 {
2619
3126
  </div>
2620
3127
  </div>
2621
3128
 
2622
- ${breakdown.length > 0 ? html10`<table>
3129
+ ${breakdown.length > 0 ? html11`<table>
2623
3130
  <thead>
2624
3131
  <tr>
2625
3132
  <th>Category</th>
@@ -2632,7 +3139,7 @@ var RoxyGunaMilan = class extends LitElement10 {
2632
3139
  const score2 = b.score ?? 0;
2633
3140
  const maxScore = b.maxScore ?? defaultMax(b.category);
2634
3141
  const pct2 = maxScore ? score2 / maxScore * 100 : 0;
2635
- return html10`<tr>
3142
+ return html11`<tr>
2636
3143
  <td>${b.category}</td>
2637
3144
  <td class="bar-cell">
2638
3145
  <div class="mini-bar">
@@ -2644,10 +3151,10 @@ var RoxyGunaMilan = class extends LitElement10 {
2644
3151
  })}
2645
3152
  </tbody>
2646
3153
  </table>` : nothing11}
2647
- ${(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0 ? html10`<div class="tags">
2648
- ${d.doshas?.map((x) => html10`<span class="dosha">${x}</span>`)}
3154
+ ${(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0 ? html11`<div class="tags">
3155
+ ${d.doshas?.map((x) => html11`<span class="dosha">${x}</span>`)}
2649
3156
  ${d.doshaCancellations?.map(
2650
- (x) => html10`<span class="cancel" title=${x.reason}>${x.dosha} cancelled</span>`
3157
+ (x) => html11`<span class="cancel" title=${x.reason}>${x.dosha} cancelled</span>`
2651
3158
  )}
2652
3159
  </div>` : nothing11}
2653
3160
  </article>`;
@@ -2655,7 +3162,7 @@ var RoxyGunaMilan = class extends LitElement10 {
2655
3162
  };
2656
3163
  RoxyGunaMilan.styles = [
2657
3164
  baseStyles,
2658
- css11`
3165
+ css12`
2659
3166
  .card {
2660
3167
  background: var(--roxy-bg, #fff);
2661
3168
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -2809,7 +3316,7 @@ function defaultMax(name) {
2809
3316
  }
2810
3317
 
2811
3318
  // packages/ui/src/components/hexagram.ts
2812
- import { css as css12, html as html11, LitElement as LitElement11, nothing as nothing12, svg as svg3 } from "lit";
3319
+ import { css as css13, html as html12, LitElement as LitElement11, nothing as nothing12, svg as svg3 } from "lit";
2813
3320
  import { customElement as customElement11, property as property11 } from "lit/decorators.js";
2814
3321
  var RoxyHexagram = class extends LitElement11 {
2815
3322
  constructor() {
@@ -2841,7 +3348,7 @@ var RoxyHexagram = class extends LitElement11 {
2841
3348
  render() {
2842
3349
  const resolved = this.resolveHexagram();
2843
3350
  if (!resolved)
2844
- return html11`<div class="roxy-empty" role="status">No hexagram data</div>`;
3351
+ return html12`<div class="roxy-empty" role="status">No hexagram data</div>`;
2845
3352
  const {
2846
3353
  hex: h,
2847
3354
  lines: castLines,
@@ -2851,16 +3358,16 @@ var RoxyHexagram = class extends LitElement11 {
2851
3358
  } = resolved;
2852
3359
  const lines = castLines ?? this.derivedLines(h);
2853
3360
  const changing = new Set(changingLinePositions ?? []);
2854
- return html11`<article class="card" aria-label="I Ching hexagram">
3361
+ return html12`<article class="card" aria-label="I Ching hexagram">
2855
3362
  <div class="glyphs">
2856
- ${h.symbol ? html11`<div class="symbol">${h.symbol}</div>` : nothing12}
3363
+ ${h.symbol ? html12`<div class="symbol">${h.symbol}</div>` : nothing12}
2857
3364
  <div class="lines" aria-hidden="true">
2858
3365
  ${lines.slice().reverse().map((l, idx) => {
2859
3366
  const realIdx = lines.length - 1 - idx + 1;
2860
3367
  const isChanging = changing.has(realIdx);
2861
3368
  const broken = l === 6 || l === 8;
2862
3369
  const cls = `${broken ? "broken" : "solid"}${isChanging ? " changing" : ""}`;
2863
- return html11`<div class="line ${cls}">
3370
+ return html12`<div class="line ${cls}">
2864
3371
  ${broken ? svg3`<span class="seg"></span><span class="seg"></span>` : svg3`<span class="seg"></span>`}
2865
3372
  </div>`;
2866
3373
  })}
@@ -2868,33 +3375,33 @@ var RoxyHexagram = class extends LitElement11 {
2868
3375
  </div>
2869
3376
  <div>
2870
3377
  <h2 class="title">
2871
- ${h.number ? html11`${h.number}. ` : nothing12}${h.english ?? h.chinese ?? "Hexagram"}
3378
+ ${h.number ? html12`${h.number}. ` : nothing12}${h.english ?? h.chinese ?? "Hexagram"}
2872
3379
  </h2>
2873
3380
  <p class="subtitle">
2874
- ${h.chinese ? html11`${h.chinese}` : nothing12}
2875
- ${h.pinyin ? html11` · ${h.pinyin}` : nothing12}
3381
+ ${h.chinese ? html12`${h.chinese}` : nothing12}
3382
+ ${h.pinyin ? html12` · ${h.pinyin}` : nothing12}
2876
3383
  </p>
2877
3384
  <div class="trigrams">
2878
- ${h.upperTrigram ? html11`<div>
3385
+ ${h.upperTrigram ? html12`<div>
2879
3386
  Upper
2880
3387
  <span class="tri-glyph"
2881
3388
  >${TRIGRAM_GLYPH[h.upperTrigram] ?? ""}</span
2882
3389
  >${h.upperTrigram}
2883
3390
  </div>` : nothing12}
2884
- ${h.lowerTrigram ? html11`<div>
3391
+ ${h.lowerTrigram ? html12`<div>
2885
3392
  Lower
2886
3393
  <span class="tri-glyph"
2887
3394
  >${TRIGRAM_GLYPH[h.lowerTrigram] ?? ""}</span
2888
3395
  >${h.lowerTrigram}
2889
3396
  </div>` : nothing12}
2890
3397
  </div>
2891
- ${h.judgment ? html11`<p class="judgment">${h.judgment}</p>` : nothing12}
2892
- ${h.image ? html11`<p class="image">${h.image}</p>` : nothing12}
2893
- ${dailyMessage ? html11`<p class="message">${dailyMessage}</p>` : nothing12}
2894
- ${h.interpretation?.general ? html11`<p>${h.interpretation.general}</p>` : nothing12}
2895
- ${changing.size > 0 ? html11`<div class="changing">
3398
+ ${h.judgment ? html12`<p class="judgment">${h.judgment}</p>` : nothing12}
3399
+ ${h.image ? html12`<p class="image">${h.image}</p>` : nothing12}
3400
+ ${dailyMessage ? html12`<p class="message">${dailyMessage}</p>` : nothing12}
3401
+ ${h.interpretation?.general ? html12`<p>${h.interpretation.general}</p>` : nothing12}
3402
+ ${changing.size > 0 ? html12`<div class="changing">
2896
3403
  Changing lines: ${Array.from(changing).sort((a, b) => a - b).join(", ")}.
2897
- ${resultingHexagram?.english ? html11` Becomes hexagram ${resultingHexagram.number}
3404
+ ${resultingHexagram?.english ? html12` Becomes hexagram ${resultingHexagram.number}
2898
3405
  ${resultingHexagram.english}.` : nothing12}
2899
3406
  </div>` : nothing12}
2900
3407
  </div>
@@ -2917,7 +3424,7 @@ var RoxyHexagram = class extends LitElement11 {
2917
3424
  };
2918
3425
  RoxyHexagram.styles = [
2919
3426
  baseStyles,
2920
- css12`
3427
+ css13`
2921
3428
  .card {
2922
3429
  background: var(--roxy-bg, #fff);
2923
3430
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -3037,7 +3544,7 @@ RoxyHexagram = __decorateClass([
3037
3544
  ], RoxyHexagram);
3038
3545
 
3039
3546
  // packages/ui/src/components/horoscope-card.ts
3040
- import { css as css13, html as html12, LitElement as LitElement12, nothing as nothing13 } from "lit";
3547
+ import { css as css14, html as html13, LitElement as LitElement12, nothing as nothing13 } from "lit";
3041
3548
  import { customElement as customElement12, property as property12 } from "lit/decorators.js";
3042
3549
  var RoxyHoroscopeCard = class extends LitElement12 {
3043
3550
  constructor() {
@@ -3048,12 +3555,12 @@ var RoxyHoroscopeCard = class extends LitElement12 {
3048
3555
  render() {
3049
3556
  const d = this.data;
3050
3557
  if (!d)
3051
- return html12`<div class="roxy-empty" role="status">No horoscope data</div>`;
3558
+ return html13`<div class="roxy-empty" role="status">No horoscope data</div>`;
3052
3559
  const sign = d.sign ?? "";
3053
3560
  const glyph = sign ? SIGN_GLYPH[capitalize(sign)] ?? "" : "";
3054
3561
  const energy = "energyRating" in d && typeof d.energyRating === "number" ? d.energyRating : null;
3055
3562
  const dateLabel = "date" in d && d.date || "week" in d && d.week || "month" in d && d.month || "";
3056
- return html12`<article
3563
+ return html13`<article
3057
3564
  class="card"
3058
3565
  aria-label=${`${this.period} horoscope for ${sign}`}
3059
3566
  >
@@ -3061,9 +3568,9 @@ var RoxyHoroscopeCard = class extends LitElement12 {
3061
3568
  <span class="glyph" aria-hidden="true">${glyph}</span>
3062
3569
  <div>
3063
3570
  <h2 class="title">${sign} ${this.period}</h2>
3064
- ${dateLabel ? html12`<div class="date">${dateLabel}</div>` : nothing13}
3571
+ ${dateLabel ? html13`<div class="date">${dateLabel}</div>` : nothing13}
3065
3572
  </div>
3066
- ${energy !== null ? html12`<span class="energy" aria-label=${`Energy ${energy} of 10`}>
3573
+ ${energy !== null ? html13`<span class="energy" aria-label=${`Energy ${energy} of 10`}>
3067
3574
  Energy ${energy}/10
3068
3575
  <span class="energy-bar"
3069
3576
  ><span style="width: ${energy / 10 * 100}%"></span
@@ -3071,26 +3578,26 @@ var RoxyHoroscopeCard = class extends LitElement12 {
3071
3578
  </span>` : nothing13}
3072
3579
  </header>
3073
3580
 
3074
- ${d.overview ? html12`<p class="overview">${d.overview}</p>` : nothing13}
3581
+ ${d.overview ? html13`<p class="overview">${d.overview}</p>` : nothing13}
3075
3582
 
3076
3583
  <div class="sections">
3077
- ${d.love ? html12`<div class="section">
3584
+ ${d.love ? html13`<div class="section">
3078
3585
  <h3>Love</h3>
3079
3586
  <p>${d.love}</p>
3080
3587
  </div>` : nothing13}
3081
- ${d.career ? html12`<div class="section">
3588
+ ${d.career ? html13`<div class="section">
3082
3589
  <h3>Career</h3>
3083
3590
  <p>${d.career}</p>
3084
3591
  </div>` : nothing13}
3085
- ${d.health ? html12`<div class="section">
3592
+ ${d.health ? html13`<div class="section">
3086
3593
  <h3>Health</h3>
3087
3594
  <p>${d.health}</p>
3088
3595
  </div>` : nothing13}
3089
- ${d.finance ? html12`<div class="section">
3596
+ ${d.finance ? html13`<div class="section">
3090
3597
  <h3>Finance</h3>
3091
3598
  <p>${d.finance}</p>
3092
3599
  </div>` : nothing13}
3093
- ${"advice" in d && d.advice ? html12`<div class="section">
3600
+ ${"advice" in d && d.advice ? html13`<div class="section">
3094
3601
  <h3>Advice</h3>
3095
3602
  <p>${d.advice}</p>
3096
3603
  </div>` : nothing13}
@@ -3104,21 +3611,21 @@ var RoxyHoroscopeCard = class extends LitElement12 {
3104
3611
  const compatibleSigns = d.compatibleSigns ?? [];
3105
3612
  if (luckyNumber === void 0 && !luckyColor && luckyNumbers.length === 0 && luckyDays.length === 0 && compatibleSigns.length === 0)
3106
3613
  return nothing13;
3107
- return html12`<div class="lucky">
3108
- ${luckyNumber !== void 0 ? html12`<span>Lucky number <strong>${luckyNumber}</strong></span>` : nothing13}
3109
- ${luckyColor ? html12`<span>Lucky color <strong>${luckyColor}</strong></span>` : nothing13}
3110
- ${luckyNumbers.length ? html12`<span
3614
+ return html13`<div class="lucky">
3615
+ ${luckyNumber !== void 0 ? html13`<span>Lucky number <strong>${luckyNumber}</strong></span>` : nothing13}
3616
+ ${luckyColor ? html13`<span>Lucky color <strong>${luckyColor}</strong></span>` : nothing13}
3617
+ ${luckyNumbers.length ? html13`<span
3111
3618
  >Lucky numbers
3112
3619
  <strong>${luckyNumbers.join(", ")}</strong></span
3113
3620
  >` : nothing13}
3114
- ${luckyDays.length ? html12`<span
3621
+ ${luckyDays.length ? html13`<span
3115
3622
  >Lucky days <strong>${luckyDays.join(", ")}</strong></span
3116
3623
  >` : nothing13}
3117
- ${compatibleSigns.length ? html12`<span class="compat-wrap">
3624
+ ${compatibleSigns.length ? html13`<span class="compat-wrap">
3118
3625
  Best with
3119
3626
  <span class="compat"
3120
3627
  >${compatibleSigns.map(
3121
- (s) => html12`<span>${s}</span>`
3628
+ (s) => html13`<span>${s}</span>`
3122
3629
  )}</span
3123
3630
  >
3124
3631
  </span>` : nothing13}
@@ -3129,7 +3636,7 @@ var RoxyHoroscopeCard = class extends LitElement12 {
3129
3636
  };
3130
3637
  RoxyHoroscopeCard.styles = [
3131
3638
  baseStyles,
3132
- css13`
3639
+ css14`
3133
3640
  .card {
3134
3641
  background: var(--roxy-bg, #fff);
3135
3642
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -3264,7 +3771,7 @@ RoxyHoroscopeCard = __decorateClass([
3264
3771
  ], RoxyHoroscopeCard);
3265
3772
 
3266
3773
  // packages/ui/src/components/kp-chart.ts
3267
- import { css as css14, html as html13, LitElement as LitElement13, nothing as nothing14 } from "lit";
3774
+ import { css as css15, html as html14, LitElement as LitElement13, nothing as nothing14 } from "lit";
3268
3775
  import { customElement as customElement13, property as property13, state as state3 } from "lit/decorators.js";
3269
3776
  var RoxyKpChart = class extends LitElement13 {
3270
3777
  constructor() {
@@ -3318,21 +3825,21 @@ var RoxyKpChart = class extends LitElement13 {
3318
3825
  }
3319
3826
  render() {
3320
3827
  if (!this.data)
3321
- return html13`<div class="roxy-empty" role="status">No KP chart data</div>`;
3828
+ return html14`<div class="roxy-empty" role="status">No KP chart data</div>`;
3322
3829
  const d = this.data;
3323
3830
  const asc = d.ascendant;
3324
- return html13`<div class="wrap" aria-label="KP chart" tabindex="0">
3831
+ return html14`<div class="wrap" aria-label="KP chart" tabindex="0">
3325
3832
  <header class="head">
3326
3833
  <h2 class="title">KP chart</h2>
3327
- ${asc ? html13`<div class="asc">
3834
+ ${asc ? html14`<div class="asc">
3328
3835
  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}
3836
+ ${asc.nakshatra ? html14`· ${asc.nakshatra}` : nothing14}
3837
+ ${asc.subLord ? html14`· sub lord ${asc.subLord}` : nothing14}
3838
+ ${typeof asc.kpNumber === "number" ? html14`· KP ${asc.kpNumber}` : nothing14}
3332
3839
  </div>` : nothing14}
3333
- ${typeof d.meta?.ayanamsa === "number" ? html13`<div class="ayan">
3840
+ ${typeof d.meta?.ayanamsa === "number" ? html14`<div class="ayan">
3334
3841
  ${d.meta.ayanamsaType ?? "Ayanamsa"}: ${formatNumber(d.meta.ayanamsa, 4)}°
3335
- ${d.meta.houseSystem ? html13`· ${d.meta.houseSystem} houses` : nothing14}
3842
+ ${d.meta.houseSystem ? html14`· ${d.meta.houseSystem} houses` : nothing14}
3336
3843
  </div>` : nothing14}
3337
3844
  </header>
3338
3845
 
@@ -3343,7 +3850,7 @@ var RoxyKpChart = class extends LitElement13 {
3343
3850
  @keydown=${this.onTabKeyDown}
3344
3851
  >
3345
3852
  ${["planets", "cusps"].map(
3346
- (t) => html13`<button
3853
+ (t) => html14`<button
3347
3854
  class="tab"
3348
3855
  role="tab"
3349
3856
  id="tab-${t}"
@@ -3367,8 +3874,8 @@ var RoxyKpChart = class extends LitElement13 {
3367
3874
  renderPlanets() {
3368
3875
  const bodies = this.bodies();
3369
3876
  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">
3877
+ return html14`<p class="roxy-empty" role="status">No planets</p>`;
3878
+ return html14`<table role="table" aria-label="KP planets and nodes">
3372
3879
  <thead>
3373
3880
  <tr>
3374
3881
  <th scope="col">Body</th>
@@ -3383,9 +3890,9 @@ var RoxyKpChart = class extends LitElement13 {
3383
3890
  </thead>
3384
3891
  <tbody>
3385
3892
  ${bodies.map(
3386
- (b) => html13`<tr>
3893
+ (b) => html14`<tr>
3387
3894
  <td class="body">
3388
- ${b.name}${b.retrograde ? html13`<span class="retro">R</span>` : nothing14}
3895
+ ${b.name}${b.retrograde ? html14`<span class="retro">R</span>` : nothing14}
3389
3896
  </td>
3390
3897
  <td>${b.sign ?? ""}</td>
3391
3898
  <td class="num">${typeof b.house === "number" ? b.house : ""}</td>
@@ -3402,8 +3909,8 @@ var RoxyKpChart = class extends LitElement13 {
3402
3909
  renderCusps() {
3403
3910
  const cusps = this.data?.cusps ?? [];
3404
3911
  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">
3912
+ return html14`<p class="roxy-empty" role="status">No cusps</p>`;
3913
+ return html14`<table role="table" aria-label="KP Placidus cusps">
3407
3914
  <thead>
3408
3915
  <tr>
3409
3916
  <th scope="col">House</th>
@@ -3418,7 +3925,7 @@ var RoxyKpChart = class extends LitElement13 {
3418
3925
  </thead>
3419
3926
  <tbody>
3420
3927
  ${cusps.map(
3421
- (c) => html13`<tr>
3928
+ (c) => html14`<tr>
3422
3929
  <td class="body num">${c.house}</td>
3423
3930
  <td>${c.sign ?? ""}</td>
3424
3931
  <td>${c.signLord ?? ""}</td>
@@ -3435,7 +3942,7 @@ var RoxyKpChart = class extends LitElement13 {
3435
3942
  };
3436
3943
  RoxyKpChart.styles = [
3437
3944
  baseStyles,
3438
- css14`
3945
+ css15`
3439
3946
  .wrap {
3440
3947
  border: 1px solid var(--roxy-border, #e4e4e7);
3441
3948
  border-radius: var(--roxy-radius-md, 8px);
@@ -3538,7 +4045,7 @@ RoxyKpChart = __decorateClass([
3538
4045
  ], RoxyKpChart);
3539
4046
 
3540
4047
  // packages/ui/src/components/kp-planets-table.ts
3541
- import { css as css15, html as html14, LitElement as LitElement14, nothing as nothing15 } from "lit";
4048
+ import { css as css16, html as html15, LitElement as LitElement14, nothing as nothing15 } from "lit";
3542
4049
  import { customElement as customElement14, property as property14 } from "lit/decorators.js";
3543
4050
  var RoxyKpPlanetsTable = class extends LitElement14 {
3544
4051
  constructor() {
@@ -3547,16 +4054,16 @@ var RoxyKpPlanetsTable = class extends LitElement14 {
3547
4054
  }
3548
4055
  render() {
3549
4056
  if (!this.data)
3550
- return html14`<div class="roxy-empty" role="status">No KP data</div>`;
4057
+ return html15`<div class="roxy-empty" role="status">No KP data</div>`;
3551
4058
  const planets = this.data.planets ?? [];
3552
- return html14`<div
4059
+ return html15`<div
3553
4060
  class="wrap"
3554
4061
  aria-label="KP planets table"
3555
4062
  tabindex="0"
3556
4063
  >
3557
4064
  <header class="head">
3558
4065
  <h2 class="title">KP planets</h2>
3559
- ${typeof this.data.ayanamsa === "number" ? html14`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : nothing15}
4066
+ ${typeof this.data.ayanamsa === "number" ? html15`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : nothing15}
3560
4067
  </header>
3561
4068
  <table role="table">
3562
4069
  <thead>
@@ -3573,10 +4080,10 @@ var RoxyKpPlanetsTable = class extends LitElement14 {
3573
4080
  </thead>
3574
4081
  <tbody>
3575
4082
  ${planets.map(
3576
- (p) => html14`<tr>
4083
+ (p) => html15`<tr>
3577
4084
  <td class="planet">
3578
4085
  ${p.planet}
3579
- ${p.retrograde ? html14`<span class="retro">R</span>` : nothing15}
4086
+ ${p.retrograde ? html15`<span class="retro">R</span>` : nothing15}
3580
4087
  </td>
3581
4088
  <td>${p.sign ?? ""}</td>
3582
4089
  <td>${p.signLord ?? ""}</td>
@@ -3594,7 +4101,7 @@ var RoxyKpPlanetsTable = class extends LitElement14 {
3594
4101
  };
3595
4102
  RoxyKpPlanetsTable.styles = [
3596
4103
  baseStyles,
3597
- css15`
4104
+ css16`
3598
4105
  .wrap {
3599
4106
  border: 1px solid var(--roxy-border, #e4e4e7);
3600
4107
  border-radius: var(--roxy-radius-md, 8px);
@@ -3663,7 +4170,7 @@ RoxyKpPlanetsTable = __decorateClass([
3663
4170
  ], RoxyKpPlanetsTable);
3664
4171
 
3665
4172
  // packages/ui/src/components/kp-ruling-planets.ts
3666
- import { css as css16, html as html15, LitElement as LitElement15, nothing as nothing16 } from "lit";
4173
+ import { css as css17, html as html16, LitElement as LitElement15, nothing as nothing16 } from "lit";
3667
4174
  import { customElement as customElement15, property as property15 } from "lit/decorators.js";
3668
4175
  var RoxyKpRulingPlanets = class extends LitElement15 {
3669
4176
  constructor() {
@@ -3672,13 +4179,13 @@ var RoxyKpRulingPlanets = class extends LitElement15 {
3672
4179
  }
3673
4180
  render() {
3674
4181
  if (!this.data)
3675
- return html15`<div class="roxy-empty" role="status">No ruling planets data</div>`;
4182
+ return html16`<div class="roxy-empty" role="status">No ruling planets data</div>`;
3676
4183
  const d = this.data;
3677
4184
  const significators = d.significators ?? [];
3678
- return html15`<div class="wrap" aria-label="KP ruling planets">
4185
+ return html16`<div class="wrap" aria-label="KP ruling planets">
3679
4186
  <header>
3680
4187
  <h2 class="title">KP ruling planets</h2>
3681
- ${d.dayLord ? html15`<div class="day-lord">Day lord: <strong>${d.dayLord}</strong></div>` : nothing16}
4188
+ ${d.dayLord ? html16`<div class="day-lord">Day lord: <strong>${d.dayLord}</strong></div>` : nothing16}
3682
4189
  </header>
3683
4190
 
3684
4191
  <div class="groups">
@@ -3702,14 +4209,14 @@ var RoxyKpRulingPlanets = class extends LitElement15 {
3702
4209
  </div>
3703
4210
  </div>
3704
4211
 
3705
- ${d.rulingPlanets?.length ? html15`<div class="rp-list" role="list" aria-label="Ruling planets by strength">
4212
+ ${d.rulingPlanets?.length ? html16`<div class="rp-list" role="list" aria-label="Ruling planets by strength">
3706
4213
  <span class="rp-label">Ruling planets</span>
3707
4214
  ${d.rulingPlanets.map(
3708
- (p, i) => html15`<span class="rp" role="listitem"><span class="rank">${i + 1}</span> ${p}</span>`
4215
+ (p, i) => html16`<span class="rp" role="listitem"><span class="rank">${i + 1}</span> ${p}</span>`
3709
4216
  )}
3710
4217
  </div>` : nothing16}
3711
4218
 
3712
- ${significators.length ? html15`<table aria-label="House significators">
4219
+ ${significators.length ? html16`<table aria-label="House significators">
3713
4220
  <thead>
3714
4221
  <tr>
3715
4222
  <th scope="col">Planet</th>
@@ -3718,7 +4225,7 @@ var RoxyKpRulingPlanets = class extends LitElement15 {
3718
4225
  </thead>
3719
4226
  <tbody>
3720
4227
  ${significators.map(
3721
- (s) => html15`<tr>
4228
+ (s) => html16`<tr>
3722
4229
  <td>${s.planet}</td>
3723
4230
  <td>${(s.signifies ?? []).join(", ")}</td>
3724
4231
  </tr>`
@@ -3730,7 +4237,7 @@ var RoxyKpRulingPlanets = class extends LitElement15 {
3730
4237
  };
3731
4238
  RoxyKpRulingPlanets.styles = [
3732
4239
  baseStyles,
3733
- css16`
4240
+ css17`
3734
4241
  .wrap {
3735
4242
  border: 1px solid var(--roxy-border, #e4e4e7);
3736
4243
  border-radius: var(--roxy-radius-md, 8px);
@@ -3836,7 +4343,7 @@ RoxyKpRulingPlanets = __decorateClass([
3836
4343
  ], RoxyKpRulingPlanets);
3837
4344
 
3838
4345
  // packages/ui/src/components/location-search.ts
3839
- import { css as css17, html as html16, LitElement as LitElement16, nothing as nothing17 } from "lit";
4346
+ import { css as css18, html as html17, LitElement as LitElement16, nothing as nothing17 } from "lit";
3840
4347
  import { customElement as customElement16, property as property16, state as state4 } from "lit/decorators.js";
3841
4348
 
3842
4349
  // packages/ui/src/utils/debounce.ts
@@ -3994,7 +4501,7 @@ var RoxyLocationSearch = class extends LitElement16 {
3994
4501
  );
3995
4502
  }
3996
4503
  render() {
3997
- return html16`<div class="field">
4504
+ return html17`<div class="field">
3998
4505
  <input
3999
4506
  type="text"
4000
4507
  role="combobox"
@@ -4011,14 +4518,14 @@ var RoxyLocationSearch = class extends LitElement16 {
4011
4518
  if (this.results.length > 0) this.isOpen = true;
4012
4519
  }}
4013
4520
  />
4014
- ${this.isLoading ? html16`<span class="spinner" role="status" aria-label="Loading"></span>` : nothing17}
4015
- ${this.isOpen ? html16`<ul
4521
+ ${this.isLoading ? html17`<span class="spinner" role="status" aria-label="Loading"></span>` : nothing17}
4522
+ ${this.isOpen ? html17`<ul
4016
4523
  id="roxy-location-listbox"
4017
4524
  class="results"
4018
4525
  role="listbox"
4019
4526
  >
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">
4527
+ ${this.results.length === 0 ? html17`<li class="empty" role="status">No cities found</li>` : this.results.map(
4528
+ (city, idx) => html17`<li role="presentation">
4022
4529
  <button
4023
4530
  type="button"
4024
4531
  class="option"
@@ -4032,7 +4539,7 @@ var RoxyLocationSearch = class extends LitElement16 {
4032
4539
  >
4033
4540
  <span class="city">${city.city}</span>
4034
4541
  <span class="where"
4035
- >${city.province ? html16`${city.province}, ` : ""}${city.country}</span
4542
+ >${city.province ? html17`${city.province}, ` : ""}${city.country}</span
4036
4543
  >
4037
4544
  <span class="tz"
4038
4545
  >UTC${city.utcOffset >= 0 ? "+" : ""}${city.utcOffset}</span
@@ -4046,7 +4553,7 @@ var RoxyLocationSearch = class extends LitElement16 {
4046
4553
  };
4047
4554
  RoxyLocationSearch.styles = [
4048
4555
  baseStyles,
4049
- css17`
4556
+ css18`
4050
4557
  :host {
4051
4558
  display: block;
4052
4559
  position: relative;
@@ -4184,7 +4691,7 @@ RoxyLocationSearch = __decorateClass([
4184
4691
  ], RoxyLocationSearch);
4185
4692
 
4186
4693
  // packages/ui/src/components/moon-phase.ts
4187
- import { css as css18, html as html17, LitElement as LitElement17, nothing as nothing18 } from "lit";
4694
+ import { css as css19, html as html18, LitElement as LitElement17, nothing as nothing18 } from "lit";
4188
4695
  import { customElement as customElement17, property as property17 } from "lit/decorators.js";
4189
4696
  var RoxyMoonPhase = class extends LitElement17 {
4190
4697
  constructor() {
@@ -4195,12 +4702,12 @@ var RoxyMoonPhase = class extends LitElement17 {
4195
4702
  render() {
4196
4703
  const d = this.data;
4197
4704
  if (!d)
4198
- return html17`<div class="roxy-empty" role="status">No moon phase data</div>`;
4705
+ return html18`<div class="roxy-empty" role="status">No moon phase data</div>`;
4199
4706
  const list = "phases" in d ? d.phases : "calendar" in d ? d.calendar : [];
4200
4707
  if (this.mode !== "current" && list.length > 0) {
4201
4708
  const month = "month" in d ? d.month : void 0;
4202
4709
  const year = "year" in d ? d.year : void 0;
4203
- return html17`<article
4710
+ return html18`<article
4204
4711
  class="card"
4205
4712
  aria-label="Moon phase calendar"
4206
4713
  >
@@ -4215,41 +4722,41 @@ var RoxyMoonPhase = class extends LitElement17 {
4215
4722
  }
4216
4723
  renderSingle(d) {
4217
4724
  const emoji = phaseEmoji(d.phase);
4218
- return html17`<article class="card" aria-label="Current moon phase">
4725
+ return html18`<article class="card" aria-label="Current moon phase">
4219
4726
  <div class="hero">
4220
4727
  <span class="emoji" aria-hidden="true">${emoji}</span>
4221
4728
  <div>
4222
4729
  <h2 class="label">${d.phase ?? "Moon"}</h2>
4223
- ${d.date ? html17`<div class="date">${d.date}</div>` : nothing18}
4730
+ ${d.date ? html18`<div class="date">${d.date}</div>` : nothing18}
4224
4731
  </div>
4225
4732
  </div>
4226
4733
  <div class="stats">
4227
- ${typeof d.illumination === "number" ? html17`<div>
4734
+ ${typeof d.illumination === "number" ? html18`<div>
4228
4735
  <span>Illumination</span>
4229
4736
  <strong>${formatIllumination(d.illumination)}</strong>
4230
4737
  </div>` : nothing18}
4231
- ${typeof d.age === "number" ? html17`<div>
4738
+ ${typeof d.age === "number" ? html18`<div>
4232
4739
  <span>Age</span>
4233
4740
  <strong>${formatNumber(d.age, 1)} days</strong>
4234
4741
  </div>` : nothing18}
4235
- ${d.sign ? html17`<div>
4742
+ ${d.sign ? html18`<div>
4236
4743
  <span>Sign</span>
4237
4744
  <strong>${d.sign}</strong>
4238
4745
  </div>` : nothing18}
4239
- ${typeof d.distance === "number" ? html17`<div>
4746
+ ${typeof d.distance === "number" ? html18`<div>
4240
4747
  <span>Distance</span>
4241
4748
  <strong>${(d.distance / 1e3).toFixed(0)}k km</strong>
4242
4749
  </div>` : nothing18}
4243
4750
  </div>
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>`)}
4751
+ ${d.meaning?.description ? html18`<p class="meaning">${d.meaning.description}</p>` : nothing18}
4752
+ ${d.meaning?.keywords?.length ? html18`<div class="keywords">
4753
+ ${d.meaning.keywords.map((k) => html18`<span>${k}</span>`)}
4247
4754
  </div>` : nothing18}
4248
4755
  </article>`;
4249
4756
  }
4250
4757
  renderListItem(p) {
4251
4758
  const emoji = phaseEmoji(p.phase);
4252
- return html17`<div class="list-item" role="listitem">
4759
+ return html18`<div class="list-item" role="listitem">
4253
4760
  <span aria-hidden="true">${emoji}</span>
4254
4761
  <span>${p.phase}</span>
4255
4762
  <span>${p.date ?? ""}</span>
@@ -4258,7 +4765,7 @@ var RoxyMoonPhase = class extends LitElement17 {
4258
4765
  };
4259
4766
  RoxyMoonPhase.styles = [
4260
4767
  baseStyles,
4261
- css18`
4768
+ css19`
4262
4769
  .card {
4263
4770
  background: var(--roxy-bg, #fff);
4264
4771
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -4361,7 +4868,7 @@ function formatIllumination(v) {
4361
4868
  }
4362
4869
 
4363
4870
  // packages/ui/src/components/nakshatra-card.ts
4364
- import { css as css19, html as html18, LitElement as LitElement18, nothing as nothing19 } from "lit";
4871
+ import { css as css20, html as html19, LitElement as LitElement18, nothing as nothing19 } from "lit";
4365
4872
  import { customElement as customElement18, property as property18 } from "lit/decorators.js";
4366
4873
  var RoxyNakshatraCard = class extends LitElement18 {
4367
4874
  constructor() {
@@ -4370,33 +4877,33 @@ var RoxyNakshatraCard = class extends LitElement18 {
4370
4877
  }
4371
4878
  render() {
4372
4879
  if (!this.data)
4373
- return html18`<div class="roxy-empty" role="status">No nakshatra data</div>`;
4880
+ return html19`<div class="roxy-empty" role="status">No nakshatra data</div>`;
4374
4881
  const n = this.data;
4375
4882
  const remedies = n.remedies;
4376
- return html18`<article class="wrap" aria-label=${`Nakshatra ${n.name}`}>
4883
+ return html19`<article class="wrap" aria-label=${`Nakshatra ${n.name}`}>
4377
4884
  <header class="head">
4378
4885
  <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}
4886
+ ${typeof n.number === "number" ? html19`<span class="number">Nakshatra ${n.number} of 27</span>` : nothing19}
4887
+ ${n.range ? html19`<span class="range">${n.range}</span>` : nothing19}
4381
4888
  </header>
4382
4889
 
4383
4890
  <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}
4891
+ ${n.lord ? html19`<div class="fact"><dt>Lord</dt><dd>${n.lord}</dd></div>` : nothing19}
4892
+ ${n.deity ? html19`<div class="fact"><dt>Deity</dt><dd>${n.deity}</dd></div>` : nothing19}
4893
+ ${n.symbol ? html19`<div class="fact"><dt>Symbol</dt><dd>${n.symbol}</dd></div>` : nothing19}
4387
4894
  </dl>
4388
4895
 
4389
- ${n.characteristics ? html18`<div class="section">
4896
+ ${n.characteristics ? html19`<div class="section">
4390
4897
  <h3>Characteristics</h3>
4391
4898
  <p>${n.characteristics}</p>
4392
4899
  </div>` : nothing19}
4393
4900
 
4394
- ${remedies ? html18`<div class="section">
4901
+ ${remedies ? html19`<div class="section">
4395
4902
  <h3>Remedies</h3>
4396
4903
  <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}
4904
+ ${remedies.mantras ? html19`<div class="remedy"><strong>Mantras:</strong> ${remedies.mantras}</div>` : nothing19}
4905
+ ${remedies.gemstones ? html19`<div class="remedy"><strong>Gemstones:</strong> ${remedies.gemstones}</div>` : nothing19}
4906
+ ${remedies.rituals ? html19`<div class="remedy"><strong>Rituals:</strong> ${remedies.rituals}</div>` : nothing19}
4400
4907
  </div>
4401
4908
  </div>` : nothing19}
4402
4909
  </article>`;
@@ -4404,7 +4911,7 @@ var RoxyNakshatraCard = class extends LitElement18 {
4404
4911
  };
4405
4912
  RoxyNakshatraCard.styles = [
4406
4913
  baseStyles,
4407
- css19`
4914
+ css20`
4408
4915
  .wrap {
4409
4916
  border: 1px solid var(--roxy-border, #e4e4e7);
4410
4917
  border-radius: var(--roxy-radius-md, 8px);
@@ -4490,7 +4997,7 @@ RoxyNakshatraCard = __decorateClass([
4490
4997
  ], RoxyNakshatraCard);
4491
4998
 
4492
4999
  // 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";
5000
+ import { css as css21, html as html20, LitElement as LitElement19, nothing as nothing20, svg as svg4 } from "lit";
4494
5001
  import { customElement as customElement19, property as property19, state as state5 } from "lit/decorators.js";
4495
5002
  var SIZE = 420;
4496
5003
  var CENTER = SIZE / 2;
@@ -4522,14 +5029,14 @@ var RoxyNatalChart = class extends LitElement19 {
4522
5029
  }
4523
5030
  render() {
4524
5031
  if (!this.data)
4525
- return html19`<div class="roxy-empty" role="status">No chart data</div>`;
5032
+ return html20`<div class="roxy-empty" role="status">No chart data</div>`;
4526
5033
  const planets = this.getPlanets();
4527
5034
  const aspects = this.data.aspects ?? [];
4528
5035
  const view = this.view;
4529
- return html19`<div class="wrap">
5036
+ return html20`<div class="wrap">
4530
5037
  <header>
4531
5038
  <h2 class="title">Natal chart</h2>
4532
- ${this.data.birthDetails ? html19`<div class="meta">
5039
+ ${this.data.birthDetails ? html20`<div class="meta">
4533
5040
  ${[this.data.birthDetails.date, this.data.birthDetails.time].filter(Boolean).join(" \xB7 ")}
4534
5041
  </div>` : nothing20}
4535
5042
  </header>
@@ -4540,7 +5047,7 @@ var RoxyNatalChart = class extends LitElement19 {
4540
5047
  @keydown=${this.onTabKeyDown}
4541
5048
  >
4542
5049
  ${["wheel", "grid"].map(
4543
- (t) => html19`<button
5050
+ (t) => html20`<button
4544
5051
  class="tab"
4545
5052
  role="tab"
4546
5053
  id="tab-${t}"
@@ -4561,7 +5068,7 @@ var RoxyNatalChart = class extends LitElement19 {
4561
5068
  <div class="legend">
4562
5069
  <span>${planets.length} planets</span>
4563
5070
  <span>${aspects.length} aspects</span>
4564
- ${this.data.houseSystem ? html19`<span>${this.data.houseSystem} houses</span>` : nothing20}
5071
+ ${this.data.houseSystem ? html20`<span>${this.data.houseSystem} houses</span>` : nothing20}
4565
5072
  <span><span class="legend-swatch" style="background: var(--roxy-success)"></span>harmonious</span>
4566
5073
  <span><span class="legend-swatch" style="background: var(--roxy-danger)"></span>challenging</span>
4567
5074
  </div>
@@ -4579,7 +5086,7 @@ var RoxyNatalChart = class extends LitElement19 {
4579
5086
  });
4580
5087
  }
4581
5088
  renderWheel(planets, aspects) {
4582
- return html19`<svg
5089
+ return html20`<svg
4583
5090
  viewBox="0 0 ${SIZE} ${SIZE}"
4584
5091
  role="img"
4585
5092
  aria-label="Natal chart wheel with twelve houses, planets, and aspects"
@@ -4612,35 +5119,35 @@ var RoxyNatalChart = class extends LitElement19 {
4612
5119
  byPair.set(k, a);
4613
5120
  }
4614
5121
  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">
5122
+ return html20`<p class="roxy-empty" role="status">No planets to grid</p>`;
5123
+ return html20`<div class="grid-scroll">
4617
5124
  <table class="aspect-grid" aria-label="Planet by planet aspect grid">
4618
5125
  <thead>
4619
5126
  <tr>
4620
5127
  <th></th>
4621
5128
  ${names.slice(0, -1).map((n) => {
4622
5129
  const g = PLANET_GLYPH[n] ?? n.slice(0, 2);
4623
- return html19`<th scope="col" title=${n}>${g}</th>`;
5130
+ return html20`<th scope="col" title=${n}>${g}</th>`;
4624
5131
  })}
4625
5132
  </tr>
4626
5133
  </thead>
4627
5134
  <tbody>
4628
5135
  ${names.slice(1).map((rowName, ri) => {
4629
5136
  const rowGlyph = PLANET_GLYPH[rowName] ?? rowName.slice(0, 2);
4630
- return html19`<tr>
5137
+ return html20`<tr>
4631
5138
  <th scope="row" title=${rowName}>${rowGlyph}</th>
4632
5139
  ${names.slice(0, ri + 1).map((colName) => {
4633
5140
  const a = byPair.get([rowName, colName].sort().join("|"));
4634
- if (!a) return html19`<td class="empty"></td>`;
5141
+ if (!a) return html20`<td class="empty"></td>`;
4635
5142
  const name = normalizeAspect(a);
4636
5143
  const sym = ASPECT_SYMBOL[name] ?? ASPECT_SYMBOL[name.replace(/-/g, "")] ?? name.slice(0, 3);
4637
5144
  const cls = ASPECT_CLASS[name] ?? "aspect-other";
4638
5145
  const orb = formatNumber(a.orb, 1);
4639
- return html19`<td class=${`cell ${cls}`} title=${`${rowName} ${name} ${colName}${orb ? ` (orb ${orb}\xB0)` : ""}`}>
5146
+ return html20`<td class=${`cell ${cls}`} title=${`${rowName} ${name} ${colName}${orb ? ` (orb ${orb}\xB0)` : ""}`}>
4640
5147
  <span class="asp">${sym}</span>
4641
5148
  </td>`;
4642
5149
  })}
4643
- ${names.slice(ri + 1, -1).map(() => html19`<td class="empty"></td>`)}
5150
+ ${names.slice(ri + 1, -1).map(() => html20`<td class="empty"></td>`)}
4644
5151
  </tr>`;
4645
5152
  })}
4646
5153
  </tbody>
@@ -4757,16 +5264,48 @@ var RoxyNatalChart = class extends LitElement19 {
4757
5264
  });
4758
5265
  }
4759
5266
  renderPlanets(planets) {
4760
- return planets.map((p) => {
4761
- if (!Number.isFinite(p.longitude)) return nothing20;
4762
- const angle = this.toAngle(p.longitude);
4763
- const glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, angle);
4764
- const degPos = polarToCartesian(CENTER, CENTER, PLANET_R - 13, angle);
5267
+ const MIN_SEPARATION = 7;
5268
+ const sorted = planets.filter((p) => Number.isFinite(p.longitude)).map((p) => ({
5269
+ p,
5270
+ trueLon: normalizeLongitude(p.longitude),
5271
+ displayLon: normalizeLongitude(p.longitude)
5272
+ })).sort((a, b) => a.trueLon - b.trueLon);
5273
+ for (let i = 1; i < sorted.length; i++) {
5274
+ const prev = sorted[i - 1];
5275
+ const cur = sorted[i];
5276
+ if (!prev || !cur) continue;
5277
+ const wanted = prev.displayLon + MIN_SEPARATION;
5278
+ if (cur.displayLon < wanted) cur.displayLon = wanted;
5279
+ }
5280
+ const last = sorted[sorted.length - 1];
5281
+ if (last && last.displayLon > 360) {
5282
+ const shift = last.displayLon - 360;
5283
+ for (const s of sorted) s.displayLon -= shift;
5284
+ }
5285
+ return sorted.map(({ p, trueLon, displayLon }) => {
5286
+ const trueAngle = this.toAngle(trueLon);
5287
+ const displayAngle = this.toAngle(displayLon);
5288
+ const glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, displayAngle);
5289
+ const degPos = polarToCartesian(
5290
+ CENTER,
5291
+ CENTER,
5292
+ PLANET_R - 13,
5293
+ displayAngle
5294
+ );
5295
+ const rimPos = polarToCartesian(CENTER, CENTER, OUTER_R - 4, trueAngle);
5296
+ const leaderInner = polarToCartesian(
5297
+ CENTER,
5298
+ CENTER,
5299
+ PLANET_R + 8,
5300
+ displayAngle
5301
+ );
4765
5302
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
4766
5303
  const sp = longitudeToSignPosition(p.longitude);
4767
5304
  const retro = p.isRetrograde === true;
4768
5305
  const degLabel = `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}'`;
5306
+ const offset = Math.abs(displayLon - trueLon) > 0.5;
4769
5307
  return svg4`<g>
5308
+ ${offset ? svg4`<line class="planet-leader" x1=${rimPos.x} y1=${rimPos.y} x2=${leaderInner.x} y2=${leaderInner.y} />` : nothing20}
4770
5309
  <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
5310
  <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
5311
  </g>`;
@@ -4777,23 +5316,23 @@ var RoxyNatalChart = class extends LitElement19 {
4777
5316
  const ai = this.data?.aspectsInterpretation;
4778
5317
  if (!summary && !ai) return nothing20;
4779
5318
  const retrogrades = summary?.retrogradePlanets ?? [];
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}
5319
+ return html20`<div class="details">
5320
+ ${summary?.dominantElement || summary?.dominantModality ? html20`<div class="pill-row">
5321
+ ${summary.dominantElement ? html20`<span class="pill">Dominant element: ${summary.dominantElement}</span>` : nothing20}
5322
+ ${summary.dominantModality ? html20`<span class="pill">Dominant modality: ${summary.dominantModality}</span>` : nothing20}
4784
5323
  </div>` : nothing20}
4785
- ${ai ? html19`<div class="pill-row">
5324
+ ${ai ? html20`<div class="pill-row">
4786
5325
  <span class="pill pill--success">Harmonious ${ai.harmonious}</span>
4787
5326
  <span class="pill pill--danger">Challenging ${ai.challenging}</span>
4788
5327
  <span class="pill pill--muted">Neutral ${ai.neutral}</span>
4789
5328
  </div>` : nothing20}
4790
- ${retrogrades.length > 0 ? html19`<div class="pill-row">
5329
+ ${retrogrades.length > 0 ? html20`<div class="pill-row">
4791
5330
  ${retrogrades.map((p) => {
4792
5331
  const glyph = PLANET_GLYPH[p] ?? p.slice(0, 2);
4793
- return html19`<span class="pill pill--muted">${glyph} ${p} R</span>`;
5332
+ return html20`<span class="pill pill--muted">${glyph} ${p} R</span>`;
4794
5333
  })}
4795
5334
  </div>` : nothing20}
4796
- ${ai?.summary ? html19`<p class="summary">${ai.summary}</p>` : nothing20}
5335
+ ${ai?.summary ? html20`<p class="summary">${ai.summary}</p>` : nothing20}
4797
5336
  ${this.renderElementModalityGrid()}
4798
5337
  </div>`;
4799
5338
  }
@@ -4820,11 +5359,11 @@ var RoxyNatalChart = class extends LitElement19 {
4820
5359
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? capitalize(p.name).slice(0, 2);
4821
5360
  cells[el]?.[mod]?.push(glyph);
4822
5361
  }
4823
- return html19`<table class="em-grid" aria-label="Element and modality distribution">
5362
+ return html20`<table class="em-grid" aria-label="Element and modality distribution">
4824
5363
  <thead>
4825
5364
  <tr>
4826
5365
  <th></th>
4827
- ${MODALITIES.map((m) => html19`<th scope="col">${m.slice(0, 3)}</th>`)}
5366
+ ${MODALITIES.map((m) => html20`<th scope="col">${m.slice(0, 3)}</th>`)}
4828
5367
  <th scope="col">Total</th>
4829
5368
  </tr>
4830
5369
  </thead>
@@ -4834,10 +5373,10 @@ var RoxyNatalChart = class extends LitElement19 {
4834
5373
  (s, m) => s + (cells[el]?.[m]?.length ?? 0),
4835
5374
  0
4836
5375
  );
4837
- return html19`<tr>
5376
+ return html20`<tr>
4838
5377
  <th scope="row">${el}</th>
4839
5378
  ${MODALITIES.map(
4840
- (m) => html19`<td>${(cells[el]?.[m] ?? []).join(" ")}</td>`
5379
+ (m) => html20`<td>${(cells[el]?.[m] ?? []).join(" ")}</td>`
4841
5380
  )}
4842
5381
  <td class="em-total">${rowTotal}</td>
4843
5382
  </tr>`;
@@ -4845,7 +5384,7 @@ var RoxyNatalChart = class extends LitElement19 {
4845
5384
  <tr>
4846
5385
  <th scope="row">Total</th>
4847
5386
  ${MODALITIES.map(
4848
- (m) => html19`<td class="em-total">${ELEMENTS.reduce((s, el) => s + (cells[el]?.[m]?.length ?? 0), 0)}</td>`
5387
+ (m) => html20`<td class="em-total">${ELEMENTS.reduce((s, el) => s + (cells[el]?.[m]?.length ?? 0), 0)}</td>`
4849
5388
  )}
4850
5389
  <td class="em-total">${planets.length}</td>
4851
5390
  </tr>
@@ -4855,18 +5394,18 @@ var RoxyNatalChart = class extends LitElement19 {
4855
5394
  renderInterpretations() {
4856
5395
  const planets = this.getPlanets().filter((p) => p.interpretation);
4857
5396
  if (planets.length === 0) return nothing20;
4858
- return html19`<section class="interpretations">
5397
+ return html20`<section class="interpretations">
4859
5398
  <h3>Planet readings</h3>
4860
5399
  ${planets.map((p, idx) => {
4861
5400
  const interp = p.interpretation;
4862
5401
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
4863
5402
  const deg = formatNumber(p.degree ?? 0, 1);
4864
- return html19`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
5403
+ return html20`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
4865
5404
  <summary>${glyph} ${p.name} <small>${p.sign ?? ""} ${deg}</small></summary>
4866
5405
  <div class="interp-body">
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}
5406
+ ${interp.summary ? html20`<p class="interp-summary">${interp.summary}</p>` : nothing20}
5407
+ ${interp.detailed ? html20`<p class="interp-detail">${interp.detailed}</p>` : nothing20}
5408
+ ${interp.keywords?.length ? html20`<div class="interp-keywords">${interp.keywords.map((k) => html20`<span class="kw">${k}</span>`)}</div>` : nothing20}
4870
5409
  </div>
4871
5410
  </details>`;
4872
5411
  })}
@@ -4904,7 +5443,7 @@ var RoxyNatalChart = class extends LitElement19 {
4904
5443
  };
4905
5444
  RoxyNatalChart.styles = [
4906
5445
  baseStyles,
4907
- css20`
5446
+ css21`
4908
5447
  .wrap {
4909
5448
  width: 100%;
4910
5449
  display: grid;
@@ -4926,7 +5465,8 @@ RoxyNatalChart.styles = [
4926
5465
  svg {
4927
5466
  display: block;
4928
5467
  width: 100%;
4929
- max-width: 360px;
5468
+ max-width: 560px;
5469
+ aspect-ratio: 1 / 1;
4930
5470
  height: auto;
4931
5471
  margin: 0 auto;
4932
5472
  }
@@ -4955,10 +5495,33 @@ RoxyNatalChart.styles = [
4955
5495
  font-family: var(--roxy-font-sans);
4956
5496
  }
4957
5497
 
5498
+ /* Below 480px the chart container shrinks to ~320px on phones.
5499
+ * Bump in-SVG text up proportionally so the 7px degree band
5500
+ * does not collapse below ~6px on screen.
5501
+ */
5502
+ @container (max-width: 480px) {
5503
+ .sign-glyph,
5504
+ .planet-glyph {
5505
+ font-size: 18px;
5506
+ }
5507
+ .planet-deg {
5508
+ font-size: 10px;
5509
+ }
5510
+ .house-num {
5511
+ font-size: 12px;
5512
+ }
5513
+ }
5514
+
4958
5515
  .planet-deg .retro {
4959
5516
  fill: var(--roxy-danger, #dc2626);
4960
5517
  }
4961
5518
 
5519
+ .planet-leader {
5520
+ stroke: var(--roxy-accent, #f59e0b);
5521
+ stroke-width: 0.5;
5522
+ opacity: 0.55;
5523
+ }
5524
+
4962
5525
  .house-num {
4963
5526
  fill: var(--roxy-muted, #71717a);
4964
5527
  font-size: 9px;
@@ -5233,7 +5796,7 @@ RoxyNatalChart = __decorateClass([
5233
5796
  ], RoxyNatalChart);
5234
5797
 
5235
5798
  // packages/ui/src/components/numerology-card.ts
5236
- import { css as css21, html as html20, LitElement as LitElement20, nothing as nothing21 } from "lit";
5799
+ import { css as css22, html as html21, LitElement as LitElement20, nothing as nothing21 } from "lit";
5237
5800
  import { customElement as customElement20, property as property20 } from "lit/decorators.js";
5238
5801
  var RoxyNumerologyCard = class extends LitElement20 {
5239
5802
  constructor() {
@@ -5244,7 +5807,7 @@ var RoxyNumerologyCard = class extends LitElement20 {
5244
5807
  render() {
5245
5808
  const d = this.data;
5246
5809
  if (!d)
5247
- return html20`<div class="roxy-empty" role="status">No numerology data</div>`;
5810
+ return html21`<div class="roxy-empty" role="status">No numerology data</div>`;
5248
5811
  const headerLabel = LABELS[this.type] ?? this.type;
5249
5812
  if ("coreNumbers" in d) return this.renderChart(d, headerLabel);
5250
5813
  if ("personalYear" in d) return this.renderPersonalYear(d, headerLabel);
@@ -5255,50 +5818,50 @@ var RoxyNumerologyCard = class extends LitElement20 {
5255
5818
  }
5256
5819
  renderNumberCard(d, headerLabel) {
5257
5820
  const keywords = d.meaning?.keywords ?? [];
5258
- return html20`<article class="card" aria-label=${headerLabel}>
5821
+ return html21`<article class="card" aria-label=${headerLabel}>
5259
5822
  <div class="hero">
5260
- ${typeof d.number === "number" ? html20`<div class="numeral">${d.number}</div>` : nothing21}
5823
+ ${typeof d.number === "number" ? html21`<div class="numeral">${d.number}</div>` : nothing21}
5261
5824
  <div>
5262
5825
  <p class="label">${headerLabel}</p>
5263
- ${d.meaning?.title ? html20`<h2 class="title">${d.meaning.title}</h2>` : nothing21}
5826
+ ${d.meaning?.title ? html21`<h2 class="title">${d.meaning.title}</h2>` : nothing21}
5264
5827
  </div>
5265
5828
  </div>
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>`)}
5829
+ ${d.meaning?.description ? html21`<p class="meaning">${d.meaning.description}</p>` : nothing21}
5830
+ ${d.calculation ? html21`<pre class="calc">${d.calculation}</pre>` : nothing21}
5831
+ ${keywords.length > 0 ? html21`<div class="chips">
5832
+ ${keywords.map((k) => html21`<span>${k}</span>`)}
5270
5833
  </div>` : nothing21}
5271
- ${d.hasKarmicDebt && d.karmicDebtNumber ? html20`<div class="karmic">
5834
+ ${d.hasKarmicDebt && d.karmicDebtNumber ? html21`<div class="karmic">
5272
5835
  Karmic debt ${d.karmicDebtNumber}.
5273
5836
  ${karmicDebtText(d.karmicDebtMeaning)}
5274
5837
  </div>` : nothing21}
5275
5838
  </article>`;
5276
5839
  }
5277
5840
  renderPersonalYear(d, headerLabel) {
5278
- return html20`<article class="card" aria-label=${headerLabel}>
5841
+ return html21`<article class="card" aria-label=${headerLabel}>
5279
5842
  <div class="hero">
5280
- ${typeof d.personalYear === "number" ? html20`<div class="numeral">${d.personalYear}</div>` : nothing21}
5843
+ ${typeof d.personalYear === "number" ? html21`<div class="numeral">${d.personalYear}</div>` : nothing21}
5281
5844
  <div>
5282
5845
  <p class="label">${headerLabel}</p>
5283
- ${d.theme ? html20`<h2 class="title">${d.theme}</h2>` : nothing21}
5846
+ ${d.theme ? html21`<h2 class="title">${d.theme}</h2>` : nothing21}
5284
5847
  </div>
5285
5848
  </div>
5286
- ${d.forecast ? html20`<p class="meaning">${d.forecast}</p>` : nothing21}
5287
- ${d.advice ? html20`<p>${d.advice}</p>` : nothing21}
5849
+ ${d.forecast ? html21`<p class="meaning">${d.forecast}</p>` : nothing21}
5850
+ ${d.advice ? html21`<p>${d.advice}</p>` : nothing21}
5288
5851
  </article>`;
5289
5852
  }
5290
5853
  renderChart(d, headerLabel) {
5291
5854
  const cores = Object.entries(d.coreNumbers).filter(
5292
5855
  ([, v]) => v !== null && v !== void 0
5293
5856
  );
5294
- return html20`<article class="card" aria-label=${headerLabel}>
5857
+ return html21`<article class="card" aria-label=${headerLabel}>
5295
5858
  <div>
5296
5859
  <p class="label">${headerLabel}</p>
5297
- ${d.profile?.name ? html20`<h2 class="title">${d.profile.name}</h2>` : nothing21}
5860
+ ${d.profile?.name ? html21`<h2 class="title">${d.profile.name}</h2>` : nothing21}
5298
5861
  </div>
5299
- ${cores.length > 0 ? html20`<div class="cores">
5862
+ ${cores.length > 0 ? html21`<div class="cores">
5300
5863
  ${cores.map(
5301
- ([k, v]) => html20`<div class="item">
5864
+ ([k, v]) => html21`<div class="item">
5302
5865
  <span>${humanize(k)}</span>
5303
5866
  <strong>${v.number ?? ""}</strong>
5304
5867
  </div>`
@@ -5309,7 +5872,7 @@ var RoxyNumerologyCard = class extends LitElement20 {
5309
5872
  };
5310
5873
  RoxyNumerologyCard.styles = [
5311
5874
  baseStyles,
5312
- css21`
5875
+ css22`
5313
5876
  .card {
5314
5877
  background: var(--roxy-bg, #fff);
5315
5878
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -5428,7 +5991,7 @@ function karmicDebtText(value) {
5428
5991
  }
5429
5992
 
5430
5993
  // packages/ui/src/components/panchang-table.ts
5431
- import { css as css22, html as html21, LitElement as LitElement21, nothing as nothing22 } from "lit";
5994
+ import { css as css23, html as html22, LitElement as LitElement21, nothing as nothing22 } from "lit";
5432
5995
  import { customElement as customElement21, property as property21 } from "lit/decorators.js";
5433
5996
  var RoxyPanchangTable = class extends LitElement21 {
5434
5997
  constructor() {
@@ -5439,7 +6002,7 @@ var RoxyPanchangTable = class extends LitElement21 {
5439
6002
  render() {
5440
6003
  const d = this.data;
5441
6004
  if (!d)
5442
- return html21`<div class="roxy-empty" role="status">No panchang data</div>`;
6005
+ return html22`<div class="roxy-empty" role="status">No panchang data</div>`;
5443
6006
  const detailed = "sunrise" in d ? d : null;
5444
6007
  const fivefold = [
5445
6008
  ["Tithi", this.formatPart(d.tithi)],
@@ -5462,7 +6025,8 @@ var RoxyPanchangTable = class extends LitElement21 {
5462
6025
  ["Yamaganda", detailed.yamaganda],
5463
6026
  ["Gulika", detailed.gulika]
5464
6027
  ] : [];
5465
- return html21`<div class="wrap" aria-label="Panchang">
6028
+ const transitions = detailed && "transitions" in detailed ? detailed.transitions : void 0;
6029
+ return html22`<div class="wrap" aria-label="Panchang">
5466
6030
  <header class="head">
5467
6031
  <h2 class="title">Panchang</h2>
5468
6032
  <span class="date">${detailed ? formatDate(detailed.date) : ""}</span>
@@ -5470,35 +6034,46 @@ var RoxyPanchangTable = class extends LitElement21 {
5470
6034
  <table>
5471
6035
  <tbody>
5472
6036
  ${fivefold.map(
5473
- ([k, v]) => html21`<tr>
6037
+ ([k, v]) => html22`<tr>
5474
6038
  <th>${k}</th>
5475
6039
  <td>${v}</td>
5476
6040
  </tr>`
5477
6041
  )}
5478
- ${detailed?.sunrise ? html21`<tr>
6042
+ ${detailed?.sunrise ? html22`<tr>
5479
6043
  <th>Sunrise</th>
5480
6044
  <td>${formatTime(detailed.sunrise)}</td>
5481
6045
  </tr>` : nothing22}
5482
- ${detailed?.sunset ? html21`<tr>
6046
+ ${detailed?.sunset ? html22`<tr>
5483
6047
  <th>Sunset</th>
5484
6048
  <td>${formatTime(detailed.sunset)}</td>
5485
6049
  </tr>` : nothing22}
5486
- ${detailed?.moonrise ? html21`<tr>
6050
+ ${detailed?.moonrise ? html22`<tr>
5487
6051
  <th>Moonrise</th>
5488
6052
  <td>${formatTime(detailed.moonrise)}</td>
5489
6053
  </tr>` : nothing22}
5490
- ${detailed?.moonset ? html21`<tr>
6054
+ ${detailed?.moonset ? html22`<tr>
5491
6055
  <th>Moonset</th>
5492
6056
  <td>${formatTime(detailed.moonset)}</td>
5493
6057
  </tr>` : nothing22}
5494
6058
  </tbody>
5495
6059
  </table>
5496
- ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? html21`
6060
+ ${transitions ? html22`
6061
+ <div class="section">Next transitions</div>
6062
+ <table>
6063
+ <tbody>
6064
+ ${this.renderTransitionRow("Tithi", transitions.tithi)}
6065
+ ${this.renderTransitionRow("Nakshatra", transitions.nakshatra)}
6066
+ ${this.renderTransitionRow("Yoga", transitions.yoga)}
6067
+ ${this.renderTransitionRow("Karana", transitions.karana)}
6068
+ </tbody>
6069
+ </table>
6070
+ ` : nothing22}
6071
+ ${this.detail === "detailed" && (muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1])) ? html22`
5497
6072
  <div class="section">Auspicious muhurtas</div>
5498
6073
  <table>
5499
6074
  <tbody>
5500
6075
  ${muhurtas.filter(([, v]) => !!v).map(
5501
- ([k, v]) => html21`<tr>
6076
+ ([k, v]) => html22`<tr>
5502
6077
  <th>${k}</th>
5503
6078
  <td>${formatTimeRange(v)}</td>
5504
6079
  </tr>`
@@ -5509,7 +6084,7 @@ var RoxyPanchangTable = class extends LitElement21 {
5509
6084
  <table>
5510
6085
  <tbody>
5511
6086
  ${inauspicious.filter(([, v]) => !!v).map(
5512
- ([k, v]) => html21`<tr>
6087
+ ([k, v]) => html22`<tr>
5513
6088
  <th>${k}</th>
5514
6089
  <td>${formatTimeRange(v)}</td>
5515
6090
  </tr>`
@@ -5519,6 +6094,15 @@ var RoxyPanchangTable = class extends LitElement21 {
5519
6094
  ` : nothing22}
5520
6095
  </div>`;
5521
6096
  }
6097
+ renderTransitionRow(label, t) {
6098
+ if (!t?.endsAt) return nothing22;
6099
+ const when = formatTime(t.endsAt);
6100
+ const next = t.next ? ` \u2192 ${t.next}` : "";
6101
+ return html22`<tr>
6102
+ <th>${label}</th>
6103
+ <td>ends ${when}${next}</td>
6104
+ </tr>`;
6105
+ }
5522
6106
  formatPart(v) {
5523
6107
  if (!v) return "";
5524
6108
  if (typeof v === "string") return v;
@@ -5526,7 +6110,8 @@ var RoxyPanchangTable = class extends LitElement21 {
5526
6110
  const obj = v;
5527
6111
  const parts = [
5528
6112
  obj.name,
5529
- obj.lord ? `(${obj.lord})` : "",
6113
+ obj.paksha ? `(${obj.paksha} paksha)` : "",
6114
+ obj.lord ? `\xB7 ${obj.lord}` : "",
5530
6115
  obj.phase
5531
6116
  ].filter(Boolean);
5532
6117
  return parts.join(" ");
@@ -5536,7 +6121,7 @@ var RoxyPanchangTable = class extends LitElement21 {
5536
6121
  };
5537
6122
  RoxyPanchangTable.styles = [
5538
6123
  baseStyles,
5539
- css22`
6124
+ css23`
5540
6125
  .wrap {
5541
6126
  border: 1px solid var(--roxy-border, #e4e4e7);
5542
6127
  border-radius: var(--roxy-radius-md, 8px);
@@ -5607,7 +6192,7 @@ RoxyPanchangTable = __decorateClass([
5607
6192
  ], RoxyPanchangTable);
5608
6193
 
5609
6194
  // packages/ui/src/components/shadbala-table.ts
5610
- import { css as css23, html as html22, LitElement as LitElement22, nothing as nothing23 } from "lit";
6195
+ import { css as css24, html as html23, LitElement as LitElement22, nothing as nothing23 } from "lit";
5611
6196
  import { customElement as customElement22, property as property22 } from "lit/decorators.js";
5612
6197
  var BALA_COMPONENTS = [
5613
6198
  { key: "sthanaBala", label: "Sthana", color: "var(--roxy-info, #0284c7)" },
@@ -5628,12 +6213,12 @@ var RoxyShadbalaTable = class extends LitElement22 {
5628
6213
  }
5629
6214
  render() {
5630
6215
  if (!this.data?.planets?.length) {
5631
- return html22`<div class="roxy-empty" role="status">No shadbala data</div>`;
6216
+ return html23`<div class="roxy-empty" role="status">No shadbala data</div>`;
5632
6217
  }
5633
6218
  const sorted = [...this.data.planets].sort(
5634
6219
  (a, b) => a.relativeRank - b.relativeRank
5635
6220
  );
5636
- return html22`<div class="wrap" aria-label="Shadbala planetary strength">
6221
+ return html23`<div class="wrap" aria-label="Shadbala planetary strength">
5637
6222
  <div class="head">
5638
6223
  <h2 class="title">Shadbala</h2>
5639
6224
  <p class="subtitle">${sorted.length} planets ranked by strength</p>
@@ -5645,7 +6230,7 @@ var RoxyShadbalaTable = class extends LitElement22 {
5645
6230
 
5646
6231
  <div class="legend" aria-label="Strength component legend">
5647
6232
  ${BALA_COMPONENTS.map(
5648
- (b) => html22`<div class="legend-row">
6233
+ (b) => html23`<div class="legend-row">
5649
6234
  <span
5650
6235
  class="legend-swatch"
5651
6236
  style="background: ${b.color}"
@@ -5665,7 +6250,7 @@ var RoxyShadbalaTable = class extends LitElement22 {
5665
6250
  const badgeClass = isAdequate ? "adequacy-badge--adequate" : "adequacy-badge--weak";
5666
6251
  const badgeLabel = isAdequate ? "adequate" : "weak";
5667
6252
  const rupasStr = formatNumber(p.totalRupas, 2) && formatNumber(p.minRequired, 2) ? `${formatNumber(p.totalRupas, 2)} / ${formatNumber(p.minRequired, 2)} R` : "";
5668
- return html22`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
6253
+ return html23`<div class="planet-row" role="listitem" aria-label="${p.planet} shadbala">
5669
6254
  <div class="planet-label">
5670
6255
  <span class="glyph" aria-hidden="true">${glyph}</span>
5671
6256
  ${p.planet}
@@ -5677,7 +6262,7 @@ var RoxyShadbalaTable = class extends LitElement22 {
5677
6262
  const v = values[i];
5678
6263
  if (v <= 0) return nothing23;
5679
6264
  const grow = v / total * 100;
5680
- return html22`<div
6265
+ return html23`<div
5681
6266
  class="bar-segment"
5682
6267
  style="flex-grow: ${grow}; background: ${b.color};"
5683
6268
  title="${b.label}: ${formatNumber(v, 1)}"
@@ -5686,7 +6271,7 @@ var RoxyShadbalaTable = class extends LitElement22 {
5686
6271
  </div>
5687
6272
  </div>
5688
6273
  <div class="pills">
5689
- ${rupasStr ? html22`<span class="rupas-label">${rupasStr}</span>` : nothing23}
6274
+ ${rupasStr ? html23`<span class="rupas-label">${rupasStr}</span>` : nothing23}
5690
6275
  <span class="${`adequacy-badge ${badgeClass}`}">${badgeLabel}</span>
5691
6276
  </div>
5692
6277
  </div>`;
@@ -5694,7 +6279,7 @@ var RoxyShadbalaTable = class extends LitElement22 {
5694
6279
  };
5695
6280
  RoxyShadbalaTable.styles = [
5696
6281
  baseStyles,
5697
- css23`
6282
+ css24`
5698
6283
  .wrap {
5699
6284
  display: grid;
5700
6285
  gap: var(--roxy-space-md, 1rem);
@@ -5850,7 +6435,7 @@ RoxyShadbalaTable = __decorateClass([
5850
6435
  ], RoxyShadbalaTable);
5851
6436
 
5852
6437
  // packages/ui/src/components/synastry-chart.ts
5853
- import { css as css24, html as html23, LitElement as LitElement23, nothing as nothing24, svg as svg5 } from "lit";
6438
+ import { css as css25, html as html24, LitElement as LitElement23, nothing as nothing24, svg as svg5 } from "lit";
5854
6439
  import { customElement as customElement23, property as property23 } from "lit/decorators.js";
5855
6440
  var SIZE2 = 360;
5856
6441
  var CENTER2 = SIZE2 / 2;
@@ -5865,7 +6450,7 @@ var RoxySynastryChart = class extends LitElement23 {
5865
6450
  }
5866
6451
  render() {
5867
6452
  if (!this.data)
5868
- return html23`<div class="roxy-empty" role="status">No synastry data</div>`;
6453
+ return html24`<div class="roxy-empty" role="status">No synastry data</div>`;
5869
6454
  const { person1, person2, compatibilityScore, analysis } = this.data;
5870
6455
  const interAspects = this.data.interAspects ?? [];
5871
6456
  const p1Planets = person1?.planets ?? [];
@@ -5876,13 +6461,13 @@ var RoxySynastryChart = class extends LitElement23 {
5876
6461
  const challenges = analysis?.challenges ?? [];
5877
6462
  const hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;
5878
6463
  if (!hasPlanets) {
5879
- return html23`<div
6464
+ return html24`<div
5880
6465
  class="wrap"
5881
6466
  aria-label="Synastry compatibility chart"
5882
6467
  >
5883
6468
  <div class="head">
5884
6469
  <h2 class="title">Synastry</h2>
5885
- ${typeof score === "number" ? html23`<span class="score" aria-label=${`Score ${score} of 100`}
6470
+ ${typeof score === "number" ? html24`<span class="score" aria-label=${`Score ${score} of 100`}
5886
6471
  >${score} / 100</span
5887
6472
  >` : nothing24}
5888
6473
  </div>
@@ -5892,31 +6477,31 @@ var RoxySynastryChart = class extends LitElement23 {
5892
6477
  <code>person2.planets</code> arrays from the natal-chart endpoint, or
5893
6478
  use the <code>&lt;roxy-data&gt;</code> fallback.
5894
6479
  </div>
5895
- ${summaryText ? html23`<p class="summary">${summaryText}</p>` : nothing24}
6480
+ ${summaryText ? html24`<p class="summary">${summaryText}</p>` : nothing24}
5896
6481
  ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing24}
5897
- ${strengths.length > 0 || challenges.length > 0 ? html23`<div class="lists">
5898
- ${strengths.length ? html23`<div>
6482
+ ${strengths.length > 0 || challenges.length > 0 ? html24`<div class="lists">
6483
+ ${strengths.length ? html24`<div>
5899
6484
  <h3>Strengths</h3>
5900
6485
  <ul>
5901
- ${strengths.map((s) => html23`<li>${s}</li>`)}
6486
+ ${strengths.map((s) => html24`<li>${s}</li>`)}
5902
6487
  </ul>
5903
6488
  </div>` : nothing24}
5904
- ${challenges.length ? html23`<div>
6489
+ ${challenges.length ? html24`<div>
5905
6490
  <h3>Challenges</h3>
5906
6491
  <ul>
5907
- ${challenges.map((s) => html23`<li>${s}</li>`)}
6492
+ ${challenges.map((s) => html24`<li>${s}</li>`)}
5908
6493
  </ul>
5909
6494
  </div>` : nothing24}
5910
6495
  </div>` : nothing24}
5911
6496
  </div>`;
5912
6497
  }
5913
- return html23`<div
6498
+ return html24`<div
5914
6499
  class="wrap"
5915
6500
  aria-label="Synastry compatibility chart"
5916
6501
  >
5917
6502
  <div class="head">
5918
6503
  <h2 class="title">Synastry</h2>
5919
- ${typeof score === "number" ? html23`<span class="score" aria-label=${`Score ${score} of 100`}
6504
+ ${typeof score === "number" ? html24`<span class="score" aria-label=${`Score ${score} of 100`}
5920
6505
  >${score} / 100</span
5921
6506
  >` : nothing24}
5922
6507
  </div>
@@ -5949,7 +6534,8 @@ var RoxySynastryChart = class extends LitElement23 {
5949
6534
  />
5950
6535
  ${this.renderSpokes()} ${this.renderSigns()}
5951
6536
  ${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)}
5952
- ${this.renderRing(p1Planets, P1_R, "p1")} ${this.renderRing(p2Planets, P2_R, "p2")}
6537
+ ${this.renderRing(p1Planets, P1_R, "p1", 1)} ${this.renderRing(p2Planets, P2_R, "p2", 2)}
6538
+ ${this.renderAscendants(this.data)}
5953
6539
  </svg>
5954
6540
  <div class="legend-row">
5955
6541
  <span><span class="swatch" style="background: var(--roxy-accent)"></span>Person 1</span>
@@ -5957,19 +6543,19 @@ var RoxySynastryChart = class extends LitElement23 {
5957
6543
  <span><span class="swatch" style="background: var(--roxy-success)"></span>harmonious</span>
5958
6544
  <span><span class="swatch" style="background: var(--roxy-danger)"></span>challenging</span>
5959
6545
  </div>
5960
- ${summaryText ? html23`<p class="summary">${summaryText}</p>` : nothing24}
6546
+ ${summaryText ? html24`<p class="summary">${summaryText}</p>` : nothing24}
5961
6547
  ${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing24}
5962
- ${strengths.length > 0 || challenges.length > 0 ? html23`<div class="lists">
5963
- ${strengths.length ? html23`<div>
6548
+ ${strengths.length > 0 || challenges.length > 0 ? html24`<div class="lists">
6549
+ ${strengths.length ? html24`<div>
5964
6550
  <h3>Strengths</h3>
5965
6551
  <ul>
5966
- ${strengths.map((s) => html23`<li>${s}</li>`)}
6552
+ ${strengths.map((s) => html24`<li>${s}</li>`)}
5967
6553
  </ul>
5968
6554
  </div>` : nothing24}
5969
- ${challenges.length ? html23`<div>
6555
+ ${challenges.length ? html24`<div>
5970
6556
  <h3>Challenges</h3>
5971
6557
  <ul>
5972
- ${challenges.map((s) => html23`<li>${s}</li>`)}
6558
+ ${challenges.map((s) => html24`<li>${s}</li>`)}
5973
6559
  </ul>
5974
6560
  </div>` : nothing24}
5975
6561
  </div>` : nothing24}
@@ -5993,19 +6579,56 @@ var RoxySynastryChart = class extends LitElement23 {
5993
6579
  return svg5`<text class="sign" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[s]}</text>`;
5994
6580
  });
5995
6581
  }
5996
- renderRing(planets, radius, cls) {
6582
+ renderRing(planets, radius, cls, personIndex) {
5997
6583
  return planets.map((p) => {
5998
6584
  if (!Number.isFinite(p.longitude)) return nothing24;
5999
- const pos = polarToCartesian(
6585
+ const angle = this.toAngle(p.longitude);
6586
+ const pos = polarToCartesian(CENTER2, CENTER2, radius, angle);
6587
+ const degOffset = personIndex === 1 ? -12 : -10;
6588
+ const degPos = polarToCartesian(
6000
6589
  CENTER2,
6001
6590
  CENTER2,
6002
- radius,
6003
- this.toAngle(p.longitude)
6591
+ radius + degOffset,
6592
+ angle
6004
6593
  );
6005
6594
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
6006
- return svg5`<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}</title>${glyph}</text>`;
6595
+ const sp = longitudeToSignPosition(p.longitude);
6596
+ const retro = p.isRetrograde === true;
6597
+ const degLabel = `${sp.degree}\xB0${String(sp.minute).padStart(2, "0")}'`;
6598
+ const tooltip = `${p.name}${retro ? " retrograde" : ""} - ${degLabel} ${sp.sign}`;
6599
+ return svg5`<g>
6600
+ <text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${tooltip}</title>${glyph}<tspan class="person-tag" dy="-0.55em" dx="0.15em">${personIndex}</tspan></text>
6601
+ <text class="planet-deg" x=${degPos.x} y=${degPos.y} text-anchor="middle" dominant-baseline="central">${sp.degree}°${retro ? svg5`<tspan class="retro"> ℞</tspan>` : nothing24}</text>
6602
+ </g>`;
6007
6603
  });
6008
6604
  }
6605
+ /**
6606
+ * Ascendant markers for both people. Drawn as small spokes at the inner
6607
+ * rim with the label outside, so the two rising signs are immediately
6608
+ * scannable on the wheel without depending on tooltips.
6609
+ */
6610
+ renderAscendants(data) {
6611
+ const items = [];
6612
+ const make = (asc, personIndex) => {
6613
+ if (!asc) return;
6614
+ const signIdx = SIGNS_ORDER.findIndex(
6615
+ (s) => s.toLowerCase() === asc.sign.toLowerCase()
6616
+ );
6617
+ if (signIdx === -1) return;
6618
+ const longitude = signIdx * 30 + asc.degree;
6619
+ const angle = this.toAngle(longitude);
6620
+ const innerR = personIndex === 1 ? P1_R + 14 : P2_R + 14;
6621
+ const tickPos = polarToCartesian(CENTER2, CENTER2, innerR, angle);
6622
+ const labelPos = polarToCartesian(CENTER2, CENTER2, OUTER_R2 + 14, angle);
6623
+ items.push(svg5`<g>
6624
+ <line class="asc-tick" x1=${tickPos.x} y1=${tickPos.y} x2=${labelPos.x} y2=${labelPos.y} />
6625
+ <text class="asc-label" x=${labelPos.x} y=${labelPos.y} text-anchor="middle" dominant-baseline="central">Asc${personIndex}</text>
6626
+ </g>`);
6627
+ };
6628
+ make(data.person1?.ascendant, 1);
6629
+ make(data.person2?.ascendant, 2);
6630
+ return items;
6631
+ }
6009
6632
  renderInterAspectLines(p1, p2, aspects) {
6010
6633
  const longitudeOf = (list, name) => {
6011
6634
  const target = capitalize(name);
@@ -6028,7 +6651,7 @@ var RoxySynastryChart = class extends LitElement23 {
6028
6651
  });
6029
6652
  }
6030
6653
  renderAspects(aspects) {
6031
- return html23`<table>
6654
+ return html24`<table>
6032
6655
  <thead>
6033
6656
  <tr>
6034
6657
  <th>Planet 1</th>
@@ -6040,7 +6663,7 @@ var RoxySynastryChart = class extends LitElement23 {
6040
6663
  </thead>
6041
6664
  <tbody>
6042
6665
  ${aspects.slice(0, 12).map(
6043
- (a) => html23`<tr>
6666
+ (a) => html24`<tr>
6044
6667
  <td>${a.planet1}</td>
6045
6668
  <td>${a.planet2}</td>
6046
6669
  <td>${normalizeAspect(a) || ""}</td>
@@ -6054,7 +6677,7 @@ var RoxySynastryChart = class extends LitElement23 {
6054
6677
  };
6055
6678
  RoxySynastryChart.styles = [
6056
6679
  baseStyles,
6057
- css24`
6680
+ css25`
6058
6681
  .wrap {
6059
6682
  display: grid;
6060
6683
  gap: var(--roxy-space-md, 1rem);
@@ -6084,7 +6707,9 @@ RoxySynastryChart.styles = [
6084
6707
  svg {
6085
6708
  display: block;
6086
6709
  width: 100%;
6087
- max-width: 400px;
6710
+ max-width: 560px;
6711
+ aspect-ratio: 1 / 1;
6712
+ height: auto;
6088
6713
  margin: 0 auto;
6089
6714
  }
6090
6715
 
@@ -6106,6 +6731,31 @@ RoxySynastryChart.styles = [
6106
6731
  font-weight: 600;
6107
6732
  font-size: 13px;
6108
6733
  }
6734
+ .person-tag {
6735
+ font-size: 7px;
6736
+ font-weight: 700;
6737
+ opacity: 0.85;
6738
+ }
6739
+ .planet-deg {
6740
+ fill: var(--roxy-muted, #71717a);
6741
+ font-size: 7px;
6742
+ font-family: var(--roxy-font-sans);
6743
+ }
6744
+ .planet-deg .retro {
6745
+ fill: var(--roxy-danger, #dc2626);
6746
+ }
6747
+ .asc-tick {
6748
+ stroke: var(--roxy-accent-fg, #b45309);
6749
+ stroke-width: 1;
6750
+ opacity: 0.75;
6751
+ }
6752
+ .asc-label {
6753
+ fill: var(--roxy-accent-fg, #b45309);
6754
+ font-size: 9px;
6755
+ font-weight: 700;
6756
+ font-family: var(--roxy-font-sans);
6757
+ letter-spacing: 0.04em;
6758
+ }
6109
6759
  .aspect {
6110
6760
  stroke-width: 0.8;
6111
6761
  fill: none;
@@ -6220,7 +6870,7 @@ function formatStrength(s) {
6220
6870
  }
6221
6871
 
6222
6872
  // packages/ui/src/components/tarot-card.ts
6223
- import { css as css25, html as html24, LitElement as LitElement24, nothing as nothing25 } from "lit";
6873
+ import { css as css26, html as html25, LitElement as LitElement24, nothing as nothing25 } from "lit";
6224
6874
  import { customElement as customElement24, property as property24, state as state6 } from "lit/decorators.js";
6225
6875
  var RoxyTarotCard = class extends LitElement24 {
6226
6876
  constructor() {
@@ -6234,7 +6884,7 @@ var RoxyTarotCard = class extends LitElement24 {
6234
6884
  render() {
6235
6885
  const d = this.data;
6236
6886
  if (!d)
6237
- return html24`<div class="roxy-empty" role="status">No tarot data</div>`;
6887
+ return html25`<div class="roxy-empty" role="status">No tarot data</div>`;
6238
6888
  if ("card" in d) return this.renderDailyCard(d);
6239
6889
  return this.renderFullCard(d);
6240
6890
  }
@@ -6242,9 +6892,9 @@ var RoxyTarotCard = class extends LitElement24 {
6242
6892
  const card = d.card;
6243
6893
  const isReversed = this.flipped !== Boolean(card.reversed);
6244
6894
  const keywords = card.keywords ?? [];
6245
- return html24`<article class="card" aria-label=${card.name ?? "Tarot card"}>
6895
+ return html25`<article class="card" aria-label=${card.name ?? "Tarot card"}>
6246
6896
  <div class="image-wrap">
6247
- ${card.imageUrl ? html24`<img
6897
+ ${card.imageUrl ? html25`<img
6248
6898
  class=${`image ${isReversed ? "reversed" : ""}`}
6249
6899
  src=${card.imageUrl}
6250
6900
  alt=${card.name ?? "Tarot card"}
@@ -6256,7 +6906,7 @@ var RoxyTarotCard = class extends LitElement24 {
6256
6906
  this.toggleFlip();
6257
6907
  }
6258
6908
  }}
6259
- />` : html24`<div
6909
+ />` : html25`<div
6260
6910
  class=${`image ${isReversed ? "reversed" : ""}`}
6261
6911
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
6262
6912
  >
@@ -6265,14 +6915,14 @@ var RoxyTarotCard = class extends LitElement24 {
6265
6915
  </div>
6266
6916
  <div>
6267
6917
  <div class="meta">
6268
- ${card.arcana ? html24`${card.arcana} arcana` : nothing25}
6269
- ${isReversed ? html24` · reversed` : nothing25}
6918
+ ${card.arcana ? html25`${card.arcana} arcana` : nothing25}
6919
+ ${isReversed ? html25` · reversed` : nothing25}
6270
6920
  </div>
6271
6921
  <h2 class="title">${card.name ?? "Tarot card"}</h2>
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>`)}
6922
+ ${d.dailyMessage ? html25`<p class="message">${d.dailyMessage}</p>` : nothing25}
6923
+ ${card.meaning ? html25`<p>${card.meaning}</p>` : nothing25}
6924
+ ${keywords.length > 0 ? html25`<div class="chips">
6925
+ ${keywords.map((k) => html25`<span>${k}</span>`)}
6276
6926
  </div>` : nothing25}
6277
6927
  <button
6278
6928
  class="flip"
@@ -6289,9 +6939,9 @@ var RoxyTarotCard = class extends LitElement24 {
6289
6939
  const isReversed = this.flipped;
6290
6940
  const orientedMeaning = isReversed ? d.reversed : d.upright;
6291
6941
  const keywords = isReversed ? d.keywords?.reversed ?? [] : d.keywords?.upright ?? [];
6292
- return html24`<article class="card" aria-label=${d.name ?? "Tarot card"}>
6942
+ return html25`<article class="card" aria-label=${d.name ?? "Tarot card"}>
6293
6943
  <div class="image-wrap">
6294
- ${d.imageUrl ? html24`<img
6944
+ ${d.imageUrl ? html25`<img
6295
6945
  class=${`image ${isReversed ? "reversed" : ""}`}
6296
6946
  src=${d.imageUrl}
6297
6947
  alt=${d.name ?? "Tarot card"}
@@ -6303,7 +6953,7 @@ var RoxyTarotCard = class extends LitElement24 {
6303
6953
  this.toggleFlip();
6304
6954
  }
6305
6955
  }}
6306
- />` : html24`<div
6956
+ />` : html25`<div
6307
6957
  class=${`image ${isReversed ? "reversed" : ""}`}
6308
6958
  style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
6309
6959
  >
@@ -6312,14 +6962,14 @@ var RoxyTarotCard = class extends LitElement24 {
6312
6962
  </div>
6313
6963
  <div>
6314
6964
  <div class="meta">
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}
6965
+ ${d.arcana ? html25`${d.arcana} arcana` : nothing25}
6966
+ ${d.number !== void 0 && d.number !== null ? html25` · ${d.number}` : nothing25}
6967
+ ${isReversed ? html25` · reversed` : nothing25}
6318
6968
  </div>
6319
6969
  <h2 class="title">${d.name ?? "Tarot card"}</h2>
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>`)}
6970
+ ${orientedMeaning?.description ? html25`<p>${orientedMeaning.description}</p>` : nothing25}
6971
+ ${keywords.length > 0 ? html25`<div class="chips">
6972
+ ${keywords.map((k) => html25`<span>${k}</span>`)}
6323
6973
  </div>` : nothing25}
6324
6974
  <button
6325
6975
  class="flip"
@@ -6335,7 +6985,7 @@ var RoxyTarotCard = class extends LitElement24 {
6335
6985
  };
6336
6986
  RoxyTarotCard.styles = [
6337
6987
  baseStyles,
6338
- css25`
6988
+ css26`
6339
6989
  .card {
6340
6990
  background: var(--roxy-bg, #fff);
6341
6991
  border: 1px solid var(--roxy-border, #e4e4e7);
@@ -6438,7 +7088,7 @@ RoxyTarotCard = __decorateClass([
6438
7088
  ], RoxyTarotCard);
6439
7089
 
6440
7090
  // packages/ui/src/components/tarot-spread.ts
6441
- import { css as css26, html as html25, LitElement as LitElement25, nothing as nothing26 } from "lit";
7091
+ import { css as css27, html as html26, LitElement as LitElement25, nothing as nothing26 } from "lit";
6442
7092
  import { customElement as customElement25, property as property25 } from "lit/decorators.js";
6443
7093
  var RoxyTarotSpread = class extends LitElement25 {
6444
7094
  constructor() {
@@ -6449,7 +7099,7 @@ var RoxyTarotSpread = class extends LitElement25 {
6449
7099
  render() {
6450
7100
  const d = this.data;
6451
7101
  if (!d)
6452
- return html25`<div class="roxy-empty" role="status">No tarot spread</div>`;
7102
+ return html26`<div class="roxy-empty" role="status">No tarot spread</div>`;
6453
7103
  const isYesNo = "answer" in d;
6454
7104
  const isDrawn = "cards" in d && !("spread" in d);
6455
7105
  const positions = isDrawn ? [] : "positions" in d ? d.positions ?? [] : [];
@@ -6461,60 +7111,60 @@ var RoxyTarotSpread = class extends LitElement25 {
6461
7111
  const summary = "summary" in d ? d.summary : void 0;
6462
7112
  const yesNoInterp = isYesNo ? d.interpretation : void 0;
6463
7113
  const answerClass = answer ? answer.toLowerCase().replace(/[^a-z]/g, "") : "";
6464
- return html25`<article class="wrap" aria-label="Tarot spread">
7114
+ return html26`<article class="wrap" aria-label="Tarot spread">
6465
7115
  <header class="head">
6466
7116
  <h2 class="title">${spreadLabel}</h2>
6467
- ${question ? html25`<span class="question">"${question}"</span>` : nothing26}
7117
+ ${question ? html26`<span class="question">"${question}"</span>` : nothing26}
6468
7118
  </header>
6469
- ${isYesNo ? html25`<div>
7119
+ ${isYesNo ? html26`<div>
6470
7120
  <span class=${`answer ${answerClass}`}>${answer}</span>
6471
- ${strength ? html25`<small> · ${strength}</small>` : nothing26}
7121
+ ${strength ? html26`<small> · ${strength}</small>` : nothing26}
6472
7122
  </div>` : nothing26}
6473
- ${positions.length > 0 ? html25`<div class="grid">
7123
+ ${positions.length > 0 ? html26`<div class="grid">
6474
7124
  ${positions.map(
6475
- (p) => html25`<div class="card">
7125
+ (p) => html26`<div class="card">
6476
7126
  <p class="label">${p.name ?? ""}</p>
6477
7127
  <div class="image">
6478
- ${p.card?.imageUrl ? html25`<img
7128
+ ${p.card?.imageUrl ? html26`<img
6479
7129
  src=${p.card.imageUrl}
6480
7130
  alt=${p.card.name ?? "tarot card"}
6481
7131
  class=${p.card.reversed ? "reversed" : ""}
6482
- />` : html25`${p.card?.name ?? "?"}`}
7132
+ />` : html26`${p.card?.name ?? "?"}`}
6483
7133
  </div>
6484
7134
  <p class="name">
6485
7135
  ${p.card?.name ?? ""}
6486
- ${p.card?.reversed ? html25`<small>(reversed)</small>` : nothing26}
7136
+ ${p.card?.reversed ? html26`<small>(reversed)</small>` : nothing26}
6487
7137
  </p>
6488
- ${p.interpretation ? html25`<p class="interp">${p.interpretation}</p>` : nothing26}
7138
+ ${p.interpretation ? html26`<p class="interp">${p.interpretation}</p>` : nothing26}
6489
7139
  </div>`
6490
7140
  )}
6491
7141
  </div>` : nothing26}
6492
- ${cards.length > 0 ? html25`<div class="grid">
7142
+ ${cards.length > 0 ? html26`<div class="grid">
6493
7143
  ${cards.map(
6494
- (c) => html25`<div class="card">
7144
+ (c) => html26`<div class="card">
6495
7145
  <div class="image">
6496
- ${c.imageUrl ? html25`<img
7146
+ ${c.imageUrl ? html26`<img
6497
7147
  src=${c.imageUrl}
6498
7148
  alt=${c.name ?? "tarot card"}
6499
7149
  class=${c.reversed ? "reversed" : ""}
6500
- />` : html25`${c.name ?? "?"}`}
7150
+ />` : html26`${c.name ?? "?"}`}
6501
7151
  </div>
6502
7152
  <p class="name">
6503
7153
  ${c.name ?? ""}
6504
- ${c.reversed ? html25`<small>(reversed)</small>` : nothing26}
7154
+ ${c.reversed ? html26`<small>(reversed)</small>` : nothing26}
6505
7155
  </p>
6506
- ${c.meaning ? html25`<p class="interp">${c.meaning}</p>` : nothing26}
7156
+ ${c.meaning ? html26`<p class="interp">${c.meaning}</p>` : nothing26}
6507
7157
  </div>`
6508
7158
  )}
6509
7159
  </div>` : nothing26}
6510
- ${summary ? html25`<p class="reading">${summary}</p>` : nothing26}
6511
- ${yesNoInterp ? html25`<p class="reading">${yesNoInterp}</p>` : nothing26}
7160
+ ${summary ? html26`<p class="reading">${summary}</p>` : nothing26}
7161
+ ${yesNoInterp ? html26`<p class="reading">${yesNoInterp}</p>` : nothing26}
6512
7162
  </article>`;
6513
7163
  }
6514
7164
  };
6515
7165
  RoxyTarotSpread.styles = [
6516
7166
  baseStyles,
6517
- css26`
7167
+ css27`
6518
7168
  .wrap {
6519
7169
  display: grid;
6520
7170
  gap: var(--roxy-space-md, 1rem);
@@ -6633,7 +7283,7 @@ RoxyTarotSpread = __decorateClass([
6633
7283
  ], RoxyTarotSpread);
6634
7284
 
6635
7285
  // packages/ui/src/components/transits-table.ts
6636
- import { css as css27, html as html26, LitElement as LitElement26, nothing as nothing27 } from "lit";
7286
+ import { css as css28, html as html27, LitElement as LitElement26, nothing as nothing27 } from "lit";
6637
7287
  import { customElement as customElement26, property as property26 } from "lit/decorators.js";
6638
7288
  var RoxyTransitsTable = class extends LitElement26 {
6639
7289
  constructor() {
@@ -6642,7 +7292,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6642
7292
  }
6643
7293
  render() {
6644
7294
  if (!this.data?.transitPlanets?.length) {
6645
- return html26`<div class="roxy-empty" role="status">No transits data</div>`;
7295
+ return html27`<div class="roxy-empty" role="status">No transits data</div>`;
6646
7296
  }
6647
7297
  const {
6648
7298
  transitDate,
@@ -6652,10 +7302,10 @@ var RoxyTransitsTable = class extends LitElement26 {
6652
7302
  summary
6653
7303
  } = this.data;
6654
7304
  const dateStr = [formatDate(transitDate), formatTime(transitTime)].filter(Boolean).join(" ");
6655
- return html26`<div class="wrap" aria-label="Transit positions table">
7305
+ return html27`<div class="wrap" aria-label="Transit positions table">
6656
7306
  <div class="head">
6657
7307
  <h2 class="title">Transits</h2>
6658
- ${dateStr ? html26`<p class="subtitle">${dateStr}</p>` : nothing27}
7308
+ ${dateStr ? html27`<p class="subtitle">${dateStr}</p>` : nothing27}
6659
7309
  </div>
6660
7310
 
6661
7311
  ${summary ? this.renderSummaryPills(summary) : nothing27}
@@ -6667,7 +7317,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6667
7317
  </div>
6668
7318
  </div>
6669
7319
 
6670
- ${transitAspects?.length ? html26`<div>
7320
+ ${transitAspects?.length ? html27`<div>
6671
7321
  <p class="section-label">Transit aspects</p>
6672
7322
  <div class="overflow-scroll">
6673
7323
  ${this.renderAspectsList(transitAspects)}
@@ -6676,7 +7326,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6676
7326
  </div>`;
6677
7327
  }
6678
7328
  renderSummaryPills(summary) {
6679
- return html26`<div class="summary-pills" role="region" aria-label="Aspect summary">
7329
+ return html27`<div class="summary-pills" role="region" aria-label="Aspect summary">
6680
7330
  <span class="pill pill--muted">
6681
7331
  Total: ${summary.totalAspects}
6682
7332
  </span>
@@ -6692,7 +7342,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6692
7342
  </div>`;
6693
7343
  }
6694
7344
  renderPlanetsTable(planets) {
6695
- return html26`<table class="planets-table">
7345
+ return html27`<table class="planets-table">
6696
7346
  <thead>
6697
7347
  <tr>
6698
7348
  <th scope="col">Planet</th>
@@ -6706,12 +7356,12 @@ var RoxyTransitsTable = class extends LitElement26 {
6706
7356
  const pGlyph = PLANET_GLYPH[capitalize(p.name)] ?? "";
6707
7357
  const sGlyph = SIGN_GLYPH[capitalize(p.sign)] ?? "";
6708
7358
  const speedArrow = p.speed >= 0 ? "\u2191" : "\u2193";
6709
- return html26`<tr>
7359
+ return html27`<tr>
6710
7360
  <td>
6711
7361
  <div class="planet-cell">
6712
7362
  <span class="glyph" aria-hidden="true">${pGlyph}</span>
6713
7363
  ${p.name}
6714
- ${p.isRetrograde ? html26`<span class="retro-badge" aria-label="retrograde">R</span>` : nothing27}
7364
+ ${p.isRetrograde ? html27`<span class="retro-badge" aria-label="retrograde">R</span>` : nothing27}
6715
7365
  </div>
6716
7366
  </td>
6717
7367
  <td>
@@ -6731,7 +7381,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6731
7381
  </table>`;
6732
7382
  }
6733
7383
  renderAspectsList(aspects) {
6734
- return html26`<div role="list" aria-label="Transit aspects">
7384
+ return html27`<div role="list" aria-label="Transit aspects">
6735
7385
  ${aspects.map((a, idx) => {
6736
7386
  const tGlyph = PLANET_GLYPH[capitalize(a.transitPlanet)] ?? "";
6737
7387
  const nGlyph = PLANET_GLYPH[capitalize(a.natalPlanet)] ?? "";
@@ -6739,7 +7389,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6739
7389
  const interp = a.interpretation;
6740
7390
  const type = (a.type ?? "").toLowerCase();
6741
7391
  const status = a.isApplying ? "Applying" : "Separating";
6742
- return html26`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
7392
+ return html27`<details class="aspect-card" role="listitem" name="transit-aspects" ?open=${idx === 0}>
6743
7393
  <summary>
6744
7394
  <span aria-hidden="true">${tGlyph}</span>
6745
7395
  ${a.transitPlanet}
@@ -6751,12 +7401,12 @@ var RoxyTransitsTable = class extends LitElement26 {
6751
7401
  </span>
6752
7402
  </summary>
6753
7403
  <div class="interp-body">
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>`)}
7404
+ ${interp?.summary ? html27`<p>${interp.summary}</p>` : nothing27}
7405
+ ${interp?.impact ? html27`<p><strong>Impact:</strong> ${interp.impact}</p>` : nothing27}
7406
+ ${interp?.timing ? html27`<p><strong>Timing:</strong> ${interp.timing}</p>` : nothing27}
7407
+ ${interp?.guidance ? html27`<p><strong>Guidance:</strong> ${interp.guidance}</p>` : nothing27}
7408
+ ${interp?.keywords?.length ? html27`<div class="interp-keywords">
7409
+ ${interp.keywords.map((k) => html27`<span class="kw">${k}</span>`)}
6760
7410
  </div>` : nothing27}
6761
7411
  </div>
6762
7412
  </details>`;
@@ -6766,7 +7416,7 @@ var RoxyTransitsTable = class extends LitElement26 {
6766
7416
  };
6767
7417
  RoxyTransitsTable.styles = [
6768
7418
  baseStyles,
6769
- css27`
7419
+ css28`
6770
7420
  .wrap {
6771
7421
  display: grid;
6772
7422
  gap: var(--roxy-space-md, 1rem);
@@ -6972,93 +7622,43 @@ RoxyTransitsTable = __decorateClass([
6972
7622
  ], RoxyTransitsTable);
6973
7623
 
6974
7624
  // packages/ui/src/components/vedic-kundli.ts
6975
- import { css as css28, html as html27, LitElement as LitElement27 } from "lit";
7625
+ import { html as html28, LitElement as LitElement27 } from "lit";
6976
7626
  import { customElement as customElement27, property as property27 } from "lit/decorators.js";
6977
7627
  var RoxyVedicKundli = class extends LitElement27 {
6978
7628
  constructor() {
6979
7629
  super(...arguments);
6980
7630
  this.data = null;
6981
- this.chartStyle = "south";
7631
+ this.chartStyle = "north";
7632
+ this.setStyle = (next) => {
7633
+ this.chartStyle = next;
7634
+ };
6982
7635
  }
6983
- buildHouses() {
6984
- if (!this.data?.meta) return [];
6985
- return buildHousesFromMeta(this.data.meta);
7636
+ viewModel() {
7637
+ if (!this.data?.meta) return null;
7638
+ return toKundliViewModel(this.data.meta, "D1 Rashi");
6986
7639
  }
6987
7640
  render() {
6988
- if (!this.data)
6989
- return html27`<div class="roxy-empty" role="status">No kundli data</div>`;
6990
- const houses = this.buildHouses();
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">
6995
- <h2 class="title">Vedic kundli</h2>
7641
+ const vm = this.viewModel();
7642
+ if (!vm)
7643
+ return html28`<div class="roxy-empty" role="status">No kundli data</div>`;
7644
+ return html28`<div class="wrap">
7645
+ <div class="header">
7646
+ <h2 class="title">Vedic kundli</h2>
7647
+ ${renderKundliStyleTablist(this.chartStyle, this.setStyle)}
7648
+ </div>
6996
7649
  <svg
6997
- viewBox="0 0 300 300"
7650
+ viewBox="0 0 400 400"
7651
+ preserveAspectRatio="xMidYMid meet"
6998
7652
  role="img"
6999
7653
  aria-label="Vedic birth chart with twelve sign houses"
7000
7654
  >
7001
7655
  <title>Vedic kundli</title>
7002
- ${frame}
7003
- ${houses.map((h) => houseGroup(h))}
7656
+ ${renderKundliSvg(vm, this.chartStyle)}
7004
7657
  </svg>
7005
7658
  </div>`;
7006
7659
  }
7007
7660
  };
7008
- RoxyVedicKundli.styles = [
7009
- baseStyles,
7010
- css28`
7011
- .wrap {
7012
- display: grid;
7013
- gap: var(--roxy-space-md, 1rem);
7014
- }
7015
- .title {
7016
- font-size: var(--roxy-text-lg, 1.125rem);
7017
- font-weight: var(--roxy-weight-bold, 600);
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
- ];
7661
+ RoxyVedicKundli.styles = [baseStyles, kundliStyles];
7062
7662
  __decorateClass([
7063
7663
  property27({ attribute: false })
7064
7664
  ], RoxyVedicKundli.prototype, "data", 2);
@@ -7070,7 +7670,7 @@ RoxyVedicKundli = __decorateClass([
7070
7670
  ], RoxyVedicKundli);
7071
7671
 
7072
7672
  // packages/ui/src/components/vedic-planets-table.ts
7073
- import { css as css29, html as html28, LitElement as LitElement28, nothing as nothing28 } from "lit";
7673
+ import { css as css29, html as html29, LitElement as LitElement28, nothing as nothing28 } from "lit";
7074
7674
  import { customElement as customElement28, property as property28 } from "lit/decorators.js";
7075
7675
  var GRAHA_ORDER = [
7076
7676
  "Lagna",
@@ -7108,9 +7708,9 @@ var RoxyVedicPlanetsTable = class extends LitElement28 {
7108
7708
  }
7109
7709
  render() {
7110
7710
  if (!this.data?.meta)
7111
- return html28`<div class="roxy-empty" role="status">No chart data</div>`;
7711
+ return html29`<div class="roxy-empty" role="status">No chart data</div>`;
7112
7712
  const rows = this.orderedRows();
7113
- return html28`<div class="wrap" aria-label="Vedic planetary positions" tabindex="0">
7713
+ return html29`<div class="wrap" aria-label="Vedic planetary positions" tabindex="0">
7114
7714
  <header class="head">
7115
7715
  <h2 class="title">Planetary positions</h2>
7116
7716
  </header>
@@ -7133,12 +7733,12 @@ var RoxyVedicPlanetsTable = class extends LitElement28 {
7133
7733
  const isLagna = (p.graha ?? name) === "Lagna";
7134
7734
  const glyph = PLANET_GLYPH[capitalize(p.graha ?? name)] ?? "";
7135
7735
  const signGlyph = SIGN_GLYPH[capitalize(p.rashi ?? "")] ?? "";
7136
- return html28`<tr class=${isLagna ? "lagna" : ""}>
7736
+ return html29`<tr class=${isLagna ? "lagna" : ""}>
7137
7737
  <td class="graha">
7138
- ${glyph ? html28`<span class="glyph">${glyph}</span>` : nothing28}${p.graha ?? name}
7738
+ ${glyph ? html29`<span class="glyph">${glyph}</span>` : nothing28}${p.graha ?? name}
7139
7739
  </td>
7140
7740
  <td>
7141
- ${signGlyph ? html28`<span class="glyph">${signGlyph}</span>` : nothing28}${p.rashi ?? ""}
7741
+ ${signGlyph ? html29`<span class="glyph">${signGlyph}</span>` : nothing28}${p.rashi ?? ""}
7142
7742
  </td>
7143
7743
  <td class="num">
7144
7744
  ${typeof p.longitude === "number" ? formatSignPosition(p.longitude) : ""}
@@ -7148,7 +7748,7 @@ var RoxyVedicPlanetsTable = class extends LitElement28 {
7148
7748
  <td>${p.nakshatra?.lord ?? ""}</td>
7149
7749
  <td class="num">${typeof p.house === "number" ? p.house : ""}</td>
7150
7750
  <td>${p.awastha ?? ""}</td>
7151
- <td>${p.isRetrograde ? html28`<span class="retro">R</span>` : nothing28}</td>
7751
+ <td>${p.isRetrograde ? html29`<span class="retro">R</span>` : nothing28}</td>
7152
7752
  </tr>`;
7153
7753
  })}
7154
7754
  </tbody>
@@ -7234,7 +7834,7 @@ RoxyVedicPlanetsTable = __decorateClass([
7234
7834
  ], RoxyVedicPlanetsTable);
7235
7835
 
7236
7836
  // packages/ui/src/components/western-planets-table.ts
7237
- import { css as css30, html as html29, LitElement as LitElement29, nothing as nothing29 } from "lit";
7837
+ import { css as css30, html as html30, LitElement as LitElement29, nothing as nothing29 } from "lit";
7238
7838
  import { customElement as customElement29, property as property29 } from "lit/decorators.js";
7239
7839
  var RoxyWesternPlanetsTable = class extends LitElement29 {
7240
7840
  constructor() {
@@ -7272,9 +7872,9 @@ var RoxyWesternPlanetsTable = class extends LitElement29 {
7272
7872
  }
7273
7873
  render() {
7274
7874
  if (!this.data?.planets)
7275
- return html29`<div class="roxy-empty" role="status">No chart data</div>`;
7875
+ return html30`<div class="roxy-empty" role="status">No chart data</div>`;
7276
7876
  const rows = this.rows();
7277
- return html29`<div class="wrap" aria-label="Western planetary positions" tabindex="0">
7877
+ return html30`<div class="wrap" aria-label="Western planetary positions" tabindex="0">
7278
7878
  <header class="head">
7279
7879
  <h2 class="title">Planetary positions</h2>
7280
7880
  </header>
@@ -7293,20 +7893,20 @@ var RoxyWesternPlanetsTable = class extends LitElement29 {
7293
7893
  const glyph = PLANET_GLYPH[capitalize(r.name)] ?? "";
7294
7894
  const signGlyph = SIGN_GLYPH[capitalize(r.sign ?? "")] ?? "";
7295
7895
  const speed = typeof r.speed === "number" ? formatNumber(r.speed, 3) : "";
7296
- return html29`<tr class=${r.isPoint ? "point" : ""}>
7896
+ return html30`<tr class=${r.isPoint ? "point" : ""}>
7297
7897
  <td class="body">
7298
- ${glyph ? html29`<span class="glyph">${glyph}</span>` : nothing29}${r.name}
7898
+ ${glyph ? html30`<span class="glyph">${glyph}</span>` : nothing29}${r.name}
7299
7899
  </td>
7300
7900
  <td>
7301
- ${signGlyph ? html29`<span class="glyph">${signGlyph}</span>` : nothing29}${r.sign ?? ""}
7901
+ ${signGlyph ? html30`<span class="glyph">${signGlyph}</span>` : nothing29}${r.sign ?? ""}
7302
7902
  </td>
7303
7903
  <td class="num">
7304
7904
  ${typeof r.longitude === "number" ? formatSignPosition(r.longitude) : ""}
7305
7905
  </td>
7306
7906
  <td class="num">${typeof r.house === "number" ? r.house : ""}</td>
7307
7907
  <td class="num">
7308
- ${speed ? html29`${speed}°/day` : nothing29}
7309
- ${r.isRetrograde ? html29`<span class="retro"> ℞</span>` : nothing29}
7908
+ ${speed ? html30`${speed}°/day` : nothing29}
7909
+ ${r.isRetrograde ? html30`<span class="retro"> ℞</span>` : nothing29}
7310
7910
  </td>
7311
7911
  </tr>`;
7312
7912
  })}
@@ -7387,7 +7987,7 @@ RoxyWesternPlanetsTable = __decorateClass([
7387
7987
  ], RoxyWesternPlanetsTable);
7388
7988
 
7389
7989
  // packages/ui/src/components/yoga-list.ts
7390
- import { css as css31, html as html30, LitElement as LitElement30, nothing as nothing30 } from "lit";
7990
+ import { css as css31, html as html31, LitElement as LitElement30, nothing as nothing30 } from "lit";
7391
7991
  import { customElement as customElement30, property as property30, state as state7 } from "lit/decorators.js";
7392
7992
  var RoxyYogaList = class extends LitElement30 {
7393
7993
  constructor() {
@@ -7400,16 +8000,16 @@ var RoxyYogaList = class extends LitElement30 {
7400
8000
  }
7401
8001
  renderQualityChip(quality) {
7402
8002
  const cls = `quality-chip quality-${quality}`;
7403
- return html30`<span class=${cls}>${quality}</span>`;
8003
+ return html31`<span class=${cls}>${quality}</span>`;
7404
8004
  }
7405
8005
  renderDetailCard(yoga) {
7406
- return html30`<div class="detail-card">
8006
+ return html31`<div class="detail-card">
7407
8007
  <p class="detail-name">
7408
8008
  ${yoga.name}
7409
8009
  ${yoga.quality ? this.renderQualityChip(yoga.quality) : nothing30}
7410
8010
  </p>
7411
- ${yoga.description ? html30`<p class="description">${yoga.description}</p>` : nothing30}
7412
- ${yoga.result ? html30`<details>
8011
+ ${yoga.description ? html31`<p class="description">${yoga.description}</p>` : nothing30}
8012
+ ${yoga.result ? html31`<details>
7413
8013
  <summary>Effects</summary>
7414
8014
  <div class="result-body">${yoga.result}</div>
7415
8015
  </details>` : nothing30}
@@ -7417,12 +8017,12 @@ var RoxyYogaList = class extends LitElement30 {
7417
8017
  }
7418
8018
  render() {
7419
8019
  if (!this.data)
7420
- return html30`<div class="roxy-empty" role="status">No yoga data</div>`;
8020
+ return html31`<div class="roxy-empty" role="status">No yoga data</div>`;
7421
8021
  const d = this.data;
7422
8022
  const lc = this.filter.toLowerCase();
7423
8023
  if ("description" in d && typeof d.description === "string") {
7424
8024
  const yoga = d;
7425
- return html30`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
8025
+ return html31`<div class="wrap">${this.renderDetailCard(yoga)}</div>`;
7426
8026
  }
7427
8027
  if ("yogas" in d && Array.isArray(d.yogas)) {
7428
8028
  const allYogas = d.yogas;
@@ -7431,10 +8031,10 @@ var RoxyYogaList = class extends LitElement30 {
7431
8031
  const detailYogas = allYogas;
7432
8032
  const filtered2 = lc ? detailYogas.filter((y) => y.name.toLowerCase().includes(lc)) : detailYogas;
7433
8033
  const total2 = d.total;
7434
- return html30`<div class="wrap">
8034
+ return html31`<div class="wrap">
7435
8035
  <div class="head">
7436
8036
  <h2 class="title">Yoga catalog</h2>
7437
- ${total2 !== void 0 ? html30`<span class="count">${total2} total</span>` : nothing30}
8037
+ ${total2 !== void 0 ? html31`<span class="count">${total2} total</span>` : nothing30}
7438
8038
  </div>
7439
8039
  <div class="search-wrap">
7440
8040
  <input
@@ -7452,17 +8052,17 @@ var RoxyYogaList = class extends LitElement30 {
7452
8052
  aria-live="polite"
7453
8053
  aria-label="Yoga results"
7454
8054
  >
7455
- ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : html30`<p class="no-results">No yogas match your search.</p>`}
8055
+ ${filtered2.length > 0 ? filtered2.map((y) => this.renderDetailCard(y)) : html31`<p class="no-results">No yogas match your search.</p>`}
7456
8056
  </div>
7457
8057
  </div>`;
7458
8058
  }
7459
8059
  const catalogYogas = allYogas;
7460
8060
  const filtered = lc ? catalogYogas.filter((y) => y.name.toLowerCase().includes(lc)) : catalogYogas;
7461
8061
  const total = d.total;
7462
- return html30`<div class="wrap">
8062
+ return html31`<div class="wrap">
7463
8063
  <div class="head">
7464
8064
  <h2 class="title">Yoga catalog</h2>
7465
- ${total !== void 0 ? html30`<span class="count">${total} total</span>` : nothing30}
8065
+ ${total !== void 0 ? html31`<span class="count">${total} total</span>` : nothing30}
7466
8066
  </div>
7467
8067
  <div class="search-wrap">
7468
8068
  <input
@@ -7481,15 +8081,15 @@ var RoxyYogaList = class extends LitElement30 {
7481
8081
  aria-label="Yoga results"
7482
8082
  >
7483
8083
  ${filtered.length > 0 ? filtered.map(
7484
- (y) => html30`<div class="yoga-chip">
8084
+ (y) => html31`<div class="yoga-chip">
7485
8085
  ${y.name}
7486
8086
  <span class="yoga-id">${y.id}</span>
7487
8087
  </div>`
7488
- ) : html30`<p class="no-results">No yogas match your search.</p>`}
8088
+ ) : html31`<p class="no-results">No yogas match your search.</p>`}
7489
8089
  </div>
7490
8090
  </div>`;
7491
8091
  }
7492
- return html30`<div class="roxy-empty" role="status">No yoga data</div>`;
8092
+ return html31`<div class="roxy-empty" role="status">No yoga data</div>`;
7493
8093
  }
7494
8094
  };
7495
8095
  RoxyYogaList.styles = [
@@ -7992,7 +8592,7 @@ var ROXY_COMPONENTS = [
7992
8592
  ];
7993
8593
 
7994
8594
  // packages/ui/src/version.ts
7995
- var ROXY_UI_VERSION = "0.3.1";
8595
+ var ROXY_UI_VERSION = "0.4.0";
7996
8596
 
7997
8597
  // packages/ui/src/index.ts
7998
8598
  var ROXY_UI_COMPONENTS = ROXY_COMPONENTS.map((c) => c.slug);