@seatlayer/core 0.50.2 → 0.52.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.
Files changed (40) hide show
  1. package/dist/{chunk-GD267BFB.js → chunk-UID7KN44.js} +75 -2
  2. package/dist/chunk-UID7KN44.js.map +1 -0
  3. package/dist/core/seatConfidence.d.cts +1 -1
  4. package/dist/core/seatConfidence.d.ts +1 -1
  5. package/dist/de-YGODYJ3E.js +292 -0
  6. package/dist/de-YGODYJ3E.js.map +1 -0
  7. package/dist/es-VMBTGVFQ.js +292 -0
  8. package/dist/es-VMBTGVFQ.js.map +1 -0
  9. package/dist/fr-SLBE2ULB.js +292 -0
  10. package/dist/fr-SLBE2ULB.js.map +1 -0
  11. package/dist/index.cjs +1606 -64
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +138 -17
  14. package/dist/index.d.ts +138 -17
  15. package/dist/index.js +829 -65
  16. package/dist/index.js.map +1 -1
  17. package/dist/picker/minimapGeometry.cjs +80 -0
  18. package/dist/picker/minimapGeometry.cjs.map +1 -0
  19. package/dist/picker/minimapGeometry.d.cts +37 -0
  20. package/dist/picker/minimapGeometry.d.ts +37 -0
  21. package/dist/picker/minimapGeometry.js +52 -0
  22. package/dist/picker/minimapGeometry.js.map +1 -0
  23. package/dist/{types-DR54nqKL.d.cts → types-tLOunI-B.d.cts} +131 -1
  24. package/dist/{types-DR54nqKL.d.ts → types-tLOunI-B.d.ts} +131 -1
  25. package/dist/view3d/crossfade/panorama.d.cts +1 -1
  26. package/dist/view3d/crossfade/panorama.d.ts +1 -1
  27. package/dist/view3d/index.cjs +1158 -123
  28. package/dist/view3d/index.cjs.map +1 -1
  29. package/dist/view3d/index.d.cts +20 -12
  30. package/dist/view3d/index.d.ts +20 -12
  31. package/dist/view3d/index.js +1158 -122
  32. package/dist/view3d/index.js.map +1 -1
  33. package/package.json +14 -1
  34. package/dist/chunk-GD267BFB.js.map +0 -1
  35. package/dist/de-ST4H423D.js +0 -56
  36. package/dist/de-ST4H423D.js.map +0 -1
  37. package/dist/es-WEC5NHRT.js +0 -56
  38. package/dist/es-WEC5NHRT.js.map +0 -1
  39. package/dist/fr-SGBFNAQV.js +0 -56
  40. package/dist/fr-SGBFNAQV.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -45,6 +45,8 @@ var init_es = __esm({
45
45
  "picker.poweredBy": "Con la tecnolog\xEDa de SeatLayer",
46
46
  "picker.testMode": "MODO DE PRUEBA",
47
47
  "picker.orphanHint": "Esto deja un asiento aislado \u2014 considera desplazarte un asiento.",
48
+ "picker.companionRequiresWheelchair": "Requiere la plaza accesible contigua",
49
+ "picker.companionPairRequired": "Selecciona la plaza accesible contigua con cada entrada de acompa\xF1ante.",
48
50
  // renderer (drawn on the Konva map — shared by the embed SDK)
49
51
  "map.aria": "Mapa de asientos. Usa las flechas para moverte entre asientos, Intro para seleccionar.",
50
52
  "map.seatsLeft": "{count} LIBRES",
@@ -83,7 +85,241 @@ var init_es = __esm({
83
85
  "picker.premiumFallbackNote": "No hay un bloque premium de {count}: mostrando los mejores disponibles",
84
86
  "picker.loading3d": "Creando el recinto en 3D\u2026",
85
87
  "picker.unavailable3d": "No se pudo iniciar la vista 3D. El mapa de asientos sigue disponible.",
86
- "picker.jumpToSection": "Ir a una secci\xF3n"
88
+ "picker.jumpToSection": "Ir a una secci\xF3n",
89
+ "picker.salesClosedPill": "Las ventas est\xE1n cerradas",
90
+ "picker.salesClosedCopy": "La venta de entradas para este evento ha finalizado.",
91
+ "picker.salesClosedCta": "Venta cerrada",
92
+ "picker.salesClosedToast": "Las ventas de este evento est\xE1n cerradas.",
93
+ "picker.holdReassurance": "Tuyos durante {time} \u2014 a\xFAn no se te cobrar\xE1 nada.",
94
+ "picker.securingSeats": "Asegurando tus asientos\u2026",
95
+ "picker.openingCheckout": "Abriendo el pago seguro\u2026",
96
+ "picker.peekSecured": "\u2713 {count} asegurados \xB7 {total} \u2014 a\xFAn no se te cobrar\xE1 nada",
97
+ "picker.priceHint": "Los colores del plano son tipos de entrada \u2014 toca uno abajo para ver solo esos asientos.",
98
+ "picker.findTogetherInstead": "Buscar asientos juntos en su lugar",
99
+ "picker.keepMyPicks": "Mantener mi selecci\xF3n",
100
+ // picker widget — buyer-facing SeatPicker.ts strings (localization pass)
101
+ "picker.accessExpiredBody": "Vuelve a iniciar sesi\xF3n o recarga la p\xE1gina para seguir viendo estos asientos. Lo que ya tengas retenido seguir\xE1 siendo tuyo.",
102
+ "picker.accessExpiredTitle": "Tu sesi\xF3n de acceso ha finalizado",
103
+ "picker.accessInvalidBody": "A\xFAn puedes reservar todo lo que aparezca como disponible. Contacta a quien te envi\xF3 este enlace para acceder al resto.",
104
+ "picker.accessInvalidTitle": "No pudimos verificar tu acceso",
105
+ "picker.accessPausedBody": "El organizador ha pausado esta selecci\xF3n. Int\xE9ntalo de nuevo en unos minutos.",
106
+ "picker.accessPausedTitle": "Estos asientos est\xE1n retenidos en este momento",
107
+ "picker.accessRetry": "Intentar de nuevo",
108
+ "picker.accessRevokedBody": "Pide a quien te envi\xF3 aqu\xED un nuevo enlace para seguir reservando estos asientos.",
109
+ "picker.accessRevokedTitle": "Este enlace de acceso ya no est\xE1 activo",
110
+ "picker.accessiblePhysicalSeat": "Asiento f\xEDsico accesible",
111
+ "picker.addTime": "A\xF1adir tiempo",
112
+ "picker.addingEllipsis": "A\xF1adiendo\u2026",
113
+ "picker.allLevels": "Todos los niveles",
114
+ "picker.allPlacesBookedTogether": "Los {count} lugares se reservan juntos como una mesa exclusiva.",
115
+ "picker.allPrices": "Todos los precios",
116
+ "picker.allSeats": "Todos los asientos",
117
+ "picker.allSetTitle": "Todo listo",
118
+ "picker.anyTicketType": "Cualquier tipo de entrada",
119
+ "picker.anyVenueZone": "Cualquier zona del recinto",
120
+ "picker.areas": "Zonas",
121
+ "picker.available": "Disponible",
122
+ "picker.backToMap": "Volver al mapa",
123
+ "picker.backToVenue": "Volver al recinto",
124
+ "picker.bestSeatsStar": "\u2726 Mejores asientos",
125
+ "picker.booth": "Palco",
126
+ "picker.capacityGuests": "{count} invitados",
127
+ "picker.change": "Cambiar",
128
+ "picker.chartDerivedModel": "Modelo derivado del plano",
129
+ "picker.chartDerivedSeatEye": "3D en vivo \xB7 vista desde el asiento derivada del plano \xB7 no verificada",
130
+ "picker.checkConnection": "Comprueba tu conexi\xF3n e int\xE9ntalo de nuevo.",
131
+ "picker.checkoutCouldNotBeOpened": "No se pudo abrir el pago. Tus asientos siguen retenidos \u2014 int\xE9ntalo de nuevo.",
132
+ "picker.chooseAnotherToCompare": "Elige otro asiento para comparar.",
133
+ "picker.chooseGuestsCopy": "Elige cu\xE1ntos invitados se sentar\xE1n juntos. Esta mesa est\xE1 reservada en exclusiva para tu grupo.",
134
+ "picker.chooseMinMaxGuests": "Elige entre {min} y {max} invitados",
135
+ "picker.clearSavedSeatComparison": "Borrar comparaci\xF3n de asientos guardada",
136
+ "picker.close": "Cerrar",
137
+ "picker.closeSeatStatus": "Cerrar estado del asiento",
138
+ "picker.closestGroupChosenInstantly": "El grupo disponible m\xE1s cercano, elegido al instante.",
139
+ "picker.collapseTicketPanel": "Contraer el panel de entradas",
140
+ "picker.compareCount": "Comparar {count}",
141
+ "picker.compareWithSaved": "Comparar con el guardado",
142
+ "picker.confirmOrCancelSeat": "Confirma o cancela este asiento",
143
+ "picker.confirmSeatLabel": "Confirmar asiento {label}",
144
+ "picker.confirmYourTable": "Confirma tu mesa",
145
+ "picker.confirmedAndOnWay": "confirmados. Una confirmaci\xF3n est\xE1 en camino.",
146
+ "picker.continue": "Continuar",
147
+ "picker.continueToCheckout": "Continuar al pago",
148
+ "picker.couldNotAddMoreTime": "No se pudo a\xF1adir m\xE1s tiempo \u2014 dir\xEDgete al pago ahora.",
149
+ "picker.couldNotFindSeatsTogether": "No pudimos encontrar {count} asientos juntos. Prueba con menos asientos u otro tipo de entrada.",
150
+ "picker.couldNotReleaseTickets": "No se pudieron liberar tus entradas. Tu retenci\xF3n no ha cambiado.",
151
+ "picker.couldNotRemoveLabel": "No se pudo quitar {label}. Tu retenci\xF3n no ha cambiado.",
152
+ "picker.currentOffer": "Oferta actual",
153
+ "picker.currentTicketOffer": "Oferta de entradas actual",
154
+ "picker.dragToLookAround": "Arrastra para mirar alrededor \xB7 despl\xE1zate para hacer zoom",
155
+ "picker.emptyWheelchairSpace": "Espacio vac\xEDo para silla de ruedas",
156
+ "picker.exitFullScreen": "Salir de pantalla completa",
157
+ "picker.fewer": "Menos",
158
+ "picker.fewerGuests": "Menos invitados",
159
+ "picker.fewerSeats": "Menos asientos",
160
+ "picker.filterAndFocusByPrice": "Filtrar y enfocar asientos por precio",
161
+ "picker.filters": "Filtros",
162
+ "picker.findBestSeatsCount.one": "Buscar {count} mejor asiento",
163
+ "picker.findBestSeatsCount.other": "Buscar {count} mejores asientos",
164
+ "picker.findBestSeatsTogether": "Buscar los mejores asientos juntos",
165
+ "picker.findNewSeats": "Buscar nuevos asientos",
166
+ "picker.findingBestSeats": "Buscando los mejores asientos\u2026",
167
+ "picker.findingEllipsis": "Buscando\u2026",
168
+ "picker.fitToScreen": "Ajustar a la pantalla",
169
+ "picker.flat2dMap": "Mapa plano 2D",
170
+ "picker.flexiblePartyTypeWord": "Grupo flexible \xB7 {typeWord}",
171
+ "picker.fullScreen": "Pantalla completa",
172
+ "picker.generalAdmission": "Entrada general",
173
+ "picker.guestCountCouldNotBeSecured": "No se pudo asegurar ese n\xFAmero de invitados. Tu retenci\xF3n de mesa actual no ha cambiado.",
174
+ "picker.guestCountNoLongerAvailable": "Ese n\xFAmero de invitados ya no est\xE1 disponible. Tu retenci\xF3n actual no ha cambiado.",
175
+ "picker.guests": "Invitados",
176
+ "picker.guestsCount": "{count} invitados",
177
+ "picker.guestsCountEdit": "{count} invitados \xB7 Editar",
178
+ "picker.held": "Retenido",
179
+ "picker.heldForYou": "Retenido para ti",
180
+ "picker.heldSeatExplanation": "Otro comprador est\xE1 reteniendo este asiento. Puede volver a estar disponible.",
181
+ "picker.heldTicketsReleased": "Se liberaron las entradas retenidas. Elige tus nuevos asientos.",
182
+ "picker.heldTicketsRestored": "Tus entradas retenidas se han restaurado.",
183
+ "picker.hidePanel": "Ocultar panel",
184
+ "picker.hideTicketPanel": "Ocultar el panel de entradas",
185
+ "picker.holdSeatsAndCheckout": "Retener asientos y pagar",
186
+ "picker.howOfferWorks": "C\xF3mo funciona la oferta \xAB{name}\xBB",
187
+ "picker.interactive3dVenueView": "Vista 3D interactiva del recinto",
188
+ "picker.keepMine": "Mantener los m\xEDos",
189
+ "picker.labelNoLongerAvailable": "{label} ya no est\xE1 disponible.",
190
+ "picker.labelRemoved": "{label} eliminado.",
191
+ "picker.labelRemovedFromHold": "{label} eliminado de tu retenci\xF3n.",
192
+ "picker.labelRestored": "{label} restaurado.",
193
+ "picker.labelsNoLongerAvailable.one": "{labels} ya no est\xE1 disponible. Elige otro asiento.",
194
+ "picker.labelsNoLongerAvailable.other": "{labels} ya no est\xE1n disponibles. Elige otro grupo.",
195
+ "picker.leftCount.one": "quedan {count}",
196
+ "picker.leftCount.other": "quedan {count}",
197
+ "picker.levelNumber": "Nivel {number}",
198
+ "picker.levels": "Niveles",
199
+ "picker.liveAvailability": "Disponibilidad en vivo \u2014 los asientos se actualizan en tiempo real",
200
+ "picker.loadingSeatMap": "Cargando el mapa de asientos\u2026",
201
+ "picker.lookAroundLive3d": "Mirar alrededor en 3D en vivo",
202
+ "picker.manualTicketsRemovedNote": "Tus entradas seleccionadas manualmente solo se eliminar\xE1n una vez que se asegure un nuevo grupo.",
203
+ "picker.map": "Mapa",
204
+ "picker.mapDidNotLoad": "El mapa de asientos no se pudo cargar",
205
+ "picker.maxTicketsForOrder": "Puedes seleccionar hasta {count} entradas para este pedido.",
206
+ "picker.minToMaxGuests": "De {min} a {max} invitados",
207
+ "picker.more": "M\xE1s",
208
+ "picker.moreGuests": "M\xE1s invitados",
209
+ "picker.moreSeats": "M\xE1s asientos",
210
+ "picker.moreSelectedCount": "{count} m\xE1s seleccionados",
211
+ "picker.moreTimeAdded": "Se a\xF1adi\xF3 m\xE1s tiempo \u2014 tus asientos siguen retenidos.",
212
+ "picker.noAccessibilityMetadata": "No hay informaci\xF3n de accesibilidad disponible",
213
+ "picker.noAuthoredRestriction": "Sin restricci\xF3n indicada por el organizador",
214
+ "picker.noSeatsSelected": "Ning\xFAn asiento seleccionado",
215
+ "picker.notChargedYet": "A\xFAn no se te cobrar\xE1 nada.",
216
+ "picker.notForSale": "No est\xE1 a la venta",
217
+ "picker.notForSaleExplanation": "Este asiento no est\xE1 incluido en la venta actual.",
218
+ "picker.numberOfGuests": "N\xFAmero de invitados",
219
+ "picker.offerDetailActive": "Este precio se aplica autom\xE1ticamente a los asientos elegibles. Las entradas en carritos activos reducen temporalmente la cantidad disponible; las retenciones liberadas o vencidas la devuelven. Cuando la oferta termine, se aplicar\xE1 la siguiente oferta coincidente o el precio normal de la entrada.",
220
+ "picker.offerDetailUpcoming": "Las entradas est\xE1n disponibles ahora a su precio normal. Esta oferta programada se aplicar\xE1 autom\xE1ticamente a los asientos elegibles cuando comience.",
221
+ "picker.offerRemainingAvailable": "{count} disponibles",
222
+ "picker.offerStarts": "comienza {time}",
223
+ "picker.offerUntil": "hasta {time}",
224
+ "picker.oneSeatSaved": "1 asiento guardado",
225
+ "picker.oneSeatSavedChooseAnother": "Un asiento guardado; elige otro para comparar",
226
+ "picker.openAuthored360": "Abrir vista 360\xB0 del recinto",
227
+ "picker.openComparison": "Abrir comparaci\xF3n",
228
+ "picker.openComparisonOfSeats": "Abrir comparaci\xF3n de {count} asientos",
229
+ "picker.openTicketPanel": "Abrir el panel de entradas",
230
+ "picker.peekFromPrice": "Desde {price}",
231
+ "picker.peekTicketsTotal.one": "{count} entrada \xB7 {total}",
232
+ "picker.peekTicketsTotal.other": "{count} entradas \xB7 {total}",
233
+ "picker.perGuestPrice": "{price} por invitado",
234
+ "picker.pickYourSeats": "Elige tus asientos",
235
+ "picker.preferredTicketType": "Tipo de entrada preferido",
236
+ "picker.preferredVenueZone": "Zona del recinto preferida",
237
+ "picker.priceNotSupplied": "Precio no indicado",
238
+ "picker.releaseHeldTickets": "Liberar entradas retenidas y elegir otros asientos",
239
+ "picker.releasingEllipsis": "Liberando\u2026",
240
+ "picker.removeHeldTicketLabel": "Quitar entrada retenida {label}",
241
+ "picker.removeSeatLabel": "Quitar {label}",
242
+ "picker.removeTicketsUntilOrFewer": "Quita entradas hasta que tu pedido tenga {count} o menos.",
243
+ "picker.replaceCurrentChoices": "\xBFReemplazar tu selecci\xF3n actual?",
244
+ "picker.review": "Revisar",
245
+ "picker.row": "Fila",
246
+ "picker.rowLabel": "Fila {label}",
247
+ "picker.saveToCompare": "Guardar para comparar",
248
+ "picker.savedForComparison": "Guardado para comparar",
249
+ "picker.savedSeatComparison": "Comparaci\xF3n de asientos guardada",
250
+ "picker.scheduledOffer": "Oferta programada",
251
+ "picker.seat": "Asiento",
252
+ "picker.seatCount.one": "{count} asiento",
253
+ "picker.seatCount.other": "{count} asientos",
254
+ "picker.seatJustTakenByAnother": "El asiento {label} acaba de ser tomado por otro comprador.",
255
+ "picker.seatLabel": "Asiento {label}",
256
+ "picker.seatNoLongerAvailable": "Ese asiento ya no est\xE1 disponible.",
257
+ "picker.seatNoLongerYours": "Algunos asientos ya no est\xE1n disponibles para ti. Se han eliminado de tu pedido.",
258
+ "picker.seatNumberLower": "asiento {label}",
259
+ "picker.seatSalesClosedForEvent": "La venta de asientos para este evento ha finalizado.",
260
+ "picker.seatSelection": "Selecci\xF3n de asientos",
261
+ "picker.seatStatusLegend": "Leyenda del estado de los asientos",
262
+ "picker.seatTaken": "Otro comprador tom\xF3 un asiento que hab\xEDas elegido. Se ha eliminado de tu pedido.",
263
+ "picker.seatsHeldForNeedMoreTime": "Tus asientos est\xE1n retenidos durante <b>{time}</b>. \xBFNecesitas m\xE1s tiempo?",
264
+ "picker.seatsJustTaken": "Se acaban de ocupar uno o m\xE1s asientos. Vuelve a elegir.",
265
+ "picker.seatsJustTakenInCategory.one": "{count} asiento reci\xE9n ocupado en {label} \xB7 quedan {left}",
266
+ "picker.seatsJustTakenInCategory.other": "{count} asientos reci\xE9n ocupados en {label} \xB7 quedan {left}",
267
+ "picker.seatsNoLongerAvailableTryAnother": "Esos asientos ya no est\xE1n disponibles. Prueba otra cantidad o tipo de entrada.",
268
+ "picker.seatsSecured": "Asientos asegurados",
269
+ "picker.seatsSelectedCount.one": "{count} seleccionado",
270
+ "picker.seatsSelectedCount.other": "{count} seleccionados",
271
+ "picker.section": "Secci\xF3n",
272
+ "picker.secureMore": "Asegurar m\xE1s",
273
+ "picker.secureMoreAndCheckout": "Asegurar {count} m\xE1s y pagar",
274
+ "picker.securedCount": "{count} asegurados",
275
+ "picker.seeItIn3d": "Verlo en 3D",
276
+ "picker.select": "Seleccionar",
277
+ "picker.selectSeats": "Seleccionar asientos",
278
+ "picker.selectTable": "Seleccionar mesa",
279
+ "picker.selectWholeTable": "Seleccionar mesa completa",
280
+ "picker.selected": "Seleccionado",
281
+ "picker.selectingEllipsis": "Seleccionando\u2026",
282
+ "picker.showAllSeats": "Mostrar todos los asientos",
283
+ "picker.showAllTicketTypes": "Mostrar los {count} tipos de entrada",
284
+ "picker.showFewer": "Mostrar menos",
285
+ "picker.showLabelSeatsOnMap": "Mostrar los asientos de {label} en el mapa",
286
+ "picker.showTicketPanel": "Mostrar el panel de entradas",
287
+ "picker.sold": "Vendido",
288
+ "picker.soldOutCopy": "No hay asientos reservados disponibles actualmente para este evento.",
289
+ "picker.soldOutEyebrow": "Este evento",
290
+ "picker.soldOutTitle": "Agotado",
291
+ "picker.soldSeatExplanation": "Este asiento ya ha sido reservado.",
292
+ "picker.statusAvailable": "disponible",
293
+ "picker.statusOnHold": "en espera",
294
+ "picker.statusTaken2": "ocupado",
295
+ "picker.table": "Mesa",
296
+ "picker.tableCapacity": "Capacidad de la mesa",
297
+ "picker.tableUpdatedForGuests": "{label} actualizado para {count} invitados.",
298
+ "picker.temporarilyHeld": "Retenido temporalmente",
299
+ "picker.ticketPrices": "Precios de entradas",
300
+ "picker.ticketTypeSoldOut": "Ese tipo de entrada est\xE1 agotado. Prueba con otro tipo de entrada.",
301
+ "picker.tickets": "Entradas",
302
+ "picker.ticketsCount.one": "{count} entrada",
303
+ "picker.ticketsCount.other": "{count} entradas",
304
+ "picker.toggleColorblindColors": "Alternar colores para dalt\xF3nicos",
305
+ "picker.total": "Total",
306
+ "picker.trayHintTapOrBest": "Toca un asiento en el mapa, o deja que elijamos los mejores disponibles por ti.",
307
+ "picker.trayHintTapOrStanding": "Toca un asiento en el mapa \u2014 o consigue entradas de pie abajo.",
308
+ "picker.undo": "Deshacer",
309
+ "picker.upcomingTicketOffer": "Pr\xF3xima oferta de entradas",
310
+ "picker.updateTable": "Actualizar mesa",
311
+ "picker.updatingEllipsis": "Actualizando\u2026",
312
+ "picker.venueView": "Vista del recinto",
313
+ "picker.viewFromHere": "Vista desde aqu\xED",
314
+ "picker.viewFromThisSeat": "Vista desde este asiento",
315
+ "picker.wholeTypeWord": "{typeWord} completo",
316
+ "picker.willChooseClosestGroup": "Elegiremos el grupo disponible m\xE1s cercano por ti.",
317
+ "picker.willFindSeatsTogether": "Buscaremos {count} asientos juntos.",
318
+ "picker.yourSeats": "Tus asientos",
319
+ "picker.zoomIn": "Acercar",
320
+ "picker.zoomOut": "Alejar",
321
+ "picker.closeVenueNav": "Cerrar",
322
+ "picker.levelsAndAreas": "Niveles y zonas"
87
323
  };
88
324
  }
89
325
  });
@@ -108,6 +344,8 @@ var init_de = __esm({
108
344
  "picker.poweredBy": "Bereitgestellt von SeatLayer",
109
345
  "picker.testMode": "TESTMODUS",
110
346
  "picker.orphanHint": "Dadurch bleibt ein einzelner Platz \xFCbrig \u2014 r\xFCcken Sie ggf. einen Platz weiter.",
347
+ "picker.companionRequiresWheelchair": "Benachbarter Rollstuhlplatz erforderlich",
348
+ "picker.companionPairRequired": "W\xE4hlen Sie zu jedem Begleitticket den benachbarten Rollstuhlplatz aus.",
111
349
  // renderer (drawn on the Konva map — shared by the embed SDK)
112
350
  "map.aria": "Sitzplan. Mit Pfeiltasten zwischen Pl\xE4tzen navigieren, Eingabe zum Ausw\xE4hlen.",
113
351
  "map.seatsLeft": "{count} FREI",
@@ -146,7 +384,241 @@ var init_de = __esm({
146
384
  "picker.premiumFallbackNote": "Kein Premium-Block mit {count} Pl\xE4tzen \u2014 beste verf\xFCgbare werden angezeigt",
147
385
  "picker.loading3d": "3D-Veranstaltungsort wird aufgebaut\u2026",
148
386
  "picker.unavailable3d": "3D konnte nicht gestartet werden. Der Sitzplan bleibt verf\xFCgbar.",
149
- "picker.jumpToSection": "Zum Bereich springen"
387
+ "picker.jumpToSection": "Zum Bereich springen",
388
+ "picker.salesClosedPill": "Der Verkauf ist beendet",
389
+ "picker.salesClosedCopy": "Der Ticketverkauf f\xFCr diese Veranstaltung ist beendet.",
390
+ "picker.salesClosedCta": "Verkauf beendet",
391
+ "picker.salesClosedToast": "Der Verkauf f\xFCr diese Veranstaltung ist beendet.",
392
+ "picker.holdReassurance": "F\xFCr {time} reserviert \u2014 noch wird nichts abgebucht.",
393
+ "picker.securingSeats": "Ihre Pl\xE4tze werden gesichert\u2026",
394
+ "picker.openingCheckout": "Sicherer Checkout wird ge\xF6ffnet\u2026",
395
+ "picker.peekSecured": "\u2713 {count} gesichert \xB7 {total} \u2014 noch wird nichts abgebucht",
396
+ "picker.priceHint": "Die Farben auf dem Plan sind Ticketkategorien \u2014 tippen Sie eine an, um nur diese Pl\xE4tze zu sehen.",
397
+ "picker.findTogetherInstead": "Stattdessen Pl\xE4tze nebeneinander finden",
398
+ "picker.keepMyPicks": "Meine Auswahl behalten",
399
+ // picker widget — buyer-facing SeatPicker.ts strings (localization pass)
400
+ "picker.accessExpiredBody": "Melden Sie sich erneut an oder laden Sie die Seite neu, um diese Pl\xE4tze weiter durchzusehen. Bereits gehaltene Pl\xE4tze bleiben Ihnen erhalten.",
401
+ "picker.accessExpiredTitle": "Ihre Zugangssitzung ist beendet",
402
+ "picker.accessInvalidBody": "Sie k\xF6nnen weiterhin alles buchen, was als verf\xFCgbar angezeigt wird. Wenden Sie sich f\xFCr Zugang zum Rest an die Person, die Ihnen den Link geschickt hat.",
403
+ "picker.accessInvalidTitle": "Ihr Zugang konnte nicht best\xE4tigt werden",
404
+ "picker.accessPausedBody": "Der Veranstalter hat diese Auswahl pausiert. Versuchen Sie es in ein paar Minuten erneut.",
405
+ "picker.accessPausedTitle": "Diese Pl\xE4tze sind gerade reserviert",
406
+ "picker.accessRetry": "Erneut versuchen",
407
+ "picker.accessRevokedBody": "Bitten Sie die Person, die Ihnen diesen Link geschickt hat, um einen neuen, um weiter buchen zu k\xF6nnen.",
408
+ "picker.accessRevokedTitle": "Dieser Zugangslink ist nicht mehr aktiv",
409
+ "picker.accessiblePhysicalSeat": "Barrierefreier fester Sitzplatz",
410
+ "picker.addTime": "Zeit hinzuf\xFCgen",
411
+ "picker.addingEllipsis": "Wird hinzugef\xFCgt\u2026",
412
+ "picker.allLevels": "Alle Ebenen",
413
+ "picker.allPlacesBookedTogether": "Alle {count} Pl\xE4tze werden gemeinsam als ein exklusiver Tisch gebucht.",
414
+ "picker.allPrices": "Alle Preise",
415
+ "picker.allSeats": "Alle Pl\xE4tze",
416
+ "picker.allSetTitle": "Alles bereit",
417
+ "picker.anyTicketType": "Beliebige Ticketart",
418
+ "picker.anyVenueZone": "Beliebiger Bereich",
419
+ "picker.areas": "Bereiche",
420
+ "picker.available": "Verf\xFCgbar",
421
+ "picker.backToMap": "Zur\xFCck zur Karte",
422
+ "picker.backToVenue": "Zur\xFCck zur \xDCbersicht",
423
+ "picker.bestSeatsStar": "\u2726 Beste Pl\xE4tze",
424
+ "picker.booth": "Loge",
425
+ "picker.capacityGuests": "{count} G\xE4ste",
426
+ "picker.change": "\xC4ndern",
427
+ "picker.chartDerivedModel": "Aus dem Plan abgeleitetes Modell",
428
+ "picker.chartDerivedSeatEye": "Live-3D \xB7 aus dem Plan abgeleitete Sitzperspektive \xB7 nicht vermessen",
429
+ "picker.checkConnection": "Bitte \xFCberpr\xFCfen Sie Ihre Verbindung und versuchen Sie es erneut.",
430
+ "picker.checkoutCouldNotBeOpened": "Der Checkout konnte nicht ge\xF6ffnet werden. Ihre Pl\xE4tze sind weiterhin reserviert \u2014 bitte versuchen Sie es erneut.",
431
+ "picker.chooseAnotherToCompare": "W\xE4hlen Sie einen weiteren Platz zum Vergleichen.",
432
+ "picker.chooseGuestsCopy": "W\xE4hlen Sie, wie viele G\xE4ste zusammensitzen. Dieser Tisch ist exklusiv f\xFCr Ihre Gruppe reserviert.",
433
+ "picker.chooseMinMaxGuests": "W\xE4hlen Sie {min}\u2013{max} G\xE4ste",
434
+ "picker.clearSavedSeatComparison": "Gespeicherten Sitzvergleich l\xF6schen",
435
+ "picker.close": "Schlie\xDFen",
436
+ "picker.closeSeatStatus": "Sitzstatus schlie\xDFen",
437
+ "picker.closestGroupChosenInstantly": "N\xE4chstgelegene verf\xFCgbare Gruppe, sofort ausgew\xE4hlt.",
438
+ "picker.collapseTicketPanel": "Ticketbereich einklappen",
439
+ "picker.compareCount": "{count} vergleichen",
440
+ "picker.compareWithSaved": "Mit Gespeichertem vergleichen",
441
+ "picker.confirmOrCancelSeat": "Diesen Platz best\xE4tigen oder abbrechen",
442
+ "picker.confirmSeatLabel": "Platz {label} best\xE4tigen",
443
+ "picker.confirmYourTable": "Ihren Tisch best\xE4tigen",
444
+ "picker.confirmedAndOnWay": "best\xE4tigt. Eine Best\xE4tigung ist unterwegs.",
445
+ "picker.continue": "Weiter",
446
+ "picker.continueToCheckout": "Weiter zum Checkout",
447
+ "picker.couldNotAddMoreTime": "Zus\xE4tzliche Zeit konnte nicht hinzugef\xFCgt werden \u2014 bitte jetzt zum Checkout gehen.",
448
+ "picker.couldNotFindSeatsTogether": "Wir konnten keine {count} zusammenh\xE4ngenden Pl\xE4tze finden. Versuchen Sie es mit weniger Pl\xE4tzen oder einer anderen Ticketart.",
449
+ "picker.couldNotReleaseTickets": "Ihre Tickets konnten nicht freigegeben werden. Ihre Reservierung bleibt unver\xE4ndert.",
450
+ "picker.couldNotRemoveLabel": "{label} konnte nicht entfernt werden. Ihre Reservierung bleibt unver\xE4ndert.",
451
+ "picker.currentOffer": "Aktuelles Angebot",
452
+ "picker.currentTicketOffer": "Aktuelles Ticketangebot",
453
+ "picker.dragToLookAround": "Ziehen zum Umsehen \xB7 Scrollen zum Zoomen",
454
+ "picker.emptyWheelchairSpace": "Leerer Rollstuhlplatz",
455
+ "picker.exitFullScreen": "Vollbild verlassen",
456
+ "picker.fewer": "Weniger",
457
+ "picker.fewerGuests": "Weniger G\xE4ste",
458
+ "picker.fewerSeats": "Weniger Pl\xE4tze",
459
+ "picker.filterAndFocusByPrice": "Pl\xE4tze nach Preis filtern und fokussieren",
460
+ "picker.filters": "Filter",
461
+ "picker.findBestSeatsCount.one": "{count} besten Platz finden",
462
+ "picker.findBestSeatsCount.other": "{count} beste Pl\xE4tze finden",
463
+ "picker.findBestSeatsTogether": "Die besten Pl\xE4tze zusammen finden",
464
+ "picker.findNewSeats": "Neue Pl\xE4tze finden",
465
+ "picker.findingBestSeats": "Beste Pl\xE4tze werden gesucht\u2026",
466
+ "picker.findingEllipsis": "Wird gesucht\u2026",
467
+ "picker.fitToScreen": "An Bildschirm anpassen",
468
+ "picker.flat2dMap": "Flache 2D-Karte",
469
+ "picker.flexiblePartyTypeWord": "Flexible Gruppe \xB7 {typeWord}",
470
+ "picker.fullScreen": "Vollbild",
471
+ "picker.generalAdmission": "Stehplatz",
472
+ "picker.guestCountCouldNotBeSecured": "Diese G\xE4steanzahl konnte nicht gesichert werden. Ihre aktuelle Tischreservierung bleibt unver\xE4ndert.",
473
+ "picker.guestCountNoLongerAvailable": "Diese G\xE4steanzahl ist nicht mehr verf\xFCgbar. Ihre aktuelle Reservierung bleibt unver\xE4ndert.",
474
+ "picker.guests": "G\xE4ste",
475
+ "picker.guestsCount": "{count} G\xE4ste",
476
+ "picker.guestsCountEdit": "{count} G\xE4ste \xB7 Bearbeiten",
477
+ "picker.held": "Reserviert",
478
+ "picker.heldForYou": "F\xFCr Sie reserviert",
479
+ "picker.heldSeatExplanation": "Ein anderer K\xE4ufer h\xE4lt diesen Platz gerade. Er k\xF6nnte wieder verf\xFCgbar werden.",
480
+ "picker.heldTicketsReleased": "Reservierte Tickets wurden freigegeben. W\xE4hlen Sie neue Pl\xE4tze.",
481
+ "picker.heldTicketsRestored": "Ihre reservierten Tickets wurden wiederhergestellt.",
482
+ "picker.hidePanel": "Bereich ausblenden",
483
+ "picker.hideTicketPanel": "Ticketbereich ausblenden",
484
+ "picker.holdSeatsAndCheckout": "Pl\xE4tze reservieren & zum Checkout",
485
+ "picker.howOfferWorks": "So funktioniert das Angebot \u201E{name}\u201C",
486
+ "picker.interactive3dVenueView": "Interaktive 3D-Ansicht des Veranstaltungsorts",
487
+ "picker.keepMine": "Meine behalten",
488
+ "picker.labelNoLongerAvailable": "{label} ist nicht mehr verf\xFCgbar.",
489
+ "picker.labelRemoved": "{label} entfernt.",
490
+ "picker.labelRemovedFromHold": "{label} aus Ihrer Reservierung entfernt.",
491
+ "picker.labelRestored": "{label} wiederhergestellt.",
492
+ "picker.labelsNoLongerAvailable.one": "{labels} ist nicht mehr verf\xFCgbar. W\xE4hlen Sie einen anderen Platz.",
493
+ "picker.labelsNoLongerAvailable.other": "{labels} sind nicht mehr verf\xFCgbar. W\xE4hlen Sie eine andere Gruppe.",
494
+ "picker.leftCount.one": "noch {count}",
495
+ "picker.leftCount.other": "noch {count}",
496
+ "picker.levelNumber": "Ebene {number}",
497
+ "picker.levels": "Ebenen",
498
+ "picker.liveAvailability": "Live-Verf\xFCgbarkeit \u2014 Pl\xE4tze aktualisieren sich in Echtzeit",
499
+ "picker.loadingSeatMap": "Sitzplan wird geladen\u2026",
500
+ "picker.lookAroundLive3d": "In Live-3D umsehen",
501
+ "picker.manualTicketsRemovedNote": "Ihre manuell gew\xE4hlten Tickets werden erst entfernt, nachdem eine neue Gruppe gesichert wurde.",
502
+ "picker.map": "Karte",
503
+ "picker.mapDidNotLoad": "Der Sitzplan konnte nicht geladen werden",
504
+ "picker.maxTicketsForOrder": "Sie k\xF6nnen f\xFCr diese Bestellung bis zu {count} Tickets ausw\xE4hlen.",
505
+ "picker.minToMaxGuests": "{min} bis {max} G\xE4ste",
506
+ "picker.more": "Mehr",
507
+ "picker.moreGuests": "Mehr G\xE4ste",
508
+ "picker.moreSeats": "Mehr Pl\xE4tze",
509
+ "picker.moreSelectedCount": "{count} weitere ausgew\xE4hlt",
510
+ "picker.moreTimeAdded": "Zus\xE4tzliche Zeit hinzugef\xFCgt \u2014 Ihre Pl\xE4tze sind weiterhin reserviert.",
511
+ "picker.noAccessibilityMetadata": "Keine Barrierefreiheits-Angaben vorhanden",
512
+ "picker.noAuthoredRestriction": "Keine vom Veranstalter angegebene Einschr\xE4nkung",
513
+ "picker.noSeatsSelected": "Keine Pl\xE4tze ausgew\xE4hlt",
514
+ "picker.notChargedYet": "Es wird noch nichts abgebucht.",
515
+ "picker.notForSale": "Nicht verk\xE4uflich",
516
+ "picker.notForSaleExplanation": "Dieser Platz ist im aktuellen Verkauf nicht enthalten.",
517
+ "picker.numberOfGuests": "Anzahl der G\xE4ste",
518
+ "picker.offerDetailActive": "Dieser Preis gilt automatisch f\xFCr berechtigte Pl\xE4tze. Tickets in aktiven Warenk\xF6rben verringern vor\xFCbergehend die verf\xFCgbare Menge; freigegebene oder abgelaufene Reservierungen geben sie zur\xFCck. Wenn das Angebot endet, greift das n\xE4chste passende Angebot oder der regul\xE4re Ticketpreis.",
519
+ "picker.offerDetailUpcoming": "Tickets sind derzeit zum regul\xE4ren Preis erh\xE4ltlich. Dieses geplante Angebot gilt automatisch f\xFCr berechtigte Pl\xE4tze, sobald es beginnt.",
520
+ "picker.offerRemainingAvailable": "{count} verf\xFCgbar",
521
+ "picker.offerStarts": "beginnt {time}",
522
+ "picker.offerUntil": "bis {time}",
523
+ "picker.oneSeatSaved": "1 Platz gespeichert",
524
+ "picker.oneSeatSavedChooseAnother": "Ein Platz gespeichert; w\xE4hlen Sie einen weiteren zum Vergleichen",
525
+ "picker.openAuthored360": "360\xB0-Ansicht des Veranstaltungsorts \xF6ffnen",
526
+ "picker.openComparison": "Vergleich \xF6ffnen",
527
+ "picker.openComparisonOfSeats": "Vergleich von {count} Pl\xE4tzen \xF6ffnen",
528
+ "picker.openTicketPanel": "Ticketbereich \xF6ffnen",
529
+ "picker.peekFromPrice": "Ab {price}",
530
+ "picker.peekTicketsTotal.one": "{count} Ticket \xB7 {total}",
531
+ "picker.peekTicketsTotal.other": "{count} Tickets \xB7 {total}",
532
+ "picker.perGuestPrice": "{price} pro Gast",
533
+ "picker.pickYourSeats": "W\xE4hlen Sie Ihre Pl\xE4tze",
534
+ "picker.preferredTicketType": "Bevorzugte Ticketart",
535
+ "picker.preferredVenueZone": "Bevorzugter Bereich",
536
+ "picker.priceNotSupplied": "Preis nicht angegeben",
537
+ "picker.releaseHeldTickets": "Reservierte Tickets freigeben und andere Pl\xE4tze w\xE4hlen",
538
+ "picker.releasingEllipsis": "Wird freigegeben\u2026",
539
+ "picker.removeHeldTicketLabel": "Reserviertes Ticket {label} entfernen",
540
+ "picker.removeSeatLabel": "{label} entfernen",
541
+ "picker.removeTicketsUntilOrFewer": "Entfernen Sie Tickets, bis Ihre Bestellung {count} oder weniger enth\xE4lt.",
542
+ "picker.replaceCurrentChoices": "Ihre aktuelle Auswahl ersetzen?",
543
+ "picker.review": "\xDCberpr\xFCfen",
544
+ "picker.row": "Reihe",
545
+ "picker.rowLabel": "Reihe {label}",
546
+ "picker.saveToCompare": "Zum Vergleich speichern",
547
+ "picker.savedForComparison": "Zum Vergleich gespeichert",
548
+ "picker.savedSeatComparison": "Gespeicherter Sitzvergleich",
549
+ "picker.scheduledOffer": "Geplantes Angebot",
550
+ "picker.seat": "Platz",
551
+ "picker.seatCount.one": "{count} Platz",
552
+ "picker.seatCount.other": "{count} Pl\xE4tze",
553
+ "picker.seatJustTakenByAnother": "Platz {label} wurde gerade von einem anderen K\xE4ufer \xFCbernommen.",
554
+ "picker.seatLabel": "Platz {label}",
555
+ "picker.seatNoLongerAvailable": "Dieser Platz ist nicht mehr verf\xFCgbar.",
556
+ "picker.seatNoLongerYours": "Einige Pl\xE4tze sind f\xFCr Sie nicht mehr verf\xFCgbar. Sie wurden aus Ihrer Bestellung entfernt.",
557
+ "picker.seatNumberLower": "Platz {label}",
558
+ "picker.seatSalesClosedForEvent": "Der Ticketverkauf f\xFCr diese Veranstaltung ist beendet.",
559
+ "picker.seatSelection": "Sitzplatzauswahl",
560
+ "picker.seatStatusLegend": "Legende zum Sitzstatus",
561
+ "picker.seatTaken": "Ein anderer K\xE4ufer hat einen von Ihnen gew\xE4hlten Platz \xFCbernommen. Er wurde aus Ihrer Bestellung entfernt.",
562
+ "picker.seatsHeldForNeedMoreTime": "Ihre Pl\xE4tze sind f\xFCr <b>{time}</b> reserviert. Mehr Zeit ben\xF6tigt?",
563
+ "picker.seatsJustTaken": "Ein oder mehrere Pl\xE4tze wurden gerade vergeben. Bitte w\xE4hlen Sie erneut.",
564
+ "picker.seatsJustTakenInCategory.one": "{count} Platz gerade vergeben in {label} \xB7 noch {left}",
565
+ "picker.seatsJustTakenInCategory.other": "{count} Pl\xE4tze gerade vergeben in {label} \xB7 noch {left}",
566
+ "picker.seatsNoLongerAvailableTryAnother": "Diese Pl\xE4tze sind nicht mehr verf\xFCgbar. Versuchen Sie eine andere Anzahl oder Ticketart.",
567
+ "picker.seatsSecured": "Pl\xE4tze gesichert",
568
+ "picker.seatsSelectedCount.one": "{count} ausgew\xE4hlt",
569
+ "picker.seatsSelectedCount.other": "{count} ausgew\xE4hlt",
570
+ "picker.section": "Bereich",
571
+ "picker.secureMore": "Mehr sichern",
572
+ "picker.secureMoreAndCheckout": "{count} weitere sichern & zum Checkout",
573
+ "picker.securedCount": "{count} gesichert",
574
+ "picker.seeItIn3d": "In 3D ansehen",
575
+ "picker.select": "Ausw\xE4hlen",
576
+ "picker.selectSeats": "Pl\xE4tze ausw\xE4hlen",
577
+ "picker.selectTable": "Tisch ausw\xE4hlen",
578
+ "picker.selectWholeTable": "Ganzen Tisch ausw\xE4hlen",
579
+ "picker.selected": "Ausgew\xE4hlt",
580
+ "picker.selectingEllipsis": "Wird ausgew\xE4hlt\u2026",
581
+ "picker.showAllSeats": "Alle Pl\xE4tze anzeigen",
582
+ "picker.showAllTicketTypes": "Alle {count} Ticketarten anzeigen",
583
+ "picker.showFewer": "Weniger anzeigen",
584
+ "picker.showLabelSeatsOnMap": "{label}-Pl\xE4tze auf der Karte anzeigen",
585
+ "picker.showTicketPanel": "Ticketbereich anzeigen",
586
+ "picker.sold": "Verkauft",
587
+ "picker.soldOutCopy": "F\xFCr diese Veranstaltung sind derzeit keine reservierten Pl\xE4tze verf\xFCgbar.",
588
+ "picker.soldOutEyebrow": "Diese Veranstaltung",
589
+ "picker.soldOutTitle": "Ausverkauft",
590
+ "picker.soldSeatExplanation": "Dieser Platz wurde bereits gebucht.",
591
+ "picker.statusAvailable": "verf\xFCgbar",
592
+ "picker.statusOnHold": "reserviert",
593
+ "picker.statusTaken2": "vergeben",
594
+ "picker.table": "Tisch",
595
+ "picker.tableCapacity": "Tischkapazit\xE4t",
596
+ "picker.tableUpdatedForGuests": "{label} auf {count} G\xE4ste aktualisiert.",
597
+ "picker.temporarilyHeld": "Vor\xFCbergehend reserviert",
598
+ "picker.ticketPrices": "Ticketpreise",
599
+ "picker.ticketTypeSoldOut": "Diese Ticketart ist ausverkauft. Versuchen Sie eine andere Ticketart.",
600
+ "picker.tickets": "Tickets",
601
+ "picker.ticketsCount.one": "{count} Ticket",
602
+ "picker.ticketsCount.other": "{count} Tickets",
603
+ "picker.toggleColorblindColors": "Farbenblindenfreundliche Farben umschalten",
604
+ "picker.total": "Gesamt",
605
+ "picker.trayHintTapOrBest": "Tippen Sie auf einen Platz in der Karte, oder lassen Sie uns die besten verf\xFCgbaren f\xFCr Sie w\xE4hlen.",
606
+ "picker.trayHintTapOrStanding": "Tippen Sie auf einen Platz in der Karte \u2014 oder holen Sie sich unten Stehplatztickets.",
607
+ "picker.undo": "R\xFCckg\xE4ngig",
608
+ "picker.upcomingTicketOffer": "Bevorstehendes Ticketangebot",
609
+ "picker.updateTable": "Tisch aktualisieren",
610
+ "picker.updatingEllipsis": "Wird aktualisiert\u2026",
611
+ "picker.venueView": "Ansicht des Veranstaltungsorts",
612
+ "picker.viewFromHere": "Ansicht von hier",
613
+ "picker.viewFromThisSeat": "Ansicht von diesem Platz",
614
+ "picker.wholeTypeWord": "Ganzer {typeWord}",
615
+ "picker.willChooseClosestGroup": "Wir w\xE4hlen die n\xE4chstgelegene verf\xFCgbare Gruppe f\xFCr Sie.",
616
+ "picker.willFindSeatsTogether": "Wir finden {count} Pl\xE4tze zusammen f\xFCr Sie.",
617
+ "picker.yourSeats": "Ihre Pl\xE4tze",
618
+ "picker.zoomIn": "Vergr\xF6\xDFern",
619
+ "picker.zoomOut": "Verkleinern",
620
+ "picker.closeVenueNav": "Schlie\xDFen",
621
+ "picker.levelsAndAreas": "Ebenen & Bereiche"
150
622
  };
151
623
  }
152
624
  });
@@ -171,6 +643,8 @@ var init_fr = __esm({
171
643
  "picker.poweredBy": "Propuls\xE9 par SeatLayer",
172
644
  "picker.testMode": "MODE TEST",
173
645
  "picker.orphanHint": "Cela laisse un si\xE8ge isol\xE9 \u2014 pensez \xE0 vous d\xE9caler d'un si\xE8ge.",
646
+ "picker.companionRequiresWheelchair": "N\xE9cessite la place fauteuil roulant adjacente",
647
+ "picker.companionPairRequired": "S\xE9lectionnez la place fauteuil roulant adjacente avec chaque billet accompagnateur.",
174
648
  // renderer (drawn on the Konva map — shared by the embed SDK)
175
649
  "map.aria": "Plan de salle. Utilisez les fl\xE8ches pour naviguer entre les si\xE8ges, Entr\xE9e pour s\xE9lectionner.",
176
650
  "map.seatsLeft": "{count} LIBRES",
@@ -209,7 +683,241 @@ var init_fr = __esm({
209
683
  "picker.premiumFallbackNote": "Aucun bloc premium de {count} \u2014 affichage des meilleures places disponibles",
210
684
  "picker.loading3d": "Cr\xE9ation de la salle en 3D\u2026",
211
685
  "picker.unavailable3d": "La 3D n\u2019a pas pu d\xE9marrer. Le plan des si\xE8ges reste disponible.",
212
- "picker.jumpToSection": "Acc\xE9der \xE0 une section"
686
+ "picker.jumpToSection": "Acc\xE9der \xE0 une section",
687
+ "picker.salesClosedPill": "Les ventes sont closes",
688
+ "picker.salesClosedCopy": "La vente de billets pour cet \xE9v\xE9nement est termin\xE9e.",
689
+ "picker.salesClosedCta": "Ventes closes",
690
+ "picker.salesClosedToast": "Les ventes sont closes pour cet \xE9v\xE9nement.",
691
+ "picker.holdReassurance": "\xC0 vous pendant {time} \u2014 vous ne serez pas encore d\xE9bit\xE9.",
692
+ "picker.securingSeats": "R\xE9servation de vos places\u2026",
693
+ "picker.openingCheckout": "Ouverture du paiement s\xE9curis\xE9\u2026",
694
+ "picker.peekSecured": "\u2713 {count} r\xE9serv\xE9es \xB7 {total} \u2014 vous ne serez pas encore d\xE9bit\xE9",
695
+ "picker.priceHint": "Les couleurs du plan correspondent aux types de billets \u2014 touchez-en un ci-dessous pour ne voir que ces places.",
696
+ "picker.findTogetherInstead": "Trouver plut\xF4t des places c\xF4te \xE0 c\xF4te",
697
+ "picker.keepMyPicks": "Garder ma s\xE9lection",
698
+ // picker widget — buyer-facing SeatPicker.ts strings (localization pass)
699
+ "picker.accessExpiredBody": "Reconnectez-vous ou rechargez la page pour continuer \xE0 consulter ces places. Ce que vous avez d\xE9j\xE0 r\xE9serv\xE9 reste \xE0 vous.",
700
+ "picker.accessExpiredTitle": "Votre session d\u2019acc\xE8s a pris fin",
701
+ "picker.accessInvalidBody": "Vous pouvez toujours r\xE9server tout ce qui est indiqu\xE9 comme disponible. Contactez la personne qui vous a envoy\xE9 ce lien pour acc\xE9der au reste.",
702
+ "picker.accessInvalidTitle": "Nous n\u2019avons pas pu v\xE9rifier votre acc\xE8s",
703
+ "picker.accessPausedBody": "L\u2019organisateur a mis cette s\xE9lection en pause. R\xE9essayez dans quelques minutes.",
704
+ "picker.accessPausedTitle": "Ces places sont actuellement en attente",
705
+ "picker.accessRetry": "R\xE9essayer",
706
+ "picker.accessRevokedBody": "Demandez \xE0 la personne qui vous a envoy\xE9 ce lien de vous en fournir un nouveau pour continuer \xE0 r\xE9server ces places.",
707
+ "picker.accessRevokedTitle": "Ce lien d\u2019acc\xE8s n\u2019est plus actif",
708
+ "picker.accessiblePhysicalSeat": "Si\xE8ge physique accessible",
709
+ "picker.addTime": "Ajouter du temps",
710
+ "picker.addingEllipsis": "Ajout en cours\u2026",
711
+ "picker.allLevels": "Tous les niveaux",
712
+ "picker.allPlacesBookedTogether": "Les {count} places sont r\xE9serv\xE9es ensemble comme une table exclusive.",
713
+ "picker.allPrices": "Tous les prix",
714
+ "picker.allSeats": "Toutes les places",
715
+ "picker.allSetTitle": "Tout est pr\xEAt",
716
+ "picker.anyTicketType": "Tout type de billet",
717
+ "picker.anyVenueZone": "Toute zone du lieu",
718
+ "picker.areas": "Zones",
719
+ "picker.available": "Disponible",
720
+ "picker.backToMap": "Retour au plan",
721
+ "picker.backToVenue": "Retour au lieu",
722
+ "picker.bestSeatsStar": "\u2726 Meilleures places",
723
+ "picker.booth": "Loge",
724
+ "picker.capacityGuests": "{count} invit\xE9s",
725
+ "picker.change": "Modifier",
726
+ "picker.chartDerivedModel": "Mod\xE8le d\xE9riv\xE9 du plan",
727
+ "picker.chartDerivedSeatEye": "3D en direct \xB7 point de vue d\xE9riv\xE9 du plan \xB7 non v\xE9rifi\xE9",
728
+ "picker.checkConnection": "V\xE9rifiez votre connexion et r\xE9essayez.",
729
+ "picker.checkoutCouldNotBeOpened": "Le paiement n\u2019a pas pu s\u2019ouvrir. Vos places sont toujours r\xE9serv\xE9es \u2014 veuillez r\xE9essayer.",
730
+ "picker.chooseAnotherToCompare": "Choisissez une autre place \xE0 comparer.",
731
+ "picker.chooseGuestsCopy": "Choisissez combien d\u2019invit\xE9s seront assis ensemble. Cette table est r\xE9serv\xE9e exclusivement \xE0 votre groupe.",
732
+ "picker.chooseMinMaxGuests": "Choisissez de {min} \xE0 {max} invit\xE9s",
733
+ "picker.clearSavedSeatComparison": "Effacer la comparaison de places enregistr\xE9e",
734
+ "picker.close": "Fermer",
735
+ "picker.closeSeatStatus": "Fermer le statut de la place",
736
+ "picker.closestGroupChosenInstantly": "Groupe disponible le plus proche, choisi instantan\xE9ment.",
737
+ "picker.collapseTicketPanel": "R\xE9duire le panneau des billets",
738
+ "picker.compareCount": "Comparer {count}",
739
+ "picker.compareWithSaved": "Comparer avec l\u2019enregistr\xE9e",
740
+ "picker.confirmOrCancelSeat": "Confirmer ou annuler cette place",
741
+ "picker.confirmSeatLabel": "Confirmer la place {label}",
742
+ "picker.confirmYourTable": "Confirmez votre table",
743
+ "picker.confirmedAndOnWay": "confirm\xE9s. Une confirmation arrive.",
744
+ "picker.continue": "Continuer",
745
+ "picker.continueToCheckout": "Continuer vers le paiement",
746
+ "picker.couldNotAddMoreTime": "Impossible d\u2019ajouter du temps \u2014 veuillez passer au paiement maintenant.",
747
+ "picker.couldNotFindSeatsTogether": "Nous n\u2019avons pas trouv\xE9 {count} places ensemble. Essayez avec moins de places ou un autre type de billet.",
748
+ "picker.couldNotReleaseTickets": "Impossible de lib\xE9rer vos billets. Votre r\xE9servation reste inchang\xE9e.",
749
+ "picker.couldNotRemoveLabel": "Impossible de retirer {label}. Votre r\xE9servation reste inchang\xE9e.",
750
+ "picker.currentOffer": "Offre actuelle",
751
+ "picker.currentTicketOffer": "Offre de billets actuelle",
752
+ "picker.dragToLookAround": "Faites glisser pour regarder autour \xB7 d\xE9filez pour zoomer",
753
+ "picker.emptyWheelchairSpace": "Emplacement vide pour fauteuil roulant",
754
+ "picker.exitFullScreen": "Quitter le plein \xE9cran",
755
+ "picker.fewer": "Moins",
756
+ "picker.fewerGuests": "Moins d\u2019invit\xE9s",
757
+ "picker.fewerSeats": "Moins de places",
758
+ "picker.filterAndFocusByPrice": "Filtrer et cibler les places par prix",
759
+ "picker.filters": "Filtres",
760
+ "picker.findBestSeatsCount.one": "Trouver {count} meilleure place",
761
+ "picker.findBestSeatsCount.other": "Trouver {count} meilleures places",
762
+ "picker.findBestSeatsTogether": "Trouver les meilleures places ensemble",
763
+ "picker.findNewSeats": "Trouver de nouvelles places",
764
+ "picker.findingBestSeats": "Recherche des meilleures places\u2026",
765
+ "picker.findingEllipsis": "Recherche\u2026",
766
+ "picker.fitToScreen": "Ajuster \xE0 l\u2019\xE9cran",
767
+ "picker.flat2dMap": "Plan 2D \xE0 plat",
768
+ "picker.flexiblePartyTypeWord": "Groupe flexible \xB7 {typeWord}",
769
+ "picker.fullScreen": "Plein \xE9cran",
770
+ "picker.generalAdmission": "Entr\xE9e g\xE9n\xE9rale",
771
+ "picker.guestCountCouldNotBeSecured": "Ce nombre d\u2019invit\xE9s n\u2019a pas pu \xEAtre s\xE9curis\xE9. Votre r\xE9servation de table actuelle reste inchang\xE9e.",
772
+ "picker.guestCountNoLongerAvailable": "Ce nombre d\u2019invit\xE9s n\u2019est plus disponible. Votre r\xE9servation actuelle reste inchang\xE9e.",
773
+ "picker.guests": "Invit\xE9s",
774
+ "picker.guestsCount": "{count} invit\xE9s",
775
+ "picker.guestsCountEdit": "{count} invit\xE9s \xB7 Modifier",
776
+ "picker.held": "R\xE9serv\xE9",
777
+ "picker.heldForYou": "R\xE9serv\xE9 pour vous",
778
+ "picker.heldSeatExplanation": "Un autre acheteur retient cette place actuellement. Elle pourrait redevenir disponible.",
779
+ "picker.heldTicketsReleased": "Les billets r\xE9serv\xE9s ont \xE9t\xE9 lib\xE9r\xE9s. Choisissez vos nouvelles places.",
780
+ "picker.heldTicketsRestored": "Vos billets r\xE9serv\xE9s ont \xE9t\xE9 restaur\xE9s.",
781
+ "picker.hidePanel": "Masquer le panneau",
782
+ "picker.hideTicketPanel": "Masquer le panneau des billets",
783
+ "picker.holdSeatsAndCheckout": "R\xE9server les places et payer",
784
+ "picker.howOfferWorks": "Comment fonctionne l\u2019offre \xAB {name} \xBB",
785
+ "picker.interactive3dVenueView": "Vue 3D interactive du lieu",
786
+ "picker.keepMine": "Garder les miennes",
787
+ "picker.labelNoLongerAvailable": "{label} n\u2019est plus disponible.",
788
+ "picker.labelRemoved": "{label} retir\xE9.",
789
+ "picker.labelRemovedFromHold": "{label} retir\xE9 de votre r\xE9servation.",
790
+ "picker.labelRestored": "{label} restaur\xE9.",
791
+ "picker.labelsNoLongerAvailable.one": "{labels} n\u2019est plus disponible. Choisissez une autre place.",
792
+ "picker.labelsNoLongerAvailable.other": "{labels} ne sont plus disponibles. Choisissez un autre groupe.",
793
+ "picker.leftCount.one": "{count} restante",
794
+ "picker.leftCount.other": "{count} restantes",
795
+ "picker.levelNumber": "Niveau {number}",
796
+ "picker.levels": "Niveaux",
797
+ "picker.liveAvailability": "Disponibilit\xE9 en direct \u2014 les places se mettent \xE0 jour en temps r\xE9el",
798
+ "picker.loadingSeatMap": "Chargement du plan de salle\u2026",
799
+ "picker.lookAroundLive3d": "Regarder autour en 3D en direct",
800
+ "picker.manualTicketsRemovedNote": "Vos billets s\xE9lectionn\xE9s manuellement ne seront retir\xE9s qu\u2019apr\xE8s qu\u2019un nouveau groupe soit s\xE9curis\xE9.",
801
+ "picker.map": "Plan",
802
+ "picker.mapDidNotLoad": "Le plan de salle n\u2019a pas pu se charger",
803
+ "picker.maxTicketsForOrder": "Vous pouvez s\xE9lectionner jusqu\u2019\xE0 {count} billets pour cette commande.",
804
+ "picker.minToMaxGuests": "De {min} \xE0 {max} invit\xE9s",
805
+ "picker.more": "Plus",
806
+ "picker.moreGuests": "Plus d\u2019invit\xE9s",
807
+ "picker.moreSeats": "Plus de places",
808
+ "picker.moreSelectedCount": "{count} de plus s\xE9lectionn\xE9es",
809
+ "picker.moreTimeAdded": "Du temps suppl\xE9mentaire a \xE9t\xE9 ajout\xE9 \u2014 vos places restent r\xE9serv\xE9es.",
810
+ "picker.noAccessibilityMetadata": "Aucune information d\u2019accessibilit\xE9 fournie",
811
+ "picker.noAuthoredRestriction": "Aucune restriction indiqu\xE9e par l\u2019organisateur",
812
+ "picker.noSeatsSelected": "Aucune place s\xE9lectionn\xE9e",
813
+ "picker.notChargedYet": "Vous ne serez pas encore d\xE9bit\xE9.",
814
+ "picker.notForSale": "Non disponible \xE0 la vente",
815
+ "picker.notForSaleExplanation": "Cette place n\u2019est pas incluse dans la vente en cours.",
816
+ "picker.numberOfGuests": "Nombre d\u2019invit\xE9s",
817
+ "picker.offerDetailActive": "Ce prix s\u2019applique automatiquement aux places \xE9ligibles. Les billets dans les paniers actifs r\xE9duisent temporairement la quantit\xE9 disponible ; les r\xE9servations lib\xE9r\xE9es ou expir\xE9es la restituent. Lorsque l\u2019offre se termine, l\u2019offre correspondante suivante ou le prix normal du billet prend le relais.",
818
+ "picker.offerDetailUpcoming": "Les billets sont disponibles maintenant \xE0 leur prix normal. Cette offre programm\xE9e s\u2019appliquera automatiquement aux places \xE9ligibles d\xE8s son d\xE9marrage.",
819
+ "picker.offerRemainingAvailable": "{count} disponibles",
820
+ "picker.offerStarts": "d\xE9bute {time}",
821
+ "picker.offerUntil": "jusqu\u2019\xE0 {time}",
822
+ "picker.oneSeatSaved": "1 place enregistr\xE9e",
823
+ "picker.oneSeatSavedChooseAnother": "Une place enregistr\xE9e ; choisissez-en une autre \xE0 comparer",
824
+ "picker.openAuthored360": "Ouvrir la vue 360\xB0 du lieu",
825
+ "picker.openComparison": "Ouvrir la comparaison",
826
+ "picker.openComparisonOfSeats": "Ouvrir la comparaison de {count} places",
827
+ "picker.openTicketPanel": "Ouvrir le panneau des billets",
828
+ "picker.peekFromPrice": "D\xE8s {price}",
829
+ "picker.peekTicketsTotal.one": "{count} billet \xB7 {total}",
830
+ "picker.peekTicketsTotal.other": "{count} billets \xB7 {total}",
831
+ "picker.perGuestPrice": "{price} par invit\xE9",
832
+ "picker.pickYourSeats": "Choisissez vos places",
833
+ "picker.preferredTicketType": "Type de billet pr\xE9f\xE9r\xE9",
834
+ "picker.preferredVenueZone": "Zone du lieu pr\xE9f\xE9r\xE9e",
835
+ "picker.priceNotSupplied": "Prix non fourni",
836
+ "picker.releaseHeldTickets": "Lib\xE9rer les billets r\xE9serv\xE9s et choisir d\u2019autres places",
837
+ "picker.releasingEllipsis": "Lib\xE9ration en cours\u2026",
838
+ "picker.removeHeldTicketLabel": "Retirer le billet r\xE9serv\xE9 {label}",
839
+ "picker.removeSeatLabel": "Retirer {label}",
840
+ "picker.removeTicketsUntilOrFewer": "Retirez des billets jusqu\u2019\xE0 ce que votre commande en contienne {count} ou moins.",
841
+ "picker.replaceCurrentChoices": "Remplacer votre s\xE9lection actuelle ?",
842
+ "picker.review": "V\xE9rifier",
843
+ "picker.row": "Rang\xE9e",
844
+ "picker.rowLabel": "Rang\xE9e {label}",
845
+ "picker.saveToCompare": "Enregistrer pour comparer",
846
+ "picker.savedForComparison": "Enregistr\xE9 pour comparaison",
847
+ "picker.savedSeatComparison": "Comparaison de places enregistr\xE9e",
848
+ "picker.scheduledOffer": "Offre programm\xE9e",
849
+ "picker.seat": "Place",
850
+ "picker.seatCount.one": "{count} place",
851
+ "picker.seatCount.other": "{count} places",
852
+ "picker.seatJustTakenByAnother": "La place {label} vient d\u2019\xEAtre prise par un autre acheteur.",
853
+ "picker.seatLabel": "Place {label}",
854
+ "picker.seatNoLongerAvailable": "Cette place n\u2019est plus disponible.",
855
+ "picker.seatNoLongerYours": "Certaines places ne vous sont plus disponibles. Elles ont \xE9t\xE9 retir\xE9es de votre commande.",
856
+ "picker.seatNumberLower": "place {label}",
857
+ "picker.seatSalesClosedForEvent": "La vente de places pour cet \xE9v\xE9nement est termin\xE9e.",
858
+ "picker.seatSelection": "S\xE9lection des places",
859
+ "picker.seatStatusLegend": "L\xE9gende du statut des places",
860
+ "picker.seatTaken": "Un autre acheteur a pris une place que vous aviez choisie. Elle a \xE9t\xE9 retir\xE9e de votre commande.",
861
+ "picker.seatsHeldForNeedMoreTime": "Vos places sont r\xE9serv\xE9es pendant <b>{time}</b>. Besoin de plus de temps ?",
862
+ "picker.seatsJustTaken": "Une ou plusieurs places viennent d\u2019\xEAtre prises. Veuillez choisir \xE0 nouveau.",
863
+ "picker.seatsJustTakenInCategory.one": "{count} place tout juste prise dans {label} \xB7 {left} restante",
864
+ "picker.seatsJustTakenInCategory.other": "{count} places tout juste prises dans {label} \xB7 {left} restantes",
865
+ "picker.seatsNoLongerAvailableTryAnother": "Ces places ne sont plus disponibles. Essayez une autre quantit\xE9 ou un autre type de billet.",
866
+ "picker.seatsSecured": "Places s\xE9curis\xE9es",
867
+ "picker.seatsSelectedCount.one": "{count} s\xE9lectionn\xE9e",
868
+ "picker.seatsSelectedCount.other": "{count} s\xE9lectionn\xE9es",
869
+ "picker.section": "Section",
870
+ "picker.secureMore": "S\xE9curiser plus",
871
+ "picker.secureMoreAndCheckout": "S\xE9curiser {count} de plus et payer",
872
+ "picker.securedCount": "{count} s\xE9curis\xE9es",
873
+ "picker.seeItIn3d": "Voir en 3D",
874
+ "picker.select": "S\xE9lectionner",
875
+ "picker.selectSeats": "S\xE9lectionner des places",
876
+ "picker.selectTable": "S\xE9lectionner la table",
877
+ "picker.selectWholeTable": "S\xE9lectionner la table enti\xE8re",
878
+ "picker.selected": "S\xE9lectionn\xE9e",
879
+ "picker.selectingEllipsis": "S\xE9lection en cours\u2026",
880
+ "picker.showAllSeats": "Afficher toutes les places",
881
+ "picker.showAllTicketTypes": "Afficher les {count} types de billets",
882
+ "picker.showFewer": "Afficher moins",
883
+ "picker.showLabelSeatsOnMap": "Afficher les places {label} sur le plan",
884
+ "picker.showTicketPanel": "Afficher le panneau des billets",
885
+ "picker.sold": "Vendu",
886
+ "picker.soldOutCopy": "Aucune place r\xE9serv\xE9e n\u2019est actuellement disponible pour cet \xE9v\xE9nement.",
887
+ "picker.soldOutEyebrow": "Cet \xE9v\xE9nement",
888
+ "picker.soldOutTitle": "Complet",
889
+ "picker.soldSeatExplanation": "Cette place a d\xE9j\xE0 \xE9t\xE9 r\xE9serv\xE9e.",
890
+ "picker.statusAvailable": "disponible",
891
+ "picker.statusOnHold": "en attente",
892
+ "picker.statusTaken2": "prise",
893
+ "picker.table": "Table",
894
+ "picker.tableCapacity": "Capacit\xE9 de la table",
895
+ "picker.tableUpdatedForGuests": "{label} mise \xE0 jour pour {count} invit\xE9s.",
896
+ "picker.temporarilyHeld": "Temporairement r\xE9serv\xE9",
897
+ "picker.ticketPrices": "Prix des billets",
898
+ "picker.ticketTypeSoldOut": "Ce type de billet est \xE9puis\xE9. Essayez un autre type de billet.",
899
+ "picker.tickets": "Billets",
900
+ "picker.ticketsCount.one": "{count} billet",
901
+ "picker.ticketsCount.other": "{count} billets",
902
+ "picker.toggleColorblindColors": "Activer/d\xE9sactiver les couleurs adapt\xE9es au daltonisme",
903
+ "picker.total": "Total",
904
+ "picker.trayHintTapOrBest": "Touchez une place sur le plan, ou laissez-nous choisir les meilleures disponibles pour vous.",
905
+ "picker.trayHintTapOrStanding": "Touchez une place sur le plan \u2014 ou prenez des billets debout ci-dessous.",
906
+ "picker.undo": "Annuler",
907
+ "picker.upcomingTicketOffer": "Offre de billets \xE0 venir",
908
+ "picker.updateTable": "Mettre \xE0 jour la table",
909
+ "picker.updatingEllipsis": "Mise \xE0 jour en cours\u2026",
910
+ "picker.venueView": "Vue du lieu",
911
+ "picker.viewFromHere": "Vue depuis ici",
912
+ "picker.viewFromThisSeat": "Vue depuis cette place",
913
+ "picker.wholeTypeWord": "{typeWord} entier",
914
+ "picker.willChooseClosestGroup": "Nous choisirons le groupe disponible le plus proche pour vous.",
915
+ "picker.willFindSeatsTogether": "Nous trouverons {count} places ensemble.",
916
+ "picker.yourSeats": "Vos places",
917
+ "picker.zoomIn": "Zoomer",
918
+ "picker.zoomOut": "D\xE9zoomer",
919
+ "picker.closeVenueNav": "Fermer",
920
+ "picker.levelsAndAreas": "Niveaux et zones"
213
921
  };
214
922
  }
215
923
  });
@@ -227,6 +935,7 @@ __export(index_exports, {
227
935
  MAX_EVENT_INVENTORY: () => MAX_EVENT_INVENTORY,
228
936
  MAX_GA_CAPACITY: () => MAX_GA_CAPACITY,
229
937
  PickerController: () => PickerController,
938
+ PickerSelectionError: () => PickerSelectionError,
230
939
  RENDERED_QUALITY_REPORT_VERSION: () => RENDERED_QUALITY_REPORT_VERSION,
231
940
  SEAT_COMMERCIAL_MARKS: () => SEAT_COMMERCIAL_MARKS,
232
941
  SUPPORTED_LOCALES: () => SUPPORTED_LOCALES,
@@ -250,6 +959,7 @@ __export(index_exports, {
250
959
  expandTable: () => expandTable,
251
960
  expandTableSlots: () => expandTableSlots,
252
961
  floorObjects: () => floorObjects,
962
+ floorOverviewTranslations: () => floorOverviewTranslations,
253
963
  floorsOf: () => floorsOf,
254
964
  formatDate: () => formatDate,
255
965
  formatMinor: () => formatMinor,
@@ -268,6 +978,7 @@ __export(index_exports, {
268
978
  isGaUnitLabel: () => isGaUnitLabel,
269
979
  isSectionHidden: () => isSectionHidden,
270
980
  layerOf: () => layerOf,
981
+ layoutFloorOverview: () => layoutFloorOverview,
271
982
  loadLocale: () => loadLocale,
272
983
  objectCenter: () => objectCenter,
273
984
  owningSectionForObject: () => owningSectionForObject,
@@ -287,6 +998,7 @@ __export(index_exports, {
287
998
  tCount: () => tCount,
288
999
  tableInventoryCount: () => tableInventoryCount,
289
1000
  tableSeatCountsBySide: () => tableSeatCountsBySide,
1001
+ translateObject: () => translateObject,
290
1002
  validGAInventorySegments: () => validGAInventorySegments
291
1003
  });
292
1004
  module.exports = __toCommonJS(index_exports);
@@ -637,7 +1349,7 @@ function resolveSegmentedRowGroups(objects) {
637
1349
  const expectedCount = ordered[0]?.segmentedRow?.componentCount ?? 0;
638
1350
  const first = ordered[0]?.segmentedRow;
639
1351
  if (!first) continue;
640
- const valid = expectedCount >= 2 && ordered.length === expectedCount && first?.boundaryBefore === "start" && ordered.every((row, index) => row.segmentedRow?.kind === "segmented-row-v1" && row.segmentedRow.groupId === groupId && row.segmentedRow.componentCount === expectedCount && row.segmentedRow.componentIndex === index && (index === 0 ? row.segmentedRow.boundaryBefore === "start" : row.segmentedRow.boundaryBefore !== "start") && row.segmentedRow.displayLabel === first.displayLabel);
1352
+ const valid = expectedCount >= 2 && ordered.length === expectedCount && first?.boundaryBefore === "start" && ordered.every((row, index) => row.segmentedRow?.kind === "segmented-row-v1" && row.segmentedRow.groupId === groupId && row.segmentedRow.componentCount === expectedCount && row.segmentedRow.componentIndex === index && row.segmentedRow.repeatLabelOnComponents === first.repeatLabelOnComponents && (index === 0 ? row.segmentedRow.boundaryBefore === "start" : row.segmentedRow.boundaryBefore !== "start") && row.segmentedRow.displayLabel === first.displayLabel);
641
1353
  if (!valid) continue;
642
1354
  const totalSeats = ordered.reduce((sum, row) => sum + row.seatCount, 0);
643
1355
  let adjacencyOffset = 0;
@@ -1531,6 +2243,75 @@ function stackFloors(doc, spread = 900) {
1531
2243
  });
1532
2244
  return { ...doc, objects, floors: void 0 };
1533
2245
  }
2246
+ function layoutFloorOverview(doc) {
2247
+ const floors = doc.floors;
2248
+ if (!floors || floors.length < 2) return doc;
2249
+ const translations = floorOverviewTranslations(doc);
2250
+ const objects = [];
2251
+ floors.forEach((floor, index) => {
2252
+ const { x: dx, y: dy } = translations[index];
2253
+ for (const object of floor.objects) objects.push(translateObject(object, dx, dy));
2254
+ });
2255
+ return {
2256
+ ...doc,
2257
+ objects,
2258
+ floors: void 0,
2259
+ referenceImage: void 0,
2260
+ backgroundImage: void 0
2261
+ };
2262
+ }
2263
+ function floorOverviewTranslations(doc) {
2264
+ const floors = doc.floors;
2265
+ if (!floors?.length) return [{ x: 0, y: 0 }];
2266
+ if (floors.length < 2) return floors.map(() => ({ x: 0, y: 0 }));
2267
+ const bounds2 = floors.map((floor) => chartBounds({
2268
+ ...doc,
2269
+ objects: floor.objects,
2270
+ floors: void 0,
2271
+ focalPoint: floor.focalPoint,
2272
+ referenceImage: void 0,
2273
+ backgroundImage: void 0
2274
+ }));
2275
+ const characteristic = Math.max(1, ...bounds2.map((bound) => Math.max(bound.width, bound.height)));
2276
+ const gap = Math.max(64, Math.min(240, characteristic * 0.08));
2277
+ const targetAspect = 16 / 10;
2278
+ let best = null;
2279
+ for (let columns2 = 1; columns2 <= floors.length; columns2 += 1) {
2280
+ const rows = Math.ceil(floors.length / columns2);
2281
+ const columnWidths2 = new Array(columns2).fill(0);
2282
+ const rowHeights2 = new Array(rows).fill(0);
2283
+ bounds2.forEach((bound, index) => {
2284
+ const column = index % columns2;
2285
+ const row = Math.floor(index / columns2);
2286
+ columnWidths2[column] = Math.max(columnWidths2[column], bound.width);
2287
+ rowHeights2[row] = Math.max(rowHeights2[row], bound.height);
2288
+ });
2289
+ const width = columnWidths2.reduce((sum, value) => sum + value, 0) + gap * (columns2 - 1);
2290
+ const height = rowHeights2.reduce((sum, value) => sum + value, 0) + gap * (rows - 1);
2291
+ const unusedCells = columns2 * rows - floors.length;
2292
+ const score = Math.abs(Math.log(width / Math.max(1, height) / targetAspect)) + unusedCells * 0.04;
2293
+ if (!best || score < best.score) best = { columns: columns2, columnWidths: columnWidths2, rowHeights: rowHeights2, score };
2294
+ }
2295
+ const { columns, columnWidths, rowHeights } = best;
2296
+ const columnOffsets = [];
2297
+ const rowOffsets = [];
2298
+ columnWidths.reduce((offset, width, index) => {
2299
+ columnOffsets[index] = offset;
2300
+ return offset + width + gap;
2301
+ }, 0);
2302
+ rowHeights.reduce((offset, height, index) => {
2303
+ rowOffsets[index] = offset;
2304
+ return offset + height + gap;
2305
+ }, 0);
2306
+ return floors.map((_floor, index) => {
2307
+ const bound = bounds2[index];
2308
+ const column = index % columns;
2309
+ const row = Math.floor(index / columns);
2310
+ const targetX = columnOffsets[column] + (columnWidths[column] - bound.width) / 2;
2311
+ const targetY = rowOffsets[row] + (rowHeights[row] - bound.height) / 2;
2312
+ return { x: targetX - bound.x, y: targetY - bound.y };
2313
+ });
2314
+ }
1534
2315
  function expandFloorObjects(objects, zones, fallbackFocal, activeConfigurationId, viewFallback, confidenceFallback) {
1535
2316
  const out = [];
1536
2317
  const segmented = resolveSegmentedRowGroups(objects);
@@ -2989,6 +3770,8 @@ var en = {
2989
3770
  "picker.poweredBy": "Powered by SeatLayer",
2990
3771
  "picker.testMode": "TEST MODE",
2991
3772
  "picker.orphanHint": "This leaves a single seat stranded \u2014 consider shifting one seat over.",
3773
+ "picker.companionRequiresWheelchair": "Requires the adjacent wheelchair place",
3774
+ "picker.companionPairRequired": "Select the adjacent wheelchair place with each companion ticket.",
2992
3775
  // renderer (drawn on the Konva map — shared by the embed SDK)
2993
3776
  "map.aria": "Seating map. Use arrow keys to move between seats, Enter to select.",
2994
3777
  "map.seatsLeft": "{count} LEFT",
@@ -3027,7 +3810,241 @@ var en = {
3027
3810
  "picker.premiumFallbackNote": "No premium block of {count} \u2014 showing best overall",
3028
3811
  "picker.loading3d": "Building the 3D venue\u2026",
3029
3812
  "picker.unavailable3d": "3D could not start. The seat map is still available.",
3030
- "picker.jumpToSection": "Jump to section"
3813
+ "picker.jumpToSection": "Jump to section",
3814
+ "picker.salesClosedPill": "Sales are closed",
3815
+ "picker.salesClosedCopy": "Ticket sales for this event have ended.",
3816
+ "picker.salesClosedCta": "Sales closed",
3817
+ "picker.salesClosedToast": "Sales are closed for this event.",
3818
+ "picker.holdReassurance": "Yours for {time} \u2014 you won\u2019t be charged yet.",
3819
+ "picker.securingSeats": "Securing your seats\u2026",
3820
+ "picker.openingCheckout": "Opening secure checkout\u2026",
3821
+ "picker.peekSecured": "\u2713 {count} secured \xB7 {total} \u2014 you won\u2019t be charged yet",
3822
+ "picker.priceHint": "Colours on the map are ticket types \u2014 tap one below to show just those seats.",
3823
+ "picker.findTogetherInstead": "Find seats together instead",
3824
+ "picker.keepMyPicks": "Keep my picks",
3825
+ // picker widget — buyer-facing SeatPicker.ts strings (localization pass)
3826
+ "picker.accessExpiredBody": "Sign in again, or reload the page, to keep browsing these seats. Anything you are already holding stays yours.",
3827
+ "picker.accessExpiredTitle": "Your access session has ended",
3828
+ "picker.accessInvalidBody": "You can still book anything shown as available. Contact whoever sent you here for access to the rest.",
3829
+ "picker.accessInvalidTitle": "We couldn\u2019t verify your access",
3830
+ "picker.accessPausedBody": "The organizer has paused this selection. Try again in a few minutes.",
3831
+ "picker.accessPausedTitle": "These seats are on hold right now",
3832
+ "picker.accessRetry": "Try again",
3833
+ "picker.accessRevokedBody": "Ask whoever sent you here for a new link to keep booking these seats.",
3834
+ "picker.accessRevokedTitle": "This access link is no longer active",
3835
+ "picker.accessiblePhysicalSeat": "Accessible physical seat",
3836
+ "picker.addTime": "Add time",
3837
+ "picker.addingEllipsis": "Adding\u2026",
3838
+ "picker.allLevels": "All levels",
3839
+ "picker.allPlacesBookedTogether": "All {count} places are booked together as one exclusive table.",
3840
+ "picker.allPrices": "All prices",
3841
+ "picker.allSeats": "All seats",
3842
+ "picker.allSetTitle": "You\u2019re all set",
3843
+ "picker.anyTicketType": "Any ticket type",
3844
+ "picker.anyVenueZone": "Any venue zone",
3845
+ "picker.areas": "Areas",
3846
+ "picker.available": "Available",
3847
+ "picker.backToMap": "Back to map",
3848
+ "picker.backToVenue": "Back to venue",
3849
+ "picker.bestSeatsStar": "\u2726 Best seats",
3850
+ "picker.booth": "Booth",
3851
+ "picker.capacityGuests": "{count} guests",
3852
+ "picker.change": "Change",
3853
+ "picker.chartDerivedModel": "Chart-derived model",
3854
+ "picker.chartDerivedSeatEye": "Live 3D \xB7 chart-derived seat-eye \xB7 not surveyed",
3855
+ "picker.checkConnection": "Check your connection and try again.",
3856
+ "picker.checkoutCouldNotBeOpened": "Checkout could not be opened. Your seats are still held \u2014 please try again.",
3857
+ "picker.chooseAnotherToCompare": "Choose another seat to compare.",
3858
+ "picker.chooseGuestsCopy": "Choose how many guests will sit together. This table is held exclusively for your party.",
3859
+ "picker.chooseMinMaxGuests": "Choose {min}\u2013{max} guests",
3860
+ "picker.clearSavedSeatComparison": "Clear saved seat comparison",
3861
+ "picker.close": "Close",
3862
+ "picker.closeSeatStatus": "Close seat status",
3863
+ "picker.closestGroupChosenInstantly": "Closest available group, chosen instantly.",
3864
+ "picker.collapseTicketPanel": "Collapse ticket panel",
3865
+ "picker.compareCount": "Compare {count}",
3866
+ "picker.compareWithSaved": "Compare with saved",
3867
+ "picker.confirmOrCancelSeat": "Confirm or cancel this seat",
3868
+ "picker.confirmSeatLabel": "Confirm seat {label}",
3869
+ "picker.confirmYourTable": "Confirm your table",
3870
+ "picker.confirmedAndOnWay": "confirmed. A confirmation is on its way.",
3871
+ "picker.continue": "Continue",
3872
+ "picker.continueToCheckout": "Continue to checkout",
3873
+ "picker.couldNotAddMoreTime": "Couldn\u2019t add more time \u2014 please head to checkout now.",
3874
+ "picker.couldNotFindSeatsTogether": "We couldn\u2019t find {count} seats together. Try fewer seats or another ticket type.",
3875
+ "picker.couldNotReleaseTickets": "Couldn\u2019t release your tickets. Your hold is unchanged.",
3876
+ "picker.couldNotRemoveLabel": "Couldn\u2019t remove {label}. Your hold is unchanged.",
3877
+ "picker.currentOffer": "Current offer",
3878
+ "picker.currentTicketOffer": "Current ticket offer",
3879
+ "picker.dragToLookAround": "Drag to look around \xB7 scroll to zoom",
3880
+ "picker.emptyWheelchairSpace": "Empty wheelchair space",
3881
+ "picker.exitFullScreen": "Exit full screen",
3882
+ "picker.fewer": "Fewer",
3883
+ "picker.fewerGuests": "Fewer guests",
3884
+ "picker.fewerSeats": "Fewer seats",
3885
+ "picker.filterAndFocusByPrice": "Filter and focus seats by price",
3886
+ "picker.filters": "Filters",
3887
+ "picker.findBestSeatsCount.one": "Find {count} best seat",
3888
+ "picker.findBestSeatsCount.other": "Find {count} best seats",
3889
+ "picker.findBestSeatsTogether": "Find the best seats together",
3890
+ "picker.findNewSeats": "Find new seats",
3891
+ "picker.findingBestSeats": "Finding the best seats\u2026",
3892
+ "picker.findingEllipsis": "Finding\u2026",
3893
+ "picker.fitToScreen": "Fit to screen",
3894
+ "picker.flat2dMap": "Flat 2D map",
3895
+ "picker.flexiblePartyTypeWord": "Flexible party \xB7 {typeWord}",
3896
+ "picker.fullScreen": "Full screen",
3897
+ "picker.generalAdmission": "General admission",
3898
+ "picker.guestCountCouldNotBeSecured": "That guest count could not be secured. Your current table hold is unchanged.",
3899
+ "picker.guestCountNoLongerAvailable": "That guest count is no longer available. Your current hold is unchanged.",
3900
+ "picker.guests": "Guests",
3901
+ "picker.guestsCount": "{count} guests",
3902
+ "picker.guestsCountEdit": "{count} guests \xB7 Edit",
3903
+ "picker.held": "Held",
3904
+ "picker.heldForYou": "Held for you",
3905
+ "picker.heldSeatExplanation": "Another buyer is holding this seat. It may become available again.",
3906
+ "picker.heldTicketsReleased": "Held tickets released. Choose your new seats.",
3907
+ "picker.heldTicketsRestored": "Your held tickets have been restored.",
3908
+ "picker.hidePanel": "Hide panel",
3909
+ "picker.hideTicketPanel": "Hide the ticket panel",
3910
+ "picker.holdSeatsAndCheckout": "Hold seats & checkout",
3911
+ "picker.howOfferWorks": "How the {name} offer works",
3912
+ "picker.interactive3dVenueView": "Interactive 3D venue view",
3913
+ "picker.keepMine": "Keep mine",
3914
+ "picker.labelNoLongerAvailable": "{label} is no longer available.",
3915
+ "picker.labelRemoved": "{label} removed.",
3916
+ "picker.labelRemovedFromHold": "{label} removed from your hold.",
3917
+ "picker.labelRestored": "{label} restored.",
3918
+ "picker.labelsNoLongerAvailable.one": "{labels} is no longer available. Choose another seat.",
3919
+ "picker.labelsNoLongerAvailable.other": "{labels} are no longer available. Choose another group.",
3920
+ "picker.leftCount.one": "{count} left",
3921
+ "picker.leftCount.other": "{count} left",
3922
+ "picker.levelNumber": "Level {number}",
3923
+ "picker.levels": "Levels",
3924
+ "picker.liveAvailability": "Live availability \u2014 seats update in real time",
3925
+ "picker.loadingSeatMap": "Loading seat map\u2026",
3926
+ "picker.lookAroundLive3d": "Look around in live 3D",
3927
+ "picker.manualTicketsRemovedNote": "Your manually selected tickets will be removed only after a new group is secured.",
3928
+ "picker.map": "Map",
3929
+ "picker.mapDidNotLoad": "The seat map didn\u2019t load",
3930
+ "picker.maxTicketsForOrder": "You can select up to {count} tickets for this order.",
3931
+ "picker.minToMaxGuests": "{min} to {max} guests",
3932
+ "picker.more": "More",
3933
+ "picker.moreGuests": "More guests",
3934
+ "picker.moreSeats": "More seats",
3935
+ "picker.moreSelectedCount": "{count} more selected",
3936
+ "picker.moreTimeAdded": "More time added \u2014 your seats are still held.",
3937
+ "picker.noAccessibilityMetadata": "No accessibility metadata supplied",
3938
+ "picker.noAuthoredRestriction": "No organizer-authored restriction",
3939
+ "picker.noSeatsSelected": "No seats selected",
3940
+ "picker.notChargedYet": "You won\u2019t be charged yet.",
3941
+ "picker.notForSale": "Not for sale",
3942
+ "picker.notForSaleExplanation": "This seat is not included in the current sale.",
3943
+ "picker.numberOfGuests": "Number of guests",
3944
+ "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.",
3945
+ "picker.offerDetailUpcoming": "Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.",
3946
+ "picker.offerRemainingAvailable": "{count} available",
3947
+ "picker.offerStarts": "starts {time}",
3948
+ "picker.offerUntil": "until {time}",
3949
+ "picker.oneSeatSaved": "1 seat saved",
3950
+ "picker.oneSeatSavedChooseAnother": "One seat saved; choose another to compare",
3951
+ "picker.openAuthored360": "Open venue 360\xB0",
3952
+ "picker.openComparison": "Open comparison",
3953
+ "picker.openComparisonOfSeats": "Open comparison of {count} seats",
3954
+ "picker.openTicketPanel": "Open ticket panel",
3955
+ "picker.peekFromPrice": "From {price}",
3956
+ "picker.peekTicketsTotal.one": "{count} ticket \xB7 {total}",
3957
+ "picker.peekTicketsTotal.other": "{count} tickets \xB7 {total}",
3958
+ "picker.perGuestPrice": "{price} per guest",
3959
+ "picker.pickYourSeats": "Pick your seats",
3960
+ "picker.preferredTicketType": "Preferred ticket type",
3961
+ "picker.preferredVenueZone": "Preferred venue zone",
3962
+ "picker.priceNotSupplied": "Price not supplied",
3963
+ "picker.releaseHeldTickets": "Release held tickets and choose different seats",
3964
+ "picker.releasingEllipsis": "Releasing\u2026",
3965
+ "picker.removeHeldTicketLabel": "Remove held ticket {label}",
3966
+ "picker.removeSeatLabel": "Remove {label}",
3967
+ "picker.removeTicketsUntilOrFewer": "Remove tickets until your order has {count} or fewer.",
3968
+ "picker.replaceCurrentChoices": "Replace your current choices?",
3969
+ "picker.review": "Review",
3970
+ "picker.row": "Row",
3971
+ "picker.rowLabel": "Row {label}",
3972
+ "picker.saveToCompare": "Save to compare",
3973
+ "picker.savedForComparison": "Saved for comparison",
3974
+ "picker.savedSeatComparison": "Saved seat comparison",
3975
+ "picker.scheduledOffer": "Scheduled offer",
3976
+ "picker.seat": "Seat",
3977
+ "picker.seatCount.one": "{count} seat",
3978
+ "picker.seatCount.other": "{count} seats",
3979
+ "picker.seatJustTakenByAnother": "Seat {label} was just taken by another buyer.",
3980
+ "picker.seatLabel": "Seat {label}",
3981
+ "picker.seatNoLongerAvailable": "That seat is no longer available.",
3982
+ "picker.seatNoLongerYours": "Some seats are no longer available to you. They have been removed from your order.",
3983
+ "picker.seatNumberLower": "seat {label}",
3984
+ "picker.seatSalesClosedForEvent": "Seat sales have closed for this event.",
3985
+ "picker.seatSelection": "Seat selection",
3986
+ "picker.seatStatusLegend": "Seat status legend",
3987
+ "picker.seatTaken": "Someone else took a seat you had picked. It has been removed from your order.",
3988
+ "picker.seatsHeldForNeedMoreTime": "Your seats are held for <b>{time}</b>. Need more time?",
3989
+ "picker.seatsJustTaken": "One or more seats were just taken. Please pick again.",
3990
+ "picker.seatsJustTakenInCategory.one": "{count} seat just taken in {label} \xB7 {left} left",
3991
+ "picker.seatsJustTakenInCategory.other": "{count} seats just taken in {label} \xB7 {left} left",
3992
+ "picker.seatsNoLongerAvailableTryAnother": "Those seats are no longer available. Try another quantity or ticket type.",
3993
+ "picker.seatsSecured": "Seats secured",
3994
+ "picker.seatsSelectedCount.one": "{count} selected",
3995
+ "picker.seatsSelectedCount.other": "{count} selected",
3996
+ "picker.section": "Section",
3997
+ "picker.secureMore": "Secure more",
3998
+ "picker.secureMoreAndCheckout": "Secure {count} more & checkout",
3999
+ "picker.securedCount": "{count} secured",
4000
+ "picker.seeItIn3d": "See it in 3D",
4001
+ "picker.select": "Select",
4002
+ "picker.selectSeats": "Select seats",
4003
+ "picker.selectTable": "Select table",
4004
+ "picker.selectWholeTable": "Select whole table",
4005
+ "picker.selected": "Selected",
4006
+ "picker.selectingEllipsis": "Selecting\u2026",
4007
+ "picker.showAllSeats": "Show all seats",
4008
+ "picker.showAllTicketTypes": "Show all {count} ticket types",
4009
+ "picker.showFewer": "Show fewer",
4010
+ "picker.showLabelSeatsOnMap": "Show {label} seats on the map",
4011
+ "picker.showTicketPanel": "Show the ticket panel",
4012
+ "picker.sold": "Sold",
4013
+ "picker.soldOutCopy": "No reserved seats are currently available for this event.",
4014
+ "picker.soldOutEyebrow": "This event",
4015
+ "picker.soldOutTitle": "Sold out",
4016
+ "picker.soldSeatExplanation": "This seat has already been booked.",
4017
+ "picker.statusAvailable": "available",
4018
+ "picker.statusOnHold": "on hold",
4019
+ "picker.statusTaken2": "taken",
4020
+ "picker.table": "Table",
4021
+ "picker.tableCapacity": "Table capacity",
4022
+ "picker.tableUpdatedForGuests": "{label} updated for {count} guests.",
4023
+ "picker.temporarilyHeld": "Temporarily held",
4024
+ "picker.ticketPrices": "Ticket prices",
4025
+ "picker.ticketTypeSoldOut": "That ticket type is sold out. Try another ticket type.",
4026
+ "picker.tickets": "Tickets",
4027
+ "picker.ticketsCount.one": "{count} ticket",
4028
+ "picker.ticketsCount.other": "{count} tickets",
4029
+ "picker.toggleColorblindColors": "Toggle colorblind-friendly colors",
4030
+ "picker.total": "Total",
4031
+ "picker.trayHintTapOrBest": "Tap a seat on the map, or let us pick the best available for you.",
4032
+ "picker.trayHintTapOrStanding": "Tap a seat on the map \u2014 or grab standing tickets below.",
4033
+ "picker.undo": "Undo",
4034
+ "picker.upcomingTicketOffer": "Upcoming ticket offer",
4035
+ "picker.updateTable": "Update table",
4036
+ "picker.updatingEllipsis": "Updating\u2026",
4037
+ "picker.venueView": "Venue view",
4038
+ "picker.viewFromHere": "View from here",
4039
+ "picker.viewFromThisSeat": "View from this seat",
4040
+ "picker.wholeTypeWord": "Whole {typeWord}",
4041
+ "picker.willChooseClosestGroup": "We\u2019ll choose the closest available group for you.",
4042
+ "picker.willFindSeatsTogether": "We\u2019ll find {count} seats together.",
4043
+ "picker.yourSeats": "Your seats",
4044
+ "picker.zoomIn": "Zoom in",
4045
+ "picker.zoomOut": "Zoom out",
4046
+ "picker.closeVenueNav": "Close",
4047
+ "picker.levelsAndAreas": "Levels & areas"
3031
4048
  };
3032
4049
 
3033
4050
  // src/i18n/index.ts
@@ -3101,10 +4118,11 @@ var WHEELCHAIR_GLYPH_MIN_PX = 10;
3101
4118
  var FILTERED_WHEELCHAIR_GLYPH_MIN_PX = 14;
3102
4119
  var SECTION_PROMINENT_SCALE = 0.45 * SEAT_LEGIBLE_SCALE;
3103
4120
  var BLOCK_MELT_TOP = 0.9 * SEAT_LEGIBLE_SCALE;
3104
- var SEAT_FOCUS_SCALE = Math.max(SEAT_LEGIBLE_SCALE * 1.1, BLOCK_MELT_TOP);
4121
+ var SEAT_DETAIL_SCALE = Math.max(SEAT_LEGIBLE_SCALE * 1.1, BLOCK_MELT_TOP);
3105
4122
  var PAN_START_SLOP_PX = 8;
3106
4123
  var GHOST_CLICK_MS = 700;
3107
4124
  var ROW_LABEL_CLEARANCE = 2;
4125
+ var FOCUSED_ROW_LABEL_MIN_PX = MIN_VISIBLE_BOOKABLE_LABEL_PX;
3108
4126
  var ROW_LABEL_COLLISION_CELL = 64;
3109
4127
  var ZONE_PROMINENT_SCALE = 0.55 * SECTION_PROMINENT_SCALE;
3110
4128
  var MAX_LABELS = 700;
@@ -3500,6 +4518,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3500
4518
  this.activeFloorId = "";
3501
4519
  /** When true on a multi-floor chart, render ALL floors stacked (3D overview). */
3502
4520
  this.stacked = false;
4521
+ /** Buyer-facing 2D overview: floors are rigidly arranged without altering topology. */
4522
+ this.floorOverview = false;
3503
4523
  /** Interactive node per seat/booth — a Circle for seats, a Rect for booths. */
3504
4524
  this.circleById = /* @__PURE__ */ new Map();
3505
4525
  /** Booth block geometry, keyed by booth id (= the unit's rowId). */
@@ -3755,6 +4775,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3755
4775
  return;
3756
4776
  }
3757
4777
  if (this.pinch && this.pointers.size >= 2) {
4778
+ this.releaseSectionFocusDim();
3758
4779
  const [a, b] = [...this.pointers.values()];
3759
4780
  const dist = Math.hypot(b.x - a.x, b.y - a.y);
3760
4781
  if (this.pinch.startDist < 1) return;
@@ -3773,6 +4794,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3773
4794
  if (!this.panStarted) {
3774
4795
  if (this.moved <= PAN_START_SLOP_PX) return;
3775
4796
  this.panStarted = true;
4797
+ this.releaseSectionFocusDim();
3776
4798
  }
3777
4799
  this.stage.position({
3778
4800
  x: this.stage.x() + (p.x - this.panLast.x),
@@ -3893,13 +4915,23 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3893
4915
  }
3894
4916
  // ---- ISeatmapRenderer -----------------------------------------------------
3895
4917
  setChart(doc, opts) {
3896
- if (doc !== this.chartDoc) this.stacked = false;
4918
+ if (doc !== this.chartDoc) {
4919
+ this.stacked = false;
4920
+ this.floorOverview = false;
4921
+ }
3897
4922
  this.cancelAssetLoads();
3898
4923
  this.chartDoc = doc;
3899
4924
  this.activeFloorId = opts?.floorId ?? floorsOf(doc)[0].id;
3900
4925
  this.objectFloor.clear();
3901
4926
  if (doc.floors && doc.floors.length > 1) {
3902
- for (const f of doc.floors) for (const o of f.objects) this.objectFloor.set(o.id, f.id);
4927
+ for (const f of doc.floors) {
4928
+ for (const o of f.objects) {
4929
+ this.objectFloor.set(o.id, f.id);
4930
+ if (o.type === "section" && o.logicalSectionId) {
4931
+ this.objectFloor.set(o.logicalSectionId, f.id);
4932
+ }
4933
+ }
4934
+ }
3903
4935
  }
3904
4936
  const view = this.floorView(doc);
3905
4937
  this.focusedId = null;
@@ -3997,7 +5029,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3997
5029
  this.seats = expandChart(view, {
3998
5030
  // A stacked overview is presentation-only and never opens a seat POV. The
3999
5031
  // per-section renderer below still resolves each object's real floor base.
4000
- floorBaseHeightM: this.stacked ? 0 : this.floorBaseHeightMFor()
5032
+ floorBaseHeightM: this.stacked || this.floorOverview ? 0 : this.floorBaseHeightMFor()
4001
5033
  });
4002
5034
  for (const s of this.seats) {
4003
5035
  this.seatById.set(s.id, s);
@@ -4018,14 +5050,21 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4018
5050
  this.overlayLayer.add(this.focusRing);
4019
5051
  this.zoomToFit();
4020
5052
  }
4021
- /** The chart to render: all floors stacked (3D overview), the active floor, or
4022
- * the whole chart for single-floor charts. */
5053
+ /** The chart to render: authored-coordinate overview, stacked 3D overview,
5054
+ * the active floor, or the whole chart for single-floor charts. */
4023
5055
  floorView(doc) {
4024
5056
  if (!doc.floors || !doc.floors.length) return {
4025
5057
  ...doc,
4026
5058
  referenceImage: void 0,
4027
5059
  backgroundImage: buyerBackgroundImage(doc)
4028
5060
  };
5061
+ if (this.floorOverview && doc.floors.length >= 2) {
5062
+ return {
5063
+ ...layoutFloorOverview(doc),
5064
+ referenceImage: void 0,
5065
+ backgroundImage: buyerBackgroundImage(doc)
5066
+ };
5067
+ }
4029
5068
  if (this.stacked && doc.floors.length >= 2) {
4030
5069
  return {
4031
5070
  ...stackFloors(doc),
@@ -4058,14 +5097,30 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4058
5097
  const multi = !!this.chartDoc.floors && this.chartDoc.floors.length >= 2;
4059
5098
  if (!multi) return;
4060
5099
  this.stacked = on;
5100
+ if (on) this.floorOverview = false;
4061
5101
  this.setChart(this.chartDoc, { floorId: this.activeFloorId });
4062
5102
  }
4063
5103
  isStacked() {
4064
5104
  return this.stacked;
4065
5105
  }
5106
+ /** Toggle the buyer's combined 2D floor overview. Every floor is translated
5107
+ * as one rigid group for clarity; its authored internal topology is exact. */
5108
+ setFloorOverview(on) {
5109
+ if (!this.chartDoc || on === this.floorOverview) return;
5110
+ const multi = !!this.chartDoc.floors && this.chartDoc.floors.length >= 2;
5111
+ if (!multi) return;
5112
+ this.floorOverview = on;
5113
+ if (on) this.stacked = false;
5114
+ this.setChart(this.chartDoc, { floorId: this.activeFloorId });
5115
+ }
5116
+ isFloorOverview() {
5117
+ return this.floorOverview;
5118
+ }
4066
5119
  /** Switch which floor is shown (2D). Re-renders + re-fits; no-op if unchanged. */
4067
5120
  setActiveFloor(floorId) {
4068
- if (!this.chartDoc || floorId === this.activeFloorId) return;
5121
+ if (!this.chartDoc || floorId === this.activeFloorId && !this.floorOverview && !this.stacked) return;
5122
+ this.floorOverview = false;
5123
+ this.stacked = false;
4069
5124
  this.setChart(this.chartDoc, { floorId });
4070
5125
  }
4071
5126
  /** Floors for the host's switcher (single-floor charts return one synthetic floor). */
@@ -4524,6 +5579,13 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4524
5579
  const h = this.stage.height();
4525
5580
  const b = this.viewMode === "perspective" && this.perspectiveBounds ? this.perspectiveBounds : this.bounds;
4526
5581
  this.fitScale = Math.min(w / b.width, h / b.height) || 1;
5582
+ if (this.opts.portraitFitCrop && !this.exportMode) {
5583
+ const widthBound = w / b.width <= h / b.height;
5584
+ const projectedH = b.height * this.fitScale;
5585
+ if (widthBound && projectedH < 0.55 * h) {
5586
+ this.fitScale = Math.min(this.fitScale * 1.3, 0.62 * h / b.height);
5587
+ }
5588
+ }
4527
5589
  const s = this.fitScale;
4528
5590
  this.stage.scale({ x: s, y: s });
4529
5591
  this.stage.position({
@@ -4533,6 +5595,18 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4533
5595
  this.afterViewChange();
4534
5596
  this.stage.batchDraw();
4535
5597
  }
5598
+ /**
5599
+ * Reframe after a host/layout resize without throwing away section context.
5600
+ * The stage already represents the usable map area (the picker side panel is
5601
+ * a sibling), so measuring it here naturally respects open/closed panels.
5602
+ */
5603
+ refitCurrentView() {
5604
+ if (this.focusedSectionId && this.sectionBounds(this.focusedSectionId)) {
5605
+ this.focusRegion(this.focusedSectionId, { animate: false });
5606
+ return;
5607
+ }
5608
+ this.zoomToFit();
5609
+ }
4536
5610
  zoomIn() {
4537
5611
  const center = { x: this.stage.width() / 2, y: this.stage.height() / 2 };
4538
5612
  this.zoomAbout(this.stage.scaleX() * _SeatmapRenderer.ZOOM_STEP, center);
@@ -5623,8 +6697,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
5623
6697
  }
5624
6698
  /**
5625
6699
  * AXS section-focus: dim + desaturate every other section, draw a calm backdrop
5626
- * panel behind this section's seats, and glide the camera in to frame it (the
5627
- * seat-pick gate below only lets buyers pick once seats are LABEL_SCALE big).
6700
+ * panel behind this section's seats, and glide the camera to a padded fit of
6701
+ * the whole section. A large section may therefore use smaller seat markers,
6702
+ * but it never clips its outer rows merely to meet a fixed detail scale.
5628
6703
  */
5629
6704
  focusSection(id) {
5630
6705
  if (!this.sections.some((section) => section.id === id || section.logicalId === id)) return;
@@ -5633,7 +6708,24 @@ var _SeatmapRenderer = class _SeatmapRenderer {
5633
6708
  this.bgLayer.batchDraw();
5634
6709
  this.overlayLayer.batchDraw();
5635
6710
  this.updateLOD();
5636
- this.focusRegion(id, { minScale: SEAT_FOCUS_SCALE });
6711
+ this.focusRegion(id);
6712
+ }
6713
+ /**
6714
+ * Release only the focus dim + backdrop, keeping the focused-section state
6715
+ * (summary card, adjacent-section navigation, LOD) intact. Called on the
6716
+ * buyer's first manual pan/pinch/wheel after a section glide: the dim reads
6717
+ * as "everything else is disabled", but every seat outside the focus was
6718
+ * always hit-testable — free roaming across sections is the product
6719
+ * behaviour, the glide is only an entrance.
6720
+ */
6721
+ releaseSectionFocusDim() {
6722
+ if (!this.focusBackdrop && !this.focusDimOverlay) return;
6723
+ this.focusBackdrop?.destroy();
6724
+ this.focusBackdrop = null;
6725
+ this.focusDimOverlay?.destroy();
6726
+ this.focusDimOverlay = null;
6727
+ this.bgLayer.batchDraw();
6728
+ this.overlayLayer.batchDraw();
5637
6729
  }
5638
6730
  /** Clear an AXS section focus — restore full-bowl brightness + drop the backdrop. */
5639
6731
  clearSectionFocus() {
@@ -5771,6 +6863,44 @@ var _SeatmapRenderer = class _SeatmapRenderer {
5771
6863
  if (!Number.isFinite(minX)) return { x: 0, y: 0, width: 1, height: 1 };
5772
6864
  return { x: minX, y: minY, width: Math.max(1, maxX - minX), height: Math.max(1, maxY - minY) };
5773
6865
  }
6866
+ /** Live minimap geometry comes from the rendered scene, so a content-aware
6867
+ * all-floor layout cannot collapse back onto the source coordinate stack. */
6868
+ getMinimapSnapshot() {
6869
+ const sections = this.sections.map((section) => {
6870
+ const inactive = this.isSectionClosed(section) || section.free <= 0;
6871
+ const selected = section.memberIds.some((id) => this.selection.has(id));
6872
+ const active2 = this.focusedSectionId === section.id || this.focusedSectionId === section.logicalId;
6873
+ return {
6874
+ id: section.logicalId,
6875
+ label: section.label,
6876
+ outline: section.outline.map((point) => ({ ...point })),
6877
+ ...this.objectFloor.get(section.id) ? { floorId: this.objectFloor.get(section.id) } : {},
6878
+ state: selected ? "selected" : inactive ? "inactive" : "available",
6879
+ active: active2
6880
+ };
6881
+ });
6882
+ return {
6883
+ sections,
6884
+ seats: sections.length ? [] : this.seats.map((seat) => ({
6885
+ x: seat.x,
6886
+ y: seat.y,
6887
+ categoryKey: seat.categoryKey,
6888
+ state: this.selection.has(seat.id) ? "selected" : this.statusById.get(seat.id) === "free" ? "available" : "inactive"
6889
+ }))
6890
+ };
6891
+ }
6892
+ /** Minimap drag/click navigation: preserve zoom and move one world point to
6893
+ * the viewport centre. Works for flat and projected views via worldToScreen. */
6894
+ panToWorld(point) {
6895
+ this.cancelGlide();
6896
+ const screen = this.worldToScreen(point);
6897
+ this.stage.position({
6898
+ x: this.stage.x() + this.stage.width() / 2 - screen.x,
6899
+ y: this.stage.y() + this.stage.height() / 2 - screen.y
6900
+ });
6901
+ this.afterViewChange();
6902
+ this.stage.batchDraw();
6903
+ }
5774
6904
  /** Legend hover: highlight one category (dim the rest), or null to clear. */
5775
6905
  setCategoryHighlight(key) {
5776
6906
  if (this.categoryHighlight === key) return;
@@ -6539,7 +7669,15 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6539
7669
  blockT = clamp((BLOCK_MELT_TOP - scale) / (BLOCK_MELT_TOP - SECTION_PROMINENT_SCALE), 0, 1);
6540
7670
  zoneT = clamp((SECTION_PROMINENT_SCALE - scale) / (SECTION_PROMINENT_SCALE - ZONE_PROMINENT_SCALE), 0, 1);
6541
7671
  }
6542
- const sectionOverview = scale < CACHE_THRESHOLD;
7672
+ if (this.floorOverview) {
7673
+ blockT = 0;
7674
+ zoneT = 0;
7675
+ }
7676
+ if (this.focusedSectionId) {
7677
+ blockT = 0;
7678
+ zoneT = 0;
7679
+ }
7680
+ const sectionOverview = !this.floorOverview && !this.focusedSectionId && scale < CACHE_THRESHOLD;
6543
7681
  if (sectionOverview) blockT = 1;
6544
7682
  if (!this.zones.length) zoneT = 0;
6545
7683
  const deferPerspectiveSeatReveal = this.viewMode === "perspective" && this.glideInProgress && this.seats.length > 2500;
@@ -6684,8 +7822,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6684
7822
  fitSectionRungLabels(sec, sx) {
6685
7823
  const paddingPx = 8;
6686
7824
  sec.subLabelFits = false;
6687
- const maxPx = SECTION_LABEL_PX * sec.labelScale;
6688
- const minPx = MIN_SECTION_LABEL_PX * sec.labelScale;
7825
+ const maxPx = SECTION_LABEL_PX * sec.labelScale * this.uiScale();
7826
+ const minPx = Math.min(MIN_SECTION_LABEL_PX * sec.labelScale, maxPx);
6689
7827
  for (let fontPx = maxPx; fontPx >= minPx; fontPx -= 1) {
6690
7828
  for (const rotation of [0, -90]) {
6691
7829
  sec.nameLabel.rotation(rotation);
@@ -6711,13 +7849,24 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6711
7849
  sec.nameLabel.rotation(0);
6712
7850
  sec.nameLabelFits = false;
6713
7851
  }
7852
+ /**
7853
+ * Screen-constant chrome must still respect the screen it is constant on:
7854
+ * an 18px zone cap sized for a 900px designer canvas eats a 360px phone map
7855
+ * (owner report, 2026-08-14). Scales the fixed-px chrome down to 66% on the
7856
+ * smallest containers; never up.
7857
+ */
7858
+ uiScale() {
7859
+ const w = this.stage?.width() ?? 900;
7860
+ return Math.min(1, Math.max(0.66, w / 900));
7861
+ }
6714
7862
  /** Size one screen-constant zone name/price pill around its shared anchor. */
6715
7863
  sizeZonePill(zone, sx) {
6716
- const padX = 10 / sx;
6717
- const padY = 6 / sx;
6718
- const gap = zone.sub ? 3 / sx : 0;
6719
- this.sizeLabel(zone.label, ZONE_LABEL_PX / sx, zone.anchor.y);
6720
- if (zone.sub) this.sizeLabel(zone.sub, ZONE_SUB_PX / sx, zone.anchor.y);
7864
+ const k = this.uiScale();
7865
+ const padX = 10 * k / sx;
7866
+ const padY = 6 * k / sx;
7867
+ const gap = zone.sub ? 3 * k / sx : 0;
7868
+ this.sizeLabel(zone.label, ZONE_LABEL_PX * k / sx, zone.anchor.y);
7869
+ if (zone.sub) this.sizeLabel(zone.sub, ZONE_SUB_PX * k / sx, zone.anchor.y);
6721
7870
  const width = Math.max(zone.label.width(), zone.sub?.width() ?? 0) + padX * 2;
6722
7871
  const height = zone.label.height() + (zone.sub ? gap + zone.sub.height() : 0) + padY * 2;
6723
7872
  zone.label.y(zone.anchor.y - (zone.sub ? (gap + zone.sub.height()) / 2 : 0));
@@ -6725,8 +7874,16 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6725
7874
  zone.back.position(zone.anchor);
6726
7875
  zone.back.size({ width, height });
6727
7876
  zone.back.offset({ x: width / 2, y: height / 2 });
6728
- zone.back.cornerRadius(7 / sx);
7877
+ zone.back.cornerRadius(7 * this.uiScale() / sx);
6729
7878
  zone.back.strokeWidth(1 / sx);
7879
+ const abs = zone.back.getAbsolutePosition();
7880
+ const minScreenY = 62 + height * sx / 2;
7881
+ if (abs.y < minScreenY) {
7882
+ const dy = (minScreenY - abs.y) / sx;
7883
+ zone.label.y(zone.label.y() + dy);
7884
+ if (zone.sub) zone.sub.y(zone.sub.y() + dy);
7885
+ zone.back.y(zone.back.y() + dy);
7886
+ }
6730
7887
  }
6731
7888
  /**
6732
7889
  * Map a container-relative screen point back to world coords. Inverts the
@@ -6951,7 +8108,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6951
8108
  * must fall through and pick.
6952
8109
  */
6953
8110
  sectionBounds(id) {
6954
- const bounds2 = this.sections.filter((section) => section.id === id || section.logicalId === id).map((section) => {
8111
+ const matching = this.sections.filter((section) => section.id === id || section.logicalId === id);
8112
+ const bounds2 = matching.map((section) => {
6955
8113
  if (this.viewMode === "perspective" && section.perspectiveAffine) {
6956
8114
  return polyBounds(section.outline.map((point) => this.projectedSectionPoint(section, point)));
6957
8115
  }
@@ -6963,11 +8121,77 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6963
8121
  return polyBounds(points);
6964
8122
  });
6965
8123
  if (!bounds2.length) return null;
6966
- const left = Math.min(...bounds2.map((box) => box.x));
6967
- const top = Math.min(...bounds2.map((box) => box.y));
6968
- const right = Math.max(...bounds2.map((box) => box.x + box.width));
6969
- const bottom = Math.max(...bounds2.map((box) => box.y + box.height));
6970
- return { x: left, y: top, width: right - left, height: bottom - top };
8124
+ let left = Math.min(...bounds2.map((box) => box.x));
8125
+ let top = Math.min(...bounds2.map((box) => box.y));
8126
+ let right = Math.max(...bounds2.map((box) => box.x + box.width));
8127
+ let bottom = Math.max(...bounds2.map((box) => box.y + box.height));
8128
+ const memberIds = new Set(matching.flatMap((section) => section.memberIds));
8129
+ for (const seatId of memberIds) {
8130
+ const seat = this.seatById.get(seatId);
8131
+ if (!seat) continue;
8132
+ const point = this.renderedSeatPoint(seat);
8133
+ left = Math.min(left, point.x - this.seatR);
8134
+ top = Math.min(top, point.y - this.seatR);
8135
+ right = Math.max(right, point.x + this.seatR);
8136
+ bottom = Math.max(bottom, point.y + this.seatR);
8137
+ }
8138
+ const includeBox = (box) => {
8139
+ if (![box.x, box.y, box.width, box.height].every(Number.isFinite)) return;
8140
+ left = Math.min(left, box.x);
8141
+ top = Math.min(top, box.y);
8142
+ right = Math.max(right, box.x + box.width);
8143
+ bottom = Math.max(bottom, box.y + box.height);
8144
+ };
8145
+ const belongsToSection = (box) => {
8146
+ const centre = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
8147
+ return matching.some((section) => pointInPolygonWithHoles(centre, section.outline, section.holes));
8148
+ };
8149
+ for (const { node, backdrop } of this.freeTextById.values()) {
8150
+ const nodeBox = node.getClientRect({ relativeTo: this.bgLayer, skipShadow: true });
8151
+ if (!belongsToSection(nodeBox)) continue;
8152
+ includeBox(nodeBox);
8153
+ if (backdrop) includeBox(backdrop.getClientRect({ relativeTo: this.bgLayer, skipShadow: true }));
8154
+ }
8155
+ for (const { node } of this.iconNodeById.values()) {
8156
+ const box = node.getClientRect({ relativeTo: this.bgLayer, skipShadow: true });
8157
+ if (belongsToSection(box)) includeBox(box);
8158
+ }
8159
+ const occupiedText = new WorldBoxIndex();
8160
+ for (const { node } of this.freeTextById.values()) {
8161
+ const box = node.getClientRect({ relativeTo: this.bgLayer, skipShadow: true, skipStroke: true });
8162
+ occupiedText.insert({
8163
+ x: box.x - ROW_LABEL_CLEARANCE,
8164
+ y: box.y - ROW_LABEL_CLEARANCE,
8165
+ width: box.width + ROW_LABEL_CLEARANCE * 2,
8166
+ height: box.height + ROW_LABEL_CLEARANCE * 2
8167
+ });
8168
+ }
8169
+ for (const plan of this.rowLabelPlan) {
8170
+ const relevant = plan.focusKeys?.some((key) => key === id || matching.some((section) => key === section.id || key === section.logicalId || key === section.zone)) ?? false;
8171
+ const label = new import_Text.Text({
8172
+ text: plan.text,
8173
+ fontSize: plan.fontSize,
8174
+ fontStyle: "700",
8175
+ fontFamily: this.labelFont(),
8176
+ rotation: plan.rotation,
8177
+ listening: false
8178
+ });
8179
+ label.offsetX(label.width() / 2);
8180
+ label.offsetY(label.height() / 2);
8181
+ const resolved = this.resolveAutomaticRowLabelPosition(label, plan, occupiedText);
8182
+ const box = this.rowLabelBox(label, resolved);
8183
+ if (relevant) includeBox(box);
8184
+ occupiedText.insert(box);
8185
+ label.destroy();
8186
+ }
8187
+ const span = Math.max(right - left, bottom - top);
8188
+ const padding = Math.max(this.seatR * 2, Math.min(24, span * 0.025));
8189
+ return {
8190
+ x: left - padding,
8191
+ y: top - padding,
8192
+ width: Math.max(1, right - left + padding * 2),
8193
+ height: Math.max(1, bottom - top + padding * 2)
8194
+ };
6971
8195
  }
6972
8196
  sectionFrameScale(id) {
6973
8197
  const b = this.sectionBounds(id);
@@ -6978,7 +8202,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6978
8202
  const margin = 1.12;
6979
8203
  if (b.width <= 0 || b.height <= 0) return this.stage.scaleX();
6980
8204
  const frameScale = Math.min(w / (b.width * margin), h / (b.height * margin));
6981
- return clamp(Math.max(frameScale, SEAT_FOCUS_SCALE), min, max);
8205
+ return clamp(frameScale, min, max);
6982
8206
  }
6983
8207
  /**
6984
8208
  * Resolve a seat tap: honour the 3D deck-drill and the AXS seat-pick gate,
@@ -6991,7 +8215,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6991
8215
  * fall through and PICK. This is the invariant: seats are never unpickable.
6992
8216
  */
6993
8217
  handleSeatTap(id) {
6994
- if (this.stacked && this.opts.onDeckTap) {
8218
+ if ((this.stacked || this.floorOverview) && this.opts.onDeckTap) {
6995
8219
  const floorId = this.objectFloor.get(this.seatById.get(id)?.rowId ?? "");
6996
8220
  if (floorId) {
6997
8221
  this.opts.onDeckTap(floorId);
@@ -7066,6 +8290,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7066
8290
  });
7067
8291
  this.stage.on("wheel", (e) => {
7068
8292
  e.evt.preventDefault();
8293
+ this.releaseSectionFocusDim();
7069
8294
  const r = this.container.getBoundingClientRect();
7070
8295
  const pointer = { x: e.evt.clientX - r.left, y: e.evt.clientY - r.top };
7071
8296
  const factor = Math.exp(-e.evt.deltaY * 2e-3);
@@ -7082,8 +8307,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7082
8307
  if (near) this.handleSeatTap(near);
7083
8308
  return;
7084
8309
  }
7085
- if (this.stacked && this.opts.onDeckTap) {
7086
- const floorId = this.deckFloorAt();
8310
+ if ((this.stacked || this.floorOverview) && this.opts.onDeckTap) {
8311
+ const sectionId = this.floorOverview ? this.sectionAt(pointer) : null;
8312
+ const floorId = (sectionId ? this.objectFloor.get(sectionId) : null) ?? this.deckFloorAt();
7087
8313
  if (floorId) {
7088
8314
  this.opts.onDeckTap(floorId);
7089
8315
  return;
@@ -7481,6 +8707,28 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7481
8707
  ...hiddenReason ? { hiddenReason } : {}
7482
8708
  };
7483
8709
  });
8710
+ const rowLabels = this.labelGroup.find((node) => node.getAttr("rowLabel") === true).filter((node) => node instanceof import_Text.Text).map((node) => {
8711
+ const screenBounds = node.getClientRect({
8712
+ skipShadow: true,
8713
+ skipStroke: true
8714
+ });
8715
+ const outside = screenBounds.x + screenBounds.width < 0 || screenBounds.x > viewport.width || screenBounds.y + screenBounds.height < 0 || screenBounds.y > viewport.height;
8716
+ const ink = node.fill();
8717
+ return {
8718
+ text: node.text(),
8719
+ visible: node.isVisible() && !outside,
8720
+ renderedFontPx: rounded(node.fontSize() * effectiveScale),
8721
+ ink: typeof ink === "string" ? ink : "",
8722
+ ...!outside ? {
8723
+ screenBox: {
8724
+ x: rounded(screenBounds.x),
8725
+ y: rounded(screenBounds.y),
8726
+ width: rounded(screenBounds.width),
8727
+ height: rounded(screenBounds.height)
8728
+ }
8729
+ } : {}
8730
+ };
8731
+ });
7484
8732
  const palette = overviewPalette(this.canvasBackground);
7485
8733
  const neutralSectionFills = new Set(
7486
8734
  ["normal", "nearly-gone", "sold-out", "closed"].map((state) => overviewStateFill(palette, state).toLowerCase())
@@ -7525,7 +8773,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7525
8773
  labels,
7526
8774
  gaAreas,
7527
8775
  hierarchyLabels,
7528
- freeTextLabels
8776
+ freeTextLabels,
8777
+ rowLabels
7529
8778
  };
7530
8779
  }
7531
8780
  /** Jump the camera to a rung's zoom band (glided). */
@@ -7537,7 +8786,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7537
8786
  }
7538
8787
  const target = rung === "sections" ? (SECTION_PROMINENT_SCALE + CACHE_THRESHOLD) / 2 : Math.max(
7539
8788
  this.seatLabelTargetScale() * 1.05,
7540
- SEAT_FOCUS_SCALE,
8789
+ SEAT_DETAIL_SCALE,
7541
8790
  CACHE_THRESHOLD * 1.3
7542
8791
  );
7543
8792
  const w = this.stage.width();
@@ -7824,14 +9073,25 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7824
9073
  for (const { objectId, node, backdrop, categoryKey, kind } of this.freeTextById.values()) {
7825
9074
  const gaDimmed = categoryKey != null && (kind === "ga-label" || kind === "ga-capacity") && this.gaCategoryDimmed(categoryKey);
7826
9075
  const gaOverviewHidden = objectId != null && (kind === "ga-label" || kind === "ga-capacity") && this.gaById.get(objectId)?.sectionId != null && effectiveScale < CACHE_THRESHOLD;
7827
- const visible = !gaDimmed && !gaOverviewHidden && isBookableLabelLegibleAtScale(node.fontSize(), effectiveScale);
9076
+ const focusedAnnotation = kind === "free-text" && this.nodeBelongsToFocusedSection(node);
9077
+ const visible = !gaDimmed && !gaOverviewHidden && (focusedAnnotation || isBookableLabelLegibleAtScale(node.fontSize(), effectiveScale));
7828
9078
  node.visible(visible);
7829
9079
  backdrop?.visible(visible);
7830
9080
  }
7831
9081
  for (const { node, fontSize } of this.iconNodeById.values()) {
7832
- node.visible(isBookableLabelLegibleAtScale(fontSize, effectiveScale));
9082
+ node.visible(this.nodeBelongsToFocusedSection(node) || isBookableLabelLegibleAtScale(fontSize, effectiveScale));
7833
9083
  }
7834
9084
  }
9085
+ /** Geometric association only: importer data remains unchanged. */
9086
+ nodeBelongsToFocusedSection(node) {
9087
+ const focus = this.focusedSectionId;
9088
+ if (!focus) return false;
9089
+ const matching = this.sections.filter((section) => section.id === focus || section.logicalId === focus || section.zone === focus);
9090
+ if (!matching.length) return false;
9091
+ const box = node.getClientRect({ relativeTo: this.bgLayer, skipShadow: true });
9092
+ const centre = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
9093
+ return matching.some((section) => pointInPolygonWithHoles(centre, section.outline, section.holes));
9094
+ }
7835
9095
  /** OV-45(a): resolve authored row-letter labels for the buyer/preview map, in
7836
9096
  * world space, matching the Designer's placement contract (start/end/both/none
7837
9097
  * preset, a free-drag `position` that wins over the preset, rotation, and
@@ -7841,18 +9101,25 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7841
9101
  this.rowLabelPlan = [];
7842
9102
  const background = this.theme.background ?? "#0e1117";
7843
9103
  const seatsByRow = /* @__PURE__ */ new Map();
9104
+ const addSeat = (key, seat) => {
9105
+ const list = seatsByRow.get(key) ?? [];
9106
+ list.push(seat);
9107
+ seatsByRow.set(key, list);
9108
+ };
7844
9109
  for (const seat of this.seats) {
7845
9110
  if (seat.kind === "booth") continue;
7846
9111
  const key = seat.logicalRowId ?? seat.rowId;
7847
- (seatsByRow.get(key) ?? seatsByRow.set(key, []).get(key)).push(seat);
9112
+ addSeat(key, seat);
9113
+ if (key !== seat.rowId) addSeat(seat.rowId, seat);
7848
9114
  }
7849
9115
  const seatOrder = (seat) => seat.logicalSeatIndex ?? Number(seat.id.split(":").pop() ?? 0);
7850
9116
  for (const obj of view.objects) {
7851
9117
  if (obj.type !== "row") continue;
7852
- if (obj.segmentedRow && obj.segmentedRow.componentIndex !== 0) continue;
7853
- const presentation = obj.segmentedRow?.labelPresentation ?? obj.labelPresentation;
9118
+ const repeatOnComponents = obj.segmentedRow?.repeatLabelOnComponents === true;
9119
+ if (obj.segmentedRow && !repeatOnComponents && obj.segmentedRow.componentIndex !== 0) continue;
9120
+ const presentation = repeatOnComponents ? obj.labelPresentation : obj.segmentedRow?.labelPresentation ?? obj.labelPresentation;
7854
9121
  if (presentation?.visible === false || presentation?.positionPreset === "none") continue;
7855
- const key = obj.segmentedRow ? obj.segmentedRow.groupId : obj.id;
9122
+ const key = obj.segmentedRow && !repeatOnComponents ? obj.segmentedRow.groupId : obj.id;
7856
9123
  const seats = (seatsByRow.get(key) ?? []).slice().sort((a, b) => seatOrder(a) - seatOrder(b));
7857
9124
  if (!seats.length) continue;
7858
9125
  const first = seats[0];
@@ -7867,6 +9134,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7867
9134
  const rotation = presentation?.rotation ?? 0;
7868
9135
  const text = obj.segmentedRow?.displayLabel ?? obj.displayLabel ?? obj.label;
7869
9136
  const opacity = this.objectFilteredOut(obj) ? 0.15 : 1;
9137
+ const containingSection = this.seatSection.get(first.id);
9138
+ const focusKeys = containingSection ? [...new Set([containingSection.id, containingSection.logicalId, containingSection.zone].filter(
9139
+ (value) => Boolean(value)
9140
+ ))] : void 0;
7870
9141
  const push = (x, y, automaticAway) => this.rowLabelPlan.push({
7871
9142
  text,
7872
9143
  x,
@@ -7875,6 +9146,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7875
9146
  fontSize,
7876
9147
  ink,
7877
9148
  opacity,
9149
+ ...focusKeys?.length ? { focusKeys } : {},
7878
9150
  ...automaticAway ? { automaticAway } : {}
7879
9151
  });
7880
9152
  if (presentation?.position) {
@@ -7894,6 +9166,18 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7894
9166
  push(last.x + away.x * gap, last.y + away.y * gap, away);
7895
9167
  }
7896
9168
  }
9169
+ const mergeRadius = (this.seatR + 12) * 2.2;
9170
+ const merged = [];
9171
+ for (const entry of this.rowLabelPlan) {
9172
+ const partner = merged.find((candidate) => candidate.text === entry.text && Math.hypot(candidate.x - entry.x, candidate.y - entry.y) <= mergeRadius);
9173
+ if (partner) {
9174
+ partner.x = (partner.x + entry.x) / 2;
9175
+ partner.y = (partner.y + entry.y) / 2;
9176
+ continue;
9177
+ }
9178
+ merged.push(entry);
9179
+ }
9180
+ this.rowLabelPlan = merged;
7897
9181
  }
7898
9182
  /** Axis-aligned world bounds of a centred, potentially rotated row label. */
7899
9183
  rowLabelBox(label, position, padding = ROW_LABEL_CLEARANCE) {
@@ -7970,12 +9254,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
7970
9254
  this.labelGroup.destroyChildren();
7971
9255
  this.labelLiftGroups.clear();
7972
9256
  this.seatLabelById.clear();
7973
- if (!show) {
7974
- this.overlayLayer.batchDraw();
7975
- return;
7976
- }
7977
9257
  let count = 0;
7978
- for (const seat of this.visibleSeatCandidates()) {
9258
+ if (show) for (const seat of this.visibleSeatCandidates()) {
7979
9259
  if (seat.kind === "booth") continue;
7980
9260
  const screen = this.worldToScreen(seat);
7981
9261
  if (screen.x < -20 || screen.x > this.stage.width() + 20 || screen.y < -20 || screen.y > this.stage.height() + 20) continue;
@@ -8066,13 +9346,16 @@ var _SeatmapRenderer = class _SeatmapRenderer {
8066
9346
  });
8067
9347
  }
8068
9348
  for (const rl of this.rowLabelPlan) {
9349
+ const focused = this.focusedSectionId != null && rl.focusKeys?.includes(this.focusedSectionId);
9350
+ const renderedFontSize = focused ? Math.max(rl.fontSize, FOCUSED_ROW_LABEL_MIN_PX / Math.max(effectiveScale, 1e-4)) : rl.fontSize;
9351
+ if (!focused && !isBookableLabelLegibleAtScale(renderedFontSize, effectiveScale)) continue;
8069
9352
  const screen = this.worldToScreen({ x: rl.x, y: rl.y });
8070
9353
  if (screen.x < -40 || screen.x > this.stage.width() + 40 || screen.y < -40 || screen.y > this.stage.height() + 40) continue;
8071
9354
  const t2 = new import_Text.Text({
8072
9355
  x: rl.x,
8073
9356
  y: rl.y,
8074
9357
  text: rl.text,
8075
- fontSize: rl.fontSize,
9358
+ fontSize: renderedFontSize,
8076
9359
  fontStyle: "700",
8077
9360
  fontFamily: this.labelFont(),
8078
9361
  fill: rl.ink,
@@ -8101,7 +9384,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
8101
9384
  const h = this.container.clientHeight || 1;
8102
9385
  if (w === this.stage.width() && h === this.stage.height()) return;
8103
9386
  this.stage.size({ width: w, height: h });
8104
- this.zoomToFit();
9387
+ this.refitCurrentView();
8105
9388
  }
8106
9389
  startFpsLoop() {
8107
9390
  const tick = (now) => {
@@ -8259,6 +9542,99 @@ function pickBestAvailable(seats, available, opts) {
8259
9542
  return { labels: ranked.slice(0, qty).map((r) => r.seat.label) };
8260
9543
  }
8261
9544
 
9545
+ // src/core/ticketRestrictions.ts
9546
+ function ticketOptionNotApplicableIssue(labels) {
9547
+ return {
9548
+ code: "ticket_option_not_applicable",
9549
+ labels,
9550
+ message: "That ticket choice is not available for the selected seat."
9551
+ };
9552
+ }
9553
+ function ticketTierAppliesToSeat(tier, seat) {
9554
+ const applicability = tier.applicability;
9555
+ if (!applicability) return true;
9556
+ if (applicability.seatLabels?.length && !applicability.seatLabels.includes(seat.label)) return false;
9557
+ if (applicability.accessibility?.length && !applicability.accessibility.some((key) => seat.accessibility?.includes(key))) return false;
9558
+ return true;
9559
+ }
9560
+ function applicableTicketTiers(doc, seat) {
9561
+ return (doc.categories.find((category) => category.key === seat.categoryKey)?.tiers ?? []).filter((tier) => ticketTierAppliesToSeat(tier, seat));
9562
+ }
9563
+ function resolveTicketTierForSeat(doc, seat, tierId) {
9564
+ const tiers = doc.categories.find((category) => category.key === seat.categoryKey)?.tiers;
9565
+ if (!tiers?.length) return { kind: "none" };
9566
+ const tier = tierId == null ? tiers.find((candidate) => ticketTierAppliesToSeat(candidate, seat)) : tiers.find((candidate) => candidate.id === tierId && ticketTierAppliesToSeat(candidate, seat));
9567
+ return tier ? { kind: "resolved", tier } : { kind: "invalid" };
9568
+ }
9569
+ function sameLogicalRow(a, b) {
9570
+ return (a.logicalRowId ?? a.rowId) === (b.logicalRowId ?? b.rowId);
9571
+ }
9572
+ function authoredIndex(seat) {
9573
+ if (Number.isInteger(seat.logicalSeatIndex)) return seat.logicalSeatIndex;
9574
+ const raw = seat.id.slice(seat.id.lastIndexOf(":") + 1);
9575
+ const index = Number(raw);
9576
+ return Number.isInteger(index) ? index : void 0;
9577
+ }
9578
+ function observablyAdjacent(a, b) {
9579
+ if (!sameLogicalRow(a, b)) return false;
9580
+ const aIndex = authoredIndex(a);
9581
+ const bIndex = authoredIndex(b);
9582
+ return aIndex != null && bIndex != null && Math.abs(aIndex - bIndex) === 1;
9583
+ }
9584
+ function companionSelectionIssue(doc, selections) {
9585
+ if (!selections.length) return null;
9586
+ const byLabel = new Map(expandChart(doc, { resolveEyeHeights: false }).map((seat) => [seat.label, seat]));
9587
+ const selected = selections.flatMap((selection) => {
9588
+ const seat = byLabel.get(selection.label);
9589
+ return seat ? [{ selection, seat }] : [];
9590
+ });
9591
+ const wheelchair = selected.map(({ seat }) => seat).filter((seat) => seat.accessibility?.includes("wheelchair"));
9592
+ const unpaired = [];
9593
+ for (const { selection, seat } of selected) {
9594
+ const resolution = resolveTicketTierForSeat(doc, seat, selection.tierId);
9595
+ const tier = resolution.kind === "resolved" ? resolution.tier : void 0;
9596
+ const isCompanion = seat.accessibility?.includes("companion") || tier?.restriction === "companion";
9597
+ if (isCompanion && !wheelchair.some((candidate) => candidate.id !== seat.id && observablyAdjacent(seat, candidate))) {
9598
+ unpaired.push(seat.label);
9599
+ }
9600
+ }
9601
+ return unpaired.length ? {
9602
+ code: "companion_pair_required",
9603
+ labels: unpaired,
9604
+ message: "Select the adjacent wheelchair place with each companion ticket."
9605
+ } : null;
9606
+ }
9607
+ function ticketSelectionIssue(doc, selections) {
9608
+ if (!selections.length) return null;
9609
+ const byLabel = new Map(expandChart(doc, { resolveEyeHeights: false }).map((seat) => [seat.label, seat]));
9610
+ const notForSaleCategories = new Set(
9611
+ doc.categories.filter((category) => category.notForSale).map((category) => category.key)
9612
+ );
9613
+ const notForSale = [];
9614
+ const inapplicable = [];
9615
+ for (const selection of selections) {
9616
+ const seat = byLabel.get(selection.label);
9617
+ if (!seat) continue;
9618
+ if (notForSaleCategories.has(seat.categoryKey)) {
9619
+ notForSale.push(seat.label);
9620
+ continue;
9621
+ }
9622
+ const resolution = resolveTicketTierForSeat(doc, seat, selection.tierId);
9623
+ if (resolution.kind === "invalid") inapplicable.push(seat.label);
9624
+ }
9625
+ if (notForSale.length) {
9626
+ return {
9627
+ code: "not_for_sale",
9628
+ labels: notForSale,
9629
+ message: "These seats are not for sale."
9630
+ };
9631
+ }
9632
+ if (inapplicable.length) {
9633
+ return ticketOptionNotApplicableIssue(inapplicable);
9634
+ }
9635
+ return companionSelectionIssue(doc, selections);
9636
+ }
9637
+
8262
9638
  // src/core/tableInventory.ts
8263
9639
  var GroupedTableProjectionError = class extends Error {
8264
9640
  constructor(objectId, message) {
@@ -8313,6 +9689,15 @@ function groupedTableInventory(doc, modelVersion) {
8313
9689
  // src/picker/PickerController.ts
8314
9690
  var DEFAULT_MAX_SELECTION = 10;
8315
9691
  var MAX_BACKOFF_MS = 15e3;
9692
+ var PickerSelectionError = class extends Error {
9693
+ constructor(issue) {
9694
+ super(issue.message);
9695
+ this.status = 422;
9696
+ this.name = "PickerSelectionError";
9697
+ this.reason = issue.code;
9698
+ this.labels = issue.labels;
9699
+ }
9700
+ };
8316
9701
  var MAP_THEME_KEYS = ["background", "rowLabelColor", "textColor", "selectionColor"];
8317
9702
  function sameMapTheme(a, b) {
8318
9703
  if (!a || !b) return !a && !b;
@@ -8442,7 +9827,9 @@ var PickerController = class {
8442
9827
  if (!r) return true;
8443
9828
  const selected = r.getSelection().map((seat) => seat.id);
8444
9829
  const floorId = this.getActiveFloorId();
9830
+ const floorOverview = r.isFloorOverview?.() ?? false;
8445
9831
  r.setChart(this.visibleDoc(), floorId ? { floorId } : void 0);
9832
+ if (floorOverview) r.setFloorOverview?.(true);
8446
9833
  if (this.opts.colorblindSafe) r.setColorblindSafe?.(true);
8447
9834
  if (this.closedSections.size) r.setClosedSections?.([...this.closedSections]);
8448
9835
  this.applySeatsMap(Object.fromEntries(this.liveStatuses), this.liveDefault);
@@ -8454,7 +9841,9 @@ var PickerController = class {
8454
9841
  const next = ids.filter((x) => typeof x === "string");
8455
9842
  if (next.length === this.hidden.size && next.every((id) => this.hidden.has(id))) return false;
8456
9843
  this.hidden = new Set(next);
9844
+ const floorOverview = this.renderer?.isFloorOverview?.() ?? false;
8457
9845
  this.renderer?.setChart(this.visibleDoc());
9846
+ if (floorOverview) this.renderer?.setFloorOverview?.(true);
8458
9847
  return true;
8459
9848
  }
8460
9849
  /** Adopt a new closed-section set; restyle (grey + non-pickable) if it differs.
@@ -8529,16 +9918,22 @@ var PickerController = class {
8529
9918
  /** Fetch chart, build label maps, mount the renderer, seed statuses, go live. */
8530
9919
  async render(host) {
8531
9920
  if (this.renderer) return null;
9921
+ const renderStartedAt = performance.now();
9922
+ const profile = {};
9923
+ const duration = (startedAt) => Math.round((performance.now() - startedAt) * 100) / 100;
8532
9924
  this.closed = false;
8533
9925
  let res;
9926
+ const chartStartedAt = performance.now();
8534
9927
  try {
8535
9928
  res = await this.api.chart(this.key);
8536
9929
  } catch (err) {
8537
9930
  this.emitError(err);
8538
9931
  return null;
8539
9932
  }
9933
+ profile.loadChart = duration(chartStartedAt);
8540
9934
  if (this.closed) return null;
8541
9935
  this._doc = res.doc;
9936
+ const normalizeStartedAt = performance.now();
8542
9937
  try {
8543
9938
  const groups = groupedTableInventory(
8544
9939
  res.doc,
@@ -8603,13 +9998,16 @@ var PickerController = class {
8603
9998
  this.labelToId.set(group.label, representative.id);
8604
9999
  this.labelToSeat.set(group.label, representative);
8605
10000
  }
10001
+ profile.normalizeBuyerModel = duration(normalizeStartedAt);
8606
10002
  const currency = res.event.currency ?? this.opts.currency;
8607
10003
  this.currency = currency ?? "USD";
10004
+ const rendererStartedAt = performance.now();
8608
10005
  const renderer = createRenderer(host, {
8609
10006
  // Group chairs are selected together for paint, but count as one logical
8610
10007
  // inventory line. The controller enforces the guest-weighted cap.
8611
10008
  maxSelection: this.rendererSelectionCap(),
8612
10009
  confirmSelection: this.opts.confirmSelection,
10010
+ portraitFitCrop: true,
8613
10011
  currency,
8614
10012
  onSelect: (seat) => {
8615
10013
  this.handleRendererSelect(seat);
@@ -8630,6 +10028,7 @@ var PickerController = class {
8630
10028
  onDeckTap: (floorId) => this.handleDeckTap(floorId),
8631
10029
  onFps: this.opts.onFps
8632
10030
  });
10031
+ profile.createRenderer = duration(rendererStartedAt);
8633
10032
  if (this.closed) {
8634
10033
  renderer.destroy();
8635
10034
  return null;
@@ -8638,6 +10037,7 @@ var PickerController = class {
8638
10037
  let seats = null;
8639
10038
  let seatsDefault = "free";
8640
10039
  let closedIds = [];
10040
+ const statusesStartedAt = performance.now();
8641
10041
  try {
8642
10042
  const objs = await this.api.objects(this.key);
8643
10043
  this.hidden = new Set(objs.hidden ?? []);
@@ -8646,18 +10046,30 @@ var PickerController = class {
8646
10046
  seatsDefault = objs.default ?? "free";
8647
10047
  } catch {
8648
10048
  }
10049
+ profile.loadStatuses = duration(statusesStartedAt);
8649
10050
  if (this.closed) {
8650
10051
  renderer.destroy();
8651
10052
  this.renderer = null;
8652
10053
  return null;
8653
10054
  }
10055
+ const sceneStartedAt = performance.now();
8654
10056
  renderer.setChart(this.visibleDoc());
10057
+ if ((res.doc.floors?.length ?? 0) > 1) renderer.setFloorOverview?.(true);
8655
10058
  if (this.opts.colorblindSafe) renderer.setColorblindSafe?.(true);
10059
+ profile.buildScene = duration(sceneStartedAt);
8656
10060
  this.closedSections = new Set(closedIds);
10061
+ const initialStateStartedAt = performance.now();
8657
10062
  if (closedIds.length) renderer.setClosedSections?.(closedIds);
8658
10063
  if (seats) this.applySeatsMap(seats, seatsDefault);
10064
+ profile.applyInitialState = duration(initialStateStartedAt);
10065
+ const firstPaintStartedAt = performance.now();
10066
+ renderer.forceDraw();
10067
+ profile.firstPaint = duration(firstPaintStartedAt);
10068
+ const realtimeStartedAt = performance.now();
8659
10069
  this.attachVisibilityListener();
8660
10070
  this.connect();
10071
+ profile.startRealtime = duration(realtimeStartedAt);
10072
+ profile.controllerTotal = duration(renderStartedAt);
8661
10073
  return {
8662
10074
  doc: res.doc,
8663
10075
  salesClosed: !!res.event.salesClosed,
@@ -8668,7 +10080,8 @@ var PickerController = class {
8668
10080
  currency,
8669
10081
  // 'test' ⇒ sandbox event: hosts render a TEST MODE ribbon (Phase 11).
8670
10082
  mode: res.event.mode ?? "live",
8671
- posterUrl: res.event.posterUrl ?? null
10083
+ posterUrl: res.event.posterUrl ?? null,
10084
+ performanceProfile: profile
8672
10085
  };
8673
10086
  }
8674
10087
  // ---- selection ------------------------------------------------------------
@@ -8724,11 +10137,23 @@ var PickerController = class {
8724
10137
  select(ids) {
8725
10138
  const before = new Set(this.getSelection().map((seat) => seat.label));
8726
10139
  const expanded = /* @__PURE__ */ new Set();
10140
+ const rejected = [];
8727
10141
  for (const id of ids) {
8728
10142
  const table = this.groupedTableBySeatId.get(id) ?? this.groupedTablesByLabel.get(id);
8729
- if (table) table.chairs.forEach((chair) => expanded.add(chair.id));
8730
- else expanded.add(id);
10143
+ if (table) {
10144
+ const resolution = this.tableTierResolution(table);
10145
+ if (resolution.kind === "invalid") rejected.push(table.label);
10146
+ else table.chairs.forEach((chair) => expanded.add(chair.id));
10147
+ } else {
10148
+ const seat = this.seatById.get(id);
10149
+ if (seat && this.seatTierResolution(seat).kind === "invalid") rejected.push(seat.label);
10150
+ else expanded.add(id);
10151
+ }
8731
10152
  }
10153
+ if (rejected.length) {
10154
+ this.emitError(new PickerSelectionError(ticketOptionNotApplicableIssue(rejected)));
10155
+ }
10156
+ if (!expanded.size) return [];
8732
10157
  this.renderer?.select?.([...expanded]);
8733
10158
  if (this.selectionGuestCount() > this.maxSelection) {
8734
10159
  this.renderer?.deselect([...expanded]);
@@ -8796,6 +10221,13 @@ var PickerController = class {
8796
10221
  }
8797
10222
  handleRendererSelect(seat) {
8798
10223
  const table = this.groupedTableBySeatId.get(seat.id);
10224
+ const resolution = table ? this.tableTierResolution(table) : this.seatTierResolution(seat);
10225
+ if (resolution.kind === "invalid") {
10226
+ this.renderer?.deselect(table ? table.chairs.map((chair) => chair.id) : [seat.id]);
10227
+ this.emitError(new PickerSelectionError(ticketOptionNotApplicableIssue([table?.label ?? seat.label])));
10228
+ this.emitSelectionChange();
10229
+ return;
10230
+ }
8799
10231
  if (table) {
8800
10232
  if (this.selectionGuestCount() > this.maxSelection) {
8801
10233
  this.renderer?.deselect(table.chairs.map((chair) => chair.id));
@@ -8877,6 +10309,8 @@ var PickerController = class {
8877
10309
  const existingGA = (this.hold_?.items ?? []).filter((item) => item.objectType === "ga");
8878
10310
  const combinedLabels = [.../* @__PURE__ */ new Set([...existingGA.map((item) => item.label), ...labels])];
8879
10311
  if (!combinedLabels.length) return null;
10312
+ const restriction = this.ticketIssue(combinedLabels);
10313
+ if (restriction) throw new PickerSelectionError(restriction);
8880
10314
  if (this.hold_ && this.holdCovers(combinedLabels)) return this.hold_;
8881
10315
  try {
8882
10316
  const selections = combinedLabels.map((label) => {
@@ -9048,6 +10482,7 @@ var PickerController = class {
9048
10482
  const available = /* @__PURE__ */ new Set();
9049
10483
  for (const seat of seats) {
9050
10484
  if (held.has(seat.label)) continue;
10485
+ if (this.seatTierResolution(seat).kind === "invalid") continue;
9051
10486
  const id = this.labelToId.get(seat.label);
9052
10487
  const status = id ? this.getStatus(id) : void 0;
9053
10488
  if ((status ?? "free") === "free") available.add(seat.label);
@@ -9129,6 +10564,8 @@ var PickerController = class {
9129
10564
  if (!this.api.book) throw new Error("picker: transport has no book() \u2014 hold-only mode");
9130
10565
  const labels = labelsArg ?? this.getSelection().map((seat) => seat.label);
9131
10566
  if (!labels.length) return null;
10567
+ const restriction = this.ticketIssue(labels);
10568
+ if (restriction) throw new PickerSelectionError(restriction);
9132
10569
  let holdId;
9133
10570
  try {
9134
10571
  if (this.hold_ && this.holdCovers(labels)) {
@@ -9255,6 +10692,20 @@ var PickerController = class {
9255
10692
  getStatus(id) {
9256
10693
  return this.renderer?.getStatus(id);
9257
10694
  }
10695
+ /** Seat ids whose category the author marked not-for-sale. */
10696
+ notForSaleCategorySeatIds() {
10697
+ const doc = this.doc;
10698
+ if (!doc) return [];
10699
+ const flagged = new Set(
10700
+ doc.categories.filter((category) => category.notForSale).map((category) => category.key)
10701
+ );
10702
+ if (!flagged.size) return [];
10703
+ const ids = [];
10704
+ for (const [id, seat] of this.seatById) {
10705
+ if (flagged.has(seat.categoryKey)) ids.push(id);
10706
+ }
10707
+ return ids;
10708
+ }
9258
10709
  flashSeat(id, color) {
9259
10710
  this.renderer?.flashSeat(id, color);
9260
10711
  }
@@ -9267,6 +10718,11 @@ var PickerController = class {
9267
10718
  zoomToFit() {
9268
10719
  this.renderer?.zoomToFit();
9269
10720
  }
10721
+ /** Preserve section focus while the usable map viewport changes. */
10722
+ refitCurrentView() {
10723
+ if (this.renderer?.refitCurrentView) this.renderer.refitCurrentView();
10724
+ else this.renderer?.zoomToFit();
10725
+ }
9270
10726
  worldToScreen(p) {
9271
10727
  return this.renderer?.worldToScreen(p) ?? { x: 0, y: 0 };
9272
10728
  }
@@ -9296,7 +10752,8 @@ var PickerController = class {
9296
10752
  /** Switch the shown floor (2D), then re-apply live seat statuses onto it. */
9297
10753
  setFloor(id) {
9298
10754
  const r = this.renderer;
9299
- if (!r?.setActiveFloor || id === r.getActiveFloorId?.()) return;
10755
+ if (!r?.setActiveFloor || id === r.getActiveFloorId?.() && !r.isFloorOverview?.()) return;
10756
+ r.setFloorOverview?.(false);
9300
10757
  r.setStacked?.(false);
9301
10758
  r.setActiveFloor(id);
9302
10759
  void this.resnapshot();
@@ -9312,12 +10769,24 @@ var PickerController = class {
9312
10769
  isMultiFloor() {
9313
10770
  return (this._doc?.floors?.length ?? 0) > 1;
9314
10771
  }
10772
+ /** Enter/leave the source-coordinate all-floor buyer overview. */
10773
+ setFloorOverview(on) {
10774
+ const r = this.renderer;
10775
+ if (!r?.setFloorOverview || on === r.isFloorOverview?.()) return;
10776
+ r.setFloorOverview(on);
10777
+ void this.resnapshot();
10778
+ this.emitSelectionChange();
10779
+ }
10780
+ isFloorOverview() {
10781
+ return this.renderer?.isFloorOverview?.() ?? false;
10782
+ }
9315
10783
  /** Tap-a-deck-to-enter: leave the 3D stack, drop to flat 2D on `floorId`, and
9316
10784
  * tell the host so it can sync its 2D/3D toggle + floor-switcher state. */
9317
10785
  handleDeckTap(floorId) {
9318
10786
  const r = this.renderer;
9319
10787
  if (!r) return;
9320
10788
  r.setViewMode?.("flat");
10789
+ r.setFloorOverview?.(false);
9321
10790
  r.setStacked?.(false);
9322
10791
  r.setActiveFloor?.(floorId);
9323
10792
  void this.resnapshot();
@@ -9386,10 +10855,30 @@ var PickerController = class {
9386
10855
  if (!r?.getVisibleWorldRect || !r.getWorldBounds) return null;
9387
10856
  return { visible: r.getVisibleWorldRect(), bounds: r.getWorldBounds() };
9388
10857
  }
10858
+ getMinimapSnapshot() {
10859
+ return this.renderer?.getMinimapSnapshot?.() ?? { sections: [], seats: [] };
10860
+ }
10861
+ panToWorld(point) {
10862
+ this.renderer?.panToWorld?.(point);
10863
+ }
10864
+ /** Leave section-focus dimming without changing the current zoom. Used when
10865
+ * the minimap viewport itself is dragged to a different part of the venue. */
10866
+ clearSectionFocus() {
10867
+ this.renderer?.clearSectionFocus?.();
10868
+ this.opts.onSectionFocus?.(null);
10869
+ }
10870
+ /** Read-only browser-render evidence for parity/performance audit tooling. */
10871
+ getRenderedQualityEvidence() {
10872
+ return this.renderer?.getRenderedQualityEvidence() ?? null;
10873
+ }
9389
10874
  /** Glide in on a section and surface its summary (same path as a section tap). */
9390
10875
  focusSection(id) {
9391
10876
  this.handleSectionTap(id);
9392
10877
  }
10878
+ /** Live seat membership count used by buyer section-navigation controls. */
10879
+ getSectionSeatCount(id) {
10880
+ return this.renderer?.sectionMembers?.(id)?.length ?? 0;
10881
+ }
9393
10882
  /** Zoom back out to the whole chart and clear the section-summary card + focus. */
9394
10883
  overview() {
9395
10884
  this.renderer?.clearSectionFocus?.();
@@ -9442,15 +10931,21 @@ var PickerController = class {
9442
10931
  }
9443
10932
  const categories = [...byCat.entries()].map(([key, count]) => {
9444
10933
  const cat = doc.categories.find((c) => c.key === key);
10934
+ const prices = cat?.tiers?.length ? cat.tiers.map((tier) => tier.price) : [cat?.price ?? 0];
10935
+ const priceMin = Math.min(...prices);
10936
+ const priceMax = Math.max(...prices);
9445
10937
  return {
9446
10938
  key,
9447
10939
  count,
9448
10940
  label: cat?.label ?? key,
9449
10941
  color: cat?.color ?? "#6e7bff",
9450
- price: cat?.price ?? 0
10942
+ price: priceMin,
10943
+ priceMin,
10944
+ priceMax
9451
10945
  };
9452
10946
  }).sort((a, b) => a.price - b.price);
9453
- const prices = categories.map((c) => c.price);
10947
+ const minimums = categories.map((c) => c.priceMin);
10948
+ const maximums = categories.map((c) => c.priceMax);
9454
10949
  const zone = sec.zone ? doc.zones?.find((z) => z.id === sec.zone) : void 0;
9455
10950
  const color = sec.color ?? zone?.color ?? categories[0]?.color ?? "#6e7bff";
9456
10951
  return {
@@ -9460,8 +10955,8 @@ var PickerController = class {
9460
10955
  ...sec.entrance && sec.entrance.trim() ? { entrance: sec.entrance.trim() } : {},
9461
10956
  color,
9462
10957
  seatsLeft,
9463
- priceMin: prices.length ? prices[0] : 0,
9464
- priceMax: prices.length ? prices[prices.length - 1] : 0,
10958
+ priceMin: minimums.length ? Math.min(...minimums) : 0,
10959
+ priceMax: maximums.length ? Math.max(...maximums) : 0,
9465
10960
  categories
9466
10961
  };
9467
10962
  }
@@ -9502,11 +10997,12 @@ var PickerController = class {
9502
10997
  const accessibility = s.accessibility?.length ? { accessibility: s.accessibility } : void 0;
9503
10998
  const wheelchair = s.wheelchairSpaceType ? { wheelchairSpaceType: s.wheelchairSpaceType } : void 0;
9504
10999
  const context = this.seatContext.get(s.id);
9505
- const tiers = this.tiersFor(s.categoryKey);
9506
- if (!tiers) {
9507
- return { id: s.id, label: s.label, ...display, ...context, categoryKey: s.categoryKey, price: this.priceFor(s.categoryKey), ...commercial, ...accessibility, ...wheelchair };
11000
+ const tiers = this.tiersForSeat(s);
11001
+ const resolution = this.seatTierResolution(s);
11002
+ if (resolution.kind !== "resolved" || !tiers) {
11003
+ return { id: s.id, label: s.label, ...display, ...context, categoryKey: s.categoryKey, price: this.priceFor(s.categoryKey), currency: this.currency, ...commercial, ...accessibility, ...wheelchair };
9508
11004
  }
9509
- const chosen = tiers.find((t2) => t2.id === this.seatTiers.get(s.id)) ?? tiers[0];
11005
+ const chosen = resolution.tier;
9510
11006
  return {
9511
11007
  id: s.id,
9512
11008
  label: s.label,
@@ -9514,6 +11010,7 @@ var PickerController = class {
9514
11010
  ...context,
9515
11011
  categoryKey: s.categoryKey,
9516
11012
  price: chosen.price,
11013
+ currency: this.currency,
9517
11014
  tiers,
9518
11015
  tierId: chosen.id,
9519
11016
  ...commercial,
@@ -9523,8 +11020,9 @@ var PickerController = class {
9523
11020
  }
9524
11021
  toTableSeat(table) {
9525
11022
  const representative = table.chairs[0];
9526
- const tiers = this.tiersFor(table.categoryKey);
9527
- const chosen = tiers?.find((tier) => tier.id === this.seatTiers.get(representative?.id ?? "")) ?? tiers?.[0];
11023
+ const tiers = this.tiersFor(table.categoryKey)?.filter((tier) => resolveTicketTierForSeat(this._doc, this.tableTierSeat(table), tier.id).kind === "resolved");
11024
+ const resolution = this.tableTierResolution(table);
11025
+ const chosen = resolution.kind === "resolved" ? resolution.tier : void 0;
9528
11026
  return {
9529
11027
  id: representative?.id ?? table.objectId,
9530
11028
  label: table.label,
@@ -9535,6 +11033,7 @@ var PickerController = class {
9535
11033
  rowLabel: table.displayLabel ?? table.label,
9536
11034
  categoryKey: table.categoryKey,
9537
11035
  price: chosen?.price ?? this.priceFor(table.categoryKey),
11036
+ currency: this.currency,
9538
11037
  ...tiers ? { tiers, tierId: chosen?.id } : {},
9539
11038
  objectType: "table",
9540
11039
  bookingMode: table.mode,
@@ -9564,6 +11063,27 @@ var PickerController = class {
9564
11063
  const t2 = this._doc?.categories.find((c) => c.key === categoryKey)?.tiers;
9565
11064
  return t2 && t2.length ? t2 : void 0;
9566
11065
  }
11066
+ tiersForSeat(seat) {
11067
+ if (!this._doc) return void 0;
11068
+ const tiers = applicableTicketTiers(this._doc, seat);
11069
+ return tiers.length ? tiers : void 0;
11070
+ }
11071
+ seatTierResolution(seat) {
11072
+ if (!this._doc) return { kind: "none" };
11073
+ return resolveTicketTierForSeat(this._doc, seat, this.seatTiers.get(seat.id));
11074
+ }
11075
+ tableTierSeat(table) {
11076
+ return {
11077
+ label: table.label,
11078
+ categoryKey: table.categoryKey,
11079
+ accessibility: table.chairs.flatMap((chair) => chair.accessibility ?? [])
11080
+ };
11081
+ }
11082
+ tableTierResolution(table) {
11083
+ if (!this._doc) return { kind: "none" };
11084
+ const representativeId = table.chairs[0]?.id ?? "";
11085
+ return resolveTicketTierForSeat(this._doc, this.tableTierSeat(table), this.seatTiers.get(representativeId));
11086
+ }
9567
11087
  /**
9568
11088
  * Choose a ticket tier for a selected seat (e.g. Adult → Child). Re-emits the
9569
11089
  * selection so the host's cart + the eventual hold carry the new tier + price.
@@ -9573,7 +11093,7 @@ var PickerController = class {
9573
11093
  setSeatTier(seatId, tierId) {
9574
11094
  const seat = this.seatById.get(seatId);
9575
11095
  if (!seat) return;
9576
- const tiers = this.tiersFor(seat.categoryKey);
11096
+ const tiers = this.tiersForSeat(seat);
9577
11097
  if (!tiers) return;
9578
11098
  if (tierId == null) this.seatTiers.delete(seatId);
9579
11099
  else if (tiers.some((t2) => t2.id === tierId)) this.seatTiers.set(seatId, tierId);
@@ -9594,6 +11114,16 @@ var PickerController = class {
9594
11114
  this.opts.onSelectionChange?.(this.getSelection());
9595
11115
  this.emitOrphanHint();
9596
11116
  }
11117
+ /** Check explicit tier applicability and companion relationships for the
11118
+ * current (or supplied) seats. Ticket display names are never semantic input. */
11119
+ ticketIssue(labels) {
11120
+ if (!this._doc) return null;
11121
+ const seats = labels ? this.seatsForLabels(labels) : this.getSelection();
11122
+ return ticketSelectionIssue(this._doc, seats.map((seat) => ({
11123
+ label: seat.label,
11124
+ ...seat.tierId ? { tierId: seat.tierId } : {}
11125
+ })));
11126
+ }
9597
11127
  /**
9598
11128
  * Orphan-seat advice on manual selection: when the buyer's current picks
9599
11129
  * strand one (or more) single free seats between unavailable same-row
@@ -9604,6 +11134,12 @@ var PickerController = class {
9604
11134
  if (!this.opts.onHint) return;
9605
11135
  const r = this.renderer;
9606
11136
  if (!r) return;
11137
+ const restriction = this.ticketIssue();
11138
+ if (restriction) {
11139
+ this.hintShown = true;
11140
+ this.opts.onHint(restriction.code === "companion_pair_required" ? t("picker.companionPairRequired") : "That ticket choice is not available for the selected seat.");
11141
+ return;
11142
+ }
9607
11143
  const selected = new Set(r.getSelection().map((s) => s.id));
9608
11144
  const stranded = selected.size ? strandedSingles(this.seatById.values(), (id) => r.getStatus(id), selected) : [];
9609
11145
  if (stranded.length > 0) {
@@ -9725,6 +11261,8 @@ var PickerController = class {
9725
11261
  ["free", "held", "booked", "not_for_sale"].forEach((st) => {
9726
11262
  if (st !== defaultSeatStatus && byStatus[st].length) r.setStatus(byStatus[st], st);
9727
11263
  });
11264
+ const notForSaleCategoryIds = this.notForSaleCategorySeatIds();
11265
+ if (notForSaleCategoryIds.length) r.setStatus(notForSaleCategoryIds, "not_for_sale");
9728
11266
  this.clearBookedHoldIfSettled();
9729
11267
  this.opts.onStatusChange?.();
9730
11268
  }
@@ -11110,6 +12648,7 @@ async function loadLocale(code) {
11110
12648
  MAX_EVENT_INVENTORY,
11111
12649
  MAX_GA_CAPACITY,
11112
12650
  PickerController,
12651
+ PickerSelectionError,
11113
12652
  RENDERED_QUALITY_REPORT_VERSION,
11114
12653
  SEAT_COMMERCIAL_MARKS,
11115
12654
  SUPPORTED_LOCALES,
@@ -11133,6 +12672,7 @@ async function loadLocale(code) {
11133
12672
  expandTable,
11134
12673
  expandTableSlots,
11135
12674
  floorObjects,
12675
+ floorOverviewTranslations,
11136
12676
  floorsOf,
11137
12677
  formatDate,
11138
12678
  formatMinor,
@@ -11151,6 +12691,7 @@ async function loadLocale(code) {
11151
12691
  isGaUnitLabel,
11152
12692
  isSectionHidden,
11153
12693
  layerOf,
12694
+ layoutFloorOverview,
11154
12695
  loadLocale,
11155
12696
  objectCenter,
11156
12697
  owningSectionForObject,
@@ -11170,6 +12711,7 @@ async function loadLocale(code) {
11170
12711
  tCount,
11171
12712
  tableInventoryCount,
11172
12713
  tableSeatCountsBySide,
12714
+ translateObject,
11173
12715
  validGAInventorySegments
11174
12716
  });
11175
12717
  //# sourceMappingURL=index.cjs.map