@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.js CHANGED
@@ -1,11 +1,9 @@
1
- var __typeError = (msg) => {
2
- throw TypeError(msg);
3
- };
4
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
- var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
- var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
- var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
1
+ import {
2
+ __privateAdd,
3
+ __privateGet,
4
+ __privateMethod,
5
+ __privateSet
6
+ } from "./chunk-HMLY7DHA.js";
9
7
 
10
8
  // src/SeatingChart.ts
11
9
  import {
@@ -120,6 +118,41 @@ var PubApi = class {
120
118
  body: { holdId, ...ttlMs ? { ttlMs } : {} }
121
119
  });
122
120
  }
121
+ /**
122
+ * Which gateways this event can actually take money through — the question
123
+ * `checkout: 'hosted'` has to answer BEFORE it shows a buyer a Pay button, so
124
+ * the answer is never discovered by failing a payment.
125
+ *
126
+ * Anonymous, and it discloses no account, key, mode or currency for a gateway
127
+ * that did not match.
128
+ */
129
+ paymentOptions(key) {
130
+ return this.request(`/pub/events/${encodeURIComponent(key)}/payment-options`);
131
+ }
132
+ /**
133
+ * Turn a live hold into an order and start a payment.
134
+ *
135
+ * The amount is NOT sent: the server recomputes it from the hold's own items,
136
+ * which is the only reason a browser cannot alter what it pays. Nor is the
137
+ * PROVIDER — the event row decides which gateway charges, and a `provider` in
138
+ * the body is checked rather than obeyed (409 `provider_mismatch`). Omitting
139
+ * it is the shape that cannot disagree.
140
+ */
141
+ startCheckout(key, input) {
142
+ return this.request(`/pub/events/${encodeURIComponent(key)}/checkout`, {
143
+ method: "POST",
144
+ body: input
145
+ });
146
+ }
147
+ /**
148
+ * Poll an order while its gateway webhook lands. The order id is an
149
+ * unguessable token the buyer already holds, so it acts as the capability —
150
+ * which is also why a buyer returning from a gateway page can be told what
151
+ * happened with nothing but the id in the return URL.
152
+ */
153
+ orderStatus(orderId) {
154
+ return this.request(`/pub/orders/${encodeURIComponent(orderId)}/status`);
155
+ }
123
156
  /**
124
157
  * Mint a one-use subscribe ticket for the next socket attempt (protocol doc
125
158
  * §3). The bearer travels here, over ordinary HTTPS where CORS and Origin
@@ -1673,7 +1706,8 @@ var EmbeddedDesigner = class {
1673
1706
  }
1674
1707
  buildSkeleton(overlay) {
1675
1708
  const style = document.createElement("style");
1676
- style.textContent = `
1709
+ style.textContent = /* @sl-css */
1710
+ `
1677
1711
  @media (prefers-reduced-motion: no-preference) {
1678
1712
  @keyframes seatlayer-designer-shimmer {
1679
1713
  0% { background-position: -320px 0; }
@@ -1799,7 +1833,6 @@ import {
1799
1833
  PickerController as PickerController2,
1800
1834
  ACCESSIBILITY_TYPES,
1801
1835
  expandChart,
1802
- generateSeatPanorama,
1803
1836
  generateSeatThumb,
1804
1837
  loadLocale as loadLocale2,
1805
1838
  setStringOverrides as setStringOverrides2,
@@ -1862,20 +1895,45 @@ function hasWebGL2() {
1862
1895
  }
1863
1896
  return _webgl2Cache;
1864
1897
  }
1898
+ function cdnChunkUrl(fileName) {
1899
+ const base = SEATLAYER_MODULE_URL ?? (typeof location !== "undefined" ? location.href : void 0);
1900
+ if (!base) throw new Error(`seatlayer: cannot resolve the ${fileName} chunk URL`);
1901
+ return new URL(`./${fileName}`, base).href;
1902
+ }
1865
1903
  async function loadVenue3d() {
1866
1904
  if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
1867
- const base = SEATLAYER_MODULE_URL ?? (typeof location !== "undefined" ? location.href : void 0);
1868
- if (!base) throw new Error("seatlayer: cannot resolve the 3D view chunk URL");
1869
- const url = new URL("./seatlayer-view3d.mjs", base).href;
1870
1905
  return import(
1871
1906
  /* @vite-ignore */
1872
- url
1907
+ cdnChunkUrl("seatlayer-view3d.mjs")
1873
1908
  );
1874
1909
  }
1875
1910
  return import("@seatlayer/core/view3d");
1876
1911
  }
1912
+ async function loadPanorama() {
1913
+ if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
1914
+ return import(
1915
+ /* @vite-ignore */
1916
+ cdnChunkUrl("seatlayer-panorama.mjs")
1917
+ );
1918
+ }
1919
+ return import("@seatlayer/core");
1920
+ }
1921
+ async function loadHostedCheckout() {
1922
+ if (typeof __SEATLAYER_CDN__ !== "undefined" && __SEATLAYER_CDN__) {
1923
+ return import(
1924
+ /* @vite-ignore */
1925
+ cdnChunkUrl("seatlayer-checkout.mjs")
1926
+ );
1927
+ }
1928
+ return import("./hostedCheckout-PLHKPT6W.js");
1929
+ }
1930
+ function paymentsOffReason(reason) {
1931
+ return reason === "unavailable_for_event" || reason === "payments_off_for_event" ? reason : "not_configured";
1932
+ }
1877
1933
  var STYLE_ID = "seatlayer-picker-style";
1878
- var CSS = `
1934
+ var CSS = (
1935
+ /* @sl-css */
1936
+ `
1879
1937
  .sl-picker{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:420px;overflow:hidden;
1880
1938
  background:var(--sl-bg);color:var(--sl-text);font-family:var(--sl-font);border-radius:var(--sl-radius);
1881
1939
  --sl-r-sm:calc(var(--sl-radius) * .55);
@@ -2612,7 +2670,8 @@ var CSS = `
2612
2670
  .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}
2613
2671
  .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)}
2614
2672
  @media(max-width:640px){.sl-modal-scrim{padding:0}.sl-modal-frame{width:100%;height:100%;border-radius:0}}
2615
- `;
2673
+ `
2674
+ );
2616
2675
  function ensureStyle() {
2617
2676
  if (document.getElementById(STYLE_ID)) return;
2618
2677
  const el = document.createElement("style");
@@ -2677,6 +2736,14 @@ var SeatPicker = class _SeatPicker {
2677
2736
  this.handedOff = false;
2678
2737
  /** Guards single onBooked + single success overlay per hold. */
2679
2738
  this.bookedShown = false;
2739
+ /**
2740
+ * In-flight or settled `payment-options` for this event, started at render in
2741
+ * hosted mode. One request, kicked off while the buyer is still choosing, so
2742
+ * pressing Pay does not wait on a lookup whose answer never changes mid-session.
2743
+ */
2744
+ this.paymentOptions = null;
2745
+ /** The mounted payment card, while one is up. */
2746
+ this.checkoutPanel = null;
2680
2747
  this.extendEl = null;
2681
2748
  this.bookedEl = null;
2682
2749
  this.gaQty = /* @__PURE__ */ new Map();
@@ -2791,6 +2858,12 @@ var SeatPicker = class _SeatPicker {
2791
2858
  onObjectUnavailable: (event) => this.opts.onSelectedObjectUnavailable?.(event)
2792
2859
  });
2793
2860
  this.api = options.transport ?? this.pubApi;
2861
+ if (options.checkout === "hosted" && !this.pubApi) {
2862
+ console.warn(
2863
+ '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.'
2864
+ );
2865
+ }
2866
+ this.checkoutMode = options.checkout === "hosted" && this.pubApi ? "hosted" : "handoff";
2794
2867
  this.maxTickets = Math.max(1, Math.floor(options.maxSelection ?? DEFAULT_MAX_SELECTION2));
2795
2868
  this.cbSafe = readStoredColorblind() ?? !!options.colorblindSafe;
2796
2869
  this.controller = new PickerController2({
@@ -3154,6 +3227,7 @@ var SeatPicker = class _SeatPicker {
3154
3227
  ensureStyle();
3155
3228
  await loadLocale2(this.opts.locale);
3156
3229
  if (this.opts.messages) setStringOverrides2(this.opts.messages);
3230
+ if (this.checkoutMode === "hosted") this.paymentOptions = this.pubApi.paymentOptions(this.opts.event);
3157
3231
  const mount = resolveContainer3(this.opts.container);
3158
3232
  const root = document.createElement("div");
3159
3233
  root.className = "sl-picker";
@@ -3451,8 +3525,40 @@ var SeatPicker = class _SeatPicker {
3451
3525
  if (this.salesClosed) this.applySalesClosed();
3452
3526
  this.syncPrices();
3453
3527
  this.syncTray();
3528
+ this.resumeHostedOrder();
3454
3529
  return this;
3455
3530
  }
3531
+ /**
3532
+ * A hosted gateway returned this buyer to a page that runs the widget, with
3533
+ * `?order=…&status=…` in the URL. Pick the order up and finish the story.
3534
+ *
3535
+ * Only `success` resumes. `cancelled` means the buyer backed out at the
3536
+ * gateway and their seats are still held — the map they are looking at IS the
3537
+ * right screen, and opening a card to say "you cancelled" would be noise.
3538
+ *
3539
+ * The two parameters are then stripped with `replaceState`, because they are a
3540
+ * one-shot instruction: leaving them in place would re-open the confirmation
3541
+ * on every later navigation, and would carry an order id into browser history
3542
+ * and any Referer this page later sends. `status` is only ever removed
3543
+ * alongside an `order` we actually consumed, so a host page that uses a
3544
+ * `status` parameter of its own keeps it.
3545
+ */
3546
+ resumeHostedOrder() {
3547
+ if (this.checkoutMode !== "hosted" || typeof location === "undefined") return;
3548
+ const params = new URLSearchParams(location.search);
3549
+ const orderId = params.get("order");
3550
+ if (!orderId) return;
3551
+ const status = params.get("status");
3552
+ params.delete("order");
3553
+ params.delete("status");
3554
+ try {
3555
+ const query = params.toString();
3556
+ history.replaceState(history.state, "", `${location.pathname}${query ? `?${query}` : ""}${location.hash}`);
3557
+ } catch {
3558
+ }
3559
+ if (status !== "success") return;
3560
+ void this.openCheckoutPanel({ kind: "resume", orderId });
3561
+ }
3456
3562
  /**
3457
3563
  * Move layout-dependent chrome between its wide dock (map regions / zoom
3458
3564
  * column) and its narrow dock (the sheet's consolidated Filters row), and
@@ -4432,7 +4538,7 @@ var SeatPicker = class _SeatPicker {
4432
4538
  this.els.map.appendChild(el);
4433
4539
  this.confirmEl = el;
4434
4540
  this.reanchorConfirm();
4435
- el.querySelector(".sl-confirm-view")?.addEventListener("click", () => this.openSeatView(seat));
4541
+ el.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
4436
4542
  el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
4437
4543
  if (this.buyerView === "venue3d") {
4438
4544
  void this.view3dHandle?.flyToSeat(seat.id);
@@ -4503,10 +4609,13 @@ var SeatPicker = class _SeatPicker {
4503
4609
  * uploaded photo (seat.viewUrl) when present, else a panorama generated from
4504
4610
  * the chart geometry — the stage placed at this seat's true bearing + size.
4505
4611
  * Zero extra dependencies: an equirectangular image panned with `repeat-x`.
4612
+ *
4613
+ * Async only because the generator is a lazy chunk (see `loadPanorama`); an
4614
+ * organizer photo needs no generator and never waits on it. The two callers
4615
+ * are click handlers, so nothing observes the promise.
4506
4616
  */
4507
- openSeatView(seat) {
4617
+ async openSeatView(seat) {
4508
4618
  if (!this.root || !this.seatViewEnabled()) return;
4509
- this.closeSeatView();
4510
4619
  const doc = this.controller.doc;
4511
4620
  const activeId = this.controller.getActiveFloorId();
4512
4621
  const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
@@ -4518,10 +4627,19 @@ var SeatPicker = class _SeatPicker {
4518
4627
  caption = t2("picker.panorama360");
4519
4628
  real = true;
4520
4629
  } else {
4521
- const pano2 = generateSeatPanorama(seat, focal, this.allSeats());
4630
+ let pano2;
4631
+ try {
4632
+ const { generateSeatPanorama } = await loadPanorama();
4633
+ pano2 = generateSeatPanorama(seat, focal, this.allSeats());
4634
+ } catch (err) {
4635
+ this.opts.onError?.(err);
4636
+ return;
4637
+ }
4638
+ if (!this.root || !this.seatViewEnabled()) return;
4522
4639
  panoUrl = pano2.url;
4523
4640
  caption = t2("picker.illustrationCaption", { m: pano2.distanceM });
4524
4641
  }
4642
+ this.closeSeatView();
4525
4643
  const el = document.createElement("div");
4526
4644
  el.className = "sl-view";
4527
4645
  el.setAttribute("role", "dialog");
@@ -4887,7 +5005,7 @@ var SeatPicker = class _SeatPicker {
4887
5005
  this.els.tray.querySelectorAll(".sl-chip .view[data-view-label]").forEach((btn) => {
4888
5006
  btn.addEventListener("click", () => {
4889
5007
  const seat = this.controller.seatByLabel(btn.dataset.viewLabel);
4890
- if (seat) this.openSeatView(seat);
5008
+ if (seat) void this.openSeatView(seat);
4891
5009
  });
4892
5010
  });
4893
5011
  this.els.tray.querySelectorAll(".sl-chip[data-locate]").forEach((chip) => {
@@ -5030,7 +5148,7 @@ var SeatPicker = class _SeatPicker {
5030
5148
  const seats = this.hold.seats ?? committed;
5031
5149
  this.handedOff = true;
5032
5150
  this.setCtaPhase("checkout");
5033
- this.opts.onCheckout?.(this.hold, seats, this.buildHandoff(this.hold));
5151
+ this.checkoutHandoff(this.hold, seats);
5034
5152
  return;
5035
5153
  }
5036
5154
  this.holdingLabels = new Set(committed.map((seat) => seat.label));
@@ -5059,7 +5177,7 @@ var SeatPicker = class _SeatPicker {
5059
5177
  this.flashHeldSeats(hold);
5060
5178
  this.setCtaPhase("checkout");
5061
5179
  this.emitHoldChange();
5062
- this.opts.onCheckout?.(hold, hold.seats ?? chosenSeats, this.buildHandoff(hold));
5180
+ this.checkoutHandoff(hold, hold.seats ?? chosenSeats);
5063
5181
  } catch (err) {
5064
5182
  this.opts.onError?.(err);
5065
5183
  const problem = err;
@@ -5164,6 +5282,109 @@ var SeatPicker = class _SeatPicker {
5164
5282
  this.bookedEl?.classList.add("on");
5165
5283
  this.opts.onBooked?.(handoff);
5166
5284
  }
5285
+ /**
5286
+ * The seats are held. Send the buyer wherever this picker's `checkout` option
5287
+ * says they go.
5288
+ *
5289
+ * The default branch is the literal call that stood here before hosted
5290
+ * checkout existed, unchanged, so nothing about an existing integration moves.
5291
+ */
5292
+ checkoutHandoff(hold, seats) {
5293
+ if (this.checkoutMode === "hosted") {
5294
+ void this.startHostedCheckout(hold, seats);
5295
+ return;
5296
+ }
5297
+ this.opts.onCheckout?.(hold, seats, this.buildHandoff(hold));
5298
+ }
5299
+ /**
5300
+ * Take the money ourselves, through the organizer's own gateway.
5301
+ *
5302
+ * Order of operations matters: ASK FIRST, load second. `payment-options` is
5303
+ * already in flight from render, and its answer decides whether any payment
5304
+ * code is fetched at all — an event that cannot charge never downloads the
5305
+ * card that would have charged it.
5306
+ *
5307
+ * An empty list is not a failure and never dead-ends the buyer. It routes them
5308
+ * to whatever the host has: `onCheckoutUnavailable` (with the server's reason,
5309
+ * so the host can say the right one of three very different sentences), then
5310
+ * `onCheckout` with the ordinary handoff. A host that supplied neither gets
5311
+ * the widget's own honest card instead of a press that did nothing.
5312
+ */
5313
+ async startHostedCheckout(hold, seats) {
5314
+ const handoff = this.buildHandoff(hold);
5315
+ let options = null;
5316
+ try {
5317
+ options = await (this.paymentOptions ??= this.pubApi.paymentOptions(this.opts.event));
5318
+ } catch (err) {
5319
+ this.opts.onError?.(err);
5320
+ }
5321
+ if (this.destroyed) return;
5322
+ const provider = options?.providers?.[0];
5323
+ if (!provider) {
5324
+ const reason = paymentsOffReason(options?.reason);
5325
+ const handled = !!this.opts.onCheckoutUnavailable || !!this.opts.onCheckout;
5326
+ this.opts.onCheckoutUnavailable?.({ reason, handoff });
5327
+ this.opts.onCheckout?.(hold, seats, handoff);
5328
+ if (!handled) {
5329
+ void this.openCheckoutPanel({
5330
+ kind: "unavailable",
5331
+ reason,
5332
+ seatCount: handoff.lineItems.reduce((sum, item) => sum + item.quantity, 0)
5333
+ });
5334
+ }
5335
+ return;
5336
+ }
5337
+ await this.openCheckoutPanel({
5338
+ kind: "pay",
5339
+ provider,
5340
+ order: {
5341
+ holdId: handoff.holdId,
5342
+ expiresAt: handoff.expiresAt,
5343
+ currency: handoff.currency,
5344
+ total: handoff.total,
5345
+ labels: handoff.lineItems.map((item) => item.displayLabel ?? item.label)
5346
+ }
5347
+ });
5348
+ }
5349
+ /**
5350
+ * Fetch the checkout chunk and put its card over the map.
5351
+ *
5352
+ * Every failure here lands the buyer back on a map with their seats still
5353
+ * held, which is a place they can act from — a blocked chunk request must not
5354
+ * leave them staring at a CTA that no longer does anything.
5355
+ */
5356
+ async openCheckoutPanel(state) {
5357
+ let mountCheckout;
5358
+ try {
5359
+ ({ mountCheckout } = await loadHostedCheckout());
5360
+ } catch (err) {
5361
+ this.opts.onError?.(err);
5362
+ this.toast("Checkout could not be opened. Your seats are still held \u2014 please try again.", "error");
5363
+ this.setCtaPhase("idle");
5364
+ return;
5365
+ }
5366
+ if (this.destroyed || !this.root) return;
5367
+ this.closeCheckoutPanel();
5368
+ this.checkoutPanel = mountCheckout({
5369
+ root: this.root,
5370
+ state,
5371
+ startSession: (input) => this.pubApi.startCheckout(this.opts.event, input),
5372
+ orderStatus: (orderId) => this.pubApi.orderStatus(orderId),
5373
+ onCancel: () => {
5374
+ this.checkoutPanel = null;
5375
+ if (!this.hold) this.setCtaPhase("idle");
5376
+ },
5377
+ onConfirmed: (order) => {
5378
+ this.opts.onOrderConfirmed?.(order);
5379
+ void this.controller.refresh();
5380
+ },
5381
+ onError: (err) => this.opts.onError?.(err)
5382
+ });
5383
+ }
5384
+ closeCheckoutPanel() {
5385
+ this.checkoutPanel?.destroy();
5386
+ this.checkoutPanel = null;
5387
+ }
5167
5388
  /** Assemble the stable {@link CheckoutHandoff} from a hold's server line items. */
5168
5389
  buildHandoff(hold) {
5169
5390
  const items = hold.items ?? [];
@@ -5398,7 +5619,7 @@ var SeatPicker = class _SeatPicker {
5398
5619
  }
5399
5620
  /** Build the view-from-seat panorama the cinematic dissolves into — reuses the
5400
5621
  * exact input path as the 2D `openSeatView` (organizer photo, else generated). */
5401
- seatViewFor3d(seatId) {
5622
+ async seatViewFor3d(seatId) {
5402
5623
  const seat = this.allSeats().find((s) => s.id === seatId);
5403
5624
  if (!seat) return null;
5404
5625
  if (seat.viewUrl) return { url: seat.viewUrl };
@@ -5407,6 +5628,7 @@ var SeatPicker = class _SeatPicker {
5407
5628
  const activeId = this.controller.getActiveFloorId();
5408
5629
  const focal = seat.focalPoint ?? doc.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc.focalPoint ?? { x: 0, y: 0 };
5409
5630
  try {
5631
+ const { generateSeatPanorama } = await loadPanorama();
5410
5632
  return { url: generateSeatPanorama(seat, focal, this.allSeats()).url };
5411
5633
  } catch {
5412
5634
  return null;
@@ -5546,9 +5768,10 @@ var SeatPicker = class _SeatPicker {
5546
5768
  // in orbit instead of dissolving into an empty overlay.
5547
5769
  getSeatView: (id) => new Promise((resolve, reject) => {
5548
5770
  const run = () => {
5549
- const view = this.seatViewFor3d(id);
5550
- if (view) resolve(view);
5551
- else reject(new Error("seat_view_unavailable"));
5771
+ void this.seatViewFor3d(id).then((view) => {
5772
+ if (view) resolve(view);
5773
+ else reject(new Error("seat_view_unavailable"));
5774
+ });
5552
5775
  };
5553
5776
  const ric = globalThis.requestIdleCallback;
5554
5777
  if (typeof ric === "function") ric(run, { timeout: 1500 });
@@ -5826,6 +6049,7 @@ var SeatPicker = class _SeatPicker {
5826
6049
  this.closeConfirm();
5827
6050
  this.dismissTableDialog(false);
5828
6051
  this.closeSeatView();
6052
+ this.closeCheckoutPanel();
5829
6053
  this.exit3d();
5830
6054
  this.stopHoldTimer();
5831
6055
  if (this.toastTimer) clearTimeout(this.toastTimer);
@@ -6552,7 +6776,9 @@ var PREVIEW_ELIGIBLE_STROKE = "#b9c0ff";
6552
6776
  var PREVIEW_UNAVAILABLE_FILL = "#303846";
6553
6777
  var PREVIEW_UNAVAILABLE_STROKE = "#4b5669";
6554
6778
  var ALLOCATION_STROKE = "#101723";
6555
- var CHANNELS_CSS = `
6779
+ var CHANNELS_CSS = (
6780
+ /* @sl-css */
6781
+ `
6556
6782
  .slm{--slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;--slm-mo-ambient:2000ms;
6557
6783
  --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);
6558
6784
  --slm-mo-spring:cubic-bezier(.34,1.3,.64,1)}
@@ -6741,7 +6967,8 @@ var CHANNELS_CSS = `
6741
6967
  .slm-ch-staged.shake{outline:2px solid #e5484d;outline-offset:2px}
6742
6968
  .slm-ch-staged.done{outline:2px solid #5bd39b;outline-offset:2px}
6743
6969
  }
6744
- `;
6970
+ `
6971
+ );
6745
6972
  function bucketRowsHtml(rows) {
6746
6973
  return rows.map((row, index) => `
6747
6974
  <div class="slm-ch-bucket" style="animation-delay:${Math.min(index, 4) * 30}ms">
@@ -8639,7 +8866,9 @@ var LEGEND = [
8639
8866
  { key: "booked", label: "Booked", color: "#22a06b" },
8640
8867
  { key: "blocked", label: "Blocked", color: "#8b94ac" }
8641
8868
  ];
8642
- var MANAGER_CSS = `
8869
+ var MANAGER_CSS = (
8870
+ /* @sl-css */
8871
+ `
8643
8872
  .slm{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:480px;overflow:hidden;
8644
8873
  background:var(--slm-bg);color:var(--slm-text);font-family:var(--slm-font);border-radius:var(--slm-radius);
8645
8874
  /* Motion tokens (motion-system \xA72), declared by the cockpit ROOT rather than
@@ -8881,7 +9110,8 @@ var MANAGER_CSS = `
8881
9110
  transition:none!important;
8882
9111
  scroll-behavior:auto!important}
8883
9112
  }
8884
- ${CHANNELS_CSS}`;
9113
+ ${CHANNELS_CSS}`
9114
+ );
8885
9115
  function injectStyle() {
8886
9116
  if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
8887
9117
  const el = document.createElement("style");