@seatlayer/js 0.49.0 → 0.50.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
@@ -2345,7 +2345,12 @@ var CSS = (
2345
2345
  never inherits (or fights) the host page's own timing. Values mirror
2346
2346
  docs/motion-system-2026-08-01.md \xA72. */
2347
2347
  --slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;
2348
- --slm-mo-out:cubic-bezier(0.2,0.8,0.2,1);--slm-mo-exit:cubic-bezier(0.4,0,1,1)}
2348
+ --slm-mo-out:cubic-bezier(0.2,0.8,0.2,1);--slm-mo-exit:cubic-bezier(0.4,0,1,1);
2349
+ /* Semantic palette tokens, defined here (not resolveTokens) because they are
2350
+ the WIDGET's meanings, not an org's brand: premium gold, environment
2351
+ warning, danger, success. Hosts can still override them with CSS. */
2352
+ --sl-premium:#e8c15a;--sl-premium-deep:#c9a24b;--sl-premium-ink:#1c1608;
2353
+ --sl-warn:#d8a425;--sl-danger:#e5484d;--sl-success:#22a06b}
2349
2354
  .sl-picker *{box-sizing:border-box;margin:0;padding:0}
2350
2355
  .sl-picker button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
2351
2356
 
@@ -2371,21 +2376,41 @@ var CSS = (
2371
2376
  .sl-close.on{display:inline-flex}
2372
2377
  .sl-close svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
2373
2378
 
2374
- /* A page or popup may already own the event heading. In that case only the
2375
- duplicate identity disappears; operational header controls stay available.
2376
- Zero block padding lets an otherwise-empty header collapse completely. */
2377
- .sl-picker[data-event-details-hidden="true"] .sl-head{padding-block:0;border-bottom-width:0}
2378
- .sl-picker[data-event-details-hidden="true"] .sl-logo,
2379
- .sl-picker[data-event-details-hidden="true"] .sl-head-info{display:none!important}
2380
- .sl-picker[data-event-details-hidden="true"] .sl-hold-pill.on,
2381
- .sl-picker[data-event-details-hidden="true"] .sl-closed-pill.on,
2382
- .sl-picker[data-event-details-hidden="true"] .sl-close.on{margin-block:10px}
2379
+ /* A page or popup may already own the event heading \u2014 but "already" is true at
2380
+ load and false thirty seconds later: the buyer scrolls, the sheet opens over
2381
+ the map, fullscreen removes the page, and two-tab comparison shopping is
2382
+ normal ticket behaviour. So identity DEMOTES instead of disappearing: one
2383
+ compact line (thumb + name) that answers "which event is this money for"
2384
+ without competing with the host's own heading. The hold clock keeps a
2385
+ labelled row around it instead of floating in a dark corner. */
2386
+ .sl-picker[data-event-details-hidden="true"] .sl-head{padding-block:7px}
2387
+ .sl-picker[data-event-details-hidden="true"] .sl-logo{width:22px;height:22px;border-radius:6px;font-size:11px}
2388
+ .sl-picker[data-event-details-hidden="true"] .sl-head-name{font-size:12.5px}
2389
+ .sl-picker[data-event-details-hidden="true"] .sl-head-meta{display:none}
2383
2390
 
2384
2391
  /* body */
2385
2392
  .sl-body{display:flex;flex:1;min-height:0}
2386
2393
  .sl-map{position:relative;flex:1;min-width:0}
2387
2394
  .sl-map-host{position:absolute;inset:0}
2388
2395
  .sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow:hidden}
2396
+ /* Wide-layout panel collapse: THE CHART IS THE PRODUCT, and a fixed 300px
2397
+ panel holding an empty cart is the map's room spent on nothing. The panel
2398
+ slides to zero (children keep their laid-out width so text doesn't rewrap
2399
+ mid-slide); the "Tickets" pill in the top-right region brings it back, and
2400
+ the first seat landing in the cart reopens it automatically \u2014 a collapsed
2401
+ panel must never hide a checkout. */
2402
+ .sl-picker[data-layout="wide"] .sl-side{transition:width var(--slm-mo-slow) var(--slm-mo-out)}
2403
+ .sl-picker[data-layout="wide"][data-side-collapsed="true"] .sl-side{width:0;min-width:0;border-left-width:0}
2404
+ /* min-width:0 above is load-bearing: the panel is a flex item, and its
2405
+ automatic minimum size (from these 300px-wide children) would otherwise
2406
+ clamp the explicit width:0 straight back to 300 \u2014 verified in-browser. */
2407
+ .sl-picker[data-layout="wide"][data-side-collapsed="true"] .sl-side>*{min-width:300px}
2408
+ .sl-side-toggle{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;padding:0 12px;
2409
+ border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;white-space:nowrap;
2410
+ background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
2411
+ .sl-side-toggle:hover{border-color:var(--sl-muted)}
2412
+ .sl-side-toggle svg{width:13px;height:13px;stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round}
2413
+ .sl-picker[data-layout="narrow"] .sl-side-toggle{display:none}
2389
2414
 
2390
2415
  /* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
2391
2416
  container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
@@ -2468,6 +2493,15 @@ var CSS = (
2468
2493
  the buyer explicitly opens the ticket panel. */
2469
2494
  .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filtersec.has{display:block!important}
2470
2495
  .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filters.has{display:flex!important}
2496
+ /* The price list is the mobile buyer's ONLY legend \u2014 colour \u2192 ticket type \u2192
2497
+ price \u2192 how many are left. Hiding it in the peek state is right (map-first);
2498
+ never restoring it forced phone buyers to tap dots to learn prices. The OPEN
2499
+ sheet brings it back, exactly like the filters row above. The tray hint comes
2500
+ back with it: the first-time buyer on a phone needs the instruction more than
2501
+ the desktop buyer, not less. */
2502
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices-sec{display:flex!important}
2503
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices{display:flex!important}
2504
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-tray-hint{display:block!important}
2471
2505
  .sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
2472
2506
  color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
2473
2507
  .sl-cbbtn:hover{border-color:var(--sl-muted)}
@@ -2477,13 +2511,13 @@ var CSS = (
2477
2511
  .sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
2478
2512
  border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
2479
2513
  .sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
2480
- .sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:9px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-accent)}
2514
+ .sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:10px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-accent)}
2481
2515
  .sl-offer-name{display:block;margin-top:2px;font-size:13px;font-weight:800;line-height:1.3}.sl-offer-line{display:block;margin-top:3px;font-size:11px;line-height:1.35;color:var(--sl-muted)}
2482
2516
  .sl-offer-info{position:relative;flex:none}.sl-offer-info>summary{list-style:none;width:25px;height:25px;border:1px solid var(--sl-line);border-radius:999px;
2483
2517
  display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
2484
2518
  .sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
2485
2519
  .sl-offer-detail{margin-top:10px;padding-top:9px;border-top:1px solid var(--sl-line);font-size:10.5px;line-height:1.45;color:var(--sl-muted)}
2486
- .sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
2520
+ .sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
2487
2521
  .sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
2488
2522
  .sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
2489
2523
  background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
@@ -2493,7 +2527,7 @@ var CSS = (
2493
2527
  padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
2494
2528
  .sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
2495
2529
  .sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
2496
- .sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:9px;font-weight:750}
2530
+ .sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:10px;font-weight:750}
2497
2531
  .sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
2498
2532
  .sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
2499
2533
  .sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
@@ -2536,7 +2570,7 @@ var CSS = (
2536
2570
  .sl-chip-id .fld{min-width:0}
2537
2571
  .sl-chip-id .fld.sec{flex:1}
2538
2572
  .sl-chip-id .fld.mid{flex:none;text-align:center}
2539
- .sl-chip-eb{display:block;font-size:8px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);margin-bottom:1px}
2573
+ .sl-chip-eb{display:block;font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);margin-bottom:1px}
2540
2574
  .sl-chip-id .val{display:block;font-weight:600;font-size:13px;line-height:1.25;white-space:nowrap}
2541
2575
  .sl-chip-id .fld.sec .val{overflow:hidden;text-overflow:ellipsis}
2542
2576
  .sl-chip-sub{display:flex;align-items:center;gap:6px;min-width:0}
@@ -2546,7 +2580,7 @@ var CSS = (
2546
2580
  .sl-chip .rm,.sl-chip .view{flex:1;min-height:26px;border-radius:0;display:flex;align-items:center;justify-content:center;
2547
2581
  color:var(--sl-muted);transition:color .15s,background .15s}
2548
2582
  .sl-chip .view{border-top:1px solid var(--sl-line)}
2549
- .sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:#e5484d;background:color-mix(in srgb,#e5484d 9%,transparent)}
2583
+ .sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:var(--sl-danger);background:color-mix(in srgb,var(--sl-danger) 9%,transparent)}
2550
2584
  .sl-chip .view:hover,.sl-chip .view:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
2551
2585
  .sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
2552
2586
  .sl-chip .view svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.8;fill:none}
@@ -2556,7 +2590,7 @@ var CSS = (
2556
2590
  .sl-live{display:none;align-items:center;gap:7px;margin:10px 14px 0;padding:7px 9px;flex:none;
2557
2591
  border:1px solid var(--sl-line);border-radius:8px;background:color-mix(in srgb,var(--sl-accent) 4%,var(--sl-surface));
2558
2592
  font-size:11px;color:var(--sl-muted)}
2559
- .sl-live .dot{width:6px;height:6px;border-radius:999px;background:#22a06b;box-shadow:0 0 6px rgba(34,160,107,.75);flex:none}
2593
+ .sl-live .dot{width:6px;height:6px;border-radius:999px;background:var(--sl-success);box-shadow:0 0 6px color-mix(in srgb,var(--sl-success) 75%,transparent);flex:none}
2560
2594
  .sl-live span:last-child{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
2561
2595
  .sl-live.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
2562
2596
 
@@ -2626,17 +2660,17 @@ var CSS = (
2626
2660
  .sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-left"]{max-width:30%}
2627
2661
  .sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
2628
2662
 
2629
- /* TEST MODE is environment context, not an action. A clipped corner ribbon
2630
- keeps it persistent without impersonating a button or competing with Map/3D.
2631
- The top-left interactive region moves below it only on test events. */
2632
- .sl-testbadge{position:absolute;top:17px;left:-38px;z-index:6;width:142px;padding:5px 0;
2633
- transform:rotate(-45deg);text-align:center;pointer-events:none;
2634
- font-size:9.5px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;
2635
- white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
2636
- box-shadow:0 2px 8px rgba(0,0,0,.28)}
2637
- .sl-picker[data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:96px}
2638
- .sl-picker[data-layout="narrow"] .sl-testbadge{top:14px;left:-35px;width:128px;font-size:8.5px}
2639
- .sl-picker[data-layout="narrow"][data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:88px}
2663
+ /* TEST MODE is environment context, not an action. A small squared chip that
2664
+ flows FIRST in the top-left anchor region: the old rotated corner ribbon sat
2665
+ at a negative offset and was clipped by the widget root's rounded
2666
+ overflow:hidden (its ends cut mid-word), and its accent fill made an
2667
+ environment flag wear the same gold as "buy". Amber warning tone, dashed
2668
+ border \u2014 reads as a tag, never as a control; the a11y chips wrap beside it
2669
+ in the same region, so no displacement hack is needed. */
2670
+ .sl-testbadge{display:inline-flex;align-items:center;padding:5px 10px;border-radius:7px;pointer-events:none;
2671
+ font-size:10px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;white-space:nowrap;
2672
+ color:var(--sl-warn);background:color-mix(in srgb,var(--sl-warn) 13%,var(--sl-surface));
2673
+ border:1px dashed color-mix(in srgb,var(--sl-warn) 55%,transparent)}
2640
2674
 
2641
2675
  /* zoom column (flows within the bottom-right region) */
2642
2676
  .sl-zoom{display:flex;flex-direction:column;gap:6px}
@@ -2646,6 +2680,18 @@ var CSS = (
2646
2680
  color:var(--sl-text);font-size:17px;font-weight:700;display:flex;align-items:center;justify-content:center;transition:border-color .15s}
2647
2681
  .sl-zoom button:hover{border-color:var(--sl-muted)}
2648
2682
  .sl-zoom svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
2683
+ /* Full-screen promotion on phones: the capability existed (native + iOS overlay
2684
+ + framed-host paths) but hid as the 4th unlabelled glyph in a corner stack.
2685
+ On narrow layouts dockLayoutChrome moves the SAME button into the top-right
2686
+ region beside Map|3D and this class dresses it as a labelled pill \u2014 the one
2687
+ accelerator with the highest payoff on a small screen gets words. */
2688
+ .sl-zfs-lbl{display:none}
2689
+ .sl-fs-pill.sl-fs-pill{display:inline-flex;align-items:center;justify-content:center;width:auto;min-height:40px;height:auto;
2690
+ padding:0 13px;gap:6px;border-radius:999px;font-size:11px;font-weight:800;
2691
+ background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
2692
+ .sl-fs-pill.sl-fs-pill:hover{border-color:var(--sl-muted)}
2693
+ .sl-fs-pill svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
2694
+ .sl-fs-pill .sl-zfs-lbl{display:inline;letter-spacing:.02em;white-space:nowrap}
2649
2695
 
2650
2696
  /* toast + boot states (toast flows in the bottom-center region) */
2651
2697
  .sl-toast{transform:translateY(6px) scale(.98);max-width:100%;
@@ -2656,9 +2702,9 @@ var CSS = (
2656
2702
  .sl-toast.has-action{pointer-events:auto;display:flex;align-items:center;gap:12px;padding-right:8px}
2657
2703
  .sl-toast-action{min-height:30px;padding:5px 10px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
2658
2704
  font:inherit;font-weight:800}
2659
- .sl-toast[data-tone="error"]{border-color:#ef4444}
2705
+ .sl-toast[data-tone="error"]{border-color:var(--sl-danger)}
2660
2706
  .sl-toast[data-tone="warning"]{border-color:var(--sl-accent)}
2661
- .sl-toast[data-tone="success"]{border-color:#22c55e}
2707
+ .sl-toast[data-tone="success"]{border-color:var(--sl-success)}
2662
2708
  .sl-toast.on[data-tone="error"]{animation:slToastNudge .32s ease-out}
2663
2709
  .sl-boot{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;align-items:center;justify-content:center;
2664
2710
  gap:10px;background:var(--sl-bg);font-size:13px;font-weight:600;color:var(--sl-muted)}
@@ -2754,7 +2800,7 @@ var CSS = (
2754
2800
  .sl-confirm-field{min-width:0;padding:12px 11px 10px;border-right:1px solid var(--sl-line)}
2755
2801
  .sl-confirm-field:last-child{border-right:0;text-align:center}
2756
2802
  .sl-confirm-field:nth-child(2){text-align:center}
2757
- .sl-confirm-key{display:block;font-size:8.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
2803
+ .sl-confirm-key{display:block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
2758
2804
  .sl-confirm-value{display:block;margin-top:4px;color:var(--sl-text);font-size:17px;line-height:1.1;font-weight:850;
2759
2805
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
2760
2806
  /* Long venue section names must read in full: smaller type + up to two lines
@@ -2819,7 +2865,7 @@ var CSS = (
2819
2865
  .sl-tip-field{min-width:0;padding:6px 9px;border-right:1px solid var(--sl-line)}
2820
2866
  .sl-tip-field:last-child{border-right:0;text-align:center}
2821
2867
  .sl-tip-grid:not(.one) .sl-tip-field:nth-child(2){text-align:center}
2822
- .sl-tip-key{display:block;font-size:7.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
2868
+ .sl-tip-key{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
2823
2869
  .sl-tip-val{display:block;margin-top:2px;color:var(--sl-text);font-size:13px;line-height:1.1;font-weight:750;
2824
2870
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
2825
2871
  .sl-tip-cat{display:flex;align-items:center;gap:7px;padding:6px 10px;font-size:11px;
@@ -2827,7 +2873,7 @@ var CSS = (
2827
2873
  .sl-tip-dot{width:8px;height:8px;border-radius:50%;flex:none}
2828
2874
  .sl-tip-name{color:var(--sl-muted);flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
2829
2875
  .sl-tip-amt{margin-left:auto;font-weight:800;color:var(--sl-text);font-variant-numeric:tabular-nums;font-size:12px}
2830
- .sl-tip-status{padding:5px 10px 7px;font-size:8.5px;letter-spacing:.09em;text-transform:uppercase;font-weight:700;color:var(--sl-muted)}
2876
+ .sl-tip-status{padding:5px 10px 7px;font-size:10px;letter-spacing:.09em;text-transform:uppercase;font-weight:700;color:var(--sl-muted)}
2831
2877
 
2832
2878
  /* Best available is a first-class shortcut, not an anonymous utility row. */
2833
2879
  .sl-ba{position:relative;flex:none;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:7px;
@@ -2843,12 +2889,12 @@ var CSS = (
2843
2889
  the confirm popover; deliberately distinct from the accent-toned qty/go. */
2844
2890
  .sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
2845
2891
  padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
2846
- color:#c9a24b;background:color-mix(in srgb,#e8c15a 10%,var(--sl-surface));
2847
- border:1px solid color-mix(in srgb,#e8c15a 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
2848
- .sl-ba-premium .star{font-size:12px;line-height:1;color:#e8c15a}
2892
+ color:var(--sl-premium-deep);background:color-mix(in srgb,var(--sl-premium) 10%,var(--sl-surface));
2893
+ border:1px solid color-mix(in srgb,var(--sl-premium) 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
2894
+ .sl-ba-premium .star{font-size:12px;line-height:1;color:var(--sl-premium)}
2849
2895
  .sl-ba-premium:hover{filter:brightness(1.05)}
2850
- .sl-ba-premium.on{color:#1c1608;background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
2851
- box-shadow:0 6px 16px color-mix(in srgb,#e8c15a 26%,transparent)}
2896
+ .sl-ba-premium.on{color:var(--sl-premium-ink);background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
2897
+ box-shadow:0 6px 16px color-mix(in srgb,var(--sl-premium) 26%,transparent)}
2852
2898
  .sl-ba-premium.on .star{color:#5a4410}
2853
2899
  .sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
2854
2900
  font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
@@ -2860,7 +2906,15 @@ var CSS = (
2860
2906
  color:var(--sl-accent-ink);font-weight:800;font-size:12px;transition:filter .15s,opacity .15s;display:flex;align-items:center;justify-content:center;gap:6px;
2861
2907
  box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
2862
2908
  .sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
2863
- .sl-picker .sl-ba-go:disabled{opacity:.62;cursor:wait}
2909
+ /* ONE disabled language, shared with the checkout CTA: surface + muted ink +
2910
+ not-allowed. A gold button at 62% still reads as gold, and cursor:wait means
2911
+ "working" \u2014 that pairing made a dead control look live. The wait cursor is
2912
+ reserved for the genuinely in-flight search, which keeps its accent fill
2913
+ via .sl-busy. */
2914
+ .sl-picker .sl-ba-go:disabled{background:var(--sl-surface);color:var(--sl-muted);cursor:not-allowed;
2915
+ filter:none;box-shadow:none}
2916
+ .sl-picker .sl-ba-go.sl-busy:disabled{background:var(--sl-accent);color:var(--sl-accent-ink);opacity:.85;cursor:wait;
2917
+ box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
2864
2918
  .sl-ba-replace{grid-column:1/-1;padding:3px 0 1px}
2865
2919
  .sl-ba-replace b{display:block;font-size:12.5px}
2866
2920
  .sl-ba-replace span{display:block;margin-top:3px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
@@ -2877,6 +2931,35 @@ var CSS = (
2877
2931
  .sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
2878
2932
  .sl-picker[data-layout="narrow"] .sl-ba-go{min-height:40px}
2879
2933
 
2934
+ /* Sales-closed is a designed state, not a set of disables: one statement in
2935
+ the tray where the buying flow was, carrying the event's own date so the
2936
+ buyer knows which night the verdict belongs to. Neutral like the header
2937
+ pill \u2014 "unavailable" never wears the accent. */
2938
+ .sl-closed-note{flex:none;display:flex;flex-direction:column;gap:4px;padding:13px;border-radius:13px;
2939
+ border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-text) 5%,var(--sl-surface))}
2940
+ .sl-closed-note b{font-size:13px;font-weight:850}
2941
+ .sl-closed-note span{font-size:11.5px;line-height:1.45;color:var(--sl-muted)}
2942
+ .sl-closed-note .when{display:block;margin-top:2px;font-size:11.5px;font-weight:700;color:var(--sl-text)}
2943
+
2944
+ /* 44px HIT FLOOR ON TOUCH. Visual sizes stay exactly as designed \u2014 the target
2945
+ grows through an invisible centred pseudo-element, the same grow-the-target-
2946
+ not-the-ink pattern the hosted event page documents for its footer links.
2947
+ Applied only to controls with clearance around them; tightly stacked pairs
2948
+ (the cart chip's remove/view rail) instead get real height on narrow. */
2949
+ @media (pointer:coarse){
2950
+ .sl-zoom button,.sl-cbbtn,.sl-close,.sl-ga-qty button,.sl-ba-qty button,.sl-price-more,
2951
+ .sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle{position:relative}
2952
+ .sl-zoom button::after,.sl-cbbtn::after,.sl-close::after,.sl-ga-qty button::after,.sl-ba-qty button::after,
2953
+ .sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after{
2954
+ content:'';position:absolute;top:50%;left:50%;width:max(100%,44px);height:max(100%,44px);
2955
+ transform:translate(-50%,-50%)}
2956
+ /* The chip rail stacks remove over view in ~53px \u2014 pseudo-targets would
2957
+ overlap and make a destructive tap ambiguous. Give the pair real height. */
2958
+ .sl-picker[data-layout="narrow"] .sl-chip{min-height:64px}
2959
+ .sl-picker[data-layout="narrow"] .sl-chip .rm,
2960
+ .sl-picker[data-layout="narrow"] .sl-chip .view{min-height:32px}
2961
+ }
2962
+
2880
2963
  /* screen-reader live region */
2881
2964
  .sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
2882
2965
 
@@ -3246,8 +3329,12 @@ var CSS = (
3246
3329
  .sl-picker[data-layout="narrow"] .sl-minimap{display:none}
3247
3330
 
3248
3331
  /* F4 legend reflection: rows + counts for out-of-band categories read muted */
3249
- .sl-price-row.sl-dim{opacity:.4}
3250
- .sl-seccard-mix-item.sl-dim{opacity:.4}
3332
+ /* Out-of-band rows are exactly the rows a buyer reads while deciding whether
3333
+ to widen a filter: whole-row opacity .4 took the label to ~2:1. Dim the
3334
+ SWATCH and soften the numerals; the words stay legible muted ink. */
3335
+ .sl-price-row.sl-dim .sl-dot{opacity:.35}
3336
+ .sl-price-row.sl-dim .sl-price-label,.sl-price-row.sl-dim .sl-price-left,.sl-price-row.sl-dim .sl-price-amt{color:var(--sl-muted)}
3337
+ .sl-seccard-mix-item.sl-dim{opacity:.55}
3251
3338
 
3252
3339
  /* Buyer-journey motion: every animation explains a state transition (selected,
3253
3340
  held, checkout handoff, conflict or booked). No decorative infinite motion
@@ -3373,6 +3460,8 @@ var SeatPicker = class _SeatPicker {
3373
3460
  // state
3374
3461
  this.currency = "USD";
3375
3462
  this.eventTimezone = null;
3463
+ /** The event's formatted date-time (event-zone), reused by the closed-state note. */
3464
+ this.eventWhenText = null;
3376
3465
  this.offerAvailability = null;
3377
3466
  this.hold = null;
3378
3467
  /** Latest server expiry for the open hold (moves on extend). */
@@ -3479,6 +3568,9 @@ var SeatPicker = class _SeatPicker {
3479
3568
  this.fsEscHandler = null;
3480
3569
  /** Host-level event chrome owns the duplicate identity outside full screen. */
3481
3570
  this.eventDetailsHidden = false;
3571
+ /** Wide-layout ticket panel collapsed (map owns the full width). */
3572
+ this.sideCollapsed = false;
3573
+ this.sideToggleEl = null;
3482
3574
  /** True once we've asked the host page to pin us fullscreen (framed, no native). */
3483
3575
  this.framedFs = false;
3484
3576
  /** Last height (px) posted to a host frame; dedupes redundant reports. */
@@ -3788,9 +3880,10 @@ var SeatPicker = class _SeatPicker {
3788
3880
  const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
3789
3881
  const hideEventDetails = this.eventDetailsHidden && !active;
3790
3882
  this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
3791
- this.els.logo?.toggleAttribute("hidden", hideEventDetails);
3792
- this.els.headInfo?.toggleAttribute("hidden", hideEventDetails);
3793
3883
  this.els.zfs?.setAttribute("aria-pressed", String(active));
3884
+ this.els.zfs?.setAttribute("title", active ? "Exit full screen" : "Full screen");
3885
+ const zfsLabel = this.els.zfs?.querySelector(".sl-zfs-lbl");
3886
+ if (zfsLabel) zfsLabel.textContent = active ? "Exit full screen" : "Full screen";
3794
3887
  this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
3795
3888
  }
3796
3889
  /**
@@ -4023,13 +4116,14 @@ var SeatPicker = class _SeatPicker {
4023
4116
  <div class="sl-map">
4024
4117
  <div class="sl-map-host" data-ref="map"></div>
4025
4118
  <div class="sl-zoom" data-ref="zoom">
4026
- <button type="button" aria-label="Zoom in" data-ref="zin">+</button>
4027
- <button type="button" aria-label="Zoom out" data-ref="zout">\u2212</button>
4028
- <button type="button" aria-label="Fit to screen" data-ref="zfit">
4119
+ <button type="button" aria-label="Zoom in" title="Zoom in" data-ref="zin">+</button>
4120
+ <button type="button" aria-label="Zoom out" title="Zoom out" data-ref="zout">\u2212</button>
4121
+ <button type="button" aria-label="Fit to screen" title="Fit to screen" data-ref="zfit">
4029
4122
  <svg viewBox="0 0 24 24"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
4030
4123
  </button>
4031
- <button type="button" aria-label="Full screen" aria-pressed="false" data-ref="zfs">
4124
+ <button type="button" aria-label="Full screen" title="Full screen" aria-pressed="false" data-ref="zfs">
4032
4125
  <svg viewBox="0 0 24 24"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
4126
+ <span class="sl-zfs-lbl" aria-hidden="true">Full screen</span>
4033
4127
  </button>
4034
4128
  </div>
4035
4129
  <div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>Loading seat map\u2026</div>
@@ -4193,17 +4287,18 @@ var SeatPicker = class _SeatPicker {
4193
4287
  badge.className = "sl-testbadge";
4194
4288
  badge.textContent = t2("picker.testMode");
4195
4289
  badge.setAttribute("aria-label", t2("picker.testMode"));
4196
- this.els.map.appendChild(badge);
4290
+ this.regions["top-left"].appendChild(badge);
4197
4291
  }
4198
4292
  const chartTheme = this.controller.doc?.theme;
4199
4293
  Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
4200
4294
  this.currency = info.currency ?? this.opts.currency ?? "USD";
4201
4295
  this.eventTimezone = info.timezone ?? null;
4202
- const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl;
4296
+ const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl ?? info.posterUrl ?? null;
4203
4297
  if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
4204
4298
  else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
4205
4299
  this.els.name.textContent = info.eventName ?? "";
4206
4300
  const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
4301
+ this.eventWhenText = when || null;
4207
4302
  this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
4208
4303
  this.buildBadge(chartTheme);
4209
4304
  const present = /* @__PURE__ */ new Set();
@@ -4294,6 +4389,8 @@ var SeatPicker = class _SeatPicker {
4294
4389
  this.buildExtendPrompt();
4295
4390
  this.buildBookedOverlay();
4296
4391
  this.buildSoldoutOverlay();
4392
+ this.buildPanelToggle();
4393
+ if (this.opts.panelCollapsed) this.setPanelCollapsed(true);
4297
4394
  this.dockLayoutChrome();
4298
4395
  await this.restoreRememberedHold();
4299
4396
  if (this.destroyed) return this;
@@ -4340,8 +4437,63 @@ var SeatPicker = class _SeatPicker {
4340
4437
  * re-render the section card in the form the layout wants (docked card/pill
4341
4438
  * on wide, sheet strip on narrow). Runs on every layout flip + once post-render.
4342
4439
  */
4440
+ /**
4441
+ * The wide-layout panel toggle: a labelled pill in the top-right region
4442
+ * beside Map|3D. Collapsing gives the map the panel's 300px; the pill's own
4443
+ * label is the way back ("Tickets"), and syncTray reopens the panel when the
4444
+ * first seat lands so the checkout can never be hidden behind a collapse.
4445
+ */
4446
+ buildPanelToggle() {
4447
+ if (!this.regions["top-right"]) return;
4448
+ const btn = document.createElement("button");
4449
+ btn.type = "button";
4450
+ btn.className = "sl-side-toggle";
4451
+ btn.addEventListener("click", () => this.setPanelCollapsed(!this.sideCollapsed));
4452
+ this.sideToggleEl = btn;
4453
+ this.regions["top-right"].appendChild(btn);
4454
+ this.syncPanelToggle();
4455
+ }
4456
+ syncPanelToggle() {
4457
+ const btn = this.sideToggleEl;
4458
+ if (!btn) return;
4459
+ const collapsed = this.sideCollapsed;
4460
+ btn.setAttribute("aria-expanded", String(!collapsed));
4461
+ btn.setAttribute("aria-label", collapsed ? "Show the ticket panel" : "Hide the ticket panel");
4462
+ btn.innerHTML = collapsed ? '<svg viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></svg><span>Tickets</span>' : '<svg viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg><span>Hide panel</span>';
4463
+ }
4464
+ /**
4465
+ * Collapse or reopen the wide-layout ticket panel in place (host-drivable —
4466
+ * a map-first embed can mount collapsed via `panelCollapsed` and reopen on
4467
+ * its own cue). Inert while collapsed so a keyboard buyer cannot tab into a
4468
+ * zero-width panel; narrow layouts ignore the state entirely (the sheet is
4469
+ * already the collapse) but keep it for the next flip back to wide.
4470
+ */
4471
+ setPanelCollapsed(collapsed) {
4472
+ if (this.destroyed) return;
4473
+ this.sideCollapsed = collapsed;
4474
+ this.applyPanelCollapsed();
4475
+ this.scheduleMotion(() => this.controller.zoomToFit(), 340);
4476
+ }
4477
+ /** Idempotent DOM apply, also run on every layout flip (inert must lift on narrow). */
4478
+ applyPanelCollapsed() {
4479
+ const wide = this.root?.dataset.layout !== "narrow";
4480
+ this.root?.setAttribute("data-side-collapsed", String(this.sideCollapsed));
4481
+ this.els.side?.toggleAttribute("inert", this.sideCollapsed && wide);
4482
+ this.syncPanelToggle();
4483
+ }
4343
4484
  dockLayoutChrome() {
4344
4485
  const narrow = this.root?.dataset.layout === "narrow";
4486
+ this.applyPanelCollapsed();
4487
+ const zfs = this.els.zfs;
4488
+ if (zfs) {
4489
+ if (narrow && this.regions["top-right"]) {
4490
+ zfs.classList.add("sl-fs-pill");
4491
+ this.regions["top-right"].appendChild(zfs);
4492
+ } else if (!narrow && this.els.zoom && zfs.parentElement !== this.els.zoom) {
4493
+ zfs.classList.remove("sl-fs-pill");
4494
+ this.els.zoom.appendChild(zfs);
4495
+ }
4496
+ }
4345
4497
  const filters = this.els.filters;
4346
4498
  if (filters) {
4347
4499
  if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
@@ -4444,6 +4596,9 @@ var SeatPicker = class _SeatPicker {
4444
4596
  text.textContent = this.tf("picker.salesClosedPill", "Sales are closed");
4445
4597
  }
4446
4598
  this.root?.setAttribute("data-sales-closed", String(this.salesClosed));
4599
+ if (this.salesClosed && this.srEl) {
4600
+ this.srEl.textContent = this.tf("picker.salesClosedToast", "Sales are closed for this event.");
4601
+ }
4447
4602
  this.syncCta();
4448
4603
  this.syncTray();
4449
4604
  }
@@ -5289,6 +5444,8 @@ var SeatPicker = class _SeatPicker {
5289
5444
  this.confirmEl = null;
5290
5445
  this.confirmSeat = seat;
5291
5446
  this.root?.setAttribute("data-confirming", "true");
5447
+ this.els.side?.toggleAttribute("inert", true);
5448
+ Object.values(this.regions).forEach((region) => region.toggleAttribute("inert", true));
5292
5449
  this.controller.setSelectionFocus(seat.id);
5293
5450
  if (previousId && previousId !== seat.id) this.controller.deselect([previousId]);
5294
5451
  if (this.tipEl) this.tipEl.style.display = "none";
@@ -5310,7 +5467,6 @@ var SeatPicker = class _SeatPicker {
5310
5467
  const el = document.createElement("div");
5311
5468
  el.className = "sl-confirm";
5312
5469
  el.setAttribute("role", "dialog");
5313
- el.setAttribute("aria-modal", "true");
5314
5470
  el.setAttribute("aria-label", `Confirm seat ${seat.label}`);
5315
5471
  el.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
5316
5472
  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.buyerView !== "venue3d" ? this.confirmThumbHtml(seat) : "") + (this.buyerView === "venue3d" ? `${this.seatConfidenceConfirmHtml(seat, `${details?.displayLabel ?? seat.displayLabel ?? seat.label} \xB7 ${price == null ? this.tf("picker.priceNotSupplied", "Price not supplied") : this.money(price)}`)}<div class="sl-confirm-inspect-row">${this.view3dCompareConfirmHtml(seat)}${this.see3dConfirmHtml()}</div>` : 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>`;
@@ -5365,6 +5521,8 @@ var SeatPicker = class _SeatPicker {
5365
5521
  this.confirmEl = null;
5366
5522
  this.confirmSeat = null;
5367
5523
  this.root?.removeAttribute("data-confirming");
5524
+ Object.values(this.regions).forEach((region) => region.toggleAttribute("inert", false));
5525
+ this.applyPanelCollapsed();
5368
5526
  this.controller.setSelectionFocus(null);
5369
5527
  }
5370
5528
  commitConfirm() {
@@ -5803,19 +5961,24 @@ var SeatPicker = class _SeatPicker {
5803
5961
  const heldItems = this.hold?.items ?? [];
5804
5962
  const parts = [];
5805
5963
  const nextTrayKeys = /* @__PURE__ */ new Set();
5806
- if (!seats.length && !heldItems.length && !gaAreas.length) {
5964
+ if (this.salesClosed && !seats.length && !heldItems.length) {
5965
+ const closedWhen = this.eventWhenText ? `<span class="when">${String(this.eventWhenText).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c])}</span>` : "";
5966
+ parts.push(
5967
+ `<div class="sl-closed-note" role="status"><b>${this.tf("picker.salesClosedPill", "Sales are closed")}</b><span>${this.tf("picker.salesClosedCopy", "Ticket sales for this event have ended.")}${closedWhen}</span></div>`
5968
+ );
5969
+ } else if (!seats.length && !heldItems.length && !gaAreas.length) {
5807
5970
  parts.push(`<div class="sl-tray-hint">Tap a seat on the map, or let us pick the best available for you.</div>`);
5808
5971
  } else if (!seats.length && !heldItems.length) {
5809
5972
  parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
5810
5973
  }
5811
5974
  const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
5812
- if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
5975
+ if (!this.salesClosed && !this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
5813
5976
  const cats = this.controller.doc?.categories ?? [];
5814
5977
  const zones = this.controller.getBestAvailableZones();
5815
5978
  if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
5816
5979
  parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Replace your current choices?</div><div class="sl-ba-replace"><b>We\u2019ll find ${this.baQty} seats together.</b><span>Your manually selected tickets will be removed only after a new group is secured.</span></div><div class="sl-ba-actions"><button type="button" data-ba-cancel>Keep mine</button><button type="button" class="replace" data-ba-replace>Find new seats</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Find the best seats together</div><div class="sl-ba-copy"><span class="wide">We\u2019ll choose the closest available group for you.</span><span class="narrow">Closest available group, chosen instantly.</span></div>` + // Premium quick-pick — present only when the chart actually has premium
5817
5980
  // seats (same present-only philosophy as the a11y filter chips).
5818
- (this.controller.hasPremiumSeats() ? `<button type="button" class="sl-ba-premium${this.baPremium ? " on" : ""}" data-ba-premium aria-pressed="${this.baPremium ? "true" : "false"}"><span class="star" aria-hidden="true">\u2605</span>${this.tf("picker.bestSeatsPremium", "Best seats")}</button>` : "") + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + (zones.length ? `<select aria-label="Preferred venue zone" data-ba-zone><option value="">Any venue zone</option>` + zones.map((zone) => `<option value="${escapeOption(zone.id)}"${this.baZone === zone.id ? " selected" : ""}>${escapeOption(zone.label)}</option>`).join("") + `</select>` : "") + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
5981
+ (this.controller.hasPremiumSeats() ? `<button type="button" class="sl-ba-premium${this.baPremium ? " on" : ""}" data-ba-premium aria-pressed="${this.baPremium ? "true" : "false"}"><span class="star" aria-hidden="true">\u2605</span>${this.tf("picker.bestSeatsPremium", "Best seats")}</button>` : "") + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + (zones.length ? `<select aria-label="Preferred venue zone" data-ba-zone><option value="">Any venue zone</option>` + zones.map((zone) => `<option value="${escapeOption(zone.id)}"${this.baZone === zone.id ? " selected" : ""}>${escapeOption(zone.label)}</option>`).join("") + `</select>` : "") + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go${this.bestAvailableBusy ? " sl-busy" : ""}"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
5819
5982
  }
5820
5983
  const idGrid = (seatId, label, objectType, quantity = 1, objectId, identity) => {
5821
5984
  const esc = (value) => String(value ?? "\u2014").replace(/[&<>"]/g, (char) => ({
@@ -6025,11 +6188,16 @@ var SeatPicker = class _SeatPicker {
6025
6188
  }
6026
6189
  if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
6027
6190
  if (total !== previousTotal) this.animateOnce(this.els.total, "sl-value-pop", 380);
6028
- if (previousCount === 0 && count > 0) this.animateOnce(this.els.cta, "sl-ready", 520);
6191
+ if (previousCount === 0 && count > 0) {
6192
+ this.animateOnce(this.els.cta, "sl-ready", 520);
6193
+ if (this.sideCollapsed && this.root?.dataset.layout !== "narrow") this.setPanelCollapsed(false);
6194
+ }
6029
6195
  if (this.els.peek) {
6030
6196
  if (count) {
6031
6197
  const holding = !!this.hold && !pendingCount;
6032
6198
  this.els.peek.innerHTML = `<span>${count} ${count === 1 ? "ticket" : "tickets"} \xB7 ${this.money(total)}</span><button type="button" class="go sl-sheet-go" data-act="${holding ? "checkout" : "open"}">${this.hold ? pendingCount ? "Secure more" : "Continue" : "Review"}</button>`;
6199
+ } else if (this.salesClosed) {
6200
+ this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
6033
6201
  } else {
6034
6202
  const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
6035
6203
  this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<button type="button" class="go sl-sheet-go" data-act="open">\u2726 Best seats</button>`;
@@ -7485,6 +7653,7 @@ var SeatPicker = class _SeatPicker {
7485
7653
  const button = this.els.tray?.querySelector(".sl-ba-go");
7486
7654
  if (button) {
7487
7655
  button.disabled = true;
7656
+ button.classList.add("sl-busy");
7488
7657
  button.innerHTML = '<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026';
7489
7658
  }
7490
7659
  try {