@seatlayer/js 0.15.1 → 0.16.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
@@ -505,19 +505,21 @@ var CSS = `
505
505
  /* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
506
506
  container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
507
507
  mobile pattern). data-sheet on the root: "peek" (default: grab handle + one
508
- summary line) / "open" (\u226450%, swipe up or auto-expand on first selection).
508
+ summary line) / "open" (room for rows + checkout, swipe up to open).
509
509
  Swipe handling lives on the sheet head ONLY \u2014 never the map host, so the
510
510
  map's raw-pointer gesture pipeline is untouched. */
511
511
  .sl-picker[data-layout="narrow"] .sl-body{flex-direction:column}
512
512
  .sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
513
513
  .sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
514
- flex:none;height:50%;transition:height .3s cubic-bezier(.2,.8,.2,1)}
514
+ flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
515
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
515
516
  .sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:86px;overflow:hidden}
516
517
  .sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
517
- .sl-picker[data-layout="narrow"] .sl-tray{flex:none}
518
- .sl-picker[data-layout="narrow"] .sl-foot{position:sticky;bottom:0;background:var(--sl-bg)}
518
+ .sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
519
+ .sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
520
+ .sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
519
521
  .sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
520
- .sl-picker[data-layout="narrow"] .sl-seats-sec{order:1}
522
+ .sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
521
523
  .sl-picker[data-layout="narrow"] .sl-tray{order:2}
522
524
  .sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
523
525
  .sl-picker[data-layout="narrow"] .sl-filters{order:4}
@@ -525,13 +527,21 @@ var CSS = `
525
527
  .sl-picker[data-layout="narrow"] .sl-pricef{order:6}
526
528
  .sl-picker[data-layout="narrow"] .sl-prices{order:7}
527
529
  .sl-picker[data-layout="narrow"] .sl-foot{order:8}
530
+ .sl-picker[data-layout="narrow"] .sl-tray-hint,
531
+ .sl-picker[data-layout="narrow"] .sl-filtersec,
532
+ .sl-picker[data-layout="narrow"] .sl-filters,
533
+ .sl-picker[data-layout="narrow"] .sl-prices-sec,
534
+ .sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
535
+ .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
536
+ .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
537
+ .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-prices-sec{display:none}
528
538
  /* touch chrome: pinch-zoom exists \u2014 hide +/\u2212 on the sheet layout (keep fit) */
529
539
  .sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zin"],
530
540
  .sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zout"]{display:none}
531
541
 
532
542
  /* bottom-sheet head: grab handle + one-line summary (narrow only). The WHOLE
533
543
  head is the tap/swipe toggle target (min 44px), so it reads as one control. */
534
- .sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 16px 8px;min-height:44px;
544
+ .sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
535
545
  cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
536
546
  .sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex}
537
547
  .sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:2px auto 7px}
@@ -546,54 +556,70 @@ var CSS = `
546
556
  /* state chevron: points UP while peeking, rotates to point DOWN when open.
547
557
  Base keeps an explicit rotate(0) \u2014 transitioning to/from a bare 'none' leaves
548
558
  the value stuck in some engines, so both endpoints must be real transforms. */
549
- .sl-sheet-chevron{flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted);
550
- transform:rotate(0deg);transition:transform .22s ease}
551
- .sl-sheet-chevron svg{width:18px;height:18px;stroke:currentColor;stroke-width:2.4;fill:none;
559
+ .sl-sheet-toggle{width:44px;height:44px;margin:-8px -8px -8px 0;border-radius:999px;flex:none;display:flex;
560
+ align-items:center;justify-content:center;color:var(--sl-muted);transition:color .15s,background .15s}
561
+ .sl-sheet-toggle:hover,.sl-sheet-toggle:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-line) 44%,transparent)}
562
+ .sl-sheet-toggle svg{width:21px;height:21px;stroke:currentColor;stroke-width:2.4;fill:none;
552
563
  stroke-linecap:round;stroke-linejoin:round}
553
- .sl-picker[data-sheet="open"] .sl-sheet-chevron{transform:rotate(180deg)}
564
+ .sl-sheet-toggle svg{transform:rotate(0deg);transition:transform .24s cubic-bezier(.2,.8,.2,1)}
565
+ .sl-picker[data-sheet="open"] .sl-sheet-toggle svg{transform:rotate(180deg)}
554
566
 
555
567
  /* consolidated Filters row inside the sheet (a11y chips + colorblind toggle
556
568
  dock here on narrow; they live on the map / zoom column on wide) */
557
569
  .sl-filtersec{display:none}
558
570
  .sl-filters{display:none;gap:6px;flex-wrap:wrap;align-items:center;padding:2px 16px 10px}
559
- .sl-picker[data-layout="narrow"] .sl-filtersec.has{display:block}
560
- .sl-picker[data-layout="narrow"] .sl-filters.has{display:flex}
571
+ .sl-picker[data-layout="narrow"] .sl-filtersec.has,
572
+ .sl-picker[data-layout="narrow"] .sl-filters.has{display:none}
573
+ .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec.has,
574
+ .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters.has{display:none}
561
575
  .sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
562
576
  color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
563
577
  .sl-cbbtn:hover{border-color:var(--sl-muted)}
564
578
  .sl-cbbtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
565
579
 
566
- /* price panel */
567
- .sl-sec{padding:14px 16px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
568
- .sl-prices{padding:4px 16px 10px;border-bottom:1px solid var(--sl-line)}
569
- .sl-price-row{display:flex;align-items:center;gap:8px;padding:5px 0;font-size:13px}
580
+ /* price panel \u2014 one compact filter control replaces the wrapping price-chip row. */
581
+ .sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
582
+ .sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
583
+ .sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
584
+ background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
585
+ .sl-prices{padding:5px 14px 10px;border-bottom:1px solid var(--sl-line)}
586
+ .sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px}
570
587
  .sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
571
588
  .sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
572
589
  .sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
573
590
  .sl-price-amt{font-weight:800;font-variant-numeric:tabular-nums}
574
- .sl-status-key{display:flex;gap:12px;flex-wrap:wrap;padding:8px 16px 12px;border-bottom:1px solid var(--sl-line);color:var(--sl-muted);font-size:11px}
591
+ .sl-status-key{display:flex;gap:13px;flex-wrap:wrap;padding:8px 0 2px;margin-top:5px;border-top:1px solid var(--sl-line);color:var(--sl-muted);font-size:10.5px}
575
592
  .sl-status-item{display:inline-flex;align-items:center;gap:6px}
576
593
  .sl-status-icon{width:17px;height:17px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;
577
- color:#fff;background:#6b7280;font-size:9px;font-weight:900;line-height:1}
578
- .sl-status-icon.sold{background:#374151;font-size:14px}
594
+ color:#fff;background:#6b7280;line-height:1}
595
+ .sl-status-icon svg{width:10px;height:10px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
596
+ .sl-status-icon.sold{background:#8b93a0}
597
+ .sl-status-icon.sold svg{width:11px;height:11px;stroke-width:2.4}
579
598
 
580
599
  /* tray */
581
- .sl-tray{flex:1;padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0}
600
+ .sl-seats-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
601
+ .sl-seat-summary{font-size:10px;letter-spacing:0;text-transform:none;white-space:nowrap}
602
+ .sl-tray{flex:1;padding:10px 14px;display:flex;flex-direction:column;gap:7px;min-height:0}
582
603
  .sl-tray-hint{font-size:12.5px;color:var(--sl-muted);line-height:1.5}
583
- .sl-chip{display:flex;align-items:center;gap:9px;padding:9px 11px;border:1px solid var(--sl-line);
584
- border-radius:var(--sl-r-sm);background:var(--sl-surface);font-size:13px;transform-origin:center}
604
+ .sl-chip{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto 30px;align-items:center;gap:8px;
605
+ min-height:53px;padding:7px 7px 7px 9px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm);
606
+ background:var(--sl-surface);font-size:13px;transform-origin:center;transition:border-color .15s,background .15s}
607
+ .sl-chip:hover{border-color:color-mix(in srgb,var(--sl-accent) 38%,var(--sl-line))}
585
608
  .sl-chip.sl-enter{animation:slChipIn .38s cubic-bezier(.2,.8,.2,1) both}
586
609
  .sl-chip.sl-leave{pointer-events:none;animation:slChipOut .16s ease-in both}
587
610
  .sl-chip.sl-held{border-color:var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
588
611
  box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
589
- .sl-chip b{font-weight:800;flex:none;min-width:max-content;white-space:nowrap}
590
- .sl-chip .cat{color:var(--sl-muted);font-size:11.5px;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
612
+ .sl-ticket-state{width:23px;height:23px;border-radius:999px;display:flex;align-items:center;justify-content:center;
613
+ background:var(--sl-accent);color:var(--sl-accent-ink)}
614
+ .sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent)}
615
+ .sl-ticket-state svg{width:13px;height:13px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
616
+ .sl-chip-main{min-width:0}
617
+ .sl-chip b{display:block;font-weight:800;min-width:max-content;white-space:nowrap}
618
+ .sl-chip-sub{display:flex;align-items:center;gap:5px;min-width:0;margin-top:2px}
619
+ .sl-chip .cat{color:var(--sl-muted);font-size:10.5px;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
591
620
  .sl-chip .amt{font-weight:700;font-variant-numeric:tabular-nums;flex:none;white-space:nowrap}
592
- .sl-chip-state{flex:none;padding:3px 6px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
593
- font-size:8.5px;line-height:1;font-weight:900;letter-spacing:.08em;text-transform:uppercase}
594
- .sl-chip-state.sl-pending{background:transparent;color:var(--sl-muted);border:1px solid var(--sl-line)}
595
- .sl-chip .rm{width:22px;height:22px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
596
- .sl-chip .rm:hover{color:var(--sl-text)}
621
+ .sl-chip .rm{width:29px;height:29px;border-radius:8px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
622
+ .sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
597
623
  .sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
598
624
 
599
625
  /* GA rows */
@@ -757,29 +783,43 @@ var CSS = `
757
783
  .sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(342px,calc(100% - 24px));
758
784
  transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
759
785
 
760
- /* best-available row */
761
- .sl-ba{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;padding:9px 11px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm)}
762
- .sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:7px;
763
- font:inherit;font-size:12px;padding:7px 8px;min-width:0;width:100%;max-width:none}
764
- .sl-ba-qty{display:flex;align-items:center;gap:7px}
765
- .sl-ba-qty button{width:24px;height:24px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
766
- font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center}
786
+ /* Best available is a first-class shortcut, not an anonymous utility row. */
787
+ .sl-ba{position:relative;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:7px;
788
+ padding:13px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));border-radius:13px;
789
+ background:linear-gradient(135deg,color-mix(in srgb,var(--sl-accent) 5%,var(--sl-surface)),color-mix(in srgb,var(--sl-accent) 11%,var(--sl-surface)))}
790
+ .sl-ba::after{content:'\u2726';position:absolute;right:10px;top:3px;color:color-mix(in srgb,var(--sl-accent) 20%,transparent);font-size:42px;line-height:1}
791
+ .sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
792
+ .sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
793
+ .sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
794
+ .sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
795
+ .sl-ba-copy .narrow{display:none}
796
+ .sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
797
+ font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
798
+ .sl-ba-qty{display:flex;align-items:center;gap:7px;padding:3px;border:1px solid var(--sl-line);border-radius:9px;background:var(--sl-surface)}
799
+ .sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
800
+ font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
767
801
  .sl-ba-qty span{min-width:14px;text-align:center;font-weight:800}
768
- .sl-ba-go{grid-column:1/-1;width:100%;min-height:38px;padding:7px 12px;border-radius:9px;border:1px solid var(--sl-line);font-weight:800;font-size:12px;
769
- transition:border-color .15s,opacity .15s;display:flex;align-items:center;justify-content:center;gap:6px}
770
- .sl-ba-go:hover{border-color:var(--sl-muted)}
771
- .sl-ba-go:disabled{opacity:.62;cursor:wait}
802
+ .sl-picker .sl-ba-go{grid-column:1/-1;width:100%;min-height:37px;padding:7px 12px;border-radius:9px;background:var(--sl-accent);
803
+ 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;
804
+ box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
805
+ .sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
806
+ .sl-picker .sl-ba-go:disabled{opacity:.62;cursor:wait}
807
+ .sl-picker[data-layout="narrow"] .sl-ba{padding:11px}
808
+ .sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
809
+ .sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
772
810
 
773
811
  /* screen-reader live region */
774
812
  .sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
775
813
 
776
814
  /* per-seat ticket-tier select + view-from-seat button in tray chips */
777
- .sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:7px;
778
- font:inherit;font-size:11px;padding:3px 5px;max-width:130px;cursor:pointer}
779
- .sl-chip .view{width:24px;height:24px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
780
- color:var(--sl-muted);transition:color .15s}
815
+ .sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:6px;
816
+ font:inherit;font-size:10px;padding:2px 4px;min-width:0;max-width:100%;cursor:pointer}
817
+ .sl-chip .view{width:20px;height:20px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
818
+ color:var(--sl-muted);opacity:.36;transition:color .15s,opacity .15s}
781
819
  .sl-chip .view:hover{color:var(--sl-text)}
782
- .sl-chip .view svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
820
+ .sl-chip:hover .view,.sl-chip .view:focus-visible{opacity:1;color:var(--sl-text)}
821
+ .sl-chip .view svg{width:12px;height:12px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
822
+ @media(pointer:coarse){.sl-chip .view{opacity:.58}}
783
823
 
784
824
  /* arena: LOD rung pills (flow within the top-center region) */
785
825
  .sl-rungs{display:none;background:var(--sl-surface);border:1px solid var(--sl-line);border-radius:999px;padding:3px}
@@ -866,10 +906,6 @@ var CSS = `
866
906
  .sl-minimap canvas{display:block}
867
907
  .sl-picker[data-layout="narrow"] .sl-minimap{display:none}
868
908
 
869
- /* F4 price-band filter \u2014 chip row in the side panel, above the price legend it
870
- * dims (keeps clear of the map's top-center rungs / top-left a11y chips). */
871
- .sl-pricef{display:flex;gap:6px;flex-wrap:wrap;padding:2px 16px 10px}
872
- .sl-pricef .sl-chip-f{padding:6px 11px;font-size:11.5px}
873
909
  /* F4 legend reflection: rows + counts for out-of-band categories read muted */
874
910
  .sl-price-row.sl-dim{opacity:.4}
875
911
  .sl-seccard-mix-item.sl-dim{opacity:.4}
@@ -1165,18 +1201,18 @@ var SeatPicker = class _SeatPicker {
1165
1201
  <div class="sl-sheet-grab"></div>
1166
1202
  <div class="sl-sheet-bar">
1167
1203
  <div class="sl-sheet-peek" data-ref="peek"></div>
1168
- <span class="sl-sheet-chevron" aria-hidden="true">
1204
+ <button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
1169
1205
  <svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
1170
- </span>
1206
+ </button>
1171
1207
  </div>
1172
1208
  </div>
1173
1209
  <div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
1174
1210
  <div class="sl-filters" data-ref="filters"></div>
1175
- <div class="sl-sec sl-prices-sec" data-ref="pricesSec">Prices</div>
1211
+ <div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
1176
1212
  <div class="sl-prices" data-ref="prices"></div>
1177
- <div class="sl-sec sl-seats-sec">Your seats</div>
1213
+ <div class="sl-sec sl-seats-sec"><span>Your seats</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
1178
1214
  <div class="sl-tray" data-ref="tray"></div>
1179
- <div class="sl-foot">
1215
+ <div class="sl-foot" data-ref="foot">
1180
1216
  <div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
1181
1217
  <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
1182
1218
  <span><b data-ref="holdTitle">Seats secured</b><span class="sl-hold-copy" data-ref="holdCopy">Checkout timer is running.</span></span>
@@ -1208,9 +1244,17 @@ var SeatPicker = class _SeatPicker {
1208
1244
  this.els.zfit.addEventListener("click", () => this.controller.zoomToFit());
1209
1245
  const head = this.els.sheetHead;
1210
1246
  if (head) {
1247
+ const toggle = this.els.sheetToggle;
1211
1248
  const setSheet = (open) => {
1212
1249
  root.dataset.sheet = open ? "open" : "peek";
1250
+ toggle?.setAttribute("aria-expanded", String(open));
1251
+ toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
1213
1252
  };
1253
+ setSheet(root.dataset.sheet === "open");
1254
+ toggle?.addEventListener("click", (e) => {
1255
+ e.stopPropagation();
1256
+ setSheet(root.dataset.sheet !== "open");
1257
+ });
1214
1258
  let startY = 0;
1215
1259
  let swiped = false;
1216
1260
  let tracking = false;
@@ -1233,7 +1277,7 @@ var SeatPicker = class _SeatPicker {
1233
1277
  });
1234
1278
  head.addEventListener("pointerup", (e) => {
1235
1279
  if (tracking && !swiped && Math.abs(e.clientY - startY) < 6) {
1236
- if (!e.target.closest(".sl-seccard")) setSheet(root.dataset.sheet !== "open");
1280
+ if (!e.target.closest(".sl-seccard,.sl-sheet-toggle")) setSheet(root.dataset.sheet !== "open");
1237
1281
  }
1238
1282
  tracking = false;
1239
1283
  head.releasePointerCapture?.(e.pointerId);
@@ -1752,39 +1796,26 @@ var SeatPicker = class _SeatPicker {
1752
1796
  }
1753
1797
  return bands;
1754
1798
  }
1755
- /**
1756
- * Build the price-band chip row in the side panel, directly under the "Prices"
1757
- * header and above the legend it dims. Living in the panel (not a map overlay)
1758
- * keeps it clear of the top-center rung pills and top-left a11y chips, and it
1759
- * rides the bottom sheet on narrow layouts. Skipped when there's <2 bands.
1760
- */
1799
+ /** Build the compact price selector in the panel header. Choosing a band both
1800
+ * filters availability and smoothly frames the matching seats on the map. */
1761
1801
  buildPriceFilter() {
1762
1802
  if (!this.els.prices || !this.els.pricesSec) return;
1763
1803
  const bands = this.priceBands();
1764
1804
  if (bands.length < 2) return;
1765
- const wrap = document.createElement("div");
1766
- wrap.className = "sl-pricef";
1767
- wrap.setAttribute("role", "group");
1768
- wrap.setAttribute("aria-label", "Filter seats by price");
1769
- const mk = (band, label, on) => `<button type="button" class="sl-chip-f${on ? " on" : ""}" data-band="${band}">${label}</button>`;
1770
- wrap.innerHTML = mk("all", "All prices", true) + bands.map((bd) => mk(bd.id, bd.label, false)).join("");
1771
- this.els.pricesSec.after(wrap);
1772
- this.priceFilterEl = wrap;
1773
- wrap.querySelectorAll("button").forEach((btn) => {
1774
- btn.addEventListener("click", () => {
1775
- wrap.querySelectorAll("button").forEach((b) => b.classList.toggle("on", b === btn));
1776
- const id = btn.dataset.band;
1777
- if (id === "all") {
1778
- this.priceBandKeys = null;
1779
- this.controller.setCategoryFilter(null);
1780
- } else {
1781
- const bd = bands.find((x) => x.id === id);
1782
- this.priceBandKeys = bd ? new Set(bd.keys) : null;
1783
- this.controller.setCategoryFilter(bd ? bd.keys : null);
1784
- }
1785
- this.syncPrices();
1786
- if (this.lastSection) this.showSectionCard(this.lastSection);
1787
- });
1805
+ const select = document.createElement("select");
1806
+ select.className = "sl-price-select";
1807
+ select.setAttribute("aria-label", "Filter and focus seats by price");
1808
+ select.innerHTML = `<option value="all">All prices</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
1809
+ this.els.pricesSec.appendChild(select);
1810
+ this.priceFilterEl = select;
1811
+ select.addEventListener("change", () => {
1812
+ const band = bands.find((candidate) => candidate.id === select.value);
1813
+ const keys = band?.keys ?? null;
1814
+ this.priceBandKeys = keys ? new Set(keys) : null;
1815
+ this.controller.setCategoryFilter(keys);
1816
+ this.controller.focusCategoryFilter(keys);
1817
+ this.syncPrices();
1818
+ if (this.lastSection) this.showSectionCard(this.lastSection);
1788
1819
  });
1789
1820
  }
1790
1821
  // ---- arena / multi-floor chrome -------------------------------------------
@@ -2184,7 +2215,7 @@ var SeatPicker = class _SeatPicker {
2184
2215
  const price = c.tiers?.length ? c.tiers[0].price : c.price;
2185
2216
  const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
2186
2217
  return `<div class="sl-price-row${dim ? " sl-dim" : ""}" data-cat="${c.key}"><span class="sl-dot" style="background:${c.color}"></span><span class="sl-price-label">${c.label}</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
2187
- }).join("") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon">H</i>Held by another buyer</span><span class="sl-status-item"><i class="sl-status-icon sold">\xD7</i>Sold</span></div>`;
2218
+ }).join("") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></i>Temporarily held</span><span class="sl-status-item"><i class="sl-status-icon sold" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M7 17L17 7"/></svg></i>Sold</span></div>`;
2188
2219
  this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
2189
2220
  row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
2190
2221
  row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
@@ -2213,14 +2244,21 @@ var SeatPicker = class _SeatPicker {
2213
2244
  } else if (!seats.length && !heldItems.length) {
2214
2245
  parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
2215
2246
  }
2247
+ if (!this.hold) {
2248
+ const cats = this.controller.doc?.categories ?? [];
2249
+ parts.push(
2250
+ `<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>` + (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>`) + `<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>`
2251
+ );
2252
+ }
2216
2253
  for (const item of heldItems) {
2217
2254
  const itemKey = `held:${item.label}`;
2218
2255
  nextTrayKeys.add(itemKey);
2219
2256
  const cat = this.controller.doc?.categories.find((c) => c.key === item.categoryKey);
2220
2257
  const tierName = item.tierId ? cat?.tiers?.find((ti) => ti.id === item.tierId)?.name : void 0;
2221
2258
  const canView2 = this.seatViewEnabled() && item.objectType !== "ga" && !!this.controller.seatByLabel(item.label);
2259
+ const viewBtn = canView2 ? `<button type="button" class="view" data-view-label="${item.label}" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: item.label })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "";
2222
2260
  parts.push(
2223
- `<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"><b>${item.label}</b><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span><span class="sl-chip-state" aria-label="Held by you" title="Held by you">Held</span><span class="amt">${this.money(item.unitPrice * (item.quantity ?? 1))}</span>` + (canView2 ? `<button type="button" class="view" data-view-label="${item.label}" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: item.label })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "") + `<button type="button" class="rm" aria-label="Remove held ticket ${item.label}"><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>`
2261
+ `<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"><span class="sl-ticket-state held" aria-label="Held for you" title="Held for you"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><div class="sl-chip-main"><b>${item.label}</b><div class="sl-chip-sub"><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>${viewBtn}</div></div><span class="amt">${this.money(item.unitPrice * (item.quantity ?? 1))}</span><button type="button" class="rm" aria-label="Remove held ticket ${item.label}"><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>`
2224
2262
  );
2225
2263
  }
2226
2264
  const heldLabels = new Set(heldItems.map((item) => item.label));
@@ -2232,7 +2270,7 @@ var SeatPicker = class _SeatPicker {
2232
2270
  const tierSelect = s.tiers && s.tiers.length ? `<select class="tier" data-tier="${s.id}" aria-label="${(0, import_core2.t)("picker.ticketTierFor", { label: s.label })}">` + s.tiers.map((ti) => `<option value="${ti.id}"${ti.id === s.tierId ? " selected" : ""}>${ti.name} \xB7 ${this.money(ti.price)}</option>`).join("") + `</select>` : "";
2233
2271
  const viewBtn = canView ? `<button type="button" class="view" data-view-label="${s.label}" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: s.label })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "";
2234
2272
  parts.push(
2235
- `<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}"><b>${s.label}</b><span class="cat">${cat?.label ?? s.categoryKey}</span>` + tierSelect + `<span class="sl-chip-state sl-pending">Selected</span><span class="amt">${this.money(s.price)}</span>` + viewBtn + `<button type="button" class="rm" aria-label="Remove ${s.label}"><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>`
2273
+ `<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><div class="sl-chip-main"><b>${s.label}</b><div class="sl-chip-sub"><span class="cat">${cat?.label ?? s.categoryKey}</span>${tierSelect}${viewBtn}</div></div><span class="amt">${this.money(s.price)}</span><button type="button" class="rm" aria-label="Remove ${s.label}"><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>`
2236
2274
  );
2237
2275
  }
2238
2276
  for (const area of gaAreas) {
@@ -2241,12 +2279,6 @@ var SeatPicker = class _SeatPicker {
2241
2279
  `<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.label}</div><div class="sl-ga-sub">${this.money(area.price)} \xB7 ${area.available} left</div></div><div class="sl-ga-qty"><button type="button" data-d="-1" aria-label="Fewer">\u2212</button><span>${qty}</span><button type="button" data-d="1" aria-label="More">+</button></div></div>`
2242
2280
  );
2243
2281
  }
2244
- if (!this.hold) {
2245
- const cats = this.controller.doc?.categories ?? [];
2246
- parts.push(
2247
- `<div class="sl-ba">` + (cats.length > 1 ? `<select aria-label="Category" data-ba-cat><option value="">Any tier</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.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\u2026` : "Best available") + `</button></div>`
2248
- );
2249
- }
2250
2282
  this.els.tray.innerHTML = parts.join("");
2251
2283
  this.lastTrayKeys = nextTrayKeys;
2252
2284
  this.els.tray.querySelectorAll("[data-ba]").forEach((btn) => {
@@ -2308,6 +2340,11 @@ var SeatPicker = class _SeatPicker {
2308
2340
  const previousTotal = this.lastTrayTotal;
2309
2341
  this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
2310
2342
  this.els.total.textContent = count ? this.money(total) : "";
2343
+ this.root?.setAttribute("data-has-selection", String(count > 0));
2344
+ this.els.foot?.classList.toggle("empty", count === 0);
2345
+ if (this.els.seatSummary) {
2346
+ this.els.seatSummary.textContent = count ? `${count} selected` : "";
2347
+ }
2311
2348
  this.syncCta(count, pendingCount);
2312
2349
  if (this.hold) {
2313
2350
  const securedCount = heldCount || this.hold.seats?.length || 0;
@@ -2326,7 +2363,7 @@ var SeatPicker = class _SeatPicker {
2326
2363
  this.els.peek.innerHTML = `<span>${count} ${count === 1 ? "ticket" : "tickets"} \xB7 ${this.money(total)}</span><span class="go">${this.hold ? pendingCount ? "Secure more" : "Continue" : "Review"}</span>`;
2327
2364
  } else {
2328
2365
  const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
2329
- this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<span class="sub">\xB7 Best available</span>`;
2366
+ this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<span class="go">\u2726 Best seats</span>`;
2330
2367
  }
2331
2368
  }
2332
2369
  this.lastTrayCount = count;