@roxyapi/ui 0.1.2 → 0.2.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 (220) hide show
  1. package/AGENTS.md +6 -0
  2. package/README.md +327 -14
  3. package/THEMING.md +24 -7
  4. package/dist/cdn/components/ashtakavarga-grid.js +349 -0
  5. package/dist/cdn/components/ashtakavarga-grid.js.map +7 -0
  6. package/dist/cdn/components/biorhythm-chart.js +15 -22
  7. package/dist/cdn/components/biorhythm-chart.js.map +3 -3
  8. package/dist/cdn/components/choghadiya-grid.js +239 -0
  9. package/dist/cdn/components/choghadiya-grid.js.map +7 -0
  10. package/dist/cdn/components/compatibility-card.js +36 -34
  11. package/dist/cdn/components/compatibility-card.js.map +4 -4
  12. package/dist/cdn/components/dasha-timeline.js +35 -39
  13. package/dist/cdn/components/dasha-timeline.js.map +4 -4
  14. package/dist/cdn/components/data.js +9 -9
  15. package/dist/cdn/components/data.js.map +4 -4
  16. package/dist/cdn/components/divisional-chart.js +279 -0
  17. package/dist/cdn/components/divisional-chart.js.map +7 -0
  18. package/dist/cdn/components/dosha-card.js +49 -49
  19. package/dist/cdn/components/dosha-card.js.map +3 -3
  20. package/dist/cdn/components/endpoint-form.js +47 -28
  21. package/dist/cdn/components/endpoint-form.js.map +4 -4
  22. package/dist/cdn/components/guna-milan.js +66 -24
  23. package/dist/cdn/components/guna-milan.js.map +4 -4
  24. package/dist/cdn/components/hexagram.js +26 -26
  25. package/dist/cdn/components/hexagram.js.map +3 -3
  26. package/dist/cdn/components/horoscope-card.js +47 -40
  27. package/dist/cdn/components/horoscope-card.js.map +4 -4
  28. package/dist/cdn/components/kp-planets-table.js +10 -10
  29. package/dist/cdn/components/kp-planets-table.js.map +4 -4
  30. package/dist/cdn/components/location-search.js +6 -6
  31. package/dist/cdn/components/location-search.js.map +3 -3
  32. package/dist/cdn/components/moon-phase.js +18 -18
  33. package/dist/cdn/components/moon-phase.js.map +4 -4
  34. package/dist/cdn/components/natal-chart.js +240 -24
  35. package/dist/cdn/components/natal-chart.js.map +4 -4
  36. package/dist/cdn/components/numerology-card.js +40 -31
  37. package/dist/cdn/components/numerology-card.js.map +4 -4
  38. package/dist/cdn/components/panchang-table.js +30 -30
  39. package/dist/cdn/components/panchang-table.js.map +4 -4
  40. package/dist/cdn/components/shadbala-table.js +312 -0
  41. package/dist/cdn/components/shadbala-table.js.map +7 -0
  42. package/dist/cdn/components/synastry-chart.js +129 -39
  43. package/dist/cdn/components/synastry-chart.js.map +4 -4
  44. package/dist/cdn/components/tarot-card.js +49 -20
  45. package/dist/cdn/components/tarot-card.js.map +3 -3
  46. package/dist/cdn/components/tarot-spread.js +43 -27
  47. package/dist/cdn/components/tarot-spread.js.map +3 -3
  48. package/dist/cdn/components/transits-table.js +391 -0
  49. package/dist/cdn/components/transits-table.js.map +7 -0
  50. package/dist/cdn/components/vedic-kundli.js +63 -27
  51. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  52. package/dist/cdn/components/yoga-list.js +334 -0
  53. package/dist/cdn/components/yoga-list.js.map +7 -0
  54. package/dist/cdn/roxy-ui.js +2104 -544
  55. package/dist/cdn/roxy-ui.js.map +4 -4
  56. package/dist/components/ashtakavarga-grid.d.ts +26 -0
  57. package/dist/components/ashtakavarga-grid.d.ts.map +1 -0
  58. package/dist/components/ashtakavarga-grid.js +457 -0
  59. package/dist/components/ashtakavarga-grid.js.map +7 -0
  60. package/dist/components/biorhythm-chart.d.ts +2 -46
  61. package/dist/components/biorhythm-chart.d.ts.map +1 -1
  62. package/dist/components/biorhythm-chart.js +24 -23
  63. package/dist/components/biorhythm-chart.js.map +2 -2
  64. package/dist/components/choghadiya-grid.d.ts +19 -0
  65. package/dist/components/choghadiya-grid.d.ts.map +1 -0
  66. package/dist/components/choghadiya-grid.js +304 -0
  67. package/dist/components/choghadiya-grid.js.map +7 -0
  68. package/dist/components/compatibility-card.d.ts +2 -27
  69. package/dist/components/compatibility-card.d.ts.map +1 -1
  70. package/dist/components/compatibility-card.js +50 -29
  71. package/dist/components/compatibility-card.js.map +3 -3
  72. package/dist/components/dasha-timeline.d.ts +2 -31
  73. package/dist/components/dasha-timeline.d.ts.map +1 -1
  74. package/dist/components/dasha-timeline.js +32 -30
  75. package/dist/components/dasha-timeline.js.map +3 -3
  76. package/dist/components/data.d.ts +11 -7
  77. package/dist/components/data.d.ts.map +1 -1
  78. package/dist/components/data.js +16 -6
  79. package/dist/components/data.js.map +3 -3
  80. package/dist/components/divisional-chart.d.ts +20 -0
  81. package/dist/components/divisional-chart.d.ts.map +1 -0
  82. package/dist/components/divisional-chart.js +471 -0
  83. package/dist/components/divisional-chart.js.map +7 -0
  84. package/dist/components/dosha-card.d.ts +2 -16
  85. package/dist/components/dosha-card.d.ts.map +1 -1
  86. package/dist/components/dosha-card.js +45 -43
  87. package/dist/components/dosha-card.js.map +2 -2
  88. package/dist/components/endpoint-form.d.ts +2 -0
  89. package/dist/components/endpoint-form.d.ts.map +1 -1
  90. package/dist/components/endpoint-form.js +71 -11
  91. package/dist/components/endpoint-form.js.map +3 -3
  92. package/dist/components/guna-milan.d.ts +2 -20
  93. package/dist/components/guna-milan.d.ts.map +1 -1
  94. package/dist/components/guna-milan.js +79 -20
  95. package/dist/components/guna-milan.js.map +4 -4
  96. package/dist/components/hexagram.d.ts +3 -27
  97. package/dist/components/hexagram.d.ts.map +1 -1
  98. package/dist/components/hexagram.js +48 -15
  99. package/dist/components/hexagram.js.map +2 -2
  100. package/dist/components/horoscope-card.d.ts +2 -20
  101. package/dist/components/horoscope-card.d.ts.map +1 -1
  102. package/dist/components/horoscope-card.js +54 -18
  103. package/dist/components/horoscope-card.js.map +3 -3
  104. package/dist/components/kp-planets-table.d.ts +2 -21
  105. package/dist/components/kp-planets-table.d.ts.map +1 -1
  106. package/dist/components/kp-planets-table.js +10 -4
  107. package/dist/components/kp-planets-table.js.map +3 -3
  108. package/dist/components/location-search.d.ts +5 -14
  109. package/dist/components/location-search.d.ts.map +1 -1
  110. package/dist/components/location-search.js +45 -5
  111. package/dist/components/location-search.js.map +2 -2
  112. package/dist/components/moon-phase.d.ts +4 -21
  113. package/dist/components/moon-phase.d.ts.map +1 -1
  114. package/dist/components/moon-phase.js +34 -4
  115. package/dist/components/moon-phase.js.map +3 -3
  116. package/dist/components/natal-chart.d.ts +9 -43
  117. package/dist/components/natal-chart.d.ts.map +1 -1
  118. package/dist/components/natal-chart.js +346 -79
  119. package/dist/components/natal-chart.js.map +3 -3
  120. package/dist/components/numerology-card.d.ts +5 -37
  121. package/dist/components/numerology-card.d.ts.map +1 -1
  122. package/dist/components/numerology-card.js +58 -30
  123. package/dist/components/numerology-card.js.map +3 -3
  124. package/dist/components/panchang-table.d.ts +3 -62
  125. package/dist/components/panchang-table.d.ts.map +1 -1
  126. package/dist/components/panchang-table.js +62 -32
  127. package/dist/components/panchang-table.js.map +3 -3
  128. package/dist/components/shadbala-table.d.ts +18 -0
  129. package/dist/components/shadbala-table.d.ts.map +1 -0
  130. package/dist/components/shadbala-table.js +400 -0
  131. package/dist/components/shadbala-table.js.map +7 -0
  132. package/dist/components/synastry-chart.d.ts +9 -28
  133. package/dist/components/synastry-chart.d.ts.map +1 -1
  134. package/dist/components/synastry-chart.js +201 -56
  135. package/dist/components/synastry-chart.js.map +3 -3
  136. package/dist/components/tarot-card.d.ts +5 -29
  137. package/dist/components/tarot-card.d.ts.map +1 -1
  138. package/dist/components/tarot-card.js +59 -20
  139. package/dist/components/tarot-card.js.map +2 -2
  140. package/dist/components/tarot-spread.d.ts +2 -24
  141. package/dist/components/tarot-spread.d.ts.map +1 -1
  142. package/dist/components/tarot-spread.js +39 -13
  143. package/dist/components/tarot-spread.js.map +2 -2
  144. package/dist/components/transits-table.d.ts +21 -0
  145. package/dist/components/transits-table.d.ts.map +1 -0
  146. package/dist/components/transits-table.js +515 -0
  147. package/dist/components/transits-table.js.map +7 -0
  148. package/dist/components/vedic-kundli.d.ts +5 -28
  149. package/dist/components/vedic-kundli.d.ts.map +1 -1
  150. package/dist/components/vedic-kundli.js +147 -83
  151. package/dist/components/vedic-kundli.js.map +3 -3
  152. package/dist/components/yoga-list.d.ts +29 -0
  153. package/dist/components/yoga-list.d.ts.map +1 -0
  154. package/dist/components/yoga-list.js +389 -0
  155. package/dist/components/yoga-list.js.map +7 -0
  156. package/dist/index.cjs +3693 -1180
  157. package/dist/index.cjs.map +4 -4
  158. package/dist/index.d.ts +11 -4
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +3709 -1196
  161. package/dist/index.js.map +4 -4
  162. package/dist/manifest.d.ts +43 -0
  163. package/dist/manifest.d.ts.map +1 -0
  164. package/dist/manifest.json +7 -2
  165. package/dist/styles/tokens.css +73 -1
  166. package/dist/tokens/index.d.ts +6 -0
  167. package/dist/tokens/index.d.ts.map +1 -1
  168. package/dist/types/index.d.ts +2 -0
  169. package/dist/types/index.d.ts.map +1 -0
  170. package/dist/types/types.gen.d.ts +27811 -0
  171. package/dist/types/types.gen.d.ts.map +1 -0
  172. package/dist/utils/debounce.d.ts +9 -1
  173. package/dist/utils/debounce.d.ts.map +1 -1
  174. package/dist/utils/format.d.ts +29 -0
  175. package/dist/utils/format.d.ts.map +1 -0
  176. package/dist/utils/kundli-render.d.ts +63 -0
  177. package/dist/utils/kundli-render.d.ts.map +1 -0
  178. package/dist/utils/string.d.ts +14 -0
  179. package/dist/utils/string.d.ts.map +1 -0
  180. package/dist/version.d.ts +2 -0
  181. package/dist/version.d.ts.map +1 -0
  182. package/package.json +7 -1
  183. package/src/components/ashtakavarga-grid.ts +354 -0
  184. package/src/components/biorhythm-chart.ts +39 -84
  185. package/src/components/choghadiya-grid.ts +185 -0
  186. package/src/components/compatibility-card.ts +85 -52
  187. package/src/components/dasha-timeline.ts +55 -73
  188. package/src/components/data.ts +28 -16
  189. package/src/components/divisional-chart.ts +214 -0
  190. package/src/components/dosha-card.ts +72 -68
  191. package/src/components/endpoint-form.ts +80 -18
  192. package/src/components/guna-milan.ts +87 -47
  193. package/src/components/hexagram.ts +53 -43
  194. package/src/components/horoscope-card.ts +59 -43
  195. package/src/components/kp-planets-table.ts +8 -27
  196. package/src/components/location-search.ts +47 -23
  197. package/src/components/moon-phase.ts +28 -25
  198. package/src/components/natal-chart.ts +364 -110
  199. package/src/components/numerology-card.ts +86 -84
  200. package/src/components/panchang-table.ts +40 -78
  201. package/src/components/shadbala-table.ts +286 -0
  202. package/src/components/synastry-chart.ts +213 -97
  203. package/src/components/tarot-card.ts +76 -62
  204. package/src/components/tarot-spread.ts +72 -45
  205. package/src/components/transits-table.ts +350 -0
  206. package/src/components/vedic-kundli.ts +59 -173
  207. package/src/components/yoga-list.ts +328 -0
  208. package/src/index.ts +18 -26
  209. package/src/manifest.ts +340 -0
  210. package/src/styles/tokens.css +73 -1
  211. package/src/tokens/index.ts +14 -0
  212. package/src/types/types.gen.ts +3 -3
  213. package/src/utils/debounce.ts +23 -4
  214. package/src/utils/format.ts +75 -0
  215. package/src/utils/kundli-render.ts +197 -0
  216. package/src/utils/string.ts +23 -0
  217. package/src/version.ts +2 -0
  218. package/dist/utils/motion.d.ts +0 -13
  219. package/dist/utils/motion.d.ts.map +0 -1
  220. package/src/utils/motion.ts +0 -18
@@ -28,6 +28,23 @@ var SIGN_GLYPH = {
28
28
  Aquarius: "\u2652",
29
29
  Pisces: "\u2653"
30
30
  };
31
+ var SIGNS_ORDER = [
32
+ "Aries",
33
+ "Taurus",
34
+ "Gemini",
35
+ "Cancer",
36
+ "Leo",
37
+ "Virgo",
38
+ "Libra",
39
+ "Scorpio",
40
+ "Sagittarius",
41
+ "Capricorn",
42
+ "Aquarius",
43
+ "Pisces"
44
+ ];
45
+ var RASHI_KEYS = SIGNS_ORDER.map(
46
+ (s) => s.toLowerCase()
47
+ );
31
48
 
32
49
  // packages/ui/src/utils/base-styles.ts
33
50
  import { css } from "lit";
@@ -115,6 +132,12 @@ var baseStyles = css`
115
132
  }
116
133
  `;
117
134
 
135
+ // packages/ui/src/utils/string.ts
136
+ function capitalize(s) {
137
+ if (!s) return "";
138
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
139
+ }
140
+
118
141
  // packages/ui/src/components/horoscope-card.ts
119
142
  var RoxyHoroscopeCard = class extends LitElement {
120
143
  constructor() {
@@ -128,8 +151,8 @@ var RoxyHoroscopeCard = class extends LitElement {
128
151
  return html`<div class="roxy-empty" role="status">No horoscope data</div>`;
129
152
  const sign = d.sign ?? "";
130
153
  const glyph = sign ? SIGN_GLYPH[capitalize(sign)] ?? "" : "";
131
- const energy = typeof d.energyRating === "number" ? d.energyRating : null;
132
- const dateLabel = d.date ?? d.week ?? d.month ?? "";
154
+ const energy = "energyRating" in d && typeof d.energyRating === "number" ? d.energyRating : null;
155
+ const dateLabel = "date" in d && d.date || "week" in d && d.week || "month" in d && d.month || "";
133
156
  return html`<article
134
157
  class="card"
135
158
  aria-label=${`${this.period} horoscope for ${sign}`}
@@ -167,31 +190,40 @@ var RoxyHoroscopeCard = class extends LitElement {
167
190
  <h3>Finance</h3>
168
191
  <p>${d.finance}</p>
169
192
  </div>` : nothing}
170
- ${d.advice ? html`<div class="section">
193
+ ${"advice" in d && d.advice ? html`<div class="section">
171
194
  <h3>Advice</h3>
172
195
  <p>${d.advice}</p>
173
196
  </div>` : nothing}
174
197
  </div>
175
198
 
176
- ${d.luckyNumber || d.luckyColor || (d.compatibleSigns?.length ?? 0) > 0 ? html`<div class="lucky">
177
- ${d.luckyNumber !== void 0 ? html`<span>Lucky number <strong>${d.luckyNumber}</strong></span>` : nothing}
178
- ${d.luckyColor ? html`<span>Lucky color <strong>${d.luckyColor}</strong></span>` : nothing}
179
- ${d.luckyNumbers?.length ? html`<span
199
+ ${(() => {
200
+ const luckyNumber = "luckyNumber" in d && d.luckyNumber !== void 0 ? d.luckyNumber : void 0;
201
+ const luckyColor = "luckyColor" in d && d.luckyColor ? d.luckyColor : "";
202
+ const luckyNumbers = "luckyNumbers" in d && d.luckyNumbers ? d.luckyNumbers : [];
203
+ const luckyDays = "luckyDays" in d && d.luckyDays ? d.luckyDays : [];
204
+ const compatibleSigns = d.compatibleSigns ?? [];
205
+ if (luckyNumber === void 0 && !luckyColor && luckyNumbers.length === 0 && luckyDays.length === 0 && compatibleSigns.length === 0)
206
+ return nothing;
207
+ return html`<div class="lucky">
208
+ ${luckyNumber !== void 0 ? html`<span>Lucky number <strong>${luckyNumber}</strong></span>` : nothing}
209
+ ${luckyColor ? html`<span>Lucky color <strong>${luckyColor}</strong></span>` : nothing}
210
+ ${luckyNumbers.length ? html`<span
180
211
  >Lucky numbers
181
- <strong>${d.luckyNumbers.join(", ")}</strong></span
212
+ <strong>${luckyNumbers.join(", ")}</strong></span
182
213
  >` : nothing}
183
- ${d.luckyDays?.length ? html`<span
184
- >Lucky days <strong>${d.luckyDays.join(", ")}</strong></span
214
+ ${luckyDays.length ? html`<span
215
+ >Lucky days <strong>${luckyDays.join(", ")}</strong></span
185
216
  >` : nothing}
186
- ${d.compatibleSigns?.length ? html`<span class="compat-wrap">
217
+ ${compatibleSigns.length ? html`<span class="compat-wrap">
187
218
  Best with
188
219
  <span class="compat"
189
- >${d.compatibleSigns.map(
190
- (s) => html`<span>${s}</span>`
191
- )}</span
220
+ >${compatibleSigns.map(
221
+ (s) => html`<span>${s}</span>`
222
+ )}</span
192
223
  >
193
224
  </span>` : nothing}
194
- </div>` : nothing}
225
+ </div>`;
226
+ })()}
195
227
  </article>`;
196
228
  }
197
229
  };
@@ -299,6 +331,13 @@ RoxyHoroscopeCard.styles = [
299
331
  font-weight: var(--roxy-weight-bold, 600);
300
332
  }
301
333
 
334
+ .compat-wrap {
335
+ width: 100%;
336
+ display: flex;
337
+ align-items: center;
338
+ flex-wrap: wrap;
339
+ gap: var(--roxy-space-xs, 0.25rem);
340
+ }
302
341
  .compat {
303
342
  display: flex;
304
343
  flex-wrap: wrap;
@@ -323,9 +362,6 @@ __decorateClass([
323
362
  RoxyHoroscopeCard = __decorateClass([
324
363
  customElement("roxy-horoscope-card")
325
364
  ], RoxyHoroscopeCard);
326
- function capitalize(s) {
327
- return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
328
- }
329
365
  export {
330
366
  RoxyHoroscopeCard
331
367
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/components/horoscope-card.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { SIGN_GLYPH } from '../tokens/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ninterface HoroscopeData {\n\tsign?: string;\n\tdate?: string;\n\toverview?: string;\n\tlove?: string;\n\tcareer?: string;\n\thealth?: string;\n\tfinance?: string;\n\tadvice?: string;\n\tluckyNumber?: number | string;\n\tluckyColor?: string;\n\tcompatibleSigns?: string[];\n\tmoonSign?: string;\n\tmoonPhase?: string;\n\tenergyRating?: number;\n\tweek?: string;\n\tmonth?: string;\n\tluckyDays?: string[];\n\tluckyNumbers?: number[];\n}\n\n/**\n * Daily, weekly, or monthly horoscope card. Pass `data` from\n * /astrology/horoscope/{sign}/{daily|weekly|monthly}.\n */\n@customElement('roxy-horoscope-card')\nexport class RoxyHoroscopeCard extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.card {\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.head {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.glyph {\n\t\t\t\tfont-size: 2.25rem;\n\t\t\t\tcolor: var(--roxy-accent-fg, #b45309);\n\t\t\t\tline-height: 1;\n\t\t\t}\n\n\t\t\t.title {\n\t\t\t\tfont-size: var(--roxy-text-xl, 1.5rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tmargin: 0;\n\t\t\t\tletter-spacing: var(--roxy-tracking-tight);\n\t\t\t\ttext-transform: capitalize;\n\t\t\t}\n\n\t\t\t.date {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t}\n\n\t\t\t.energy {\n\t\t\t\tmargin-left: auto;\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\t\t\t.energy-bar {\n\t\t\t\tdisplay: inline-block;\n\t\t\t\twidth: 6rem;\n\t\t\t\theight: 6px;\n\t\t\t\tbackground: var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\toverflow: hidden;\n\t\t\t\tmargin-left: 6px;\n\t\t\t\tvertical-align: middle;\n\t\t\t}\n\t\t\t.energy-bar > span {\n\t\t\t\tdisplay: block;\n\t\t\t\theight: 100%;\n\t\t\t\tbackground: var(--roxy-accent, #f59e0b);\n\t\t\t\ttransition:\n\t\t\t\t\twidth var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\n\t\t\t.overview {\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.sections {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.section h3 {\n\t\t\t\tmargin: 0 0 var(--roxy-space-xs, 0.25rem) 0;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tletter-spacing: 0.06em;\n\t\t\t}\n\t\t\t.section p {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t}\n\n\t\t\t.lucky {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t\tborder-top: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tpadding-top: var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\n\t\t\t.lucky strong {\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\n\t\t\t.compat {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\t.compat span {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-accent, #f59e0b) 16%, transparent);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tpadding: 2px 8px;\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\ttext-transform: capitalize;\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: HoroscopeData | null = null;\n\n\t@property({ type: String, reflect: true })\n\tperiod: 'daily' | 'weekly' | 'monthly' = 'daily';\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 horoscope data</div>`;\n\n\t\tconst sign = d.sign ?? '';\n\t\tconst glyph = sign ? (SIGN_GLYPH[capitalize(sign)] ?? '') : '';\n\t\tconst energy = typeof d.energyRating === 'number' ? d.energyRating : null;\n\t\tconst dateLabel = d.date ?? d.week ?? d.month ?? '';\n\n\t\treturn html`<article\n\t\t\tclass=\"card\"\n\t\t\taria-label=${`${this.period} horoscope for ${sign}`}\n\t\t>\n\t\t\t<header class=\"head\">\n\t\t\t\t<span class=\"glyph\" aria-hidden=\"true\">${glyph}</span>\n\t\t\t\t<div>\n\t\t\t\t\t<h2 class=\"title\">${sign} ${this.period}</h2>\n\t\t\t\t\t${dateLabel ? html`<div class=\"date\">${dateLabel}</div>` : nothing}\n\t\t\t\t</div>\n\t\t\t\t${\n\t\t\t\t\tenergy !== null\n\t\t\t\t\t\t? html`<span class=\"energy\" aria-label=${`Energy ${energy} of 10`}>\n\t\t\t\t\t\t\tEnergy ${energy}/10\n\t\t\t\t\t\t\t<span class=\"energy-bar\"\n\t\t\t\t\t\t\t\t><span style=\"width: ${(energy / 10) * 100}%\"></span\n\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</header>\n\n\t\t\t${d.overview ? html`<p class=\"overview\">${d.overview}</p>` : nothing}\n\n\t\t\t<div class=\"sections\">\n\t\t\t\t${\n\t\t\t\t\td.love\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Love</h3>\n\t\t\t\t\t\t\t<p>${d.love}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.career\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Career</h3>\n\t\t\t\t\t\t\t<p>${d.career}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.health\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Health</h3>\n\t\t\t\t\t\t\t<p>${d.health}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.finance\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Finance</h3>\n\t\t\t\t\t\t\t<p>${d.finance}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.advice\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Advice</h3>\n\t\t\t\t\t\t\t<p>${d.advice}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t${\n\t\t\t\td.luckyNumber || d.luckyColor || (d.compatibleSigns?.length ?? 0) > 0\n\t\t\t\t\t? html`<div class=\"lucky\">\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\td.luckyNumber !== undefined\n\t\t\t\t\t\t\t\t? html`<span>Lucky number <strong>${d.luckyNumber}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\td.luckyColor\n\t\t\t\t\t\t\t\t? html`<span>Lucky color <strong>${d.luckyColor}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\td.luckyNumbers?.length\n\t\t\t\t\t\t\t\t? html`<span\n\t\t\t\t\t\t\t\t\t>Lucky numbers\n\t\t\t\t\t\t\t\t\t<strong>${d.luckyNumbers.join(', ')}</strong></span\n\t\t\t\t\t\t\t\t>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\td.luckyDays?.length\n\t\t\t\t\t\t\t\t? html`<span\n\t\t\t\t\t\t\t\t\t>Lucky days <strong>${d.luckyDays.join(', ')}</strong></span\n\t\t\t\t\t\t\t\t>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\td.compatibleSigns?.length\n\t\t\t\t\t\t\t\t? html`<span class=\"compat-wrap\">\n\t\t\t\t\t\t\t\t\tBest with\n\t\t\t\t\t\t\t\t\t<span class=\"compat\"\n\t\t\t\t\t\t\t\t\t\t>${d.compatibleSigns.map(\n\t\t\t\t\t\t\t\t\t\t\t(s) => html`<span>${s}</span>`,\n\t\t\t\t\t\t\t\t\t\t)}</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t</article>`;\n\t}\n}\n\nfunction capitalize(s: string): string {\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-horoscope-card': RoxyHoroscopeCard;\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};\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/** 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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/components/horoscope-card.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts", "../../src/utils/string.ts"],
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { SIGN_GLYPH } from '../tokens/index.js';\nimport type {\n\tGetDailyHoroscopeResponse,\n\tGetMonthlyHoroscopeResponse,\n\tGetWeeklyHoroscopeResponse,\n} from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { capitalize } from '../utils/string.js';\n\ntype HoroscopeData =\n\t| GetDailyHoroscopeResponse\n\t| GetWeeklyHoroscopeResponse\n\t| GetMonthlyHoroscopeResponse;\n\n/**\n * Daily, weekly, or monthly horoscope card. Pass `data` from\n * /astrology/horoscope/{sign}/{daily|weekly|monthly}.\n */\n@customElement('roxy-horoscope-card')\nexport class RoxyHoroscopeCard extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.card {\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.head {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.glyph {\n\t\t\t\tfont-size: 2.25rem;\n\t\t\t\tcolor: var(--roxy-accent-fg, #b45309);\n\t\t\t\tline-height: 1;\n\t\t\t}\n\n\t\t\t.title {\n\t\t\t\tfont-size: var(--roxy-text-xl, 1.5rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tmargin: 0;\n\t\t\t\tletter-spacing: var(--roxy-tracking-tight);\n\t\t\t\ttext-transform: capitalize;\n\t\t\t}\n\n\t\t\t.date {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t}\n\n\t\t\t.energy {\n\t\t\t\tmargin-left: auto;\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\t\t\t.energy-bar {\n\t\t\t\tdisplay: inline-block;\n\t\t\t\twidth: 6rem;\n\t\t\t\theight: 6px;\n\t\t\t\tbackground: var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\toverflow: hidden;\n\t\t\t\tmargin-left: 6px;\n\t\t\t\tvertical-align: middle;\n\t\t\t}\n\t\t\t.energy-bar > span {\n\t\t\t\tdisplay: block;\n\t\t\t\theight: 100%;\n\t\t\t\tbackground: var(--roxy-accent, #f59e0b);\n\t\t\t\ttransition:\n\t\t\t\t\twidth var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\n\t\t\t.overview {\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.sections {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.section h3 {\n\t\t\t\tmargin: 0 0 var(--roxy-space-xs, 0.25rem) 0;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tletter-spacing: 0.06em;\n\t\t\t}\n\t\t\t.section p {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t}\n\n\t\t\t.lucky {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t\tborder-top: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tpadding-top: var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\n\t\t\t.lucky strong {\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\n\t\t\t.compat-wrap {\n\t\t\t\twidth: 100%;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\t.compat {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\t.compat span {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-accent, #f59e0b) 16%, transparent);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tpadding: 2px 8px;\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\ttext-transform: capitalize;\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: HoroscopeData | null = null;\n\n\t@property({ type: String, reflect: true })\n\tperiod: 'daily' | 'weekly' | 'monthly' = 'daily';\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 horoscope data</div>`;\n\n\t\tconst sign = d.sign ?? '';\n\t\tconst glyph = sign ? (SIGN_GLYPH[capitalize(sign)] ?? '') : '';\n\t\tconst energy =\n\t\t\t'energyRating' in d && typeof d.energyRating === 'number'\n\t\t\t\t? d.energyRating\n\t\t\t\t: null;\n\t\tconst dateLabel =\n\t\t\t('date' in d && d.date) ||\n\t\t\t('week' in d && d.week) ||\n\t\t\t('month' in d && d.month) ||\n\t\t\t'';\n\n\t\treturn html`<article\n\t\t\tclass=\"card\"\n\t\t\taria-label=${`${this.period} horoscope for ${sign}`}\n\t\t>\n\t\t\t<header class=\"head\">\n\t\t\t\t<span class=\"glyph\" aria-hidden=\"true\">${glyph}</span>\n\t\t\t\t<div>\n\t\t\t\t\t<h2 class=\"title\">${sign} ${this.period}</h2>\n\t\t\t\t\t${dateLabel ? html`<div class=\"date\">${dateLabel}</div>` : nothing}\n\t\t\t\t</div>\n\t\t\t\t${\n\t\t\t\t\tenergy !== null\n\t\t\t\t\t\t? html`<span class=\"energy\" aria-label=${`Energy ${energy} of 10`}>\n\t\t\t\t\t\t\tEnergy ${energy}/10\n\t\t\t\t\t\t\t<span class=\"energy-bar\"\n\t\t\t\t\t\t\t\t><span style=\"width: ${(energy / 10) * 100}%\"></span\n\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</header>\n\n\t\t\t${d.overview ? html`<p class=\"overview\">${d.overview}</p>` : nothing}\n\n\t\t\t<div class=\"sections\">\n\t\t\t\t${\n\t\t\t\t\td.love\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Love</h3>\n\t\t\t\t\t\t\t<p>${d.love}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.career\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Career</h3>\n\t\t\t\t\t\t\t<p>${d.career}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.health\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Health</h3>\n\t\t\t\t\t\t\t<p>${d.health}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\td.finance\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Finance</h3>\n\t\t\t\t\t\t\t<p>${d.finance}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\t'advice' in d && d.advice\n\t\t\t\t\t\t? html`<div class=\"section\">\n\t\t\t\t\t\t\t<h3>Advice</h3>\n\t\t\t\t\t\t\t<p>${d.advice}</p>\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t${(() => {\n\t\t\t\tconst luckyNumber =\n\t\t\t\t\t'luckyNumber' in d && d.luckyNumber !== undefined\n\t\t\t\t\t\t? d.luckyNumber\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst luckyColor =\n\t\t\t\t\t'luckyColor' in d && d.luckyColor ? d.luckyColor : '';\n\t\t\t\tconst luckyNumbers =\n\t\t\t\t\t'luckyNumbers' in d && d.luckyNumbers ? d.luckyNumbers : [];\n\t\t\t\tconst luckyDays = 'luckyDays' in d && d.luckyDays ? d.luckyDays : [];\n\t\t\t\tconst compatibleSigns = d.compatibleSigns ?? [];\n\t\t\t\tif (\n\t\t\t\t\tluckyNumber === undefined &&\n\t\t\t\t\t!luckyColor &&\n\t\t\t\t\tluckyNumbers.length === 0 &&\n\t\t\t\t\tluckyDays.length === 0 &&\n\t\t\t\t\tcompatibleSigns.length === 0\n\t\t\t\t)\n\t\t\t\t\treturn nothing;\n\t\t\t\treturn html`<div class=\"lucky\">\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tluckyNumber !== undefined\n\t\t\t\t\t\t\t\t? html`<span>Lucky number <strong>${luckyNumber}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tluckyColor\n\t\t\t\t\t\t\t\t? html`<span>Lucky color <strong>${luckyColor}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tluckyNumbers.length\n\t\t\t\t\t\t\t\t? html`<span\n\t\t\t\t\t\t\t\t\t>Lucky numbers\n\t\t\t\t\t\t\t\t\t<strong>${luckyNumbers.join(', ')}</strong></span\n\t\t\t\t\t\t\t\t>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tluckyDays.length\n\t\t\t\t\t\t\t\t? html`<span\n\t\t\t\t\t\t\t\t\t>Lucky days <strong>${luckyDays.join(', ')}</strong></span\n\t\t\t\t\t\t\t\t>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tcompatibleSigns.length\n\t\t\t\t\t\t\t\t? html`<span class=\"compat-wrap\">\n\t\t\t\t\t\t\t\t\tBest with\n\t\t\t\t\t\t\t\t\t<span class=\"compat\"\n\t\t\t\t\t\t\t\t\t\t>${compatibleSigns.map(\n\t\t\t\t\t\t\t\t\t\t\t(s) => html`<span>${s}</span>`,\n\t\t\t\t\t\t\t\t\t\t)}</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>`;\n\t\t\t})()}\n\t\t</article>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-horoscope-card': RoxyHoroscopeCard;\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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\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
6
  "names": ["css", "css"]
7
7
  }
@@ -1,30 +1,12 @@
1
1
  import { LitElement } from 'lit';
2
- interface KpPlanet {
3
- planet?: string;
4
- name?: string;
5
- sign?: string;
6
- signLord?: string;
7
- nakshatra?: string;
8
- nakshatraLord?: string;
9
- pada?: number;
10
- starLord?: string;
11
- subLord?: string;
12
- subSubLord?: string;
13
- kpNumber?: number;
14
- retrograde?: boolean;
15
- longitude?: number;
16
- }
17
- interface KpData {
18
- ayanamsa?: number | string;
19
- planets?: KpPlanet[];
20
- }
2
+ import type { KpPlanetsResponse } from '../types/index.js';
21
3
  /**
22
4
  * KP planets table with sub-lord and sub-sub-lord columns. Renders
23
5
  * /vedic-astrology/kp/planets.
24
6
  */
25
7
  export declare class RoxyKpPlanetsTable extends LitElement {
26
8
  static styles: import("lit").CSSResult[];
27
- data: KpData | null;
9
+ data: KpPlanetsResponse | null;
28
10
  render(): import("lit").TemplateResult<1>;
29
11
  }
30
12
  declare global {
@@ -32,5 +14,4 @@ declare global {
32
14
  'roxy-kp-planets-table': RoxyKpPlanetsTable;
33
15
  }
34
16
  }
35
- export {};
36
17
  //# sourceMappingURL=kp-planets-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kp-planets-table.d.ts","sourceRoot":"","sources":["../../src/components/kp-planets-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAIrD,UAAU,QAAQ;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,MAAM;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,qBACa,kBAAmB,SAAQ,UAAU;IACjD,MAAM,CAAC,MAAM,4BA8DX;IAGF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3B,MAAM;CAmDN;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,uBAAuB,EAAE,kBAAkB,CAAC;KAC5C;CACD"}
1
+ {"version":3,"file":"kp-planets-table.d.ts","sourceRoot":"","sources":["../../src/components/kp-planets-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D;;;GAGG;AACH,qBACa,kBAAmB,SAAQ,UAAU;IACjD,MAAM,CAAC,MAAM,4BA8DX;IAGF,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEtC,MAAM;CAmDN;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,uBAAuB,EAAE,kBAAkB,CAAC;KAC5C;CACD"}
@@ -99,6 +99,12 @@ var baseStyles = css`
99
99
  }
100
100
  `;
101
101
 
102
+ // packages/ui/src/utils/format.ts
103
+ function formatNumber(value, dp = 1) {
104
+ if (typeof value !== "number" || !Number.isFinite(value)) return "";
105
+ return value.toFixed(dp).replace(/\.?0+$/, "");
106
+ }
107
+
102
108
  // packages/ui/src/components/kp-planets-table.ts
103
109
  var RoxyKpPlanetsTable = class extends LitElement {
104
110
  constructor() {
@@ -116,7 +122,7 @@ var RoxyKpPlanetsTable = class extends LitElement {
116
122
  >
117
123
  <header class="head">
118
124
  <h2 class="title">KP planets</h2>
119
- ${this.data.ayanamsa ? html`<span class="ayanamsa">Ayanamsa: ${this.data.ayanamsa}</span>` : nothing}
125
+ ${typeof this.data.ayanamsa === "number" ? html`<span class="ayanamsa">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}°</span>` : nothing}
120
126
  </header>
121
127
  <table role="table">
122
128
  <thead>
@@ -135,13 +141,13 @@ var RoxyKpPlanetsTable = class extends LitElement {
135
141
  ${planets.map(
136
142
  (p) => html`<tr>
137
143
  <td class="planet">
138
- ${p.planet ?? p.name ?? ""}
144
+ ${p.planet}
139
145
  ${p.retrograde ? html`<span class="retro">R</span>` : nothing}
140
146
  </td>
141
147
  <td>${p.sign ?? ""}</td>
142
148
  <td>${p.signLord ?? ""}</td>
143
149
  <td>${p.nakshatra ?? ""}</td>
144
- <td>${p.starLord ?? p.nakshatraLord ?? ""}</td>
150
+ <td>${p.nakshatraLord ?? ""}</td>
145
151
  <td>${p.subLord ?? ""}</td>
146
152
  <td>${p.subSubLord ?? ""}</td>
147
153
  <td>${p.kpNumber ?? ""}</td>
@@ -209,7 +215,7 @@ RoxyKpPlanetsTable.styles = [
209
215
  color: var(--roxy-fg, #0a0a0a);
210
216
  }
211
217
  .retro {
212
- color: var(--roxy-warning, #ea580c);
218
+ color: var(--roxy-warning-fg, #9a3412);
213
219
  font-size: var(--roxy-text-xs, 0.75rem);
214
220
  margin-left: 4px;
215
221
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/components/kp-planets-table.ts", "../../src/utils/base-styles.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ninterface KpPlanet {\n\tplanet?: string;\n\tname?: string;\n\tsign?: string;\n\tsignLord?: string;\n\tnakshatra?: string;\n\tnakshatraLord?: string;\n\tpada?: number;\n\tstarLord?: string;\n\tsubLord?: string;\n\tsubSubLord?: string;\n\tkpNumber?: number;\n\tretrograde?: boolean;\n\tlongitude?: number;\n}\n\ninterface KpData {\n\tayanamsa?: number | string;\n\tplanets?: KpPlanet[];\n}\n\n/**\n * KP planets table with sub-lord and sub-sub-lord columns. Renders\n * /vedic-astrology/kp/planets.\n */\n@customElement('roxy-kp-planets-table')\nexport class RoxyKpPlanetsTable extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.wrap {\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\toverflow: auto;\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t}\n\t\t\t.head {\n\t\t\t\tpadding: var(--roxy-space-md, 1rem);\n\t\t\t\tborder-bottom: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-lg, 1.125rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.ayanamsa {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\ttable {\n\t\t\t\twidth: 100%;\n\t\t\t\tborder-collapse: collapse;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tmin-width: 560px;\n\t\t\t}\n\t\t\tthead {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);\n\t\t\t}\n\t\t\tth,\n\t\t\ttd {\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\ttext-align: left;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\t\t\tth {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tletter-spacing: 0.04em;\n\t\t\t}\n\t\t\ttbody tr {\n\t\t\t\tborder-top: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t}\n\t\t\ttd.planet {\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t}\n\t\t\t.retro {\n\t\t\t\tcolor: var(--roxy-warning, #ea580c);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tmargin-left: 4px;\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: KpData | null = null;\n\n\trender() {\n\t\tif (!this.data)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No KP data</div>`;\n\t\tconst planets = this.data.planets ?? [];\n\n\t\treturn html`<div\n\t\t\tclass=\"wrap\"\n\t\t\taria-label=\"KP planets table\"\n\t\t\ttabindex=\"0\"\n\t\t>\n\t\t\t<header class=\"head\">\n\t\t\t\t<h2 class=\"title\">KP planets</h2>\n\t\t\t\t${\n\t\t\t\t\tthis.data.ayanamsa\n\t\t\t\t\t\t? html`<span class=\"ayanamsa\">Ayanamsa: ${this.data.ayanamsa}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</header>\n\t\t\t<table role=\"table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"col\">Planet</th>\n\t\t\t\t\t\t<th scope=\"col\">Sign</th>\n\t\t\t\t\t\t<th scope=\"col\">Sign lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Nakshatra</th>\n\t\t\t\t\t\t<th scope=\"col\">Star lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Sub lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Sub sub lord</th>\n\t\t\t\t\t\t<th scope=\"col\">KP no.</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t${planets.map(\n\t\t\t\t\t\t(p) => html`<tr>\n\t\t\t\t\t\t\t<td class=\"planet\">\n\t\t\t\t\t\t\t\t${p.planet ?? p.name ?? ''}\n\t\t\t\t\t\t\t\t${p.retrograde ? html`<span class=\"retro\">R</span>` : nothing}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>${p.sign ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.signLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.nakshatra ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.starLord ?? p.nakshatraLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.subLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.subSubLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.kpNumber ?? ''}</td>\n\t\t\t\t\t\t</tr>`,\n\t\t\t\t\t)}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-kp-planets-table': RoxyKpPlanetsTable;\n\t}\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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n"],
5
- "mappings": ";;;;;;;;;;;;AAAA,SAAS,OAAAA,MAAK,MAAM,YAAY,eAAe;AAC/C,SAAS,eAAe,gBAAgB;;;ACDxC,SAAS,WAAW;AAMb,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ADwBnB,IAAM,qBAAN,cAAiC,WAAW;AAAA,EAA5C;AAAA;AAkEN,gBAAsB;AAAA;AAAA,EAEtB,SAAS;AACR,QAAI,CAAC,KAAK;AACT,aAAO;AACR,UAAM,UAAU,KAAK,KAAK,WAAW,CAAC;AAEtC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQJ,KAAK,KAAK,WACP,wCAAwC,KAAK,KAAK,QAAQ,YAC1D,OACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAgBG,QAAQ;AAAA,MACT,CAAC,MAAM;AAAA;AAAA,UAEH,EAAE,UAAU,EAAE,QAAQ,EAAE;AAAA,UACxB,EAAE,aAAa,qCAAqC,OAAO;AAAA;AAAA,aAExD,EAAE,QAAQ,EAAE;AAAA,aACZ,EAAE,YAAY,EAAE;AAAA,aAChB,EAAE,aAAa,EAAE;AAAA,aACjB,EAAE,YAAY,EAAE,iBAAiB,EAAE;AAAA,aACnC,EAAE,WAAW,EAAE;AAAA,aACf,EAAE,cAAc,EAAE;AAAA,aAClB,EAAE,YAAY,EAAE;AAAA;AAAA,IAExB,CAAC;AAAA;AAAA;AAAA;AAAA,EAIL;AACD;AAvHa,mBACL,SAAS;AAAA,EACf;AAAA,EACAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4DD;AAGA;AAAA,EADC,SAAS,EAAE,WAAW,MAAM,CAAC;AAAA,GAjElB,mBAkEZ;AAlEY,qBAAN;AAAA,EADN,cAAc,uBAAuB;AAAA,GACzB;",
3
+ "sources": ["../../src/components/kp-planets-table.ts", "../../src/utils/base-styles.ts", "../../src/utils/format.ts"],
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { KpPlanetsResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\n\n/**\n * KP planets table with sub-lord and sub-sub-lord columns. Renders\n * /vedic-astrology/kp/planets.\n */\n@customElement('roxy-kp-planets-table')\nexport class RoxyKpPlanetsTable extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.wrap {\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\toverflow: auto;\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t}\n\t\t\t.head {\n\t\t\t\tpadding: var(--roxy-space-md, 1rem);\n\t\t\t\tborder-bottom: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-lg, 1.125rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.ayanamsa {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\ttable {\n\t\t\t\twidth: 100%;\n\t\t\t\tborder-collapse: collapse;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tmin-width: 560px;\n\t\t\t}\n\t\t\tthead {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-border, #e4e4e7) 20%, transparent);\n\t\t\t}\n\t\t\tth,\n\t\t\ttd {\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\ttext-align: left;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\t\t\tth {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tletter-spacing: 0.04em;\n\t\t\t}\n\t\t\ttbody tr {\n\t\t\t\tborder-top: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t}\n\t\t\ttd.planet {\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t}\n\t\t\t.retro {\n\t\t\t\tcolor: var(--roxy-warning-fg, #9a3412);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tmargin-left: 4px;\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: KpPlanetsResponse | null = null;\n\n\trender() {\n\t\tif (!this.data)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No KP data</div>`;\n\t\tconst planets = this.data.planets ?? [];\n\n\t\treturn html`<div\n\t\t\tclass=\"wrap\"\n\t\t\taria-label=\"KP planets table\"\n\t\t\ttabindex=\"0\"\n\t\t>\n\t\t\t<header class=\"head\">\n\t\t\t\t<h2 class=\"title\">KP planets</h2>\n\t\t\t\t${\n\t\t\t\t\ttypeof this.data.ayanamsa === 'number'\n\t\t\t\t\t\t? html`<span class=\"ayanamsa\">Ayanamsa: ${formatNumber(this.data.ayanamsa, 2)}\u00B0</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</header>\n\t\t\t<table role=\"table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"col\">Planet</th>\n\t\t\t\t\t\t<th scope=\"col\">Sign</th>\n\t\t\t\t\t\t<th scope=\"col\">Sign lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Nakshatra</th>\n\t\t\t\t\t\t<th scope=\"col\">Star lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Sub lord</th>\n\t\t\t\t\t\t<th scope=\"col\">Sub sub lord</th>\n\t\t\t\t\t\t<th scope=\"col\">KP no.</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t${planets.map(\n\t\t\t\t\t\t(p) => html`<tr>\n\t\t\t\t\t\t\t<td class=\"planet\">\n\t\t\t\t\t\t\t\t${p.planet}\n\t\t\t\t\t\t\t\t${p.retrograde ? html`<span class=\"retro\">R</span>` : nothing}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>${p.sign ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.signLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.nakshatra ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.nakshatraLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.subLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.subSubLord ?? ''}</td>\n\t\t\t\t\t\t\t<td>${p.kpNumber ?? ''}</td>\n\t\t\t\t\t\t</tr>`,\n\t\t\t\t\t)}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-kp-planets-table': RoxyKpPlanetsTable;\n\t}\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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n", "/**\n * Display formatters for ISO timestamps and floats coming back from the API.\n * Every helper returns \"\" for nullish or unparseable input so it falls out of\n * template literals cleanly.\n */\n\nexport function formatTime(input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tif (/^\\d{4}-\\d{2}-\\d{2}$/.test(input)) return '';\n\tconst bareTime = /^\\d{2}:\\d{2}(:\\d{2})?$/.test(input);\n\tconst iso = bareTime ? `1970-01-01T${input}` : input;\n\tconst d = new Date(iso);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleTimeString(undefined, {\n\t\thour: 'numeric',\n\t\tminute: '2-digit',\n\t\thour12: true,\n\t});\n}\n\nexport function formatDate(input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tconst d = new Date(\n\t\t/^\\d{4}-\\d{2}-\\d{2}$/.test(input) ? `${input}T00:00:00` : input,\n\t);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleDateString(undefined, {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: 'numeric',\n\t});\n}\n\nexport function formatTimeRange(\n\tt: { start?: string; end?: string } | undefined,\n): string {\n\tif (!t) return '';\n\tconst start = formatTime(t.start);\n\tconst end = formatTime(t.end);\n\tif (start && end) return `${start} - ${end}`;\n\treturn start || end || '';\n}\n\nexport function formatNumber(value: unknown, dp = 1): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn value.toFixed(dp).replace(/\\.?0+$/, '');\n}\n\nexport function formatPercent(value: unknown, dp = 1): string {\n\tconst n = formatNumber(value, dp);\n\treturn n ? `${n}%` : '';\n}\n\n/**\n * CSS class name per aspect type. Used by natal and synastry chart aspect\n * lines so the same color encoding (harmonious vs challenging) applies in\n * both wheels. Keys are lowercase canonical names, values are CSS class\n * suffixes the chart components define in their `:host` styles.\n */\nexport const ASPECT_CLASS: Record<string, string> = {\n\tconjunction: 'aspect-conjunction',\n\tsextile: 'aspect-sextile',\n\tsquare: 'aspect-square',\n\ttrine: 'aspect-trine',\n\topposition: 'aspect-opposition',\n};\n\n/**\n * Normalize an aspect entry's `type` field to a lowercase, hyphen-separated\n * canonical name (`SEMI_SEXTILE` \u2192 `semi-sextile`). Accepts any aspect-shaped\n * object so both natal and synastry inter-aspect entries can share this.\n */\nexport function normalizeAspect(a: { type?: string }): string {\n\treturn (a.type ?? '').toLowerCase().replace(/_/g, '-');\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;AAAA,SAAS,OAAAA,MAAK,MAAM,YAAY,eAAe;AAC/C,SAAS,eAAe,gBAAgB;;;ACDxC,SAAS,WAAW;AAMb,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqCnB,SAAS,aAAa,OAAgB,KAAK,GAAW;AAC5D,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,MAAM,QAAQ,EAAE,EAAE,QAAQ,UAAU,EAAE;AAC9C;;;AFnCO,IAAM,qBAAN,cAAiC,WAAW;AAAA,EAA5C;AAAA;AAkEN,gBAAiC;AAAA;AAAA,EAEjC,SAAS;AACR,QAAI,CAAC,KAAK;AACT,aAAO;AACR,UAAM,UAAU,KAAK,KAAK,WAAW,CAAC;AAEtC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQJ,OAAO,KAAK,KAAK,aAAa,WAC3B,wCAAwC,aAAa,KAAK,KAAK,UAAU,CAAC,CAAC,aAC3E,OACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAgBG,QAAQ;AAAA,MACT,CAAC,MAAM;AAAA;AAAA,UAEH,EAAE,MAAM;AAAA,UACR,EAAE,aAAa,qCAAqC,OAAO;AAAA;AAAA,aAExD,EAAE,QAAQ,EAAE;AAAA,aACZ,EAAE,YAAY,EAAE;AAAA,aAChB,EAAE,aAAa,EAAE;AAAA,aACjB,EAAE,iBAAiB,EAAE;AAAA,aACrB,EAAE,WAAW,EAAE;AAAA,aACf,EAAE,cAAc,EAAE;AAAA,aAClB,EAAE,YAAY,EAAE;AAAA;AAAA,IAExB,CAAC;AAAA;AAAA;AAAA;AAAA,EAIL;AACD;AAvHa,mBACL,SAAS;AAAA,EACf;AAAA,EACAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4DD;AAGA;AAAA,EADC,SAAS,EAAE,WAAW,MAAM,CAAC;AAAA,GAjElB,mBAkEZ;AAlEY,qBAAN;AAAA,EADN,cAAc,uBAAuB;AAAA,GACzB;",
6
6
  "names": ["css", "css"]
7
7
  }
@@ -1,23 +1,11 @@
1
1
  import { LitElement } from 'lit';
2
- export interface CityResult {
3
- city: string;
4
- province?: string;
5
- country: string;
6
- iso2?: string;
7
- latitude: number;
8
- longitude: number;
9
- timezone: string;
10
- utcOffset: number;
11
- population?: number;
12
- }
13
2
  /**
14
3
  * Stateful location search input. Calls /location/search and emits
15
4
  * `roxy-location-select` CustomEvent with the chosen city. Required for any
16
5
  * chart endpoint.
17
6
  *
18
- * Lifted from jyotish-vedic-astrology-app/src/components/city-search.tsx,
19
- * keeping the 300ms debounce and click-outside behavior, replacing React
20
- * state with Lit reactive properties and using direct fetch to RoxyAPI.
7
+ * Behavior: 300ms input debounce, click-outside dismiss, keyboard navigation
8
+ * with arrow keys / Enter / Escape, AbortController on stale requests.
21
9
  *
22
10
  * Attributes:
23
11
  * api-key optional. Direct call to roxyapi.com when set.
@@ -39,9 +27,12 @@ export declare class RoxyLocationSearch extends LitElement {
39
27
  private isLoading;
40
28
  private highlight;
41
29
  private clickOutsideHandler?;
30
+ private abortController?;
31
+ private secretKeyWarned;
42
32
  private debouncedFetch;
43
33
  connectedCallback(): void;
44
34
  disconnectedCallback(): void;
35
+ private warnIfSecretKey;
45
36
  private fetchResults;
46
37
  private onInput;
47
38
  private select;
@@ -1 +1 @@
1
- {"version":3,"file":"location-search.d.ts","sourceRoot":"","sources":["../../src/components/location-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAKrD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;;;;;;;;;;;;;;GAeG;AACH,qBACa,kBAAmB,SAAQ,UAAU;IACjD,MAAM,CAAC,MAAM,4BAwGX;IAGF,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,QAAQ,SAAgD;IAGxD,WAAW,SAAiB;IAG5B,YAAY,SAAM;IAGlB,OAAO,CAAC,KAAK,CAAM;IAGnB,OAAO,CAAC,OAAO,CAAoB;IAGnC,OAAO,CAAC,MAAM,CAAS;IAGvB,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,SAAS,CAAM;IAEvB,OAAO,CAAC,mBAAmB,CAAC,CAA0B;IACtD,OAAO,CAAC,cAAc,CAEd;IAER,iBAAiB,IAAI,IAAI;IAUzB,oBAAoB,IAAI,IAAI;YAOd,YAAY;IAyB1B,OAAO,CAAC,OAAO,CAUb;IAEF,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,SAAS,CAsBf;IAEF,MAAM;CAwDN;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,sBAAsB,EAAE,kBAAkB,CAAC;KAC3C;CACD"}
1
+ {"version":3,"file":"location-search.d.ts","sourceRoot":"","sources":["../../src/components/location-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAQrD;;;;;;;;;;;;;;GAcG;AACH,qBACa,kBAAmB,SAAQ,UAAU;IACjD,MAAM,CAAC,MAAM,4BAwGX;IAGF,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,QAAQ,SAAgD;IAGxD,WAAW,SAAiB;IAG5B,YAAY,SAAM;IAGlB,OAAO,CAAC,KAAK,CAAM;IAGnB,OAAO,CAAC,OAAO,CAAoB;IAGnC,OAAO,CAAC,MAAM,CAAS;IAGvB,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,SAAS,CAAM;IAEvB,OAAO,CAAC,mBAAmB,CAAC,CAA0B;IACtD,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,cAAc,CAEd;IAER,iBAAiB,IAAI,IAAI;IAUzB,oBAAoB,IAAI,IAAI;IAY5B,OAAO,CAAC,eAAe;YAqBT,YAAY;IAoC1B,OAAO,CAAC,OAAO,CAUb;IAEF,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,SAAS,CAsBf;IAEF,MAAM;CAwDN;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,sBAAsB,EAAE,kBAAkB,CAAC;KAC3C;CACD"}
@@ -102,10 +102,20 @@ var baseStyles = css`
102
102
  // packages/ui/src/utils/debounce.ts
103
103
  function debounce(fn, wait) {
104
104
  let timer;
105
- return ((...args) => {
105
+ const debounced = ((...args) => {
106
106
  if (timer) clearTimeout(timer);
107
- timer = setTimeout(() => fn(...args), wait);
107
+ timer = setTimeout(() => {
108
+ timer = void 0;
109
+ fn(...args);
110
+ }, wait);
108
111
  });
112
+ debounced.cancel = () => {
113
+ if (timer) {
114
+ clearTimeout(timer);
115
+ timer = void 0;
116
+ }
117
+ };
118
+ return debounced;
109
119
  }
110
120
 
111
121
  // packages/ui/src/components/location-search.ts
@@ -120,6 +130,7 @@ var RoxyLocationSearch = class extends LitElement {
120
130
  this.isOpen = false;
121
131
  this.isLoading = false;
122
132
  this.highlight = -1;
133
+ this.secretKeyWarned = false;
123
134
  this.debouncedFetch = debounce((q) => {
124
135
  void this.fetchResults(q);
125
136
  }, 300);
@@ -171,8 +182,32 @@ var RoxyLocationSearch = class extends LitElement {
171
182
  if (this.clickOutsideHandler) {
172
183
  document.removeEventListener("mousedown", this.clickOutsideHandler);
173
184
  }
185
+ this.debouncedFetch.cancel();
186
+ if (this.abortController) {
187
+ this.abortController.abort();
188
+ this.abortController = void 0;
189
+ }
190
+ }
191
+ warnIfSecretKey() {
192
+ if (this.secretKeyWarned) return;
193
+ if (!this.apiKey) return;
194
+ if (this.apiKey.startsWith("pk_")) return;
195
+ this.secretKeyWarned = true;
196
+ const message = "Possible secret key in client-side <roxy-location-search>; use a `pk_` publishable key with origin allowlist instead.";
197
+ console.warn(message);
198
+ this.dispatchEvent(
199
+ new CustomEvent("roxy-validation-error", {
200
+ detail: { reason: "possible-secret-key", message },
201
+ bubbles: true,
202
+ composed: true
203
+ })
204
+ );
174
205
  }
175
206
  async fetchResults(q) {
207
+ this.warnIfSecretKey();
208
+ if (this.abortController) this.abortController.abort();
209
+ const controller = new AbortController();
210
+ this.abortController = controller;
176
211
  this.isLoading = true;
177
212
  try {
178
213
  const url = new URL(this.endpoint);
@@ -183,17 +218,22 @@ var RoxyLocationSearch = class extends LitElement {
183
218
  };
184
219
  if (this.apiKey) headers["X-API-Key"] = this.apiKey;
185
220
  if (this.publishableKey) headers["X-API-Key"] = this.publishableKey;
186
- const res = await fetch(url, { headers });
221
+ const res = await fetch(url, { headers, signal: controller.signal });
187
222
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
188
223
  const json = await res.json();
224
+ if (controller.signal.aborted) return;
189
225
  this.results = json.cities ?? [];
190
226
  this.isOpen = this.results.length > 0;
191
227
  this.highlight = this.results.length > 0 ? 0 : -1;
192
- } catch (_err) {
228
+ } catch (err) {
229
+ if (err?.name === "AbortError") return;
193
230
  this.results = [];
194
231
  this.isOpen = false;
195
232
  } finally {
196
- this.isLoading = false;
233
+ if (this.abortController === controller) {
234
+ this.abortController = void 0;
235
+ }
236
+ if (!controller.signal.aborted) this.isLoading = false;
197
237
  }
198
238
  }
199
239
  select(city) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/location-search.ts", "../../src/utils/base-styles.ts", "../../src/utils/debounce.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { debounce } from '../utils/debounce.js';\n\nexport interface CityResult {\n\tcity: string;\n\tprovince?: string;\n\tcountry: string;\n\tiso2?: string;\n\tlatitude: number;\n\tlongitude: number;\n\ttimezone: string;\n\tutcOffset: number;\n\tpopulation?: number;\n}\n\ninterface CitySearchResponse {\n\ttotal?: number;\n\tcities?: CityResult[];\n}\n\n/**\n * Stateful location search input. Calls /location/search and emits\n * `roxy-location-select` CustomEvent with the chosen city. Required for any\n * chart endpoint.\n *\n * Lifted from jyotish-vedic-astrology-app/src/components/city-search.tsx,\n * keeping the 300ms debounce and click-outside behavior, replacing React\n * state with Lit reactive properties and using direct fetch to RoxyAPI.\n *\n * Attributes:\n * api-key optional. Direct call to roxyapi.com when set.\n * publishable-key optional. Browser-safe pk_* key with allowed_origins.\n * endpoint optional. Override URL (default https://roxyapi.com/api/v2/location/search).\n * placeholder optional. Input placeholder.\n * default-value optional. Pre-filled query.\n */\n@customElement('roxy-location-search')\nexport class RoxyLocationSearch extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t:host {\n\t\t\t\tdisplay: block;\n\t\t\t\tposition: relative;\n\t\t\t}\n\t\t\t.field {\n\t\t\t\tposition: relative;\n\t\t\t}\n\t\t\tinput {\n\t\t\t\twidth: 100%;\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-family: inherit;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\ttransition:\n\t\t\t\t\tborder-color var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t\tbox-sizing: border-box;\n\t\t\t}\n\t\t\tinput:focus {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t\tborder-color: var(--roxy-accent-fg, #b45309);\n\t\t\t}\n\t\t\t.spinner {\n\t\t\t\tposition: absolute;\n\t\t\t\tright: 12px;\n\t\t\t\ttop: 50%;\n\t\t\t\ttransform: translateY(-50%);\n\t\t\t\twidth: 14px;\n\t\t\t\theight: 14px;\n\t\t\t\tborder: 2px solid var(--roxy-muted, #71717a);\n\t\t\t\tborder-top-color: transparent;\n\t\t\t\tborder-radius: 50%;\n\t\t\t\tanimation: roxy-spin 700ms linear infinite;\n\t\t\t}\n\t\t\t@keyframes roxy-spin {\n\t\t\t\tto {\n\t\t\t\t\ttransform: translateY(-50%) rotate(360deg);\n\t\t\t\t}\n\t\t\t}\n\t\t\t@media (prefers-reduced-motion: reduce) {\n\t\t\t\t.spinner {\n\t\t\t\t\tanimation: none;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.results {\n\t\t\t\tposition: absolute;\n\t\t\t\tz-index: 50;\n\t\t\t\ttop: calc(100% + 4px);\n\t\t\t\tleft: 0;\n\t\t\t\tright: 0;\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tbox-shadow: var(--roxy-shadow-md);\n\t\t\t\tmax-height: 22rem;\n\t\t\t\toverflow-y: auto;\n\t\t\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\t\t\t.option {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: baseline;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t\twidth: 100%;\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tbackground: transparent;\n\t\t\t\tborder: 0;\n\t\t\t\ttext-align: left;\n\t\t\t\tfont-family: inherit;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransition: background-color var(--roxy-motion-duration, 200ms);\n\t\t\t}\n\t\t\t.option:hover,\n\t\t\t.option[aria-selected='true'] {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-accent, #f59e0b) 10%, transparent);\n\t\t\t}\n\t\t\t.option .city {\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.option .where {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tflex-grow: 1;\n\t\t\t}\n\t\t\t.option .tz {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t}\n\t\t\t.empty {\n\t\t\t\tpadding: var(--roxy-space-md, 1rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String, attribute: 'api-key' })\n\tapiKey?: string;\n\n\t@property({ type: String, attribute: 'publishable-key' })\n\tpublishableKey?: string;\n\n\t@property({ type: String })\n\tendpoint = 'https://roxyapi.com/api/v2/location/search';\n\n\t@property({ type: String })\n\tplaceholder = 'Search city';\n\n\t@property({ type: String, attribute: 'default-value' })\n\tdefaultValue = '';\n\n\t@state()\n\tprivate query = '';\n\n\t@state()\n\tprivate results: CityResult[] = [];\n\n\t@state()\n\tprivate isOpen = false;\n\n\t@state()\n\tprivate isLoading = false;\n\n\t@state()\n\tprivate highlight = -1;\n\n\tprivate clickOutsideHandler?: (e: MouseEvent) => void;\n\tprivate debouncedFetch = debounce((q: string) => {\n\t\tvoid this.fetchResults(q);\n\t}, 300);\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis.query = this.defaultValue;\n\t\tthis.clickOutsideHandler = (e: MouseEvent) => {\n\t\t\tconst path = e.composedPath();\n\t\t\tif (!path.includes(this)) this.isOpen = false;\n\t\t};\n\t\tdocument.addEventListener('mousedown', this.clickOutsideHandler);\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tsuper.disconnectedCallback();\n\t\tif (this.clickOutsideHandler) {\n\t\t\tdocument.removeEventListener('mousedown', this.clickOutsideHandler);\n\t\t}\n\t}\n\n\tprivate async fetchResults(q: string) {\n\t\tthis.isLoading = true;\n\t\ttry {\n\t\t\tconst url = new URL(this.endpoint);\n\t\t\turl.searchParams.set('q', q);\n\t\t\turl.searchParams.set('limit', '8');\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tAccept: 'application/json',\n\t\t\t};\n\t\t\tif (this.apiKey) headers['X-API-Key'] = this.apiKey;\n\t\t\tif (this.publishableKey) headers['X-API-Key'] = this.publishableKey;\n\t\t\tconst res = await fetch(url, { headers });\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\tconst json = (await res.json()) as CitySearchResponse;\n\t\t\tthis.results = json.cities ?? [];\n\t\t\tthis.isOpen = this.results.length > 0;\n\t\t\tthis.highlight = this.results.length > 0 ? 0 : -1;\n\t\t} catch (_err) {\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t} finally {\n\t\t\tthis.isLoading = false;\n\t\t}\n\t}\n\n\tprivate onInput = (e: Event) => {\n\t\tconst value = (e.target as HTMLInputElement).value;\n\t\tthis.query = value;\n\t\tif (value.length < 2) {\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t\tthis.highlight = -1;\n\t\t\treturn;\n\t\t}\n\t\tthis.debouncedFetch(value);\n\t};\n\n\tprivate select(city: CityResult) {\n\t\tthis.query = `${city.city}${city.province ? `, ${city.province}` : ''}, ${city.country}`;\n\t\tthis.isOpen = false;\n\t\tthis.results = [];\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-location-select', {\n\t\t\t\tdetail: city,\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate onKeyDown = (e: KeyboardEvent) => {\n\t\tif (!this.isOpen || this.results.length === 0) {\n\t\t\tif (e.key === 'ArrowDown' && this.query.length >= 2) {\n\t\t\t\tvoid this.fetchResults(this.query);\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (e.key === 'ArrowDown') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight = (this.highlight + 1) % this.results.length;\n\t\t} else if (e.key === 'ArrowUp') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight =\n\t\t\t\t(this.highlight - 1 + this.results.length) % this.results.length;\n\t\t} else if (e.key === 'Enter') {\n\t\t\te.preventDefault();\n\t\t\tconst target = this.results[this.highlight] ?? this.results[0];\n\t\t\tif (target) this.select(target);\n\t\t} else if (e.key === 'Escape') {\n\t\t\tthis.isOpen = false;\n\t\t}\n\t};\n\n\trender() {\n\t\treturn html`<div class=\"field\">\n\t\t\t<input\n\t\t\t\ttype=\"text\"\n\t\t\t\trole=\"combobox\"\n\t\t\t\taria-expanded=${this.isOpen ? 'true' : 'false'}\n\t\t\t\taria-controls=\"roxy-location-listbox\"\n\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\tautocomplete=\"off\"\n\t\t\t\tplaceholder=${this.placeholder}\n\t\t\t\t.value=${this.query}\n\t\t\t\t@input=${this.onInput}\n\t\t\t\t@keydown=${this.onKeyDown}\n\t\t\t\t@focus=${() => {\n\t\t\t\t\tif (this.results.length > 0) this.isOpen = true;\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t${this.isLoading ? html`<span class=\"spinner\" role=\"status\" aria-label=\"Loading\"></span>` : nothing}\n\t\t\t${\n\t\t\t\tthis.isOpen\n\t\t\t\t\t? html`<ul\n\t\t\t\t\t\tid=\"roxy-location-listbox\"\n\t\t\t\t\t\tclass=\"results\"\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t>\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tthis.results.length === 0\n\t\t\t\t\t\t\t\t? html`<li class=\"empty\" role=\"status\">No cities found</li>`\n\t\t\t\t\t\t\t\t: this.results.map(\n\t\t\t\t\t\t\t\t\t\t(city, idx) => html`<li role=\"presentation\">\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"option\"\n\t\t\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\t\t\taria-selected=${this.highlight === idx ? 'true' : 'false'}\n\t\t\t\t\t\t\t\t\t\t\t@click=${() => this.select(city)}\n\t\t\t\t\t\t\t\t\t\t\t@mouseenter=${() => {\n\t\t\t\t\t\t\t\t\t\t\t\tthis.highlight = idx;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"city\">${city.city}</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"where\"\n\t\t\t\t\t\t\t\t\t\t\t\t>${city.province ? html`${city.province}, ` : ''}${city.country}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"tz\"\n\t\t\t\t\t\t\t\t\t\t\t\t>UTC${city.utcOffset >= 0 ? '+' : ''}${city.utcOffset}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</li>`,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t</ul>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-location-search': RoxyLocationSearch;\n\t}\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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n", "/**\n * Lightweight debounce for input handlers. Used by location search.\n */\nexport function debounce<F extends (...args: never[]) => unknown>(\n\tfn: F,\n\twait: number,\n): F {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\treturn ((...args: Parameters<F>) => {\n\t\tif (timer) clearTimeout(timer);\n\t\ttimer = setTimeout(() => fn(...args), wait);\n\t}) as F;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport type { SearchCitiesResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { debounce } from '../utils/debounce.js';\n\ntype CityResult = SearchCitiesResponse['cities'][number];\n\n/**\n * Stateful location search input. Calls /location/search and emits\n * `roxy-location-select` CustomEvent with the chosen city. Required for any\n * chart endpoint.\n *\n * Behavior: 300ms input debounce, click-outside dismiss, keyboard navigation\n * with arrow keys / Enter / Escape, AbortController on stale requests.\n *\n * Attributes:\n * api-key optional. Direct call to roxyapi.com when set.\n * publishable-key optional. Browser-safe pk_* key with allowed_origins.\n * endpoint optional. Override URL (default https://roxyapi.com/api/v2/location/search).\n * placeholder optional. Input placeholder.\n * default-value optional. Pre-filled query.\n */\n@customElement('roxy-location-search')\nexport class RoxyLocationSearch extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t:host {\n\t\t\t\tdisplay: block;\n\t\t\t\tposition: relative;\n\t\t\t}\n\t\t\t.field {\n\t\t\t\tposition: relative;\n\t\t\t}\n\t\t\tinput {\n\t\t\t\twidth: 100%;\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-family: inherit;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\ttransition:\n\t\t\t\t\tborder-color var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t\tbox-sizing: border-box;\n\t\t\t}\n\t\t\tinput:focus {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t\tborder-color: var(--roxy-accent-fg, #b45309);\n\t\t\t}\n\t\t\t.spinner {\n\t\t\t\tposition: absolute;\n\t\t\t\tright: 12px;\n\t\t\t\ttop: 50%;\n\t\t\t\ttransform: translateY(-50%);\n\t\t\t\twidth: 14px;\n\t\t\t\theight: 14px;\n\t\t\t\tborder: 2px solid var(--roxy-muted, #71717a);\n\t\t\t\tborder-top-color: transparent;\n\t\t\t\tborder-radius: 50%;\n\t\t\t\tanimation: roxy-spin 700ms linear infinite;\n\t\t\t}\n\t\t\t@keyframes roxy-spin {\n\t\t\t\tto {\n\t\t\t\t\ttransform: translateY(-50%) rotate(360deg);\n\t\t\t\t}\n\t\t\t}\n\t\t\t@media (prefers-reduced-motion: reduce) {\n\t\t\t\t.spinner {\n\t\t\t\t\tanimation: none;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.results {\n\t\t\t\tposition: absolute;\n\t\t\t\tz-index: 50;\n\t\t\t\ttop: calc(100% + 4px);\n\t\t\t\tleft: 0;\n\t\t\t\tright: 0;\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tbox-shadow: var(--roxy-shadow-md);\n\t\t\t\tmax-height: 22rem;\n\t\t\t\toverflow-y: auto;\n\t\t\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\t\t\t.option {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: baseline;\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t\twidth: 100%;\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tbackground: transparent;\n\t\t\t\tborder: 0;\n\t\t\t\ttext-align: left;\n\t\t\t\tfont-family: inherit;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransition: background-color var(--roxy-motion-duration, 200ms);\n\t\t\t}\n\t\t\t.option:hover,\n\t\t\t.option[aria-selected='true'] {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-accent, #f59e0b) 10%, transparent);\n\t\t\t}\n\t\t\t.option .city {\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.option .where {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tflex-grow: 1;\n\t\t\t}\n\t\t\t.option .tz {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t}\n\t\t\t.empty {\n\t\t\t\tpadding: var(--roxy-space-md, 1rem);\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String, attribute: 'api-key' })\n\tapiKey?: string;\n\n\t@property({ type: String, attribute: 'publishable-key' })\n\tpublishableKey?: string;\n\n\t@property({ type: String })\n\tendpoint = 'https://roxyapi.com/api/v2/location/search';\n\n\t@property({ type: String })\n\tplaceholder = 'Search city';\n\n\t@property({ type: String, attribute: 'default-value' })\n\tdefaultValue = '';\n\n\t@state()\n\tprivate query = '';\n\n\t@state()\n\tprivate results: CityResult[] = [];\n\n\t@state()\n\tprivate isOpen = false;\n\n\t@state()\n\tprivate isLoading = false;\n\n\t@state()\n\tprivate highlight = -1;\n\n\tprivate clickOutsideHandler?: (e: MouseEvent) => void;\n\tprivate abortController?: AbortController;\n\tprivate secretKeyWarned = false;\n\tprivate debouncedFetch = debounce((q: string) => {\n\t\tvoid this.fetchResults(q);\n\t}, 300);\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis.query = this.defaultValue;\n\t\tthis.clickOutsideHandler = (e: MouseEvent) => {\n\t\t\tconst path = e.composedPath();\n\t\t\tif (!path.includes(this)) this.isOpen = false;\n\t\t};\n\t\tdocument.addEventListener('mousedown', this.clickOutsideHandler);\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tsuper.disconnectedCallback();\n\t\tif (this.clickOutsideHandler) {\n\t\t\tdocument.removeEventListener('mousedown', this.clickOutsideHandler);\n\t\t}\n\t\tthis.debouncedFetch.cancel();\n\t\tif (this.abortController) {\n\t\t\tthis.abortController.abort();\n\t\t\tthis.abortController = undefined;\n\t\t}\n\t}\n\n\tprivate warnIfSecretKey() {\n\t\tif (this.secretKeyWarned) return;\n\t\tif (!this.apiKey) return;\n\t\t// Browser-safe publishable keys carry the `pk_` prefix and a server-side\n\t\t// origin allowlist. Anything else (a raw secret key, UUID-style token)\n\t\t// must not ship to the browser.\n\t\tif (this.apiKey.startsWith('pk_')) return;\n\t\tthis.secretKeyWarned = true;\n\t\tconst message =\n\t\t\t'Possible secret key in client-side <roxy-location-search>; use a `pk_` publishable key with origin allowlist instead.';\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(message);\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-validation-error', {\n\t\t\t\tdetail: { reason: 'possible-secret-key', message },\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate async fetchResults(q: string) {\n\t\tthis.warnIfSecretKey();\n\t\t// Abort any in-flight request so a stale response cannot overwrite a\n\t\t// fresher one (debounced typing) or land after disconnect.\n\t\tif (this.abortController) this.abortController.abort();\n\t\tconst controller = new AbortController();\n\t\tthis.abortController = controller;\n\t\tthis.isLoading = true;\n\t\ttry {\n\t\t\tconst url = new URL(this.endpoint);\n\t\t\turl.searchParams.set('q', q);\n\t\t\turl.searchParams.set('limit', '8');\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tAccept: 'application/json',\n\t\t\t};\n\t\t\tif (this.apiKey) headers['X-API-Key'] = this.apiKey;\n\t\t\tif (this.publishableKey) headers['X-API-Key'] = this.publishableKey;\n\t\t\tconst res = await fetch(url, { headers, signal: controller.signal });\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\tconst json = (await res.json()) as SearchCitiesResponse;\n\t\t\tif (controller.signal.aborted) return;\n\t\t\tthis.results = json.cities ?? [];\n\t\t\tthis.isOpen = this.results.length > 0;\n\t\t\tthis.highlight = this.results.length > 0 ? 0 : -1;\n\t\t} catch (err) {\n\t\t\tif ((err as { name?: string })?.name === 'AbortError') return;\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t} finally {\n\t\t\tif (this.abortController === controller) {\n\t\t\t\tthis.abortController = undefined;\n\t\t\t}\n\t\t\tif (!controller.signal.aborted) this.isLoading = false;\n\t\t}\n\t}\n\n\tprivate onInput = (e: Event) => {\n\t\tconst value = (e.target as HTMLInputElement).value;\n\t\tthis.query = value;\n\t\tif (value.length < 2) {\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t\tthis.highlight = -1;\n\t\t\treturn;\n\t\t}\n\t\tthis.debouncedFetch(value);\n\t};\n\n\tprivate select(city: CityResult) {\n\t\tthis.query = `${city.city}${city.province ? `, ${city.province}` : ''}, ${city.country}`;\n\t\tthis.isOpen = false;\n\t\tthis.results = [];\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-location-select', {\n\t\t\t\tdetail: city,\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate onKeyDown = (e: KeyboardEvent) => {\n\t\tif (!this.isOpen || this.results.length === 0) {\n\t\t\tif (e.key === 'ArrowDown' && this.query.length >= 2) {\n\t\t\t\tvoid this.fetchResults(this.query);\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (e.key === 'ArrowDown') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight = (this.highlight + 1) % this.results.length;\n\t\t} else if (e.key === 'ArrowUp') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight =\n\t\t\t\t(this.highlight - 1 + this.results.length) % this.results.length;\n\t\t} else if (e.key === 'Enter') {\n\t\t\te.preventDefault();\n\t\t\tconst target = this.results[this.highlight] ?? this.results[0];\n\t\t\tif (target) this.select(target);\n\t\t} else if (e.key === 'Escape') {\n\t\t\tthis.isOpen = false;\n\t\t}\n\t};\n\n\trender() {\n\t\treturn html`<div class=\"field\">\n\t\t\t<input\n\t\t\t\ttype=\"text\"\n\t\t\t\trole=\"combobox\"\n\t\t\t\taria-expanded=${this.isOpen ? 'true' : 'false'}\n\t\t\t\taria-controls=\"roxy-location-listbox\"\n\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\tautocomplete=\"off\"\n\t\t\t\tplaceholder=${this.placeholder}\n\t\t\t\t.value=${this.query}\n\t\t\t\t@input=${this.onInput}\n\t\t\t\t@keydown=${this.onKeyDown}\n\t\t\t\t@focus=${() => {\n\t\t\t\t\tif (this.results.length > 0) this.isOpen = true;\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t${this.isLoading ? html`<span class=\"spinner\" role=\"status\" aria-label=\"Loading\"></span>` : nothing}\n\t\t\t${\n\t\t\t\tthis.isOpen\n\t\t\t\t\t? html`<ul\n\t\t\t\t\t\tid=\"roxy-location-listbox\"\n\t\t\t\t\t\tclass=\"results\"\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t>\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\tthis.results.length === 0\n\t\t\t\t\t\t\t\t? html`<li class=\"empty\" role=\"status\">No cities found</li>`\n\t\t\t\t\t\t\t\t: this.results.map(\n\t\t\t\t\t\t\t\t\t\t(city, idx) => html`<li role=\"presentation\">\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"option\"\n\t\t\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\t\t\taria-selected=${this.highlight === idx ? 'true' : 'false'}\n\t\t\t\t\t\t\t\t\t\t\t@click=${() => this.select(city)}\n\t\t\t\t\t\t\t\t\t\t\t@mouseenter=${() => {\n\t\t\t\t\t\t\t\t\t\t\t\tthis.highlight = idx;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"city\">${city.city}</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"where\"\n\t\t\t\t\t\t\t\t\t\t\t\t>${city.province ? html`${city.province}, ` : ''}${city.country}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"tz\"\n\t\t\t\t\t\t\t\t\t\t\t\t>UTC${city.utcOffset >= 0 ? '+' : ''}${city.utcOffset}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</li>`,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t</ul>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-location-search': RoxyLocationSearch;\n\t}\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`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n", "/**\n * Lightweight debounce for input handlers. Used by location search.\n *\n * The returned function exposes a `.cancel()` method so callers can clear a\n * pending invocation when the host element disconnects, preventing the timer\n * from firing on a detached node and mutating reactive state after teardown.\n */\nexport interface Debounced<F extends (...args: never[]) => unknown> {\n\t(...args: Parameters<F>): void;\n\tcancel: () => void;\n}\n\nexport function debounce<F extends (...args: never[]) => unknown>(\n\tfn: F,\n\twait: number,\n): Debounced<F> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst debounced = ((...args: Parameters<F>) => {\n\t\tif (timer) clearTimeout(timer);\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = undefined;\n\t\t\tfn(...args);\n\t\t}, wait);\n\t}) as Debounced<F>;\n\tdebounced.cancel = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\treturn debounced;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["css", "css"]
7
7
  }