@sankhyatronics/sankhya-cms 0.2.0 → 0.3.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 (209) hide show
  1. package/custom-elements.json +10968 -3080
  2. package/dist/attention-panel/index.js +2 -2
  3. package/dist/bar-chart/index.js +2 -2
  4. package/dist/bento-grid/index.d.ts +25 -0
  5. package/dist/bento-grid/index.js +4 -4
  6. package/dist/booking/index.d.ts +309 -0
  7. package/dist/booking/index.js +20 -0
  8. package/dist/booking/index.js.map +1 -0
  9. package/dist/card/index.js +2 -2
  10. package/dist/carousel/index.d.ts +20 -6
  11. package/dist/carousel/index.js +3 -3
  12. package/dist/chip/index.js +2 -2
  13. package/dist/{chunk-OLA35A2O.js → chunk-2GTR37PU.js} +2 -2
  14. package/dist/{chunk-7NIWQWTA.js → chunk-2HMOX253.js} +128 -25
  15. package/dist/chunk-2HMOX253.js.map +1 -0
  16. package/dist/{chunk-DH53HZPB.js → chunk-37KFBKPD.js} +32 -4
  17. package/dist/chunk-37KFBKPD.js.map +1 -0
  18. package/dist/{chunk-EE6PAN3B.js → chunk-4XPKKYVZ.js} +23 -15
  19. package/dist/chunk-4XPKKYVZ.js.map +1 -0
  20. package/dist/chunk-535QAZ6A.js +236 -0
  21. package/dist/chunk-535QAZ6A.js.map +1 -0
  22. package/dist/{chunk-AKJQ345O.js → chunk-5AWGZQWU.js} +2 -2
  23. package/dist/chunk-6OZKZG5J.js +300 -0
  24. package/dist/chunk-6OZKZG5J.js.map +1 -0
  25. package/dist/chunk-AYNT7G3J.js +805 -0
  26. package/dist/chunk-AYNT7G3J.js.map +1 -0
  27. package/dist/chunk-BEWX3E6P.js +32 -0
  28. package/dist/chunk-BEWX3E6P.js.map +1 -0
  29. package/dist/chunk-CZUGPKXV.js +275 -0
  30. package/dist/chunk-CZUGPKXV.js.map +1 -0
  31. package/dist/{chunk-2DXHPEB5.js → chunk-DJFWZOZM.js} +107 -11
  32. package/dist/chunk-DJFWZOZM.js.map +1 -0
  33. package/dist/chunk-DPANTC3O.js +1714 -0
  34. package/dist/chunk-DPANTC3O.js.map +1 -0
  35. package/dist/chunk-EKZFFY45.js +426 -0
  36. package/dist/chunk-EKZFFY45.js.map +1 -0
  37. package/dist/chunk-ER7MCPDV.js +7 -0
  38. package/dist/chunk-ER7MCPDV.js.map +1 -0
  39. package/dist/chunk-EW2RK5Y6.js +383 -0
  40. package/dist/chunk-EW2RK5Y6.js.map +1 -0
  41. package/dist/{chunk-BLQV2YMO.js → chunk-EZOKT7NJ.js} +2 -2
  42. package/dist/chunk-FF2LWEF3.js +386 -0
  43. package/dist/chunk-FF2LWEF3.js.map +1 -0
  44. package/dist/chunk-FOUK3NHR.js +7 -0
  45. package/dist/chunk-FOUK3NHR.js.map +1 -0
  46. package/dist/{chunk-O4FCRKDX.js → chunk-GB4NGCZI.js} +6 -3
  47. package/dist/chunk-GB4NGCZI.js.map +1 -0
  48. package/dist/{chunk-5F5PTVWR.js → chunk-GOFSMD6W.js} +9 -10
  49. package/dist/chunk-GOFSMD6W.js.map +1 -0
  50. package/dist/{chunk-6D6ROU7X.js → chunk-IGMVF3KK.js} +2 -2
  51. package/dist/{chunk-INMSEYT6.js → chunk-IVPVYVWG.js} +2 -2
  52. package/dist/{chunk-ZRC5BVDX.js → chunk-JURCF4OD.js} +174 -12
  53. package/dist/chunk-JURCF4OD.js.map +1 -0
  54. package/dist/{chunk-FZ36KL73.js → chunk-KZ7PMFWS.js} +11 -1
  55. package/dist/chunk-KZ7PMFWS.js.map +1 -0
  56. package/dist/{chunk-EMCQBT3S.js → chunk-LO6TZBGD.js} +129 -35
  57. package/dist/chunk-LO6TZBGD.js.map +1 -0
  58. package/dist/chunk-MFRGAT6D.js +860 -0
  59. package/dist/chunk-MFRGAT6D.js.map +1 -0
  60. package/dist/chunk-MIBVQXRU.js +1098 -0
  61. package/dist/chunk-MIBVQXRU.js.map +1 -0
  62. package/dist/{chunk-35TX3AOA.js → chunk-NABSRUXG.js} +2 -2
  63. package/dist/{chunk-2FJDNJ7R.js → chunk-NXKDZWS7.js} +44 -6
  64. package/dist/chunk-NXKDZWS7.js.map +1 -0
  65. package/dist/{chunk-KMU6ABQF.js → chunk-NXUS2CH7.js} +14 -1
  66. package/dist/chunk-NXUS2CH7.js.map +1 -0
  67. package/dist/{chunk-GJO3RGOB.js → chunk-OCPGOLVF.js} +56 -11
  68. package/dist/chunk-OCPGOLVF.js.map +1 -0
  69. package/dist/chunk-OEOE6IFY.js +628 -0
  70. package/dist/chunk-OEOE6IFY.js.map +1 -0
  71. package/dist/{chunk-OELI3IWC.js → chunk-PV6OCXXN.js} +2 -2
  72. package/dist/{chunk-5C6C6K5U.js → chunk-Q66CJO3P.js} +200 -12
  73. package/dist/chunk-Q66CJO3P.js.map +1 -0
  74. package/dist/{chunk-MG2VTZA4.js → chunk-QS3ANOWN.js} +2 -2
  75. package/dist/{chunk-3OEEOBHB.js → chunk-QSJ543V2.js} +2 -2
  76. package/dist/{chunk-FML6AG2V.js → chunk-RANC3OI6.js} +2 -2
  77. package/dist/chunk-T4BIN37Q.js +32 -0
  78. package/dist/chunk-T4BIN37Q.js.map +1 -0
  79. package/dist/{chunk-YVTFQHVY.js → chunk-TEAZZPDQ.js} +2 -2
  80. package/dist/chunk-TJK55CJ2.js +49 -0
  81. package/dist/chunk-TJK55CJ2.js.map +1 -0
  82. package/dist/chunk-UKIVLF7S.js +32 -0
  83. package/dist/chunk-UKIVLF7S.js.map +1 -0
  84. package/dist/chunk-V4R7PYP5.js +905 -0
  85. package/dist/chunk-V4R7PYP5.js.map +1 -0
  86. package/dist/{chunk-AG2COC64.js → chunk-VVYQ2DDS.js} +2 -2
  87. package/dist/chunk-WAMOAAMN.js +16 -0
  88. package/dist/chunk-WAMOAAMN.js.map +1 -0
  89. package/dist/chunk-WAYHHL4J.js +1234 -0
  90. package/dist/chunk-WAYHHL4J.js.map +1 -0
  91. package/dist/{chunk-PHEGSCOM.js → chunk-WH6HE6SY.js} +10 -4
  92. package/dist/chunk-WH6HE6SY.js.map +1 -0
  93. package/dist/{chunk-5MY6OWMK.js → chunk-XP6DI3VY.js} +59 -4
  94. package/dist/chunk-XP6DI3VY.js.map +1 -0
  95. package/dist/{chunk-QLA4BH2B.js → chunk-XS6LNTSH.js} +73 -11
  96. package/dist/chunk-XS6LNTSH.js.map +1 -0
  97. package/dist/{chunk-WT4OATGP.js → chunk-ZKFCDVB4.js} +16 -7
  98. package/dist/chunk-ZKFCDVB4.js.map +1 -0
  99. package/dist/cms-theme.css +22 -0
  100. package/dist/contact-form/index.d.ts +220 -0
  101. package/dist/contact-form/index.js +16 -0
  102. package/dist/contact-form/index.js.map +1 -0
  103. package/dist/content-block/index.d.ts +39 -3
  104. package/dist/content-block/index.js +8 -5
  105. package/dist/cookie-consent/index.d.ts +60 -12
  106. package/dist/cookie-consent/index.js +3 -2
  107. package/dist/date-tile/index.js +2 -2
  108. package/dist/dropdown/index.d.ts +22 -1
  109. package/dist/dropdown/index.js +4 -2
  110. package/dist/feature-split/index.d.ts +6 -1
  111. package/dist/feature-split/index.js +5 -5
  112. package/dist/features-section/index.d.ts +49 -5
  113. package/dist/features-section/index.js +6 -6
  114. package/dist/flow/index.js +2 -2
  115. package/dist/footer/index.d.ts +13 -0
  116. package/dist/footer/index.js +2 -2
  117. package/dist/header/index.d.ts +127 -1
  118. package/dist/header/index.js +3 -2
  119. package/dist/hero/index.d.ts +75 -6
  120. package/dist/hero/index.js +6 -3
  121. package/dist/icon-button/index.d.ts +5 -3
  122. package/dist/icon-button/index.js +2 -2
  123. package/dist/index.d.ts +9 -3
  124. package/dist/index.js +118 -57
  125. package/dist/items-accordion/index.d.ts +5 -1
  126. package/dist/items-accordion/index.js +4 -4
  127. package/dist/kpi-card/index.js +2 -2
  128. package/dist/markdown/index.d.ts +18 -0
  129. package/dist/markdown/index.js +3 -3
  130. package/dist/mega-menu/index.d.ts +95 -7
  131. package/dist/mega-menu/index.js +7 -2
  132. package/dist/menu-grid/index.js +2 -2
  133. package/dist/menu-grid-item/index.js +2 -2
  134. package/dist/menu-item/index.d.ts +6 -0
  135. package/dist/menu-item/index.js +3 -2
  136. package/dist/plan-cards/index.d.ts +69 -0
  137. package/dist/plan-cards/index.js +13 -0
  138. package/dist/plan-cards/index.js.map +1 -0
  139. package/dist/product-nav/index.d.ts +127 -0
  140. package/dist/product-nav/index.js +11 -0
  141. package/dist/product-nav/index.js.map +1 -0
  142. package/dist/promo-banner/index.d.ts +34 -7
  143. package/dist/promo-banner/index.js +6 -3
  144. package/dist/quote-dialog/index.d.ts +174 -0
  145. package/dist/quote-dialog/index.js +22 -0
  146. package/dist/quote-dialog/index.js.map +1 -0
  147. package/dist/schema/index.d.ts +8 -0
  148. package/dist/schema/index.js +6 -0
  149. package/dist/schema/index.js.map +1 -1
  150. package/dist/screenshot-tour/index.d.ts +54 -0
  151. package/dist/screenshot-tour/index.js +9 -0
  152. package/dist/screenshot-tour/index.js.map +1 -0
  153. package/dist/section/index.js +3 -3
  154. package/dist/segmented-control/index.js +2 -2
  155. package/dist/select/index.js +2 -2
  156. package/dist/shared/styles.d.ts +7 -1
  157. package/dist/shared/styles.js +3 -1
  158. package/dist/stats/index.js +2 -2
  159. package/dist/steps/index.d.ts +12 -0
  160. package/dist/steps/index.js +4 -3
  161. package/dist/tag-strip/index.d.ts +7 -0
  162. package/dist/tag-strip/index.js +2 -2
  163. package/dist/testimonials/index.d.ts +9 -1
  164. package/dist/testimonials/index.js +3 -3
  165. package/dist/tooltip/index.js +2 -2
  166. package/package.json +26 -2
  167. package/dist/chunk-2DXHPEB5.js.map +0 -1
  168. package/dist/chunk-2FJDNJ7R.js.map +0 -1
  169. package/dist/chunk-4AFOKKSU.js +0 -158
  170. package/dist/chunk-4AFOKKSU.js.map +0 -1
  171. package/dist/chunk-5C6C6K5U.js.map +0 -1
  172. package/dist/chunk-5F5PTVWR.js.map +0 -1
  173. package/dist/chunk-5MY6OWMK.js.map +0 -1
  174. package/dist/chunk-7NIWQWTA.js.map +0 -1
  175. package/dist/chunk-DH53HZPB.js.map +0 -1
  176. package/dist/chunk-EE6PAN3B.js.map +0 -1
  177. package/dist/chunk-EMCQBT3S.js.map +0 -1
  178. package/dist/chunk-FZ36KL73.js.map +0 -1
  179. package/dist/chunk-GJO3RGOB.js.map +0 -1
  180. package/dist/chunk-HMUZON6I.js +0 -202
  181. package/dist/chunk-HMUZON6I.js.map +0 -1
  182. package/dist/chunk-J6EO4R73.js +0 -633
  183. package/dist/chunk-J6EO4R73.js.map +0 -1
  184. package/dist/chunk-KMU6ABQF.js.map +0 -1
  185. package/dist/chunk-MSSH3HFK.js +0 -205
  186. package/dist/chunk-MSSH3HFK.js.map +0 -1
  187. package/dist/chunk-O4FCRKDX.js.map +0 -1
  188. package/dist/chunk-PHEGSCOM.js.map +0 -1
  189. package/dist/chunk-QLA4BH2B.js.map +0 -1
  190. package/dist/chunk-SQ3OZO32.js +0 -486
  191. package/dist/chunk-SQ3OZO32.js.map +0 -1
  192. package/dist/chunk-UAIUBPDN.js +0 -11
  193. package/dist/chunk-UAIUBPDN.js.map +0 -1
  194. package/dist/chunk-WT4OATGP.js.map +0 -1
  195. package/dist/chunk-XUGEXERO.js +0 -708
  196. package/dist/chunk-XUGEXERO.js.map +0 -1
  197. package/dist/chunk-ZRC5BVDX.js.map +0 -1
  198. /package/dist/{chunk-OLA35A2O.js.map → chunk-2GTR37PU.js.map} +0 -0
  199. /package/dist/{chunk-AKJQ345O.js.map → chunk-5AWGZQWU.js.map} +0 -0
  200. /package/dist/{chunk-BLQV2YMO.js.map → chunk-EZOKT7NJ.js.map} +0 -0
  201. /package/dist/{chunk-6D6ROU7X.js.map → chunk-IGMVF3KK.js.map} +0 -0
  202. /package/dist/{chunk-INMSEYT6.js.map → chunk-IVPVYVWG.js.map} +0 -0
  203. /package/dist/{chunk-35TX3AOA.js.map → chunk-NABSRUXG.js.map} +0 -0
  204. /package/dist/{chunk-OELI3IWC.js.map → chunk-PV6OCXXN.js.map} +0 -0
  205. /package/dist/{chunk-MG2VTZA4.js.map → chunk-QS3ANOWN.js.map} +0 -0
  206. /package/dist/{chunk-3OEEOBHB.js.map → chunk-QSJ543V2.js.map} +0 -0
  207. /package/dist/{chunk-FML6AG2V.js.map → chunk-RANC3OI6.js.map} +0 -0
  208. /package/dist/{chunk-YVTFQHVY.js.map → chunk-TEAZZPDQ.js.map} +0 -0
  209. /package/dist/{chunk-AG2COC64.js.map → chunk-VVYQ2DDS.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contact-form/ContactForm.ts","../src/contact-form/ContactFormEvents.ts"],"sourcesContent":["import { LitElement, css, html, nothing, svg } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\n\nimport { controlBaseStyles, fontFamilyStyles } from '../shared/styles';\nimport {\n CONTACT_FORM_ERROR_EVENT,\n CONTACT_FORM_SENT_EVENT,\n CONTACT_FORM_SUBMIT_EVENT,\n type ContactFormErrorEventDetail,\n type ContactFormErrorKind,\n type ContactFormSentEventDetail,\n type ContactFormSubmitEventDetail\n} from './ContactFormEvents';\nimport {\n CONTACT_FIELD_NAMES,\n CONTACT_LIMITS,\n COUNTER_SHOW_RATIO,\n DEFAULT_ERROR_MESSAGES,\n ERROR_SUMMARY_MIN,\n counterBand,\n safeMailto,\n splitMailto,\n type ContactErrorMessages,\n type ContactErrors,\n type ContactFieldLabels,\n type ContactFieldName,\n type ContactQuery,\n type ContactSubmitResult,\n type ContactTopic,\n type ContactValues,\n type TopicGroup,\n buildContactPayload,\n codePointLength,\n fillTemplate,\n firstInvalidField,\n groupTopics,\n normalizeTopics,\n parseTopicsAttribute,\n readContactQuery,\n resolvePreselectedTopic,\n submitContact,\n validateContact\n} from './contact-form-client';\n\nconst SUCCESS_ICON = svg`<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 12l5 5L20 7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg>`;\n\n/** Field order for focus after a failed check (the same order the form shows them in). */\nconst FIELD_ORDER: readonly ContactFieldName[] = CONTACT_FIELD_NAMES;\n\ntype TextFieldName = 'name' | 'email' | 'company' | 'phone';\n\n/**\n * A contact form for a website: name, work e-mail, company, an optional phone, a topic, a message and a\n * required consent box. It posts the answers as JSON to `endpoint` and shows what came back: a success\n * note (with the reference, when the API gives one), field messages from the server, or a short note when\n * the message could not be sent. The text the server sends is never shown. Only the component's own\n * labels and messages, plus the server's per-field messages, appear on the page.\n *\n * Every rule is checked in the browser first, the same rules the server applies, so a visitor sees the\n * problem before anything is sent. The server still decides, and a field it rejects is shown with its\n * message while the visitor's input is kept. A hidden honeypot field (`website`) must stay empty.\n *\n * `topics` lists the choices, grouped by each item's `group`. The topic is preselected from the page URL:\n * `?product=` wins, then `?topic=`, each when it names one of the topics. `default-topic` is the fallback.\n * An \"About:\" pill shows when the URL chose the topic.\n *\n * The form does not clear the message on an error, and the button is disabled while a request is in\n * flight. Removing the element aborts a request that has not finished.\n *\n * When three or more fields fail, a summary (`role=\"alert\"`) at the top of the form lists each error as a link\n * to its field, and focus moves to the summary. With one or two failing fields, focus goes to the first one.\n *\n * The notes for a network failure or a rate limit may contain `{mailto}`; it becomes a `mailto:` link to the\n * `mailto` address. A message the server sends is shown as plain text and is never given a link.\n *\n * The message counter appears from 80% of the limit. Screen readers are told when it passes 80%, 90% and the limit.\n *\n * None of the events carries personal data (no name, e-mail, company, phone or message text).\n *\n * @fires st-contact-form-submit - Bubbles and is composed. Detail `{ topic: string }` (the chosen topic's value). The checks passed and the request is about to be sent.\n * @fires st-contact-form-sent - Bubbles and is composed. Detail `{ reference?: string }`. The form has been cleared.\n * @fires st-contact-form-error - Bubbles and is composed. Detail `{ kind: 'validation' | 'rate_limited' | 'unavailable' }`. The input is kept.\n * @csspart form - The form.\n * @csspart field - Each field's wrapper (label, control and error).\n * @csspart label - Each field's label.\n * @csspart input - Each text input, the topic select and the message textarea.\n * @csspart error - Each field's error message.\n * @csspart summary - The error summary shown when three or more fields fail.\n * @csspart required-note - The \"* Required\" note.\n * @csspart submit - The submit button.\n * @csspart success - The success note.\n * @csspart alert - The form-level alert (rate limit, unavailable, or a server message that is not on one field).\n * @csspart about - The \"About:\" pill.\n * @csspart consent - The consent label.\n */\n@customElement('st-contact-form')\nexport class ContactForm extends LitElement {\n static styles = css`\n ${controlBaseStyles}\n\n :host {\n display: block;\n box-sizing: border-box;\n ${fontFamilyStyles}\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n form {\n display: flex;\n flex-direction: column;\n gap: 16px;\n margin: 0;\n min-width: 0;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: 16px;\n }\n\n .full {\n grid-column: 1 / -1;\n }\n\n @media (min-width: 560px) {\n .grid {\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n }\n }\n\n .field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n }\n\n /* Text uses the theme's own colour tokens (they swap in dark themes). The --st-cms-text* tokens are fixed\n light-surface colours and would be unreadable on a dark surface. */\n .label {\n font-size: 14px;\n font-weight: var(--st-font-weight-semibold);\n color: var(--st-color-text-strong);\n }\n\n .input {\n width: 100%;\n min-height: 44px;\n padding: 10px 14px;\n border: 1px solid var(--st-color-border, var(--st-cms-primary-200));\n border-radius: 12px;\n background: var(--st-color-surface);\n color: var(--st-color-text-strong);\n /* 16px stops iOS zooming into the field. */\n font: inherit;\n font-size: 16px;\n }\n\n /* Browsers dim the placeholder; the muted text token is already 4.5:1 on the surface in both themes. */\n .input::placeholder {\n color: var(--st-color-text-muted);\n opacity: 1;\n }\n\n .select {\n appearance: auto;\n }\n\n .required-note {\n margin: 0;\n font-size: 14px;\n color: var(--st-color-text);\n }\n\n .summary {\n margin: 0;\n padding: 14px 16px;\n border: 1px solid var(--st-color-danger);\n border-radius: 12px;\n background: var(--st-color-danger-subtle);\n color: var(--st-color-danger-text);\n font-size: 14px;\n line-height: 1.5;\n }\n\n .summary:focus-visible {\n outline: 2px solid var(--st-cms-brand-azure);\n outline-offset: 2px;\n }\n\n .summary-title {\n margin: 0 0 6px;\n font-weight: var(--st-font-weight-semibold);\n }\n\n .summary ul {\n margin: 0;\n padding-left: 20px;\n }\n\n .summary a,\n .alert a {\n color: inherit;\n font-weight: var(--st-font-weight-semibold);\n text-decoration: underline;\n }\n\n textarea.input {\n min-height: 144px;\n resize: vertical;\n }\n\n .input:focus-visible {\n outline: 2px solid var(--st-cms-brand-azure);\n outline-offset: 2px;\n }\n\n .input[aria-invalid='true'] {\n border-color: var(--st-color-danger);\n box-shadow: inset 0 0 0 1px var(--st-color-danger);\n }\n\n .counter {\n margin: 0;\n align-self: flex-end;\n font-size: 12px;\n color: var(--st-color-text-muted);\n }\n\n .counter.over {\n color: var(--st-color-danger-text);\n font-weight: var(--st-font-weight-semibold);\n }\n\n .error {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--st-color-danger-text);\n }\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* Off-screen, not display:none: the browser still fills it in, a person and a screen reader do not see it. */\n .hp {\n position: absolute;\n left: -10000px;\n top: auto;\n width: 1px;\n height: 1px;\n overflow: hidden;\n }\n\n .about {\n margin: 0;\n }\n\n .pill {\n display: inline-flex;\n align-items: center;\n min-height: 32px;\n padding: 4px 12px;\n border-radius: 9999px;\n background: var(--st-color-subtle, var(--st-cms-surface-soft));\n color: var(--st-color-text-strong);\n font-size: 13px;\n font-weight: var(--st-font-weight-semibold);\n }\n\n .consent {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-height: 44px;\n font-size: 14px;\n line-height: 1.5;\n color: var(--st-color-text);\n cursor: pointer;\n }\n\n .consent input {\n flex: none;\n width: 20px;\n height: 20px;\n margin: 2px 0 0;\n accent-color: var(--st-cms-brand-navy);\n }\n\n .privacy {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--st-color-text);\n }\n\n .privacy a {\n color: var(--st-color-text-strong);\n font-weight: var(--st-font-weight-semibold);\n text-decoration: underline;\n }\n\n .submit {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-height: 48px;\n padding: 0 24px;\n border: 0;\n border-radius: 9999px;\n background: var(--st-cms-brand-navy);\n color: var(--st-cms-on-dark);\n font: inherit;\n font-size: 16px;\n font-weight: var(--st-font-weight-semibold);\n cursor: pointer;\n transition: opacity 150ms ease;\n }\n\n .submit:hover:not(:disabled) {\n opacity: 0.92;\n }\n\n .submit:disabled {\n opacity: 0.6;\n cursor: progress;\n }\n\n .submit:focus-visible {\n outline: 2px solid var(--st-cms-brand-azure);\n outline-offset: 2px;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .submit {\n transition: none;\n }\n }\n\n .success {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 16px;\n padding: 16px 18px;\n border-radius: 16px;\n background: var(--st-color-subtle, var(--st-cms-surface-soft));\n color: var(--st-color-text-strong);\n }\n\n .success-icon {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 9999px;\n background: var(--st-cms-highlight);\n color: var(--st-cms-navy);\n }\n\n .success-icon svg {\n width: 18px;\n height: 18px;\n }\n\n .success-text {\n margin: 0;\n font-size: 15px;\n line-height: 1.6;\n }\n\n .reference {\n font-weight: var(--st-font-weight-bold);\n }\n\n .alert {\n margin: 0;\n }\n\n .alert.has-text {\n margin-bottom: 16px;\n padding: 12px 14px;\n border-radius: 12px;\n background: var(--st-color-danger-subtle);\n color: var(--st-color-danger-text);\n font-size: 14px;\n line-height: 1.5;\n }\n `;\n\n /** Absolute `http(s)` URL of the contact API. Empty: the form still renders, and submitting shows the error note without sending. */\n @property({ type: String })\n endpoint = '';\n\n /** The choices for the topic select. Also set as the JSON attribute `topics`. Items with a `group` are put under that `<optgroup>`. */\n @property({ attribute: 'topics', converter: { fromAttribute: parseTopicsAttribute } })\n topics: ContactTopic[] = [];\n\n /** The topic to preselect when the page URL does not choose one. Ignored unless it is one of `topics`. */\n @property({ type: String, attribute: 'default-topic' })\n defaultTopic = '';\n\n /** Shows the optional phone field. Set `show-phone=\"false\"` to hide it. */\n @property({ attribute: 'show-phone', converter: { fromAttribute: (value: string | null) => value !== 'false' } })\n showPhone = true;\n\n /** Language sent with the message. Empty uses the page's `lang`, or `en`. */\n @property({ type: String })\n locale = '';\n\n /** Page sent with the message. Empty uses the page's path. */\n @property({ type: String })\n page = '';\n\n @property({ type: String, attribute: 'name-label' })\n nameLabel = 'Full name';\n\n @property({ type: String, attribute: 'email-label' })\n emailLabel = 'Work email';\n\n @property({ type: String, attribute: 'company-label' })\n companyLabel = 'Company';\n\n @property({ type: String, attribute: 'phone-label' })\n phoneLabel = 'Phone (optional)';\n\n @property({ type: String, attribute: 'topic-label' })\n topicLabel = 'Topic';\n\n @property({ type: String, attribute: 'topic-placeholder' })\n topicPlaceholder = 'Select a topic';\n\n @property({ type: String, attribute: 'message-label' })\n messageLabel = 'Message';\n\n @property({ type: String, attribute: 'name-placeholder' })\n namePlaceholder = 'Your name';\n\n @property({ type: String, attribute: 'email-placeholder' })\n emailPlaceholder = 'you@company.com';\n\n @property({ type: String, attribute: 'company-placeholder' })\n companyPlaceholder = 'Your company';\n\n @property({ type: String, attribute: 'message-placeholder' })\n messagePlaceholder = 'Tell us what you need';\n\n /** The message counter. `{count}` and `{max}` are filled in. */\n @property({ type: String, attribute: 'message-counter' })\n messageCounter = '{count} / {max}';\n\n @property({ type: String, attribute: 'submit-label' })\n submitLabel = 'Send message';\n\n @property({ type: String, attribute: 'sending-label' })\n sendingLabel = 'Sending…';\n\n /** Shown in brackets after each required label, for screen readers. */\n @property({ type: String, attribute: 'required-label' })\n requiredLabel = 'required';\n\n /** The text before the topic in the \"About:\" pill. */\n @property({ type: String, attribute: 'about-label' })\n aboutLabel = 'About:';\n\n @property({ type: String, attribute: 'consent-label' })\n consentLabel = 'I agree that Sankhyatronics may use these details to reply to my enquiry.';\n\n /** A line of privacy text above the submit button. Empty hides it. */\n @property({ type: String, attribute: 'privacy-note' })\n privacyNote = '';\n\n @property({ type: String, attribute: 'privacy-link-label' })\n privacyLinkLabel = 'Privacy Policy';\n\n @property({ type: String, attribute: 'privacy-href' })\n privacyHref = '/privacy-policy';\n\n @property({ type: String, attribute: 'sent-note' })\n sentNote = 'Thanks, your message has been sent. We will reply soon.';\n\n @property({ type: String, attribute: 'reference-label' })\n referenceLabel = 'Your reference:';\n\n @property({ type: String, attribute: 'error-note' })\n errorNote = 'Sorry, your message could not be sent. Please try again later.';\n\n /** Rate-limit note. `{seconds}` is filled in when the server says how long to wait. */\n @property({ type: String, attribute: 'rate-limited-note' })\n rateLimitedNote = 'You have sent several messages in a short time. Please wait {seconds} seconds and try again.';\n\n @property({ type: String, attribute: 'rate-limited-generic-note' })\n rateLimitedGenericNote = 'You have sent several messages in a short time. Please wait a little and try again.';\n\n /** The field-error summary for screen readers, when several fields fail. `{count}` is filled in. */\n @property({ type: String, attribute: 'error-summary' })\n errorSummary = 'Please fix {count} fields.';\n\n /** The summary when one field fails. */\n @property({ type: String, attribute: 'error-summary-one' })\n errorSummaryOne = 'Please fix {count} field.';\n\n /** The note above the fields. The form's `aria-describedby` points at it. Empty hides it. */\n @property({ type: String, attribute: 'required-note' })\n requiredNote = '* Required';\n\n /** Address for `{mailto}` in the error and rate-limit notes. Empty, or not an address, leaves the placeholder out. */\n @property({ type: String })\n mailto = '';\n\n /** Replaces the English validation messages (`required`, `tooLong`, ...). Only the keys given are replaced. */\n @property({ attribute: false })\n errorMessages: Partial<ContactErrorMessages> = {};\n\n @state() private errors: ContactErrors = {};\n @state() private alertText = '';\n /** True when `alertText` is one of the component's own notes, so `{mailto}` in it becomes a link. */\n @state() private alertOwn = false;\n @state() private outcome: { reference?: string } | null = null;\n @state() private sending = false;\n @state() private messageLength = 0;\n /** What screen readers last heard about the counter; changes only when the band changes. */\n @state() private counterAnnouncement = '';\n private counterBandValue = 0;\n\n @query('.submit') private submitEl?: HTMLButtonElement;\n\n /** `product` and `topic` from the page URL, read once on connect. */\n private queryParams: ContactQuery = { product: null, topic: null };\n private inflight: AbortController | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n this.queryParams = readContactQuery(window.location.search);\n }\n\n disconnectedCallback(): void {\n this.inflight?.abort();\n this.inflight = null;\n this.sending = false;\n super.disconnectedCallback();\n }\n\n private get topicList(): ContactTopic[] {\n return normalizeTopics(this.topics);\n }\n\n private get preselect(): { value: string; fromQuery: boolean } {\n return resolvePreselectedTopic(this.topicList, {\n defaultTopic: this.defaultTopic,\n product: this.queryParams.product,\n topic: this.queryParams.topic\n });\n }\n\n private labels(): ContactFieldLabels {\n return {\n name: this.nameLabel,\n email: this.emailLabel,\n company: this.companyLabel,\n phone: this.phoneLabel,\n topic: this.topicLabel,\n message: this.messageLabel\n };\n }\n\n /** The defaults, with each key replaced by the override when it is a string. */\n private messages(): ContactErrorMessages {\n const merged = { ...DEFAULT_ERROR_MESSAGES };\n for (const key of Object.keys(merged) as (keyof ContactErrorMessages)[]) {\n const override = this.errorMessages[key];\n if (typeof override === 'string') merged[key] = override;\n }\n return merged;\n }\n\n private readText(form: HTMLFormElement, name: string): string {\n const field = form.elements.namedItem(name);\n return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement ? field.value : '';\n }\n\n private readValues(form: HTMLFormElement): ContactValues {\n const consent = form.elements.namedItem('consent');\n return {\n name: this.readText(form, 'name'),\n email: this.readText(form, 'email'),\n company: this.readText(form, 'company'),\n phone: this.readText(form, 'phone'),\n topic: this.readText(form, 'topic'),\n message: this.readText(form, 'message'),\n consent: consent instanceof HTMLInputElement && consent.checked,\n website: this.readText(form, 'website')\n };\n }\n\n private async onSubmit(event: SubmitEvent): Promise<void> {\n event.preventDefault();\n if (this.sending) return;\n const form = event.currentTarget as HTMLFormElement;\n const values = this.readValues(form);\n // A new attempt replaces the previous answer, so an old success note does not stay up beside new errors.\n this.outcome = null;\n const errors = validateContact(values, { labels: this.labels(), topics: this.topicList, messages: this.messages() });\n this.errors = errors;\n this.alertText = '';\n this.alertOwn = false;\n if (firstInvalidField(errors)) {\n this.focusProblem(errors);\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent<ContactFormSubmitEventDetail>(CONTACT_FORM_SUBMIT_EVENT, {\n detail: { topic: values.topic.trim() },\n bubbles: true,\n composed: true\n })\n );\n\n const payload = buildContactPayload(values, {\n page: this.page || window.location.pathname,\n locale: this.locale || document.documentElement.lang || 'en',\n now: new Date()\n });\n const controller = new AbortController();\n this.inflight = controller;\n this.sending = true;\n // Disabling the button drops focus, so it is given back after the answer when the visitor was on it.\n const hadFocus = this.shadowRoot?.activeElement === this.submitEl;\n\n const result = await submitContact({ endpoint: this.endpoint.trim(), payload, signal: controller.signal });\n // Removed from the page, or superseded: nothing to show.\n if (this.inflight !== controller) return;\n this.inflight = null;\n this.sending = false;\n this.applyResult(result, form);\n if (hadFocus && result.kind !== 'validation') {\n void this.updateComplete.then(() => this.submitEl?.focus());\n }\n }\n\n private applyResult(result: ContactSubmitResult, form: HTMLFormElement): void {\n switch (result.kind) {\n case 'sent':\n form.reset();\n this.errors = {};\n this.alertText = '';\n this.alertOwn = false;\n this.messageLength = 0;\n this.counterBandValue = 0;\n this.counterAnnouncement = '';\n this.outcome = result.reference === undefined ? {} : { reference: result.reference };\n this.dispatchEvent(\n new CustomEvent<ContactFormSentEventDetail>(CONTACT_FORM_SENT_EVENT, {\n detail: result.reference === undefined ? {} : { reference: result.reference },\n bubbles: true,\n composed: true\n })\n );\n return;\n case 'validation': {\n // A field that is not on the form (the phone field when it is hidden) is shown in the alert instead.\n const shown: ContactErrors = {};\n const hidden: string[] = [];\n for (const name of FIELD_ORDER) {\n const message = result.errors[name];\n if (message === undefined) continue;\n if (name === 'phone' && !this.showPhone) hidden.push(message);\n else shown[name] = message;\n }\n this.errors = shown;\n this.alertText = [result.formMessage, ...hidden].filter(Boolean).join(' ');\n this.alertOwn = false;\n this.focusProblem(shown);\n this.dispatchError('validation');\n return;\n }\n case 'rate_limited':\n this.alertOwn = true;\n this.alertText =\n result.retryAfterSeconds === undefined\n ? this.rateLimitedGenericNote\n : fillTemplate(this.rateLimitedNote, { seconds: result.retryAfterSeconds });\n this.dispatchError('rate_limited');\n return;\n case 'unavailable':\n this.alertOwn = true;\n this.alertText = this.errorNote;\n this.dispatchError('unavailable');\n return;\n }\n }\n\n private focusField(name: ContactFieldName | undefined): void {\n if (!name) return;\n void this.updateComplete.then(() => this.renderRoot.querySelector<HTMLElement>(`#cf-${name}`)?.focus());\n }\n\n /** Many failing fields: focus the summary, which links to each. One or two: the first field. */\n private focusProblem(errors: ContactErrors): void {\n if (this.failingFields(errors).length >= ERROR_SUMMARY_MIN) {\n void this.updateComplete.then(() => this.renderRoot.querySelector<HTMLElement>('#cf-summary')?.focus());\n return;\n }\n this.focusField(firstInvalidField(errors));\n }\n\n private failingFields(errors: ContactErrors = this.errors): ContactFieldName[] {\n return FIELD_ORDER.filter(name => errors[name] !== undefined && errors[name] !== '');\n }\n\n private onSummaryLink(event: Event, name: ContactFieldName): void {\n event.preventDefault();\n this.focusField(name);\n }\n\n private dispatchError(kind: ContactFormErrorKind): void {\n this.dispatchEvent(\n new CustomEvent<ContactFormErrorEventDetail>(CONTACT_FORM_ERROR_EVENT, {\n detail: { kind },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private onMessageInput(event: Event): void {\n const field = event.target as HTMLTextAreaElement;\n this.messageLength = codePointLength(field.value.trim());\n // The polite announcement is made only when the band changes (80%, 90%, over), not on every key.\n const band = counterBand(this.messageLength, CONTACT_LIMITS.messageMax);\n if (band !== this.counterBandValue) {\n this.counterBandValue = band;\n this.counterAnnouncement =\n band === 0 ? '' : fillTemplate(this.messageCounter, { count: this.messageLength, max: CONTACT_LIMITS.messageMax });\n }\n }\n\n private errorSummaryText(): string {\n const count = this.failingFields().length;\n if (count === 0) return '';\n return fillTemplate(count === 1 ? this.errorSummaryOne : this.errorSummary, { count });\n }\n\n /** The alert's text. Only the component's own notes get a link for `{mailto}`; server text stays plain. */\n private renderAlertText() {\n if (!this.alertOwn) return this.alertText;\n const address = safeMailto(this.mailto);\n return splitMailto(this.alertText).map(piece =>\n piece !== null ? piece : address ? html`<a href=\"mailto:${address}\">${address}</a>` : nothing\n );\n }\n\n private renderSummary() {\n const failing = this.failingFields();\n if (failing.length < ERROR_SUMMARY_MIN) return nothing;\n return html`<div class=\"summary\" part=\"summary\" id=\"cf-summary\" role=\"alert\" tabindex=\"-1\">\n <p class=\"summary-title\">${this.errorSummaryText()}</p>\n <ul>\n ${failing.map(\n name => html`<li><a href=\"#cf-${name}\" @click=${(event: Event) => this.onSummaryLink(event, name)}>${this.errors[name]}</a></li>`\n )}\n </ul>\n </div>`;\n }\n\n private requiredMark() {\n return html`<span aria-hidden=\"true\"> *</span><span class=\"sr-only\"> (${this.requiredLabel})</span>`;\n }\n\n private renderInput(\n name: TextFieldName,\n label: string,\n options: { type: string; autocomplete: string; placeholder?: string; inputmode?: string; required: boolean }\n ) {\n const error = this.errors[name];\n return html`<div class=\"field\" part=\"field\">\n <label class=\"label\" part=\"label\" for=\"cf-${name}\">${label}${options.required ? this.requiredMark() : nothing}</label>\n <input\n class=\"input\"\n part=\"input\"\n id=\"cf-${name}\"\n name=${name}\n type=${options.type}\n autocomplete=${options.autocomplete}\n inputmode=${options.inputmode ?? nothing}\n placeholder=${options.placeholder || nothing}\n ?required=${options.required}\n aria-invalid=${error ? 'true' : 'false'}\n aria-describedby=${error ? `cf-${name}-error` : nothing}\n />\n ${error ? html`<p class=\"error\" part=\"error\" id=\"cf-${name}-error\">${error}</p>` : nothing}\n </div>`;\n }\n\n /** Not `renderOptions`: LitElement already has that instance property. */\n private renderChoices(topics: readonly ContactTopic[], selected: string) {\n return topics.map(\n topic => html`<option value=${topic.value} ?selected=${topic.value === selected}>${topic.label}</option>`\n );\n }\n\n private renderTopic(groups: readonly TopicGroup[], selected: string) {\n const error = this.errors.topic;\n return html`<div class=\"field full\" part=\"field\">\n <label class=\"label\" part=\"label\" for=\"cf-topic\">${this.topicLabel}${this.requiredMark()}</label>\n <select\n class=\"input select\"\n part=\"input\"\n id=\"cf-topic\"\n name=\"topic\"\n required\n aria-invalid=${error ? 'true' : 'false'}\n aria-describedby=${error ? 'cf-topic-error' : nothing}\n >\n <option value=\"\" disabled ?selected=${selected === ''}>${this.topicPlaceholder}</option>\n ${groups.map(group =>\n group.group === undefined\n ? this.renderChoices(group.topics, selected)\n : html`<optgroup label=${group.group}>${this.renderChoices(group.topics, selected)}</optgroup>`\n )}\n </select>\n ${error ? html`<p class=\"error\" part=\"error\" id=\"cf-topic-error\">${error}</p>` : nothing}\n </div>`;\n }\n\n private renderMessage() {\n const error = this.errors.message;\n const max = CONTACT_LIMITS.messageMax;\n const over = this.messageLength > max;\n const showCounter = this.messageLength >= max * COUNTER_SHOW_RATIO;\n const describedBy = [showCounter ? 'cf-message-count' : '', error ? 'cf-message-error' : ''].filter(Boolean).join(' ');\n return html`<div class=\"field full\" part=\"field\">\n <label class=\"label\" part=\"label\" for=\"cf-message\">${this.messageLabel}${this.requiredMark()}</label>\n <textarea\n class=\"input\"\n part=\"input\"\n id=\"cf-message\"\n name=\"message\"\n rows=\"6\"\n placeholder=${this.messagePlaceholder || nothing}\n required\n aria-invalid=${error ? 'true' : 'false'}\n aria-describedby=${describedBy || nothing}\n @input=${this.onMessageInput}\n ></textarea>\n <p class=\"sr-only\" id=\"cf-message-live\" aria-live=\"polite\">${this.counterAnnouncement}</p>\n ${showCounter\n ? html`<p class=\"counter ${over ? 'over' : ''}\" id=\"cf-message-count\" aria-hidden=\"true\">\n ${fillTemplate(this.messageCounter, { count: this.messageLength, max })}\n </p>`\n : nothing}\n ${error ? html`<p class=\"error\" part=\"error\" id=\"cf-message-error\">${error}</p>` : nothing}\n </div>`;\n }\n\n private renderConsent() {\n const error = this.errors.consent;\n return html`<div class=\"field full\" part=\"field\">\n <label class=\"consent\" part=\"consent\">\n <input\n type=\"checkbox\"\n id=\"cf-consent\"\n name=\"consent\"\n required\n aria-invalid=${error ? 'true' : 'false'}\n aria-describedby=${error ? 'cf-consent-error' : nothing}\n />\n <span>${this.consentLabel}${this.requiredMark()}</span>\n </label>\n ${error ? html`<p class=\"error\" part=\"error\" id=\"cf-consent-error\">${error}</p>` : nothing}\n </div>`;\n }\n\n render() {\n const preselect = this.preselect;\n const topics = this.topicList;\n const aboutTopic = preselect.fromQuery ? topics.find(item => item.value === preselect.value) : undefined;\n const sending = this.sending;\n const outcome = this.outcome;\n return html`\n <div class=\"live\" role=\"status\">\n ${outcome\n ? html`<div class=\"success\" part=\"success\">\n <span class=\"success-icon\" aria-hidden=\"true\">${SUCCESS_ICON}</span>\n <p class=\"success-text\">\n ${this.sentNote}${outcome.reference\n ? html` ${this.referenceLabel} <strong class=\"reference\">${outcome.reference}</strong>`\n : nothing}\n </p>\n </div>`\n : nothing}\n </div>\n <div class=\"alert ${this.alertText ? 'has-text' : ''}\" part=\"alert\" role=\"alert\">${this.renderAlertText()}</div>\n <form\n part=\"form\"\n novalidate\n aria-busy=${sending ? 'true' : 'false'}\n aria-describedby=${this.requiredNote ? 'cf-required-note' : nothing}\n @submit=${this.onSubmit}\n >\n ${this.requiredNote\n ? html`<p class=\"required-note\" part=\"required-note\" id=\"cf-required-note\">${this.requiredNote}</p>`\n : nothing}\n ${this.renderSummary()}\n <p class=\"sr-only\" aria-live=\"polite\">${this.failingFields().length < ERROR_SUMMARY_MIN ? this.errorSummaryText() : ''}</p>\n ${aboutTopic\n ? html`<p class=\"about\"><span class=\"pill\" part=\"about\">${this.aboutLabel} ${aboutTopic.label}</span></p>`\n : nothing}\n <div class=\"grid\">\n ${this.renderInput('name', this.nameLabel, {\n type: 'text',\n autocomplete: 'name',\n placeholder: this.namePlaceholder,\n required: true\n })}\n ${this.renderInput('email', this.emailLabel, {\n type: 'email',\n autocomplete: 'email',\n placeholder: this.emailPlaceholder,\n required: true\n })}\n ${this.renderInput('company', this.companyLabel, {\n type: 'text',\n autocomplete: 'organization',\n placeholder: this.companyPlaceholder,\n required: true\n })}\n ${this.showPhone\n ? this.renderInput('phone', this.phoneLabel, { type: 'tel', autocomplete: 'tel', inputmode: 'tel', required: false })\n : nothing}\n ${this.renderTopic(groupTopics(topics), preselect.value)}\n </div>\n ${this.renderMessage()}\n ${this.renderConsent()}\n ${this.privacyNote\n ? html`<p class=\"privacy\">${this.privacyNote} <a href=${this.privacyHref}>${this.privacyLinkLabel}</a></p>`\n : nothing}\n <div class=\"hp\" aria-hidden=\"true\" inert>\n <input type=\"text\" name=\"website\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\" />\n </div>\n <button class=\"submit\" part=\"submit\" type=\"submit\" ?disabled=${sending}>\n ${sending ? this.sendingLabel : this.submitLabel}\n </button>\n </form>\n `;\n }\n}\n","/*\n * Events of `st-contact-form`. They bubble and are composed, and none of them carries personal data: no name,\n * e-mail address, company, phone number or message text. `topic` is one of the configured topic values.\n */\n\n/**\n * Fired on `st-contact-form` once the fields pass the checks and the request is about to be sent.\n * Detail `{ topic }`. Bubbles and is composed.\n */\nexport const CONTACT_FORM_SUBMIT_EVENT = 'st-contact-form-submit';\n\n/** Fired on `st-contact-form` when the API accepted the message. Bubbles and is composed. */\nexport const CONTACT_FORM_SENT_EVENT = 'st-contact-form-sent';\n\n/** Fired on `st-contact-form` when a submission was not accepted. Bubbles and is composed. */\nexport const CONTACT_FORM_ERROR_EVENT = 'st-contact-form-error';\n\n/**\n * Why a submission was not accepted: `validation` (the server rejected a field), `rate_limited` (too many\n * messages in a short time) or `unavailable` (the form could not reach the API, or the API failed).\n */\nexport type ContactFormErrorKind = 'validation' | 'rate_limited' | 'unavailable';\n\nexport interface ContactFormSubmitEventDetail {\n /** The chosen topic's `value` (a configured key, not typed by the visitor). */\n topic: string;\n}\n\nexport interface ContactFormSentEventDetail {\n /** The reference the API gave the message, when it gave one. */\n reference?: string;\n}\n\nexport interface ContactFormErrorEventDetail {\n kind: ContactFormErrorKind;\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  fontFamilyStyles
3
- } from "./chunk-KMU6ABQF.js";
3
+ } from "./chunk-NXUS2CH7.js";
4
4
  import {
5
5
  __decorateClass
6
6
  } from "./chunk-EUXUH3YW.js";
@@ -214,4 +214,4 @@ BarChart = __decorateClass([
214
214
  export {
215
215
  BarChart
216
216
  };
217
- //# sourceMappingURL=chunk-AG2COC64.js.map
217
+ //# sourceMappingURL=chunk-VVYQ2DDS.js.map
@@ -0,0 +1,16 @@
1
+ // src/shared/modal-state.ts
2
+ var MODAL_OPEN_ATTRIBUTE = "data-st-modal-open";
3
+ var openModals = /* @__PURE__ */ new Set();
4
+ function setModalOpen(owner, open) {
5
+ if (open) openModals.add(owner);
6
+ else openModals.delete(owner);
7
+ const root = document.documentElement;
8
+ if (openModals.size > 0) root.setAttribute(MODAL_OPEN_ATTRIBUTE, "");
9
+ else root.removeAttribute(MODAL_OPEN_ATTRIBUTE);
10
+ }
11
+
12
+ export {
13
+ MODAL_OPEN_ATTRIBUTE,
14
+ setModalOpen
15
+ };
16
+ //# sourceMappingURL=chunk-WAMOAAMN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/modal-state.ts"],"sourcesContent":["/**\n * Page-wide \"a modal is open\" signal. While any modal (st-quote-dialog) is open, `<html>` carries\n * `data-st-modal-open`, so other fixed UI such as st-cookie-consent can step aside.\n */\nexport const MODAL_OPEN_ATTRIBUTE = 'data-st-modal-open';\n\nconst openModals = new Set<object>();\n\n/** Marks `owner` as open or closed; the attribute stays while at least one owner is open. */\nexport function setModalOpen(owner: object, open: boolean): void {\n if (open) openModals.add(owner);\n else openModals.delete(owner);\n const root = document.documentElement;\n if (openModals.size > 0) root.setAttribute(MODAL_OPEN_ATTRIBUTE, '');\n else root.removeAttribute(MODAL_OPEN_ATTRIBUTE);\n}\n"],"mappings":";AAIO,IAAM,uBAAuB;AAEpC,IAAM,aAAa,oBAAI,IAAY;AAG5B,SAAS,aAAa,OAAe,MAAqB;AAC/D,MAAI,KAAM,YAAW,IAAI,KAAK;AAAA,MACzB,YAAW,OAAO,KAAK;AAC5B,QAAM,OAAO,SAAS;AACtB,MAAI,WAAW,OAAO,EAAG,MAAK,aAAa,sBAAsB,EAAE;AAAA,MAC9D,MAAK,gBAAgB,oBAAoB;AAChD;","names":[]}