@seatlayer/js 0.50.1 → 0.51.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
@@ -2659,6 +2659,10 @@ var DEFAULT_MAX_SELECTION2 = 10;
2659
2659
  var EXTEND_PROMPT_MS = 6e4;
2660
2660
  var MAX_3D_SEATS_DEFAULT = 6e4;
2661
2661
  var MAX_3D_SECTION_PILLS = 12;
2662
+ function tfStatic(key, fallback) {
2663
+ const v = (0, import_core2.t)(key);
2664
+ return v === key ? fallback : v;
2665
+ }
2662
2666
  function pointInPolygon(x, y, poly) {
2663
2667
  let inside = false;
2664
2668
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
@@ -2833,11 +2837,30 @@ var CSS2 = (
2833
2837
  .sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
2834
2838
  .sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
2835
2839
  flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
2836
- .sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
2840
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(380px,64%)}
2841
+ /* With the price list expanded the sheet takes the with-selection height so
2842
+ the tray (cart + best-seats) is never crushed under the fixed-size list. */
2843
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-side{height:min(480px,74%)}
2837
2844
  .sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:76px;overflow:hidden}
2838
2845
  .sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
2839
- .sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
2846
+ .sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:96px;overflow-y:auto;overscroll-behavior:contain}
2847
+ .sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-tray{min-height:48px}
2848
+ .sl-picker[data-layout="narrow"] .sl-prices{max-height:150px;overflow-y:auto;overscroll-behavior:contain}
2840
2849
  .sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
2850
+ /* NARROW FOOT SAYS EVERYTHING ONCE (owner report: with a hold the footer ate
2851
+ ~190px and the ticket list got a sliver). The sheet head already carries
2852
+ "N tickets \xB7 $total" and the header pill already ticks the clock \u2014 so the
2853
+ total row hides, the secured note collapses to one compact line, and the
2854
+ badge tucks in. The tray gets the pixels; a thin scrollbar shows there is
2855
+ more to scroll. */
2856
+ .sl-picker[data-layout="narrow"] .sl-total{display:none}
2857
+ .sl-picker[data-layout="narrow"] .sl-foot{padding:8px 12px 8px}
2858
+ .sl-picker[data-layout="narrow"] .sl-hold-note{margin-bottom:6px;padding:5px 9px}
2859
+ .sl-picker[data-layout="narrow"] .sl-hold-note svg{display:none}
2860
+ .sl-picker[data-layout="narrow"] .sl-hold-note b{display:inline;margin-right:6px;font-size:11px}
2861
+ .sl-picker[data-layout="narrow"] .sl-hold-copy{display:inline;white-space:normal;font-size:10.5px}
2862
+ .sl-picker[data-layout="narrow"] .sl-powered{margin-top:4px;padding:2px 8px;font-size:11px}
2863
+ .sl-picker[data-layout="narrow"] .sl-tray{scrollbar-width:thin}
2841
2864
  .sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
2842
2865
  .sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
2843
2866
  .sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
@@ -2845,6 +2868,7 @@ var CSS2 = (
2845
2868
  .sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
2846
2869
  .sl-picker[data-layout="narrow"] .sl-filters{order:4}
2847
2870
  .sl-picker[data-layout="narrow"] .sl-prices-sec{order:5}
2871
+ .sl-picker[data-layout="narrow"] .sl-prices-hint{order:6}
2848
2872
  .sl-picker[data-layout="narrow"] .sl-pricef{order:6}
2849
2873
  .sl-picker[data-layout="narrow"] .sl-prices{order:7}
2850
2874
  .sl-picker[data-layout="narrow"] .sl-foot{order:8}
@@ -2852,6 +2876,7 @@ var CSS2 = (
2852
2876
  .sl-picker[data-layout="narrow"] .sl-filtersec,
2853
2877
  .sl-picker[data-layout="narrow"] .sl-filters,
2854
2878
  .sl-picker[data-layout="narrow"] .sl-prices-sec,
2879
+ .sl-picker[data-layout="narrow"] .sl-prices-hint,
2855
2880
  .sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
2856
2881
  .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
2857
2882
  .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
@@ -2869,7 +2894,13 @@ var CSS2 = (
2869
2894
  head is the tap/swipe toggle target (min 44px), so it reads as one control. */
2870
2895
  .sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
2871
2896
  cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
2872
- .sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:64px;padding:4px 10px 6px}
2897
+ .sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:52px;padding:3px 10px 5px}
2898
+ /* OPEN sheet: the head is context, not controls \u2014 the pill it carries at peek
2899
+ ("Best seats"/"Continue") duplicates the card and CTA right below it, and
2900
+ every saved pixel is tray room (owner call 2026-08-14). */
2901
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-head{min-height:42px}
2902
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-bar{min-height:32px}
2903
+ .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-peek .go{display:none}
2873
2904
  .sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-sheet-head{height:100%}
2874
2905
  .sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:1px auto 5px}
2875
2906
  .sl-sheet-bar{display:flex;align-items:center;gap:8px;min-height:44px}
@@ -2910,9 +2941,19 @@ var CSS2 = (
2910
2941
  sheet brings it back, exactly like the filters row above. The tray hint comes
2911
2942
  back with it: the first-time buyer on a phone needs the instruction more than
2912
2943
  the desktop buyer, not less. */
2913
- .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices-sec{display:flex!important}
2914
- .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices{display:flex!important}
2944
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-sec{display:flex!important}
2945
+ /* MOBILE SPACE BELONGS TO THE CART (owner call 2026-08-14): the price list is
2946
+ one tap away behind the "Ticket prices" row, not a permanent tenant. Zone
2947
+ flags and section taps already answer "what does it cost" in context. */
2948
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-hint{display:block!important}
2949
+ .sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices{display:flex!important}
2915
2950
  .sl-picker[data-layout="narrow"][data-sheet="open"] .sl-tray-hint{display:block!important}
2951
+ /* The row reads as the toggle it is on narrow: chevron + pointer. */
2952
+ .sl-picker[data-layout="narrow"] .sl-prices-sec{cursor:pointer}
2953
+ .sl-picker[data-layout="narrow"] .sl-prices-sec::after{content:'';width:8px;height:8px;flex:none;margin-left:auto;
2954
+ border-right:2px solid var(--sl-muted);border-bottom:2px solid var(--sl-muted);
2955
+ transform:rotate(45deg);transition:transform .2s cubic-bezier(.2,.8,.2,1)}
2956
+ .sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-prices-sec::after{transform:rotate(-135deg)}
2916
2957
  .sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
2917
2958
  color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
2918
2959
  .sl-cbbtn:hover{border-color:var(--sl-muted)}
@@ -2931,7 +2972,13 @@ var CSS2 = (
2931
2972
  .sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
2932
2973
  .sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
2933
2974
  .sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
2934
- background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
2975
+ background-color:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
2976
+ /* One sentence that teaches the whole colour code \u2014 a first-time buyer sees
2977
+ "Head Tables \xB7 $165" and coloured dots with nothing connecting them. Gone
2978
+ the moment they have seats (they've learned it) and while sales are closed. */
2979
+ .sl-prices-hint{padding:0 14px 7px;font-size:11px;line-height:1.45;color:var(--sl-muted)}
2980
+ .sl-picker[data-has-selection="true"] .sl-prices-hint,
2981
+ .sl-picker[data-sales-closed="true"] .sl-prices-hint{display:none}
2935
2982
  .sl-prices{display:flex;flex-direction:column;padding:4px 14px 8px;border-bottom:1px solid var(--sl-line)}
2936
2983
  .sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
2937
2984
  .sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px;
@@ -3091,11 +3138,11 @@ var CSS2 = (
3091
3138
  color:var(--sl-text);font-size:17px;font-weight:700;display:flex;align-items:center;justify-content:center;transition:border-color .15s}
3092
3139
  .sl-zoom button:hover{border-color:var(--sl-muted)}
3093
3140
  .sl-zoom svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
3094
- /* Full-screen promotion on phones: the capability existed (native + iOS overlay
3095
- + framed-host paths) but hid as the 4th unlabelled glyph in a corner stack.
3096
- On narrow layouts dockLayoutChrome moves the SAME button into the top-right
3097
- region beside Map|3D and this class dresses it as a labelled pill \u2014 the one
3098
- accelerator with the highest payoff on a small screen gets words. */
3141
+ /* Full-screen promotion: the capability existed (native + iOS overlay +
3142
+ framed-host paths) but hid as the 4th unlabelled glyph in a corner stack \u2014
3143
+ even the owner could not pick it out. dockLayoutChrome docks the SAME
3144
+ button into the top-right region beside Map|3D on EVERY layout and this
3145
+ class dresses it as a labelled pill. */
3099
3146
  .sl-zfs-lbl{display:none}
3100
3147
  .sl-fs-pill.sl-fs-pill{display:inline-flex;align-items:center;justify-content:center;width:auto;min-height:40px;height:auto;
3101
3148
  padding:0 13px;gap:6px;border-radius:999px;font-size:11px;font-weight:800;
@@ -3103,6 +3150,16 @@ var CSS2 = (
3103
3150
  .sl-fs-pill.sl-fs-pill:hover{border-color:var(--sl-muted)}
3104
3151
  .sl-fs-pill svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
3105
3152
  .sl-fs-pill .sl-zfs-lbl{display:inline;letter-spacing:.02em;white-space:nowrap}
3153
+ /* In the header, icons carry it (owner call): square Full screen glyph, and
3154
+ on narrow a price-tag toggle that opens the sheet with the list expanded. */
3155
+ .sl-head .sl-fs-pill{min-height:34px;min-width:34px;width:34px;padding:0;flex:none}
3156
+ .sl-head .sl-fs-pill .sl-zfs-lbl{display:none}
3157
+ .sl-prices-btn{display:none;align-items:center;justify-content:center;width:34px;min-height:34px;flex:none;
3158
+ border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
3159
+ .sl-prices-btn:hover{border-color:var(--sl-muted)}
3160
+ .sl-prices-btn svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
3161
+ .sl-prices-btn[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
3162
+ .sl-picker[data-layout="narrow"] .sl-prices-btn{display:inline-flex}
3106
3163
 
3107
3164
  /* toast + boot states (toast flows in the bottom-center region) */
3108
3165
  .sl-toast{transform:translateY(6px) scale(.98);max-width:100%;
@@ -3230,8 +3287,21 @@ var CSS2 = (
3230
3287
  .sl-confirm-add svg{width:16px;height:16px;stroke:currentColor;stroke-width:2.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
3231
3288
  .sl-confirm-cancel{background:color-mix(in srgb,var(--sl-line) 44%,transparent)!important;border:1px solid var(--sl-line)!important;color:var(--sl-muted)!important}
3232
3289
  .sl-confirm-cancel:hover{color:var(--sl-text)}
3233
- .sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(342px,calc(100% - 24px));
3290
+ .sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(310px,calc(100% - 24px));
3234
3291
  transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
3292
+ /* Phone-size confirm card (owner call 2026-08-14): same content \u2014 identity,
3293
+ price band, view photo, 3D link \u2014 on a tighter grid. The values stay large
3294
+ enough to double-check against the map; buttons keep tap-size. */
3295
+ .sl-picker[data-layout="narrow"] .sl-confirm-field{padding:9px 9px 8px}
3296
+ .sl-picker[data-layout="narrow"] .sl-confirm-value{font-size:15px}
3297
+ .sl-picker[data-layout="narrow"] .sl-confirm-cat{padding:8px 10px}
3298
+ .sl-picker[data-layout="narrow"] .sl-confirm-cat-name{font-size:12.5px}
3299
+ .sl-picker[data-layout="narrow"] .sl-confirm-price{font-size:15px}
3300
+ .sl-picker[data-layout="narrow"] .sl-confirm-body{padding:9px 10px 10px}
3301
+ .sl-picker[data-layout="narrow"] .sl-confirm-thumbwrap{height:62px;margin-bottom:6px}
3302
+ .sl-picker[data-layout="narrow"] .sl-confirm-3d{margin-top:7px;padding:6px}
3303
+ .sl-picker[data-layout="narrow"] .sl-confirm-row{margin-top:8px}
3304
+ .sl-picker[data-layout="narrow"] .sl-confirm-row button{min-height:42px}
3235
3305
 
3236
3306
  /* Atomic whole/variable-table chooser. Lives inside the widget so the same
3237
3307
  accessible dialog works in inline, modal, desktop and 390px mobile hosts. */
@@ -3307,8 +3377,11 @@ var CSS2 = (
3307
3377
  .sl-ba-premium.on{color:var(--sl-premium-ink);background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
3308
3378
  box-shadow:0 6px 16px color-mix(in srgb,var(--sl-premium) 26%,transparent)}
3309
3379
  .sl-ba-premium.on .star{color:#5a4410}
3310
- .sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
3311
- font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
3380
+ .sl-ba select,.sl-price-select{appearance:none;-webkit-appearance:none;
3381
+ background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23949ca9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
3382
+ background-repeat:no-repeat;background-position:right 10px center}
3383
+ .sl-ba select{background-color:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
3384
+ font:inherit;font-size:11px;padding:7px 26px 7px 8px;min-width:0;width:100%;max-width:none}
3312
3385
  .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)}
3313
3386
  .sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
3314
3387
  font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
@@ -3338,9 +3411,23 @@ var CSS2 = (
3338
3411
  .sl-picker[data-layout="narrow"] .sl-ba-copy{display:none}
3339
3412
  .sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
3340
3413
  .sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
3341
- .sl-picker[data-layout="narrow"] .sl-ba select{min-height:40px}
3414
+ .sl-picker[data-layout="narrow"] .sl-ba{grid-template-columns:auto minmax(0,1fr)}
3415
+ .sl-picker[data-layout="narrow"] .sl-ba select{grid-column:1/-1;min-height:38px}
3342
3416
  .sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
3343
- .sl-picker[data-layout="narrow"] .sl-ba-go{min-height:40px}
3417
+ /* Quantity and Go share ONE row: a full-width row holding only "\u2212 2 +" was
3418
+ dead space, and the Find button still gets the lion's share. */
3419
+ .sl-picker[data-layout="narrow"] .sl-ba-qty{grid-column:1}
3420
+ .sl-picker[data-layout="narrow"] .sl-ba-go{grid-column:2;min-height:40px}
3421
+ /* Re-entry to best-available once the cart has seats (the card yields its
3422
+ space to chips, but the accelerator must stay reachable) + the quiet way
3423
+ back out of a reopened card. */
3424
+ .sl-ba-reopen{align-self:flex-start;display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:5px 12px;margin-top:2px;
3425
+ border:1px dashed color-mix(in srgb,var(--sl-accent) 40%,var(--sl-line));border-radius:999px;
3426
+ color:var(--sl-accent);font-size:11.5px;font-weight:750;transition:border-color .15s,background .15s}
3427
+ .sl-ba-reopen:hover,.sl-ba-reopen:focus-visible{background:color-mix(in srgb,var(--sl-accent) 8%,transparent)}
3428
+ .sl-ba-back{grid-column:1/-1;justify-self:start;min-height:30px;padding:3px 0;color:var(--sl-muted);
3429
+ font-size:11px;font-weight:700;text-decoration:underline;text-underline-offset:3px;transition:color .15s}
3430
+ .sl-ba-back:hover,.sl-ba-back:focus-visible{color:var(--sl-text)}
3344
3431
 
3345
3432
  /* Sales-closed is a designed state, not a set of disables: one statement in
3346
3433
  the tray where the buying flow was, carrying the event's own date so the
@@ -3359,9 +3446,9 @@ var CSS2 = (
3359
3446
  (the cart chip's remove/view rail) instead get real height on narrow. */
3360
3447
  @media (pointer:coarse){
3361
3448
  .sl-zoom button,.sl-cbbtn,.sl-close,.sl-ga-qty button,.sl-ba-qty button,.sl-price-more,
3362
- .sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle{position:relative}
3449
+ .sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle,.sl-ba-reopen{position:relative}
3363
3450
  .sl-zoom button::after,.sl-cbbtn::after,.sl-close::after,.sl-ga-qty button::after,.sl-ba-qty button::after,
3364
- .sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after{
3451
+ .sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after,.sl-ba-reopen::after{
3365
3452
  content:'';position:absolute;top:50%;left:50%;width:max(100%,44px);height:max(100%,44px);
3366
3453
  transform:translate(-50%,-50%)}
3367
3454
  /* The chip rail stacks remove over view in ~53px \u2014 pseudo-targets would
@@ -3464,12 +3551,6 @@ var CSS2 = (
3464
3551
  border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
3465
3552
  border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
3466
3553
  .sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
3467
- .sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
3468
- width:min(720px,100%);overflow:visible}
3469
- .sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
3470
- .sl-view3d-locator select{width:100%;min-width:0}
3471
- .sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:100%;overflow-x:auto}
3472
- .sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
3473
3554
  /* On phones the library owns the bottom edge for seat/panorama/overview actions.
3474
3555
  Keep venue navigation in a separate top rail so those control families never
3475
3556
  stack over one another. */
@@ -3479,9 +3560,6 @@ var CSS2 = (
3479
3560
  .sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open{left:12px;top:68px;max-width:calc(100% - 24px);padding:8px;
3480
3561
  border:1px solid rgba(150,165,205,.35);border-radius:14px;background:rgba(8,12,22,.9);backdrop-filter:blur(10px)}
3481
3562
  .sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open>div{display:flex}
3482
- .sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator{display:grid;grid-template-columns:1fr 1fr;width:100%;overflow:visible}
3483
- .sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator select{width:100%;min-width:0;flex:auto}
3484
- .sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator button{width:100%}
3485
3563
  /* Seat-eye is a decision state, not another venue-navigation state. Once the
3486
3564
  buyer arrives, clear the floor/area/locator rails and the module's duplicate
3487
3565
  Overview action. The picker-owned Back button becomes the one predictable
@@ -3907,6 +3985,15 @@ var SeatPicker = class _SeatPicker {
3907
3985
  this.baZone = "";
3908
3986
  /** "★ Best seats" premium quick-pick toggle — biases best-available to premium seats. */
3909
3987
  this.baPremium = false;
3988
+ /**
3989
+ * The buyer re-opened the best-seats form with seats already in the cart
3990
+ * ("I picked one, actually find us three together"). The card normally
3991
+ * yields this space to the ticket chips the moment anything is selected —
3992
+ * which removed the accelerator at exactly the moment a one-handed phone
3993
+ * buyer wants it. Cleared when the cart empties, a hold lands, or the
3994
+ * buyer backs out.
3995
+ */
3996
+ this.baReopen = false;
3910
3997
  this.bestAvailableConfirm = false;
3911
3998
  this.releasingHold = false;
3912
3999
  /** Event sales window is closed (read-only load state / live close). */
@@ -3979,9 +4066,12 @@ var SeatPicker = class _SeatPicker {
3979
4066
  this.fsEscHandler = null;
3980
4067
  /** Host-level event chrome owns the duplicate identity outside full screen. */
3981
4068
  this.eventDetailsHidden = false;
4069
+ /** True while the hold note reads "N more selected" — the tick must not overwrite it. */
4070
+ this.holdCopyPending = false;
3982
4071
  /** Wide-layout ticket panel collapsed (map owns the full width). */
3983
4072
  this.sideCollapsed = false;
3984
4073
  this.sideToggleEl = null;
4074
+ this.pricesBtnEl = null;
3985
4075
  /** True once we've asked the host page to pin us fullscreen (framed, no native). */
3986
4076
  this.framedFs = false;
3987
4077
  /** Last height (px) posted to a host frame; dedupes redundant reports. */
@@ -4094,7 +4184,7 @@ var SeatPicker = class _SeatPicker {
4094
4184
  if (this.tableDialog?.physicalSeatIds.includes(seat.id)) this.dismissTableDialog();
4095
4185
  },
4096
4186
  onSelectionLimit: () => {
4097
- this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
4187
+ this.toast((0, import_core2.t)("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
4098
4188
  },
4099
4189
  onViewChange: () => {
4100
4190
  this.reanchorConfirm();
@@ -4210,8 +4300,8 @@ var SeatPicker = class _SeatPicker {
4210
4300
  return `<span class="sl-cx-mark" role="img" aria-label="${title}" title="${title}">\u25D0</span>`;
4211
4301
  }
4212
4302
  wheelchairProvisionLabel(type) {
4213
- if (type === "no-seat") return "Empty wheelchair space";
4214
- if (type === "seat-present") return "Accessible physical seat";
4303
+ if (type === "no-seat") return this.tf("picker.emptyWheelchairSpace", "Empty wheelchair space");
4304
+ if (type === "seat-present") return this.tf("picker.accessiblePhysicalSeat", "Accessible physical seat");
4215
4305
  return "";
4216
4306
  }
4217
4307
  wheelchairConfirmHtml(type) {
@@ -4292,9 +4382,9 @@ var SeatPicker = class _SeatPicker {
4292
4382
  const hideEventDetails = this.eventDetailsHidden && !active;
4293
4383
  this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
4294
4384
  this.els.zfs?.setAttribute("aria-pressed", String(active));
4295
- this.els.zfs?.setAttribute("title", active ? "Exit full screen" : "Full screen");
4385
+ this.els.zfs?.setAttribute("title", active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen"));
4296
4386
  const zfsLabel = this.els.zfs?.querySelector(".sl-zfs-lbl");
4297
- if (zfsLabel) zfsLabel.textContent = active ? "Exit full screen" : "Full screen";
4387
+ if (zfsLabel) zfsLabel.textContent = active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen");
4298
4388
  this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
4299
4389
  }
4300
4390
  /**
@@ -4359,7 +4449,7 @@ var SeatPicker = class _SeatPicker {
4359
4449
  frame.className = "sl-modal-frame";
4360
4450
  frame.setAttribute("role", "dialog");
4361
4451
  frame.setAttribute("aria-modal", "true");
4362
- frame.setAttribute("aria-label", "Seat selection");
4452
+ frame.setAttribute("aria-label", tfStatic("picker.seatSelection", "Seat selection"));
4363
4453
  frame.tabIndex = -1;
4364
4454
  scrim.appendChild(frame);
4365
4455
  document.body.appendChild(scrim);
@@ -4519,7 +4609,7 @@ var SeatPicker = class _SeatPicker {
4519
4609
  <svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
4520
4610
  <span data-ref="closedPillText"></span>
4521
4611
  </span>
4522
- <button type="button" class="sl-close" data-ref="close" aria-label="Close">
4612
+ <button type="button" class="sl-close" data-ref="close" aria-label="${this.tf("picker.close", "Close")}">
4523
4613
  <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>
4524
4614
  </button>
4525
4615
  </div>
@@ -4527,17 +4617,17 @@ var SeatPicker = class _SeatPicker {
4527
4617
  <div class="sl-map">
4528
4618
  <div class="sl-map-host" data-ref="map"></div>
4529
4619
  <div class="sl-zoom" data-ref="zoom">
4530
- <button type="button" aria-label="Zoom in" title="Zoom in" data-ref="zin">+</button>
4531
- <button type="button" aria-label="Zoom out" title="Zoom out" data-ref="zout">\u2212</button>
4532
- <button type="button" aria-label="Fit to screen" title="Fit to screen" data-ref="zfit">
4620
+ <button type="button" aria-label="${this.tf("picker.zoomIn", "Zoom in")}" title="${this.tf("picker.zoomIn", "Zoom in")}" data-ref="zin">+</button>
4621
+ <button type="button" aria-label="${this.tf("picker.zoomOut", "Zoom out")}" title="${this.tf("picker.zoomOut", "Zoom out")}" data-ref="zout">\u2212</button>
4622
+ <button type="button" aria-label="${this.tf("picker.fitToScreen", "Fit to screen")}" title="${this.tf("picker.fitToScreen", "Fit to screen")}" data-ref="zfit">
4533
4623
  <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>
4534
4624
  </button>
4535
- <button type="button" aria-label="Full screen" title="Full screen" aria-pressed="false" data-ref="zfs">
4625
+ <button type="button" aria-label="${this.tf("picker.fullScreen", "Full screen")}" title="${this.tf("picker.fullScreen", "Full screen")}" aria-pressed="false" data-ref="zfs">
4536
4626
  <svg viewBox="0 0 24 24"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
4537
- <span class="sl-zfs-lbl" aria-hidden="true">Full screen</span>
4627
+ <span class="sl-zfs-lbl" aria-hidden="true">${this.tf("picker.fullScreen", "Full screen")}</span>
4538
4628
  </button>
4539
4629
  </div>
4540
- <div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>Loading seat map\u2026</div>
4630
+ <div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>${this.tf("picker.loadingSeatMap", "Loading seat map\u2026")}</div>
4541
4631
  <div class="sl-toast" data-ref="toast" role="status" aria-live="polite"></div>
4542
4632
  </div>
4543
4633
  <div class="sl-side" data-ref="side">
@@ -4545,24 +4635,25 @@ var SeatPicker = class _SeatPicker {
4545
4635
  <div class="sl-sheet-grab"></div>
4546
4636
  <div class="sl-sheet-bar">
4547
4637
  <div class="sl-sheet-peek" data-ref="peek"></div>
4548
- <button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
4638
+ <button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="${this.tf("picker.openTicketPanel", "Open ticket panel")}" aria-expanded="false">
4549
4639
  <svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
4550
4640
  </button>
4551
4641
  </div>
4552
4642
  </div>
4553
- <div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
4643
+ <div class="sl-sec sl-filtersec" data-ref="filtersSec">${this.tf("picker.filters", "Filters")}</div>
4554
4644
  <div class="sl-filters" data-ref="filters"></div>
4555
4645
  <div class="sl-offer" data-ref="offer" role="status" aria-live="polite"></div>
4556
- <div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
4646
+ <div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>${this.tf("picker.ticketPrices", "Ticket prices")}</span></div>
4647
+ <div class="sl-prices-hint" data-ref="pricesHint"></div>
4557
4648
  <div class="sl-prices" data-ref="prices"></div>
4558
- <div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText">Live availability \u2014 seats update in real time</span></div>
4559
- <div class="sl-sec sl-seats-sec"><span>Your seats</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
4649
+ <div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText">${this.tf("picker.liveAvailability", "Live availability \u2014 seats update in real time")}</span></div>
4650
+ <div class="sl-sec sl-seats-sec"><span>${this.tf("picker.yourSeats", "Your seats")}</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
4560
4651
  <div class="sl-tray" data-ref="tray"></div>
4561
4652
  <div class="sl-foot" data-ref="foot">
4562
4653
  <div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
4563
4654
  <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
4564
- <span><b data-ref="holdTitle">Seats secured</b><span class="sl-hold-copy" data-ref="holdCopy">Checkout timer is running.</span></span>
4565
- <button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats">Change</button>
4655
+ <span><b data-ref="holdTitle">${this.tf("picker.seatsSecured", "Seats secured")}</b><span class="sl-hold-copy" data-ref="holdCopy">${this.tf("picker.notChargedYet", "You won\u2019t be charged yet.")}</span></span>
4656
+ <button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="${this.tf("picker.releaseHeldTickets", "Release held tickets and choose different seats")}">${this.tf("picker.change", "Change")}</button>
4566
4657
  </div>
4567
4658
  <div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
4568
4659
  <button type="button" class="sl-cta" data-ref="cta" disabled></button>
@@ -4613,7 +4704,7 @@ var SeatPicker = class _SeatPicker {
4613
4704
  const setSheet = (open) => {
4614
4705
  root.dataset.sheet = open ? "open" : "peek";
4615
4706
  toggle?.setAttribute("aria-expanded", String(open));
4616
- toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
4707
+ toggle?.setAttribute("aria-label", open ? this.tf("picker.collapseTicketPanel", "Collapse ticket panel") : this.tf("picker.openTicketPanel", "Open ticket panel"));
4617
4708
  };
4618
4709
  setSheet(root.dataset.sheet === "open");
4619
4710
  toggle?.addEventListener("click", (e) => {
@@ -4659,6 +4750,39 @@ var SeatPicker = class _SeatPicker {
4659
4750
  head.releasePointerCapture?.(e.pointerId);
4660
4751
  });
4661
4752
  }
4753
+ const togglePrices = () => {
4754
+ if (this.root?.dataset.layout !== "narrow") return;
4755
+ const open = this.root.getAttribute("data-prices-open") === "true";
4756
+ this.root.setAttribute("data-prices-open", String(!open));
4757
+ this.els.pricesSec?.setAttribute("aria-expanded", String(!open));
4758
+ this.pricesBtnEl?.setAttribute("aria-pressed", String(!open));
4759
+ if (!open && this.root.dataset.sheet !== "open") {
4760
+ this.root.dataset.sheet = "open";
4761
+ const sheetToggle = this.els.sheetToggle;
4762
+ sheetToggle?.setAttribute("aria-expanded", "true");
4763
+ sheetToggle?.setAttribute("aria-label", "Collapse ticket panel");
4764
+ }
4765
+ };
4766
+ this.els.pricesSec?.addEventListener("click", (e) => {
4767
+ if (e.target.closest("select,button")) return;
4768
+ togglePrices();
4769
+ });
4770
+ this.els.pricesSec?.addEventListener("keydown", (e) => {
4771
+ if (e.key !== "Enter" && e.key !== " ") return;
4772
+ if (e.target.closest("select,button")) return;
4773
+ e.preventDefault();
4774
+ togglePrices();
4775
+ });
4776
+ const pricesBtn = document.createElement("button");
4777
+ pricesBtn.type = "button";
4778
+ pricesBtn.className = "sl-prices-btn";
4779
+ pricesBtn.setAttribute("aria-label", this.tf("picker.ticketPrices", "Ticket prices"));
4780
+ pricesBtn.setAttribute("title", this.tf("picker.ticketPrices", "Ticket prices"));
4781
+ pricesBtn.setAttribute("aria-pressed", "false");
4782
+ pricesBtn.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0L3 13V5a2 2 0 0 1 2-2h8l7.6 7.6a2 2 0 0 1 0 2.8z"/><circle cx="7.5" cy="7.5" r="1.3"/></svg>';
4783
+ pricesBtn.addEventListener("click", () => togglePrices());
4784
+ this.els.headInfo?.parentElement?.insertBefore(pricesBtn, this.els.hold ?? null);
4785
+ this.pricesBtnEl = pricesBtn;
4662
4786
  this.tipEl = document.createElement("div");
4663
4787
  this.tipEl.setAttribute("role", "tooltip");
4664
4788
  this.tipEl.className = "sl-tip";
@@ -4676,7 +4800,7 @@ var SeatPicker = class _SeatPicker {
4676
4800
  const info = await this.controller.render(canvasHost);
4677
4801
  if (this.destroyed) return this;
4678
4802
  if (!info) {
4679
- this.els.boot.innerHTML = '<div class="sl-boot-title">The seat map didn\u2019t load</div><div>Check your connection and try again.</div><button type="button" class="sl-boot-retry">Try again</button>';
4803
+ this.els.boot.innerHTML = `<div class="sl-boot-title">${this.tf("picker.mapDidNotLoad", "The seat map didn\u2019t load")}</div><div>${this.tf("picker.checkConnection", "Check your connection and try again.")}</div><button type="button" class="sl-boot-retry">${this.tf("picker.accessRetry", "Try again")}</button>`;
4680
4804
  this.els.boot.querySelector("button").addEventListener("click", () => {
4681
4805
  const container = this.opts.container;
4682
4806
  const opts = this.opts;
@@ -4711,6 +4835,12 @@ var SeatPicker = class _SeatPicker {
4711
4835
  const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
4712
4836
  this.eventWhenText = when || null;
4713
4837
  this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
4838
+ if (this.els.pricesHint) {
4839
+ this.els.pricesHint.textContent = this.tf(
4840
+ "picker.priceHint",
4841
+ "Colours on the map are ticket types \u2014 tap one below to show just those seats."
4842
+ );
4843
+ }
4714
4844
  this.buildBadge(chartTheme);
4715
4845
  const present = /* @__PURE__ */ new Set();
4716
4846
  let hasLimitedView = false;
@@ -4737,7 +4867,7 @@ var SeatPicker = class _SeatPicker {
4737
4867
  const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-a11y="1" data-f="${key}">${label}</button>`;
4738
4868
  chips.insertAdjacentHTML(
4739
4869
  "beforeend",
4740
- mk("all", "All seats") + import_core2.ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
4870
+ mk("all", this.tf("picker.allSeats", "All seats")) + import_core2.ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
4741
4871
  );
4742
4872
  const active = /* @__PURE__ */ new Set();
4743
4873
  const syncChips = () => {
@@ -4783,7 +4913,7 @@ var SeatPicker = class _SeatPicker {
4783
4913
  cb.type = "button";
4784
4914
  cb.className = "sl-cbbtn";
4785
4915
  this.cbEl = cb;
4786
- cb.setAttribute("aria-label", "Toggle colorblind-friendly colors");
4916
+ cb.setAttribute("aria-label", this.tf("picker.toggleColorblindColors", "Toggle colorblind-friendly colors"));
4787
4917
  cb.setAttribute("aria-pressed", String(this.cbSafe));
4788
4918
  cb.innerHTML = '<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>';
4789
4919
  this.els.zfit.parentElement.appendChild(cb);
@@ -4869,8 +4999,8 @@ var SeatPicker = class _SeatPicker {
4869
4999
  if (!btn) return;
4870
5000
  const collapsed = this.sideCollapsed;
4871
5001
  btn.setAttribute("aria-expanded", String(!collapsed));
4872
- btn.setAttribute("aria-label", collapsed ? "Show the ticket panel" : "Hide the ticket panel");
4873
- 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>';
5002
+ btn.setAttribute("aria-label", collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel"));
5003
+ btn.innerHTML = collapsed ? `<svg viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></svg><span>${this.tf("picker.tickets", "Tickets")}</span>` : `<svg viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg><span>${this.tf("picker.hidePanel", "Hide panel")}</span>`;
4874
5004
  }
4875
5005
  /**
4876
5006
  * Collapse or reopen the wide-layout ticket panel in place (host-drivable —
@@ -4896,13 +5026,24 @@ var SeatPicker = class _SeatPicker {
4896
5026
  const narrow = this.root?.dataset.layout === "narrow";
4897
5027
  this.applyPanelCollapsed();
4898
5028
  const zfs = this.els.zfs;
4899
- if (zfs) {
4900
- if (narrow && this.regions["top-right"]) {
4901
- zfs.classList.add("sl-fs-pill");
4902
- this.regions["top-right"].appendChild(zfs);
4903
- } else if (!narrow && this.els.zoom && zfs.parentElement !== this.els.zoom) {
4904
- zfs.classList.remove("sl-fs-pill");
4905
- this.els.zoom.appendChild(zfs);
5029
+ const head = this.els.headInfo?.parentElement;
5030
+ if (zfs && head && zfs.parentElement !== head) {
5031
+ zfs.classList.add("sl-fs-pill");
5032
+ head.insertBefore(zfs, this.els.hold ?? null);
5033
+ }
5034
+ const pricesSec = this.els.pricesSec;
5035
+ if (pricesSec) {
5036
+ if (narrow) {
5037
+ pricesSec.setAttribute("role", "button");
5038
+ pricesSec.tabIndex = 0;
5039
+ pricesSec.setAttribute(
5040
+ "aria-expanded",
5041
+ String(this.root?.getAttribute("data-prices-open") === "true")
5042
+ );
5043
+ } else {
5044
+ pricesSec.removeAttribute("role");
5045
+ pricesSec.removeAttribute("aria-expanded");
5046
+ pricesSec.tabIndex = -1;
4906
5047
  }
4907
5048
  }
4908
5049
  const filters = this.els.filters;
@@ -4929,7 +5070,7 @@ var SeatPicker = class _SeatPicker {
4929
5070
  this.extendEl = el2;
4930
5071
  this.els.extendTxt = el2.querySelector('[data-ref="extendTxt"]');
4931
5072
  this.els.extendBtn = el2.querySelector('[data-ref="extendBtn"]');
4932
- this.els.extendBtn.textContent = "Add time";
5073
+ this.els.extendBtn.textContent = this.tf("picker.addTime", "Add time");
4933
5074
  this.els.extendBtn.addEventListener("click", () => void this.handleExtend());
4934
5075
  }
4935
5076
  /** Success overlay + onBooked fire when the held seats settle to booked. */
@@ -4938,7 +5079,7 @@ var SeatPicker = class _SeatPicker {
4938
5079
  el2.className = "sl-booked";
4939
5080
  el2.setAttribute("role", "status");
4940
5081
  el2.setAttribute("aria-live", "polite");
4941
- el2.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title">You're all set</div><div class="sl-booked-sub" data-ref="bookedSub"></div>`;
5082
+ el2.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title">${this.tf("picker.allSetTitle", "You're all set")}</div><div class="sl-booked-sub" data-ref="bookedSub"></div>`;
4942
5083
  this.root.appendChild(el2);
4943
5084
  this.bookedEl = el2;
4944
5085
  this.els.bookedSub = el2.querySelector('[data-ref="bookedSub"]');
@@ -4949,8 +5090,7 @@ var SeatPicker = class _SeatPicker {
4949
5090
  * host `messages` override (which makes `t()` return the override, not the key).
4950
5091
  */
4951
5092
  tf(key, fallback) {
4952
- const v = (0, import_core2.t)(key);
4953
- return v === key ? fallback : v;
5093
+ return tfStatic(key, fallback);
4954
5094
  }
4955
5095
  /** Sold-out overlay — an informational state with no unavailable action. */
4956
5096
  buildSoldoutOverlay() {
@@ -5135,7 +5275,7 @@ var SeatPicker = class _SeatPicker {
5135
5275
  }
5136
5276
  canAddTicket() {
5137
5277
  if (this.totalTicketCount() < this.maxTickets) return true;
5138
- this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
5278
+ this.toast((0, import_core2.t)("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
5139
5279
  return false;
5140
5280
  }
5141
5281
  pendingGATotal(gaAreas) {
@@ -5158,30 +5298,32 @@ var SeatPicker = class _SeatPicker {
5158
5298
  }
5159
5299
  if (this.confirmSeat || this.tableDialog && !this.tableDialogHeld) {
5160
5300
  cta.disabled = true;
5161
- cta.textContent = this.tableDialog ? "Confirm your table" : "Confirm or cancel this seat";
5301
+ cta.textContent = this.tableDialog ? this.tf("picker.confirmYourTable", "Confirm your table") : this.tf("picker.confirmOrCancelSeat", "Confirm or cancel this seat");
5162
5302
  return;
5163
5303
  }
5164
5304
  if (this.ctaPhase === "holding") {
5165
5305
  cta.disabled = true;
5166
- cta.innerHTML = '<span class="sl-cta-spin" aria-hidden="true"></span>Securing seats\u2026';
5306
+ cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}`;
5167
5307
  return;
5168
5308
  }
5169
5309
  if (this.ctaPhase === "checkout") {
5170
5310
  cta.disabled = true;
5171
- cta.innerHTML = '<span class="sl-cta-spin" aria-hidden="true"></span>Opening checkout\u2026';
5311
+ cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.openingCheckout", "Opening secure checkout\u2026")}`;
5172
5312
  return;
5173
5313
  }
5174
5314
  cta.disabled = count === 0;
5175
- cta.textContent = this.hold ? pending ? `Secure ${pending} more & checkout` : "Continue to checkout" : count ? "Hold seats & checkout" : "Select seats";
5315
+ cta.textContent = this.hold ? pending ? (0, import_core2.t)("picker.secureMoreAndCheckout", { count: pending }) : this.tf("picker.continueToCheckout", "Continue to checkout") : count ? this.tf("picker.holdSeatsAndCheckout", "Hold seats & checkout") : this.tf("picker.selectSeats", "Select seats");
5176
5316
  }
5177
5317
  setCtaPhase(phase) {
5178
5318
  this.ctaPhase = phase;
5179
5319
  this.syncCta();
5320
+ this.renderPeek(this.lastTrayCount, this.lastTrayTotal, this.pendingSelectionCount());
5180
5321
  if (phase === "checkout") {
5181
5322
  this.scheduleMotion(() => {
5182
5323
  if (this.ctaPhase !== "checkout") return;
5183
5324
  this.ctaPhase = "idle";
5184
5325
  this.syncCta();
5326
+ this.renderPeek(this.lastTrayCount, this.lastTrayTotal, this.pendingSelectionCount());
5185
5327
  }, 1100);
5186
5328
  }
5187
5329
  }
@@ -5231,7 +5373,7 @@ var SeatPicker = class _SeatPicker {
5231
5373
  this.syncTray();
5232
5374
  this.emitHoldChange();
5233
5375
  this.opts.onHoldRestored?.(restored, restored.seats ?? [], this.buildHandoff(restored));
5234
- if (automatic) this.toast("Your held tickets have been restored.", "success");
5376
+ if (automatic) this.toast(this.tf("picker.heldTicketsRestored", "Your held tickets have been restored."), "success");
5235
5377
  return restored;
5236
5378
  } catch (error) {
5237
5379
  const status = error?.status;
@@ -5449,8 +5591,8 @@ var SeatPicker = class _SeatPicker {
5449
5591
  if (bands.length < 2) return;
5450
5592
  const select = document.createElement("select");
5451
5593
  select.className = "sl-price-select";
5452
- select.setAttribute("aria-label", "Filter and focus seats by price");
5453
- select.innerHTML = `<option value="all">All prices</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
5594
+ select.setAttribute("aria-label", this.tf("picker.filterAndFocusByPrice", "Filter and focus seats by price"));
5595
+ select.innerHTML = `<option value="all">${this.tf("picker.allPrices", "All prices")}</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
5454
5596
  this.els.pricesSec.appendChild(select);
5455
5597
  select.addEventListener("change", () => {
5456
5598
  const band = bands.find((candidate) => candidate.id === select.value);
@@ -5477,8 +5619,8 @@ var SeatPicker = class _SeatPicker {
5477
5619
  const projection = document.createElement("div");
5478
5620
  projection.className = "sl-projection";
5479
5621
  projection.setAttribute("role", "group");
5480
- projection.setAttribute("aria-label", "Venue view");
5481
- projection.innerHTML = '<button type="button" data-view="map" aria-pressed="true" title="Flat 2D map">Map</button><button type="button" data-view="venue3d" aria-pressed="false" title="Interactive 3D venue view">3D</button>';
5622
+ projection.setAttribute("aria-label", this.tf("picker.venueView", "Venue view"));
5623
+ projection.innerHTML = `<button type="button" data-view="map" aria-pressed="true" title="${this.tf("picker.flat2dMap", "Flat 2D map")}">${this.tf("picker.map", "Map")}</button><button type="button" data-view="venue3d" aria-pressed="false" title="${this.tf("picker.interactive3dVenueView", "Interactive 3D venue view")}">3D</button>`;
5482
5624
  projection.querySelectorAll("button").forEach((button) => {
5483
5625
  button.addEventListener("click", () => {
5484
5626
  this.setBuyerView(button.dataset.view);
@@ -5601,6 +5743,10 @@ var SeatPicker = class _SeatPicker {
5601
5743
  renderSectionCard(summary) {
5602
5744
  if (!this.els.map) return;
5603
5745
  this.secCardEl?.remove();
5746
+ if (this.root?.dataset.layout === "narrow" && (this.hold || this.committedSelection().length || this.pendingGACount())) {
5747
+ this.lastSection = null;
5748
+ return;
5749
+ }
5604
5750
  const paid = summary.categories.length ? summary.categories.map((c) => this.paidPrice(c.key, null, c.price)) : [summary.priceMin, summary.priceMax];
5605
5751
  const paidMin = Math.min(...paid);
5606
5752
  const paidMax = Math.max(...paid);
@@ -5701,13 +5847,13 @@ var SeatPicker = class _SeatPicker {
5701
5847
  }
5702
5848
  const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
5703
5849
  const status = this.controller.getStatus(seat.id) ?? "free";
5704
- const statusText = status === "free" ? "available" : status === "held" ? "on hold" : "taken";
5850
+ const statusText = status === "free" ? this.tf("picker.statusAvailable", "available") : status === "held" ? this.tf("picker.statusOnHold", "on hold") : this.tf("picker.statusTaken2", "taken");
5705
5851
  const price = cat ? this.catPrice(cat) : void 0;
5706
5852
  const table = this.controller.tableSelection(seat.id);
5707
5853
  const details = table ?? this.controller.seatDetails(seat.id);
5708
5854
  const typeWord = this.rowTypeWord(details);
5709
5855
  const buyerName = details?.rowLabel ?? details?.displayLabel ?? seat.displayLabel ?? seat.label;
5710
- const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ? `${table.minOccupancy} to ${table.maxOccupancy} guests` : `${table.capacity} guests`}` : details?.objectType === "booth" ? `${typeWord} ${buyerName}` : details?.objectType === "table" ? `${typeWord} ${buyerName}, seat ${details.seatNumber ?? seat.label}` : `Seat ${details?.displayLabel ?? seat.displayLabel ?? seat.label}`;
5856
+ const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ? (0, import_core2.t)("picker.minToMaxGuests", { min: table.minOccupancy, max: table.maxOccupancy }) : (0, import_core2.t)("picker.capacityGuests", { count: table.capacity })}` : details?.objectType === "booth" ? `${typeWord} ${buyerName}` : details?.objectType === "table" ? `${typeWord} ${buyerName}, ${(0, import_core2.t)("picker.seatNumberLower", { label: details.seatNumber ?? seat.label })}` : (0, import_core2.t)("picker.seatLabel", { label: details?.displayLabel ?? seat.displayLabel ?? seat.label });
5711
5857
  this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${price != null ? `, ${this.money(price)}` : ""}, ${statusText}`;
5712
5858
  }
5713
5859
  // ---- atomic table confirmation / guest quantity ---------------------------
@@ -5728,7 +5874,7 @@ var SeatPicker = class _SeatPicker {
5728
5874
  const typeWord = this.rowTypeWord(table);
5729
5875
  const el2 = document.createElement("div");
5730
5876
  el2.className = "sl-table-scrim";
5731
- el2.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc(variable ? `Flexible party \xB7 ${typeWord}` : `Whole ${typeWord}`)}</div><h2 class="sl-table-title" id="sl-table-title">${esc(table.displayLabel ?? table.label)}</h2><p class="sl-table-copy" id="sl-table-copy">${variable ? `Choose how many guests will sit together. This table is held exclusively for your party.` : `All ${table.capacity} places are booked together as one exclusive table.`}</p></div><div class="sl-table-body"><div class="sl-table-summary"><span>${esc(cat?.label ?? table.categoryKey)}</span><b data-table-unit>${this.money(this.paidPrice(table.categoryKey, table.tierId ?? null, table.price))} per guest</b><span class="muted">Table capacity</span><span>${table.capacity} guests</span><span class="muted">Total</span><b data-table-total></b></div>` + (variable ? `<label class="sl-table-qtylabel" id="sl-table-qty-label">Number of guests</label><div class="sl-table-stepper" role="group" aria-labelledby="sl-table-qty-label"><button type="button" data-table-step="-1" aria-label="Fewer guests">\u2212</button><output aria-live="polite" data-table-qty>${table.quantity}</output><button type="button" data-table-step="1" aria-label="More guests">+</button></div><div class="sl-table-range">Choose ${table.minOccupancy}\u2013${table.maxOccupancy} guests</div>` : `<input type="hidden" data-table-qty value="${table.capacity}">`) + `<div class="sl-table-actions"><button type="button" class="sl-table-cancel">Cancel</button><button type="button" class="sl-table-confirm">${held ? "Update table" : variable ? "Select table" : "Select whole table"}</button></div></div></section>`;
5877
+ el2.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc(variable ? (0, import_core2.t)("picker.flexiblePartyTypeWord", { typeWord }) : (0, import_core2.t)("picker.wholeTypeWord", { typeWord }))}</div><h2 class="sl-table-title" id="sl-table-title">${esc(table.displayLabel ?? table.label)}</h2><p class="sl-table-copy" id="sl-table-copy">${variable ? this.tf("picker.chooseGuestsCopy", "Choose how many guests will sit together. This table is held exclusively for your party.") : (0, import_core2.t)("picker.allPlacesBookedTogether", { count: table.capacity })}</p></div><div class="sl-table-body"><div class="sl-table-summary"><span>${esc(cat?.label ?? table.categoryKey)}</span><b data-table-unit>${(0, import_core2.t)("picker.perGuestPrice", { price: this.money(this.paidPrice(table.categoryKey, table.tierId ?? null, table.price)) })}</b><span class="muted">${this.tf("picker.tableCapacity", "Table capacity")}</span><span>${(0, import_core2.t)("picker.guestsCount", { count: table.capacity })}</span><span class="muted">${this.tf("picker.total", "Total")}</span><b data-table-total></b></div>` + (variable ? `<label class="sl-table-qtylabel" id="sl-table-qty-label">${this.tf("picker.numberOfGuests", "Number of guests")}</label><div class="sl-table-stepper" role="group" aria-labelledby="sl-table-qty-label"><button type="button" data-table-step="-1" aria-label="${this.tf("picker.fewerGuests", "Fewer guests")}">\u2212</button><output aria-live="polite" data-table-qty>${table.quantity}</output><button type="button" data-table-step="1" aria-label="${this.tf("picker.moreGuests", "More guests")}">+</button></div><div class="sl-table-range">${(0, import_core2.t)("picker.chooseMinMaxGuests", { min: table.minOccupancy, max: table.maxOccupancy })}</div>` : `<input type="hidden" data-table-qty value="${table.capacity}">`) + `<div class="sl-table-actions"><button type="button" class="sl-table-cancel">${(0, import_core2.t)("common.cancel")}</button><button type="button" class="sl-table-confirm">${held ? this.tf("picker.updateTable", "Update table") : variable ? this.tf("picker.selectTable", "Select table") : this.tf("picker.selectWholeTable", "Select whole table")}</button></div></div></section>`;
5732
5878
  this.root.appendChild(el2);
5733
5879
  this.tableDialogEl = el2;
5734
5880
  this.renderTableDialogState();
@@ -5787,17 +5933,17 @@ var SeatPicker = class _SeatPicker {
5787
5933
  const button = this.tableDialogEl?.querySelector(".sl-table-confirm");
5788
5934
  if (button) {
5789
5935
  button.disabled = true;
5790
- button.textContent = held ? "Updating\u2026" : "Selecting\u2026";
5936
+ button.textContent = held ? this.tf("picker.updatingEllipsis", "Updating\u2026") : this.tf("picker.selectingEllipsis", "Selecting\u2026");
5791
5937
  }
5792
5938
  if (held) {
5793
5939
  try {
5794
5940
  const updated = await this.controller.replaceTableQuantity(table.label, table.quantity, this.opts.holdTtlMs);
5795
5941
  if (!updated) {
5796
- this.toast("That guest count could not be secured. Your current table hold is unchanged.", "warning");
5942
+ this.toast(this.tf("picker.guestCountCouldNotBeSecured", "That guest count could not be secured. Your current table hold is unchanged."), "warning");
5797
5943
  this.renderTableDialogState();
5798
5944
  if (button) {
5799
5945
  button.disabled = false;
5800
- button.textContent = "Update table";
5946
+ button.textContent = this.tf("picker.updateTable", "Update table");
5801
5947
  }
5802
5948
  return;
5803
5949
  }
@@ -5811,13 +5957,13 @@ var SeatPicker = class _SeatPicker {
5811
5957
  this.dismissTableDialog();
5812
5958
  this.syncTray();
5813
5959
  this.emitHoldChange();
5814
- this.toast(`${table.label} updated for ${table.quantity} guests.`, "success");
5960
+ this.toast((0, import_core2.t)("picker.tableUpdatedForGuests", { label: table.label, count: table.quantity }), "success");
5815
5961
  } catch (error) {
5816
5962
  this.opts.onError?.(error);
5817
- this.toast("That guest count is no longer available. Your current hold is unchanged.", "error");
5963
+ this.toast(this.tf("picker.guestCountNoLongerAvailable", "That guest count is no longer available. Your current hold is unchanged."), "error");
5818
5964
  if (button) {
5819
5965
  button.disabled = false;
5820
- button.textContent = "Update table";
5966
+ button.textContent = this.tf("picker.updateTable", "Update table");
5821
5967
  }
5822
5968
  }
5823
5969
  return;
@@ -5825,7 +5971,7 @@ var SeatPicker = class _SeatPicker {
5825
5971
  if (!this.controller.setTableQuantity(table.label, table.quantity)) {
5826
5972
  if (button) {
5827
5973
  button.disabled = false;
5828
- button.textContent = table.bookingMode === "variable" ? "Select table" : "Select whole table";
5974
+ button.textContent = table.bookingMode === "variable" ? this.tf("picker.selectTable", "Select table") : this.tf("picker.selectWholeTable", "Select whole table");
5829
5975
  }
5830
5976
  return;
5831
5977
  }
@@ -5871,16 +6017,16 @@ var SeatPicker = class _SeatPicker {
5871
6017
  '"': "&quot;"
5872
6018
  })[char]);
5873
6019
  const identityFields = [
5874
- details?.sectionLabel ? `<div class="sl-confirm-field"><span class="sl-confirm-key">Section</span><span class="sl-confirm-value">${safe(details.sectionLabel)}</span></div>` : "",
6020
+ details?.sectionLabel ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${this.tf("picker.section", "Section")}</span><span class="sl-confirm-value">${safe(details.sectionLabel)}</span></div>` : "",
5875
6021
  details?.rowLabel || details?.objectType === "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${safe(this.rowTypeWord(details))}</span><span class="sl-confirm-value">${safe(this.rowShort(details) ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : "",
5876
- details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">Seat</span><span class="sl-confirm-value">${safe(details?.seatNumber ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : ""
6022
+ details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-confirm-value">${safe(details?.seatNumber ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : ""
5877
6023
  ].filter(Boolean).join("");
5878
6024
  const el2 = document.createElement("div");
5879
6025
  el2.className = "sl-confirm";
5880
6026
  el2.setAttribute("role", "dialog");
5881
- el2.setAttribute("aria-label", `Confirm seat ${seat.label}`);
6027
+ el2.setAttribute("aria-label", (0, import_core2.t)("picker.confirmSeatLabel", { label: seat.label }));
5882
6028
  el2.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
5883
- el2.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() && this.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>`;
6029
+ el2.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() && this.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">${(0, import_core2.t)("common.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>${this.tf("picker.select", "Select")}</button></div></div>`;
5884
6030
  this.els.map.appendChild(el2);
5885
6031
  this.confirmEl = el2;
5886
6032
  const thumb = el2.querySelector(".sl-confirm-thumb");
@@ -6033,7 +6179,7 @@ var SeatPicker = class _SeatPicker {
6033
6179
  el2.className = "sl-view";
6034
6180
  el2.setAttribute("role", "dialog");
6035
6181
  el2.setAttribute("aria-label", (0, import_core2.t)("picker.viewFromSeat", { label: seat.label }));
6036
- el2.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="Close"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint">Drag to look around \xB7 scroll to zoom</span></div>`;
6182
+ el2.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="${this.tf("picker.close", "Close")}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint">${this.tf("picker.dragToLookAround", "Drag to look around \xB7 scroll to zoom")}</span></div>`;
6037
6183
  this.root.appendChild(el2);
6038
6184
  this.viewEl = el2;
6039
6185
  const pano = el2.querySelector(".sl-view-pano");
@@ -6229,27 +6375,27 @@ var SeatPicker = class _SeatPicker {
6229
6375
  copy.className = "sl-offer-copy";
6230
6376
  const kicker = document.createElement("span");
6231
6377
  kicker.className = "sl-offer-kicker";
6232
- kicker.textContent = active ? "Current ticket offer" : "Upcoming ticket offer";
6378
+ kicker.textContent = active ? this.tf("picker.currentTicketOffer", "Current ticket offer") : this.tf("picker.upcomingTicketOffer", "Upcoming ticket offer");
6233
6379
  const name = document.createElement("strong");
6234
6380
  name.className = "sl-offer-name";
6235
- name.textContent = offer.name || (active ? "Current offer" : "Scheduled offer");
6381
+ name.textContent = offer.name || (active ? this.tf("picker.currentOffer", "Current offer") : this.tf("picker.scheduledOffer", "Scheduled offer"));
6236
6382
  const line = document.createElement("span");
6237
6383
  line.className = "sl-offer-line";
6238
6384
  const facts = [];
6239
6385
  if (active && availability.fromPrice != null) facts.push(this.money(availability.fromPrice / 100));
6240
- if (active && offer.remaining != null) facts.push(`${offer.remaining} available`);
6241
- if (active && offer.endsAt != null) facts.push(`until ${formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale)}`);
6242
- if (upcoming?.startsAt != null) facts.push(`starts ${formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale)}`);
6386
+ if (active && offer.remaining != null) facts.push((0, import_core2.t)("picker.offerRemainingAvailable", { count: offer.remaining }));
6387
+ if (active && offer.endsAt != null) facts.push((0, import_core2.t)("picker.offerUntil", { time: formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale) }));
6388
+ if (upcoming?.startsAt != null) facts.push((0, import_core2.t)("picker.offerStarts", { time: formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale) }));
6243
6389
  line.textContent = facts.join(" \xB7 ");
6244
6390
  copy.append(kicker, name, line);
6245
6391
  const info = document.createElement("details");
6246
6392
  info.className = "sl-offer-info";
6247
6393
  const summary = document.createElement("summary");
6248
- summary.setAttribute("aria-label", `How the ${name.textContent} offer works`);
6394
+ summary.setAttribute("aria-label", (0, import_core2.t)("picker.howOfferWorks", { name: name.textContent ?? "" }));
6249
6395
  summary.textContent = "i";
6250
6396
  const detail = document.createElement("div");
6251
6397
  detail.className = "sl-offer-detail";
6252
- detail.textContent = active ? `This price applies automatically to eligible seats. Tickets in active carts temporarily reduce the available quantity; released or expired holds return it. When the offer ends, the next matching offer or normal ticket price takes over.` : `Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.`;
6398
+ detail.textContent = active ? this.tf("picker.offerDetailActive", "This price applies automatically to eligible seats. Tickets in active carts temporarily reduce the available quantity; released or expired holds return it. When the offer ends, the next matching offer or normal ticket price takes over.") : this.tf("picker.offerDetailUpcoming", "Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.");
6253
6399
  info.append(summary, detail);
6254
6400
  main.append(copy, info);
6255
6401
  host.replaceChildren(main);
@@ -6285,8 +6431,8 @@ var SeatPicker = class _SeatPicker {
6285
6431
  const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
6286
6432
  const active = this.focusedCatKey === c.key;
6287
6433
  const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
6288
- return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? "Show all seats" : `Show ${c.label} seats on the map`)}"><span class="sl-dot" style="background:${escapeOption(c.color)}"></span><span class="sl-price-label">${escapeOption(c.label)}` + (offer?.offerName ? `<small class="sl-price-offer">${escapeOption(offer.offerName)}</small>` : "") + `</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (previous != null ? `<span class="sl-price-was">${escapeOption(this.money(previous))}</span>` : "") + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
6289
- }).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? `Show all ${doc.categories.length} ticket types` : "Show fewer") + `</button>` : "") + `<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>`;
6434
+ return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? this.tf("picker.showAllSeats", "Show all seats") : (0, import_core2.t)("picker.showLabelSeatsOnMap", { label: c.label }))}"><span class="sl-dot" style="background:${escapeOption(c.color)}"></span><span class="sl-price-label">${escapeOption(c.label)}` + (offer?.offerName ? `<small class="sl-price-offer">${escapeOption(offer.offerName)}</small>` : "") + `</span><span class="sl-price-left">${(0, import_core2.tCount)("picker.leftCount", left[c.key] ?? 0)}</span>` + (previous != null ? `<span class="sl-price-was">${escapeOption(this.money(previous))}</span>` : "") + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
6435
+ }).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? (0, import_core2.t)("picker.showAllTicketTypes", { count: doc.categories.length }) : this.tf("picker.showFewer", "Show fewer")) + `</button>` : "") + `<div class="sl-status-key" aria-label="${this.tf("picker.seatStatusLegend", "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>${this.tf("picker.temporarilyHeld", "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>${this.tf("picker.sold", "Sold")}</span></div>`;
6290
6436
  this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
6291
6437
  row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
6292
6438
  row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
@@ -6344,7 +6490,7 @@ var SeatPicker = class _SeatPicker {
6344
6490
  const now = left[cat.key] ?? 0;
6345
6491
  if (before === void 0 || now >= before) continue;
6346
6492
  const taken = before - now;
6347
- textEl.textContent = `${taken} seat${taken === 1 ? "" : "s"} just taken in ${cat.label} \xB7 ${now} left`;
6493
+ textEl.textContent = (0, import_core2.tCount)("picker.seatsJustTakenInCategory", taken, { label: cat.label, left: now });
6348
6494
  this.els.live?.classList.remove("on");
6349
6495
  void this.els.live?.offsetWidth;
6350
6496
  this.els.live?.classList.add("on");
@@ -6362,12 +6508,17 @@ var SeatPicker = class _SeatPicker {
6362
6508
  const gone = this.controller.getSelection().filter((s) => !ownLabels.has(s.label) && (this.controller.getStatus(s.id) ?? "free") !== "free");
6363
6509
  if (!gone.length) return;
6364
6510
  this.controller.deselect(gone.map((s) => s.id));
6365
- this.toast(`Seat ${gone[0].label} was just taken by another buyer.`, "error");
6511
+ this.toast((0, import_core2.t)("picker.seatJustTakenByAnother", { label: gone[0].label }), "error");
6366
6512
  }
6367
6513
  syncTray() {
6368
6514
  if (!this.els.tray) return;
6369
6515
  this.updateSelectionCapacity();
6370
6516
  const seats = this.committedSelection();
6517
+ if (this.root?.dataset.layout === "narrow" && this.secCardEl && (this.hold || seats.length || this.pendingGACount())) {
6518
+ this.secCardEl.remove();
6519
+ this.secCardEl = null;
6520
+ this.lastSection = null;
6521
+ }
6371
6522
  const gaAreas = this.controller.getGAAreas();
6372
6523
  const heldItems = this.hold?.items ?? [];
6373
6524
  const parts = [];
@@ -6378,18 +6529,22 @@ var SeatPicker = class _SeatPicker {
6378
6529
  `<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>`
6379
6530
  );
6380
6531
  } else if (!seats.length && !heldItems.length && !gaAreas.length) {
6381
- parts.push(`<div class="sl-tray-hint">Tap a seat on the map, or let us pick the best available for you.</div>`);
6532
+ parts.push(`<div class="sl-tray-hint">${this.tf("picker.trayHintTapOrBest", "Tap a seat on the map, or let us pick the best available for you.")}</div>`);
6382
6533
  } else if (!seats.length && !heldItems.length) {
6383
- parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
6534
+ parts.push(`<div class="sl-tray-hint">${this.tf("picker.trayHintTapOrStanding", "Tap a seat on the map \u2014 or grab standing tickets below.")}</div>`);
6384
6535
  }
6385
6536
  const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
6386
- if (!this.salesClosed && !this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
6537
+ if (noPicks) this.baReopen = false;
6538
+ if (!this.salesClosed && !this.hold && (noPicks || this.baReopen || this.bestAvailableBusy || this.bestAvailableConfirm)) {
6387
6539
  const cats = this.controller.doc?.categories ?? [];
6388
6540
  const zones = this.controller.getBestAvailableZones();
6389
6541
  if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
6390
- 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
6542
+ parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>${this.tf("picker.replaceCurrentChoices", "Replace your current choices?")}</div><div class="sl-ba-replace"><b>${(0, import_core2.t)("picker.willFindSeatsTogether", { count: this.baQty })}</b><span>${this.tf("picker.manualTicketsRemovedNote", "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>${this.tf("picker.keepMine", "Keep mine")}</button><button type="button" class="replace" data-ba-replace>${this.tf("picker.findNewSeats", "Find new seats")}</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>${this.tf("picker.findBestSeatsTogether", "Find the best seats together")}</div><div class="sl-ba-copy"><span class="wide">${this.tf("picker.willChooseClosestGroup", "We\u2019ll choose the closest available group for you.")}</span><span class="narrow">${this.tf("picker.closestGroupChosenInstantly", "Closest available group, chosen instantly.")}</span></div>` + // Premium quick-pick — present only when the chart actually has premium
6391
6543
  // seats (same present-only philosophy as the a11y filter chips).
6392
- (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>`);
6544
+ (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="${this.tf("picker.preferredTicketType", "Preferred ticket type")}" data-ba-cat><option value="">${this.tf("picker.anyTicketType", "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="${this.tf("picker.preferredVenueZone", "Preferred venue zone")}" data-ba-zone><option value="">${this.tf("picker.anyVenueZone", "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="${this.tf("picker.fewerSeats", "Fewer seats")}">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="${this.tf("picker.moreSeats", "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>${this.tf("picker.findingBestSeats", "Finding the best seats\u2026")}` : (0, import_core2.tCount)("picker.findBestSeatsCount", this.baQty)) + `</button>` + // Reopened over an existing selection: the buyer needs the way back
6545
+ // stated as plainly as the way forward (Go routes through the
6546
+ // "Replace your current choices?" confirm, so nothing is lost yet).
6547
+ (this.baReopen ? `<button type="button" class="sl-ba-back" data-ba-close>${this.tf("picker.keepMyPicks", "Keep my picks")}</button>` : "") + `</div>`);
6393
6548
  }
6394
6549
  const idGrid = (seatId, label, objectType, quantity = 1, objectId, identity) => {
6395
6550
  const esc = (value) => String(value ?? "\u2014").replace(/[&<>"]/g, (char) => ({
@@ -6401,21 +6556,21 @@ var SeatPicker = class _SeatPicker {
6401
6556
  const d = seatId ? this.controller.seatDetails(seatId) : null;
6402
6557
  const area = objectType === "ga" ? gaAreas.find((candidate) => candidate.id === objectId) : void 0;
6403
6558
  const effectiveType = objectType === "ga" ? "ga" : d?.objectType ?? objectType;
6404
- const typeWord = identity?.displayType?.trim() || d?.displayType?.trim() || area?.displayType?.trim() || (effectiveType === "table" ? "Table" : effectiveType === "booth" ? "Booth" : effectiveType === "ga" ? "General admission" : "Row");
6559
+ const typeWord = identity?.displayType?.trim() || d?.displayType?.trim() || area?.displayType?.trim() || (effectiveType === "table" ? this.tf("picker.table", "Table") : effectiveType === "booth" ? this.tf("picker.booth", "Booth") : effectiveType === "ga" ? this.tf("picker.generalAdmission", "General admission") : this.tf("picker.row", "Row"));
6405
6560
  const buyerName = identity?.rowLabel ?? identity?.displayLabel ?? d?.rowLabel ?? d?.displayLabel ?? area?.displayLabel ?? area?.label ?? label;
6406
6561
  if (effectiveType === "table" && identity?.bookingMode) {
6407
- return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span><span class="fld mid"><span class="sl-chip-eb">Guests</span><span class="val">${quantity}</span></span></div>`;
6562
+ return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span><span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.guests", "Guests")}</span><span class="val">${quantity}</span></span></div>`;
6408
6563
  }
6409
6564
  if (effectiveType === "ga") {
6410
- return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span>` + (quantity > 1 ? `<span class="fld mid"><span class="sl-chip-eb">Tickets</span><span class="val">${quantity}</span></span>` : "") + `</div>`;
6565
+ return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span>` + (quantity > 1 ? `<span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.tickets", "Tickets")}</span><span class="val">${quantity}</span></span>` : "") + `</div>`;
6411
6566
  }
6412
6567
  if (effectiveType === "booth") {
6413
- return `<div class="sl-chip-id">` + (d?.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">Section</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span></div>`;
6568
+ return `<div class="sl-chip-id">` + (d?.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.section", "Section")}</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span></div>`;
6414
6569
  }
6415
6570
  if (!d?.sectionLabel && !d?.rowLabel && !d?.seatNumber) {
6416
- return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">Seat</span><span class="val">${esc(buyerName)}</span></span></div>`;
6571
+ return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.seat", "Seat")}</span><span class="val">${esc(buyerName)}</span></span></div>`;
6417
6572
  }
6418
- return `<div class="sl-chip-id">` + (d.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">Section</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + (d.rowLabel ? `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(this.rowShort(d))}</span></span>` : "") + (d.seatNumber ? `<span class="fld mid"><span class="sl-chip-eb">Seat</span><span class="val">${esc(d.seatNumber)}</span></span>` : "") + `</div>`;
6573
+ return `<div class="sl-chip-id">` + (d.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.section", "Section")}</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + (d.rowLabel ? `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(this.rowShort(d))}</span></span>` : "") + (d.seatNumber ? `<span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.seat", "Seat")}</span><span class="val">${esc(d.seatNumber)}</span></span>` : "") + `</div>`;
6419
6574
  };
6420
6575
  const iconRail = (rmAria, viewLabel) => `<div class="sl-chip-rail"><button type="button" class="rm" aria-label="${rmAria}"><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>` + (viewLabel ? `<button type="button" class="view" data-view-label="${viewLabel}" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: viewLabel })}"><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>` : "") + `</div>`;
6421
6576
  for (const item of heldItems) {
@@ -6427,7 +6582,7 @@ var SeatPicker = class _SeatPicker {
6427
6582
  const table = item.objectType === "table" ? this.controller.tableSelection(item.label) : null;
6428
6583
  const canView2 = this.seatViewEnabled() && !!heldSeat && item.objectType !== "table";
6429
6584
  parts.push(
6430
- `<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label, item.objectType, item.quantity ?? 1, item.objectId, table ?? void 0) + `<div class="sl-chip-sub"><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><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>` + (table?.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(item.label)}">${item.quantity ?? 1} guests \xB7 Edit</button>` : "") + this.wheelchairChipMarker(heldSeat?.wheelchairSpaceType) + this.commercialChipMarker(heldSeat?.commercial) + `<span class="amt">${this.money(this.paidPrice(item.categoryKey, item.tierId, item.unitPrice) * (item.quantity ?? 1))}</span></div></div>` + iconRail(`Remove held ticket ${item.label}`, canView2 ? item.label : null) + `</div>`
6585
+ `<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label, item.objectType, item.quantity ?? 1, item.objectId, table ?? void 0) + `<div class="sl-chip-sub"><span class="sl-ticket-state held" aria-label="${this.tf("picker.heldForYou", "Held for you")}" title="${this.tf("picker.heldForYou", "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><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>` + (table?.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(item.label)}">${(0, import_core2.t)("picker.guestsCountEdit", { count: item.quantity ?? 1 })}</button>` : "") + this.wheelchairChipMarker(heldSeat?.wheelchairSpaceType) + this.commercialChipMarker(heldSeat?.commercial) + `<span class="amt">${this.money(this.paidPrice(item.categoryKey, item.tierId, item.unitPrice) * (item.quantity ?? 1))}</span></div></div>` + iconRail((0, import_core2.t)("picker.removeHeldTicketLabel", { label: item.label }), canView2 ? item.label : null) + `</div>`
6431
6586
  );
6432
6587
  }
6433
6588
  const heldLabels = new Set(heldItems.map((item) => item.label));
@@ -6438,13 +6593,18 @@ var SeatPicker = class _SeatPicker {
6438
6593
  const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
6439
6594
  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(this.paidPrice(s.categoryKey, ti.id, ti.price))}</option>`).join("") + `</select>` : "";
6440
6595
  parts.push(
6441
- `<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.label, s.objectType, s.quantity ?? 1, s.objectId, s) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>` + (s.objectType === "table" && s.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(s.label)}">${s.quantity ?? 1} guests \xB7 Edit</button>` : "") + `${this.wheelchairChipMarker(s.wheelchairSpaceType)}${this.commercialChipMarker(s.commercial)}${tierSelect}<span class="amt">${this.money(this.paidPrice(s.categoryKey, s.tierId ?? null, s.price) * (s.quantity ?? 1))}</span></div></div>` + iconRail(`Remove ${s.label}`, canView && s.objectType !== "table" ? s.label : null) + `</div>`
6596
+ `<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.label, s.objectType, s.quantity ?? 1, s.objectId, s) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="${this.tf("picker.selected", "Selected")}" title="${this.tf("picker.selected", "Selected")}"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>` + (s.objectType === "table" && s.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(s.label)}">${(0, import_core2.t)("picker.guestsCountEdit", { count: s.quantity ?? 1 })}</button>` : "") + `${this.wheelchairChipMarker(s.wheelchairSpaceType)}${this.commercialChipMarker(s.commercial)}${tierSelect}<span class="amt">${this.money(this.paidPrice(s.categoryKey, s.tierId ?? null, s.price) * (s.quantity ?? 1))}</span></div></div>` + iconRail((0, import_core2.t)("picker.removeSeatLabel", { label: s.label }), canView && s.objectType !== "table" ? s.label : null) + `</div>`
6442
6597
  );
6443
6598
  }
6444
6599
  for (const area of gaAreas) {
6445
6600
  const qty = this.gaQty.get(area.id) ?? 0;
6446
6601
  parts.push(
6447
- `<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.displayLabel ?? area.label}</div><div class="sl-ga-sub">${area.displayType ?? "General admission"} \xB7 ${this.money(this.paidPrice(area.categoryKey, null, 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>`
6602
+ `<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.displayLabel ?? area.label}</div><div class="sl-ga-sub">${area.displayType ?? this.tf("picker.generalAdmission", "General admission")} \xB7 ${this.money(this.paidPrice(area.categoryKey, null, area.price))} \xB7 ${(0, import_core2.tCount)("picker.leftCount", area.available)}</div></div><div class="sl-ga-qty"><button type="button" data-d="-1" aria-label="${this.tf("picker.fewer", "Fewer")}">\u2212</button><span>${qty}</span><button type="button" data-d="1" aria-label="${this.tf("picker.more", "More")}">+</button></div></div>`
6603
+ );
6604
+ }
6605
+ if (!this.salesClosed && !this.hold && !noPicks && !this.baReopen && !this.bestAvailableBusy && !this.bestAvailableConfirm) {
6606
+ parts.push(
6607
+ `<button type="button" class="sl-ba-reopen" data-ba-reopen><span aria-hidden="true">\u2726</span>${this.tf("picker.findTogetherInstead", "Find seats together instead")}</button>`
6448
6608
  );
6449
6609
  }
6450
6610
  this.els.tray.innerHTML = parts.join("");
@@ -6479,6 +6639,16 @@ var SeatPicker = class _SeatPicker {
6479
6639
  this.syncTray();
6480
6640
  this.els.tray.querySelector(".sl-ba-go")?.focus();
6481
6641
  });
6642
+ this.els.tray.querySelector("[data-ba-reopen]")?.addEventListener("click", () => {
6643
+ this.baReopen = true;
6644
+ this.syncTray();
6645
+ this.els.tray.querySelector(".sl-ba-go")?.focus();
6646
+ });
6647
+ this.els.tray.querySelector("[data-ba-close]")?.addEventListener("click", () => {
6648
+ this.baReopen = false;
6649
+ this.syncTray();
6650
+ this.els.tray.querySelector("[data-ba-reopen]")?.focus();
6651
+ });
6482
6652
  this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
6483
6653
  this.bestAvailableConfirm = false;
6484
6654
  void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium, zoneId: this.baZone || void 0 });
@@ -6491,15 +6661,15 @@ var SeatPicker = class _SeatPicker {
6491
6661
  return;
6492
6662
  }
6493
6663
  const id = chip.dataset.seat;
6494
- const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? "Seat";
6664
+ const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? this.tf("picker.seat", "Seat");
6495
6665
  const remove = () => {
6496
6666
  this.controller.deselect([id]);
6497
- this.toast(`${label} removed.`, "neutral", {
6498
- label: "Undo",
6667
+ this.toast((0, import_core2.t)("picker.labelRemoved", { label }), "neutral", {
6668
+ label: this.tf("picker.undo", "Undo"),
6499
6669
  onClick: () => {
6500
6670
  const restored = this.controller.select([id]);
6501
6671
  this.toast(
6502
- restored.length ? `${label} restored.` : `${label} is no longer available.`,
6672
+ restored.length ? (0, import_core2.t)("picker.labelRestored", { label }) : (0, import_core2.t)("picker.labelNoLongerAvailable", { label }),
6503
6673
  restored.length ? "success" : "warning"
6504
6674
  );
6505
6675
  }
@@ -6571,30 +6741,31 @@ var SeatPicker = class _SeatPicker {
6571
6741
  const pendingCount = this.pendingSelectionCount();
6572
6742
  const previousCount = this.lastTrayCount;
6573
6743
  const previousTotal = this.lastTrayTotal;
6574
- this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
6744
+ this.els.count.textContent = count ? (0, import_core2.tCount)("picker.ticketsCount", count) : this.tf("picker.noSeatsSelected", "No seats selected");
6575
6745
  this.els.total.textContent = count ? this.money(total) : "";
6576
6746
  this.root?.setAttribute("data-has-selection", String(count > 0));
6577
6747
  this.root?.setAttribute(
6578
6748
  "data-ba-active",
6579
- String(this.bestAvailableConfirm || this.bestAvailableBusy)
6749
+ String(this.bestAvailableConfirm || this.bestAvailableBusy || this.baReopen)
6580
6750
  );
6581
6751
  this.els.foot?.classList.toggle("empty", count === 0);
6582
6752
  if (this.els.seatSummary) {
6583
- this.els.seatSummary.textContent = count ? `${count} selected` : "";
6753
+ this.els.seatSummary.textContent = count ? (0, import_core2.tCount)("picker.seatsSelectedCount", count) : "";
6584
6754
  }
6585
6755
  this.syncCta(count, pendingCount);
6586
6756
  if (this.hold) {
6587
6757
  const securedCount = heldCount || this.hold.seats?.length || 0;
6588
6758
  if (this.els.holdTitle) {
6589
- this.els.holdTitle.textContent = `${securedCount} secured`;
6759
+ this.els.holdTitle.textContent = (0, import_core2.t)("picker.securedCount", { count: securedCount });
6590
6760
  }
6591
6761
  if (this.els.holdCopy) {
6592
- this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected` : "Checkout timer running";
6762
+ this.holdCopyPending = pendingCount > 0;
6763
+ this.els.holdCopy.textContent = pendingCount ? (0, import_core2.t)("picker.moreSelectedCount", { count: pendingCount }) : this.holdReassuranceText();
6593
6764
  }
6594
6765
  const change = this.els.holdChange;
6595
6766
  if (change) {
6596
6767
  change.disabled = this.releasingHold;
6597
- change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
6768
+ change.textContent = this.releasingHold ? this.tf("picker.releasingEllipsis", "Releasing\u2026") : this.tf("picker.change", "Change");
6598
6769
  }
6599
6770
  }
6600
6771
  if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
@@ -6603,21 +6774,39 @@ var SeatPicker = class _SeatPicker {
6603
6774
  this.animateOnce(this.els.cta, "sl-ready", 520);
6604
6775
  if (this.sideCollapsed && this.root?.dataset.layout !== "narrow") this.setPanelCollapsed(false);
6605
6776
  }
6606
- if (this.els.peek) {
6607
- if (count) {
6608
- const holding = !!this.hold && !pendingCount;
6609
- 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>`;
6610
- } else if (this.salesClosed) {
6611
- this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
6612
- } else {
6613
- const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
6614
- 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>`;
6615
- }
6616
- }
6777
+ this.renderPeek(count, total, pendingCount);
6617
6778
  this.lastTrayCount = count;
6618
6779
  this.lastTrayTotal = total;
6619
6780
  this.opts.onSelectionChange?.(seats);
6620
6781
  }
6782
+ /**
6783
+ * The bottom-sheet's collapsed one-liner. Called from syncTray on every
6784
+ * repaint AND from setCtaPhase: while the widget is securing seats or
6785
+ * opening checkout, the peek is the phone buyer's ONLY line of sight, and a
6786
+ * pill still reading "Continue" at the moment money is about to move is the
6787
+ * emotional trapdoor the audit flagged. The phase lines restate what is
6788
+ * secured and promise no charge yet.
6789
+ */
6790
+ renderPeek(count, total, pendingCount) {
6791
+ if (!this.els.peek) return;
6792
+ if (count && this.ctaPhase === "holding") {
6793
+ this.els.peek.innerHTML = `<span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}</span>`;
6794
+ return;
6795
+ }
6796
+ if (count && this.ctaPhase === "checkout") {
6797
+ this.els.peek.innerHTML = `<span>${(0, import_core2.t)("picker.peekSecured", { count, total: this.money(total) })}</span>`;
6798
+ return;
6799
+ }
6800
+ if (count) {
6801
+ const toCheckout = !!this.hold;
6802
+ this.els.peek.innerHTML = `<span>${(0, import_core2.tCount)("picker.peekTicketsTotal", count, { total: this.money(total) })}</span><button type="button" class="go sl-sheet-go" data-act="${toCheckout ? "checkout" : "open"}">${this.hold ? pendingCount ? this.tf("picker.secureMore", "Secure more") : this.tf("picker.continue", "Continue") : this.tf("picker.review", "Review")}</button>`;
6803
+ } else if (this.salesClosed) {
6804
+ this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
6805
+ } else {
6806
+ const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
6807
+ this.els.peek.innerHTML = (prices.length ? `<span>${(0, import_core2.t)("picker.peekFromPrice", { price: this.money(Math.min(...prices)) })}</span>` : `<span>${this.tf("picker.pickYourSeats", "Pick your seats")}</span>`) + `<button type="button" class="go sl-sheet-go" data-act="open">${this.tf("picker.bestSeatsStar", "\u2726 Best seats")}</button>`;
6808
+ }
6809
+ }
6621
6810
  async removeHeldLabel(label, chip) {
6622
6811
  if (!label || this.releasingLabels.has(label)) return false;
6623
6812
  this.releasingLabels.add(label);
@@ -6628,7 +6817,7 @@ var SeatPicker = class _SeatPicker {
6628
6817
  const preserveAcrossNavigation = this.handedOff;
6629
6818
  const released = await this.controller.releaseLabels([label]);
6630
6819
  if (!released) {
6631
- this.toast(`Couldn't remove ${label}. Your hold is unchanged.`, "error");
6820
+ this.toast((0, import_core2.t)("picker.couldNotRemoveLabel", { label }), "error");
6632
6821
  return false;
6633
6822
  }
6634
6823
  const remaining = this.controller.currentHold();
@@ -6644,7 +6833,7 @@ var SeatPicker = class _SeatPicker {
6644
6833
  }
6645
6834
  this.syncTray();
6646
6835
  this.emitHoldChange();
6647
- this.toast(`${label} removed from your hold.`, "success");
6836
+ this.toast((0, import_core2.t)("picker.labelRemovedFromHold", { label }), "success");
6648
6837
  return true;
6649
6838
  } finally {
6650
6839
  this.releasingLabels.delete(label);
@@ -6658,23 +6847,23 @@ var SeatPicker = class _SeatPicker {
6658
6847
  const button = this.els.holdChange;
6659
6848
  if (button) {
6660
6849
  button.disabled = true;
6661
- button.textContent = "Releasing\u2026";
6850
+ button.textContent = this.tf("picker.releasingEllipsis", "Releasing\u2026");
6662
6851
  }
6663
6852
  try {
6664
6853
  await this.release();
6665
- if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
6854
+ if (!this.hold) this.toast(this.tf("picker.heldTicketsReleased", "Held tickets released. Choose your new seats."), "success");
6666
6855
  } finally {
6667
6856
  this.releasingHold = false;
6668
6857
  if (button?.isConnected) {
6669
6858
  button.disabled = false;
6670
- button.textContent = "Change";
6859
+ button.textContent = this.tf("picker.change", "Change");
6671
6860
  }
6672
6861
  }
6673
6862
  }
6674
6863
  async handleCta() {
6675
6864
  if (this.salesClosed) return;
6676
6865
  if (this.totalTicketCount() > this.maxTickets) {
6677
- this.toast(`Remove tickets until your order has ${this.maxTickets} or fewer.`, "warning");
6866
+ this.toast((0, import_core2.t)("picker.removeTicketsUntilOrFewer", { count: this.maxTickets }), "warning");
6678
6867
  return;
6679
6868
  }
6680
6869
  const committed = this.committedSelection();
@@ -6700,7 +6889,7 @@ var SeatPicker = class _SeatPicker {
6700
6889
  hold = h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : hold;
6701
6890
  }
6702
6891
  if (!hold) {
6703
- this.toast("One or more seats were just taken. Please pick again.", "error");
6892
+ this.toast(this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again."), "error");
6704
6893
  this.setCtaPhase("idle");
6705
6894
  this.syncTray();
6706
6895
  return;
@@ -6717,7 +6906,7 @@ var SeatPicker = class _SeatPicker {
6717
6906
  const problem = err;
6718
6907
  const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
6719
6908
  if (problem.reason === "event_closed") this.setSalesClosed(true);
6720
- const message = problem.reason === "event_closed" ? "Seat sales have closed for this event." : labels.length ? `${labels.join(", ")} ${labels.length === 1 ? "is" : "are"} no longer available. Choose another ${labels.length === 1 ? "seat" : "group"}.` : "One or more seats were just taken. Please pick again.";
6909
+ const message = problem.reason === "event_closed" ? this.tf("picker.seatSalesClosedForEvent", "Seat sales have closed for this event.") : labels.length ? labels.length === 1 ? (0, import_core2.t)("picker.labelsNoLongerAvailable.one", { labels: labels.join(", ") }) : (0, import_core2.t)("picker.labelsNoLongerAvailable.other", { labels: labels.join(", ") }) : this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again.");
6721
6910
  this.toast(message, "error");
6722
6911
  this.setCtaPhase("idle");
6723
6912
  } finally {
@@ -6726,12 +6915,19 @@ var SeatPicker = class _SeatPicker {
6726
6915
  this.syncTray();
6727
6916
  }
6728
6917
  }
6918
+ /** "Yours for m:ss — you won't be charged yet." — the hold note's promise. */
6919
+ holdReassuranceText() {
6920
+ const ms = Math.max(0, this.holdExpiresAt - Date.now());
6921
+ const m = Math.floor(ms / 6e4);
6922
+ const s = String(Math.floor(ms % 6e4 / 1e3)).padStart(2, "0");
6923
+ return (0, import_core2.t)("picker.holdReassurance", { time: `${m}:${s}` });
6924
+ }
6729
6925
  startHoldTimer(expiresAt) {
6730
6926
  this.stopHoldTimer();
6731
6927
  this.holdExpiresAt = expiresAt;
6732
6928
  if (this.hold) this.rememberHold(this.hold);
6733
6929
  const pill = this.els.hold;
6734
- pill.innerHTML = '<span class="sl-hold-dot" aria-hidden="true"></span><span>Held</span><span class="sl-hold-time" data-ref="holdTime"></span>';
6930
+ pill.innerHTML = `<span class="sl-hold-dot" aria-hidden="true"></span><span>${this.tf("picker.held", "Held")}</span><span class="sl-hold-time" data-ref="holdTime"></span>`;
6735
6931
  const time = pill.querySelector('[data-ref="holdTime"]');
6736
6932
  this.els.holdNote?.classList.add("on");
6737
6933
  const tick = () => {
@@ -6739,6 +6935,9 @@ var SeatPicker = class _SeatPicker {
6739
6935
  const m = Math.floor(ms / 6e4);
6740
6936
  const s = String(Math.floor(ms % 6e4 / 1e3)).padStart(2, "0");
6741
6937
  if (time) time.textContent = `${m}:${s}`;
6938
+ if (!this.holdCopyPending && this.els.holdCopy) {
6939
+ this.els.holdCopy.textContent = this.holdReassuranceText();
6940
+ }
6742
6941
  pill.classList.add("on");
6743
6942
  pill.classList.toggle("is-expiring", ms > 0 && ms <= EXTEND_PROMPT_MS);
6744
6943
  this.setExtendPrompt(ms > 0 && ms <= EXTEND_PROMPT_MS, ms);
@@ -6758,8 +6957,10 @@ var SeatPicker = class _SeatPicker {
6758
6957
  setExtendPrompt(show, ms) {
6759
6958
  if (!this.extendEl) return;
6760
6959
  if (show && this.controller.currentHold() && !this.bookedShown) {
6761
- const secs = Math.ceil(ms / 1e3);
6762
- this.els.extendTxt.innerHTML = `Your seats are held for <b>0:${String(secs).padStart(2, "0")}</b>. Need more time?`;
6960
+ const totalSecs = Math.ceil(ms / 1e3);
6961
+ const m = Math.floor(totalSecs / 60);
6962
+ const s = String(totalSecs % 60).padStart(2, "0");
6963
+ this.els.extendTxt.innerHTML = (0, import_core2.t)("picker.seatsHeldForNeedMoreTime", { time: `${m}:${s}` });
6763
6964
  this.extendEl.classList.add("on");
6764
6965
  } else {
6765
6966
  this.extendEl.classList.remove("on");
@@ -6769,7 +6970,7 @@ var SeatPicker = class _SeatPicker {
6769
6970
  const btn = this.els.extendBtn;
6770
6971
  btn.disabled = true;
6771
6972
  const prev = btn.textContent;
6772
- btn.textContent = "Adding\u2026";
6973
+ btn.textContent = this.tf("picker.addingEllipsis", "Adding\u2026");
6773
6974
  try {
6774
6975
  const h = await this.controller.extendHold(this.opts.holdTtlMs);
6775
6976
  if (h) {
@@ -6778,13 +6979,13 @@ var SeatPicker = class _SeatPicker {
6778
6979
  this.extendEl?.classList.remove("on");
6779
6980
  this.rememberHold(this.hold);
6780
6981
  this.emitHoldChange();
6781
- this.toast("More time added \u2014 your seats are still held.", "success");
6982
+ this.toast(this.tf("picker.moreTimeAdded", "More time added \u2014 your seats are still held."), "success");
6782
6983
  } else {
6783
- this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
6984
+ this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
6784
6985
  }
6785
6986
  } catch (err) {
6786
6987
  this.opts.onError?.(err);
6787
- this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
6988
+ this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
6788
6989
  } finally {
6789
6990
  btn.disabled = false;
6790
6991
  btn.textContent = prev;
@@ -6811,7 +7012,7 @@ var SeatPicker = class _SeatPicker {
6811
7012
  this.forgetHold();
6812
7013
  const n = handoff.lineItems.reduce((sum, i) => sum + i.quantity, 0);
6813
7014
  if (this.els.bookedSub) {
6814
- this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${n} ${n === 1 ? "ticket" : "tickets"}</span> confirmed. A confirmation is on its way.`;
7015
+ this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${(0, import_core2.tCount)("picker.ticketsCount", n)}</span> ${this.tf("picker.confirmedAndOnWay", "confirmed. A confirmation is on its way.")}`;
6815
7016
  }
6816
7017
  this.bookedEl?.classList.add("on");
6817
7018
  this.opts.onBooked?.(handoff);
@@ -6893,7 +7094,7 @@ var SeatPicker = class _SeatPicker {
6893
7094
  ({ mountCheckout: mountCheckout2 } = await loadHostedCheckout());
6894
7095
  } catch (err) {
6895
7096
  this.opts.onError?.(err);
6896
- this.toast("Checkout could not be opened. Your seats are still held \u2014 please try again.", "error");
7097
+ this.toast(this.tf("picker.checkoutCouldNotBeOpened", "Checkout could not be opened. Your seats are still held \u2014 please try again."), "error");
6897
7098
  this.setCtaPhase("idle");
6898
7099
  return;
6899
7100
  }
@@ -7008,9 +7209,9 @@ var SeatPicker = class _SeatPicker {
7008
7209
  rowTypeWord(details) {
7009
7210
  const authored = details?.displayType?.trim() || details?.rowType?.trim();
7010
7211
  if (authored) return authored;
7011
- if (details?.objectType === "table") return "Table";
7012
- if (details?.objectType === "booth") return "Booth";
7013
- return "Row";
7212
+ if (details?.objectType === "table") return this.tf("picker.table", "Table");
7213
+ if (details?.objectType === "booth") return this.tf("picker.booth", "Booth");
7214
+ return this.tf("picker.row", "Row");
7014
7215
  }
7015
7216
  rowShort(details) {
7016
7217
  const row = details?.rowLabel;
@@ -7033,7 +7234,7 @@ var SeatPicker = class _SeatPicker {
7033
7234
  const isGroupedTable = details.objectType === "table" && !!details.bookingMode;
7034
7235
  const isBooth = details.objectType === "booth";
7035
7236
  const hasLoc = details.sectionLabel || details.rowLabel || details.seatNumber;
7036
- const grid = isGroupedTable ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div><div class="sl-tip-field"><span class="sl-tip-key">Guests</span><span class="sl-tip-val">${details.bookingMode === "variable" ? `${details.minOccupancy}\u2013${details.maxOccupancy}` : details.capacity}</span></div></div>` : isBooth ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">Section</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div></div>` : hasLoc ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">Section</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + (details.rowLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(this.rowShort(details))}</span></div>` : "") + (details.seatNumber ? `<div class="sl-tip-field"><span class="sl-tip-key">Seat</span><span class="sl-tip-val">${esc(details.seatNumber)}</span></div>` : "") + `</div>` : `<div class="sl-tip-grid one"><div class="sl-tip-field"><span class="sl-tip-key">Seat</span><span class="sl-tip-val">${esc(details.displayLabel ?? details.label)}</span></div></div>`;
7237
+ const grid = isGroupedTable ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div><div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.guests", "Guests")}</span><span class="sl-tip-val">${details.bookingMode === "variable" ? `${details.minOccupancy}\u2013${details.maxOccupancy}` : details.capacity}</span></div></div>` : isBooth ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.section", "Section")}</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div></div>` : hasLoc ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.section", "Section")}</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + (details.rowLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(this.rowShort(details))}</span></div>` : "") + (details.seatNumber ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-tip-val">${esc(details.seatNumber)}</span></div>` : "") + `</div>` : `<div class="sl-tip-grid one"><div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-tip-val">${esc(details.displayLabel ?? details.label)}</span></div></div>`;
7037
7238
  const statusLine = details.status === "free" ? "" : `<div class="sl-tip-status">${details.status === "held" ? (0, import_core2.t)("map.statusHeld") : (0, import_core2.t)("map.statusTaken")}</div>`;
7038
7239
  const limited = this.limitedViewLabel(details.commercial);
7039
7240
  const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc(limited)}</div>` : "";
@@ -7324,7 +7525,7 @@ var SeatPicker = class _SeatPicker {
7324
7525
  const identity = details?.displayLabel ?? seat.displayLabel ?? seat.label;
7325
7526
  const section = details?.sectionLabel;
7326
7527
  const row = this.rowShort(details);
7327
- const location2 = [section, row ? `Row ${row}` : null, identity].filter(Boolean).join(" \xB7 ");
7528
+ const location2 = [section, row ? (0, import_core2.t)("picker.rowLabel", { label: row }) : null, identity].filter(Boolean).join(" \xB7 ");
7328
7529
  const copy = status === "held" ? {
7329
7530
  title: this.tf("picker.temporarilyHeld", "Temporarily held"),
7330
7531
  message: this.tf("picker.heldSeatExplanation", "Another buyer is holding this seat. It may become available again.")
@@ -7420,7 +7621,7 @@ var SeatPicker = class _SeatPicker {
7420
7621
  chip = document.createElement("div");
7421
7622
  chip.className = "sl-view3d-compare-saved";
7422
7623
  chip.setAttribute("role", "group");
7423
- chip.setAttribute("aria-label", "Saved seat comparison");
7624
+ chip.setAttribute("aria-label", this.tf("picker.savedSeatComparison", "Saved seat comparison"));
7424
7625
  const main2 = document.createElement("button");
7425
7626
  main2.type = "button";
7426
7627
  main2.className = "main";
@@ -7432,7 +7633,7 @@ var SeatPicker = class _SeatPicker {
7432
7633
  clear.type = "button";
7433
7634
  clear.className = "clear";
7434
7635
  clear.textContent = "\xD7";
7435
- clear.setAttribute("aria-label", "Clear saved seat comparison");
7636
+ clear.setAttribute("aria-label", this.tf("picker.clearSavedSeatComparison", "Clear saved seat comparison"));
7436
7637
  clear.addEventListener("click", () => this.clearView3dComparison());
7437
7638
  chip.append(main2, clear);
7438
7639
  overlay.appendChild(chip);
@@ -7441,8 +7642,8 @@ var SeatPicker = class _SeatPicker {
7441
7642
  const count = this.view3dCompareSeatIds.length;
7442
7643
  const main = chip.querySelector(".main");
7443
7644
  if (main) {
7444
- main.textContent = count > 1 ? `Compare ${count}` : "1 seat saved";
7445
- main.setAttribute("aria-label", count > 1 ? `Open comparison of ${count} seats` : "One seat saved; choose another to compare");
7645
+ main.textContent = count > 1 ? (0, import_core2.t)("picker.compareCount", { count }) : this.tf("picker.oneSeatSaved", "1 seat saved");
7646
+ main.setAttribute("aria-label", count > 1 ? (0, import_core2.t)("picker.openComparisonOfSeats", { count }) : this.tf("picker.oneSeatSavedChooseAnother", "One seat saved; choose another to compare"));
7446
7647
  }
7447
7648
  }
7448
7649
  view3dComparisonSnapshot(seatId) {
@@ -7719,8 +7920,7 @@ var SeatPicker = class _SeatPicker {
7719
7920
  const wantFloors = floors.length > 1;
7720
7921
  const wantZones = zones.length > 1;
7721
7922
  const wantSections = sections.length > 1;
7722
- const wantLocator = allSections.length > 0 && handle.rows().length > 0;
7723
- if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
7923
+ if (!wantFloors && !wantZones && !wantSections) return;
7724
7924
  const nav = document.createElement("div");
7725
7925
  nav.className = "sl-view3d-nav";
7726
7926
  const finderToggle = document.createElement("button");
@@ -7730,7 +7930,7 @@ var SeatPicker = class _SeatPicker {
7730
7930
  const setFinderOpen = (open) => {
7731
7931
  nav.classList.toggle("is-open", open);
7732
7932
  finderToggle.setAttribute("aria-expanded", String(open));
7733
- finderToggle.textContent = open ? "Close seat finder" : "Find a seat";
7933
+ finderToggle.textContent = open ? this.tf("picker.closeVenueNav", "Close") : wantFloors && (wantZones || wantSections) ? this.tf("picker.levelsAndAreas", "Levels & areas") : wantFloors ? this.tf("picker.levels", "Levels") : this.tf("picker.areas", "Areas");
7734
7934
  };
7735
7935
  finderToggle.addEventListener("click", () => setFinderOpen(!nav.classList.contains("is-open")));
7736
7936
  nav.addEventListener("keydown", (event) => {
@@ -7767,7 +7967,7 @@ var SeatPicker = class _SeatPicker {
7767
7967
  row.appendChild(b);
7768
7968
  };
7769
7969
  add(this.tf("picker.allLevels", "All levels"), null);
7770
- for (const f of floors) add(f.label || `Level ${f.index + 1}`, f.index);
7970
+ for (const f of floors) add(f.label || (0, import_core2.t)("picker.levelNumber", { number: f.index + 1 }), f.index);
7771
7971
  nav.appendChild(row);
7772
7972
  }
7773
7973
  if (wantZones || wantSections) {
@@ -7810,86 +8010,6 @@ var SeatPicker = class _SeatPicker {
7810
8010
  }
7811
8011
  nav.appendChild(row);
7812
8012
  }
7813
- if (wantLocator) {
7814
- const locator = document.createElement("div");
7815
- locator.className = "sl-view3d-locator";
7816
- locator.setAttribute("role", "group");
7817
- locator.setAttribute("aria-label", "Find an exact seat in 3D");
7818
- const sectionSelect = document.createElement("select");
7819
- sectionSelect.setAttribute("aria-label", "Choose section in 3D");
7820
- const rowSelect = document.createElement("select");
7821
- rowSelect.setAttribute("aria-label", "Choose row in 3D");
7822
- const seatSelect = document.createElement("select");
7823
- seatSelect.setAttribute("aria-label", "Choose seat in 3D");
7824
- const view = document.createElement("button");
7825
- view.type = "button";
7826
- view.textContent = "Inspect seat";
7827
- view.disabled = true;
7828
- const fill = (select, placeholder, entries) => {
7829
- select.replaceChildren();
7830
- const first = document.createElement("option");
7831
- first.value = "";
7832
- first.textContent = placeholder;
7833
- select.appendChild(first);
7834
- for (const entry of entries) {
7835
- const option = document.createElement("option");
7836
- option.value = entry.id;
7837
- option.textContent = entry.label;
7838
- select.appendChild(option);
7839
- }
7840
- select.value = "";
7841
- };
7842
- fill(sectionSelect, "1. Section", allSections.map((section) => ({
7843
- id: section.id,
7844
- label: `${section.label} \xB7 ${section.seatCount.toLocaleString()} seats`
7845
- })));
7846
- fill(rowSelect, "2. Row", []);
7847
- fill(seatSelect, "3. Seat", []);
7848
- rowSelect.disabled = true;
7849
- seatSelect.disabled = true;
7850
- sectionSelect.addEventListener("change", () => {
7851
- const sectionId = sectionSelect.value;
7852
- view.disabled = true;
7853
- fill(seatSelect, "3. Seat", []);
7854
- seatSelect.disabled = true;
7855
- if (!sectionId || !handle.focusSection(sectionId)) {
7856
- fill(rowSelect, "2. Row", []);
7857
- rowSelect.disabled = true;
7858
- return;
7859
- }
7860
- const rows = handle.rows(sectionId);
7861
- fill(rowSelect, "2. Row", rows.map((row) => ({
7862
- id: row.id,
7863
- label: `${row.label} \xB7 ${row.seatCount} seats`
7864
- })));
7865
- rowSelect.disabled = rows.length === 0;
7866
- });
7867
- rowSelect.addEventListener("change", () => {
7868
- const rowId = rowSelect.value;
7869
- view.disabled = true;
7870
- if (!rowId || !handle.focusRow(rowId)) {
7871
- fill(seatSelect, "3. Seat", []);
7872
- seatSelect.disabled = true;
7873
- return;
7874
- }
7875
- const seats = handle.seatsInRow(rowId);
7876
- fill(seatSelect, "3. Seat", seats);
7877
- seatSelect.disabled = seats.length === 0;
7878
- });
7879
- seatSelect.addEventListener("change", () => {
7880
- const seatId = seatSelect.value;
7881
- view.disabled = !seatId;
7882
- });
7883
- view.addEventListener("click", () => {
7884
- const seatId = seatSelect.value;
7885
- if (seatId) {
7886
- setFinderOpen(false);
7887
- this.onView3dSeatPick(seatId);
7888
- }
7889
- });
7890
- locator.append(sectionSelect, rowSelect, seatSelect, view);
7891
- nav.appendChild(locator);
7892
- }
7893
8013
  overlay.appendChild(nav);
7894
8014
  }
7895
8015
  async enter3d(flySeatId) {
@@ -7905,7 +8025,7 @@ var SeatPicker = class _SeatPicker {
7905
8025
  const overlay = document.createElement("div");
7906
8026
  overlay.className = "sl-view3d";
7907
8027
  overlay.setAttribute("role", "group");
7908
- overlay.setAttribute("aria-label", "Interactive 3D venue view");
8028
+ overlay.setAttribute("aria-label", this.tf("picker.interactive3dVenueView", "Interactive 3D venue view"));
7909
8029
  const back = document.createElement("button");
7910
8030
  back.type = "button";
7911
8031
  back.className = "sl-view3d-back";
@@ -7931,7 +8051,7 @@ var SeatPicker = class _SeatPicker {
7931
8051
  fullscreen.type = "button";
7932
8052
  fullscreen.className = "sl-view3d-fs";
7933
8053
  fullscreen.textContent = "\u26F6";
7934
- fullscreen.setAttribute("aria-label", "Full screen");
8054
+ fullscreen.setAttribute("aria-label", this.tf("picker.fullScreen", "Full screen"));
7935
8055
  fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
7936
8056
  fullscreen.addEventListener("click", () => this.toggleFullscreen());
7937
8057
  overlay.appendChild(fullscreen);
@@ -7968,7 +8088,7 @@ var SeatPicker = class _SeatPicker {
7968
8088
  onSeatInspect: (id) => this.onView3dSeatPick(id),
7969
8089
  onSectionFocusChange: (sectionId) => {
7970
8090
  const select = overlay.querySelector(
7971
- 'select[aria-label="Choose section in 3D"]'
8091
+ 'select[data-locator="section"]'
7972
8092
  );
7973
8093
  const next = sectionId ?? "";
7974
8094
  if (!select || select.value === next) return;
@@ -8065,7 +8185,7 @@ var SeatPicker = class _SeatPicker {
8065
8185
  if (button) {
8066
8186
  button.disabled = true;
8067
8187
  button.classList.add("sl-busy");
8068
- button.innerHTML = '<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026';
8188
+ button.innerHTML = `<span class="sl-ba-spin" aria-hidden="true"></span>${this.tf("picker.findingEllipsis", "Finding\u2026")}`;
8069
8189
  }
8070
8190
  try {
8071
8191
  const h = await this.controller.bestAvailable(qty, categoryKey, { ...opts, ttlMs: this.opts.holdTtlMs });
@@ -8087,7 +8207,7 @@ var SeatPicker = class _SeatPicker {
8087
8207
  } catch (err) {
8088
8208
  this.opts.onError?.(err);
8089
8209
  const reason = err?.reason;
8090
- const message = reason === "not_enough_together" ? `We couldn't find ${qty} seats together. Try fewer seats or another ticket type.` : reason === "sold_out" ? "That ticket type is sold out. Try another ticket type." : reason === "event_closed" ? "Seat sales have closed for this event." : "Those seats are no longer available. Try another quantity or ticket type.";
8210
+ const message = reason === "not_enough_together" ? (0, import_core2.t)("picker.couldNotFindSeatsTogether", { count: qty }) : reason === "sold_out" ? this.tf("picker.ticketTypeSoldOut", "That ticket type is sold out. Try another ticket type.") : reason === "event_closed" ? this.tf("picker.seatSalesClosedForEvent", "Seat sales have closed for this event.") : this.tf("picker.seatsNoLongerAvailableTryAnother", "Those seats are no longer available. Try another quantity or ticket type.");
8091
8211
  this.toast(message, "error");
8092
8212
  return null;
8093
8213
  } finally {
@@ -8116,7 +8236,7 @@ var SeatPicker = class _SeatPicker {
8116
8236
  }
8117
8237
  }
8118
8238
  if (!released) {
8119
- this.toast("Couldn't release your tickets. Your hold is unchanged.", "error");
8239
+ this.toast(this.tf("picker.couldNotReleaseTickets", "Couldn't release your tickets. Your hold is unchanged."), "error");
8120
8240
  return;
8121
8241
  }
8122
8242
  this.hold = null;