@roxyapi/ui 0.9.0 → 0.11.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 (378) hide show
  1. package/AGENTS.md +53 -27
  2. package/README.md +42 -12
  3. package/components-catalog.json +1022 -0
  4. package/dist/cdn/components/angel-number-card.js +52 -0
  5. package/dist/cdn/components/angel-number-card.js.map +7 -0
  6. package/dist/cdn/components/angel-number-lookup.js +52 -0
  7. package/dist/cdn/components/angel-number-lookup.js.map +7 -0
  8. package/dist/cdn/components/ashtakavarga-grid.js +10 -3
  9. package/dist/cdn/components/ashtakavarga-grid.js.map +4 -4
  10. package/dist/cdn/components/aspects-table.js +52 -0
  11. package/dist/cdn/components/aspects-table.js.map +7 -0
  12. package/dist/cdn/components/biorhythm-chart.js +10 -3
  13. package/dist/cdn/components/biorhythm-chart.js.map +4 -4
  14. package/dist/cdn/components/bodygraph.js +17 -10
  15. package/dist/cdn/components/bodygraph.js.map +4 -4
  16. package/dist/cdn/components/choghadiya-grid.js +10 -3
  17. package/dist/cdn/components/choghadiya-grid.js.map +4 -4
  18. package/dist/cdn/components/compatibility-card.js +10 -3
  19. package/dist/cdn/components/compatibility-card.js.map +4 -4
  20. package/dist/cdn/components/crystal-card.js +52 -0
  21. package/dist/cdn/components/crystal-card.js.map +7 -0
  22. package/dist/cdn/components/crystal-grid.js +52 -0
  23. package/dist/cdn/components/crystal-grid.js.map +7 -0
  24. package/dist/cdn/components/dasha-timeline.js +10 -3
  25. package/dist/cdn/components/dasha-timeline.js.map +4 -4
  26. package/dist/cdn/components/data.js +10 -3
  27. package/dist/cdn/components/data.js.map +4 -4
  28. package/dist/cdn/components/divisional-chart.js +31 -24
  29. package/dist/cdn/components/divisional-chart.js.map +4 -4
  30. package/dist/cdn/components/dosha-card.js +10 -3
  31. package/dist/cdn/components/dosha-card.js.map +4 -4
  32. package/dist/cdn/components/dream-card.js +52 -0
  33. package/dist/cdn/components/dream-card.js.map +7 -0
  34. package/dist/cdn/components/dream-search.js +52 -0
  35. package/dist/cdn/components/dream-search.js.map +7 -0
  36. package/dist/cdn/components/endpoint-form.js +3 -3
  37. package/dist/cdn/components/endpoint-form.js.map +3 -3
  38. package/dist/cdn/components/forecast-digest.js +52 -0
  39. package/dist/cdn/components/forecast-digest.js.map +7 -0
  40. package/dist/cdn/components/forecast-timeline.js +10 -3
  41. package/dist/cdn/components/forecast-timeline.js.map +4 -4
  42. package/dist/cdn/components/guna-milan.js +10 -3
  43. package/dist/cdn/components/guna-milan.js.map +4 -4
  44. package/dist/cdn/components/hd-connection.js +52 -0
  45. package/dist/cdn/components/hd-connection.js.map +7 -0
  46. package/dist/cdn/components/hd-penta.js +52 -0
  47. package/dist/cdn/components/hd-penta.js.map +7 -0
  48. package/dist/cdn/components/hd-variables.js +52 -0
  49. package/dist/cdn/components/hd-variables.js.map +7 -0
  50. package/dist/cdn/components/hexagram.js +10 -3
  51. package/dist/cdn/components/hexagram.js.map +4 -4
  52. package/dist/cdn/components/hora-table.js +52 -0
  53. package/dist/cdn/components/hora-table.js.map +7 -0
  54. package/dist/cdn/components/horoscope-card.js +10 -3
  55. package/dist/cdn/components/horoscope-card.js.map +4 -4
  56. package/dist/cdn/components/kp-chart.js +10 -3
  57. package/dist/cdn/components/kp-chart.js.map +4 -4
  58. package/dist/cdn/components/kp-planets-table.js +10 -3
  59. package/dist/cdn/components/kp-planets-table.js.map +4 -4
  60. package/dist/cdn/components/kp-ruling-planets.js +10 -3
  61. package/dist/cdn/components/kp-ruling-planets.js.map +4 -4
  62. package/dist/cdn/components/location-search.js +3 -3
  63. package/dist/cdn/components/location-search.js.map +2 -2
  64. package/dist/cdn/components/moon-phase.js +10 -3
  65. package/dist/cdn/components/moon-phase.js.map +4 -4
  66. package/dist/cdn/components/nakshatra-card.js +10 -3
  67. package/dist/cdn/components/nakshatra-card.js.map +4 -4
  68. package/dist/cdn/components/natal-chart.js +16 -9
  69. package/dist/cdn/components/natal-chart.js.map +4 -4
  70. package/dist/cdn/components/numerology-card.js +10 -3
  71. package/dist/cdn/components/numerology-card.js.map +4 -4
  72. package/dist/cdn/components/panchang-table.js +10 -3
  73. package/dist/cdn/components/panchang-table.js.map +4 -4
  74. package/dist/cdn/components/reference-card.js +52 -0
  75. package/dist/cdn/components/reference-card.js.map +7 -0
  76. package/dist/cdn/components/shadbala-table.js +10 -3
  77. package/dist/cdn/components/shadbala-table.js.map +4 -4
  78. package/dist/cdn/components/synastry-chart.js +16 -9
  79. package/dist/cdn/components/synastry-chart.js.map +4 -4
  80. package/dist/cdn/components/tarot-card.js +10 -3
  81. package/dist/cdn/components/tarot-card.js.map +4 -4
  82. package/dist/cdn/components/tarot-catalog.js +52 -0
  83. package/dist/cdn/components/tarot-catalog.js.map +7 -0
  84. package/dist/cdn/components/tarot-spread.js +10 -3
  85. package/dist/cdn/components/tarot-spread.js.map +4 -4
  86. package/dist/cdn/components/transits-table.js +10 -3
  87. package/dist/cdn/components/transits-table.js.map +4 -4
  88. package/dist/cdn/components/vedic-aspects.js +52 -0
  89. package/dist/cdn/components/vedic-aspects.js.map +7 -0
  90. package/dist/cdn/components/vedic-kundli.js +31 -24
  91. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  92. package/dist/cdn/components/vedic-planets-table.js +10 -3
  93. package/dist/cdn/components/vedic-planets-table.js.map +4 -4
  94. package/dist/cdn/components/western-planets-table.js +10 -3
  95. package/dist/cdn/components/western-planets-table.js.map +4 -4
  96. package/dist/cdn/components/yoga-list.js +10 -3
  97. package/dist/cdn/components/yoga-list.js.map +4 -4
  98. package/dist/cdn/roxy-ui.js +92 -81
  99. package/dist/cdn/roxy-ui.js.map +4 -4
  100. package/dist/components/angel-number-card.d.ts +17 -0
  101. package/dist/components/angel-number-card.d.ts.map +1 -0
  102. package/dist/components/angel-number-card.js +2 -0
  103. package/dist/components/angel-number-card.js.map +7 -0
  104. package/dist/components/angel-number-lookup.d.ts +21 -0
  105. package/dist/components/angel-number-lookup.d.ts.map +1 -0
  106. package/dist/components/angel-number-lookup.js +2 -0
  107. package/dist/components/angel-number-lookup.js.map +7 -0
  108. package/dist/components/ashtakavarga-grid.d.ts +4 -5
  109. package/dist/components/ashtakavarga-grid.d.ts.map +1 -1
  110. package/dist/components/ashtakavarga-grid.js +1 -1
  111. package/dist/components/ashtakavarga-grid.js.map +4 -4
  112. package/dist/components/aspects-table.d.ts +23 -0
  113. package/dist/components/aspects-table.d.ts.map +1 -0
  114. package/dist/components/aspects-table.js +2 -0
  115. package/dist/components/aspects-table.js.map +7 -0
  116. package/dist/components/biorhythm-chart.d.ts +4 -5
  117. package/dist/components/biorhythm-chart.d.ts.map +1 -1
  118. package/dist/components/biorhythm-chart.js +1 -1
  119. package/dist/components/biorhythm-chart.js.map +4 -4
  120. package/dist/components/bodygraph.d.ts +4 -5
  121. package/dist/components/bodygraph.d.ts.map +1 -1
  122. package/dist/components/bodygraph.js +9 -9
  123. package/dist/components/bodygraph.js.map +4 -4
  124. package/dist/components/choghadiya-grid.d.ts +4 -5
  125. package/dist/components/choghadiya-grid.d.ts.map +1 -1
  126. package/dist/components/choghadiya-grid.js +1 -1
  127. package/dist/components/choghadiya-grid.js.map +4 -4
  128. package/dist/components/compatibility-card.d.ts +4 -5
  129. package/dist/components/compatibility-card.d.ts.map +1 -1
  130. package/dist/components/compatibility-card.js +1 -1
  131. package/dist/components/compatibility-card.js.map +4 -4
  132. package/dist/components/crystal-card.d.ts +19 -0
  133. package/dist/components/crystal-card.d.ts.map +1 -0
  134. package/dist/components/crystal-card.js +2 -0
  135. package/dist/components/crystal-card.js.map +7 -0
  136. package/dist/components/crystal-grid.d.ts +26 -0
  137. package/dist/components/crystal-grid.d.ts.map +1 -0
  138. package/dist/components/crystal-grid.js +2 -0
  139. package/dist/components/crystal-grid.js.map +7 -0
  140. package/dist/components/dasha-timeline.d.ts +4 -5
  141. package/dist/components/dasha-timeline.d.ts.map +1 -1
  142. package/dist/components/dasha-timeline.js +1 -1
  143. package/dist/components/dasha-timeline.js.map +4 -4
  144. package/dist/components/data.d.ts +5 -8
  145. package/dist/components/data.d.ts.map +1 -1
  146. package/dist/components/data.js +1 -1
  147. package/dist/components/data.js.map +4 -4
  148. package/dist/components/divisional-chart.d.ts +4 -5
  149. package/dist/components/divisional-chart.d.ts.map +1 -1
  150. package/dist/components/divisional-chart.js +59 -59
  151. package/dist/components/divisional-chart.js.map +4 -4
  152. package/dist/components/dosha-card.d.ts +4 -5
  153. package/dist/components/dosha-card.d.ts.map +1 -1
  154. package/dist/components/dosha-card.js +1 -1
  155. package/dist/components/dosha-card.js.map +4 -4
  156. package/dist/components/dream-card.d.ts +16 -0
  157. package/dist/components/dream-card.d.ts.map +1 -0
  158. package/dist/components/dream-card.js +2 -0
  159. package/dist/components/dream-card.js.map +7 -0
  160. package/dist/components/dream-search.d.ts +18 -0
  161. package/dist/components/dream-search.d.ts.map +1 -0
  162. package/dist/components/dream-search.js +2 -0
  163. package/dist/components/dream-search.js.map +7 -0
  164. package/dist/components/endpoint-form.d.ts +10 -5
  165. package/dist/components/endpoint-form.d.ts.map +1 -1
  166. package/dist/components/endpoint-form.js +1 -1
  167. package/dist/components/endpoint-form.js.map +3 -3
  168. package/dist/components/forecast-digest.d.ts +19 -0
  169. package/dist/components/forecast-digest.d.ts.map +1 -0
  170. package/dist/components/forecast-digest.js +2 -0
  171. package/dist/components/forecast-digest.js.map +7 -0
  172. package/dist/components/forecast-timeline.d.ts +8 -6
  173. package/dist/components/forecast-timeline.d.ts.map +1 -1
  174. package/dist/components/forecast-timeline.js +1 -1
  175. package/dist/components/forecast-timeline.js.map +4 -4
  176. package/dist/components/guna-milan.d.ts +4 -5
  177. package/dist/components/guna-milan.d.ts.map +1 -1
  178. package/dist/components/guna-milan.js +1 -1
  179. package/dist/components/guna-milan.js.map +4 -4
  180. package/dist/components/hd-connection.d.ts +18 -0
  181. package/dist/components/hd-connection.d.ts.map +1 -0
  182. package/dist/components/hd-connection.js +2 -0
  183. package/dist/components/hd-connection.js.map +7 -0
  184. package/dist/components/hd-penta.d.ts +18 -0
  185. package/dist/components/hd-penta.d.ts.map +1 -0
  186. package/dist/components/hd-penta.js +2 -0
  187. package/dist/components/hd-penta.js.map +7 -0
  188. package/dist/components/hd-variables.d.ts +17 -0
  189. package/dist/components/hd-variables.d.ts.map +1 -0
  190. package/dist/components/hd-variables.js +2 -0
  191. package/dist/components/hd-variables.js.map +7 -0
  192. package/dist/components/hexagram.d.ts +4 -5
  193. package/dist/components/hexagram.d.ts.map +1 -1
  194. package/dist/components/hexagram.js +1 -1
  195. package/dist/components/hexagram.js.map +4 -4
  196. package/dist/components/hora-table.d.ts +17 -0
  197. package/dist/components/hora-table.d.ts.map +1 -0
  198. package/dist/components/hora-table.js +2 -0
  199. package/dist/components/hora-table.js.map +7 -0
  200. package/dist/components/horoscope-card.d.ts +4 -5
  201. package/dist/components/horoscope-card.d.ts.map +1 -1
  202. package/dist/components/horoscope-card.js +1 -1
  203. package/dist/components/horoscope-card.js.map +4 -4
  204. package/dist/components/kp-chart.d.ts +4 -5
  205. package/dist/components/kp-chart.d.ts.map +1 -1
  206. package/dist/components/kp-chart.js +1 -1
  207. package/dist/components/kp-chart.js.map +4 -4
  208. package/dist/components/kp-planets-table.d.ts +4 -5
  209. package/dist/components/kp-planets-table.d.ts.map +1 -1
  210. package/dist/components/kp-planets-table.js +1 -1
  211. package/dist/components/kp-planets-table.js.map +4 -4
  212. package/dist/components/kp-ruling-planets.d.ts +4 -5
  213. package/dist/components/kp-ruling-planets.d.ts.map +1 -1
  214. package/dist/components/kp-ruling-planets.js +1 -1
  215. package/dist/components/kp-ruling-planets.js.map +4 -4
  216. package/dist/components/location-search.js +1 -1
  217. package/dist/components/location-search.js.map +2 -2
  218. package/dist/components/moon-phase.d.ts +5 -5
  219. package/dist/components/moon-phase.d.ts.map +1 -1
  220. package/dist/components/moon-phase.js +1 -1
  221. package/dist/components/moon-phase.js.map +4 -4
  222. package/dist/components/nakshatra-card.d.ts +4 -5
  223. package/dist/components/nakshatra-card.d.ts.map +1 -1
  224. package/dist/components/nakshatra-card.js +1 -1
  225. package/dist/components/nakshatra-card.js.map +4 -4
  226. package/dist/components/natal-chart.d.ts +4 -5
  227. package/dist/components/natal-chart.d.ts.map +1 -1
  228. package/dist/components/natal-chart.js +8 -8
  229. package/dist/components/natal-chart.js.map +4 -4
  230. package/dist/components/numerology-card.d.ts +15 -10
  231. package/dist/components/numerology-card.d.ts.map +1 -1
  232. package/dist/components/numerology-card.js +1 -1
  233. package/dist/components/numerology-card.js.map +4 -4
  234. package/dist/components/panchang-table.d.ts +4 -5
  235. package/dist/components/panchang-table.d.ts.map +1 -1
  236. package/dist/components/panchang-table.js +1 -1
  237. package/dist/components/panchang-table.js.map +4 -4
  238. package/dist/components/reference-card.d.ts +19 -0
  239. package/dist/components/reference-card.d.ts.map +1 -0
  240. package/dist/components/reference-card.js +2 -0
  241. package/dist/components/reference-card.js.map +7 -0
  242. package/dist/components/shadbala-table.d.ts +4 -5
  243. package/dist/components/shadbala-table.d.ts.map +1 -1
  244. package/dist/components/shadbala-table.js +1 -1
  245. package/dist/components/shadbala-table.js.map +4 -4
  246. package/dist/components/synastry-chart.d.ts +4 -5
  247. package/dist/components/synastry-chart.d.ts.map +1 -1
  248. package/dist/components/synastry-chart.js +7 -7
  249. package/dist/components/synastry-chart.js.map +4 -4
  250. package/dist/components/tarot-card.d.ts +4 -5
  251. package/dist/components/tarot-card.d.ts.map +1 -1
  252. package/dist/components/tarot-card.js +1 -1
  253. package/dist/components/tarot-card.js.map +4 -4
  254. package/dist/components/tarot-catalog.d.ts +20 -0
  255. package/dist/components/tarot-catalog.d.ts.map +1 -0
  256. package/dist/components/tarot-catalog.js +2 -0
  257. package/dist/components/tarot-catalog.js.map +7 -0
  258. package/dist/components/tarot-spread.d.ts +7 -8
  259. package/dist/components/tarot-spread.d.ts.map +1 -1
  260. package/dist/components/tarot-spread.js +1 -1
  261. package/dist/components/tarot-spread.js.map +4 -4
  262. package/dist/components/transits-table.d.ts +4 -5
  263. package/dist/components/transits-table.d.ts.map +1 -1
  264. package/dist/components/transits-table.js +1 -1
  265. package/dist/components/transits-table.js.map +4 -4
  266. package/dist/components/vedic-aspects.d.ts +16 -0
  267. package/dist/components/vedic-aspects.d.ts.map +1 -0
  268. package/dist/components/vedic-aspects.js +2 -0
  269. package/dist/components/vedic-aspects.js.map +7 -0
  270. package/dist/components/vedic-kundli.d.ts +22 -5
  271. package/dist/components/vedic-kundli.d.ts.map +1 -1
  272. package/dist/components/vedic-kundli.js +63 -63
  273. package/dist/components/vedic-kundli.js.map +4 -4
  274. package/dist/components/vedic-planets-table.d.ts +4 -5
  275. package/dist/components/vedic-planets-table.d.ts.map +1 -1
  276. package/dist/components/vedic-planets-table.js +1 -1
  277. package/dist/components/vedic-planets-table.js.map +4 -4
  278. package/dist/components/western-planets-table.d.ts +4 -5
  279. package/dist/components/western-planets-table.d.ts.map +1 -1
  280. package/dist/components/western-planets-table.js +1 -1
  281. package/dist/components/western-planets-table.js.map +4 -4
  282. package/dist/components/yoga-list.d.ts +6 -7
  283. package/dist/components/yoga-list.d.ts.map +1 -1
  284. package/dist/components/yoga-list.js +1 -1
  285. package/dist/components/yoga-list.js.map +4 -4
  286. package/dist/generated/endpoint-bindings.d.ts +15 -0
  287. package/dist/generated/endpoint-bindings.d.ts.map +1 -0
  288. package/dist/index.cjs +73 -73
  289. package/dist/index.cjs.map +4 -4
  290. package/dist/index.d.ts +16 -0
  291. package/dist/index.d.ts.map +1 -1
  292. package/dist/index.js +71 -71
  293. package/dist/index.js.map +4 -4
  294. package/dist/manifest.d.ts.map +1 -1
  295. package/dist/manifest.json +39 -24
  296. package/dist/styles/tokens-css.d.ts +1 -1
  297. package/dist/styles/tokens-css.d.ts.map +1 -1
  298. package/dist/styles/tokens.css +4 -0
  299. package/dist/types/index.d.ts +1 -1
  300. package/dist/types/index.d.ts.map +1 -1
  301. package/dist/types/types.gen.d.ts +1103 -164
  302. package/dist/types/types.gen.d.ts.map +1 -1
  303. package/dist/utils/angel-sections.d.ts +14 -0
  304. package/dist/utils/angel-sections.d.ts.map +1 -0
  305. package/dist/utils/base-element.d.ts +73 -0
  306. package/dist/utils/base-element.d.ts.map +1 -0
  307. package/dist/utils/base-styles.d.ts.map +1 -1
  308. package/dist/utils/fetch-controller.d.ts +60 -0
  309. package/dist/utils/fetch-controller.d.ts.map +1 -0
  310. package/dist/utils/kundli-render.d.ts +2 -1
  311. package/dist/utils/kundli-render.d.ts.map +1 -1
  312. package/dist/utils/kundli-styles.d.ts.map +1 -1
  313. package/dist/utils/markup-data.d.ts +34 -0
  314. package/dist/utils/markup-data.d.ts.map +1 -1
  315. package/dist/version.d.ts +1 -1
  316. package/dist/version.d.ts.map +1 -1
  317. package/package.json +2 -1
  318. package/src/components/angel-number-card.ts +210 -0
  319. package/src/components/angel-number-lookup.ts +207 -0
  320. package/src/components/ashtakavarga-grid.ts +15 -20
  321. package/src/components/aspects-table.ts +329 -0
  322. package/src/components/biorhythm-chart.ts +14 -18
  323. package/src/components/bodygraph.ts +9 -20
  324. package/src/components/choghadiya-grid.ts +15 -19
  325. package/src/components/compatibility-card.ts +9 -19
  326. package/src/components/crystal-card.ts +242 -0
  327. package/src/components/crystal-grid.ts +182 -0
  328. package/src/components/dasha-timeline.ts +9 -20
  329. package/src/components/data.ts +8 -27
  330. package/src/components/divisional-chart.ts +10 -18
  331. package/src/components/dosha-card.ts +8 -19
  332. package/src/components/dream-card.ts +88 -0
  333. package/src/components/dream-search.ts +135 -0
  334. package/src/components/endpoint-form.ts +149 -55
  335. package/src/components/forecast-digest.ts +213 -0
  336. package/src/components/forecast-timeline.ts +24 -19
  337. package/src/components/guna-milan.ts +8 -19
  338. package/src/components/hd-connection.ts +188 -0
  339. package/src/components/hd-penta.ts +165 -0
  340. package/src/components/hd-variables.ts +128 -0
  341. package/src/components/hexagram.ts +10 -18
  342. package/src/components/hora-table.ts +149 -0
  343. package/src/components/horoscope-card.ts +9 -19
  344. package/src/components/kp-chart.ts +11 -20
  345. package/src/components/kp-planets-table.ts +11 -20
  346. package/src/components/kp-ruling-planets.ts +8 -18
  347. package/src/components/moon-phase.ts +10 -19
  348. package/src/components/nakshatra-card.ts +11 -20
  349. package/src/components/natal-chart.ts +20 -23
  350. package/src/components/numerology-card.ts +90 -31
  351. package/src/components/panchang-table.ts +9 -19
  352. package/src/components/reference-card.ts +212 -0
  353. package/src/components/shadbala-table.ts +15 -18
  354. package/src/components/synastry-chart.ts +17 -20
  355. package/src/components/tarot-card.ts +9 -20
  356. package/src/components/tarot-catalog.ts +129 -0
  357. package/src/components/tarot-spread.ts +20 -21
  358. package/src/components/transits-table.ts +17 -20
  359. package/src/components/vedic-aspects.ts +188 -0
  360. package/src/components/vedic-kundli.ts +46 -19
  361. package/src/components/vedic-planets-table.ts +11 -19
  362. package/src/components/western-planets-table.ts +11 -19
  363. package/src/components/yoga-list.ts +17 -23
  364. package/src/generated/endpoint-bindings.ts +655 -0
  365. package/src/index.ts +27 -0
  366. package/src/manifest.ts +198 -6
  367. package/src/styles/tokens-css.ts +4 -0
  368. package/src/styles/tokens.css +4 -0
  369. package/src/types/index.ts +1 -1
  370. package/src/types/types.gen.ts +1123 -164
  371. package/src/utils/angel-sections.ts +38 -0
  372. package/src/utils/base-element.ts +183 -0
  373. package/src/utils/base-styles.ts +33 -0
  374. package/src/utils/fetch-controller.ts +166 -0
  375. package/src/utils/kundli-render.ts +9 -2
  376. package/src/utils/kundli-styles.ts +7 -1
  377. package/src/utils/markup-data.ts +45 -0
  378. package/src/version.ts +1 -1
@@ -1,12 +1,12 @@
1
- import { css, html, LitElement, nothing } from 'lit';
1
+ import { css, html, nothing } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
  import type {
4
4
  GetBasicPanchangResponse,
5
5
  GetDetailedPanchangResponse,
6
6
  } from '../types/index.js';
7
+ import { RoxyDataElement } from '../utils/base-element.js';
7
8
  import { baseStyles } from '../utils/base-styles.js';
8
9
  import { formatDate, formatTime, formatTimeRange } from '../utils/format.js';
9
- import { MarkupDataController } from '../utils/markup-data.js';
10
10
 
11
11
  type PanchangData = GetBasicPanchangResponse | GetDetailedPanchangResponse;
12
12
  type PanchangTime = GetDetailedPanchangResponse['rahuKaal'];
@@ -24,14 +24,14 @@ type PanchangTime = GetDetailedPanchangResponse['rahuKaal'];
24
24
  * on timing without parsing the raw response.
25
25
  */
26
26
  @customElement('roxy-panchang-table')
27
- export class RoxyPanchangTable extends LitElement {
27
+ export class RoxyPanchangTable extends RoxyDataElement<PanchangData> {
28
28
  static styles = [
29
29
  baseStyles,
30
30
  css`
31
31
  .wrap {
32
32
  border: 1px solid var(--roxy-border, #e4e4e7);
33
33
  border-radius: var(--roxy-radius-md, 8px);
34
- background: var(--roxy-bg, #fff);
34
+ background: var(--roxy-surface, #fff);
35
35
  overflow: hidden;
36
36
  box-shadow: var(--roxy-shadow-sm);
37
37
  }
@@ -88,24 +88,14 @@ export class RoxyPanchangTable extends LitElement {
88
88
  `,
89
89
  ];
90
90
 
91
- constructor() {
92
- super();
93
- // Enables hydrating `data` from a direct-child
94
- // <script type="application/json" class="roxy-data"> for server-rendered
95
- // and cached consumers. The JavaScript `data` property still wins.
96
- new MarkupDataController(this);
97
- }
98
-
99
- @property({ attribute: false })
100
- data: PanchangData | null = null;
101
-
102
91
  @property({ type: String, reflect: true })
103
92
  detail: 'basic' | 'detailed' = 'detailed';
104
93
 
105
- render() {
106
- const d = this.data;
107
- if (!d)
108
- return html`<div class="roxy-empty" role="status">No panchang data</div>`;
94
+ protected renderEmpty() {
95
+ return html`<div class="roxy-empty" role="status">No panchang data</div>`;
96
+ }
97
+
98
+ protected renderData(d: PanchangData) {
109
99
  const detailed = 'sunrise' in d ? d : null;
110
100
 
111
101
  const fivefold: Array<[string, string]> = [
@@ -0,0 +1,212 @@
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
+ import type {
4
+ GetCenterResponse,
5
+ GetCompoundNumberResponse,
6
+ GetGateResponse,
7
+ GetNumberMeaningResponse,
8
+ GetPlanetMeaningResponse,
9
+ GetRashiResponse,
10
+ GetTrigramResponse,
11
+ GetZodiacSignResponse,
12
+ } from '../types/index.js';
13
+ import { RoxyDataElement } from '../utils/base-element.js';
14
+ import { baseStyles } from '../utils/base-styles.js';
15
+ import { humanize } from '../utils/string.js';
16
+
17
+ /**
18
+ * Every reference / glossary lookup this card renders. They do NOT share field names (a zodiac sign has 17 fields, a number meaning has 3), so the card renders heuristically rather than by fixed keys: a symbol + title hero, the prose fields as labelled paragraphs, keyword and string-list fields as chips, and every remaining scalar as an attribute grid. One card replaces eight near-identical bespoke ones.
19
+ */
20
+ type ReferenceData =
21
+ | GetZodiacSignResponse
22
+ | GetPlanetMeaningResponse
23
+ | GetRashiResponse
24
+ | GetTrigramResponse
25
+ | GetGateResponse
26
+ | GetCenterResponse
27
+ | GetNumberMeaningResponse
28
+ | GetCompoundNumberResponse;
29
+
30
+ /** Keys used to derive the title, in priority order. */
31
+ const TITLE_KEYS = ['name', 'english', 'title'];
32
+ /** Keys whose string value is always treated as prose, even when short. */
33
+ const PROSE_KEYS = new Set([
34
+ 'description',
35
+ 'meaning',
36
+ 'characteristics',
37
+ 'tagline',
38
+ 'definedMeaning',
39
+ 'undefinedMeaning',
40
+ 'motto',
41
+ ]);
42
+ /** Keys never shown as their own row/section (rendered elsewhere or noise). */
43
+ const SKIP_KEYS = new Set(['id', 'symbol', 'keywords', ...TITLE_KEYS]);
44
+
45
+ @customElement('roxy-reference-card')
46
+ export class RoxyReferenceCard extends RoxyDataElement<ReferenceData> {
47
+ static styles = [
48
+ baseStyles,
49
+ css`
50
+ .card {
51
+ background: var(--roxy-surface, #fff);
52
+ color: var(--roxy-fg, #0a0a0a);
53
+ border: 1px solid var(--roxy-border, #e4e4e7);
54
+ border-radius: var(--roxy-radius-md, 8px);
55
+ padding: var(--roxy-space-lg, 1.5rem);
56
+ box-shadow: var(--roxy-shadow-sm);
57
+ display: grid;
58
+ gap: var(--roxy-space-md, 1rem);
59
+ }
60
+ .head {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: var(--roxy-space-md, 1rem);
64
+ }
65
+ .symbol {
66
+ font-size: 2.5rem;
67
+ line-height: 1;
68
+ color: var(--roxy-accent-ink, #b45309);
69
+ flex: none;
70
+ }
71
+ .label {
72
+ margin: 0;
73
+ font-size: var(--roxy-text-xs, 0.75rem);
74
+ color: var(--roxy-muted, #71717a);
75
+ text-transform: uppercase;
76
+ letter-spacing: 0.06em;
77
+ }
78
+ .name {
79
+ margin: 0;
80
+ font-size: var(--roxy-text-lg, 1.125rem);
81
+ font-weight: var(--roxy-weight-bold, 600);
82
+ }
83
+ .prose {
84
+ margin: 0;
85
+ line-height: 1.6;
86
+ }
87
+ .prose .prose-label {
88
+ display: block;
89
+ font-size: var(--roxy-text-xs, 0.75rem);
90
+ color: var(--roxy-muted, #71717a);
91
+ text-transform: uppercase;
92
+ letter-spacing: 0.05em;
93
+ margin-bottom: 2px;
94
+ }
95
+ .chips {
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ gap: var(--roxy-space-xs, 0.25rem);
99
+ }
100
+ .chips span {
101
+ background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 14%, transparent);
102
+ padding: 2px 8px;
103
+ border-radius: var(--roxy-radius-full, 9999px);
104
+ font-size: var(--roxy-text-xs, 0.75rem);
105
+ }
106
+ .list h3,
107
+ .facts-wrap h3 {
108
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
109
+ font-size: var(--roxy-text-xs, 0.75rem);
110
+ color: var(--roxy-muted, #71717a);
111
+ text-transform: uppercase;
112
+ letter-spacing: 0.06em;
113
+ }
114
+ .facts {
115
+ display: grid;
116
+ grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
117
+ gap: var(--roxy-space-sm, 0.5rem);
118
+ margin: 0;
119
+ }
120
+ .fact dt {
121
+ color: var(--roxy-muted, #71717a);
122
+ font-size: var(--roxy-text-xs, 0.75rem);
123
+ text-transform: capitalize;
124
+ }
125
+ .fact dd {
126
+ margin: 0;
127
+ font-size: var(--roxy-text-sm, 0.875rem);
128
+ }
129
+ `,
130
+ ];
131
+
132
+ protected renderData(d: ReferenceData) {
133
+ const rec = d as Record<string, unknown>;
134
+ const title = this.deriveTitle(rec);
135
+ const symbol = typeof rec.symbol === 'string' ? rec.symbol : undefined;
136
+ const keywords = Array.isArray(rec.keywords)
137
+ ? (rec.keywords as unknown[]).map(String)
138
+ : [];
139
+
140
+ const prose: Array<[string, string]> = [];
141
+ const lists: Array<[string, string[]]> = [];
142
+ const facts: Array<[string, string]> = [];
143
+ for (const [key, value] of Object.entries(rec)) {
144
+ if (SKIP_KEYS.has(key) || value == null) continue;
145
+ if (Array.isArray(value)) {
146
+ const items = value.filter((v) => v != null).map(String);
147
+ if (items.length > 0) lists.push([key, items]);
148
+ } else if (typeof value === 'string') {
149
+ if (PROSE_KEYS.has(key) || value.length > 48) prose.push([key, value]);
150
+ else facts.push([key, value]);
151
+ } else if (typeof value === 'number' || typeof value === 'boolean') {
152
+ facts.push([key, String(value)]);
153
+ }
154
+ }
155
+
156
+ return html`<article class="card" aria-label=${title}>
157
+ <header class="head">
158
+ ${symbol ? html`<span class="symbol" aria-hidden="true">${symbol}</span>` : nothing}
159
+ <div>
160
+ <p class="label">Reference</p>
161
+ <h2 class="name">${title}</h2>
162
+ </div>
163
+ </header>
164
+ ${keywords.length > 0 ? html`<div class="chips">${keywords.map((k) => html`<span>${k}</span>`)}</div>` : nothing}
165
+ ${prose.map(
166
+ ([key, text]) => html`<p class="prose">
167
+ <span class="prose-label">${humanize(key)}</span>${text}
168
+ </p>`,
169
+ )}
170
+ ${
171
+ facts.length > 0
172
+ ? html`<div class="facts-wrap">
173
+ <dl class="facts">
174
+ ${facts.map(
175
+ ([key, value]) => html`<div class="fact">
176
+ <dt>${humanize(key)}</dt>
177
+ <dd>${value}</dd>
178
+ </div>`,
179
+ )}
180
+ </dl>
181
+ </div>`
182
+ : nothing
183
+ }
184
+ ${lists.map(
185
+ ([key, items]) => html`<div class="list">
186
+ <h3>${humanize(key)}</h3>
187
+ <div class="chips">${items.map((i) => html`<span>${i}</span>`)}</div>
188
+ </div>`,
189
+ )}
190
+ </article>`;
191
+ }
192
+
193
+ protected renderEmpty() {
194
+ return html`<div class="roxy-empty" role="status">No reference data</div>`;
195
+ }
196
+
197
+ private deriveTitle(rec: Record<string, unknown>): string {
198
+ for (const key of TITLE_KEYS) {
199
+ if (typeof rec[key] === 'string') return rec[key] as string;
200
+ }
201
+ const type = typeof rec.type === 'string' ? humanize(rec.type) : '';
202
+ const number = rec.number;
203
+ if (number != null) return `${type} ${number}`.trim();
204
+ return 'Reference';
205
+ }
206
+ }
207
+
208
+ declare global {
209
+ interface HTMLElementTagNameMap {
210
+ 'roxy-reference-card': RoxyReferenceCard;
211
+ }
212
+ }
@@ -1,10 +1,10 @@
1
- import { css, html, LitElement, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
3
  import { PLANET_GLYPH } from '../tokens/index.js';
4
4
  import type { ShadbalaResponse } from '../types/index.js';
5
+ import { RoxyDataElement } from '../utils/base-element.js';
5
6
  import { baseStyles } from '../utils/base-styles.js';
6
7
  import { formatNumber } from '../utils/format.js';
7
- import { MarkupDataController } from '../utils/markup-data.js';
8
8
  import { capitalize } from '../utils/string.js';
9
9
 
10
10
  type Planet = ShadbalaResponse['planets'][number];
@@ -40,11 +40,17 @@ const BALA_COMPONENTS: Array<{
40
40
  * Pass `data` from /vedic-astrology/shadbala.
41
41
  */
42
42
  @customElement('roxy-shadbala-table')
43
- export class RoxyShadbalaTable extends LitElement {
43
+ export class RoxyShadbalaTable extends RoxyDataElement<ShadbalaResponse> {
44
44
  static styles = [
45
45
  baseStyles,
46
46
  css`
47
47
  .wrap {
48
+ background: var(--roxy-surface, #fff);
49
+ color: var(--roxy-fg, #0a0a0a);
50
+ border: 1px solid var(--roxy-border, #e4e4e7);
51
+ border-radius: var(--roxy-radius-md, 8px);
52
+ padding: var(--roxy-space-lg, 1.5rem);
53
+ box-shadow: var(--roxy-shadow-sm);
48
54
  display: grid;
49
55
  gap: var(--roxy-space-md, 1rem);
50
56
  }
@@ -192,23 +198,14 @@ export class RoxyShadbalaTable extends LitElement {
192
198
  `,
193
199
  ];
194
200
 
195
- constructor() {
196
- super();
197
- // Enables hydrating `data` from a direct-child
198
- // <script type="application/json" class="roxy-data"> for server-rendered
199
- // and cached consumers. The JavaScript `data` property still wins.
200
- new MarkupDataController(this);
201
+ protected renderEmpty() {
202
+ return html`<div class="roxy-empty" role="status">No shadbala data</div>`;
201
203
  }
202
204
 
203
- @property({ attribute: false })
204
- data: ShadbalaResponse | null = null;
205
+ protected renderData(d: ShadbalaResponse) {
206
+ if (!d.planets?.length) return this.renderEmpty();
205
207
 
206
- render() {
207
- if (!this.data?.planets?.length) {
208
- return html`<div class="roxy-empty" role="status">No shadbala data</div>`;
209
- }
210
-
211
- const sorted = [...this.data.planets].sort(
208
+ const sorted = [...d.planets].sort(
212
209
  (a, b) => a.relativeRank - b.relativeRank,
213
210
  );
214
211
 
@@ -1,10 +1,11 @@
1
- import { css, html, LitElement, nothing, svg } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import { css, html, nothing, svg } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
3
  import { PLANET_GLYPH, SIGN_GLYPH, SIGNS_ORDER } from '../tokens/index.js';
4
4
  import type {
5
5
  CalculateSynastryResponse,
6
6
  NatalChartResponse,
7
7
  } from '../types/index.js';
8
+ import { RoxyDataElement } from '../utils/base-element.js';
8
9
  import { baseStyles } from '../utils/base-styles.js';
9
10
  import { longitudeToSignPosition, polarToCartesian } from '../utils/degree.js';
10
11
  import {
@@ -12,7 +13,6 @@ import {
12
13
  formatNumber,
13
14
  normalizeAspect,
14
15
  } from '../utils/format.js';
15
- import { MarkupDataController } from '../utils/markup-data.js';
16
16
  import { capitalize } from '../utils/string.js';
17
17
 
18
18
  type PlanetEntry = NatalChartResponse['planets'][number];
@@ -40,11 +40,17 @@ const P2_R = 96;
40
40
  * /astrology/synastry.
41
41
  */
42
42
  @customElement('roxy-synastry-chart')
43
- export class RoxySynastryChart extends LitElement {
43
+ export class RoxySynastryChart extends RoxyDataElement<SynastryWithPlanets> {
44
44
  static styles = [
45
45
  baseStyles,
46
46
  css`
47
47
  .wrap {
48
+ background: var(--roxy-surface, #fff);
49
+ color: var(--roxy-fg, #0a0a0a);
50
+ border: 1px solid var(--roxy-border, #e4e4e7);
51
+ border-radius: var(--roxy-radius-md, 8px);
52
+ padding: var(--roxy-space-lg, 1.5rem);
53
+ box-shadow: var(--roxy-shadow-sm);
48
54
  display: grid;
49
55
  gap: var(--roxy-space-md, 1rem);
50
56
  }
@@ -73,7 +79,7 @@ export class RoxySynastryChart extends LitElement {
73
79
  svg {
74
80
  display: block;
75
81
  width: 100%;
76
- max-width: 560px;
82
+ max-width: var(--roxy-chart-max-width, 560px);
77
83
  aspect-ratio: 1 / 1;
78
84
  height: auto;
79
85
  margin: 0 auto;
@@ -225,22 +231,13 @@ export class RoxySynastryChart extends LitElement {
225
231
  `,
226
232
  ];
227
233
 
228
- constructor() {
229
- super();
230
- // Enables hydrating `data` from a direct-child
231
- // <script type="application/json" class="roxy-data"> for server-rendered
232
- // and cached consumers. The JavaScript `data` property still wins.
233
- new MarkupDataController(this);
234
+ protected renderEmpty() {
235
+ return html`<div class="roxy-empty" role="status">No synastry data</div>`;
234
236
  }
235
237
 
236
- @property({ attribute: false })
237
- data: SynastryWithPlanets | null = null;
238
-
239
- render() {
240
- if (!this.data)
241
- return html`<div class="roxy-empty" role="status">No synastry data</div>`;
242
- const { person1, person2, compatibilityScore, analysis } = this.data;
243
- const interAspects = this.data.interAspects ?? [];
238
+ protected renderData(d: SynastryWithPlanets) {
239
+ const { person1, person2, compatibilityScore, analysis } = d;
240
+ const interAspects = d.interAspects ?? [];
244
241
  const p1Planets = person1?.planets ?? [];
245
242
  const p2Planets = person2?.planets ?? [];
246
243
 
@@ -353,7 +350,7 @@ export class RoxySynastryChart extends LitElement {
353
350
  ${this.renderSpokes()} ${this.renderSigns()}
354
351
  ${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)}
355
352
  ${this.renderRing(p1Planets, P1_R, 'p1', 1)} ${this.renderRing(p2Planets, P2_R, 'p2', 2)}
356
- ${this.renderAscendants(this.data)}
353
+ ${this.renderAscendants(d)}
357
354
  </svg>
358
355
  <div class="legend-row">
359
356
  <span><span class="swatch" style="background: var(--roxy-accent)"></span>Person 1</span>
@@ -1,8 +1,8 @@
1
- import { css, html, LitElement, nothing } from 'lit';
2
- import { customElement, property, state } from 'lit/decorators.js';
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement, state } from 'lit/decorators.js';
3
3
  import type { GetCardResponse, GetDailyCardResponse } from '../types/index.js';
4
+ import { RoxyDataElement } from '../utils/base-element.js';
4
5
  import { baseStyles } from '../utils/base-styles.js';
5
- import { MarkupDataController } from '../utils/markup-data.js';
6
6
 
7
7
  type TarotData = GetCardResponse | GetDailyCardResponse;
8
8
 
@@ -11,12 +11,12 @@ type TarotData = GetCardResponse | GetDailyCardResponse;
11
11
  * between upright and reversed where the data shape supports it.
12
12
  */
13
13
  @customElement('roxy-tarot-card')
14
- export class RoxyTarotCard extends LitElement {
14
+ export class RoxyTarotCard extends RoxyDataElement<TarotData> {
15
15
  static styles = [
16
16
  baseStyles,
17
17
  css`
18
18
  .card {
19
- background: var(--roxy-bg, #fff);
19
+ background: var(--roxy-surface, #fff);
20
20
  border: 1px solid var(--roxy-border, #e4e4e7);
21
21
  border-radius: var(--roxy-radius-md, 8px);
22
22
  padding: var(--roxy-space-lg, 1.5rem);
@@ -107,17 +107,6 @@ export class RoxyTarotCard extends LitElement {
107
107
  `,
108
108
  ];
109
109
 
110
- constructor() {
111
- super();
112
- // Enables hydrating `data` from a direct-child
113
- // <script type="application/json" class="roxy-data"> for server-rendered
114
- // and cached consumers. The JavaScript `data` property still wins.
115
- new MarkupDataController(this);
116
- }
117
-
118
- @property({ attribute: false })
119
- data: TarotData | null = null;
120
-
121
110
  @state()
122
111
  private flipped = false;
123
112
 
@@ -125,11 +114,11 @@ export class RoxyTarotCard extends LitElement {
125
114
  this.flipped = !this.flipped;
126
115
  };
127
116
 
128
- render() {
129
- const d = this.data;
130
- if (!d)
131
- return html`<div class="roxy-empty" role="status">No tarot data</div>`;
117
+ protected renderEmpty() {
118
+ return html`<div class="roxy-empty" role="status">No tarot data</div>`;
119
+ }
132
120
 
121
+ protected renderData(d: TarotData) {
133
122
  if ('card' in d) return this.renderDailyCard(d);
134
123
  return this.renderFullCard(d);
135
124
  }
@@ -0,0 +1,129 @@
1
+ import { css, html } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import type { ListCardsResponse } from '../types/index.js';
4
+ import { RoxyDataElement } from '../utils/base-element.js';
5
+ import { baseStyles } from '../utils/base-styles.js';
6
+ import { capitalize } from '../utils/string.js';
7
+
8
+ /** A single card row from the catalog response. Kept spec-derived so the tile never reads a field the API does not return. */
9
+ type CatalogCard = ListCardsResponse['cards'][number];
10
+
11
+ /**
12
+ * Tarot catalog. Renders GET /tarot/cards as a responsive gallery of the deck: each tile carries the Rider-Waite-Smith artwork, the card name, and an arcana/suit caption. Filter the deck server-side (arcana, suit, number, paging) and pass the page response; the component renders whatever cards it carries. Pairs with `<roxy-tarot-card>` for a single-card detail view and `<roxy-tarot-spread>` for readings.
13
+ */
14
+ @customElement('roxy-tarot-catalog')
15
+ export class RoxyTarotCatalog extends RoxyDataElement<ListCardsResponse> {
16
+ static styles = [
17
+ baseStyles,
18
+ css`
19
+ .wrap {
20
+ display: grid;
21
+ gap: var(--roxy-space-md, 1rem);
22
+ }
23
+ .head {
24
+ display: flex;
25
+ align-items: baseline;
26
+ justify-content: space-between;
27
+ gap: var(--roxy-space-sm, 0.5rem);
28
+ flex-wrap: wrap;
29
+ }
30
+ .title {
31
+ margin: 0;
32
+ font-size: var(--roxy-text-lg, 1.125rem);
33
+ font-weight: var(--roxy-weight-bold, 600);
34
+ color: var(--roxy-fg, #0a0a0a);
35
+ }
36
+ .count {
37
+ color: var(--roxy-muted, #71717a);
38
+ font-size: var(--roxy-text-sm, 0.875rem);
39
+ }
40
+ .grid {
41
+ display: grid;
42
+ grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
43
+ gap: var(--roxy-space-md, 1rem);
44
+ margin: 0;
45
+ padding: 0;
46
+ list-style: none;
47
+ }
48
+ .tile {
49
+ display: grid;
50
+ gap: var(--roxy-space-xs, 0.25rem);
51
+ background: var(--roxy-surface, #fff);
52
+ border: 1px solid var(--roxy-border, #e4e4e7);
53
+ border-radius: var(--roxy-radius-md, 8px);
54
+ padding: var(--roxy-space-sm, 0.5rem);
55
+ box-shadow: var(--roxy-shadow-sm);
56
+ }
57
+ .art {
58
+ aspect-ratio: 2 / 3;
59
+ width: 100%;
60
+ border-radius: var(--roxy-radius-sm, 4px);
61
+ object-fit: cover;
62
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 35%, transparent);
63
+ }
64
+ .name {
65
+ margin: 0;
66
+ font-size: var(--roxy-text-sm, 0.875rem);
67
+ font-weight: var(--roxy-weight-bold, 600);
68
+ color: var(--roxy-fg, #0a0a0a);
69
+ }
70
+ .meta {
71
+ margin: 0;
72
+ font-size: var(--roxy-text-xs, 0.75rem);
73
+ color: var(--roxy-muted, #71717a);
74
+ }
75
+ `,
76
+ ];
77
+
78
+ /**
79
+ * Override the auto-derived gallery heading. Empty by default, in which case the heading is "Tarot deck".
80
+ */
81
+ @property({ type: String, reflect: true })
82
+ heading = '';
83
+
84
+ protected renderEmpty() {
85
+ return html`<div class="roxy-empty" role="status">No cards</div>`;
86
+ }
87
+
88
+ protected renderData(d: ListCardsResponse) {
89
+ const cards = d.cards ?? [];
90
+ if (cards.length === 0) return this.renderEmpty();
91
+
92
+ const title = this.heading || 'Tarot deck';
93
+ const total = typeof d.total === 'number' ? d.total : cards.length;
94
+
95
+ return html`<section class="wrap" aria-label=${title}>
96
+ <header class="head">
97
+ <h2 class="title">${title}</h2>
98
+ <span class="count">${total} ${total === 1 ? 'card' : 'cards'}</span>
99
+ </header>
100
+ <ul class="grid">
101
+ ${cards.map(
102
+ (c) => html`<li class="tile">
103
+ ${
104
+ c.imageUrl
105
+ ? html`<img class="art" src=${c.imageUrl} alt=${c.name ?? 'Tarot card'} loading="lazy" />`
106
+ : html`<div class="art" aria-hidden="true"></div>`
107
+ }
108
+ <p class="name">${c.name}</p>
109
+ <p class="meta">${cardMeta(c)}</p>
110
+ </li>`,
111
+ )}
112
+ </ul>
113
+ </section>`;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Caption line for a catalog tile. Minor Arcana cards name their suit (`Minor · Cups`); Major Arcana cards read `Major Arcana`. Both derive only from the spec `arcana` and `suit` fields.
119
+ */
120
+ function cardMeta(c: CatalogCard): string {
121
+ if (c.suit) return `${capitalize(c.arcana)} · ${capitalize(c.suit)}`;
122
+ return `${capitalize(c.arcana)} Arcana`;
123
+ }
124
+
125
+ declare global {
126
+ interface HTMLElementTagNameMap {
127
+ 'roxy-tarot-catalog': RoxyTarotCatalog;
128
+ }
129
+ }