@seatlayer/js 0.37.0 → 0.38.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.
package/dist/index.cjs CHANGED
@@ -8,6 +8,14 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __typeError = (msg) => {
9
9
  throw TypeError(msg);
10
10
  };
11
+ var __esm = (fn, res, err) => function __init() {
12
+ if (err) throw err[0];
13
+ try {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ } catch (e) {
16
+ throw err = [e], e;
17
+ }
18
+ };
11
19
  var __export = (target, all) => {
12
20
  for (var name in all)
13
21
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -35,6 +43,354 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
35
43
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
36
44
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
37
45
 
46
+ // src/hostedCheckout.ts
47
+ var hostedCheckout_exports = {};
48
+ __export(hostedCheckout_exports, {
49
+ errorCopy: () => errorCopy,
50
+ formatMoney: () => formatMoney,
51
+ mountCheckout: () => mountCheckout,
52
+ unavailableCopy: () => unavailableCopy
53
+ });
54
+ function ensureStyle() {
55
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
56
+ const el2 = document.createElement("style");
57
+ el2.id = STYLE_ID;
58
+ el2.textContent = CSS;
59
+ document.head.appendChild(el2);
60
+ }
61
+ function formatMoney(amount, currency) {
62
+ try {
63
+ return new Intl.NumberFormat(void 0, { style: "currency", currency }).format(amount);
64
+ } catch {
65
+ return `${amount.toFixed(2)} ${currency}`;
66
+ }
67
+ }
68
+ function unavailableCopy(reason, seatCount) {
69
+ const held = `${seatCount} ${seatCount === 1 ? "seat is" : "seats are"} held for a limited time.`;
70
+ if (reason === "payments_off_for_event") {
71
+ return {
72
+ title: "This event isn\u2019t sold online",
73
+ body: `${held} The organiser isn\u2019t taking payment for this event here.`,
74
+ detail: "Nothing has been charged. Check where you found this event for how to get tickets."
75
+ };
76
+ }
77
+ if (reason === "unavailable_for_event") {
78
+ return {
79
+ title: "This event isn\u2019t taking payment yet",
80
+ body: `${held} Online payment is not switched on for this event.`,
81
+ detail: "Nothing has been charged. If you were sent here to pay, let the organiser know \u2014 only they can turn payment on for this event."
82
+ };
83
+ }
84
+ return {
85
+ title: "Finish in the ticketing checkout",
86
+ body: `${held} Payment for this event is taken elsewhere.`,
87
+ detail: "Nothing has been charged. Continue in the checkout on this page to pay for your seats."
88
+ };
89
+ }
90
+ function errorCopy(code) {
91
+ switch (code) {
92
+ case "gateway_not_connected":
93
+ case "payments_not_enabled_for_event":
94
+ return "This event is not taking online payments. Contact the organiser to buy these seats.";
95
+ case "provider_mismatch":
96
+ return "This event\u2019s payment setup changed while you were choosing. Nothing was charged \u2014 please try again.";
97
+ case "hold_not_active":
98
+ case "hold_not_found":
99
+ return "Your seats were released before checkout started. Nothing was charged \u2014 please pick again.";
100
+ case "checkout_already_started":
101
+ return "A payment for these seats is already in progress. Finish that one, or wait for it to time out before starting again.";
102
+ case "event_closed":
103
+ return "Sales for this event have closed.";
104
+ case "gateway_currency_mismatch":
105
+ case "unsupported_currency":
106
+ case "mixed_currency_hold":
107
+ case "price_unusable":
108
+ return "These seats cannot be checked out right now because of a pricing configuration problem. Nothing was charged \u2014 please contact the organiser.";
109
+ case "rate_limited":
110
+ return "Too many attempts. Wait a moment and try again.";
111
+ default:
112
+ return "We could not start the payment. Nothing was charged \u2014 please try again.";
113
+ }
114
+ }
115
+ function loadRazorpay() {
116
+ const existing = window.Razorpay;
117
+ if (existing) return Promise.resolve(existing);
118
+ return new Promise((resolve, reject) => {
119
+ const previous = document.querySelector(`script[src="${RAZORPAY_SCRIPT}"]`);
120
+ const tag = previous ?? document.createElement("script");
121
+ const done = () => {
122
+ const ctor = window.Razorpay;
123
+ if (ctor) resolve(ctor);
124
+ else reject(new Error("razorpay_unavailable"));
125
+ };
126
+ tag.addEventListener("load", done, { once: true });
127
+ tag.addEventListener("error", () => reject(new Error("razorpay_script_failed")), { once: true });
128
+ if (previous) return;
129
+ tag.src = RAZORPAY_SCRIPT;
130
+ tag.async = true;
131
+ document.head.appendChild(tag);
132
+ });
133
+ }
134
+ function el(tag, className, text) {
135
+ const node = document.createElement(tag);
136
+ if (className) node.className = className;
137
+ if (text !== void 0) node.textContent = text;
138
+ return node;
139
+ }
140
+ function mountCheckout(mount) {
141
+ ensureStyle();
142
+ let live = true;
143
+ let confirmed = false;
144
+ const scrim = el("div", "sl-hco");
145
+ scrim.setAttribute("role", "dialog");
146
+ scrim.setAttribute("aria-modal", "true");
147
+ const card = el("div", "sl-hco-card");
148
+ scrim.appendChild(card);
149
+ const destroy = () => {
150
+ live = false;
151
+ scrim.remove();
152
+ document.removeEventListener("keydown", onKey, true);
153
+ };
154
+ const cancel = () => {
155
+ destroy();
156
+ mount.onCancel();
157
+ };
158
+ function onKey(event) {
159
+ if (event.key !== "Escape" || !scrim.isConnected) return;
160
+ event.stopPropagation();
161
+ event.preventDefault();
162
+ cancel();
163
+ }
164
+ document.addEventListener("keydown", onKey, true);
165
+ const title = el("h2", "sl-hco-title", "Checkout");
166
+ const titleId = `sl-hco-t-${Math.random().toString(36).slice(2, 8)}`;
167
+ title.id = titleId;
168
+ scrim.setAttribute("aria-labelledby", titleId);
169
+ const show = (...nodes) => {
170
+ card.replaceChildren(title, ...nodes);
171
+ };
172
+ const fail = (message) => {
173
+ if (!live) return;
174
+ const status = el("p", "sl-hco-status sl-hco-error", message);
175
+ status.setAttribute("role", "alert");
176
+ const back2 = el("button", "sl-hco-back", "Back to seats");
177
+ back2.type = "button";
178
+ back2.addEventListener("click", cancel);
179
+ show(status, back2);
180
+ };
181
+ const waiting = (message) => {
182
+ if (!live) return;
183
+ const status = el("p", "sl-hco-status", message);
184
+ status.setAttribute("role", "status");
185
+ show(status);
186
+ };
187
+ const awaitConfirmation = async (orderId) => {
188
+ waiting("Payment received \u2014 confirming your seats\u2026");
189
+ const deadline = Date.now() + CONFIRM_TIMEOUT_MS;
190
+ while (live && Date.now() < deadline) {
191
+ try {
192
+ const body = await mount.orderStatus(orderId);
193
+ if (!live) return;
194
+ if (body.status === "confirmed") {
195
+ confirmed = true;
196
+ title.textContent = "Your tickets are confirmed";
197
+ const status = el(
198
+ "p",
199
+ "sl-hco-status",
200
+ `${body.seatCount} ${body.seatCount === 1 ? "seat" : "seats"} confirmed. We have emailed your tickets.`
201
+ );
202
+ const receipt = el("dl", "sl-hco-receipt");
203
+ receipt.append(
204
+ el("dt", void 0, "Paid"),
205
+ el("dd", void 0, `${body.amountFormatted} ${body.currency}`),
206
+ el("dt", void 0, "Order"),
207
+ el("dd", "sl-hco-ref", body.orderId)
208
+ );
209
+ const close = el("button", "sl-hco-back", "Close");
210
+ close.type = "button";
211
+ close.addEventListener("click", cancel);
212
+ show(status, receipt, close);
213
+ mount.onConfirmed(body);
214
+ return;
215
+ }
216
+ if (body.status === "failed" || body.status === "expired") {
217
+ fail(body.status === "expired" ? "Your seats were released before payment completed. Nothing was charged \u2014 please pick again." : "The payment did not complete. If you were charged, it has been refunded automatically.");
218
+ return;
219
+ }
220
+ } catch {
221
+ }
222
+ await new Promise((resolve) => setTimeout(resolve, CONFIRM_POLL_MS));
223
+ }
224
+ if (!live || confirmed) return;
225
+ fail("Still confirming with the payment provider. If your payment went through, your tickets will arrive by email shortly \u2014 you do not need to pay again.");
226
+ };
227
+ if (mount.state.kind === "unavailable") {
228
+ const copy = unavailableCopy(mount.state.reason, mount.state.seatCount);
229
+ title.textContent = copy.title;
230
+ const kicker = el("p", "sl-hco-label", "Seats held");
231
+ const back2 = el("button", "sl-hco-back", "Back to seat map");
232
+ back2.type = "button";
233
+ back2.addEventListener("click", cancel);
234
+ card.replaceChildren(
235
+ kicker,
236
+ title,
237
+ el("p", "sl-hco-status", copy.body),
238
+ el("p", "sl-hco-note", copy.detail),
239
+ back2
240
+ );
241
+ mount.root.appendChild(scrim);
242
+ back2.focus();
243
+ return { destroy };
244
+ }
245
+ if (mount.state.kind === "resume") {
246
+ mount.root.appendChild(scrim);
247
+ void awaitConfirmation(mount.state.orderId);
248
+ return { destroy };
249
+ }
250
+ const { order, provider } = mount.state;
251
+ const summary = el("div", "sl-hco-summary");
252
+ const seats = el("div", "sl-hco-seats");
253
+ for (const label of order.labels) seats.appendChild(el("span", "sl-hco-seat", label));
254
+ const totalText = formatMoney(order.total, order.currency);
255
+ const totalRow = el("div", "sl-hco-total");
256
+ totalRow.append(el("span", void 0, "Total"), el("strong", void 0, totalText));
257
+ summary.append(seats, totalRow);
258
+ const form = el("form", "sl-hco-form");
259
+ const emailLabel = el("label", "sl-hco-label", "Email \u2014 your tickets go here");
260
+ const email = el("input", "sl-hco-input");
261
+ email.type = "email";
262
+ email.required = true;
263
+ email.autocomplete = "email";
264
+ email.placeholder = "you@example.com";
265
+ email.id = `${titleId}-email`;
266
+ emailLabel.htmlFor = email.id;
267
+ const nameLabel = el("label", "sl-hco-label", "Name (optional)");
268
+ const name = el("input", "sl-hco-input");
269
+ name.type = "text";
270
+ name.autocomplete = "name";
271
+ name.placeholder = "Your name";
272
+ name.id = `${titleId}-name`;
273
+ nameLabel.htmlFor = name.id;
274
+ const pay = el("button", "sl-hco-pay", `Pay ${totalText}`);
275
+ pay.type = "submit";
276
+ pay.disabled = true;
277
+ const back = el("button", "sl-hco-back", "Back to seats");
278
+ back.type = "button";
279
+ back.addEventListener("click", cancel);
280
+ const until = new Date(order.expiresAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
281
+ const note = el(
282
+ "p",
283
+ "sl-hco-note",
284
+ `Your seats are held until ${until}. Payment is handled by ${provider === "razorpay" ? "Razorpay" : "Stripe"} \u2014 we never see your card details.`
285
+ );
286
+ const emailValid = () => /.+@.+\..+/.test(email.value.trim());
287
+ email.addEventListener("input", () => {
288
+ pay.disabled = !emailValid();
289
+ });
290
+ const start = async () => {
291
+ waiting("Opening secure payment\u2026");
292
+ try {
293
+ const body = await mount.startSession({
294
+ holdId: order.holdId,
295
+ buyerEmail: email.value.trim(),
296
+ // Deliberately no `provider`: since W2 the EVENT row decides which
297
+ // gateway charges, and naming one here can only ever 409 on a mismatch
298
+ // the buyer cannot do anything about.
299
+ ...name.value.trim() ? { buyerName: name.value.trim() } : {}
300
+ });
301
+ if (!live) return;
302
+ if (body.redirectUrl) {
303
+ waiting("Taking you to secure payment\u2026");
304
+ window.location.assign(body.redirectUrl);
305
+ return;
306
+ }
307
+ if (body.clientPayload) {
308
+ const payload = body.clientPayload;
309
+ const Razorpay = await loadRazorpay();
310
+ if (!live) return;
311
+ waiting("Waiting for payment\u2026");
312
+ new Razorpay({
313
+ key: payload.key,
314
+ order_id: payload.orderId,
315
+ amount: payload.amount,
316
+ currency: payload.currency,
317
+ name: payload.name,
318
+ prefill: payload.prefill,
319
+ // The handler fires on the browser's word alone, so it only starts the
320
+ // wait — the webhook is what actually confirms the order.
321
+ handler: () => {
322
+ void awaitConfirmation(body.orderId);
323
+ },
324
+ modal: { ondismiss: () => {
325
+ if (live) details();
326
+ } }
327
+ }).open();
328
+ return;
329
+ }
330
+ fail(errorCopy("gateway_unavailable"));
331
+ } catch (err) {
332
+ mount.onError?.(err);
333
+ fail(errorCopy(err?.code));
334
+ }
335
+ };
336
+ form.addEventListener("submit", (event) => {
337
+ event.preventDefault();
338
+ if (emailValid()) void start();
339
+ });
340
+ form.append(emailLabel, email, nameLabel, name, pay, back, note);
341
+ const details = () => {
342
+ title.textContent = "Checkout";
343
+ show(summary, form);
344
+ pay.disabled = !emailValid();
345
+ };
346
+ details();
347
+ mount.root.appendChild(scrim);
348
+ email.focus();
349
+ return { destroy };
350
+ }
351
+ var CONFIRM_TIMEOUT_MS, CONFIRM_POLL_MS, RAZORPAY_SCRIPT, STYLE_ID, CSS;
352
+ var init_hostedCheckout = __esm({
353
+ "src/hostedCheckout.ts"() {
354
+ "use strict";
355
+ CONFIRM_TIMEOUT_MS = 9e4;
356
+ CONFIRM_POLL_MS = 2e3;
357
+ RAZORPAY_SCRIPT = "https://checkout.razorpay.com/v1/checkout.js";
358
+ STYLE_ID = "seatlayer-checkout-style";
359
+ CSS = /* @sl-css */
360
+ `
361
+ .sl-hco{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
362
+ padding:16px;background:color-mix(in srgb, var(--sl-bg) 82%, transparent);backdrop-filter:blur(3px)}
363
+ .sl-hco-card{width:100%;max-width:380px;max-height:100%;overflow:auto;padding:22px;
364
+ background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);
365
+ border-radius:var(--sl-radius);box-shadow:0 18px 48px rgba(0,0,0,.28)}
366
+ .sl-hco-title{margin:0 0 14px;font-size:19px;font-weight:650;letter-spacing:-.01em}
367
+ .sl-hco-summary{margin-bottom:16px;padding-bottom:14px;border-bottom:1px solid var(--sl-line)}
368
+ .sl-hco-seats{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}
369
+ .sl-hco-seat{padding:3px 8px;font-size:12px;font-weight:600;border-radius:calc(var(--sl-radius) * .5);
370
+ background:var(--sl-bg);border:1px solid var(--sl-line)}
371
+ .sl-hco-total{display:flex;justify-content:space-between;align-items:baseline;font-size:14px}
372
+ .sl-hco-total strong{font-size:17px;font-weight:700}
373
+ .sl-hco-label{display:block;margin:12px 0 5px;font-size:12px;font-weight:600;color:var(--sl-muted)}
374
+ .sl-hco-input{width:100%;padding:10px 11px;font:inherit;font-size:15px;color:var(--sl-text);
375
+ background:var(--sl-bg);border:1px solid var(--sl-line);border-radius:calc(var(--sl-radius) * .55)}
376
+ .sl-hco-input:focus-visible{outline:2px solid var(--sl-accent);outline-offset:1px}
377
+ .sl-hco-pay{width:100%;margin-top:16px;padding:12px;font:inherit;font-size:15px;font-weight:650;
378
+ color:var(--sl-accent-ink);background:var(--sl-accent);border:0;
379
+ border-radius:calc(var(--sl-radius) * .55);cursor:pointer}
380
+ .sl-hco-pay[disabled]{opacity:.5;cursor:default}
381
+ .sl-hco-back{width:100%;margin-top:8px;padding:10px;font:inherit;font-size:14px;color:var(--sl-muted);
382
+ background:none;border:0;cursor:pointer;text-decoration:underline}
383
+ .sl-hco-note{margin:12px 0 0;font-size:12px;line-height:1.5;color:var(--sl-muted)}
384
+ .sl-hco-status{margin:8px 0 0;font-size:14px;line-height:1.55}
385
+ .sl-hco-error{color:var(--sl-danger, #c0392b)}
386
+ .sl-hco-receipt{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:14px 0 0;font-size:13px}
387
+ .sl-hco-receipt dt{color:var(--sl-muted)}
388
+ .sl-hco-receipt dd{margin:0;text-align:right}
389
+ .sl-hco-ref{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all}
390
+ `;
391
+ }
392
+ });
393
+
38
394
  // src/index.ts
39
395
  var index_exports = {};
40
396
  __export(index_exports, {
@@ -185,6 +541,41 @@ var PubApi = class {
185
541
  body: { holdId, ...ttlMs ? { ttlMs } : {} }
186
542
  });
187
543
  }
544
+ /**
545
+ * Which gateways this event can actually take money through — the question
546
+ * `checkout: 'hosted'` has to answer BEFORE it shows a buyer a Pay button, so
547
+ * the answer is never discovered by failing a payment.
548
+ *
549
+ * Anonymous, and it discloses no account, key, mode or currency for a gateway
550
+ * that did not match.
551
+ */
552
+ paymentOptions(key) {
553
+ return this.request(`/pub/events/${encodeURIComponent(key)}/payment-options`);
554
+ }
555
+ /**
556
+ * Turn a live hold into an order and start a payment.
557
+ *
558
+ * The amount is NOT sent: the server recomputes it from the hold's own items,
559
+ * which is the only reason a browser cannot alter what it pays. Nor is the
560
+ * PROVIDER — the event row decides which gateway charges, and a `provider` in
561
+ * the body is checked rather than obeyed (409 `provider_mismatch`). Omitting
562
+ * it is the shape that cannot disagree.
563
+ */
564
+ startCheckout(key, input) {
565
+ return this.request(`/pub/events/${encodeURIComponent(key)}/checkout`, {
566
+ method: "POST",
567
+ body: input
568
+ });
569
+ }
570
+ /**
571
+ * Poll an order while its gateway webhook lands. The order id is an
572
+ * unguessable token the buyer already holds, so it acts as the capability —
573
+ * which is also why a buyer returning from a gateway page can be told what
574
+ * happened with nothing but the id in the return URL.
575
+ */
576
+ orderStatus(orderId) {
577
+ return this.request(`/pub/orders/${encodeURIComponent(orderId)}/status`);
578
+ }
188
579
  /**
189
580
  * Mint a one-use subscribe ticket for the next socket attempt (protocol doc
190
581
  * §3). The bearer travels here, over ordinary HTTPS where CORS and Origin
@@ -845,9 +1236,9 @@ var DEFAULT_API_BASE = "https://api.seatlayer.io";
845
1236
  var DEFAULT_MAX_SELECTION = 10;
846
1237
  function resolveContainer(container) {
847
1238
  if (typeof container === "string") {
848
- const el = document.querySelector(container);
849
- if (!el) throw new Error(`seatmap: container "${container}" not found`);
850
- return el;
1239
+ const el2 = document.querySelector(container);
1240
+ if (!el2) throw new Error(`seatmap: container "${container}" not found`);
1241
+ return el2;
851
1242
  }
852
1243
  if (!(container instanceof HTMLElement)) {
853
1244
  throw new Error("seatmap: container must be a CSS selector or an HTMLElement");
@@ -1738,7 +2129,8 @@ var EmbeddedDesigner = class {
1738
2129
  }
1739
2130
  buildSkeleton(overlay) {
1740
2131
  const style = document.createElement("style");
1741
- style.textContent = `
2132
+ style.textContent = /* @sl-css */
2133
+ `
1742
2134
  @media (prefers-reduced-motion: no-preference) {
1743
2135
  @keyframes seatlayer-designer-shimmer {
1744
2136
  0% { background-position: -320px 0; }
@@ -1877,9 +2269,9 @@ function pointInPolygon(x, y, poly) {
1877
2269
  }
1878
2270
  function resolveContainer3(container) {
1879
2271
  if (typeof container === "string") {
1880
- const el = document.querySelector(container);
1881
- if (!el) throw new Error(`seatmap: container "${container}" not found`);
1882
- return el;
2272
+ const el2 = document.querySelector(container);
2273
+ if (!el2) throw new Error(`seatmap: container "${container}" not found`);
2274
+ return el2;
1883
2275
  }
1884
2276
  if (!(container instanceof HTMLElement)) {
1885
2277
  throw new Error("seatmap: container must be a CSS selector or an HTMLElement");
@@ -1918,20 +2310,45 @@ function hasWebGL2() {
1918
2310
  }
1919
2311
  return _webgl2Cache;
1920
2312
  }
2313
+ function cdnChunkUrl(fileName) {
2314
+ const base = SEATLAYER_MODULE_URL ?? (typeof location !== "undefined" ? location.href : void 0);
2315
+ if (!base) throw new Error(`seatlayer: cannot resolve the ${fileName} chunk URL`);
2316
+ return new URL(`./${fileName}`, base).href;
2317
+ }
1921
2318
  async function loadVenue3d() {
1922
2319
  if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
1923
- const base = SEATLAYER_MODULE_URL ?? (typeof location !== "undefined" ? location.href : void 0);
1924
- if (!base) throw new Error("seatlayer: cannot resolve the 3D view chunk URL");
1925
- const url = new URL("./seatlayer-view3d.mjs", base).href;
1926
2320
  return import(
1927
2321
  /* @vite-ignore */
1928
- url
2322
+ cdnChunkUrl("seatlayer-view3d.mjs")
1929
2323
  );
1930
2324
  }
1931
2325
  return import("@seatlayer/core/view3d");
1932
2326
  }
1933
- var STYLE_ID = "seatlayer-picker-style";
1934
- var CSS = `
2327
+ async function loadPanorama() {
2328
+ if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
2329
+ return import(
2330
+ /* @vite-ignore */
2331
+ cdnChunkUrl("seatlayer-panorama.mjs")
2332
+ );
2333
+ }
2334
+ return import("@seatlayer/core");
2335
+ }
2336
+ async function loadHostedCheckout() {
2337
+ if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
2338
+ return import(
2339
+ /* @vite-ignore */
2340
+ cdnChunkUrl("seatlayer-checkout.mjs")
2341
+ );
2342
+ }
2343
+ return Promise.resolve().then(() => (init_hostedCheckout(), hostedCheckout_exports));
2344
+ }
2345
+ function paymentsOffReason(reason) {
2346
+ return reason === "unavailable_for_event" || reason === "payments_off_for_event" ? reason : "not_configured";
2347
+ }
2348
+ var STYLE_ID2 = "seatlayer-picker-style";
2349
+ var CSS2 = (
2350
+ /* @sl-css */
2351
+ `
1935
2352
  .sl-picker{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:420px;overflow:hidden;
1936
2353
  background:var(--sl-bg);color:var(--sl-text);font-family:var(--sl-font);border-radius:var(--sl-radius);
1937
2354
  --sl-r-sm:calc(var(--sl-radius) * .55);
@@ -2668,13 +3085,14 @@ var CSS = `
2668
3085
  .sl-modal-scrim{position:fixed;inset:0;z-index:2147483000;background:rgba(5,7,12,.66);display:flex;align-items:center;justify-content:center;padding:18px}
2669
3086
  .sl-modal-frame{width:min(1200px,100%);height:min(820px,100%);border-radius:16px;overflow:hidden;box-shadow:0 40px 120px -30px rgba(0,0,0,.8)}
2670
3087
  @media(max-width:640px){.sl-modal-scrim{padding:0}.sl-modal-frame{width:100%;height:100%;border-radius:0}}
2671
- `;
2672
- function ensureStyle() {
2673
- if (document.getElementById(STYLE_ID)) return;
2674
- const el = document.createElement("style");
2675
- el.id = STYLE_ID;
2676
- el.textContent = CSS;
2677
- document.head.appendChild(el);
3088
+ `
3089
+ );
3090
+ function ensureStyle2() {
3091
+ if (document.getElementById(STYLE_ID2)) return;
3092
+ const el2 = document.createElement("style");
3093
+ el2.id = STYLE_ID2;
3094
+ el2.textContent = CSS2;
3095
+ document.head.appendChild(el2);
2678
3096
  }
2679
3097
  function resolveTokens(chart, host) {
2680
3098
  const accent = host?.accent ?? chart?.accent ?? "#f4b740";
@@ -2733,6 +3151,14 @@ var SeatPicker = class _SeatPicker {
2733
3151
  this.handedOff = false;
2734
3152
  /** Guards single onBooked + single success overlay per hold. */
2735
3153
  this.bookedShown = false;
3154
+ /**
3155
+ * In-flight or settled `payment-options` for this event, started at render in
3156
+ * hosted mode. One request, kicked off while the buyer is still choosing, so
3157
+ * pressing Pay does not wait on a lookup whose answer never changes mid-session.
3158
+ */
3159
+ this.paymentOptions = null;
3160
+ /** The mounted payment card, while one is up. */
3161
+ this.checkoutPanel = null;
2736
3162
  this.extendEl = null;
2737
3163
  this.bookedEl = null;
2738
3164
  this.gaQty = /* @__PURE__ */ new Map();
@@ -2847,6 +3273,12 @@ var SeatPicker = class _SeatPicker {
2847
3273
  onObjectUnavailable: (event) => this.opts.onSelectedObjectUnavailable?.(event)
2848
3274
  });
2849
3275
  this.api = options.transport ?? this.pubApi;
3276
+ if (options.checkout === "hosted" && !this.pubApi) {
3277
+ console.warn(
3278
+ 'seatlayer: checkout: "hosted" needs the widget\'s own transport \u2014 a custom `transport` owns its backend, so the picker is staying on onCheckout for this mount.'
3279
+ );
3280
+ }
3281
+ this.checkoutMode = options.checkout === "hosted" && this.pubApi ? "hosted" : "handoff";
2850
3282
  this.maxTickets = Math.max(1, Math.floor(options.maxSelection ?? DEFAULT_MAX_SELECTION2));
2851
3283
  this.cbSafe = readStoredColorblind() ?? !!options.colorblindSafe;
2852
3284
  this.controller = new import_core2.PickerController({
@@ -3152,7 +3584,7 @@ var SeatPicker = class _SeatPicker {
3152
3584
  }
3153
3585
  /** Mount the full picker as a document-level modal. Resolves after render. */
3154
3586
  static async open(options) {
3155
- ensureStyle();
3587
+ ensureStyle2();
3156
3588
  const scrim = document.createElement("div");
3157
3589
  scrim.className = "sl-modal-scrim";
3158
3590
  const frame = document.createElement("div");
@@ -3207,9 +3639,10 @@ var SeatPicker = class _SeatPicker {
3207
3639
  async render() {
3208
3640
  if (this.rendered) return this;
3209
3641
  this.rendered = true;
3210
- ensureStyle();
3642
+ ensureStyle2();
3211
3643
  await (0, import_core2.loadLocale)(this.opts.locale);
3212
3644
  if (this.opts.messages) (0, import_core2.setStringOverrides)(this.opts.messages);
3645
+ if (this.checkoutMode === "hosted") this.paymentOptions = this.pubApi.paymentOptions(this.opts.event);
3213
3646
  const mount = resolveContainer3(this.opts.container);
3214
3647
  const root = document.createElement("div");
3215
3648
  root.className = "sl-picker";
@@ -3294,8 +3727,8 @@ var SeatPicker = class _SeatPicker {
3294
3727
  </div>
3295
3728
  </div>
3296
3729
  </div>`;
3297
- root.querySelectorAll("[data-ref]").forEach((el) => {
3298
- this.els[el.dataset.ref] = el;
3730
+ root.querySelectorAll("[data-ref]").forEach((el2) => {
3731
+ this.els[el2.dataset.ref] = el2;
3299
3732
  });
3300
3733
  this.mapHost = this.els.map;
3301
3734
  const applyLayout = () => {
@@ -3507,8 +3940,40 @@ var SeatPicker = class _SeatPicker {
3507
3940
  if (this.salesClosed) this.applySalesClosed();
3508
3941
  this.syncPrices();
3509
3942
  this.syncTray();
3943
+ this.resumeHostedOrder();
3510
3944
  return this;
3511
3945
  }
3946
+ /**
3947
+ * A hosted gateway returned this buyer to a page that runs the widget, with
3948
+ * `?order=…&status=…` in the URL. Pick the order up and finish the story.
3949
+ *
3950
+ * Only `success` resumes. `cancelled` means the buyer backed out at the
3951
+ * gateway and their seats are still held — the map they are looking at IS the
3952
+ * right screen, and opening a card to say "you cancelled" would be noise.
3953
+ *
3954
+ * The two parameters are then stripped with `replaceState`, because they are a
3955
+ * one-shot instruction: leaving them in place would re-open the confirmation
3956
+ * on every later navigation, and would carry an order id into browser history
3957
+ * and any Referer this page later sends. `status` is only ever removed
3958
+ * alongside an `order` we actually consumed, so a host page that uses a
3959
+ * `status` parameter of its own keeps it.
3960
+ */
3961
+ resumeHostedOrder() {
3962
+ if (this.checkoutMode !== "hosted" || typeof location === "undefined") return;
3963
+ const params = new URLSearchParams(location.search);
3964
+ const orderId = params.get("order");
3965
+ if (!orderId) return;
3966
+ const status = params.get("status");
3967
+ params.delete("order");
3968
+ params.delete("status");
3969
+ try {
3970
+ const query = params.toString();
3971
+ history.replaceState(history.state, "", `${location.pathname}${query ? `?${query}` : ""}${location.hash}`);
3972
+ } catch {
3973
+ }
3974
+ if (status !== "success") return;
3975
+ void this.openCheckoutPanel({ kind: "resume", orderId });
3976
+ }
3512
3977
  /**
3513
3978
  * Move layout-dependent chrome between its wide dock (map regions / zoom
3514
3979
  * column) and its narrow dock (the sheet's consolidated Filters row), and
@@ -3534,27 +3999,27 @@ var SeatPicker = class _SeatPicker {
3534
3999
  }
3535
4000
  /** The "Need more time?" prompt shown in the hold's final EXTEND_PROMPT_MS. */
3536
4001
  buildExtendPrompt() {
3537
- const el = document.createElement("div");
3538
- el.className = "sl-extend";
3539
- el.setAttribute("role", "status");
3540
- el.innerHTML = `<span class="sl-extend-txt" data-ref="extendTxt"></span><button type="button" class="sl-extend-btn" data-ref="extendBtn"></button>`;
3541
- (this.regions["bottom-center"] ?? this.els.map).appendChild(el);
3542
- this.extendEl = el;
3543
- this.els.extendTxt = el.querySelector('[data-ref="extendTxt"]');
3544
- this.els.extendBtn = el.querySelector('[data-ref="extendBtn"]');
4002
+ const el2 = document.createElement("div");
4003
+ el2.className = "sl-extend";
4004
+ el2.setAttribute("role", "status");
4005
+ el2.innerHTML = `<span class="sl-extend-txt" data-ref="extendTxt"></span><button type="button" class="sl-extend-btn" data-ref="extendBtn"></button>`;
4006
+ (this.regions["bottom-center"] ?? this.els.map).appendChild(el2);
4007
+ this.extendEl = el2;
4008
+ this.els.extendTxt = el2.querySelector('[data-ref="extendTxt"]');
4009
+ this.els.extendBtn = el2.querySelector('[data-ref="extendBtn"]');
3545
4010
  this.els.extendBtn.textContent = "Add time";
3546
4011
  this.els.extendBtn.addEventListener("click", () => void this.handleExtend());
3547
4012
  }
3548
4013
  /** Success overlay + onBooked fire when the held seats settle to booked. */
3549
4014
  buildBookedOverlay() {
3550
- const el = document.createElement("div");
3551
- el.className = "sl-booked";
3552
- el.setAttribute("role", "status");
3553
- el.setAttribute("aria-live", "polite");
3554
- el.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title">You're all set</div><div class="sl-booked-sub" data-ref="bookedSub"></div>`;
3555
- this.root.appendChild(el);
3556
- this.bookedEl = el;
3557
- this.els.bookedSub = el.querySelector('[data-ref="bookedSub"]');
4015
+ const el2 = document.createElement("div");
4016
+ el2.className = "sl-booked";
4017
+ el2.setAttribute("role", "status");
4018
+ el2.setAttribute("aria-live", "polite");
4019
+ el2.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title">You're all set</div><div class="sl-booked-sub" data-ref="bookedSub"></div>`;
4020
+ this.root.appendChild(el2);
4021
+ this.bookedEl = el2;
4022
+ this.els.bookedSub = el2.querySelector('[data-ref="bookedSub"]');
3558
4023
  }
3559
4024
  /**
3560
4025
  * Localized string with a literal fallback. `t()` returns the key itself for
@@ -3568,13 +4033,13 @@ var SeatPicker = class _SeatPicker {
3568
4033
  /** Sold-out overlay — centered over the map, disabled waitlist stub (Gap 2). */
3569
4034
  buildSoldoutOverlay() {
3570
4035
  if (!this.els.map) return;
3571
- const el = document.createElement("div");
3572
- el.className = "sl-soldout";
3573
- el.setAttribute("role", "status");
4036
+ const el2 = document.createElement("div");
4037
+ el2.className = "sl-soldout";
4038
+ el2.setAttribute("role", "status");
3574
4039
  const name = (this.controller.doc?.theme?.brandName ?? this.opts.theme?.brandName ?? this.els.name?.textContent ?? this.tf("picker.soldOutEyebrow", "This event")).toUpperCase();
3575
- el.innerHTML = `<div class="sl-soldout-eyebrow">${name}</div><div class="sl-soldout-title">${this.tf("picker.soldOutTitle", "Sold out")}</div><p class="sl-soldout-copy">${this.tf("picker.soldOutCopy", "Every seat is gone. Join the waitlist and we\u2019ll email you if seats are released.")}</p><button type="button" class="sl-soldout-btn" disabled>${this.tf("picker.waitlist", "Join waitlist")}</button>`;
3576
- this.els.map.appendChild(el);
3577
- this.soldoutEl = el;
4040
+ el2.innerHTML = `<div class="sl-soldout-eyebrow">${name}</div><div class="sl-soldout-title">${this.tf("picker.soldOutTitle", "Sold out")}</div><p class="sl-soldout-copy">${this.tf("picker.soldOutCopy", "Every seat is gone. Join the waitlist and we\u2019ll email you if seats are released.")}</p><button type="button" class="sl-soldout-btn" disabled>${this.tf("picker.waitlist", "Join waitlist")}</button>`;
4041
+ this.els.map.appendChild(el2);
4042
+ this.soldoutEl = el2;
3578
4043
  }
3579
4044
  /**
3580
4045
  * Recompute the sold-out state on every price/availability sync. Sold-out ⇔
@@ -3631,10 +4096,10 @@ var SeatPicker = class _SeatPicker {
3631
4096
  if (this.badgeHidden(chartTheme)) return;
3632
4097
  const foot = this.els.foot;
3633
4098
  if (!foot) return;
3634
- const el = document.createElement("div");
3635
- el.className = "sl-powered";
3636
- el.innerHTML = `<span class="sl-powered-mark" aria-hidden="true">` + SEATLAYER_ATTRIBUTION_MARK_SVG + `</span><span>${this.tf("picker.poweredBy", "Powered by SeatLayer")}</span>`;
3637
- foot.appendChild(el);
4099
+ const el2 = document.createElement("div");
4100
+ el2.className = "sl-powered";
4101
+ el2.innerHTML = `<span class="sl-powered-mark" aria-hidden="true">` + SEATLAYER_ATTRIBUTION_MARK_SVG + `</span><span>${this.tf("picker.poweredBy", "Powered by SeatLayer")}</span>`;
4102
+ foot.appendChild(el2);
3638
4103
  }
3639
4104
  // ---- Feature 6: chrome anchor regions -------------------------------------
3640
4105
  /**
@@ -3648,11 +4113,11 @@ var SeatPicker = class _SeatPicker {
3648
4113
  if (!this.els.map) return;
3649
4114
  const REGIONS = ["top-left", "top-center", "top-right", "left-rail", "bottom-left", "bottom-center", "bottom-right"];
3650
4115
  for (const region of REGIONS) {
3651
- const el = document.createElement("div");
3652
- el.className = "sl-anchor";
3653
- el.dataset.region = region;
3654
- this.els.map.appendChild(el);
3655
- this.regions[region] = el;
4116
+ const el2 = document.createElement("div");
4117
+ el2.className = "sl-anchor";
4118
+ el2.dataset.region = region;
4119
+ this.els.map.appendChild(el2);
4120
+ this.regions[region] = el2;
3656
4121
  }
3657
4122
  }
3658
4123
  // ---- F3 minimap -----------------------------------------------------------
@@ -3672,12 +4137,12 @@ var SeatPicker = class _SeatPicker {
3672
4137
  this.motionTimers.add(timer);
3673
4138
  }
3674
4139
  /** Restart one finite CSS animation without leaving a permanent state class. */
3675
- animateOnce(el, className, duration = 600) {
3676
- if (!el || this.reducedMotion()) return;
3677
- el.classList.remove(className);
3678
- void el.offsetWidth;
3679
- el.classList.add(className);
3680
- this.scheduleMotion(() => el.classList.remove(className), duration);
4140
+ animateOnce(el2, className, duration = 600) {
4141
+ if (!el2 || this.reducedMotion()) return;
4142
+ el2.classList.remove(className);
4143
+ void el2.offsetWidth;
4144
+ el2.classList.add(className);
4145
+ this.scheduleMotion(() => el2.classList.remove(className), duration);
3681
4146
  }
3682
4147
  /** Selection feedback belongs on the selected seat, not across the whole map. */
3683
4148
  flashPickedSeat(id) {
@@ -4331,13 +4796,13 @@ var SeatPicker = class _SeatPicker {
4331
4796
  const cat = this.controller.doc?.categories.find((candidate) => candidate.key === table.categoryKey);
4332
4797
  const variable = table.bookingMode === "variable";
4333
4798
  const typeWord = this.rowTypeWord(table);
4334
- const el = document.createElement("div");
4335
- el.className = "sl-table-scrim";
4336
- el.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc3(variable ? `Flexible party \xB7 ${typeWord}` : `Whole ${typeWord}`)}</div><h2 class="sl-table-title" id="sl-table-title">${esc3(table.displayLabel ?? table.label)}</h2><p class="sl-table-copy" id="sl-table-copy">${variable ? `Choose how many guests will sit together. This table is held exclusively for your party.` : `All ${table.capacity} places are booked together as one exclusive table.`}</p></div><div class="sl-table-body"><div class="sl-table-summary"><span>${esc3(cat?.label ?? table.categoryKey)}</span><b data-table-unit>${this.money(this.paidPrice(table.categoryKey, table.tierId ?? null, table.price))} per guest</b><span class="muted">Table capacity</span><span>${table.capacity} guests</span><span class="muted">Total</span><b data-table-total></b></div>` + (variable ? `<label class="sl-table-qtylabel" id="sl-table-qty-label">Number of guests</label><div class="sl-table-stepper" role="group" aria-labelledby="sl-table-qty-label"><button type="button" data-table-step="-1" aria-label="Fewer guests">\u2212</button><output aria-live="polite" data-table-qty>${table.quantity}</output><button type="button" data-table-step="1" aria-label="More guests">+</button></div><div class="sl-table-range">Choose ${table.minOccupancy}\u2013${table.maxOccupancy} guests</div>` : `<input type="hidden" data-table-qty value="${table.capacity}">`) + `<div class="sl-table-actions"><button type="button" class="sl-table-cancel">Cancel</button><button type="button" class="sl-table-confirm">${held ? "Update table" : variable ? "Select table" : "Select whole table"}</button></div></div></section>`;
4337
- this.root.appendChild(el);
4338
- this.tableDialogEl = el;
4799
+ const el2 = document.createElement("div");
4800
+ el2.className = "sl-table-scrim";
4801
+ el2.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc3(variable ? `Flexible party \xB7 ${typeWord}` : `Whole ${typeWord}`)}</div><h2 class="sl-table-title" id="sl-table-title">${esc3(table.displayLabel ?? table.label)}</h2><p class="sl-table-copy" id="sl-table-copy">${variable ? `Choose how many guests will sit together. This table is held exclusively for your party.` : `All ${table.capacity} places are booked together as one exclusive table.`}</p></div><div class="sl-table-body"><div class="sl-table-summary"><span>${esc3(cat?.label ?? table.categoryKey)}</span><b data-table-unit>${this.money(this.paidPrice(table.categoryKey, table.tierId ?? null, table.price))} per guest</b><span class="muted">Table capacity</span><span>${table.capacity} guests</span><span class="muted">Total</span><b data-table-total></b></div>` + (variable ? `<label class="sl-table-qtylabel" id="sl-table-qty-label">Number of guests</label><div class="sl-table-stepper" role="group" aria-labelledby="sl-table-qty-label"><button type="button" data-table-step="-1" aria-label="Fewer guests">\u2212</button><output aria-live="polite" data-table-qty>${table.quantity}</output><button type="button" data-table-step="1" aria-label="More guests">+</button></div><div class="sl-table-range">Choose ${table.minOccupancy}\u2013${table.maxOccupancy} guests</div>` : `<input type="hidden" data-table-qty value="${table.capacity}">`) + `<div class="sl-table-actions"><button type="button" class="sl-table-cancel">Cancel</button><button type="button" class="sl-table-confirm">${held ? "Update table" : variable ? "Select table" : "Select whole table"}</button></div></div></section>`;
4802
+ this.root.appendChild(el2);
4803
+ this.tableDialogEl = el2;
4339
4804
  this.renderTableDialogState();
4340
- el.querySelectorAll("[data-table-step]").forEach((button) => {
4805
+ el2.querySelectorAll("[data-table-step]").forEach((button) => {
4341
4806
  button.addEventListener("click", () => {
4342
4807
  if (!this.tableDialog) return;
4343
4808
  const next = Math.max(
@@ -4348,14 +4813,14 @@ var SeatPicker = class _SeatPicker {
4348
4813
  this.renderTableDialogState();
4349
4814
  });
4350
4815
  });
4351
- el.querySelector(".sl-table-cancel").addEventListener("click", () => this.cancelTableDialog());
4352
- el.querySelector(".sl-table-confirm").addEventListener("click", () => void this.confirmTableDialog());
4353
- el.addEventListener("mousedown", (event) => {
4354
- if (event.target === el) this.cancelTableDialog();
4816
+ el2.querySelector(".sl-table-cancel").addEventListener("click", () => this.cancelTableDialog());
4817
+ el2.querySelector(".sl-table-confirm").addEventListener("click", () => void this.confirmTableDialog());
4818
+ el2.addEventListener("mousedown", (event) => {
4819
+ if (event.target === el2) this.cancelTableDialog();
4355
4820
  });
4356
- el.addEventListener("keydown", (event) => {
4821
+ el2.addEventListener("keydown", (event) => {
4357
4822
  if (event.key !== "Tab") return;
4358
- const focusable = [...el.querySelectorAll('button:not(:disabled),[tabindex]:not([tabindex="-1"])')];
4823
+ const focusable = [...el2.querySelectorAll('button:not(:disabled),[tabindex]:not([tabindex="-1"])')];
4359
4824
  if (!focusable.length) return;
4360
4825
  const first = focusable[0];
4361
4826
  const last = focusable[focusable.length - 1];
@@ -4367,22 +4832,22 @@ var SeatPicker = class _SeatPicker {
4367
4832
  first.focus();
4368
4833
  }
4369
4834
  });
4370
- requestAnimationFrame(() => el.querySelector(variable ? '[data-table-step="-1"]' : ".sl-table-confirm")?.focus());
4835
+ requestAnimationFrame(() => el2.querySelector(variable ? '[data-table-step="-1"]' : ".sl-table-confirm")?.focus());
4371
4836
  }
4372
4837
  renderTableDialogState() {
4373
4838
  const table = this.tableDialog;
4374
- const el = this.tableDialogEl;
4375
- if (!table || !el) return;
4376
- const output = el.querySelector("output[data-table-qty]");
4839
+ const el2 = this.tableDialogEl;
4840
+ if (!table || !el2) return;
4841
+ const output = el2.querySelector("output[data-table-qty]");
4377
4842
  if (output) output.value = String(table.quantity);
4378
- const hidden = el.querySelector("input[data-table-qty]");
4843
+ const hidden = el2.querySelector("input[data-table-qty]");
4379
4844
  if (hidden) hidden.value = String(table.quantity);
4380
- el.querySelectorAll("[data-table-step]").forEach((button) => {
4845
+ el2.querySelectorAll("[data-table-step]").forEach((button) => {
4381
4846
  const delta = Number(button.dataset.tableStep);
4382
4847
  button.disabled = delta < 0 ? table.quantity <= table.minOccupancy : table.quantity >= table.maxOccupancy;
4383
4848
  });
4384
4849
  const unit = this.paidPrice(table.categoryKey, table.tierId ?? null, table.price);
4385
- const total = el.querySelector("[data-table-total]");
4850
+ const total = el2.querySelector("[data-table-total]");
4386
4851
  if (total) total.textContent = this.money(unit * table.quantity);
4387
4852
  }
4388
4853
  async confirmTableDialog() {
@@ -4478,18 +4943,18 @@ var SeatPicker = class _SeatPicker {
4478
4943
  details?.rowLabel || details?.objectType === "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${safe(this.rowTypeWord(details))}</span><span class="sl-confirm-value">${safe(this.rowShort(details) ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : "",
4479
4944
  details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">Seat</span><span class="sl-confirm-value">${safe(details?.seatNumber ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : ""
4480
4945
  ].filter(Boolean).join("");
4481
- const el = document.createElement("div");
4482
- el.className = "sl-confirm";
4483
- el.setAttribute("role", "dialog");
4484
- el.setAttribute("aria-modal", "true");
4485
- el.setAttribute("aria-label", `Confirm seat ${seat.label}`);
4486
- el.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
4487
- el.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + this.see3dConfirmHtml() + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
4488
- this.els.map.appendChild(el);
4489
- this.confirmEl = el;
4946
+ const el2 = document.createElement("div");
4947
+ el2.className = "sl-confirm";
4948
+ el2.setAttribute("role", "dialog");
4949
+ el2.setAttribute("aria-modal", "true");
4950
+ el2.setAttribute("aria-label", `Confirm seat ${seat.label}`);
4951
+ el2.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
4952
+ el2.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + this.see3dConfirmHtml() + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
4953
+ this.els.map.appendChild(el2);
4954
+ this.confirmEl = el2;
4490
4955
  this.reanchorConfirm();
4491
- el.querySelector(".sl-confirm-view")?.addEventListener("click", () => this.openSeatView(seat));
4492
- el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
4956
+ el2.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
4957
+ el2.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
4493
4958
  if (this.buyerView === "venue3d") {
4494
4959
  void this.view3dHandle?.flyToSeat(seat.id);
4495
4960
  } else {
@@ -4498,9 +4963,9 @@ var SeatPicker = class _SeatPicker {
4498
4963
  void this.enter3d(seat.id);
4499
4964
  }
4500
4965
  });
4501
- el.querySelector(".sl-confirm-add").addEventListener("click", () => this.commitConfirm());
4502
- el.querySelector(".sl-confirm-cancel").addEventListener("click", () => this.cancelConfirm());
4503
- requestAnimationFrame(() => el.querySelector(".sl-confirm-add")?.focus());
4966
+ el2.querySelector(".sl-confirm-add").addEventListener("click", () => this.commitConfirm());
4967
+ el2.querySelector(".sl-confirm-cancel").addEventListener("click", () => this.cancelConfirm());
4968
+ requestAnimationFrame(() => el2.querySelector(".sl-confirm-add")?.focus());
4504
4969
  }
4505
4970
  reanchorConfirm() {
4506
4971
  if (!this.confirmEl || !this.confirmSeat) return;
@@ -4559,10 +5024,13 @@ var SeatPicker = class _SeatPicker {
4559
5024
  * uploaded photo (seat.viewUrl) when present, else a panorama generated from
4560
5025
  * the chart geometry — the stage placed at this seat's true bearing + size.
4561
5026
  * Zero extra dependencies: an equirectangular image panned with `repeat-x`.
5027
+ *
5028
+ * Async only because the generator is a lazy chunk (see `loadPanorama`); an
5029
+ * organizer photo needs no generator and never waits on it. The two callers
5030
+ * are click handlers, so nothing observes the promise.
4562
5031
  */
4563
- openSeatView(seat) {
5032
+ async openSeatView(seat) {
4564
5033
  if (!this.root || !this.seatViewEnabled()) return;
4565
- this.closeSeatView();
4566
5034
  const doc = this.controller.doc;
4567
5035
  const activeId = this.controller.getActiveFloorId();
4568
5036
  const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
@@ -4574,18 +5042,27 @@ var SeatPicker = class _SeatPicker {
4574
5042
  caption = (0, import_core2.t)("picker.panorama360");
4575
5043
  real = true;
4576
5044
  } else {
4577
- const pano2 = (0, import_core2.generateSeatPanorama)(seat, focal, this.allSeats());
5045
+ let pano2;
5046
+ try {
5047
+ const { generateSeatPanorama } = await loadPanorama();
5048
+ pano2 = generateSeatPanorama(seat, focal, this.allSeats());
5049
+ } catch (err) {
5050
+ this.opts.onError?.(err);
5051
+ return;
5052
+ }
5053
+ if (!this.root || !this.seatViewEnabled()) return;
4578
5054
  panoUrl = pano2.url;
4579
5055
  caption = (0, import_core2.t)("picker.illustrationCaption", { m: pano2.distanceM });
4580
5056
  }
4581
- const el = document.createElement("div");
4582
- el.className = "sl-view";
4583
- el.setAttribute("role", "dialog");
4584
- el.setAttribute("aria-label", (0, import_core2.t)("picker.viewFromSeat", { label: seat.label }));
4585
- el.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="Close"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint">Drag to look around \xB7 scroll to zoom</span></div>`;
4586
- this.root.appendChild(el);
4587
- this.viewEl = el;
4588
- const pano = el.querySelector(".sl-view-pano");
5057
+ this.closeSeatView();
5058
+ const el2 = document.createElement("div");
5059
+ el2.className = "sl-view";
5060
+ el2.setAttribute("role", "dialog");
5061
+ el2.setAttribute("aria-label", (0, import_core2.t)("picker.viewFromSeat", { label: seat.label }));
5062
+ el2.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="Close"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint">Drag to look around \xB7 scroll to zoom</span></div>`;
5063
+ this.root.appendChild(el2);
5064
+ this.viewEl = el2;
5065
+ const pano = el2.querySelector(".sl-view-pano");
4589
5066
  pano.style.backgroundImage = `url("${panoUrl}")`;
4590
5067
  const VFOV_DEG = 70;
4591
5068
  const MAX_PITCH_DEG = 35;
@@ -4637,7 +5114,7 @@ var SeatPicker = class _SeatPicker {
4637
5114
  pano.addEventListener("pointerup", onUp);
4638
5115
  pano.addEventListener("pointercancel", onUp);
4639
5116
  pano.addEventListener("wheel", onWheel, { passive: false });
4640
- const closeBtn = el.querySelector(".sl-view-x");
5117
+ const closeBtn = el2.querySelector(".sl-view-x");
4641
5118
  closeBtn.addEventListener("click", () => this.closeSeatView());
4642
5119
  const onKey = (e) => {
4643
5120
  if (e.key === "Escape") {
@@ -4645,7 +5122,7 @@ var SeatPicker = class _SeatPicker {
4645
5122
  this.closeSeatView();
4646
5123
  }
4647
5124
  };
4648
- el.addEventListener("keydown", onKey);
5125
+ el2.addEventListener("keydown", onKey);
4649
5126
  closeBtn.focus();
4650
5127
  this.viewCleanup = () => {
4651
5128
  pano.removeEventListener("pointerdown", onDown);
@@ -4653,7 +5130,7 @@ var SeatPicker = class _SeatPicker {
4653
5130
  pano.removeEventListener("pointerup", onUp);
4654
5131
  pano.removeEventListener("pointercancel", onUp);
4655
5132
  pano.removeEventListener("wheel", onWheel);
4656
- el.removeEventListener("keydown", onKey);
5133
+ el2.removeEventListener("keydown", onKey);
4657
5134
  };
4658
5135
  }
4659
5136
  closeSeatView() {
@@ -4943,7 +5420,7 @@ var SeatPicker = class _SeatPicker {
4943
5420
  this.els.tray.querySelectorAll(".sl-chip .view[data-view-label]").forEach((btn) => {
4944
5421
  btn.addEventListener("click", () => {
4945
5422
  const seat = this.controller.seatByLabel(btn.dataset.viewLabel);
4946
- if (seat) this.openSeatView(seat);
5423
+ if (seat) void this.openSeatView(seat);
4947
5424
  });
4948
5425
  });
4949
5426
  this.els.tray.querySelectorAll(".sl-chip[data-locate]").forEach((chip) => {
@@ -4964,8 +5441,8 @@ var SeatPicker = class _SeatPicker {
4964
5441
  });
4965
5442
  });
4966
5443
  if (this.salesClosed) {
4967
- this.els.tray.querySelectorAll(".sl-ba-go,[data-ba],[data-ba-cat],[data-ba-zone],[data-ba-replace],.sl-ga button").forEach((el) => {
4968
- el.disabled = true;
5444
+ this.els.tray.querySelectorAll(".sl-ba-go,[data-ba],[data-ba-cat],[data-ba-zone],[data-ba-replace],.sl-ga button").forEach((el2) => {
5445
+ el2.disabled = true;
4969
5446
  });
4970
5447
  }
4971
5448
  const gaTotal = this.pendingGATotal(gaAreas);
@@ -5086,7 +5563,7 @@ var SeatPicker = class _SeatPicker {
5086
5563
  const seats = this.hold.seats ?? committed;
5087
5564
  this.handedOff = true;
5088
5565
  this.setCtaPhase("checkout");
5089
- this.opts.onCheckout?.(this.hold, seats, this.buildHandoff(this.hold));
5566
+ this.checkoutHandoff(this.hold, seats);
5090
5567
  return;
5091
5568
  }
5092
5569
  this.holdingLabels = new Set(committed.map((seat) => seat.label));
@@ -5115,7 +5592,7 @@ var SeatPicker = class _SeatPicker {
5115
5592
  this.flashHeldSeats(hold);
5116
5593
  this.setCtaPhase("checkout");
5117
5594
  this.emitHoldChange();
5118
- this.opts.onCheckout?.(hold, hold.seats ?? chosenSeats, this.buildHandoff(hold));
5595
+ this.checkoutHandoff(hold, hold.seats ?? chosenSeats);
5119
5596
  } catch (err) {
5120
5597
  this.opts.onError?.(err);
5121
5598
  const problem = err;
@@ -5220,6 +5697,109 @@ var SeatPicker = class _SeatPicker {
5220
5697
  this.bookedEl?.classList.add("on");
5221
5698
  this.opts.onBooked?.(handoff);
5222
5699
  }
5700
+ /**
5701
+ * The seats are held. Send the buyer wherever this picker's `checkout` option
5702
+ * says they go.
5703
+ *
5704
+ * The default branch is the literal call that stood here before hosted
5705
+ * checkout existed, unchanged, so nothing about an existing integration moves.
5706
+ */
5707
+ checkoutHandoff(hold, seats) {
5708
+ if (this.checkoutMode === "hosted") {
5709
+ void this.startHostedCheckout(hold, seats);
5710
+ return;
5711
+ }
5712
+ this.opts.onCheckout?.(hold, seats, this.buildHandoff(hold));
5713
+ }
5714
+ /**
5715
+ * Take the money ourselves, through the organizer's own gateway.
5716
+ *
5717
+ * Order of operations matters: ASK FIRST, load second. `payment-options` is
5718
+ * already in flight from render, and its answer decides whether any payment
5719
+ * code is fetched at all — an event that cannot charge never downloads the
5720
+ * card that would have charged it.
5721
+ *
5722
+ * An empty list is not a failure and never dead-ends the buyer. It routes them
5723
+ * to whatever the host has: `onCheckoutUnavailable` (with the server's reason,
5724
+ * so the host can say the right one of three very different sentences), then
5725
+ * `onCheckout` with the ordinary handoff. A host that supplied neither gets
5726
+ * the widget's own honest card instead of a press that did nothing.
5727
+ */
5728
+ async startHostedCheckout(hold, seats) {
5729
+ const handoff = this.buildHandoff(hold);
5730
+ let options = null;
5731
+ try {
5732
+ options = await (this.paymentOptions ??= this.pubApi.paymentOptions(this.opts.event));
5733
+ } catch (err) {
5734
+ this.opts.onError?.(err);
5735
+ }
5736
+ if (this.destroyed) return;
5737
+ const provider = options?.providers?.[0];
5738
+ if (!provider) {
5739
+ const reason = paymentsOffReason(options?.reason);
5740
+ const handled = !!this.opts.onCheckoutUnavailable || !!this.opts.onCheckout;
5741
+ this.opts.onCheckoutUnavailable?.({ reason, handoff });
5742
+ this.opts.onCheckout?.(hold, seats, handoff);
5743
+ if (!handled) {
5744
+ void this.openCheckoutPanel({
5745
+ kind: "unavailable",
5746
+ reason,
5747
+ seatCount: handoff.lineItems.reduce((sum, item) => sum + item.quantity, 0)
5748
+ });
5749
+ }
5750
+ return;
5751
+ }
5752
+ await this.openCheckoutPanel({
5753
+ kind: "pay",
5754
+ provider,
5755
+ order: {
5756
+ holdId: handoff.holdId,
5757
+ expiresAt: handoff.expiresAt,
5758
+ currency: handoff.currency,
5759
+ total: handoff.total,
5760
+ labels: handoff.lineItems.map((item) => item.displayLabel ?? item.label)
5761
+ }
5762
+ });
5763
+ }
5764
+ /**
5765
+ * Fetch the checkout chunk and put its card over the map.
5766
+ *
5767
+ * Every failure here lands the buyer back on a map with their seats still
5768
+ * held, which is a place they can act from — a blocked chunk request must not
5769
+ * leave them staring at a CTA that no longer does anything.
5770
+ */
5771
+ async openCheckoutPanel(state) {
5772
+ let mountCheckout2;
5773
+ try {
5774
+ ({ mountCheckout: mountCheckout2 } = await loadHostedCheckout());
5775
+ } catch (err) {
5776
+ this.opts.onError?.(err);
5777
+ this.toast("Checkout could not be opened. Your seats are still held \u2014 please try again.", "error");
5778
+ this.setCtaPhase("idle");
5779
+ return;
5780
+ }
5781
+ if (this.destroyed || !this.root) return;
5782
+ this.closeCheckoutPanel();
5783
+ this.checkoutPanel = mountCheckout2({
5784
+ root: this.root,
5785
+ state,
5786
+ startSession: (input) => this.pubApi.startCheckout(this.opts.event, input),
5787
+ orderStatus: (orderId) => this.pubApi.orderStatus(orderId),
5788
+ onCancel: () => {
5789
+ this.checkoutPanel = null;
5790
+ if (!this.hold) this.setCtaPhase("idle");
5791
+ },
5792
+ onConfirmed: (order) => {
5793
+ this.opts.onOrderConfirmed?.(order);
5794
+ void this.controller.refresh();
5795
+ },
5796
+ onError: (err) => this.opts.onError?.(err)
5797
+ });
5798
+ }
5799
+ closeCheckoutPanel() {
5800
+ this.checkoutPanel?.destroy();
5801
+ this.checkoutPanel = null;
5802
+ }
5223
5803
  /** Assemble the stable {@link CheckoutHandoff} from a hold's server line items. */
5224
5804
  buildHandoff(hold) {
5225
5805
  const items = hold.items ?? [];
@@ -5251,28 +5831,28 @@ var SeatPicker = class _SeatPicker {
5251
5831
  );
5252
5832
  }
5253
5833
  toast(msg, tone = "neutral", action) {
5254
- const el = this.els.toast;
5255
- if (!el) return;
5256
- el.replaceChildren();
5834
+ const el2 = this.els.toast;
5835
+ if (!el2) return;
5836
+ el2.replaceChildren();
5257
5837
  const copy = document.createElement("span");
5258
5838
  copy.textContent = msg;
5259
- el.appendChild(copy);
5260
- el.classList.toggle("has-action", !!action);
5839
+ el2.appendChild(copy);
5840
+ el2.classList.toggle("has-action", !!action);
5261
5841
  if (action) {
5262
5842
  const button = document.createElement("button");
5263
5843
  button.type = "button";
5264
5844
  button.className = "sl-toast-action";
5265
5845
  button.textContent = action.label;
5266
5846
  button.addEventListener("click", action.onClick, { once: true });
5267
- el.appendChild(button);
5847
+ el2.appendChild(button);
5268
5848
  }
5269
- el.dataset.tone = tone;
5270
- el.classList.add("on");
5849
+ el2.dataset.tone = tone;
5850
+ el2.classList.add("on");
5271
5851
  if (this.toastTimer) clearTimeout(this.toastTimer);
5272
5852
  this.toastTimer = setTimeout(() => {
5273
- el.classList.remove("on");
5274
- el.classList.remove("has-action");
5275
- el.dataset.tone = "neutral";
5853
+ el2.classList.remove("on");
5854
+ el2.classList.remove("has-action");
5855
+ el2.dataset.tone = "neutral";
5276
5856
  }, 4200);
5277
5857
  }
5278
5858
  placeTooltip() {
@@ -5454,7 +6034,7 @@ var SeatPicker = class _SeatPicker {
5454
6034
  }
5455
6035
  /** Build the view-from-seat panorama the cinematic dissolves into — reuses the
5456
6036
  * exact input path as the 2D `openSeatView` (organizer photo, else generated). */
5457
- seatViewFor3d(seatId) {
6037
+ async seatViewFor3d(seatId) {
5458
6038
  const seat = this.allSeats().find((s) => s.id === seatId);
5459
6039
  if (!seat) return null;
5460
6040
  if (seat.viewUrl) return { url: seat.viewUrl };
@@ -5463,7 +6043,8 @@ var SeatPicker = class _SeatPicker {
5463
6043
  const activeId = this.controller.getActiveFloorId();
5464
6044
  const focal = seat.focalPoint ?? doc.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc.focalPoint ?? { x: 0, y: 0 };
5465
6045
  try {
5466
- return { url: (0, import_core2.generateSeatPanorama)(seat, focal, this.allSeats()).url };
6046
+ const { generateSeatPanorama } = await loadPanorama();
6047
+ return { url: generateSeatPanorama(seat, focal, this.allSeats()).url };
5467
6048
  } catch {
5468
6049
  return null;
5469
6050
  }
@@ -5602,9 +6183,10 @@ var SeatPicker = class _SeatPicker {
5602
6183
  // in orbit instead of dissolving into an empty overlay.
5603
6184
  getSeatView: (id) => new Promise((resolve, reject) => {
5604
6185
  const run = () => {
5605
- const view = this.seatViewFor3d(id);
5606
- if (view) resolve(view);
5607
- else reject(new Error("seat_view_unavailable"));
6186
+ void this.seatViewFor3d(id).then((view) => {
6187
+ if (view) resolve(view);
6188
+ else reject(new Error("seat_view_unavailable"));
6189
+ });
5608
6190
  };
5609
6191
  const ric = globalThis.requestIdleCallback;
5610
6192
  if (typeof ric === "function") ric(run, { timeout: 1500 });
@@ -5882,6 +6464,7 @@ var SeatPicker = class _SeatPicker {
5882
6464
  this.closeConfirm();
5883
6465
  this.dismissTableDialog(false);
5884
6466
  this.closeSeatView();
6467
+ this.closeCheckoutPanel();
5885
6468
  this.exit3d();
5886
6469
  this.stopHoldTimer();
5887
6470
  if (this.toastTimer) clearTimeout(this.toastTimer);
@@ -6603,7 +7186,9 @@ var PREVIEW_ELIGIBLE_STROKE = "#b9c0ff";
6603
7186
  var PREVIEW_UNAVAILABLE_FILL = "#303846";
6604
7187
  var PREVIEW_UNAVAILABLE_STROKE = "#4b5669";
6605
7188
  var ALLOCATION_STROKE = "#101723";
6606
- var CHANNELS_CSS = `
7189
+ var CHANNELS_CSS = (
7190
+ /* @sl-css */
7191
+ `
6607
7192
  .slm{--slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;--slm-mo-ambient:2000ms;
6608
7193
  --slm-mo-out:cubic-bezier(.2,.8,.2,1);--slm-mo-in-out:cubic-bezier(.4,0,.2,1);--slm-mo-exit:cubic-bezier(.4,0,1,1);
6609
7194
  --slm-mo-spring:cubic-bezier(.34,1.3,.64,1)}
@@ -6792,7 +7377,8 @@ var CHANNELS_CSS = `
6792
7377
  .slm-ch-staged.shake{outline:2px solid #e5484d;outline-offset:2px}
6793
7378
  .slm-ch-staged.done{outline:2px solid #5bd39b;outline-offset:2px}
6794
7379
  }
6795
- `;
7380
+ `
7381
+ );
6796
7382
  function bucketRowsHtml(rows) {
6797
7383
  return rows.map((row, index) => `
6798
7384
  <div class="slm-ch-bucket" style="animation-delay:${Math.min(index, 4) * 30}ms">
@@ -7258,7 +7844,7 @@ var ChannelsMode = class {
7258
7844
  paintSectionTargets(sections) {
7259
7845
  const layer = this.layer;
7260
7846
  if (!layer) return;
7261
- layer.querySelectorAll(".slm-ch-section-target").forEach((el) => el.remove());
7847
+ layer.querySelectorAll(".slm-ch-section-target").forEach((el2) => el2.remove());
7262
7848
  if (!sections) return;
7263
7849
  for (const [id, section] of sections) {
7264
7850
  const width = section.maxX - section.minX;
@@ -7304,7 +7890,7 @@ var ChannelsMode = class {
7304
7890
  paintFlags(clusters) {
7305
7891
  const layer = this.layer;
7306
7892
  if (!layer) return;
7307
- layer.querySelectorAll(".slm-ch-flag").forEach((el) => el.remove());
7893
+ layer.querySelectorAll(".slm-ch-flag").forEach((el2) => el2.remove());
7308
7894
  if (this.view === "preview") return;
7309
7895
  const ranked = [...clusters.entries()].sort((a, b) => b[1].n - a[1].n).slice(0, MAX_FLAGS);
7310
7896
  for (const [channelId, cluster] of ranked) {
@@ -8667,9 +9253,9 @@ function toLocalInput(ms) {
8667
9253
  }
8668
9254
  function resolveContainer4(container) {
8669
9255
  if (typeof container === "string") {
8670
- const el = document.querySelector(container);
8671
- if (!el) throw new Error(`seatmanager: container "${container}" not found`);
8672
- return el;
9256
+ const el2 = document.querySelector(container);
9257
+ if (!el2) throw new Error(`seatmanager: container "${container}" not found`);
9258
+ return el2;
8673
9259
  }
8674
9260
  if (!(container instanceof HTMLElement)) {
8675
9261
  throw new Error("seatmanager: container must be a CSS selector or an HTMLElement");
@@ -8680,7 +9266,7 @@ function toRenderStatus(s) {
8680
9266
  return s === "blocked" ? "not_for_sale" : s;
8681
9267
  }
8682
9268
  var DEFAULT_API_BASE3 = "https://api.seatlayer.io";
8683
- var STYLE_ID2 = "seatlayer-manager-style";
9269
+ var STYLE_ID3 = "seatlayer-manager-style";
8684
9270
  var FEED_CAP = 80;
8685
9271
  var MAX_LIVE_SEAT_PULSES = 16;
8686
9272
  var MAX_LIVE_SECTION_PULSES = 4;
@@ -8690,7 +9276,9 @@ var LEGEND = [
8690
9276
  { key: "booked", label: "Booked", color: "#22a06b" },
8691
9277
  { key: "blocked", label: "Blocked", color: "#8b94ac" }
8692
9278
  ];
8693
- var MANAGER_CSS = `
9279
+ var MANAGER_CSS = (
9280
+ /* @sl-css */
9281
+ `
8694
9282
  .slm{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:480px;overflow:hidden;
8695
9283
  background:var(--slm-bg);color:var(--slm-text);font-family:var(--slm-font);border-radius:var(--slm-radius);
8696
9284
  /* Motion tokens (motion-system \xA72), declared by the cockpit ROOT rather than
@@ -8932,13 +9520,14 @@ var MANAGER_CSS = `
8932
9520
  transition:none!important;
8933
9521
  scroll-behavior:auto!important}
8934
9522
  }
8935
- ${CHANNELS_CSS}`;
9523
+ ${CHANNELS_CSS}`
9524
+ );
8936
9525
  function injectStyle() {
8937
- if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
8938
- const el = document.createElement("style");
8939
- el.id = STYLE_ID2;
8940
- el.textContent = MANAGER_CSS;
8941
- document.head.appendChild(el);
9526
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID3)) return;
9527
+ const el2 = document.createElement("style");
9528
+ el2.id = STYLE_ID3;
9529
+ el2.textContent = MANAGER_CSS;
9530
+ document.head.appendChild(el2);
8942
9531
  }
8943
9532
  function themeVars(theme) {
8944
9533
  const t3 = theme ?? {};
@@ -10089,16 +10678,16 @@ var SeatManager = class {
10089
10678
  paintModeTabs() {
10090
10679
  const available = [];
10091
10680
  this.els.modes?.querySelectorAll("[data-mode]").forEach((b) => {
10092
- const el = b;
10093
- const mode = el.dataset.mode;
10681
+ const el2 = b;
10682
+ const mode = el2.dataset.mode;
10094
10683
  const permitted = mode !== "channels" || !!this.channels;
10095
- el.hidden = !permitted;
10684
+ el2.hidden = !permitted;
10096
10685
  if (!permitted) return;
10097
- available.push({ mode, label: el.textContent ?? mode });
10686
+ available.push({ mode, label: el2.textContent ?? mode });
10098
10687
  const active = mode === this.mode;
10099
- el.classList.toggle("on", active);
10100
- el.setAttribute("aria-selected", String(active));
10101
- el.tabIndex = active ? 0 : -1;
10688
+ el2.classList.toggle("on", active);
10689
+ el2.setAttribute("aria-selected", String(active));
10690
+ el2.tabIndex = active ? 0 : -1;
10102
10691
  });
10103
10692
  const tools = this.els.tools;
10104
10693
  if (tools) {
@@ -10884,13 +11473,13 @@ var SeatManager = class {
10884
11473
  this.toast(msg, "err");
10885
11474
  }
10886
11475
  toast(msg, kind) {
10887
- const el = this.els.toast;
10888
- if (!el) return;
10889
- el.textContent = msg;
10890
- el.className = `slm-toast on ${kind}`;
11476
+ const el2 = this.els.toast;
11477
+ if (!el2) return;
11478
+ el2.textContent = msg;
11479
+ el2.className = `slm-toast on ${kind}`;
10891
11480
  if (this.toastTimer) clearTimeout(this.toastTimer);
10892
11481
  this.toastTimer = setTimeout(() => {
10893
- el.className = "slm-toast";
11482
+ el2.className = "slm-toast";
10894
11483
  }, 3200);
10895
11484
  }
10896
11485
  fail(err) {