@roxyapi/ui 0.9.0 → 0.10.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 (176) hide show
  1. package/AGENTS.md +24 -6
  2. package/README.md +22 -9
  3. package/dist/cdn/components/angel-number-card.js +45 -0
  4. package/dist/cdn/components/angel-number-card.js.map +7 -0
  5. package/dist/cdn/components/angel-number-lookup.js +45 -0
  6. package/dist/cdn/components/angel-number-lookup.js.map +7 -0
  7. package/dist/cdn/components/ashtakavarga-grid.js +3 -3
  8. package/dist/cdn/components/ashtakavarga-grid.js.map +3 -3
  9. package/dist/cdn/components/biorhythm-chart.js +3 -3
  10. package/dist/cdn/components/biorhythm-chart.js.map +3 -3
  11. package/dist/cdn/components/bodygraph.js +8 -8
  12. package/dist/cdn/components/bodygraph.js.map +3 -3
  13. package/dist/cdn/components/choghadiya-grid.js +3 -3
  14. package/dist/cdn/components/choghadiya-grid.js.map +3 -3
  15. package/dist/cdn/components/compatibility-card.js +2 -2
  16. package/dist/cdn/components/compatibility-card.js.map +3 -3
  17. package/dist/cdn/components/crystal-grid.js +45 -0
  18. package/dist/cdn/components/crystal-grid.js.map +7 -0
  19. package/dist/cdn/components/dasha-timeline.js +2 -2
  20. package/dist/cdn/components/dasha-timeline.js.map +3 -3
  21. package/dist/cdn/components/data.js +2 -2
  22. package/dist/cdn/components/data.js.map +3 -3
  23. package/dist/cdn/components/divisional-chart.js +7 -7
  24. package/dist/cdn/components/divisional-chart.js.map +3 -3
  25. package/dist/cdn/components/dosha-card.js +3 -3
  26. package/dist/cdn/components/dosha-card.js.map +3 -3
  27. package/dist/cdn/components/dream-card.js +45 -0
  28. package/dist/cdn/components/dream-card.js.map +7 -0
  29. package/dist/cdn/components/forecast-timeline.js +3 -3
  30. package/dist/cdn/components/forecast-timeline.js.map +3 -3
  31. package/dist/cdn/components/guna-milan.js +3 -3
  32. package/dist/cdn/components/guna-milan.js.map +3 -3
  33. package/dist/cdn/components/hexagram.js +2 -2
  34. package/dist/cdn/components/hexagram.js.map +3 -3
  35. package/dist/cdn/components/horoscope-card.js +3 -3
  36. package/dist/cdn/components/horoscope-card.js.map +3 -3
  37. package/dist/cdn/components/kp-chart.js +2 -2
  38. package/dist/cdn/components/kp-chart.js.map +3 -3
  39. package/dist/cdn/components/kp-planets-table.js +3 -3
  40. package/dist/cdn/components/kp-planets-table.js.map +3 -3
  41. package/dist/cdn/components/kp-ruling-planets.js +3 -3
  42. package/dist/cdn/components/kp-ruling-planets.js.map +3 -3
  43. package/dist/cdn/components/moon-phase.js +3 -3
  44. package/dist/cdn/components/moon-phase.js.map +3 -3
  45. package/dist/cdn/components/nakshatra-card.js +3 -3
  46. package/dist/cdn/components/nakshatra-card.js.map +3 -3
  47. package/dist/cdn/components/natal-chart.js +2 -2
  48. package/dist/cdn/components/natal-chart.js.map +3 -3
  49. package/dist/cdn/components/numerology-card.js +3 -3
  50. package/dist/cdn/components/numerology-card.js.map +3 -3
  51. package/dist/cdn/components/panchang-table.js +2 -2
  52. package/dist/cdn/components/panchang-table.js.map +3 -3
  53. package/dist/cdn/components/shadbala-table.js +3 -3
  54. package/dist/cdn/components/shadbala-table.js.map +3 -3
  55. package/dist/cdn/components/synastry-chart.js +5 -5
  56. package/dist/cdn/components/synastry-chart.js.map +3 -3
  57. package/dist/cdn/components/tarot-card.js.map +3 -3
  58. package/dist/cdn/components/tarot-spread.js +3 -3
  59. package/dist/cdn/components/tarot-spread.js.map +3 -3
  60. package/dist/cdn/components/transits-table.js +3 -3
  61. package/dist/cdn/components/transits-table.js.map +3 -3
  62. package/dist/cdn/components/vedic-kundli.js +16 -16
  63. package/dist/cdn/components/vedic-kundli.js.map +3 -3
  64. package/dist/cdn/components/vedic-planets-table.js +3 -3
  65. package/dist/cdn/components/vedic-planets-table.js.map +3 -3
  66. package/dist/cdn/components/western-planets-table.js +2 -2
  67. package/dist/cdn/components/western-planets-table.js.map +3 -3
  68. package/dist/cdn/components/yoga-list.js +2 -2
  69. package/dist/cdn/components/yoga-list.js.map +3 -3
  70. package/dist/cdn/roxy-ui.js +67 -67
  71. package/dist/cdn/roxy-ui.js.map +4 -4
  72. package/dist/components/angel-number-card.d.ts +18 -0
  73. package/dist/components/angel-number-card.d.ts.map +1 -0
  74. package/dist/components/angel-number-card.js +2 -0
  75. package/dist/components/angel-number-card.js.map +7 -0
  76. package/dist/components/angel-number-lookup.d.ts +18 -0
  77. package/dist/components/angel-number-lookup.d.ts.map +1 -0
  78. package/dist/components/angel-number-lookup.js +2 -0
  79. package/dist/components/angel-number-lookup.js.map +7 -0
  80. package/dist/components/ashtakavarga-grid.js +1 -1
  81. package/dist/components/ashtakavarga-grid.js.map +3 -3
  82. package/dist/components/biorhythm-chart.js +1 -1
  83. package/dist/components/biorhythm-chart.js.map +3 -3
  84. package/dist/components/bodygraph.js +4 -4
  85. package/dist/components/bodygraph.js.map +3 -3
  86. package/dist/components/choghadiya-grid.js +1 -1
  87. package/dist/components/choghadiya-grid.js.map +3 -3
  88. package/dist/components/compatibility-card.js +1 -1
  89. package/dist/components/compatibility-card.js.map +3 -3
  90. package/dist/components/crystal-grid.d.ts +27 -0
  91. package/dist/components/crystal-grid.d.ts.map +1 -0
  92. package/dist/components/crystal-grid.js +2 -0
  93. package/dist/components/crystal-grid.js.map +7 -0
  94. package/dist/components/dasha-timeline.js +1 -1
  95. package/dist/components/dasha-timeline.js.map +3 -3
  96. package/dist/components/data.js +1 -1
  97. package/dist/components/data.js.map +3 -3
  98. package/dist/components/divisional-chart.js +34 -34
  99. package/dist/components/divisional-chart.js.map +3 -3
  100. package/dist/components/dosha-card.js +1 -1
  101. package/dist/components/dosha-card.js.map +3 -3
  102. package/dist/components/dream-card.d.ts +17 -0
  103. package/dist/components/dream-card.d.ts.map +1 -0
  104. package/dist/components/dream-card.js +2 -0
  105. package/dist/components/dream-card.js.map +7 -0
  106. package/dist/components/forecast-timeline.js.map +3 -3
  107. package/dist/components/guna-milan.js +1 -1
  108. package/dist/components/guna-milan.js.map +3 -3
  109. package/dist/components/hexagram.js +1 -1
  110. package/dist/components/hexagram.js.map +3 -3
  111. package/dist/components/horoscope-card.js +1 -1
  112. package/dist/components/horoscope-card.js.map +3 -3
  113. package/dist/components/kp-chart.js +1 -1
  114. package/dist/components/kp-chart.js.map +3 -3
  115. package/dist/components/kp-planets-table.js +1 -1
  116. package/dist/components/kp-planets-table.js.map +3 -3
  117. package/dist/components/kp-ruling-planets.js +1 -1
  118. package/dist/components/kp-ruling-planets.js.map +3 -3
  119. package/dist/components/moon-phase.js +1 -1
  120. package/dist/components/moon-phase.js.map +3 -3
  121. package/dist/components/nakshatra-card.js +1 -1
  122. package/dist/components/nakshatra-card.js.map +3 -3
  123. package/dist/components/natal-chart.js +5 -5
  124. package/dist/components/natal-chart.js.map +3 -3
  125. package/dist/components/numerology-card.d.ts +7 -3
  126. package/dist/components/numerology-card.d.ts.map +1 -1
  127. package/dist/components/numerology-card.js +1 -1
  128. package/dist/components/numerology-card.js.map +3 -3
  129. package/dist/components/panchang-table.js +1 -1
  130. package/dist/components/panchang-table.js.map +3 -3
  131. package/dist/components/shadbala-table.js +1 -1
  132. package/dist/components/shadbala-table.js.map +3 -3
  133. package/dist/components/synastry-chart.js +4 -4
  134. package/dist/components/synastry-chart.js.map +3 -3
  135. package/dist/components/tarot-card.js +1 -1
  136. package/dist/components/tarot-card.js.map +3 -3
  137. package/dist/components/tarot-spread.js +1 -1
  138. package/dist/components/tarot-spread.js.map +3 -3
  139. package/dist/components/transits-table.js +1 -1
  140. package/dist/components/transits-table.js.map +3 -3
  141. package/dist/components/vedic-kundli.d.ts +18 -0
  142. package/dist/components/vedic-kundli.d.ts.map +1 -1
  143. package/dist/components/vedic-kundli.js +62 -62
  144. package/dist/components/vedic-kundli.js.map +3 -3
  145. package/dist/components/vedic-planets-table.js +1 -1
  146. package/dist/components/vedic-planets-table.js.map +3 -3
  147. package/dist/components/western-planets-table.js +1 -1
  148. package/dist/components/western-planets-table.js.map +3 -3
  149. package/dist/components/yoga-list.js +1 -1
  150. package/dist/components/yoga-list.js.map +3 -3
  151. package/dist/index.cjs +74 -74
  152. package/dist/index.cjs.map +4 -4
  153. package/dist/index.d.ts +5 -0
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/index.js +74 -74
  156. package/dist/index.js.map +4 -4
  157. package/dist/manifest.d.ts.map +1 -1
  158. package/dist/manifest.json +4 -0
  159. package/dist/utils/kundli-render.d.ts +2 -1
  160. package/dist/utils/kundli-render.d.ts.map +1 -1
  161. package/dist/utils/markup-data.d.ts +34 -0
  162. package/dist/utils/markup-data.d.ts.map +1 -1
  163. package/dist/version.d.ts +1 -1
  164. package/dist/version.d.ts.map +1 -1
  165. package/package.json +1 -1
  166. package/src/components/angel-number-card.ts +234 -0
  167. package/src/components/angel-number-lookup.ts +208 -0
  168. package/src/components/crystal-grid.ts +191 -0
  169. package/src/components/dream-card.ts +98 -0
  170. package/src/components/numerology-card.ts +22 -10
  171. package/src/components/vedic-kundli.ts +37 -2
  172. package/src/index.ts +14 -0
  173. package/src/manifest.ts +57 -5
  174. package/src/utils/kundli-render.ts +9 -2
  175. package/src/utils/markup-data.ts +45 -0
  176. package/src/version.ts +1 -1
@@ -1,11 +1,11 @@
1
- var L=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var v=(t,e,r,a)=>{for(var n=a>1?void 0:a?B(e,r):e,o=t.length-1,s;o>=0;o--)(s=t[o])&&(n=(a?s(e,r,n):s(n))||n);return a&&n&&L(e,r,n),n};import{css as re,html as c,LitElement as te,nothing as p}from"lit";import{customElement as ne,property as ae}from"lit/decorators.js";var R={Sun:"\u2609",Moon:"\u263D",Mercury:"\u263F",Venus:"\u2640",Earth:"\u2641",Mars:"\u2642",Jupiter:"\u2643",Saturn:"\u2644",Uranus:"\u2645",Neptune:"\u2646",Pluto:"\u2647",Rahu:"\u260A",Ketu:"\u260B",Ascendant:"Asc",Lagna:"La",NorthNode:"\u260A",SouthNode:"\u260B","North node":"\u260A","South node":"\u260B",Chiron:"\u26B7",Lilith:"\u26B8","Black moon lilith":"\u26B8"};var _=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],se=_.map(t=>t.toLowerCase());import{css as O}from"lit";var C=O`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;import{nothing as M,svg as d}from"lit";var x=4,f=9,T=(100+2*f)*x,H=(100+2*f)*x;function l(t,e){return{x:(t+f)*x,y:(e+f)*x}}var m=50,w=t=>2*m-t,$={head:{64:[45.5,11.2],61:[50,11.2],63:[54.5,11.2]},ajna:{47:[45.3,18],24:[50,18],4:[54.7,18],17:[45.6,20.6],11:[54.4,20.6],43:[50,25.3]},throat:{62:[45.5,32.3],23:[50,32.3],56:[54.5,32.3],16:[42,34.6],35:[58,34.6],12:[58,37.6],20:[42,40.6],45:[58,40.6],31:[46,42.4],8:[50,42.4],33:[54,42.4]},g:{1:[50,47.5],7:[45.6,50.3],13:[54.4,50.3],10:[40,53.3],25:[60,53.3],15:[45.6,56.6],46:[54.4,56.6],2:[50,59]},heart:{21:[62.5,58.5],51:[62.5,61.3],26:[62.5,64.1],40:[73,61.3]},spleen:{48:[20,70.6],57:[24,72.3],44:[28,74],50:[32,75.6],32:[28,77.2],28:[24,78.9],18:[20,80.6]},sacral:{5:[45.5,72.5],14:[50,72.5],29:[54.5,72.5],34:[42,75.6],27:[42,79],59:[58,79],42:[45.5,81.4],3:[50,81.4],9:[54.5,81.4]},"solar-plexus":{},root:{53:[45.5,89.9],60:[50,89.9],52:[54.5,89.9],54:[42,92.7],19:[58,92.7],38:[42,95.3],39:[58,95.3],58:[42,98],41:[58,98]}},j={48:36,57:22,44:37,50:6,32:49,28:55,18:30};function z(){let t={},e={};for(let[r,[a,n]]of Object.entries($.spleen))$["solar-plexus"][j[Number(r)]]=[w(a),n];for(let[r,a]of Object.entries($))for(let[n,[o,s]]of Object.entries(a))t[Number(n)]=l(o,s),e[Number(n)]=r;return{points:t,centerOf:e}}var{points:S,centerOf:pe}=z(),ue=T/2;function u(t){return t.map(([e,r])=>l(e,r))}function A(t,e,r){return u([[m-t,e],[m+t,e],[m+t,r],[m-t,r]])}var P=[[18.4,68],[18.4,81.8],[34.7,74.9]],Y=[{id:"head",label:"Head",color:"gold",points:u([[40,14.3],[60,14.3],[50,6]]),labelAnchor:l(63,9),labelAlign:"start"},{id:"ajna",label:"Ajna",color:"green",points:u([[40,15.6],[60,15.6],[50,27.6]]),labelAnchor:l(62,21),labelAlign:"start"},{id:"throat",label:"Throat",color:"brown",points:A(9.5,30.4,43.6),labelAnchor:l(83,34),labelAlign:"start"},{id:"g",label:"G",color:"gold",points:u([[50,45],[60.7,53.3],[50,61.6],[39.3,53.3]]),labelAnchor:l(13,51),labelAlign:"end"},{id:"heart",label:"Heart",color:"red",points:u([[61.5,57],[76.5,61.3],[61.5,65.6]]),labelAnchor:l(85,56),labelAlign:"start"},{id:"spleen",label:"Spleen",color:"brown",points:u(P),labelAnchor:l(13,70),labelAlign:"end"},{id:"sacral",label:"Sacral",color:"red",points:A(9.5,70.6,83.6),labelAnchor:l(85,88),labelAlign:"start"},{id:"solar-plexus",label:"Solar Plexus",color:"brown",points:u(P.map(([t,e])=>[w(t),e])),labelAnchor:l(87,73),labelAlign:"start"},{id:"root",label:"Root",color:"brown",points:A(9.5,87.9,99.9),labelAnchor:l(50,103),labelAlign:"middle"}],D=[[64,47],[61,24],[63,4],[17,62],[11,56],[43,23],[16,48],[20,34],[20,10],[7,31],[1,8],[13,33],[21,45],[12,22],[35,36],[57,20],[15,5],[2,14],[46,29],[34,10],[10,57],[25,51],[27,50],[57,34],[26,44],[18,58],[28,38],[32,54],[3,60],[9,52],[42,53],[59,6],[19,49],[39,55],[41,30],[37,40]],V=q();function q(){let t=w,e=[[50,-2],[60,-2],[60.5,9],[57,18],[56,21],[54,24],[52,27],[54,28],[64,30],[80,34],[83.5,40],[84,56],[83,74],[82,84],[76,92],[68,97],[64,99],[62,100],[60,100]],r=[`M ${b(e[0])}`];for(let a=1;a+2<e.length;a+=3)r.push(`C ${b(e[a])} ${b(e[a+1])} ${b(e[a+2])}`);r.push(`L ${y(e[e.length-1],t)}`);for(let a=e.length-3;a>=1;a-=3)r.push(`C ${y(e[a+1],t)} ${y(e[a],t)} ${y(e[a-1],t)}`);return r.push("Z"),r.join(" ")}function b([t,e]){let r=l(t,e);return`${r.x} ${r.y}`}function y([t,e],r){let a=l(r(t),e);return`${a.x} ${a.y}`}function U(t){return t.map(e=>`${e.x},${e.y}`).join(" ")}function k(t,e){return t<e?`${t}-${e}`:`${e}-${t}`}function X(){return d`<path class="bg-body" d=${V} />`}function K(t,e){let r=[];for(let[a,n]of D){let o=S[a],s=S[n];if(!o||!s)continue;if(r.push(d`<line class="bg-channel" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`),t.has(k(a,n))){r.push(d`<line class="bg-channel on" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`);continue}let i={x:(o.x+s.x)/2,y:(o.y+s.y)/2};e.has(a)&&r.push(d`<line class="bg-half" x1=${o.x} y1=${o.y} x2=${i.x} y2=${i.y} />`),e.has(n)&&r.push(d`<line class="bg-half" x1=${s.x} y1=${s.y} x2=${i.x} y2=${i.y} />`)}return r}function J(t,e,r){let a=r.x-e.x,n=r.y-e.y,o=a*a+n*n;if(o===0)return e;let s=Math.max(0,Math.min(1,((t.x-e.x)*a+(t.y-e.y)*n)/o));return{x:e.x+s*a,y:e.y+s*n}}function W(t,e){let r=e[0],a=Number.POSITIVE_INFINITY;for(let n=0;n<e.length;n++){let o=J(t,e[n],e[(n+1)%e.length]),s=(o.x-t.x)**2+(o.y-t.y)**2;s<a&&(a=s,r=o)}return r}function F(t){return Y.map(e=>{let r=t.has(e.id),a=`bg-center bg-${e.color}${r?" defined":""}`,n=W(e.labelAnchor,e.points);return d`<g>
1
+ var L=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var v=(t,e,r,a)=>{for(var n=a>1?void 0:a?B(e,r):e,o=t.length-1,s;o>=0;o--)(s=t[o])&&(n=(a?s(e,r,n):s(n))||n);return a&&n&&L(e,r,n),n};import{css as re,html as c,LitElement as te,nothing as p}from"lit";import{customElement as ne,property as ae}from"lit/decorators.js";var R={Sun:"\u2609",Moon:"\u263D",Mercury:"\u263F",Venus:"\u2640",Earth:"\u2641",Mars:"\u2642",Jupiter:"\u2643",Saturn:"\u2644",Uranus:"\u2645",Neptune:"\u2646",Pluto:"\u2647",Rahu:"\u260A",Ketu:"\u260B",Ascendant:"Asc",Lagna:"La",NorthNode:"\u260A",SouthNode:"\u260B","North node":"\u260A","South node":"\u260B",Chiron:"\u26B7",Lilith:"\u26B8","Black moon lilith":"\u26B8"};var _=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],se=_.map(t=>t.toLowerCase());import{css as O}from"lit";var C=O`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;import{nothing as M,svg as d}from"lit";var x=4,f=9,T=(100+2*f)*x,H=(100+2*f)*x;function l(t,e){return{x:(t+f)*x,y:(e+f)*x}}var m=50,w=t=>2*m-t,$={head:{64:[45.5,11.2],61:[50,11.2],63:[54.5,11.2]},ajna:{47:[45.3,18],24:[50,18],4:[54.7,18],17:[45.6,20.6],11:[54.4,20.6],43:[50,25.3]},throat:{62:[45.5,32.3],23:[50,32.3],56:[54.5,32.3],16:[42,34.6],35:[58,34.6],12:[58,37.6],20:[42,40.6],45:[58,40.6],31:[46,42.4],8:[50,42.4],33:[54,42.4]},g:{1:[50,47.5],7:[45.6,50.3],13:[54.4,50.3],10:[40,53.3],25:[60,53.3],15:[45.6,56.6],46:[54.4,56.6],2:[50,59]},heart:{21:[62.5,58.5],51:[62.5,61.3],26:[62.5,64.1],40:[73,61.3]},spleen:{48:[20,70.6],57:[24,72.3],44:[28,74],50:[32,75.6],32:[28,77.2],28:[24,78.9],18:[20,80.6]},sacral:{5:[45.5,72.5],14:[50,72.5],29:[54.5,72.5],34:[42,75.6],27:[42,79],59:[58,79],42:[45.5,81.4],3:[50,81.4],9:[54.5,81.4]},"solar-plexus":{},root:{53:[45.5,89.9],60:[50,89.9],52:[54.5,89.9],54:[42,92.7],19:[58,92.7],38:[42,95.3],39:[58,95.3],58:[42,98],41:[58,98]}},j={48:36,57:22,44:37,50:6,32:49,28:55,18:30};function z(){let t={},e={};for(let[r,[a,n]]of Object.entries($.spleen))$["solar-plexus"][j[Number(r)]]=[w(a),n];for(let[r,a]of Object.entries($))for(let[n,[o,s]]of Object.entries(a))t[Number(n)]=l(o,s),e[Number(n)]=r;return{points:t,centerOf:e}}var{points:S,centerOf:pe}=z(),ue=T/2;function u(t){return t.map(([e,r])=>l(e,r))}function A(t,e,r){return u([[m-t,e],[m+t,e],[m+t,r],[m-t,r]])}var P=[[18.4,68],[18.4,81.8],[34.7,74.9]],D=[{id:"head",label:"Head",color:"gold",points:u([[40,14.3],[60,14.3],[50,6]]),labelAnchor:l(63,9),labelAlign:"start"},{id:"ajna",label:"Ajna",color:"green",points:u([[40,15.6],[60,15.6],[50,27.6]]),labelAnchor:l(62,21),labelAlign:"start"},{id:"throat",label:"Throat",color:"brown",points:A(9.5,30.4,43.6),labelAnchor:l(83,34),labelAlign:"start"},{id:"g",label:"G",color:"gold",points:u([[50,45],[60.7,53.3],[50,61.6],[39.3,53.3]]),labelAnchor:l(13,51),labelAlign:"end"},{id:"heart",label:"Heart",color:"red",points:u([[61.5,57],[76.5,61.3],[61.5,65.6]]),labelAnchor:l(85,56),labelAlign:"start"},{id:"spleen",label:"Spleen",color:"brown",points:u(P),labelAnchor:l(13,70),labelAlign:"end"},{id:"sacral",label:"Sacral",color:"red",points:A(9.5,70.6,83.6),labelAnchor:l(85,88),labelAlign:"start"},{id:"solar-plexus",label:"Solar Plexus",color:"brown",points:u(P.map(([t,e])=>[w(t),e])),labelAnchor:l(87,73),labelAlign:"start"},{id:"root",label:"Root",color:"brown",points:A(9.5,87.9,99.9),labelAnchor:l(50,103),labelAlign:"middle"}],Y=[[64,47],[61,24],[63,4],[17,62],[11,56],[43,23],[16,48],[20,34],[20,10],[7,31],[1,8],[13,33],[21,45],[12,22],[35,36],[57,20],[15,5],[2,14],[46,29],[34,10],[10,57],[25,51],[27,50],[57,34],[26,44],[18,58],[28,38],[32,54],[3,60],[9,52],[42,53],[59,6],[19,49],[39,55],[41,30],[37,40]],V=q();function q(){let t=w,e=[[50,-2],[60,-2],[60.5,9],[57,18],[56,21],[54,24],[52,27],[54,28],[64,30],[80,34],[83.5,40],[84,56],[83,74],[82,84],[76,92],[68,97],[64,99],[62,100],[60,100]],r=[`M ${b(e[0])}`];for(let a=1;a+2<e.length;a+=3)r.push(`C ${b(e[a])} ${b(e[a+1])} ${b(e[a+2])}`);r.push(`L ${y(e[e.length-1],t)}`);for(let a=e.length-3;a>=1;a-=3)r.push(`C ${y(e[a+1],t)} ${y(e[a],t)} ${y(e[a-1],t)}`);return r.push("Z"),r.join(" ")}function b([t,e]){let r=l(t,e);return`${r.x} ${r.y}`}function y([t,e],r){let a=l(r(t),e);return`${a.x} ${a.y}`}function U(t){return t.map(e=>`${e.x},${e.y}`).join(" ")}function k(t,e){return t<e?`${t}-${e}`:`${e}-${t}`}function X(){return d`<path class="bg-body" d=${V} />`}function J(t,e){let r=[];for(let[a,n]of Y){let o=S[a],s=S[n];if(!o||!s)continue;if(r.push(d`<line class="bg-channel" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`),t.has(k(a,n))){r.push(d`<line class="bg-channel on" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`);continue}let i={x:(o.x+s.x)/2,y:(o.y+s.y)/2};e.has(a)&&r.push(d`<line class="bg-half" x1=${o.x} y1=${o.y} x2=${i.x} y2=${i.y} />`),e.has(n)&&r.push(d`<line class="bg-half" x1=${s.x} y1=${s.y} x2=${i.x} y2=${i.y} />`)}return r}function K(t,e,r){let a=r.x-e.x,n=r.y-e.y,o=a*a+n*n;if(o===0)return e;let s=Math.max(0,Math.min(1,((t.x-e.x)*a+(t.y-e.y)*n)/o));return{x:e.x+s*a,y:e.y+s*n}}function W(t,e){let r=e[0],a=Number.POSITIVE_INFINITY;for(let n=0;n<e.length;n++){let o=K(t,e[n],e[(n+1)%e.length]),s=(o.x-t.x)**2+(o.y-t.y)**2;s<a&&(a=s,r=o)}return r}function F(t){return D.map(e=>{let r=t.has(e.id),a=`bg-center bg-${e.color}${r?" defined":""}`,n=W(e.labelAnchor,e.points);return d`<g>
2
2
  <line class="bg-leader" x1=${e.labelAnchor.x} y1=${e.labelAnchor.y} x2=${n.x} y2=${n.y} />
3
3
  <polygon class=${a} points=${U(e.points)}><title>${e.label}: ${r?"defined":"open"}</title></polygon>
4
4
  <text class="bg-center-label" x=${e.labelAnchor.x} y=${e.labelAnchor.y} text-anchor=${e.labelAlign} dominant-baseline="central">${e.label}</text>
5
- </g>`})}function Z(t,e){let r=[];for(let[a,n]of Object.entries(S)){let o=Number(a);if(!t.has(o))continue;let s=e.get(o);r.push(d`<text class="bg-gate" x=${n.x} y=${n.y} text-anchor="middle" dominant-baseline="central">${o}${s?d`<title>${s}</title>`:M}</text>`)}return r}function E(t,e){return k(t,e)}var N=`0 0 ${T} ${H}`;function G(t){return d`
5
+ </g>`})}function Z(t,e){let r=[];for(let[a,n]of Object.entries(S)){let o=Number(a);if(!t.has(o))continue;let s=e.get(o);r.push(d`<text class="bg-gate" x=${n.x} y=${n.y} text-anchor="middle" dominant-baseline="central">${o}${s?d`<title>${s}</title>`:M}</text>`)}return r}function N(t,e){return k(t,e)}var E=`0 0 ${T} ${H}`;function G(t){return d`
6
6
  ${X()}
7
- ${K(t.activeChannels,t.activeGates)}
7
+ ${J(t.activeChannels,t.activeGates)}
8
8
  ${F(t.definedCenters)}
9
9
  ${Z(t.activeGates,t.gateTitles)}
10
- `}var Q="roxy-data";function ee(t){return t.nodeName==="SCRIPT"&&t.getAttribute("type")==="application/json"}var h=class{constructor(e){this.host=e,e.addController(this)}hostConnected(){if(this.host.data!=null)return;let e=this.read();e!==void 0&&(this.host.data=e,this.host.requestUpdate())}read(){let e=this.findInlineScript();return e?this.parse(e.textContent):void 0}findInlineScript(){for(let e of Array.from(this.host.children))if(ee(e)&&e.classList.contains(Q))return e;return null}parse(e){if(e?.trim())try{return JSON.parse(e)}catch{return}}};function I(t){return t?t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():""}var g=class extends te{constructor(){super();this.data=null;new h(this)}render(){let r=this.data;if(!r)return c`<div class="roxy-empty" role="status">No bodygraph data</div>`;let a=new Set((r.centers??[]).filter(i=>i.defined).map(i=>i.id)),n=new Set((r.gates??[]).map(i=>i.gate).filter(i=>i!=null)),o=new Set((r.channels??[]).map(i=>E(i.gateA,i.gateB))),s=this.buildGateTitles(r.gates??[]);return c`<div class="wrap"><header class="head"><h2 class="title">Bodygraph</h2>${r.type?c`<div class="type-line">${r.type}${r.profile?c`· Profile ${r.profile}`:p}</div>`:p}</header><div class="layout"><svg viewBox="${N}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${G({definedCenters:a,activeChannels:o,activeGates:n,gateTitles:s})}</svg> ${this.renderSummary(r)}</div></div>`}buildGateTitles(r){let a=new Map;for(let n of r){if(n.gate==null)continue;let o=[`Gate ${n.gate}`];n.line!=null&&(o[0]+=`.${n.line}`),n.gateName&&o.push(n.gateName);let s=n.planet?I(n.planet):"",i=s?R[s]??s:"";i&&o.push(`${i} ${n.side??""}`.trim()),a.set(n.gate,o.join(" \xB7 "))}return a}renderSummary(r){let a=[{label:"Type",value:r.type},{label:"Strategy",value:r.strategy},{label:"Authority",value:r.authority},{label:"Profile",value:r.profile},{label:"Definition",value:r.definition}],n=r.incarnationCross;return c`<div class="summary"><div class="facts">${a.map(o=>o.value?c`<div class="fact"><span>${o.label}</span> <strong>${o.value}</strong></div>`:p)}</div>${n?.name?c`<p class="cross">${n.name} ${n.gates?.length?c`<span class="gates">(${n.gates.join(", ")})</span>`:p}</p>`:p} ${r.signature||r.notSelf?c`<div class="themes">${r.signature?c`<span class="pill pill--good">Signature: ${r.signature}</span>`:p} ${r.notSelf?c`<span class="pill pill--shadow">Not-self: ${r.notSelf}</span>`:p}</div>`:p}<div class="legend"><span class="legend-caption">Center colors when defined. Open centers are outlined.</span> <span><span class="swatch bg-gold defined"></span>Head, G</span> <span><span class="swatch bg-green defined"></span>Ajna</span> <span><span class="swatch bg-brown defined"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class="swatch bg-red defined"></span>Heart, Sacral</span> <span><span class="swatch"></span>Open center</span></div></div>`}};g.styles=[C,re`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`],v([ae({attribute:!1})],g.prototype,"data",2),g=v([ne("roxy-bodygraph")],g);export{g as RoxyBodygraph};
10
+ `}var Q="roxy-data";function ee(t){return t.nodeName==="SCRIPT"&&t.getAttribute("type")==="application/json"}var h=class{constructor(e){this.host=e,e.addController(this)}hostConnected(){if(this.host.data!=null)return;let e=this.read();e!==void 0&&(this.host.data=e,this.host.requestUpdate())}read(){let e=this.findInlineScript();return e?this.parse(e.textContent):void 0}findInlineScript(){for(let e of Array.from(this.host.children))if(ee(e)&&e.classList.contains(Q))return e;return null}parse(e){if(e?.trim())try{return JSON.parse(e)}catch{return}}};function I(t){return t?t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():""}var g=class extends te{constructor(){super();this.data=null;new h(this)}render(){let r=this.data;if(!r)return c`<div class="roxy-empty" role="status">No bodygraph data</div>`;let a=new Set((r.centers??[]).filter(i=>i.defined).map(i=>i.id)),n=new Set((r.gates??[]).map(i=>i.gate).filter(i=>i!=null)),o=new Set((r.channels??[]).map(i=>N(i.gateA,i.gateB))),s=this.buildGateTitles(r.gates??[]);return c`<div class="wrap"><header class="head"><h2 class="title">Bodygraph</h2>${r.type?c`<div class="type-line">${r.type}${r.profile?c`· Profile ${r.profile}`:p}</div>`:p}</header><div class="layout"><svg viewBox="${E}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${G({definedCenters:a,activeChannels:o,activeGates:n,gateTitles:s})}</svg> ${this.renderSummary(r)}</div></div>`}buildGateTitles(r){let a=new Map;for(let n of r){if(n.gate==null)continue;let o=[`Gate ${n.gate}`];n.line!=null&&(o[0]+=`.${n.line}`),n.gateName&&o.push(n.gateName);let s=n.planet?I(n.planet):"",i=s?R[s]??s:"";i&&o.push(`${i} ${n.side??""}`.trim()),a.set(n.gate,o.join(" \xB7 "))}return a}renderSummary(r){let a=[{label:"Type",value:r.type},{label:"Strategy",value:r.strategy},{label:"Authority",value:r.authority},{label:"Profile",value:r.profile},{label:"Definition",value:r.definition}],n=r.incarnationCross;return c`<div class="summary"><div class="facts">${a.map(o=>o.value?c`<div class="fact"><span>${o.label}</span> <strong>${o.value}</strong></div>`:p)}</div>${n?.name?c`<p class="cross">${n.name} ${n.gates?.length?c`<span class="gates">(${n.gates.join(", ")})</span>`:p}</p>`:p} ${r.signature||r.notSelf?c`<div class="themes">${r.signature?c`<span class="pill pill--good">Signature: ${r.signature}</span>`:p} ${r.notSelf?c`<span class="pill pill--shadow">Not-self: ${r.notSelf}</span>`:p}</div>`:p}<div class="legend"><span class="legend-caption">Center colors when defined. Open centers are outlined.</span> <span><span class="swatch bg-gold defined"></span>Head, G</span> <span><span class="swatch bg-green defined"></span>Ajna</span> <span><span class="swatch bg-brown defined"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class="swatch bg-red defined"></span>Heart, Sacral</span> <span><span class="swatch"></span>Open center</span></div></div>`}};g.styles=[C,re`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`],v([ae({attribute:!1})],g.prototype,"data",2),g=v([ne("roxy-bodygraph")],g);export{g as RoxyBodygraph};
11
11
  //# sourceMappingURL=bodygraph.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/bodygraph.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts", "../../src/utils/bodygraph-render.ts", "../../src/utils/markup-data.ts", "../../src/utils/string.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { PLANET_GLYPH } from '../tokens/index.js';\nimport type { GenerateBodygraphResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tBODYGRAPH_VIEWBOX,\n\ttype BodygraphCenterId,\n\tchannelKey,\n\trenderBodygraphSvg,\n} from '../utils/bodygraph-render.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize } from '../utils/string.js';\n\ntype GateActivation = GenerateBodygraphResponse['gates'][number];\n\n/**\n * Human Design bodygraph. Pass `data` from /human-design/bodygraph. Renders the\n * nine centers in their canonical positions and shapes, filled when defined and\n * outlined when open, the 36 channels as wiring between gates with active\n * channels emphasized, and the activated gate numbers. A summary block lists\n * type, strategy, authority, profile, definition, incarnation cross, signature,\n * and not-self theme.\n *\n * The chart is theme-driven through `--roxy-*` custom properties on `:host`, so\n * it adopts the host palette in light and dark without runtime color probing.\n */\n@customElement('roxy-bodygraph')\nexport class RoxyBodygraph extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GenerateBodygraphResponse | null = null;\n\n\trender() {\n\t\tconst d = this.data;\n\t\tif (!d)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No bodygraph data</div>`;\n\n\t\tconst definedCenters = new Set<BodygraphCenterId>(\n\t\t\t(d.centers ?? [])\n\t\t\t\t.filter((c) => c.defined)\n\t\t\t\t.map((c) => c.id as BodygraphCenterId),\n\t\t);\n\t\tconst activeGates = new Set<number>(\n\t\t\t(d.gates ?? []).map((g) => g.gate).filter((n): n is number => n != null),\n\t\t);\n\t\tconst activeChannels = new Set<string>(\n\t\t\t(d.channels ?? []).map((c) => channelKey(c.gateA, c.gateB)),\n\t\t);\n\t\tconst gateTitles = this.buildGateTitles(d.gates ?? []);\n\n\t\treturn html`<div class=\"wrap\"><header class=\"head\"><h2 class=\"title\">Bodygraph</h2>${\n\t\t\t\t\td.type\n\t\t\t\t\t\t? html`<div class=\"type-line\">${d.type}${d.profile ? html`\u00B7 Profile ${d.profile}` : nothing}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><div class=\"layout\"><svg viewBox=\"${BODYGRAPH_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette\"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${renderBodygraphSvg({\n\t\t\t\t\t\tdefinedCenters,\n\t\t\t\t\t\tactiveChannels,\n\t\t\t\t\t\tactiveGates,\n\t\t\t\t\t\tgateTitles,\n\t\t\t\t\t})}</svg> ${this.renderSummary(d)}</div></div>`;\n\t}\n\n\tprivate buildGateTitles(gates: GateActivation[]): Map<number, string> {\n\t\tconst titles = new Map<number, string>();\n\t\tfor (const g of gates) {\n\t\t\tif (g.gate == null) continue;\n\t\t\tconst parts: string[] = [`Gate ${g.gate}`];\n\t\t\tif (g.line != null) parts[0] += `.${g.line}`;\n\t\t\tif (g.gateName) parts.push(g.gateName);\n\t\t\tconst planet = g.planet ? capitalize(g.planet) : '';\n\t\t\tconst glyph = planet ? (PLANET_GLYPH[planet] ?? planet) : '';\n\t\t\tif (glyph) parts.push(`${glyph} ${g.side ?? ''}`.trim());\n\t\t\ttitles.set(g.gate, parts.join(' \u00B7 '));\n\t\t}\n\t\treturn titles;\n\t}\n\n\tprivate renderSummary(d: GenerateBodygraphResponse) {\n\t\tconst facts: Array<{ label: string; value?: string }> = [\n\t\t\t{ label: 'Type', value: d.type },\n\t\t\t{ label: 'Strategy', value: d.strategy },\n\t\t\t{ label: 'Authority', value: d.authority },\n\t\t\t{ label: 'Profile', value: d.profile },\n\t\t\t{ label: 'Definition', value: d.definition },\n\t\t];\n\t\tconst ic = d.incarnationCross;\n\t\treturn html`<div class=\"summary\"><div class=\"facts\">${facts.map((f) =>\n\t\t\t\t\tf.value\n\t\t\t\t\t\t? html`<div class=\"fact\"><span>${f.label}</span> <strong>${f.value}</strong></div>`\n\t\t\t\t\t\t: nothing,\n\t\t\t\t)}</div>${\n\t\t\t\tic?.name\n\t\t\t\t\t? html`<p class=\"cross\">${ic.name} ${\n\t\t\t\t\t\t\tic.gates?.length\n\t\t\t\t\t\t\t\t? html`<span class=\"gates\">(${ic.gates.join(', ')})</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.signature || d.notSelf\n\t\t\t\t\t? html`<div class=\"themes\">${d.signature ? html`<span class=\"pill pill--good\">Signature: ${d.signature}</span>` : nothing} ${d.notSelf ? html`<span class=\"pill pill--shadow\">Not-self: ${d.notSelf}</span>` : nothing}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"legend\"><span class=\"legend-caption\">Center colors when defined. Open centers are outlined.</span> <span><span class=\"swatch bg-gold defined\"></span>Head, G</span> <span><span class=\"swatch bg-green defined\"></span>Ajna</span> <span><span class=\"swatch bg-brown defined\"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class=\"swatch bg-red defined\"></span>Heart, Sacral</span> <span><span class=\"swatch\"></span>Open center</span></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bodygraph': RoxyBodygraph;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\n};\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "import type { TemplateResult } from 'lit';\nimport { nothing, svg } from 'lit';\n\n/**\n * Fixed geometry and renderer for the Human Design bodygraph. The diagram is a\n * standard, invariant layout: nine centers in canonical positions and shapes,\n * wired by 36 channels that each join two gates, with the 64 gates at fixed\n * points on the center edges, all overlaid on a front-facing human silhouette so\n * each center lands on the body part it governs. Only which centers are defined\n * and which channels and gates are active changes per chart, so this module holds\n * the geometry and the {@link RoxyBodygraph} component supplies the live state\n * from the /human-design/bodygraph response.\n *\n * @remarks Every point is authored in one normalized 0 to 100 canonical grid (x\n * left to right, y top to bottom) taken from the reference Jovian Archive\n * bodygraph, then scaled into {@link BODYGRAPH_VIEWBOX} by a single transform so\n * the chart and the body share one coordinate space and scale together. The grid\n * is sized so the nine centers fill the figure exactly as in the canonical\n * \"nine centers on the human body\" diagram: the Head center at the crown, Ajna at\n * the forehead, Throat at the neck, G at the chest, Heart at the right chest,\n * Spleen on the left torso, Solar Plexus on the right torso (its mirror), Sacral\n * at the lower abdomen, and Root at the pelvis. Two structural truths the layout\n * preserves: the Spleen (left) and the Solar Plexus (right) are mirror images at\n * the Sacral height, so the chart is narrow at the top and wide at the bottom;\n * and the Heart sits low and to the right of the G center, above the Solar\n * Plexus. The reference chart spreads the two side centers to the page edge for\n * channel clarity; here they are shifted inward by a fixed amount so they rest on\n * the torso sides inside the figure, with every channel topology preserved. Center shapes follow the canonical orientations (Head triangle\n * up, Ajna triangle down, Throat and Sacral and Root squares, G diamond, Heart\n * triangle pointing right, Spleen triangle pointing right with its base on the\n * far-left edge, Solar Plexus its mirror). The 36 channel gate pairs are the\n * gold-standard set used by the RoxyAPI Human Design engine.\n */\n\nexport type BodygraphCenterId =\n\t| 'head'\n\t| 'ajna'\n\t| 'throat'\n\t| 'g'\n\t| 'heart'\n\t| 'sacral'\n\t| 'solar-plexus'\n\t| 'spleen'\n\t| 'root';\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\n/**\n * One center's drawable geometry: its semantic traditional color, the polygon\n * point list of its canonical shape, and the anchor where its name label sits.\n * Shapes are explicit point lists so the triangle and diamond orientations match\n * the canonical diagram exactly. Label anchors sit in the empty margins outside\n * each shape so they never collide with the gate numbers printed on the edges.\n */\ninterface CenterGeometry {\n\tid: BodygraphCenterId;\n\tlabel: string;\n\tcolor: CenterColor;\n\tpoints: Point[];\n\tlabelAnchor: Point;\n\tlabelAlign: 'start' | 'middle' | 'end';\n}\n\n/**\n * Traditional Human Design center color group. A defined center is filled with\n * this semantic color (constant across light and dark, like chart data colors);\n * an open center is left transparent with a thin theme-aware outline. The four\n * groups mirror the canonical scheme: gold for the identity and inspiration\n * centers (Head, G), green for the mental awareness center (Ajna), red for the\n * life-force motors of will and vitality (Heart, Sacral), brown for the\n * pressure, expression, and remaining awareness centers (Throat, Spleen, Solar\n * Plexus, Root).\n */\nexport type CenterColor = 'gold' | 'green' | 'red' | 'brown';\n\n/**\n * Viewport mapping from the normalized 0 to 100 grid into the SVG viewBox. The\n * chart proper occupies the inner grid; {@link PAD} is the slim margin (in grid\n * units) the body silhouette extends past the centers on every side, so the head\n * rises just above the Head center, the hips sit just below Root, and the\n * shoulders spread just outside Spleen and Solar Plexus. UNIT scales grid units\n * to viewBox units; the chart keeps its natural narrow-top, wide-bottom shape\n * because x and y share one scale.\n */\nconst UNIT = 4;\nconst PAD = 9;\nconst VIEW_W = (100 + 2 * PAD) * UNIT;\nconst VIEW_H = (100 + 2 * PAD) * UNIT;\n\n/** Map a normalized grid point (0 to 100) into viewBox units. */\nfunction g(x: number, y: number): Point {\n\treturn { x: (x + PAD) * UNIT, y: (y + PAD) * UNIT };\n}\n\n/** Chart horizontal center, the axis of symmetry for the body and the side centers. */\nconst AXIS = 50;\n\n/** Reflect a normalized grid x across {@link AXIS}, the vertical axis of symmetry. */\nconst mirrorX = (x: number): number => 2 * AXIS - x;\n\n/**\n * Gate positions authored per center in the normalized 0 to 100 grid, before the\n * mapping through {@link g}. Symmetry is structural, not hand-typed: every center\n * on the central column is authored balanced about {@link AXIS}; the Spleen is\n * authored once on the left torso and the Solar Plexus is derived as its exact\n * mirror in {@link buildGatePoints}, so the two side centers can never drift out\n * of alignment. The Heart is the one center the canonical bodygraph places off\n * the axis (low and to the right of the G center, with no left counterpart).\n * Within each center the gates follow the canonical reading order so the numbers\n * print where a printed chart shows them.\n *\n * @remarks Solar Plexus is intentionally empty here and filled by reflecting\n * Spleen; {@link SPLEEN_TO_SOLAR_PLEXUS} pairs each Spleen gate with the Solar\n * Plexus gate at its mirror position (base-top to base-top, apex to apex).\n */\nconst GATES_BY_CENTER: Record<\n\tBodygraphCenterId,\n\tRecord<number, [number, number]>\n> = {\n\thead: { 64: [45.5, 11.2], 61: [50, 11.2], 63: [54.5, 11.2] },\n\tajna: {\n\t\t47: [45.3, 18.0],\n\t\t24: [50, 18.0],\n\t\t4: [54.7, 18.0],\n\t\t17: [45.6, 20.6],\n\t\t11: [54.4, 20.6],\n\t\t43: [50, 25.3],\n\t},\n\tthroat: {\n\t\t62: [45.5, 32.3],\n\t\t23: [50, 32.3],\n\t\t56: [54.5, 32.3],\n\t\t16: [42, 34.6],\n\t\t35: [58, 34.6],\n\t\t12: [58, 37.6],\n\t\t20: [42, 40.6],\n\t\t45: [58, 40.6],\n\t\t31: [46, 42.4],\n\t\t8: [50, 42.4],\n\t\t33: [54, 42.4],\n\t},\n\tg: {\n\t\t1: [50, 47.5],\n\t\t7: [45.6, 50.3],\n\t\t13: [54.4, 50.3],\n\t\t10: [40, 53.3],\n\t\t25: [60, 53.3],\n\t\t15: [45.6, 56.6],\n\t\t46: [54.4, 56.6],\n\t\t2: [50, 59.0],\n\t},\n\theart: {\n\t\t21: [62.5, 58.5],\n\t\t51: [62.5, 61.3],\n\t\t26: [62.5, 64.1],\n\t\t40: [73, 61.3],\n\t},\n\tspleen: {\n\t\t48: [20, 70.6],\n\t\t57: [24, 72.3],\n\t\t44: [28, 74.0],\n\t\t50: [32, 75.6],\n\t\t32: [28, 77.2],\n\t\t28: [24, 78.9],\n\t\t18: [20, 80.6],\n\t},\n\tsacral: {\n\t\t5: [45.5, 72.5],\n\t\t14: [50, 72.5],\n\t\t29: [54.5, 72.5],\n\t\t34: [42, 75.6],\n\t\t27: [42, 79.0],\n\t\t59: [58, 79.0],\n\t\t42: [45.5, 81.4],\n\t\t3: [50, 81.4],\n\t\t9: [54.5, 81.4],\n\t},\n\t'solar-plexus': {},\n\troot: {\n\t\t53: [45.5, 89.9],\n\t\t60: [50, 89.9],\n\t\t52: [54.5, 89.9],\n\t\t54: [42, 92.7],\n\t\t19: [58, 92.7],\n\t\t38: [42, 95.3],\n\t\t39: [58, 95.3],\n\t\t58: [42, 98.0],\n\t\t41: [58, 98.0],\n\t},\n};\n\n/** Spleen gate to the Solar Plexus gate at its mirror position (base-top, ..., apex, ..., base-bottom). */\nconst SPLEEN_TO_SOLAR_PLEXUS: Record<number, number> = {\n\t48: 36,\n\t57: 22,\n\t44: 37,\n\t50: 6,\n\t32: 49,\n\t28: 55,\n\t18: 30,\n};\n\n/**\n * Assemble the viewBox-space gate anchors and the gate to center index from\n * {@link GATES_BY_CENTER}, filling the Solar Plexus by reflecting the Spleen so\n * the two side centers are guaranteed mirror images. Each gate sits on the\n * canonical edge of its center where its channels connect, so channel lines join\n * gate to gate cleanly and the activated numbers print in their traditional spots.\n */\nfunction buildGatePoints(): {\n\tpoints: Record<number, Point>;\n\tcenterOf: Record<number, BodygraphCenterId>;\n} {\n\tconst points: Record<number, Point> = {};\n\tconst centerOf: Record<number, BodygraphCenterId> = {};\n\tfor (const [spleenGate, [x, y]] of Object.entries(GATES_BY_CENTER.spleen)) {\n\t\tGATES_BY_CENTER['solar-plexus'][\n\t\t\tSPLEEN_TO_SOLAR_PLEXUS[Number(spleenGate)]\n\t\t] = [mirrorX(x), y];\n\t}\n\tfor (const [center, gates] of Object.entries(GATES_BY_CENTER) as Array<\n\t\t[BodygraphCenterId, Record<number, [number, number]>]\n\t>) {\n\t\tfor (const [gate, [x, y]] of Object.entries(gates)) {\n\t\t\tpoints[Number(gate)] = g(x, y);\n\t\t\tcenterOf[Number(gate)] = center;\n\t\t}\n\t}\n\treturn { points, centerOf };\n}\n\n/**\n * The viewBox-space gate anchors ({@link GATE_POINTS}) and the gate to center\n * index ({@link GATE_CENTER}). Exported so the geometry tests can assert the\n * layout invariants (side-center mirror symmetry, central-column balance, gates\n * inside their centers) without rendering.\n */\nexport const { points: GATE_POINTS, centerOf: GATE_CENTER } = buildGatePoints();\n\n/** Horizontal axis of symmetry in viewBox units, the reflection axis for geometry tests. */\nexport const CHART_AXIS_X = VIEW_W / 2;\n\n/** Build a polygon from normalized grid corner pairs, mapping each through {@link g}. */\nfunction shape(corners: ReadonlyArray<readonly [number, number]>): Point[] {\n\treturn corners.map(([x, y]) => g(x, y));\n}\n\n/** A square center, centered on {@link AXIS}, spanning the given half-width and y range. */\nfunction squareShape(halfWidth: number, top: number, bottom: number): Point[] {\n\treturn shape([\n\t\t[AXIS - halfWidth, top],\n\t\t[AXIS + halfWidth, top],\n\t\t[AXIS + halfWidth, bottom],\n\t\t[AXIS - halfWidth, bottom],\n\t]);\n}\n\n/** The Spleen triangle (base on the far-left edge, apex pointing right toward center). */\nconst SPLEEN_SHAPE: ReadonlyArray<readonly [number, number]> = [\n\t[18.4, 68.0],\n\t[18.4, 81.8],\n\t[34.7, 74.9],\n];\n\n/**\n * Center shapes in canonical orientation and color, labels anchored in the\n * margins. Central-column centers are built centered on {@link AXIS}; the Solar\n * Plexus shape is the Spleen reflected across the axis, so the side centers stay\n * exact mirrors. The Heart is the deliberate off-axis exception.\n */\nexport const CENTER_GEOMETRY: readonly CenterGeometry[] = [\n\t{\n\t\tid: 'head',\n\t\tlabel: 'Head',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[40.0, 14.3],\n\t\t\t[60.0, 14.3],\n\t\t\t[50.0, 6.0],\n\t\t]),\n\t\tlabelAnchor: g(63, 9),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'ajna',\n\t\tlabel: 'Ajna',\n\t\tcolor: 'green',\n\t\tpoints: shape([\n\t\t\t[40.0, 15.6],\n\t\t\t[60.0, 15.6],\n\t\t\t[50.0, 27.6],\n\t\t]),\n\t\tlabelAnchor: g(62, 21),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'throat',\n\t\tlabel: 'Throat',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 30.4, 43.6),\n\t\tlabelAnchor: g(83, 34),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'g',\n\t\tlabel: 'G',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[50.0, 45.0],\n\t\t\t[60.7, 53.3],\n\t\t\t[50.0, 61.6],\n\t\t\t[39.3, 53.3],\n\t\t]),\n\t\tlabelAnchor: g(13, 51),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'heart',\n\t\tlabel: 'Heart',\n\t\tcolor: 'red',\n\t\tpoints: shape([\n\t\t\t[61.5, 57.0],\n\t\t\t[76.5, 61.3],\n\t\t\t[61.5, 65.6],\n\t\t]),\n\t\tlabelAnchor: g(85, 56),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'spleen',\n\t\tlabel: 'Spleen',\n\t\tcolor: 'brown',\n\t\tpoints: shape(SPLEEN_SHAPE),\n\t\tlabelAnchor: g(13, 70),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'sacral',\n\t\tlabel: 'Sacral',\n\t\tcolor: 'red',\n\t\tpoints: squareShape(9.5, 70.6, 83.6),\n\t\t// Lower-right, below the Solar Plexus: a left-side leader would clip the\n\t\t// Spleen, which sits at the same height as the Sacral.\n\t\tlabelAnchor: g(85, 88),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'solar-plexus',\n\t\tlabel: 'Solar Plexus',\n\t\tcolor: 'brown',\n\t\tpoints: shape(\n\t\t\tSPLEEN_SHAPE.map(([x, y]) => [mirrorX(x), y] as [number, number]),\n\t\t),\n\t\tlabelAnchor: g(87, 73),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'root',\n\t\tlabel: 'Root',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 87.9, 99.9),\n\t\tlabelAnchor: g(50, 103),\n\t\tlabelAlign: 'middle',\n\t},\n];\n\n/**\n * The 36 channels as ordered gate pairs. This is the canonical Human Design\n * channel set; a channel is active only when both of its gates are activated,\n * which the live response reports in its `channels` array. The static list lets\n * the renderer draw every channel as a hanging (inactive) line and overlay the\n * active ones, so an open bodygraph still shows its full wiring skeleton.\n */\nexport const CHANNEL_PAIRS: ReadonlyArray<readonly [number, number]> = [\n\t[64, 47],\n\t[61, 24],\n\t[63, 4],\n\t[17, 62],\n\t[11, 56],\n\t[43, 23],\n\t[16, 48],\n\t[20, 34],\n\t[20, 10],\n\t[7, 31],\n\t[1, 8],\n\t[13, 33],\n\t[21, 45],\n\t[12, 22],\n\t[35, 36],\n\t[57, 20],\n\t[15, 5],\n\t[2, 14],\n\t[46, 29],\n\t[34, 10],\n\t[10, 57],\n\t[25, 51],\n\t[27, 50],\n\t[57, 34],\n\t[26, 44],\n\t[18, 58],\n\t[28, 38],\n\t[32, 54],\n\t[3, 60],\n\t[9, 52],\n\t[42, 53],\n\t[59, 6],\n\t[19, 49],\n\t[39, 55],\n\t[41, 30],\n\t[37, 40],\n];\n\n/**\n * Front-facing standing figure behind the chart, mirror-symmetric about\n * {@link AXIS}. Authored in the same normalized grid as the centers, so it scales\n * with the chart and frames it as in the canonical \"nine centers on the human\n * body\" diagram: a rounded head holding the Head center, a short neck at the\n * Throat, shoulders that slope to arms hanging down the outside of the torso so\n * their span frames the Spleen (left) and Solar Plexus (right), a torso that\n * narrows at the waist below the chest, then hips ending at the pelvis just below\n * the Root center. The outline is one closed loop: crown, temple, jaw, neck,\n * shoulder, down the outer arm to the hand beside the hip, in under the hip to\n * the pelvis hem, mirrored back up the left side. The right half is built from\n * cubic beziers and reflected so the figure is exactly symmetric. Drawn first and\n * behind everything so the centers, wiring, and gate numbers stay legible on top.\n */\nconst BODY_PATH = buildBodyPath();\n\nfunction buildBodyPath(): string {\n\tconst m = mirrorX;\n\t// Right-side outline in grid units (x, y) from the crown down to the\n\t// pelvis-right corner: a start point, then triples of (ctrl1, ctrl2, end). A\n\t// rounded head hugging the Head and Ajna centers, a narrowed neck, shoulders at\n\t// their widest, the outer torso running just past the Spleen and Solar Plexus\n\t// (right edges near x 82 at y 74), then waist and hip to a flat pelvis hem. The\n\t// left side is the mirror and the hem is a straight line, so the figure reads as\n\t// a torso, not a point.\n\tconst r: Array<[number, number]> = [\n\t\t[50, -2], // crown apex (start)\n\t\t[60, -2], // crown round (ctrl)\n\t\t[60.5, 9], // head side (ctrl)\n\t\t[57, 18], // brow, head holds Head + Ajna (end)\n\t\t[56, 21], // cheek (ctrl)\n\t\t[54, 24], // jaw (ctrl)\n\t\t[52, 27], // neck right, narrowed (end)\n\t\t[54, 28], // neck base (ctrl)\n\t\t[64, 30], // trapezius slope (ctrl)\n\t\t[80, 34], // shoulder / deltoid, the widest point (end)\n\t\t[83.5, 40], // upper torso side (ctrl)\n\t\t[84, 56], // outer torso, frames the side centers (ctrl)\n\t\t[83, 74], // torso side past Spleen / Solar Plexus (end)\n\t\t[82, 84], // waist (ctrl)\n\t\t[76, 92], // hip (ctrl)\n\t\t[68, 97], // hip (end)\n\t\t[64, 99], // toward the pelvis (ctrl)\n\t\t[62, 100], // pelvis (ctrl)\n\t\t[60, 100], // pelvis-right corner (end)\n\t];\n\tconst segs: string[] = [`M ${pt(r[0])}`];\n\t// Walk the right side as cubic beziers, three points per segment.\n\tfor (let i = 1; i + 2 < r.length; i += 3) {\n\t\tsegs.push(`C ${pt(r[i])} ${pt(r[i + 1])} ${pt(r[i + 2])}`);\n\t}\n\t// Flat pelvis hem across to the mirrored corner.\n\tsegs.push(`L ${ptm(r[r.length - 1], m)}`);\n\t// Mirror the right walk back up the left side to the crown.\n\tfor (let i = r.length - 3; i >= 1; i -= 3) {\n\t\tsegs.push(`C ${ptm(r[i + 1], m)} ${ptm(r[i], m)} ${ptm(r[i - 1], m)}`);\n\t}\n\tsegs.push('Z');\n\treturn segs.join(' ');\n}\n\nfunction pt([x, y]: [number, number]): string {\n\tconst p = g(x, y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction ptm([x, y]: [number, number], m: (x: number) => number): string {\n\tconst p = g(m(x), y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction polygonPoints(pts: Point[]): string {\n\treturn pts.map((p) => `${p.x},${p.y}`).join(' ');\n}\n\nfunction pairKey(a: number, b: number): string {\n\treturn a < b ? `${a}-${b}` : `${b}-${a}`;\n}\n\n/** Render the body silhouette behind the chart. */\nfunction renderBody(): TemplateResult {\n\treturn svg`<path class=\"bg-body\" d=${BODY_PATH} />`;\n}\n\n/**\n * Render every channel as a single line joining its two gates, so the wiring\n * always reads as a connected diagram rather than stubs poking out of centers.\n * Each of the 36 channels draws a faint full-length connector; a channel with\n * both gates active is redrawn thick and solid (a defined channel); a channel\n * with only one gate active lights that gate's half toward the midpoint over the\n * connector (a hanging gate). This mirrors how a printed bodygraph colors a full\n * channel only when both gates are active and shows a single hanging gate\n * otherwise, while keeping every connection visible.\n */\nfunction renderChannels(\n\tactiveChannels: Set<string>,\n\tactiveGates: Set<number>,\n): TemplateResult[] {\n\tconst lines: TemplateResult[] = [];\n\tfor (const [a, b] of CHANNEL_PAIRS) {\n\t\tconst pa = GATE_POINTS[a];\n\t\tconst pb = GATE_POINTS[b];\n\t\tif (!pa || !pb) continue;\n\t\tlines.push(\n\t\t\tsvg`<line class=\"bg-channel\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t);\n\t\tif (activeChannels.has(pairKey(a, b))) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-channel on\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst mid = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };\n\t\tif (activeGates.has(a)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pa.x} y1=${pa.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t\tif (activeGates.has(b)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pb.x} y1=${pb.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t}\n\treturn lines;\n}\n\n/** Closest point to `p` on segment `a`-`b`, clamped to the segment ends. */\nfunction closestPointOnSegment(p: Point, a: Point, b: Point): Point {\n\tconst dx = b.x - a.x;\n\tconst dy = b.y - a.y;\n\tconst len2 = dx * dx + dy * dy;\n\tif (len2 === 0) return a;\n\tconst t = Math.max(\n\t\t0,\n\t\tMath.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2),\n\t);\n\treturn { x: a.x + t * dx, y: a.y + t * dy };\n}\n\n/** Closest point on a closed polygon's perimeter to `p`, where a label leader should land. */\nfunction closestPointOnPolygon(p: Point, poly: readonly Point[]): Point {\n\tlet best = poly[0];\n\tlet bestDist = Number.POSITIVE_INFINITY;\n\tfor (let i = 0; i < poly.length; i++) {\n\t\tconst q = closestPointOnSegment(p, poly[i], poly[(i + 1) % poly.length]);\n\t\tconst d = (q.x - p.x) ** 2 + (q.y - p.y) ** 2;\n\t\tif (d < bestDist) {\n\t\t\tbestDist = d;\n\t\t\tbest = q;\n\t\t}\n\t}\n\treturn best;\n}\n\n/**\n * Render the nine center shapes, filled with their semantic color when defined\n * and outlined when open, each with a margin label tied to its shape by a thin\n * leader line so the Heart and every other center is unambiguous regardless of\n * whether it is defined.\n */\nfunction renderCenters(defined: Set<BodygraphCenterId>): TemplateResult[] {\n\treturn CENTER_GEOMETRY.map((c) => {\n\t\tconst isDefined = defined.has(c.id);\n\t\tconst cls = `bg-center bg-${c.color}${isDefined ? ' defined' : ''}`;\n\t\tconst edge = closestPointOnPolygon(c.labelAnchor, c.points);\n\t\treturn svg`<g>\n\t\t\t<line class=\"bg-leader\" x1=${c.labelAnchor.x} y1=${c.labelAnchor.y} x2=${edge.x} y2=${edge.y} />\n\t\t\t<polygon class=${cls} points=${polygonPoints(c.points)}><title>${c.label}: ${isDefined ? 'defined' : 'open'}</title></polygon>\n\t\t\t<text class=\"bg-center-label\" x=${c.labelAnchor.x} y=${c.labelAnchor.y} text-anchor=${c.labelAlign} dominant-baseline=\"central\">${c.label}</text>\n\t\t</g>`;\n\t});\n}\n\n/**\n * Render the activated gate numbers at their fixed points. Numbers sit on top of\n * the filled centers, so a halo (a wider, background-colored stroke under the\n * fill) keeps them legible against any center color in both themes.\n */\nfunction renderGateNumbers(\n\tactiveGates: Set<number>,\n\ttitles: Map<number, string>,\n): TemplateResult[] {\n\tconst out: TemplateResult[] = [];\n\tfor (const [gate, p] of Object.entries(GATE_POINTS)) {\n\t\tconst num = Number(gate);\n\t\tif (!activeGates.has(num)) continue;\n\t\tconst title = titles.get(num);\n\t\tout.push(\n\t\t\tsvg`<text class=\"bg-gate\" x=${p.x} y=${p.y} text-anchor=\"middle\" dominant-baseline=\"central\">${num}${title ? svg`<title>${title}</title>` : nothing}</text>`,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport interface BodygraphRenderInput {\n\tdefinedCenters: Set<BodygraphCenterId>;\n\tactiveChannels: Set<string>;\n\tactiveGates: Set<number>;\n\tgateTitles: Map<number, string>;\n}\n\n/** Build the lookup key for an active channel from its two gate numbers. */\nexport function channelKey(a: number, b: number): string {\n\treturn pairKey(a, b);\n}\n\nexport const BODYGRAPH_VIEWBOX = `0 0 ${VIEW_W} ${VIEW_H}`;\n\n/**\n * Render the full bodygraph SVG inner content for the given live state. The\n * caller wraps it in an `<svg>` with {@link BODYGRAPH_VIEWBOX} and applies its\n * own theming CSS. Draw order: body silhouette under channels under centers\n * under gate numbers, so the body is the backdrop, the wiring sits behind the\n * shapes, and the numbers stay legible on top.\n */\nexport function renderBodygraphSvg(\n\tinput: BodygraphRenderInput,\n): TemplateResult {\n\treturn svg`\n\t\t${renderBody()}\n\t\t${renderChannels(input.activeChannels, input.activeGates)}\n\t\t${renderCenters(input.definedCenters)}\n\t\t${renderGateNumbers(input.activeGates, input.gateTitles)}\n\t`;\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n"],
5
- "mappings": "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",
6
- "names": ["css", "html", "LitElement", "nothing", "customElement", "property", "PLANET_GLYPH", "SIGNS_ORDER", "RASHI_KEYS", "s", "css", "baseStyles", "nothing", "svg", "UNIT", "PAD", "VIEW_W", "VIEW_H", "g", "x", "y", "AXIS", "mirrorX", "GATES_BY_CENTER", "SPLEEN_TO_SOLAR_PLEXUS", "buildGatePoints", "points", "centerOf", "spleenGate", "center", "gates", "gate", "GATE_POINTS", "GATE_CENTER", "CHART_AXIS_X", "shape", "corners", "squareShape", "halfWidth", "top", "bottom", "SPLEEN_SHAPE", "CENTER_GEOMETRY", "CHANNEL_PAIRS", "BODY_PATH", "buildBodyPath", "m", "r", "segs", "pt", "i", "ptm", "p", "polygonPoints", "pts", "pairKey", "a", "b", "renderBody", "renderChannels", "activeChannels", "activeGates", "lines", "pa", "pb", "mid", "closestPointOnSegment", "dx", "dy", "len2", "t", "closestPointOnPolygon", "poly", "best", "bestDist", "q", "d", "renderCenters", "defined", "c", "isDefined", "cls", "edge", "renderGateNumbers", "titles", "out", "num", "title", "channelKey", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "input", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "text", "capitalize", "s", "RoxyBodygraph", "LitElement", "MarkupDataController", "d", "html", "definedCenters", "c", "activeGates", "g", "n", "activeChannels", "channelKey", "gateTitles", "nothing", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "gates", "titles", "parts", "planet", "capitalize", "glyph", "PLANET_GLYPH", "facts", "ic", "f", "baseStyles", "css", "__decorateClass", "property", "customElement"]
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { PLANET_GLYPH } from '../tokens/index.js';\nimport type { GenerateBodygraphResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tBODYGRAPH_VIEWBOX,\n\ttype BodygraphCenterId,\n\tchannelKey,\n\trenderBodygraphSvg,\n} from '../utils/bodygraph-render.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize } from '../utils/string.js';\n\ntype GateActivation = GenerateBodygraphResponse['gates'][number];\n\n/**\n * Human Design bodygraph. Pass `data` from /human-design/bodygraph. Renders the\n * nine centers in their canonical positions and shapes, filled when defined and\n * outlined when open, the 36 channels as wiring between gates with active\n * channels emphasized, and the activated gate numbers. A summary block lists\n * type, strategy, authority, profile, definition, incarnation cross, signature,\n * and not-self theme.\n *\n * The chart is theme-driven through `--roxy-*` custom properties on `:host`, so\n * it adopts the host palette in light and dark without runtime color probing.\n */\n@customElement('roxy-bodygraph')\nexport class RoxyBodygraph extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GenerateBodygraphResponse | null = null;\n\n\trender() {\n\t\tconst d = this.data;\n\t\tif (!d)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No bodygraph data</div>`;\n\n\t\tconst definedCenters = new Set<BodygraphCenterId>(\n\t\t\t(d.centers ?? [])\n\t\t\t\t.filter((c) => c.defined)\n\t\t\t\t.map((c) => c.id as BodygraphCenterId),\n\t\t);\n\t\tconst activeGates = new Set<number>(\n\t\t\t(d.gates ?? []).map((g) => g.gate).filter((n): n is number => n != null),\n\t\t);\n\t\tconst activeChannels = new Set<string>(\n\t\t\t(d.channels ?? []).map((c) => channelKey(c.gateA, c.gateB)),\n\t\t);\n\t\tconst gateTitles = this.buildGateTitles(d.gates ?? []);\n\n\t\treturn html`<div class=\"wrap\"><header class=\"head\"><h2 class=\"title\">Bodygraph</h2>${\n\t\t\t\t\td.type\n\t\t\t\t\t\t? html`<div class=\"type-line\">${d.type}${d.profile ? html`\u00B7 Profile ${d.profile}` : nothing}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><div class=\"layout\"><svg viewBox=\"${BODYGRAPH_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette\"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${renderBodygraphSvg({\n\t\t\t\t\t\tdefinedCenters,\n\t\t\t\t\t\tactiveChannels,\n\t\t\t\t\t\tactiveGates,\n\t\t\t\t\t\tgateTitles,\n\t\t\t\t\t})}</svg> ${this.renderSummary(d)}</div></div>`;\n\t}\n\n\tprivate buildGateTitles(gates: GateActivation[]): Map<number, string> {\n\t\tconst titles = new Map<number, string>();\n\t\tfor (const g of gates) {\n\t\t\tif (g.gate == null) continue;\n\t\t\tconst parts: string[] = [`Gate ${g.gate}`];\n\t\t\tif (g.line != null) parts[0] += `.${g.line}`;\n\t\t\tif (g.gateName) parts.push(g.gateName);\n\t\t\tconst planet = g.planet ? capitalize(g.planet) : '';\n\t\t\tconst glyph = planet ? (PLANET_GLYPH[planet] ?? planet) : '';\n\t\t\tif (glyph) parts.push(`${glyph} ${g.side ?? ''}`.trim());\n\t\t\ttitles.set(g.gate, parts.join(' \u00B7 '));\n\t\t}\n\t\treturn titles;\n\t}\n\n\tprivate renderSummary(d: GenerateBodygraphResponse) {\n\t\tconst facts: Array<{ label: string; value?: string }> = [\n\t\t\t{ label: 'Type', value: d.type },\n\t\t\t{ label: 'Strategy', value: d.strategy },\n\t\t\t{ label: 'Authority', value: d.authority },\n\t\t\t{ label: 'Profile', value: d.profile },\n\t\t\t{ label: 'Definition', value: d.definition },\n\t\t];\n\t\tconst ic = d.incarnationCross;\n\t\treturn html`<div class=\"summary\"><div class=\"facts\">${facts.map((f) =>\n\t\t\t\t\tf.value\n\t\t\t\t\t\t? html`<div class=\"fact\"><span>${f.label}</span> <strong>${f.value}</strong></div>`\n\t\t\t\t\t\t: nothing,\n\t\t\t\t)}</div>${\n\t\t\t\tic?.name\n\t\t\t\t\t? html`<p class=\"cross\">${ic.name} ${\n\t\t\t\t\t\t\tic.gates?.length\n\t\t\t\t\t\t\t\t? html`<span class=\"gates\">(${ic.gates.join(', ')})</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.signature || d.notSelf\n\t\t\t\t\t? html`<div class=\"themes\">${d.signature ? html`<span class=\"pill pill--good\">Signature: ${d.signature}</span>` : nothing} ${d.notSelf ? html`<span class=\"pill pill--shadow\">Not-self: ${d.notSelf}</span>` : nothing}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"legend\"><span class=\"legend-caption\">Center colors when defined. Open centers are outlined.</span> <span><span class=\"swatch bg-gold defined\"></span>Head, G</span> <span><span class=\"swatch bg-green defined\"></span>Ajna</span> <span><span class=\"swatch bg-brown defined\"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class=\"swatch bg-red defined\"></span>Heart, Sacral</span> <span><span class=\"swatch\"></span>Open center</span></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bodygraph': RoxyBodygraph;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\n};\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "import type { TemplateResult } from 'lit';\nimport { nothing, svg } from 'lit';\n\n/**\n * Fixed geometry and renderer for the Human Design bodygraph. The diagram is a\n * standard, invariant layout: nine centers in canonical positions and shapes,\n * wired by 36 channels that each join two gates, with the 64 gates at fixed\n * points on the center edges, all overlaid on a front-facing human silhouette so\n * each center lands on the body part it governs. Only which centers are defined\n * and which channels and gates are active changes per chart, so this module holds\n * the geometry and the {@link RoxyBodygraph} component supplies the live state\n * from the /human-design/bodygraph response.\n *\n * @remarks Every point is authored in one normalized 0 to 100 canonical grid (x\n * left to right, y top to bottom) taken from the reference Jovian Archive\n * bodygraph, then scaled into {@link BODYGRAPH_VIEWBOX} by a single transform so\n * the chart and the body share one coordinate space and scale together. The grid\n * is sized so the nine centers fill the figure exactly as in the canonical\n * \"nine centers on the human body\" diagram: the Head center at the crown, Ajna at\n * the forehead, Throat at the neck, G at the chest, Heart at the right chest,\n * Spleen on the left torso, Solar Plexus on the right torso (its mirror), Sacral\n * at the lower abdomen, and Root at the pelvis. Two structural truths the layout\n * preserves: the Spleen (left) and the Solar Plexus (right) are mirror images at\n * the Sacral height, so the chart is narrow at the top and wide at the bottom;\n * and the Heart sits low and to the right of the G center, above the Solar\n * Plexus. The reference chart spreads the two side centers to the page edge for\n * channel clarity; here they are shifted inward by a fixed amount so they rest on\n * the torso sides inside the figure, with every channel topology preserved. Center shapes follow the canonical orientations (Head triangle\n * up, Ajna triangle down, Throat and Sacral and Root squares, G diamond, Heart\n * triangle pointing right, Spleen triangle pointing right with its base on the\n * far-left edge, Solar Plexus its mirror). The 36 channel gate pairs are the\n * gold-standard set used by the RoxyAPI Human Design engine.\n */\n\nexport type BodygraphCenterId =\n\t| 'head'\n\t| 'ajna'\n\t| 'throat'\n\t| 'g'\n\t| 'heart'\n\t| 'sacral'\n\t| 'solar-plexus'\n\t| 'spleen'\n\t| 'root';\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\n/**\n * One center's drawable geometry: its semantic traditional color, the polygon\n * point list of its canonical shape, and the anchor where its name label sits.\n * Shapes are explicit point lists so the triangle and diamond orientations match\n * the canonical diagram exactly. Label anchors sit in the empty margins outside\n * each shape so they never collide with the gate numbers printed on the edges.\n */\ninterface CenterGeometry {\n\tid: BodygraphCenterId;\n\tlabel: string;\n\tcolor: CenterColor;\n\tpoints: Point[];\n\tlabelAnchor: Point;\n\tlabelAlign: 'start' | 'middle' | 'end';\n}\n\n/**\n * Traditional Human Design center color group. A defined center is filled with\n * this semantic color (constant across light and dark, like chart data colors);\n * an open center is left transparent with a thin theme-aware outline. The four\n * groups mirror the canonical scheme: gold for the identity and inspiration\n * centers (Head, G), green for the mental awareness center (Ajna), red for the\n * life-force motors of will and vitality (Heart, Sacral), brown for the\n * pressure, expression, and remaining awareness centers (Throat, Spleen, Solar\n * Plexus, Root).\n */\nexport type CenterColor = 'gold' | 'green' | 'red' | 'brown';\n\n/**\n * Viewport mapping from the normalized 0 to 100 grid into the SVG viewBox. The\n * chart proper occupies the inner grid; {@link PAD} is the slim margin (in grid\n * units) the body silhouette extends past the centers on every side, so the head\n * rises just above the Head center, the hips sit just below Root, and the\n * shoulders spread just outside Spleen and Solar Plexus. UNIT scales grid units\n * to viewBox units; the chart keeps its natural narrow-top, wide-bottom shape\n * because x and y share one scale.\n */\nconst UNIT = 4;\nconst PAD = 9;\nconst VIEW_W = (100 + 2 * PAD) * UNIT;\nconst VIEW_H = (100 + 2 * PAD) * UNIT;\n\n/** Map a normalized grid point (0 to 100) into viewBox units. */\nfunction g(x: number, y: number): Point {\n\treturn { x: (x + PAD) * UNIT, y: (y + PAD) * UNIT };\n}\n\n/** Chart horizontal center, the axis of symmetry for the body and the side centers. */\nconst AXIS = 50;\n\n/** Reflect a normalized grid x across {@link AXIS}, the vertical axis of symmetry. */\nconst mirrorX = (x: number): number => 2 * AXIS - x;\n\n/**\n * Gate positions authored per center in the normalized 0 to 100 grid, before the\n * mapping through {@link g}. Symmetry is structural, not hand-typed: every center\n * on the central column is authored balanced about {@link AXIS}; the Spleen is\n * authored once on the left torso and the Solar Plexus is derived as its exact\n * mirror in {@link buildGatePoints}, so the two side centers can never drift out\n * of alignment. The Heart is the one center the canonical bodygraph places off\n * the axis (low and to the right of the G center, with no left counterpart).\n * Within each center the gates follow the canonical reading order so the numbers\n * print where a printed chart shows them.\n *\n * @remarks Solar Plexus is intentionally empty here and filled by reflecting\n * Spleen; {@link SPLEEN_TO_SOLAR_PLEXUS} pairs each Spleen gate with the Solar\n * Plexus gate at its mirror position (base-top to base-top, apex to apex).\n */\nconst GATES_BY_CENTER: Record<\n\tBodygraphCenterId,\n\tRecord<number, [number, number]>\n> = {\n\thead: { 64: [45.5, 11.2], 61: [50, 11.2], 63: [54.5, 11.2] },\n\tajna: {\n\t\t47: [45.3, 18.0],\n\t\t24: [50, 18.0],\n\t\t4: [54.7, 18.0],\n\t\t17: [45.6, 20.6],\n\t\t11: [54.4, 20.6],\n\t\t43: [50, 25.3],\n\t},\n\tthroat: {\n\t\t62: [45.5, 32.3],\n\t\t23: [50, 32.3],\n\t\t56: [54.5, 32.3],\n\t\t16: [42, 34.6],\n\t\t35: [58, 34.6],\n\t\t12: [58, 37.6],\n\t\t20: [42, 40.6],\n\t\t45: [58, 40.6],\n\t\t31: [46, 42.4],\n\t\t8: [50, 42.4],\n\t\t33: [54, 42.4],\n\t},\n\tg: {\n\t\t1: [50, 47.5],\n\t\t7: [45.6, 50.3],\n\t\t13: [54.4, 50.3],\n\t\t10: [40, 53.3],\n\t\t25: [60, 53.3],\n\t\t15: [45.6, 56.6],\n\t\t46: [54.4, 56.6],\n\t\t2: [50, 59.0],\n\t},\n\theart: {\n\t\t21: [62.5, 58.5],\n\t\t51: [62.5, 61.3],\n\t\t26: [62.5, 64.1],\n\t\t40: [73, 61.3],\n\t},\n\tspleen: {\n\t\t48: [20, 70.6],\n\t\t57: [24, 72.3],\n\t\t44: [28, 74.0],\n\t\t50: [32, 75.6],\n\t\t32: [28, 77.2],\n\t\t28: [24, 78.9],\n\t\t18: [20, 80.6],\n\t},\n\tsacral: {\n\t\t5: [45.5, 72.5],\n\t\t14: [50, 72.5],\n\t\t29: [54.5, 72.5],\n\t\t34: [42, 75.6],\n\t\t27: [42, 79.0],\n\t\t59: [58, 79.0],\n\t\t42: [45.5, 81.4],\n\t\t3: [50, 81.4],\n\t\t9: [54.5, 81.4],\n\t},\n\t'solar-plexus': {},\n\troot: {\n\t\t53: [45.5, 89.9],\n\t\t60: [50, 89.9],\n\t\t52: [54.5, 89.9],\n\t\t54: [42, 92.7],\n\t\t19: [58, 92.7],\n\t\t38: [42, 95.3],\n\t\t39: [58, 95.3],\n\t\t58: [42, 98.0],\n\t\t41: [58, 98.0],\n\t},\n};\n\n/** Spleen gate to the Solar Plexus gate at its mirror position (base-top, ..., apex, ..., base-bottom). */\nconst SPLEEN_TO_SOLAR_PLEXUS: Record<number, number> = {\n\t48: 36,\n\t57: 22,\n\t44: 37,\n\t50: 6,\n\t32: 49,\n\t28: 55,\n\t18: 30,\n};\n\n/**\n * Assemble the viewBox-space gate anchors and the gate to center index from\n * {@link GATES_BY_CENTER}, filling the Solar Plexus by reflecting the Spleen so\n * the two side centers are guaranteed mirror images. Each gate sits on the\n * canonical edge of its center where its channels connect, so channel lines join\n * gate to gate cleanly and the activated numbers print in their traditional spots.\n */\nfunction buildGatePoints(): {\n\tpoints: Record<number, Point>;\n\tcenterOf: Record<number, BodygraphCenterId>;\n} {\n\tconst points: Record<number, Point> = {};\n\tconst centerOf: Record<number, BodygraphCenterId> = {};\n\tfor (const [spleenGate, [x, y]] of Object.entries(GATES_BY_CENTER.spleen)) {\n\t\tGATES_BY_CENTER['solar-plexus'][\n\t\t\tSPLEEN_TO_SOLAR_PLEXUS[Number(spleenGate)]\n\t\t] = [mirrorX(x), y];\n\t}\n\tfor (const [center, gates] of Object.entries(GATES_BY_CENTER) as Array<\n\t\t[BodygraphCenterId, Record<number, [number, number]>]\n\t>) {\n\t\tfor (const [gate, [x, y]] of Object.entries(gates)) {\n\t\t\tpoints[Number(gate)] = g(x, y);\n\t\t\tcenterOf[Number(gate)] = center;\n\t\t}\n\t}\n\treturn { points, centerOf };\n}\n\n/**\n * The viewBox-space gate anchors ({@link GATE_POINTS}) and the gate to center\n * index ({@link GATE_CENTER}). Exported so the geometry tests can assert the\n * layout invariants (side-center mirror symmetry, central-column balance, gates\n * inside their centers) without rendering.\n */\nexport const { points: GATE_POINTS, centerOf: GATE_CENTER } = buildGatePoints();\n\n/** Horizontal axis of symmetry in viewBox units, the reflection axis for geometry tests. */\nexport const CHART_AXIS_X = VIEW_W / 2;\n\n/** Build a polygon from normalized grid corner pairs, mapping each through {@link g}. */\nfunction shape(corners: ReadonlyArray<readonly [number, number]>): Point[] {\n\treturn corners.map(([x, y]) => g(x, y));\n}\n\n/** A square center, centered on {@link AXIS}, spanning the given half-width and y range. */\nfunction squareShape(halfWidth: number, top: number, bottom: number): Point[] {\n\treturn shape([\n\t\t[AXIS - halfWidth, top],\n\t\t[AXIS + halfWidth, top],\n\t\t[AXIS + halfWidth, bottom],\n\t\t[AXIS - halfWidth, bottom],\n\t]);\n}\n\n/** The Spleen triangle (base on the far-left edge, apex pointing right toward center). */\nconst SPLEEN_SHAPE: ReadonlyArray<readonly [number, number]> = [\n\t[18.4, 68.0],\n\t[18.4, 81.8],\n\t[34.7, 74.9],\n];\n\n/**\n * Center shapes in canonical orientation and color, labels anchored in the\n * margins. Central-column centers are built centered on {@link AXIS}; the Solar\n * Plexus shape is the Spleen reflected across the axis, so the side centers stay\n * exact mirrors. The Heart is the deliberate off-axis exception.\n */\nexport const CENTER_GEOMETRY: readonly CenterGeometry[] = [\n\t{\n\t\tid: 'head',\n\t\tlabel: 'Head',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[40.0, 14.3],\n\t\t\t[60.0, 14.3],\n\t\t\t[50.0, 6.0],\n\t\t]),\n\t\tlabelAnchor: g(63, 9),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'ajna',\n\t\tlabel: 'Ajna',\n\t\tcolor: 'green',\n\t\tpoints: shape([\n\t\t\t[40.0, 15.6],\n\t\t\t[60.0, 15.6],\n\t\t\t[50.0, 27.6],\n\t\t]),\n\t\tlabelAnchor: g(62, 21),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'throat',\n\t\tlabel: 'Throat',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 30.4, 43.6),\n\t\tlabelAnchor: g(83, 34),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'g',\n\t\tlabel: 'G',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[50.0, 45.0],\n\t\t\t[60.7, 53.3],\n\t\t\t[50.0, 61.6],\n\t\t\t[39.3, 53.3],\n\t\t]),\n\t\tlabelAnchor: g(13, 51),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'heart',\n\t\tlabel: 'Heart',\n\t\tcolor: 'red',\n\t\tpoints: shape([\n\t\t\t[61.5, 57.0],\n\t\t\t[76.5, 61.3],\n\t\t\t[61.5, 65.6],\n\t\t]),\n\t\tlabelAnchor: g(85, 56),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'spleen',\n\t\tlabel: 'Spleen',\n\t\tcolor: 'brown',\n\t\tpoints: shape(SPLEEN_SHAPE),\n\t\tlabelAnchor: g(13, 70),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'sacral',\n\t\tlabel: 'Sacral',\n\t\tcolor: 'red',\n\t\tpoints: squareShape(9.5, 70.6, 83.6),\n\t\t// Lower-right, below the Solar Plexus: a left-side leader would clip the\n\t\t// Spleen, which sits at the same height as the Sacral.\n\t\tlabelAnchor: g(85, 88),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'solar-plexus',\n\t\tlabel: 'Solar Plexus',\n\t\tcolor: 'brown',\n\t\tpoints: shape(\n\t\t\tSPLEEN_SHAPE.map(([x, y]) => [mirrorX(x), y] as [number, number]),\n\t\t),\n\t\tlabelAnchor: g(87, 73),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'root',\n\t\tlabel: 'Root',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 87.9, 99.9),\n\t\tlabelAnchor: g(50, 103),\n\t\tlabelAlign: 'middle',\n\t},\n];\n\n/**\n * The 36 channels as ordered gate pairs. This is the canonical Human Design\n * channel set; a channel is active only when both of its gates are activated,\n * which the live response reports in its `channels` array. The static list lets\n * the renderer draw every channel as a hanging (inactive) line and overlay the\n * active ones, so an open bodygraph still shows its full wiring skeleton.\n */\nexport const CHANNEL_PAIRS: ReadonlyArray<readonly [number, number]> = [\n\t[64, 47],\n\t[61, 24],\n\t[63, 4],\n\t[17, 62],\n\t[11, 56],\n\t[43, 23],\n\t[16, 48],\n\t[20, 34],\n\t[20, 10],\n\t[7, 31],\n\t[1, 8],\n\t[13, 33],\n\t[21, 45],\n\t[12, 22],\n\t[35, 36],\n\t[57, 20],\n\t[15, 5],\n\t[2, 14],\n\t[46, 29],\n\t[34, 10],\n\t[10, 57],\n\t[25, 51],\n\t[27, 50],\n\t[57, 34],\n\t[26, 44],\n\t[18, 58],\n\t[28, 38],\n\t[32, 54],\n\t[3, 60],\n\t[9, 52],\n\t[42, 53],\n\t[59, 6],\n\t[19, 49],\n\t[39, 55],\n\t[41, 30],\n\t[37, 40],\n];\n\n/**\n * Front-facing standing figure behind the chart, mirror-symmetric about\n * {@link AXIS}. Authored in the same normalized grid as the centers, so it scales\n * with the chart and frames it as in the canonical \"nine centers on the human\n * body\" diagram: a rounded head holding the Head center, a short neck at the\n * Throat, shoulders that slope to arms hanging down the outside of the torso so\n * their span frames the Spleen (left) and Solar Plexus (right), a torso that\n * narrows at the waist below the chest, then hips ending at the pelvis just below\n * the Root center. The outline is one closed loop: crown, temple, jaw, neck,\n * shoulder, down the outer arm to the hand beside the hip, in under the hip to\n * the pelvis hem, mirrored back up the left side. The right half is built from\n * cubic beziers and reflected so the figure is exactly symmetric. Drawn first and\n * behind everything so the centers, wiring, and gate numbers stay legible on top.\n */\nconst BODY_PATH = buildBodyPath();\n\nfunction buildBodyPath(): string {\n\tconst m = mirrorX;\n\t// Right-side outline in grid units (x, y) from the crown down to the\n\t// pelvis-right corner: a start point, then triples of (ctrl1, ctrl2, end). A\n\t// rounded head hugging the Head and Ajna centers, a narrowed neck, shoulders at\n\t// their widest, the outer torso running just past the Spleen and Solar Plexus\n\t// (right edges near x 82 at y 74), then waist and hip to a flat pelvis hem. The\n\t// left side is the mirror and the hem is a straight line, so the figure reads as\n\t// a torso, not a point.\n\tconst r: Array<[number, number]> = [\n\t\t[50, -2], // crown apex (start)\n\t\t[60, -2], // crown round (ctrl)\n\t\t[60.5, 9], // head side (ctrl)\n\t\t[57, 18], // brow, head holds Head + Ajna (end)\n\t\t[56, 21], // cheek (ctrl)\n\t\t[54, 24], // jaw (ctrl)\n\t\t[52, 27], // neck right, narrowed (end)\n\t\t[54, 28], // neck base (ctrl)\n\t\t[64, 30], // trapezius slope (ctrl)\n\t\t[80, 34], // shoulder / deltoid, the widest point (end)\n\t\t[83.5, 40], // upper torso side (ctrl)\n\t\t[84, 56], // outer torso, frames the side centers (ctrl)\n\t\t[83, 74], // torso side past Spleen / Solar Plexus (end)\n\t\t[82, 84], // waist (ctrl)\n\t\t[76, 92], // hip (ctrl)\n\t\t[68, 97], // hip (end)\n\t\t[64, 99], // toward the pelvis (ctrl)\n\t\t[62, 100], // pelvis (ctrl)\n\t\t[60, 100], // pelvis-right corner (end)\n\t];\n\tconst segs: string[] = [`M ${pt(r[0])}`];\n\t// Walk the right side as cubic beziers, three points per segment.\n\tfor (let i = 1; i + 2 < r.length; i += 3) {\n\t\tsegs.push(`C ${pt(r[i])} ${pt(r[i + 1])} ${pt(r[i + 2])}`);\n\t}\n\t// Flat pelvis hem across to the mirrored corner.\n\tsegs.push(`L ${ptm(r[r.length - 1], m)}`);\n\t// Mirror the right walk back up the left side to the crown.\n\tfor (let i = r.length - 3; i >= 1; i -= 3) {\n\t\tsegs.push(`C ${ptm(r[i + 1], m)} ${ptm(r[i], m)} ${ptm(r[i - 1], m)}`);\n\t}\n\tsegs.push('Z');\n\treturn segs.join(' ');\n}\n\nfunction pt([x, y]: [number, number]): string {\n\tconst p = g(x, y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction ptm([x, y]: [number, number], m: (x: number) => number): string {\n\tconst p = g(m(x), y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction polygonPoints(pts: Point[]): string {\n\treturn pts.map((p) => `${p.x},${p.y}`).join(' ');\n}\n\nfunction pairKey(a: number, b: number): string {\n\treturn a < b ? `${a}-${b}` : `${b}-${a}`;\n}\n\n/** Render the body silhouette behind the chart. */\nfunction renderBody(): TemplateResult {\n\treturn svg`<path class=\"bg-body\" d=${BODY_PATH} />`;\n}\n\n/**\n * Render every channel as a single line joining its two gates, so the wiring\n * always reads as a connected diagram rather than stubs poking out of centers.\n * Each of the 36 channels draws a faint full-length connector; a channel with\n * both gates active is redrawn thick and solid (a defined channel); a channel\n * with only one gate active lights that gate's half toward the midpoint over the\n * connector (a hanging gate). This mirrors how a printed bodygraph colors a full\n * channel only when both gates are active and shows a single hanging gate\n * otherwise, while keeping every connection visible.\n */\nfunction renderChannels(\n\tactiveChannels: Set<string>,\n\tactiveGates: Set<number>,\n): TemplateResult[] {\n\tconst lines: TemplateResult[] = [];\n\tfor (const [a, b] of CHANNEL_PAIRS) {\n\t\tconst pa = GATE_POINTS[a];\n\t\tconst pb = GATE_POINTS[b];\n\t\tif (!pa || !pb) continue;\n\t\tlines.push(\n\t\t\tsvg`<line class=\"bg-channel\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t);\n\t\tif (activeChannels.has(pairKey(a, b))) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-channel on\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst mid = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };\n\t\tif (activeGates.has(a)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pa.x} y1=${pa.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t\tif (activeGates.has(b)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pb.x} y1=${pb.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t}\n\treturn lines;\n}\n\n/** Closest point to `p` on segment `a`-`b`, clamped to the segment ends. */\nfunction closestPointOnSegment(p: Point, a: Point, b: Point): Point {\n\tconst dx = b.x - a.x;\n\tconst dy = b.y - a.y;\n\tconst len2 = dx * dx + dy * dy;\n\tif (len2 === 0) return a;\n\tconst t = Math.max(\n\t\t0,\n\t\tMath.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2),\n\t);\n\treturn { x: a.x + t * dx, y: a.y + t * dy };\n}\n\n/** Closest point on a closed polygon's perimeter to `p`, where a label leader should land. */\nfunction closestPointOnPolygon(p: Point, poly: readonly Point[]): Point {\n\tlet best = poly[0];\n\tlet bestDist = Number.POSITIVE_INFINITY;\n\tfor (let i = 0; i < poly.length; i++) {\n\t\tconst q = closestPointOnSegment(p, poly[i], poly[(i + 1) % poly.length]);\n\t\tconst d = (q.x - p.x) ** 2 + (q.y - p.y) ** 2;\n\t\tif (d < bestDist) {\n\t\t\tbestDist = d;\n\t\t\tbest = q;\n\t\t}\n\t}\n\treturn best;\n}\n\n/**\n * Render the nine center shapes, filled with their semantic color when defined\n * and outlined when open, each with a margin label tied to its shape by a thin\n * leader line so the Heart and every other center is unambiguous regardless of\n * whether it is defined.\n */\nfunction renderCenters(defined: Set<BodygraphCenterId>): TemplateResult[] {\n\treturn CENTER_GEOMETRY.map((c) => {\n\t\tconst isDefined = defined.has(c.id);\n\t\tconst cls = `bg-center bg-${c.color}${isDefined ? ' defined' : ''}`;\n\t\tconst edge = closestPointOnPolygon(c.labelAnchor, c.points);\n\t\treturn svg`<g>\n\t\t\t<line class=\"bg-leader\" x1=${c.labelAnchor.x} y1=${c.labelAnchor.y} x2=${edge.x} y2=${edge.y} />\n\t\t\t<polygon class=${cls} points=${polygonPoints(c.points)}><title>${c.label}: ${isDefined ? 'defined' : 'open'}</title></polygon>\n\t\t\t<text class=\"bg-center-label\" x=${c.labelAnchor.x} y=${c.labelAnchor.y} text-anchor=${c.labelAlign} dominant-baseline=\"central\">${c.label}</text>\n\t\t</g>`;\n\t});\n}\n\n/**\n * Render the activated gate numbers at their fixed points. Numbers sit on top of\n * the filled centers, so a halo (a wider, background-colored stroke under the\n * fill) keeps them legible against any center color in both themes.\n */\nfunction renderGateNumbers(\n\tactiveGates: Set<number>,\n\ttitles: Map<number, string>,\n): TemplateResult[] {\n\tconst out: TemplateResult[] = [];\n\tfor (const [gate, p] of Object.entries(GATE_POINTS)) {\n\t\tconst num = Number(gate);\n\t\tif (!activeGates.has(num)) continue;\n\t\tconst title = titles.get(num);\n\t\tout.push(\n\t\t\tsvg`<text class=\"bg-gate\" x=${p.x} y=${p.y} text-anchor=\"middle\" dominant-baseline=\"central\">${num}${title ? svg`<title>${title}</title>` : nothing}</text>`,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport interface BodygraphRenderInput {\n\tdefinedCenters: Set<BodygraphCenterId>;\n\tactiveChannels: Set<string>;\n\tactiveGates: Set<number>;\n\tgateTitles: Map<number, string>;\n}\n\n/** Build the lookup key for an active channel from its two gate numbers. */\nexport function channelKey(a: number, b: number): string {\n\treturn pairKey(a, b);\n}\n\nexport const BODYGRAPH_VIEWBOX = `0 0 ${VIEW_W} ${VIEW_H}`;\n\n/**\n * Render the full bodygraph SVG inner content for the given live state. The\n * caller wraps it in an `<svg>` with {@link BODYGRAPH_VIEWBOX} and applies its\n * own theming CSS. Draw order: body silhouette under channels under centers\n * under gate numbers, so the body is the backdrop, the wiring sits behind the\n * shapes, and the numbers stay legible on top.\n */\nexport function renderBodygraphSvg(\n\tinput: BodygraphRenderInput,\n): TemplateResult {\n\treturn svg`\n\t\t${renderBody()}\n\t\t${renderChannels(input.activeChannels, input.activeGates)}\n\t\t${renderCenters(input.definedCenters)}\n\t\t${renderGateNumbers(input.activeGates, input.gateTitles)}\n\t`;\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * Serialize a RoxyAPI response for embedding inside an inline `<script type=\"application/json\" class=\"roxy-data\">`. This is the safe writer counterpart to {@link MarkupDataController}, the reader: server-rendered and cached consumers (WordPress, JSX SSR, static HTML) emit the script with this, and the component hydrates `data` from it on connect.\n *\n * @remarks\n * Use this instead of a bare `JSON.stringify`. A raw stringify of a response that contains the literal text `</script>` (common in long interpretation prose) closes the script element early, corrupting the page and creating an HTML-injection vector. This escapes the script-unsafe characters to their `\\uXXXX` JSON escapes, which `JSON.parse` restores to the original characters, so the value the component receives is identical to the response you passed.\n *\n * `<` is the load-bearing escape (it defeats `</script>` and `<!--`). `>` and `&` are escaped for defence in depth, and U+2028 / U+2029 because they are valid in JSON yet are line terminators in a `<script>` context and break some parsers. The escapes introduce only `\\uXXXX` sequences, so the replacements never feed each other and order is irrelevant.\n *\n * Pass the unwrapped RoxyAPI response, the same shape you would assign to `element.data`. Do not pass the SDK envelope (`{ data, error, request, response }`).\n *\n * @example\n * ```ts\n * import { serializeRoxyData } from '@roxyapi/ui';\n *\n * const { data } = await roxy.dreams.getDreamSymbol({ path: { id: 'water' } });\n * const html = `<roxy-dream-card><script type=\"application/json\" class=\"roxy-data\">${serializeRoxyData(data)}</script></roxy-dream-card>`;\n * ```\n */\nexport function serializeRoxyData(data: unknown): string {\n\treturn JSON.stringify(data)\n\t\t.replace(/</g, '\\\\u003c')\n\t\t.replace(/>/g, '\\\\u003e')\n\t\t.replace(/&/g, '\\\\u0026')\n\t\t.replace(/\\u2028/g, '\\\\u2028')\n\t\t.replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Build the complete `<script type=\"application/json\" class=\"roxy-data\">\u2026</script>` element a server nests inside a `roxy-*` component for the no-JavaScript hydration path. The payload is escaped via {@link serializeRoxyData}, so it is safe to drop straight into HTML output.\n *\n * @remarks\n * The element carries both `type=\"application/json\"` and `class=\"roxy-data\"` because {@link MarkupDataController} reads only a direct-child script that has both. Emit one of these inside the target component; the JavaScript `data` property still wins if it is later assigned.\n *\n * @example\n * ```ts\n * import { roxyDataScript } from '@roxyapi/ui';\n *\n * const { data } = await roxy.crystals.getCrystal({ path: { id: 'amethyst' } });\n * const html = `<roxy-crystal-grid>${roxyDataScript(data)}</roxy-crystal-grid>`;\n * ```\n */\nexport function roxyDataScript(data: unknown): string {\n\treturn `<script type=\"application/json\" class=\"${ROXY_DATA_CLASS}\">${serializeRoxyData(data)}</script>`;\n}\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n"],
5
+ "mappings": "wMAAA,OAAS,OAAAA,GAAK,QAAAC,EAAM,cAAAC,GAAY,WAAAC,MAAe,MAC/C,OAAS,iBAAAC,GAAe,YAAAC,OAAgB,oBCKjC,IAAMC,EAAuC,CACnD,IAAK,SACL,KAAM,SACN,QAAS,SACT,MAAO,SACP,MAAO,SACP,KAAM,SACN,QAAS,SACT,OAAQ,SACR,OAAQ,SACR,QAAS,SACT,MAAO,SACP,KAAM,SACN,KAAM,SACN,UAAW,MACX,MAAO,KACP,UAAW,SACX,UAAW,SACX,aAAc,SACd,aAAc,SACd,OAAQ,SACR,OAAQ,SACR,oBAAqB,QACtB,EAiDO,IAAMC,EAAc,CAC1B,QACA,SACA,SACA,SACA,MACA,QACA,QACA,UACA,cACA,YACA,WACA,QACD,EAOaC,GAAaD,EAAY,IAAKE,GAC1CA,EAAE,YAAY,CACf,ECpGA,OAAS,OAAAC,MAAW,MAMb,IAAMC,EAAaD,myCCL1B,OAAS,WAAAE,EAAS,OAAAC,MAAW,MAsF7B,IAAMC,EAAO,EACPC,EAAM,EACNC,GAAU,IAAM,EAAID,GAAOD,EAC3BG,GAAU,IAAM,EAAIF,GAAOD,EAGjC,SAASI,EAAEC,EAAWC,EAAkB,CACvC,MAAO,CAAE,GAAID,EAAIJ,GAAOD,EAAM,GAAIM,EAAIL,GAAOD,CAAK,CACnD,CAGA,IAAMO,EAAO,GAGPC,EAAWH,GAAsB,EAAIE,EAAOF,EAiB5CI,EAGF,CACH,KAAM,CAAE,GAAI,CAAC,KAAM,IAAI,EAAG,GAAI,CAAC,GAAI,IAAI,EAAG,GAAI,CAAC,KAAM,IAAI,CAAE,EAC3D,KAAM,CACL,GAAI,CAAC,KAAM,EAAI,EACf,GAAI,CAAC,GAAI,EAAI,EACb,EAAG,CAAC,KAAM,EAAI,EACd,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,CACd,EACA,OAAQ,CACP,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,EAAG,CAAC,GAAI,IAAI,EACZ,GAAI,CAAC,GAAI,IAAI,CACd,EACA,EAAG,CACF,EAAG,CAAC,GAAI,IAAI,EACZ,EAAG,CAAC,KAAM,IAAI,EACd,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,KAAM,IAAI,EACf,EAAG,CAAC,GAAI,EAAI,CACb,EACA,MAAO,CACN,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,CACd,EACA,OAAQ,CACP,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,EAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,CACd,EACA,OAAQ,CACP,EAAG,CAAC,KAAM,IAAI,EACd,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,EAAI,EACb,GAAI,CAAC,GAAI,EAAI,EACb,GAAI,CAAC,KAAM,IAAI,EACf,EAAG,CAAC,GAAI,IAAI,EACZ,EAAG,CAAC,KAAM,IAAI,CACf,EACA,eAAgB,CAAC,EACjB,KAAM,CACL,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,KAAM,IAAI,EACf,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,IAAI,EACb,GAAI,CAAC,GAAI,EAAI,EACb,GAAI,CAAC,GAAI,EAAI,CACd,CACD,EAGMC,EAAiD,CACtD,GAAI,GACJ,GAAI,GACJ,GAAI,GACJ,GAAI,EACJ,GAAI,GACJ,GAAI,GACJ,GAAI,EACL,EASA,SAASC,GAGP,CACD,IAAMC,EAAgC,CAAC,EACjCC,EAA8C,CAAC,EACrD,OAAW,CAACC,EAAY,CAACT,EAAGC,CAAC,CAAC,IAAK,OAAO,QAAQG,EAAgB,MAAM,EACvEA,EAAgB,cAAc,EAC7BC,EAAuB,OAAOI,CAAU,CAAC,CAC1C,EAAI,CAACN,EAAQH,CAAC,EAAGC,CAAC,EAEnB,OAAW,CAACS,EAAQC,CAAK,IAAK,OAAO,QAAQP,CAAe,EAG3D,OAAW,CAACQ,EAAM,CAACZ,EAAGC,CAAC,CAAC,IAAK,OAAO,QAAQU,CAAK,EAChDJ,EAAO,OAAOK,CAAI,CAAC,EAAIb,EAAEC,EAAGC,CAAC,EAC7BO,EAAS,OAAOI,CAAI,CAAC,EAAIF,EAG3B,MAAO,CAAE,OAAAH,EAAQ,SAAAC,CAAS,CAC3B,CAQO,GAAM,CAAE,OAAQK,EAAa,SAAUC,EAAY,EAAIR,EAAgB,EAGjES,GAAelB,EAAS,EAGrC,SAASmB,EAAMC,EAA4D,CAC1E,OAAOA,EAAQ,IAAI,CAAC,CAACjB,EAAGC,CAAC,IAAMF,EAAEC,EAAGC,CAAC,CAAC,CACvC,CAGA,SAASiB,EAAYC,EAAmBC,EAAaC,EAAyB,CAC7E,OAAOL,EAAM,CACZ,CAACd,EAAOiB,EAAWC,CAAG,EACtB,CAAClB,EAAOiB,EAAWC,CAAG,EACtB,CAAClB,EAAOiB,EAAWE,CAAM,EACzB,CAACnB,EAAOiB,EAAWE,CAAM,CAC1B,CAAC,CACF,CAGA,IAAMC,EAAyD,CAC9D,CAAC,KAAM,EAAI,EACX,CAAC,KAAM,IAAI,EACX,CAAC,KAAM,IAAI,CACZ,EAQaC,EAA6C,CACzD,CACC,GAAI,OACJ,MAAO,OACP,MAAO,OACP,OAAQP,EAAM,CACb,CAAC,GAAM,IAAI,EACX,CAAC,GAAM,IAAI,EACX,CAAC,GAAM,CAAG,CACX,CAAC,EACD,YAAajB,EAAE,GAAI,CAAC,EACpB,WAAY,OACb,EACA,CACC,GAAI,OACJ,MAAO,OACP,MAAO,QACP,OAAQiB,EAAM,CACb,CAAC,GAAM,IAAI,EACX,CAAC,GAAM,IAAI,EACX,CAAC,GAAM,IAAI,CACZ,CAAC,EACD,YAAajB,EAAE,GAAI,EAAE,EACrB,WAAY,OACb,EACA,CACC,GAAI,SACJ,MAAO,SACP,MAAO,QACP,OAAQmB,EAAY,IAAK,KAAM,IAAI,EACnC,YAAanB,EAAE,GAAI,EAAE,EACrB,WAAY,OACb,EACA,CACC,GAAI,IACJ,MAAO,IACP,MAAO,OACP,OAAQiB,EAAM,CACb,CAAC,GAAM,EAAI,EACX,CAAC,KAAM,IAAI,EACX,CAAC,GAAM,IAAI,EACX,CAAC,KAAM,IAAI,CACZ,CAAC,EACD,YAAajB,EAAE,GAAI,EAAE,EACrB,WAAY,KACb,EACA,CACC,GAAI,QACJ,MAAO,QACP,MAAO,MACP,OAAQiB,EAAM,CACb,CAAC,KAAM,EAAI,EACX,CAAC,KAAM,IAAI,EACX,CAAC,KAAM,IAAI,CACZ,CAAC,EACD,YAAajB,EAAE,GAAI,EAAE,EACrB,WAAY,OACb,EACA,CACC,GAAI,SACJ,MAAO,SACP,MAAO,QACP,OAAQiB,EAAMM,CAAY,EAC1B,YAAavB,EAAE,GAAI,EAAE,EACrB,WAAY,KACb,EACA,CACC,GAAI,SACJ,MAAO,SACP,MAAO,MACP,OAAQmB,EAAY,IAAK,KAAM,IAAI,EAGnC,YAAanB,EAAE,GAAI,EAAE,EACrB,WAAY,OACb,EACA,CACC,GAAI,eACJ,MAAO,eACP,MAAO,QACP,OAAQiB,EACPM,EAAa,IAAI,CAAC,CAACtB,EAAGC,CAAC,IAAM,CAACE,EAAQH,CAAC,EAAGC,CAAC,CAAqB,CACjE,EACA,YAAaF,EAAE,GAAI,EAAE,EACrB,WAAY,OACb,EACA,CACC,GAAI,OACJ,MAAO,OACP,MAAO,QACP,OAAQmB,EAAY,IAAK,KAAM,IAAI,EACnC,YAAanB,EAAE,GAAI,GAAG,EACtB,WAAY,QACb,CACD,EASayB,EAA0D,CACtE,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,CAAC,EACN,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,EAAG,EAAE,EACN,CAAC,EAAG,CAAC,EACL,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,CAAC,EACN,CAAC,EAAG,EAAE,EACN,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,EAAG,EAAE,EACN,CAAC,EAAG,EAAE,EACN,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,CAAC,EACN,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,CACR,EAgBMC,EAAYC,EAAc,EAEhC,SAASA,GAAwB,CAChC,IAAMC,EAAIxB,EAQJyB,EAA6B,CAClC,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,KAAM,CAAC,EACR,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,KAAM,EAAE,EACT,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,EAAE,EACP,CAAC,GAAI,GAAG,EACR,CAAC,GAAI,GAAG,CACT,EACMC,EAAiB,CAAC,KAAKC,EAAGF,EAAE,CAAC,CAAC,CAAC,EAAE,EAEvC,QAASG,EAAI,EAAGA,EAAI,EAAIH,EAAE,OAAQG,GAAK,EACtCF,EAAK,KAAK,KAAKC,EAAGF,EAAEG,CAAC,CAAC,CAAC,IAAID,EAAGF,EAAEG,EAAI,CAAC,CAAC,CAAC,IAAID,EAAGF,EAAEG,EAAI,CAAC,CAAC,CAAC,EAAE,EAG1DF,EAAK,KAAK,KAAKG,EAAIJ,EAAEA,EAAE,OAAS,CAAC,EAAGD,CAAC,CAAC,EAAE,EAExC,QAASI,EAAIH,EAAE,OAAS,EAAGG,GAAK,EAAGA,GAAK,EACvCF,EAAK,KAAK,KAAKG,EAAIJ,EAAEG,EAAI,CAAC,EAAGJ,CAAC,CAAC,IAAIK,EAAIJ,EAAEG,CAAC,EAAGJ,CAAC,CAAC,IAAIK,EAAIJ,EAAEG,EAAI,CAAC,EAAGJ,CAAC,CAAC,EAAE,EAEtE,OAAAE,EAAK,KAAK,GAAG,EACNA,EAAK,KAAK,GAAG,CACrB,CAEA,SAASC,EAAG,CAAC9B,EAAGC,CAAC,EAA6B,CAC7C,IAAMgC,EAAIlC,EAAEC,EAAGC,CAAC,EAChB,MAAO,GAAGgC,EAAE,CAAC,IAAIA,EAAE,CAAC,EACrB,CAEA,SAASD,EAAI,CAAChC,EAAGC,CAAC,EAAqB0B,EAAkC,CACxE,IAAMM,EAAIlC,EAAE4B,EAAE3B,CAAC,EAAGC,CAAC,EACnB,MAAO,GAAGgC,EAAE,CAAC,IAAIA,EAAE,CAAC,EACrB,CAEA,SAASC,EAAcC,EAAsB,CAC5C,OAAOA,EAAI,IAAKF,GAAM,GAAGA,EAAE,CAAC,IAAIA,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,CAChD,CAEA,SAASG,EAAQC,EAAWC,EAAmB,CAC9C,OAAOD,EAAIC,EAAI,GAAGD,CAAC,IAAIC,CAAC,GAAK,GAAGA,CAAC,IAAID,CAAC,EACvC,CAGA,SAASE,GAA6B,CACrC,OAAO7C,4BAA8B+B,CAAS,KAC/C,CAYA,SAASe,EACRC,EACAC,EACmB,CACnB,IAAMC,EAA0B,CAAC,EACjC,OAAW,CAAC,EAAGL,CAAC,IAAKd,EAAe,CACnC,IAAMoB,EAAK/B,EAAY,CAAC,EAClBgC,EAAKhC,EAAYyB,CAAC,EACxB,GAAI,CAACM,GAAM,CAACC,EAAI,SAIhB,GAHAF,EAAM,KACLjD,gCAAkCkD,EAAG,CAAC,OAAOA,EAAG,CAAC,OAAOC,EAAG,CAAC,OAAOA,EAAG,CAAC,KACxE,EACIJ,EAAe,IAAIL,EAAQ,EAAGE,CAAC,CAAC,EAAG,CACtCK,EAAM,KACLjD,mCAAqCkD,EAAG,CAAC,OAAOA,EAAG,CAAC,OAAOC,EAAG,CAAC,OAAOA,EAAG,CAAC,KAC3E,EACA,QACD,CACA,IAAMC,EAAM,CAAE,GAAIF,EAAG,EAAIC,EAAG,GAAK,EAAG,GAAID,EAAG,EAAIC,EAAG,GAAK,CAAE,EACrDH,EAAY,IAAI,CAAC,GACpBC,EAAM,KACLjD,6BAA+BkD,EAAG,CAAC,OAAOA,EAAG,CAAC,OAAOE,EAAI,CAAC,OAAOA,EAAI,CAAC,KACvE,EAEGJ,EAAY,IAAIJ,CAAC,GACpBK,EAAM,KACLjD,6BAA+BmD,EAAG,CAAC,OAAOA,EAAG,CAAC,OAAOC,EAAI,CAAC,OAAOA,EAAI,CAAC,KACvE,CAEF,CACA,OAAOH,CACR,CAGA,SAASI,EAAsBd,EAAUI,EAAUC,EAAiB,CACnE,IAAMU,EAAKV,EAAE,EAAID,EAAE,EACbY,EAAKX,EAAE,EAAID,EAAE,EACba,EAAOF,EAAKA,EAAKC,EAAKA,EAC5B,GAAIC,IAAS,EAAG,OAAOb,EACvB,IAAMc,EAAI,KAAK,IACd,EACA,KAAK,IAAI,IAAKlB,EAAE,EAAII,EAAE,GAAKW,GAAMf,EAAE,EAAII,EAAE,GAAKY,GAAMC,CAAI,CACzD,EACA,MAAO,CAAE,EAAGb,EAAE,EAAIc,EAAIH,EAAI,EAAGX,EAAE,EAAIc,EAAIF,CAAG,CAC3C,CAGA,SAASG,EAAsBnB,EAAUoB,EAA+B,CACvE,IAAIC,EAAOD,EAAK,CAAC,EACbE,EAAW,OAAO,kBACtB,QAASxB,EAAI,EAAGA,EAAIsB,EAAK,OAAQtB,IAAK,CACrC,IAAMyB,EAAIT,EAAsBd,EAAGoB,EAAKtB,CAAC,EAAGsB,GAAMtB,EAAI,GAAKsB,EAAK,MAAM,CAAC,EACjEI,GAAKD,EAAE,EAAIvB,EAAE,IAAM,GAAKuB,EAAE,EAAIvB,EAAE,IAAM,EACxCwB,EAAIF,IACPA,EAAWE,EACXH,EAAOE,EAET,CACA,OAAOF,CACR,CAQA,SAASI,EAAcC,EAAmD,CACzE,OAAOpC,EAAgB,IAAKqC,GAAM,CACjC,IAAMC,EAAYF,EAAQ,IAAIC,EAAE,EAAE,EAC5BE,EAAM,gBAAgBF,EAAE,KAAK,GAAGC,EAAY,WAAa,EAAE,GAC3DE,EAAOX,EAAsBQ,EAAE,YAAaA,EAAE,MAAM,EAC1D,OAAOlE;AAAA,gCACuBkE,EAAE,YAAY,CAAC,OAAOA,EAAE,YAAY,CAAC,OAAOG,EAAK,CAAC,OAAOA,EAAK,CAAC;AAAA,oBAC3ED,CAAG,WAAW5B,EAAc0B,EAAE,MAAM,CAAC,WAAWA,EAAE,KAAK,KAAKC,EAAY,UAAY,MAAM;AAAA,qCACzED,EAAE,YAAY,CAAC,MAAMA,EAAE,YAAY,CAAC,gBAAgBA,EAAE,UAAU,gCAAgCA,EAAE,KAAK;AAAA,OAE3I,CAAC,CACF,CAOA,SAASI,EACRtB,EACAuB,EACmB,CACnB,IAAMC,EAAwB,CAAC,EAC/B,OAAW,CAACtD,EAAMqB,CAAC,IAAK,OAAO,QAAQpB,CAAW,EAAG,CACpD,IAAMsD,EAAM,OAAOvD,CAAI,EACvB,GAAI,CAAC8B,EAAY,IAAIyB,CAAG,EAAG,SAC3B,IAAMC,EAAQH,EAAO,IAAIE,CAAG,EAC5BD,EAAI,KACHxE,4BAA8BuC,EAAE,CAAC,MAAMA,EAAE,CAAC,qDAAqDkC,CAAG,GAAGC,EAAQ1E,WAAa0E,CAAK,WAAa3E,CAAO,SACpJ,CACD,CACA,OAAOyE,CACR,CAUO,SAASG,EAAWhC,EAAWC,EAAmB,CACxD,OAAOF,EAAQC,EAAGC,CAAC,CACpB,CAEO,IAAMgC,EAAoB,OAAOzE,CAAM,IAAIC,CAAM,GASjD,SAASyE,EACfC,EACiB,CACjB,OAAO9E;AAAA,IACJ6C,EAAW,CAAC;AAAA,IACZC,EAAegC,EAAM,eAAgBA,EAAM,WAAW,CAAC;AAAA,IACvDd,EAAcc,EAAM,cAAc,CAAC;AAAA,IACnCR,EAAkBQ,EAAM,YAAaA,EAAM,UAAU,CAAC;AAAA,EAE1D,CCpnBA,IAAMC,EAAkB,YAkDxB,SAASC,GAAaC,EAAsB,CAC3C,OACCA,EAAG,WAAa,UAAYA,EAAG,aAAa,MAAM,IAAM,kBAE1D,CA2CO,IAAMC,EAAN,KAAsE,CAG5E,YAAYC,EAAgB,CAC3B,KAAK,KAAOA,EACZA,EAAK,cAAc,IAAI,CACxB,CAEA,eAAgB,CAGf,GAAI,KAAK,KAAK,MAAQ,KAAM,OAE5B,IAAMC,EAAS,KAAK,KAAK,EACrBA,IAAW,SAEf,KAAK,KAAK,KAAOA,EACjB,KAAK,KAAK,cAAc,EACzB,CAKQ,MAAsB,CAC7B,IAAMC,EAAS,KAAK,iBAAiB,EACrC,OAAOA,EAAS,KAAK,MAAMA,EAAO,WAAW,EAAI,MAClD,CAKQ,kBAAmC,CAC1C,QAAWC,KAAS,MAAM,KAAK,KAAK,KAAK,QAAQ,EAChD,GAAIN,GAAaM,CAAK,GAAKA,EAAM,UAAU,SAASC,CAAe,EAClE,OAAOD,EAGT,OAAO,IACR,CAEQ,MAAME,EAAoC,CACjD,GAAKA,GAAM,KAAK,EAChB,GAAI,CACH,OAAO,KAAK,MAAMA,CAAI,CACvB,MAAQ,CAGP,MACD,CACD,CACD,ECnJO,SAASC,EAAWC,EAAmB,CAC7C,OAAKA,EACEA,EAAE,OAAO,CAAC,EAAE,YAAY,EAAIA,EAAE,MAAM,CAAC,EAAE,YAAY,EAD3C,EAEhB,CLaO,IAAMC,EAAN,cAA4BC,EAAW,CAM7C,aAAc,CACb,MAAM,EAQP,UAAyC,KAJxC,IAAIC,EAAqB,IAAI,CAC9B,CAKA,QAAS,CACR,IAAMC,EAAI,KAAK,KACf,GAAI,CAACA,EACJ,OAAOC,iEAER,IAAMC,EAAiB,IAAI,KACzBF,EAAE,SAAW,CAAC,GACb,OAAQG,GAAMA,EAAE,OAAO,EACvB,IAAKA,GAAMA,EAAE,EAAuB,CACvC,EACMC,EAAc,IAAI,KACtBJ,EAAE,OAAS,CAAC,GAAG,IAAKK,GAAMA,EAAE,IAAI,EAAE,OAAQC,GAAmBA,GAAK,IAAI,CACxE,EACMC,EAAiB,IAAI,KACzBP,EAAE,UAAY,CAAC,GAAG,IAAKG,GAAMK,EAAWL,EAAE,MAAOA,EAAE,KAAK,CAAC,CAC3D,EACMM,EAAa,KAAK,gBAAgBT,EAAE,OAAS,CAAC,CAAC,EAErD,OAAOC,2EACJD,EAAE,KACCC,2BAA8BD,EAAE,IAAI,GAAGA,EAAE,QAAUC,cAAiBD,EAAE,OAAO,GAAKU,CAAO,SACzFA,CACJ,8CAA8CC,CAAiB,0ZAA0ZC,EAAmB,CAC1e,eAAAV,EACA,eAAAK,EACA,YAAAH,EACA,WAAAK,CACD,CAAC,CAAC,UAAU,KAAK,cAAcT,CAAC,CAAC,cACrC,CAEQ,gBAAgBa,EAA8C,CACrE,IAAMC,EAAS,IAAI,IACnB,QAAWT,KAAKQ,EAAO,CACtB,GAAIR,EAAE,MAAQ,KAAM,SACpB,IAAMU,EAAkB,CAAC,QAAQV,EAAE,IAAI,EAAE,EACrCA,EAAE,MAAQ,OAAMU,EAAM,CAAC,GAAK,IAAIV,EAAE,IAAI,IACtCA,EAAE,UAAUU,EAAM,KAAKV,EAAE,QAAQ,EACrC,IAAMW,EAASX,EAAE,OAASY,EAAWZ,EAAE,MAAM,EAAI,GAC3Ca,EAAQF,EAAUG,EAAaH,CAAM,GAAKA,EAAU,GACtDE,GAAOH,EAAM,KAAK,GAAGG,CAAK,IAAIb,EAAE,MAAQ,EAAE,GAAG,KAAK,CAAC,EACvDS,EAAO,IAAIT,EAAE,KAAMU,EAAM,KAAK,QAAK,CAAC,CACrC,CACA,OAAOD,CACR,CAEQ,cAAcd,EAA8B,CACnD,IAAMoB,EAAkD,CACvD,CAAE,MAAO,OAAQ,MAAOpB,EAAE,IAAK,EAC/B,CAAE,MAAO,WAAY,MAAOA,EAAE,QAAS,EACvC,CAAE,MAAO,YAAa,MAAOA,EAAE,SAAU,EACzC,CAAE,MAAO,UAAW,MAAOA,EAAE,OAAQ,EACrC,CAAE,MAAO,aAAc,MAAOA,EAAE,UAAW,CAC5C,EACMqB,EAAKrB,EAAE,iBACb,OAAOC,4CAA+CmB,EAAM,IAAKE,GAC9DA,EAAE,MACCrB,4BAA+BqB,EAAE,KAAK,mBAAmBA,EAAE,KAAK,kBAChEZ,CACJ,CAAC,SACDW,GAAI,KACDpB,qBAAwBoB,EAAG,IAAI,IAC/BA,EAAG,OAAO,OACPpB,yBAA4BoB,EAAG,MAAM,KAAK,IAAI,CAAC,WAC/CX,CACJ,OACCA,CACJ,IACCV,EAAE,WAAaA,EAAE,QACdC,wBAA2BD,EAAE,UAAYC,6CAAgDD,EAAE,SAAS,UAAYU,CAAO,IAAIV,EAAE,QAAUC,8CAAiDD,EAAE,OAAO,UAAYU,CAAO,SACpNA,CACJ,odACF,CACD,EAzFab,EACL,OAAS,CACf0B,EACAC,k4GACD,EAWAC,EAAA,CADCC,GAAS,CAAE,UAAW,EAAM,CAAC,GAdlB7B,EAeZ,oBAfYA,EAAN4B,EAAA,CADNE,GAAc,gBAAgB,GAClB9B",
6
+ "names": ["css", "html", "LitElement", "nothing", "customElement", "property", "PLANET_GLYPH", "SIGNS_ORDER", "RASHI_KEYS", "s", "css", "baseStyles", "nothing", "svg", "UNIT", "PAD", "VIEW_W", "VIEW_H", "g", "x", "y", "AXIS", "mirrorX", "GATES_BY_CENTER", "SPLEEN_TO_SOLAR_PLEXUS", "buildGatePoints", "points", "centerOf", "spleenGate", "center", "gates", "gate", "GATE_POINTS", "GATE_CENTER", "CHART_AXIS_X", "shape", "corners", "squareShape", "halfWidth", "top", "bottom", "SPLEEN_SHAPE", "CENTER_GEOMETRY", "CHANNEL_PAIRS", "BODY_PATH", "buildBodyPath", "m", "r", "segs", "pt", "i", "ptm", "p", "polygonPoints", "pts", "pairKey", "a", "b", "renderBody", "renderChannels", "activeChannels", "activeGates", "lines", "pa", "pb", "mid", "closestPointOnSegment", "dx", "dy", "len2", "t", "closestPointOnPolygon", "poly", "best", "bestDist", "q", "d", "renderCenters", "defined", "c", "isDefined", "cls", "edge", "renderGateNumbers", "titles", "out", "num", "title", "channelKey", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "input", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "ROXY_DATA_CLASS", "text", "capitalize", "s", "RoxyBodygraph", "LitElement", "MarkupDataController", "d", "html", "definedCenters", "c", "activeGates", "g", "n", "activeChannels", "channelKey", "gateTitles", "nothing", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "gates", "titles", "parts", "planet", "capitalize", "glyph", "PLANET_GLYPH", "facts", "ic", "f", "baseStyles", "css", "__decorateClass", "property", "customElement"]
7
7
  }
@@ -1,2 +1,2 @@
1
- var x=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var d=(a,e,r,o)=>{for(var t=o>1?void 0:o?y(e,r):e,i=a.length-1,c;i>=0;i--)(c=a[i])&&(t=(o?c(e,r,t):c(t))||t);return o&&t&&x(e,r,t),t};import{css as w,html as n,LitElement as N,nothing as p}from"lit";import{customElement as C,property as L}from"lit/decorators.js";var u={Sun:"\u2609",Moon:"\u263D",Mercury:"\u263F",Venus:"\u2640",Earth:"\u2641",Mars:"\u2642",Jupiter:"\u2643",Saturn:"\u2644",Uranus:"\u2645",Neptune:"\u2646",Pluto:"\u2647",Rahu:"\u260A",Ketu:"\u260B",Ascendant:"Asc",Lagna:"La",NorthNode:"\u260A",SouthNode:"\u260B","North node":"\u260A","South node":"\u260B",Chiron:"\u26B7",Lilith:"\u26B8","Black moon lilith":"\u26B8"};var f=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],T=f.map(a=>a.toLowerCase());import{css as b}from"lit";var m=b`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;var v="roxy-data";function S(a){return a.nodeName==="SCRIPT"&&a.getAttribute("type")==="application/json"}var l=class{constructor(e){this.host=e,e.addController(this)}hostConnected(){if(this.host.data!=null)return;let e=this.read();e!==void 0&&(this.host.data=e,this.host.requestUpdate())}read(){let e=this.findInlineScript();return e?this.parse(e.textContent):void 0}findInlineScript(){for(let e of Array.from(this.host.children))if(S(e)&&e.classList.contains(v))return e;return null}parse(e){if(e?.trim())try{return JSON.parse(e)}catch{return}}};function g(a){return a?a.charAt(0).toUpperCase()+a.slice(1).toLowerCase():""}function h(a){try{let e=new Date(a);return Number.isNaN(e.getTime())?a:e.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}catch{return a}}var s=class extends N{constructor(){super();this.data=null;new l(this)}isCurrent(r){let o=Date.now(),t=Date.parse(r.start),i=Date.parse(r.end);return Number.isNaN(t)||Number.isNaN(i)?!1:o>=t&&o<i}renderTile(r){let o=r.effect==="Good"?"good":r.effect==="Bad"?"bad":"neutral",t=this.isCurrent(r),i=u[g(r.lord)]??"",c=`${h(r.start)} - ${h(r.end)}`;return n`<div class="cho-tile ${o}${t?" now":""}" role="listitem" aria-current="${t?"time":"false"}"><span class="tile-name">${r.name}${t?n`<span class="now-badge">Now</span>`:p} </span><span class="tile-time" aria-label="Time range">${c}</span> <span class="tile-lord">${i?n`<span aria-hidden="true">${i}</span>`:p} ${r.lord}</span></div>`}render(){if(!this.data)return n`<div class="roxy-empty" role="status">No choghadiya data</div>`;let{date:r,dayChoghadiya:o,nightChoghadiya:t}=this.data;return n`<div class="wrap"><div class="header"><h2 class="title">Choghadiya</h2>${r?n`<p class="subtitle">${r}</p>`:p}</div><div class="cho-grid"><section class="period-col" aria-label="Day muhurta periods"><h3 class="period-heading">Day</h3><div role="list" aria-label="Daytime choghadiya">${o&&o.length>0?o.map(i=>this.renderTile(i)):n`<p class="roxy-empty" role="status">No daytime periods</p>`}</div></section><section class="period-col" aria-label="Night muhurta periods"><h3 class="period-heading">Night</h3><div role="list" aria-label="Nighttime choghadiya">${t&&t.length>0?t.map(i=>this.renderTile(i)):n`<p class="roxy-empty" role="status">No nighttime periods</p>`}</div></section></div></div>`}};s.styles=[m,w`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.header{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.cho-grid{gap:var(--roxy-space-md,1rem);grid-template-columns:1fr;display:grid}@media (width>=720px){.cho-grid{grid-template-columns:1fr 1fr}}.period-col{gap:var(--roxy-space-xs,.25rem);display:grid}.period-heading{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a)}.cho-tile{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:1fr auto;align-items:center;gap:.25em .75em;padding:.55em .85em;display:grid}.cho-tile.good{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-success,#22c55e) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.bad{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-danger,#ef4444) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.neutral{color:var(--roxy-fg,#0a0a0a);background:0 0}.cho-tile.now{outline:2px solid var(--roxy-accent,#f59e0b);outline-offset:1px;box-shadow:0 0 0 4px color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.tile-name{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);grid-column:1}.tile-time{font-size:var(--roxy-text-xs,.75rem);opacity:.8;white-space:nowrap;text-align:right;grid-area:1/2/3;align-self:center}.tile-lord{font-size:var(--roxy-text-sm,.875rem);opacity:.85;grid-column:1;align-items:center;gap:.25em;display:flex}`],d([L({attribute:!1})],s.prototype,"data",2),s=d([C("roxy-choghadiya-grid")],s);export{s as RoxyChoghadiyaGrid};
1
+ var x=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var d=(a,e,r,o)=>{for(var t=o>1?void 0:o?y(e,r):e,i=a.length-1,c;i>=0;i--)(c=a[i])&&(t=(o?c(e,r,t):c(t))||t);return o&&t&&x(e,r,t),t};import{css as w,html as n,LitElement as N,nothing as p}from"lit";import{customElement as C,property as L}from"lit/decorators.js";var u={Sun:"\u2609",Moon:"\u263D",Mercury:"\u263F",Venus:"\u2640",Earth:"\u2641",Mars:"\u2642",Jupiter:"\u2643",Saturn:"\u2644",Uranus:"\u2645",Neptune:"\u2646",Pluto:"\u2647",Rahu:"\u260A",Ketu:"\u260B",Ascendant:"Asc",Lagna:"La",NorthNode:"\u260A",SouthNode:"\u260B","North node":"\u260A","South node":"\u260B",Chiron:"\u26B7",Lilith:"\u26B8","Black moon lilith":"\u26B8"};var f=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],R=f.map(a=>a.toLowerCase());import{css as b}from"lit";var g=b`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;var v="roxy-data";function S(a){return a.nodeName==="SCRIPT"&&a.getAttribute("type")==="application/json"}var l=class{constructor(e){this.host=e,e.addController(this)}hostConnected(){if(this.host.data!=null)return;let e=this.read();e!==void 0&&(this.host.data=e,this.host.requestUpdate())}read(){let e=this.findInlineScript();return e?this.parse(e.textContent):void 0}findInlineScript(){for(let e of Array.from(this.host.children))if(S(e)&&e.classList.contains(v))return e;return null}parse(e){if(e?.trim())try{return JSON.parse(e)}catch{return}}};function m(a){return a?a.charAt(0).toUpperCase()+a.slice(1).toLowerCase():""}function h(a){try{let e=new Date(a);return Number.isNaN(e.getTime())?a:e.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}catch{return a}}var s=class extends N{constructor(){super();this.data=null;new l(this)}isCurrent(r){let o=Date.now(),t=Date.parse(r.start),i=Date.parse(r.end);return Number.isNaN(t)||Number.isNaN(i)?!1:o>=t&&o<i}renderTile(r){let o=r.effect==="Good"?"good":r.effect==="Bad"?"bad":"neutral",t=this.isCurrent(r),i=u[m(r.lord)]??"",c=`${h(r.start)} - ${h(r.end)}`;return n`<div class="cho-tile ${o}${t?" now":""}" role="listitem" aria-current="${t?"time":"false"}"><span class="tile-name">${r.name}${t?n`<span class="now-badge">Now</span>`:p} </span><span class="tile-time" aria-label="Time range">${c}</span> <span class="tile-lord">${i?n`<span aria-hidden="true">${i}</span>`:p} ${r.lord}</span></div>`}render(){if(!this.data)return n`<div class="roxy-empty" role="status">No choghadiya data</div>`;let{date:r,dayChoghadiya:o,nightChoghadiya:t}=this.data;return n`<div class="wrap"><div class="header"><h2 class="title">Choghadiya</h2>${r?n`<p class="subtitle">${r}</p>`:p}</div><div class="cho-grid"><section class="period-col" aria-label="Day muhurta periods"><h3 class="period-heading">Day</h3><div role="list" aria-label="Daytime choghadiya">${o&&o.length>0?o.map(i=>this.renderTile(i)):n`<p class="roxy-empty" role="status">No daytime periods</p>`}</div></section><section class="period-col" aria-label="Night muhurta periods"><h3 class="period-heading">Night</h3><div role="list" aria-label="Nighttime choghadiya">${t&&t.length>0?t.map(i=>this.renderTile(i)):n`<p class="roxy-empty" role="status">No nighttime periods</p>`}</div></section></div></div>`}};s.styles=[g,w`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.header{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.cho-grid{gap:var(--roxy-space-md,1rem);grid-template-columns:1fr;display:grid}@media (width>=720px){.cho-grid{grid-template-columns:1fr 1fr}}.period-col{gap:var(--roxy-space-xs,.25rem);display:grid}.period-heading{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a)}.cho-tile{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:1fr auto;align-items:center;gap:.25em .75em;padding:.55em .85em;display:grid}.cho-tile.good{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-success,#22c55e) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.bad{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-danger,#ef4444) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.neutral{color:var(--roxy-fg,#0a0a0a);background:0 0}.cho-tile.now{outline:2px solid var(--roxy-accent,#f59e0b);outline-offset:1px;box-shadow:0 0 0 4px color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.tile-name{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);grid-column:1}.tile-time{font-size:var(--roxy-text-xs,.75rem);opacity:.8;white-space:nowrap;text-align:right;grid-area:1/2/3;align-self:center}.tile-lord{font-size:var(--roxy-text-sm,.875rem);opacity:.85;grid-column:1;align-items:center;gap:.25em;display:flex}`],d([L({attribute:!1})],s.prototype,"data",2),s=d([C("roxy-choghadiya-grid")],s);export{s as RoxyChoghadiyaGrid};
2
2
  //# sourceMappingURL=choghadiya-grid.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/choghadiya-grid.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts", "../../src/utils/markup-data.ts", "../../src/utils/string.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { PLANET_GLYPH } from '../tokens/index.js';\nimport type { GetChoghadiyaResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize } from '../utils/string.js';\n\ntype ChoghadiyaPeriod = GetChoghadiyaResponse['dayChoghadiya'][number];\n\n/**\n * Format an ISO 8601 datetime string to a short local time (HH:MM).\n * Falls back to the raw string when parsing fails.\n */\nfunction fmtTime(iso: string): string {\n\ttry {\n\t\tconst d = new Date(iso);\n\t\tif (Number.isNaN(d.getTime())) return iso;\n\t\treturn d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n\t} catch {\n\t\treturn iso;\n\t}\n}\n\n/**\n * Choghadiya muhurta grid. Accepts a GetChoghadiyaResponse and renders\n * 8 daytime and 8 nighttime muhurta tiles in a two-column responsive layout.\n * Good periods are highlighted in green, Bad periods in red.\n */\n@customElement('roxy-choghadiya-grid')\nexport class RoxyChoghadiyaGrid extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.header{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.cho-grid{gap:var(--roxy-space-md,1rem);grid-template-columns:1fr;display:grid}@media (width>=720px){.cho-grid{grid-template-columns:1fr 1fr}}.period-col{gap:var(--roxy-space-xs,.25rem);display:grid}.period-heading{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a)}.cho-tile{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:1fr auto;align-items:center;gap:.25em .75em;padding:.55em .85em;display:grid}.cho-tile.good{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-success,#22c55e) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.bad{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-danger,#ef4444) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.neutral{color:var(--roxy-fg,#0a0a0a);background:0 0}.cho-tile.now{outline:2px solid var(--roxy-accent,#f59e0b);outline-offset:1px;box-shadow:0 0 0 4px color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.tile-name{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);grid-column:1}.tile-time{font-size:var(--roxy-text-xs,.75rem);opacity:.8;white-space:nowrap;text-align:right;grid-area:1/2/3;align-self:center}.tile-lord{font-size:var(--roxy-text-sm,.875rem);opacity:.85;grid-column:1;align-items:center;gap:.25em;display:flex}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GetChoghadiyaResponse | null = null;\n\n\t/**\n\t * True when the current wall-clock time falls inside this period. Both\n\t * `start` and `end` are ISO 8601 with timezone, so the comparison is\n\t * timezone-aware via the host's `Date` parsing.\n\t */\n\tprivate isCurrent(period: ChoghadiyaPeriod): boolean {\n\t\tconst now = Date.now();\n\t\tconst start = Date.parse(period.start);\n\t\tconst end = Date.parse(period.end);\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) return false;\n\t\treturn now >= start && now < end;\n\t}\n\n\tprivate renderTile(period: ChoghadiyaPeriod) {\n\t\tconst effectClass =\n\t\t\tperiod.effect === 'Good'\n\t\t\t\t? 'good'\n\t\t\t\t: period.effect === 'Bad'\n\t\t\t\t\t? 'bad'\n\t\t\t\t\t: 'neutral';\n\t\tconst current = this.isCurrent(period);\n\t\tconst lordGlyph = PLANET_GLYPH[capitalize(period.lord)] ?? '';\n\t\tconst timeRange = `${fmtTime(period.start)} - ${fmtTime(period.end)}`;\n\t\treturn html`<div class=\"cho-tile ${effectClass}${current ? ' now' : ''}\" role=\"listitem\" aria-current=\"${current ? 'time' : 'false'}\"><span class=\"tile-name\">${period.name}${current ? html`<span class=\"now-badge\">Now</span>` : nothing} </span><span class=\"tile-time\" aria-label=\"Time range\">${timeRange}</span> <span class=\"tile-lord\">${lordGlyph ? html`<span aria-hidden=\"true\">${lordGlyph}</span>` : nothing} ${period.lord}</span></div>`;\n\t}\n\n\trender() {\n\t\tif (!this.data)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No choghadiya data</div>`;\n\n\t\tconst { date, dayChoghadiya, nightChoghadiya } = this.data;\n\n\t\treturn html`<div class=\"wrap\"><div class=\"header\"><h2 class=\"title\">Choghadiya</h2>${date ? html`<p class=\"subtitle\">${date}</p>` : nothing}</div><div class=\"cho-grid\"><section class=\"period-col\" aria-label=\"Day muhurta periods\"><h3 class=\"period-heading\">Day</h3><div role=\"list\" aria-label=\"Daytime choghadiya\">${\n\t\t\t\t\t\t\tdayChoghadiya && dayChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? dayChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">No daytime periods</p>`\n\t\t\t\t\t\t}</div></section><section class=\"period-col\" aria-label=\"Night muhurta periods\"><h3 class=\"period-heading\">Night</h3><div role=\"list\" aria-label=\"Nighttime choghadiya\">${\n\t\t\t\t\t\t\tnightChoghadiya && nightChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? nightChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">No nighttime periods</p>`\n\t\t\t\t\t\t}</div></section></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-choghadiya-grid': RoxyChoghadiyaGrid;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\n};\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n"],
5
- "mappings": "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",
6
- "names": ["css", "html", "LitElement", "nothing", "customElement", "property", "PLANET_GLYPH", "SIGNS_ORDER", "RASHI_KEYS", "s", "css", "baseStyles", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "text", "capitalize", "s", "fmtTime", "iso", "d", "RoxyChoghadiyaGrid", "LitElement", "MarkupDataController", "period", "now", "start", "end", "effectClass", "current", "lordGlyph", "PLANET_GLYPH", "capitalize", "timeRange", "html", "nothing", "date", "dayChoghadiya", "nightChoghadiya", "p", "baseStyles", "css", "__decorateClass", "property", "customElement"]
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { PLANET_GLYPH } from '../tokens/index.js';\nimport type { GetChoghadiyaResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize } from '../utils/string.js';\n\ntype ChoghadiyaPeriod = GetChoghadiyaResponse['dayChoghadiya'][number];\n\n/**\n * Format an ISO 8601 datetime string to a short local time (HH:MM).\n * Falls back to the raw string when parsing fails.\n */\nfunction fmtTime(iso: string): string {\n\ttry {\n\t\tconst d = new Date(iso);\n\t\tif (Number.isNaN(d.getTime())) return iso;\n\t\treturn d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n\t} catch {\n\t\treturn iso;\n\t}\n}\n\n/**\n * Choghadiya muhurta grid. Accepts a GetChoghadiyaResponse and renders\n * 8 daytime and 8 nighttime muhurta tiles in a two-column responsive layout.\n * Good periods are highlighted in green, Bad periods in red.\n */\n@customElement('roxy-choghadiya-grid')\nexport class RoxyChoghadiyaGrid extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.header{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.cho-grid{gap:var(--roxy-space-md,1rem);grid-template-columns:1fr;display:grid}@media (width>=720px){.cho-grid{grid-template-columns:1fr 1fr}}.period-col{gap:var(--roxy-space-xs,.25rem);display:grid}.period-heading{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a)}.cho-tile{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:1fr auto;align-items:center;gap:.25em .75em;padding:.55em .85em;display:grid}.cho-tile.good{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-success,#22c55e) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.bad{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-danger,#ef4444) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.neutral{color:var(--roxy-fg,#0a0a0a);background:0 0}.cho-tile.now{outline:2px solid var(--roxy-accent,#f59e0b);outline-offset:1px;box-shadow:0 0 0 4px color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.tile-name{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);grid-column:1}.tile-time{font-size:var(--roxy-text-xs,.75rem);opacity:.8;white-space:nowrap;text-align:right;grid-area:1/2/3;align-self:center}.tile-lord{font-size:var(--roxy-text-sm,.875rem);opacity:.85;grid-column:1;align-items:center;gap:.25em;display:flex}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GetChoghadiyaResponse | null = null;\n\n\t/**\n\t * True when the current wall-clock time falls inside this period. Both\n\t * `start` and `end` are ISO 8601 with timezone, so the comparison is\n\t * timezone-aware via the host's `Date` parsing.\n\t */\n\tprivate isCurrent(period: ChoghadiyaPeriod): boolean {\n\t\tconst now = Date.now();\n\t\tconst start = Date.parse(period.start);\n\t\tconst end = Date.parse(period.end);\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) return false;\n\t\treturn now >= start && now < end;\n\t}\n\n\tprivate renderTile(period: ChoghadiyaPeriod) {\n\t\tconst effectClass =\n\t\t\tperiod.effect === 'Good'\n\t\t\t\t? 'good'\n\t\t\t\t: period.effect === 'Bad'\n\t\t\t\t\t? 'bad'\n\t\t\t\t\t: 'neutral';\n\t\tconst current = this.isCurrent(period);\n\t\tconst lordGlyph = PLANET_GLYPH[capitalize(period.lord)] ?? '';\n\t\tconst timeRange = `${fmtTime(period.start)} - ${fmtTime(period.end)}`;\n\t\treturn html`<div class=\"cho-tile ${effectClass}${current ? ' now' : ''}\" role=\"listitem\" aria-current=\"${current ? 'time' : 'false'}\"><span class=\"tile-name\">${period.name}${current ? html`<span class=\"now-badge\">Now</span>` : nothing} </span><span class=\"tile-time\" aria-label=\"Time range\">${timeRange}</span> <span class=\"tile-lord\">${lordGlyph ? html`<span aria-hidden=\"true\">${lordGlyph}</span>` : nothing} ${period.lord}</span></div>`;\n\t}\n\n\trender() {\n\t\tif (!this.data)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No choghadiya data</div>`;\n\n\t\tconst { date, dayChoghadiya, nightChoghadiya } = this.data;\n\n\t\treturn html`<div class=\"wrap\"><div class=\"header\"><h2 class=\"title\">Choghadiya</h2>${date ? html`<p class=\"subtitle\">${date}</p>` : nothing}</div><div class=\"cho-grid\"><section class=\"period-col\" aria-label=\"Day muhurta periods\"><h3 class=\"period-heading\">Day</h3><div role=\"list\" aria-label=\"Daytime choghadiya\">${\n\t\t\t\t\t\t\tdayChoghadiya && dayChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? dayChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">No daytime periods</p>`\n\t\t\t\t\t\t}</div></section><section class=\"period-col\" aria-label=\"Night muhurta periods\"><h3 class=\"period-heading\">Night</h3><div role=\"list\" aria-label=\"Nighttime choghadiya\">${\n\t\t\t\t\t\t\tnightChoghadiya && nightChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? nightChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">No nighttime periods</p>`\n\t\t\t\t\t\t}</div></section></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-choghadiya-grid': RoxyChoghadiyaGrid;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\n};\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * Serialize a RoxyAPI response for embedding inside an inline `<script type=\"application/json\" class=\"roxy-data\">`. This is the safe writer counterpart to {@link MarkupDataController}, the reader: server-rendered and cached consumers (WordPress, JSX SSR, static HTML) emit the script with this, and the component hydrates `data` from it on connect.\n *\n * @remarks\n * Use this instead of a bare `JSON.stringify`. A raw stringify of a response that contains the literal text `</script>` (common in long interpretation prose) closes the script element early, corrupting the page and creating an HTML-injection vector. This escapes the script-unsafe characters to their `\\uXXXX` JSON escapes, which `JSON.parse` restores to the original characters, so the value the component receives is identical to the response you passed.\n *\n * `<` is the load-bearing escape (it defeats `</script>` and `<!--`). `>` and `&` are escaped for defence in depth, and U+2028 / U+2029 because they are valid in JSON yet are line terminators in a `<script>` context and break some parsers. The escapes introduce only `\\uXXXX` sequences, so the replacements never feed each other and order is irrelevant.\n *\n * Pass the unwrapped RoxyAPI response, the same shape you would assign to `element.data`. Do not pass the SDK envelope (`{ data, error, request, response }`).\n *\n * @example\n * ```ts\n * import { serializeRoxyData } from '@roxyapi/ui';\n *\n * const { data } = await roxy.dreams.getDreamSymbol({ path: { id: 'water' } });\n * const html = `<roxy-dream-card><script type=\"application/json\" class=\"roxy-data\">${serializeRoxyData(data)}</script></roxy-dream-card>`;\n * ```\n */\nexport function serializeRoxyData(data: unknown): string {\n\treturn JSON.stringify(data)\n\t\t.replace(/</g, '\\\\u003c')\n\t\t.replace(/>/g, '\\\\u003e')\n\t\t.replace(/&/g, '\\\\u0026')\n\t\t.replace(/\\u2028/g, '\\\\u2028')\n\t\t.replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Build the complete `<script type=\"application/json\" class=\"roxy-data\">\u2026</script>` element a server nests inside a `roxy-*` component for the no-JavaScript hydration path. The payload is escaped via {@link serializeRoxyData}, so it is safe to drop straight into HTML output.\n *\n * @remarks\n * The element carries both `type=\"application/json\"` and `class=\"roxy-data\"` because {@link MarkupDataController} reads only a direct-child script that has both. Emit one of these inside the target component; the JavaScript `data` property still wins if it is later assigned.\n *\n * @example\n * ```ts\n * import { roxyDataScript } from '@roxyapi/ui';\n *\n * const { data } = await roxy.crystals.getCrystal({ path: { id: 'amethyst' } });\n * const html = `<roxy-crystal-grid>${roxyDataScript(data)}</roxy-crystal-grid>`;\n * ```\n */\nexport function roxyDataScript(data: unknown): string {\n\treturn `<script type=\"application/json\" class=\"${ROXY_DATA_CLASS}\">${serializeRoxyData(data)}</script>`;\n}\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["css", "html", "LitElement", "nothing", "customElement", "property", "PLANET_GLYPH", "SIGNS_ORDER", "RASHI_KEYS", "s", "css", "baseStyles", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "ROXY_DATA_CLASS", "text", "capitalize", "s", "fmtTime", "iso", "d", "RoxyChoghadiyaGrid", "LitElement", "MarkupDataController", "period", "now", "start", "end", "effectClass", "current", "lordGlyph", "PLANET_GLYPH", "capitalize", "timeRange", "html", "nothing", "date", "dayChoghadiya", "nightChoghadiya", "p", "baseStyles", "css", "__decorateClass", "property", "customElement"]
7
7
  }