@asksable/site-connector 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -1
- package/dist/bones/booking-widget.bones.json +495 -0
- package/dist/bones/registry.d.ts +2 -0
- package/dist/bones/registry.d.ts.map +1 -0
- package/dist/bones/registry.js +10 -0
- package/dist/bones/registry.js.map +1 -0
- package/dist/booking-widget.d.ts +1 -0
- package/dist/booking-widget.d.ts.map +1 -1
- package/dist/booking-widget.js +640 -300
- package/dist/booking-widget.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +8 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +20 -1
- package/dist/provider.js.map +1 -1
- package/dist/styles.css +725 -325
- package/dist/translations.d.ts +282 -0
- package/dist/translations.d.ts.map +1 -0
- package/dist/translations.js +348 -0
- package/dist/translations.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/styles.css
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/* Mobile-web hygiene scoped to the widget. Each rule is a known
|
|
2
|
+
iOS Safari / Android Chrome gotcha called out by the alvsr-mobile-
|
|
3
|
+
ui-review audit. -webkit-text-size-adjust stops iOS Safari from
|
|
4
|
+
auto-enlarging the widget's text in landscape; -webkit-tap-
|
|
5
|
+
highlight-color removes the default blue tap flash; touch-action:
|
|
6
|
+
manipulation kills the legacy 300ms double-tap delay on iOS so
|
|
7
|
+
buttons feel responsive. */
|
|
8
|
+
.bw,
|
|
9
|
+
.bw * {
|
|
10
|
+
-webkit-text-size-adjust: 100%;
|
|
11
|
+
-webkit-tap-highlight-color: transparent;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.bw button,
|
|
15
|
+
.bw [role='button'],
|
|
16
|
+
.bw a {
|
|
17
|
+
touch-action: manipulation;
|
|
18
|
+
}
|
|
19
|
+
|
|
1
20
|
.bw {
|
|
2
21
|
/* Theme tokens — overridable per host site (color + font). */
|
|
3
22
|
--bw-font: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
@@ -77,43 +96,6 @@
|
|
|
77
96
|
animation: bw-shimmer 1.5s ease infinite;
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
.bw-skel--title {
|
|
81
|
-
width: min(340px, 70%);
|
|
82
|
-
height: 32px;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.bw-skel--text {
|
|
86
|
-
width: 48%;
|
|
87
|
-
height: 14px;
|
|
88
|
-
margin-top: 14px;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.bw-skel--text-lg {
|
|
92
|
-
width: min(520px, 85%);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.bw-skel--card {
|
|
96
|
-
width: 100%;
|
|
97
|
-
height: 56px;
|
|
98
|
-
margin-bottom: 10px;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.bw-skel--calendar {
|
|
102
|
-
width: 100%;
|
|
103
|
-
height: 300px;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.bw-skel--input {
|
|
107
|
-
width: 100%;
|
|
108
|
-
height: 40px;
|
|
109
|
-
margin-bottom: 12px;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.bw-skel--textarea {
|
|
113
|
-
width: 100%;
|
|
114
|
-
height: 92px;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
99
|
.bw-skel--slot {
|
|
118
100
|
/* Default mobile pill height: matches .bw-slot { padding: 12px 0;
|
|
119
101
|
font-size: 13px } ≈ 44px. Desktop overrides this below to match
|
|
@@ -122,198 +104,6 @@
|
|
|
122
104
|
border-radius: var(--bw-radius);
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
.bw-skel-list {
|
|
126
|
-
display: flex;
|
|
127
|
-
flex-direction: column;
|
|
128
|
-
gap: 0;
|
|
129
|
-
margin-top: 16px;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.bw-skel-slots {
|
|
133
|
-
margin-top: 8px;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.bw-skel-slots-grid {
|
|
137
|
-
display: grid;
|
|
138
|
-
grid-template-columns: repeat(4, 1fr);
|
|
139
|
-
gap: 10px;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.bw-skel-mobile {
|
|
143
|
-
display: none;
|
|
144
|
-
padding: 0 24px 24px;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/* === Initial-load skeleton chrome ===
|
|
148
|
-
Each skeleton primitive mirrors a real widget element so the
|
|
149
|
-
chrome doesn't reflow when actual content arrives. */
|
|
150
|
-
|
|
151
|
-
.bw-skel--label {
|
|
152
|
-
width: 110px;
|
|
153
|
-
height: 18px;
|
|
154
|
-
margin-bottom: 16px;
|
|
155
|
-
border-radius: var(--bw-radius-sm);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/* Service group spacing matches .bw-svc-group + .bw-svc-group rules */
|
|
159
|
-
.bw-skel-svc-group {
|
|
160
|
-
display: flex;
|
|
161
|
-
flex-direction: column;
|
|
162
|
-
gap: 14px;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.bw-skel-svc-group + .bw-skel-svc-group {
|
|
166
|
-
margin-top: 28px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.bw-skel--svc-category {
|
|
170
|
-
width: 90px;
|
|
171
|
-
height: 14px;
|
|
172
|
-
border-radius: var(--bw-radius-sm);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/* Service row: 80x80 image + flex column with name/desc/meta lines.
|
|
176
|
-
Mirrors the rendered .bw-svc-row layout exactly. */
|
|
177
|
-
.bw-skel-svc-row {
|
|
178
|
-
display: flex;
|
|
179
|
-
align-items: flex-start;
|
|
180
|
-
gap: 16px;
|
|
181
|
-
padding: 8px 0;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.bw-skel--svc-image {
|
|
185
|
-
flex-shrink: 0;
|
|
186
|
-
width: 80px;
|
|
187
|
-
height: 80px;
|
|
188
|
-
border-radius: var(--bw-radius-md);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.bw-skel-svc-info {
|
|
192
|
-
display: flex;
|
|
193
|
-
flex: 1;
|
|
194
|
-
min-width: 0;
|
|
195
|
-
flex-direction: column;
|
|
196
|
-
gap: 8px;
|
|
197
|
-
padding-top: 2px;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.bw-skel--svc-name {
|
|
201
|
-
width: 70%;
|
|
202
|
-
height: 16px;
|
|
203
|
-
border-radius: var(--bw-radius-sm);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.bw-skel--svc-desc {
|
|
207
|
-
width: 100%;
|
|
208
|
-
height: 14px;
|
|
209
|
-
border-radius: var(--bw-radius-sm);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.bw-skel--svc-meta {
|
|
213
|
-
width: 50%;
|
|
214
|
-
height: 14px;
|
|
215
|
-
margin-top: 4px;
|
|
216
|
-
border-radius: var(--bw-radius-sm);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/* Calendar card: month header row, 7-col day-of-week strip, 6-row
|
|
220
|
-
day grid (42 cells), timezone chip below. */
|
|
221
|
-
.bw-skel-cal-card {
|
|
222
|
-
/* The real .bw-cal-card supplies its own padding/border/radius;
|
|
223
|
-
just lay the skeleton primitives inside it. */
|
|
224
|
-
display: flex;
|
|
225
|
-
flex-direction: column;
|
|
226
|
-
gap: 12px;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.bw-skel-cal-header {
|
|
230
|
-
display: flex;
|
|
231
|
-
align-items: center;
|
|
232
|
-
justify-content: space-between;
|
|
233
|
-
gap: 16px;
|
|
234
|
-
margin-bottom: 4px;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.bw-skel--cal-month {
|
|
238
|
-
width: 130px;
|
|
239
|
-
height: 22px;
|
|
240
|
-
border-radius: var(--bw-radius-sm);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.bw-skel-cal-navs {
|
|
244
|
-
display: flex;
|
|
245
|
-
gap: 8px;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.bw-skel--cal-nav {
|
|
249
|
-
width: 32px;
|
|
250
|
-
height: 32px;
|
|
251
|
-
border-radius: var(--bw-radius-pill);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.bw-skel-cal-dows {
|
|
255
|
-
display: grid;
|
|
256
|
-
grid-template-columns: repeat(7, 1fr);
|
|
257
|
-
gap: 4px;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.bw-skel--cal-dow {
|
|
261
|
-
height: 14px;
|
|
262
|
-
border-radius: var(--bw-radius-sm);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.bw-skel-cal-grid {
|
|
266
|
-
display: grid;
|
|
267
|
-
grid-template-columns: repeat(7, 1fr);
|
|
268
|
-
gap: 4px;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.bw-skel--cal-day {
|
|
272
|
-
height: 44px;
|
|
273
|
-
border-radius: var(--bw-radius-sm);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.bw-skel--cal-tz {
|
|
277
|
-
width: 140px;
|
|
278
|
-
height: 16px;
|
|
279
|
-
margin: 12px auto 0;
|
|
280
|
-
border-radius: var(--bw-radius-sm);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/* Slots column heading + pills. */
|
|
284
|
-
.bw-skel--slots-heading {
|
|
285
|
-
width: 60%;
|
|
286
|
-
height: 18px;
|
|
287
|
-
margin-bottom: 16px;
|
|
288
|
-
border-radius: var(--bw-radius-sm);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/* Skeleton body uses the same grid as the real .bw-body so column
|
|
292
|
-
widths match without reflow. */
|
|
293
|
-
.bw-skel-body {
|
|
294
|
-
display: grid;
|
|
295
|
-
grid-template-columns: 28% 1fr 22%;
|
|
296
|
-
gap: 32px;
|
|
297
|
-
padding: 32px 48px 48px;
|
|
298
|
-
align-items: start;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
@media (max-width: 1024px) {
|
|
302
|
-
.bw-skel-body {
|
|
303
|
-
display: flex;
|
|
304
|
-
flex-direction: column;
|
|
305
|
-
padding: 16px 24px 24px;
|
|
306
|
-
gap: 24px;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/* On mobile only show the services column at first paint —
|
|
310
|
-
subsequent steps reveal as user advances. */
|
|
311
|
-
.bw-skel-body .bw-col--center,
|
|
312
|
-
.bw-skel-body .bw-col--right {
|
|
313
|
-
display: none;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
107
|
.bw-loading,
|
|
318
108
|
.bw-empty {
|
|
319
109
|
display: flex;
|
|
@@ -394,6 +184,86 @@
|
|
|
394
184
|
min-height: 0;
|
|
395
185
|
}
|
|
396
186
|
|
|
187
|
+
/* Mobile-only review column. Desktop uses bw-details-view for review,
|
|
188
|
+
so this col stays hidden at all desktop widths and only the mobile
|
|
189
|
+
media query opts it back in on step 4. */
|
|
190
|
+
.bw-col--review {
|
|
191
|
+
display: none;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Review variant runs full-bleed inside its col: no card chrome (no
|
|
195
|
+
border, no rounded background), generous type. Sized for arm's-
|
|
196
|
+
length reading on phone — body 15px floor, total 17px, with a
|
|
197
|
+
thin hairline separating each group. */
|
|
198
|
+
.bw-summary--review {
|
|
199
|
+
width: 100%;
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
gap: 20px;
|
|
203
|
+
padding: 0;
|
|
204
|
+
border: 0;
|
|
205
|
+
border-radius: 0;
|
|
206
|
+
background: transparent;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.bw-summary--review .bw-summary-title {
|
|
210
|
+
margin-bottom: 4px;
|
|
211
|
+
font-size: 17px;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
letter-spacing: -0.01em;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.bw-summary--review .bw-summary-rows {
|
|
217
|
+
gap: 12px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.bw-summary--review .bw-summary-row {
|
|
221
|
+
font-size: 15px;
|
|
222
|
+
line-height: 1.5;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.bw-summary--review .bw-summary-total {
|
|
226
|
+
font-size: 17px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.bw-summary-group {
|
|
230
|
+
display: flex;
|
|
231
|
+
flex-direction: column;
|
|
232
|
+
gap: 12px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.bw-summary-subhead {
|
|
236
|
+
display: block;
|
|
237
|
+
font-size: 13px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
letter-spacing: -0.005em;
|
|
240
|
+
color: var(--bw-text-muted);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.bw-summary-row--stack {
|
|
244
|
+
flex-direction: column;
|
|
245
|
+
align-items: flex-start;
|
|
246
|
+
gap: 4px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.bw-summary-row--stack .bw-summary-val {
|
|
250
|
+
text-align: left;
|
|
251
|
+
white-space: normal;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.bw-summary-notes {
|
|
255
|
+
margin: 0;
|
|
256
|
+
font-size: 15px;
|
|
257
|
+
line-height: 1.55;
|
|
258
|
+
color: var(--bw-text);
|
|
259
|
+
white-space: pre-wrap;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.bw-summary-rows--total {
|
|
263
|
+
padding-top: 16px;
|
|
264
|
+
border-top: 1px solid var(--bw-border);
|
|
265
|
+
}
|
|
266
|
+
|
|
397
267
|
.bw-step-1 {
|
|
398
268
|
display: flex;
|
|
399
269
|
flex-direction: column;
|
|
@@ -622,6 +492,22 @@
|
|
|
622
492
|
background: var(--bw-hover);
|
|
623
493
|
}
|
|
624
494
|
|
|
495
|
+
/* Provider has no slots on the selected date. Visually muted, not
|
|
496
|
+
clickable, with an "Unavailable" subtitle under the name (rendered
|
|
497
|
+
inline via .bw-staff-option-desc). */
|
|
498
|
+
.bw-staff-option.is-disabled {
|
|
499
|
+
cursor: not-allowed;
|
|
500
|
+
opacity: 0.5;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.bw-staff-option.is-disabled:hover {
|
|
504
|
+
background: transparent;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.bw-staff-option.is-disabled .bw-staff-option-desc {
|
|
508
|
+
color: var(--bw-muted);
|
|
509
|
+
}
|
|
510
|
+
|
|
625
511
|
/* When the picker is in expanded mode (full list visible), let it
|
|
626
512
|
stretch to fill the column so the scroll list extends down to the
|
|
627
513
|
bottom of the left column instead of capping at an arbitrary
|
|
@@ -984,10 +870,187 @@
|
|
|
984
870
|
animation: bw-slots-fade-in 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
985
871
|
}
|
|
986
872
|
|
|
987
|
-
@media (prefers-reduced-motion: reduce) {
|
|
988
|
-
.bw-svc-selected,
|
|
989
|
-
.bw-svc-scroll,
|
|
990
|
-
.bw-provider-section {
|
|
873
|
+
@media (prefers-reduced-motion: reduce) {
|
|
874
|
+
.bw-svc-selected,
|
|
875
|
+
.bw-svc-scroll,
|
|
876
|
+
.bw-provider-section {
|
|
877
|
+
animation: none;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/* Provider list — replaces the old dropdown so every option is
|
|
882
|
+
visible at a glance. Two layouts share the same DOM:
|
|
883
|
+
Desktop (>=1025px): vertical stack of horizontal rows
|
|
884
|
+
(avatar | name + desc) — reads like the old dropdown list.
|
|
885
|
+
Mobile (<=1024px): horizontal scrolling strip of square-ish
|
|
886
|
+
cards (avatar above name) so the row fits on a phone column.
|
|
887
|
+
Selection, hover, and disabled-state chrome is shared between
|
|
888
|
+
both layouts so the visual language matches the rest of the
|
|
889
|
+
widget. */
|
|
890
|
+
.bw-staff-row {
|
|
891
|
+
display: flex;
|
|
892
|
+
flex-direction: column;
|
|
893
|
+
gap: 4px;
|
|
894
|
+
padding: 0;
|
|
895
|
+
margin: 0;
|
|
896
|
+
/* Desktop list shouldn't scroll horizontally; if the column is
|
|
897
|
+
ever taller than the card, the surrounding scroller handles
|
|
898
|
+
vertical overflow. */
|
|
899
|
+
max-height: 320px;
|
|
900
|
+
overflow-y: auto;
|
|
901
|
+
overscroll-behavior: contain;
|
|
902
|
+
scrollbar-width: thin;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.bw-staff-row::-webkit-scrollbar {
|
|
906
|
+
width: 6px;
|
|
907
|
+
height: 6px;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.bw-staff-row::-webkit-scrollbar-track {
|
|
911
|
+
background: transparent;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.bw-staff-row::-webkit-scrollbar-thumb {
|
|
915
|
+
background: var(--bw-border);
|
|
916
|
+
border-radius: 3px;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.bw-staff-card {
|
|
920
|
+
display: flex;
|
|
921
|
+
flex-direction: row;
|
|
922
|
+
align-items: center;
|
|
923
|
+
gap: 12px;
|
|
924
|
+
width: 100%;
|
|
925
|
+
padding: 10px 12px;
|
|
926
|
+
border: 0;
|
|
927
|
+
border-radius: var(--bw-radius-sm);
|
|
928
|
+
background: transparent;
|
|
929
|
+
color: var(--bw-text);
|
|
930
|
+
cursor: pointer;
|
|
931
|
+
text-align: left;
|
|
932
|
+
transition:
|
|
933
|
+
background var(--bw-duration) var(--bw-ease),
|
|
934
|
+
color var(--bw-duration) var(--bw-ease);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.bw-staff-card:hover {
|
|
938
|
+
background: var(--bw-hover);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
.bw-staff-card.is-active {
|
|
942
|
+
background: var(--bw-bg);
|
|
943
|
+
/* Inset shadow so the active state shows a crisp black ring
|
|
944
|
+
without shifting layout (default card has no border). */
|
|
945
|
+
box-shadow: inset 0 0 0 1.5px var(--bw-text);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.bw-staff-card.is-active:hover {
|
|
949
|
+
background: var(--bw-bg);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.bw-staff-card.is-disabled {
|
|
953
|
+
cursor: not-allowed;
|
|
954
|
+
opacity: 0.5;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.bw-staff-card.is-disabled:hover {
|
|
958
|
+
background: transparent;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.bw-staff-card-avatar {
|
|
962
|
+
position: relative;
|
|
963
|
+
display: inline-flex;
|
|
964
|
+
align-items: center;
|
|
965
|
+
justify-content: center;
|
|
966
|
+
width: 36px;
|
|
967
|
+
height: 36px;
|
|
968
|
+
border-radius: 999px;
|
|
969
|
+
background: var(--bw-border-light);
|
|
970
|
+
color: var(--bw-text-secondary);
|
|
971
|
+
flex-shrink: 0;
|
|
972
|
+
overflow: hidden;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.bw-staff-card-avatar svg {
|
|
976
|
+
width: 16px;
|
|
977
|
+
height: 16px;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
.bw-staff-card-avatar .bw-staff-initials {
|
|
981
|
+
font-size: 12px;
|
|
982
|
+
font-weight: 500;
|
|
983
|
+
color: var(--bw-text);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.bw-staff-card-info {
|
|
987
|
+
display: flex;
|
|
988
|
+
flex-direction: column;
|
|
989
|
+
gap: 2px;
|
|
990
|
+
min-width: 0;
|
|
991
|
+
flex: 1;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.bw-staff-card-name {
|
|
995
|
+
font-size: 14px;
|
|
996
|
+
font-weight: 500;
|
|
997
|
+
line-height: 1.25;
|
|
998
|
+
color: var(--bw-text);
|
|
999
|
+
overflow: hidden;
|
|
1000
|
+
text-overflow: ellipsis;
|
|
1001
|
+
white-space: nowrap;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.bw-staff-card-desc {
|
|
1005
|
+
font-size: 12px;
|
|
1006
|
+
line-height: 1.25;
|
|
1007
|
+
color: var(--bw-text-muted);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/* Mobile uses the same vertical list as desktop — no horizontal
|
|
1011
|
+
scrolling chip strip. The single layout keeps the visual language
|
|
1012
|
+
identical across breakpoints; if the list exceeds the card's
|
|
1013
|
+
built-in 320px max-height, it scrolls vertically inside the
|
|
1014
|
+
card. */
|
|
1015
|
+
|
|
1016
|
+
/* Mobile step 2 stacks three frames in bw-col--center: the calendar
|
|
1017
|
+
card (existing), the provider card, then the times card. Each
|
|
1018
|
+
mobile-only card matches the calendar card chrome so the column
|
|
1019
|
+
reads as one family. Hidden on desktop where each section has
|
|
1020
|
+
its own column. */
|
|
1021
|
+
.bw-mobile-card,
|
|
1022
|
+
.bw-provider-section--mobile {
|
|
1023
|
+
display: none;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
@media (max-width: 1024px) {
|
|
1027
|
+
.bw-mobile-card {
|
|
1028
|
+
display: flex;
|
|
1029
|
+
flex-direction: column;
|
|
1030
|
+
gap: 12px;
|
|
1031
|
+
margin-top: 16px;
|
|
1032
|
+
padding: 20px 20px 24px;
|
|
1033
|
+
border: 1px solid var(--bw-border);
|
|
1034
|
+
border-radius: var(--bw-radius-lg);
|
|
1035
|
+
background: var(--bw-bg);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/* Times card needs extra bottom breathing room so the slot pills
|
|
1039
|
+
don't sit flush against the card border. */
|
|
1040
|
+
.bw-mobile-card.bw-slots-mobile {
|
|
1041
|
+
padding-bottom: 28px;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/* Label sits at the top of each mobile card. Its desktop margin
|
|
1045
|
+
would double up with the parent's flex gap, so we zero it. */
|
|
1046
|
+
.bw-mobile-card > .bw-label {
|
|
1047
|
+
margin-bottom: 0;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/* The provider section uses .bw-mobile-card chrome — kill the
|
|
1051
|
+
desktop bw-provider-section animation so it doesn't replay
|
|
1052
|
+
when the section enters the mobile flow. */
|
|
1053
|
+
.bw-mobile-card.bw-provider-section--mobile {
|
|
991
1054
|
animation: none;
|
|
992
1055
|
}
|
|
993
1056
|
}
|
|
@@ -1368,7 +1431,11 @@
|
|
|
1368
1431
|
|
|
1369
1432
|
.bw-time-slots {
|
|
1370
1433
|
display: grid;
|
|
1371
|
-
|
|
1434
|
+
/* 3-column grid on mobile so each pill gets more horizontal room
|
|
1435
|
+
and longer locale time strings (e.g. "10:30 a. m.") don't crowd
|
|
1436
|
+
each other. Desktop overrides this to 1fr further down to render
|
|
1437
|
+
a vertical pill list. */
|
|
1438
|
+
grid-template-columns: repeat(3, 1fr);
|
|
1372
1439
|
gap: 10px;
|
|
1373
1440
|
margin-top: 8px;
|
|
1374
1441
|
}
|
|
@@ -1416,7 +1483,11 @@
|
|
|
1416
1483
|
}
|
|
1417
1484
|
|
|
1418
1485
|
.bw-slot {
|
|
1419
|
-
|
|
1486
|
+
display: flex;
|
|
1487
|
+
align-items: center;
|
|
1488
|
+
justify-content: center;
|
|
1489
|
+
padding: 12px 14px;
|
|
1490
|
+
min-width: 0;
|
|
1420
1491
|
border: 1px solid var(--bw-border);
|
|
1421
1492
|
border-radius: var(--bw-radius);
|
|
1422
1493
|
background: transparent;
|
|
@@ -1537,6 +1608,208 @@
|
|
|
1537
1608
|
color: var(--bw-text-muted);
|
|
1538
1609
|
}
|
|
1539
1610
|
|
|
1611
|
+
/* ===========================================================================
|
|
1612
|
+
Custom IntakeSelect — replaces the native <select>. Trigger matches
|
|
1613
|
+
the .bw-field input chrome (border, radius, padding, font). Menu sits
|
|
1614
|
+
absolutely below, scrolls if taller than max-height, animates in.
|
|
1615
|
+
=========================================================================== */
|
|
1616
|
+
.bw-select {
|
|
1617
|
+
position: relative;
|
|
1618
|
+
width: 100%;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
.bw-select-trigger {
|
|
1622
|
+
display: flex;
|
|
1623
|
+
align-items: center;
|
|
1624
|
+
justify-content: space-between;
|
|
1625
|
+
gap: 12px;
|
|
1626
|
+
width: 100%;
|
|
1627
|
+
padding: 11px 14px 11px 16px;
|
|
1628
|
+
border: 1px solid var(--bw-border);
|
|
1629
|
+
border-radius: var(--bw-radius-md);
|
|
1630
|
+
background: transparent;
|
|
1631
|
+
color: var(--bw-text);
|
|
1632
|
+
font-size: 13px;
|
|
1633
|
+
font-family: inherit;
|
|
1634
|
+
text-align: left;
|
|
1635
|
+
cursor: pointer;
|
|
1636
|
+
transition:
|
|
1637
|
+
border-color var(--bw-duration) var(--bw-ease),
|
|
1638
|
+
box-shadow var(--bw-duration) var(--bw-ease);
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
.bw-select-trigger:hover {
|
|
1642
|
+
border-color: var(--bw-text-secondary);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
.bw-select-trigger:focus-visible,
|
|
1646
|
+
.bw-select-trigger.is-open {
|
|
1647
|
+
outline: 0;
|
|
1648
|
+
border-color: var(--bw-primary);
|
|
1649
|
+
box-shadow: 0 0 0 3px rgba(15, 15, 15, 0.05);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
.bw-select-value {
|
|
1653
|
+
flex: 1;
|
|
1654
|
+
min-width: 0;
|
|
1655
|
+
overflow: hidden;
|
|
1656
|
+
text-overflow: ellipsis;
|
|
1657
|
+
white-space: nowrap;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
.bw-select-value.is-placeholder {
|
|
1661
|
+
color: var(--bw-text-muted);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
.bw-select-chevron {
|
|
1665
|
+
flex-shrink: 0;
|
|
1666
|
+
display: inline-flex;
|
|
1667
|
+
align-items: center;
|
|
1668
|
+
justify-content: center;
|
|
1669
|
+
width: 16px;
|
|
1670
|
+
height: 16px;
|
|
1671
|
+
color: var(--bw-text-secondary);
|
|
1672
|
+
transition: transform var(--bw-duration) var(--bw-ease);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
.bw-select-trigger.is-open .bw-select-chevron {
|
|
1676
|
+
transform: rotate(180deg);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.bw-select-chevron svg {
|
|
1680
|
+
width: 16px;
|
|
1681
|
+
height: 16px;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.bw-select-menu {
|
|
1685
|
+
position: absolute;
|
|
1686
|
+
top: calc(100% + 6px);
|
|
1687
|
+
left: 0;
|
|
1688
|
+
right: 0;
|
|
1689
|
+
/* Sits above the widget's own footer (z-index up to 102 on mobile)
|
|
1690
|
+
and any nearby chrome so flip-up + flip-down both render cleanly. */
|
|
1691
|
+
z-index: 110;
|
|
1692
|
+
/* Cap menu height to whichever is smaller — the 280px design max
|
|
1693
|
+
or 60% of the viewport — so on short phones the dropdown never
|
|
1694
|
+
dominates the screen and always leaves the trigger visible. */
|
|
1695
|
+
max-height: min(280px, 60svh);
|
|
1696
|
+
overflow-y: auto;
|
|
1697
|
+
margin: 0;
|
|
1698
|
+
padding: 6px;
|
|
1699
|
+
list-style: none;
|
|
1700
|
+
background: var(--bw-bg);
|
|
1701
|
+
border: 1px solid var(--bw-border);
|
|
1702
|
+
border-radius: var(--bw-radius-md);
|
|
1703
|
+
box-shadow: var(--bw-shadow-lift);
|
|
1704
|
+
/* 250ms ease-out-expo open animation per the widget's motion
|
|
1705
|
+
spec. Scale + fade + 4px lift mirrors the popup-animation rule
|
|
1706
|
+
used elsewhere in the Sable product. */
|
|
1707
|
+
animation: bw-select-open 250ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
1708
|
+
outline: 0;
|
|
1709
|
+
scrollbar-width: thin;
|
|
1710
|
+
scrollbar-color: var(--bw-border) transparent;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
@keyframes bw-select-open {
|
|
1714
|
+
from {
|
|
1715
|
+
opacity: 0;
|
|
1716
|
+
transform: translateY(-4px) scale(0.97);
|
|
1717
|
+
}
|
|
1718
|
+
to {
|
|
1719
|
+
opacity: 1;
|
|
1720
|
+
transform: translateY(0) scale(1);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/* Flip-up variant: when there's no room below the trigger (select
|
|
1725
|
+
sits near the bottom of the widget AND the host page has a fixed
|
|
1726
|
+
footer below it), the menu opens upward instead. Computed at open
|
|
1727
|
+
time by IntakeSelect.computePlacement. */
|
|
1728
|
+
.bw-select-menu.is-above {
|
|
1729
|
+
top: auto;
|
|
1730
|
+
bottom: calc(100% + 6px);
|
|
1731
|
+
animation-name: bw-select-open-above;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
@keyframes bw-select-open-above {
|
|
1735
|
+
from {
|
|
1736
|
+
opacity: 0;
|
|
1737
|
+
transform: translateY(4px) scale(0.97);
|
|
1738
|
+
}
|
|
1739
|
+
to {
|
|
1740
|
+
opacity: 1;
|
|
1741
|
+
transform: translateY(0) scale(1);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.bw-select-option {
|
|
1746
|
+
display: flex;
|
|
1747
|
+
align-items: center;
|
|
1748
|
+
justify-content: space-between;
|
|
1749
|
+
gap: 12px;
|
|
1750
|
+
padding: 9px 12px;
|
|
1751
|
+
border-radius: var(--bw-radius-sm);
|
|
1752
|
+
color: var(--bw-text);
|
|
1753
|
+
font-size: 14px;
|
|
1754
|
+
cursor: pointer;
|
|
1755
|
+
user-select: none;
|
|
1756
|
+
transition: background-color var(--bw-duration) var(--bw-ease);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.bw-select-option.is-focused {
|
|
1760
|
+
background: var(--bw-hover);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
.bw-select-option.is-active {
|
|
1764
|
+
color: var(--bw-text);
|
|
1765
|
+
font-weight: 500;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
.bw-select-option-label {
|
|
1769
|
+
flex: 1;
|
|
1770
|
+
min-width: 0;
|
|
1771
|
+
overflow: hidden;
|
|
1772
|
+
text-overflow: ellipsis;
|
|
1773
|
+
white-space: nowrap;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
.bw-select-option-check {
|
|
1777
|
+
flex-shrink: 0;
|
|
1778
|
+
display: inline-flex;
|
|
1779
|
+
align-items: center;
|
|
1780
|
+
justify-content: center;
|
|
1781
|
+
width: 14px;
|
|
1782
|
+
height: 14px;
|
|
1783
|
+
color: var(--bw-primary);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
.bw-select-option-check svg {
|
|
1787
|
+
width: 14px;
|
|
1788
|
+
height: 14px;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1792
|
+
.bw-select-menu {
|
|
1793
|
+
animation: none;
|
|
1794
|
+
}
|
|
1795
|
+
.bw-select-chevron {
|
|
1796
|
+
transition: none;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
@media (max-width: 1024px) {
|
|
1801
|
+
/* Match the bumped 16px input font on mobile so the dropdown sits
|
|
1802
|
+
in the same visual family as the rest of the form fields. */
|
|
1803
|
+
.bw-select-trigger {
|
|
1804
|
+
font-size: 16px;
|
|
1805
|
+
padding: 12px 14px 12px 16px;
|
|
1806
|
+
}
|
|
1807
|
+
.bw-select-option {
|
|
1808
|
+
font-size: 16px;
|
|
1809
|
+
padding: 12px;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1540
1813
|
.bw-field--notes {
|
|
1541
1814
|
position: relative;
|
|
1542
1815
|
}
|
|
@@ -1574,6 +1847,8 @@
|
|
|
1574
1847
|
}
|
|
1575
1848
|
|
|
1576
1849
|
.bw-required {
|
|
1850
|
+
/* 4px gap between the label and the asterisk so they don't kiss. */
|
|
1851
|
+
margin-left: 4px;
|
|
1577
1852
|
color: var(--bw-error-text);
|
|
1578
1853
|
font-weight: 400;
|
|
1579
1854
|
}
|
|
@@ -1620,6 +1895,9 @@
|
|
|
1620
1895
|
text-align: right;
|
|
1621
1896
|
text-overflow: ellipsis;
|
|
1622
1897
|
white-space: nowrap;
|
|
1898
|
+
/* Hold countdown + price + date numerals all benefit from tabular
|
|
1899
|
+
figures so they don't shift horizontally as digits change. */
|
|
1900
|
+
font-variant-numeric: tabular-nums;
|
|
1623
1901
|
}
|
|
1624
1902
|
|
|
1625
1903
|
.bw-summary-total span:first-child,
|
|
@@ -1746,18 +2024,77 @@
|
|
|
1746
2024
|
}
|
|
1747
2025
|
|
|
1748
2026
|
.bw-confirm-btn:disabled,
|
|
1749
|
-
.bw-
|
|
2027
|
+
.bw-confirm-btn.is-disabled,
|
|
2028
|
+
.bw-footer-next:disabled,
|
|
2029
|
+
.bw-footer-next.is-disabled {
|
|
1750
2030
|
opacity: 0.35;
|
|
1751
2031
|
cursor: default;
|
|
1752
2032
|
}
|
|
1753
2033
|
|
|
1754
2034
|
.bw-done {
|
|
1755
2035
|
display: flex;
|
|
2036
|
+
flex: 1;
|
|
1756
2037
|
flex-direction: column;
|
|
1757
2038
|
align-items: center;
|
|
2039
|
+
justify-content: center;
|
|
1758
2040
|
gap: 16px;
|
|
2041
|
+
min-height: 80svh;
|
|
1759
2042
|
padding: 80px 20px;
|
|
1760
2043
|
text-align: center;
|
|
2044
|
+
animation: bw-done-fade-in 480ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
.bw-done-icon,
|
|
2048
|
+
.bw-done-title,
|
|
2049
|
+
.bw-done-text,
|
|
2050
|
+
.bw-done > .bw-btn-primary {
|
|
2051
|
+
animation: bw-done-stagger-in 520ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
.bw-done-icon {
|
|
2055
|
+
animation-delay: 80ms;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
.bw-done-title {
|
|
2059
|
+
animation-delay: 180ms;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
.bw-done-text {
|
|
2063
|
+
animation-delay: 260ms;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
.bw-done > .bw-btn-primary {
|
|
2067
|
+
animation-delay: 360ms;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
@keyframes bw-done-fade-in {
|
|
2071
|
+
from {
|
|
2072
|
+
opacity: 0;
|
|
2073
|
+
}
|
|
2074
|
+
to {
|
|
2075
|
+
opacity: 1;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
@keyframes bw-done-stagger-in {
|
|
2080
|
+
from {
|
|
2081
|
+
opacity: 0;
|
|
2082
|
+
transform: translateY(8px) scale(0.985);
|
|
2083
|
+
}
|
|
2084
|
+
to {
|
|
2085
|
+
opacity: 1;
|
|
2086
|
+
transform: translateY(0) scale(1);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2091
|
+
.bw-done,
|
|
2092
|
+
.bw-done-icon,
|
|
2093
|
+
.bw-done-title,
|
|
2094
|
+
.bw-done-text,
|
|
2095
|
+
.bw-done > .bw-btn-primary {
|
|
2096
|
+
animation: none;
|
|
2097
|
+
}
|
|
1761
2098
|
}
|
|
1762
2099
|
|
|
1763
2100
|
.bw-done-icon {
|
|
@@ -1814,17 +2151,17 @@
|
|
|
1814
2151
|
}
|
|
1815
2152
|
|
|
1816
2153
|
@media (max-width: 1024px) {
|
|
1817
|
-
/* Lock
|
|
1818
|
-
bounce
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
container below — content longer than the body height (rare:
|
|
1823
|
-
long service list on step 1, busy slot day on step 2) scrolls
|
|
1824
|
-
internally instead of growing the widget. */
|
|
2154
|
+
/* Lock the frame to a single height across all steps so the widget
|
|
2155
|
+
doesn't bounce as the user moves between services / date / etc.
|
|
2156
|
+
Sized to fit the tallest step (date + time) with a little
|
|
2157
|
+
breathing room above and below. Body scrolls internally when a
|
|
2158
|
+
step exceeds it. */
|
|
1825
2159
|
.bw {
|
|
1826
|
-
|
|
1827
|
-
|
|
2160
|
+
/* svh (small viewport height) doesn't jump when iOS Safari's URL
|
|
2161
|
+
bar toggles, unlike vh which uses the largest possible height
|
|
2162
|
+
and overflows when bars become visible. */
|
|
2163
|
+
min-height: 82svh;
|
|
2164
|
+
max-height: 82svh;
|
|
1828
2165
|
}
|
|
1829
2166
|
|
|
1830
2167
|
/* Lock header/footer in the column so they never shrink under
|
|
@@ -1842,34 +2179,23 @@
|
|
|
1842
2179
|
overflow-y: auto;
|
|
1843
2180
|
-webkit-overflow-scrolling: touch;
|
|
1844
2181
|
overscroll-behavior: contain;
|
|
1845
|
-
/*
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
from sitting right at the fade line. */
|
|
2182
|
+
/* Top fade only; the bottom taper is rendered as a gradient
|
|
2183
|
+
overlay above .bw-footer so content fades into the footer
|
|
2184
|
+
edge, not into the body frame's own bottom. */
|
|
1849
2185
|
-webkit-mask-image: linear-gradient(
|
|
1850
2186
|
to bottom,
|
|
1851
2187
|
transparent 0,
|
|
1852
2188
|
#000 24px,
|
|
1853
|
-
#000
|
|
1854
|
-
transparent 100%
|
|
2189
|
+
#000 100%
|
|
1855
2190
|
);
|
|
1856
2191
|
mask-image: linear-gradient(
|
|
1857
2192
|
to bottom,
|
|
1858
2193
|
transparent 0,
|
|
1859
2194
|
#000 24px,
|
|
1860
|
-
#000
|
|
1861
|
-
transparent 100%
|
|
2195
|
+
#000 100%
|
|
1862
2196
|
);
|
|
1863
2197
|
}
|
|
1864
2198
|
|
|
1865
|
-
.bw-skel-desktop {
|
|
1866
|
-
display: none;
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1869
|
-
.bw-skel-mobile {
|
|
1870
|
-
display: block;
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
2199
|
.bw-mobile-header {
|
|
1874
2200
|
display: block;
|
|
1875
2201
|
position: relative;
|
|
@@ -1912,9 +2238,10 @@
|
|
|
1912
2238
|
flex: 1 1 0;
|
|
1913
2239
|
width: auto;
|
|
1914
2240
|
min-width: 20px;
|
|
1915
|
-
max-width:
|
|
1916
|
-
|
|
1917
|
-
|
|
2241
|
+
max-width: 32px;
|
|
2242
|
+
/* Bumped from 3px to 4px for a stronger active-step signal. */
|
|
2243
|
+
height: 4px;
|
|
2244
|
+
border-radius: 2px;
|
|
1918
2245
|
background: var(--bw-border);
|
|
1919
2246
|
transition: background 200ms ease;
|
|
1920
2247
|
}
|
|
@@ -1924,7 +2251,9 @@
|
|
|
1924
2251
|
}
|
|
1925
2252
|
|
|
1926
2253
|
.bw-title {
|
|
1927
|
-
|
|
2254
|
+
/* iOS Title 1 = 28pt. Was 24px (no-man's land between Title 1
|
|
2255
|
+
and Title 2). 28px gives better scale separation from body. */
|
|
2256
|
+
font-size: 28px;
|
|
1928
2257
|
letter-spacing: -0.5px;
|
|
1929
2258
|
}
|
|
1930
2259
|
|
|
@@ -1948,7 +2277,7 @@
|
|
|
1948
2277
|
flex-direction: column;
|
|
1949
2278
|
flex: 1;
|
|
1950
2279
|
min-height: 0;
|
|
1951
|
-
padding: 0 24px;
|
|
2280
|
+
padding: 0 24px 24px;
|
|
1952
2281
|
}
|
|
1953
2282
|
|
|
1954
2283
|
.bw-col {
|
|
@@ -1956,20 +2285,24 @@
|
|
|
1956
2285
|
}
|
|
1957
2286
|
|
|
1958
2287
|
/* Mobile 4-step flow:
|
|
1959
|
-
Step 1: services (bw-col--left, bw-step-2 only
|
|
1960
|
-
Step 2: provider (bw-col--
|
|
1961
|
-
Step 3:
|
|
1962
|
-
Step 4:
|
|
2288
|
+
Step 1: services (bw-col--left, bw-step-2 only)
|
|
2289
|
+
Step 2: calendar + provider chips + slots (bw-col--center)
|
|
2290
|
+
Step 3: form (bw-col--right, form fields visible)
|
|
2291
|
+
Step 4: review (bw-col--review with the full summary card,
|
|
2292
|
+
scrollable; footer only carries back + confirm buttons).
|
|
2293
|
+
Provider lives inside bw-cal-card on mobile so the visual
|
|
2294
|
+
order reads date → provider → time. The desktop dropdown in
|
|
2295
|
+
bw-step-1 is hidden on mobile (we use chips here instead). */
|
|
1963
2296
|
.bw-body[data-mobile-step="1"] .bw-col--left,
|
|
1964
|
-
.bw-body[data-mobile-step="2"] .bw-col--
|
|
1965
|
-
.bw-body[data-mobile-step="3"] .bw-col--
|
|
1966
|
-
.bw-body[data-mobile-step="4"] .bw-col--
|
|
2297
|
+
.bw-body[data-mobile-step="2"] .bw-col--center,
|
|
2298
|
+
.bw-body[data-mobile-step="3"] .bw-col--right,
|
|
2299
|
+
.bw-body[data-mobile-step="4"] .bw-col--review {
|
|
1967
2300
|
display: flex;
|
|
1968
2301
|
padding-top: 16px;
|
|
1969
|
-
padding-bottom:
|
|
2302
|
+
padding-bottom: 24px;
|
|
1970
2303
|
}
|
|
1971
2304
|
|
|
1972
|
-
.bw-body[data-mobile-step="
|
|
2305
|
+
.bw-body[data-mobile-step="3"] .bw-right-inner {
|
|
1973
2306
|
padding-bottom: 20px;
|
|
1974
2307
|
}
|
|
1975
2308
|
|
|
@@ -1990,27 +2323,9 @@
|
|
|
1990
2323
|
display: none;
|
|
1991
2324
|
}
|
|
1992
2325
|
|
|
1993
|
-
/*
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
service-picker stub inside bw-step-1. */
|
|
1997
|
-
.bw-body[data-mobile-step="2"] .bw-step-1 {
|
|
1998
|
-
display: flex;
|
|
1999
|
-
flex: 0 0 auto;
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
.bw-body[data-mobile-step="2"] .bw-step-2 {
|
|
2003
|
-
display: none;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
.bw-body[data-mobile-step="2"] .bw-step-1 .bw-service-picker {
|
|
2007
|
-
display: none;
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
.bw-body[data-mobile-step="2"] .bw-step-1 .bw-provider-section .bw-section-divider {
|
|
2011
|
-
/* Mobile rhythm uses whitespace, not 1px lines */
|
|
2012
|
-
display: none;
|
|
2013
|
-
}
|
|
2326
|
+
/* Desktop dropdown lives in bw-col--left's bw-step-1; we don't show
|
|
2327
|
+
bw-col--left at all on mobile step 2, so this is implicitly
|
|
2328
|
+
hidden. The chip strip below handles provider selection. */
|
|
2014
2329
|
|
|
2015
2330
|
.bw-svc-scroll {
|
|
2016
2331
|
display: none;
|
|
@@ -2102,25 +2417,38 @@
|
|
|
2102
2417
|
}
|
|
2103
2418
|
|
|
2104
2419
|
.bw-footer {
|
|
2420
|
+
position: relative;
|
|
2105
2421
|
display: flex;
|
|
2106
2422
|
flex-direction: column;
|
|
2107
2423
|
gap: 20px;
|
|
2108
|
-
|
|
2109
|
-
|
|
2424
|
+
/* .bw-footer is a sibling of .bw-content, not .bw-body, so the
|
|
2425
|
+
frame-to-frame mobile gap has to live on the footer flex item.
|
|
2426
|
+
Bottom padding uses env(safe-area-inset-bottom) so notched iOS
|
|
2427
|
+
devices clear the home indicator naturally, while Touch ID
|
|
2428
|
+
devices + Android don't waste 34px of space. Floors at 24px so
|
|
2429
|
+
there's always real breathing room below the Confirm button.
|
|
2430
|
+
Host site MUST include `viewport-fit=cover` in its viewport
|
|
2431
|
+
meta for env(safe-area-inset-*) to be non-zero on iOS. */
|
|
2432
|
+
margin-top: 16px;
|
|
2433
|
+
padding: 16px 24px
|
|
2434
|
+
max(24px, calc(env(safe-area-inset-bottom) + 16px));
|
|
2110
2435
|
border-top: 1px solid var(--bw-border-light);
|
|
2111
2436
|
}
|
|
2112
2437
|
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2438
|
+
/* Gradient overlay sits above the footer (in the 16px gap and a
|
|
2439
|
+
touch into the body's frame above it) so scrolling content fades
|
|
2440
|
+
into the footer edge instead of slicing or tapering inside the
|
|
2441
|
+
body's own frame. */
|
|
2442
|
+
.bw-footer::before {
|
|
2443
|
+
content: '';
|
|
2444
|
+
position: absolute;
|
|
2445
|
+
inset: -32px 0 100%;
|
|
2446
|
+
pointer-events: none;
|
|
2447
|
+
background: linear-gradient(
|
|
2448
|
+
to bottom,
|
|
2449
|
+
rgba(255, 255, 255, 0) 0%,
|
|
2450
|
+
var(--bw-bg) 100%
|
|
2451
|
+
);
|
|
2124
2452
|
}
|
|
2125
2453
|
|
|
2126
2454
|
.bw-footer-btns {
|
|
@@ -2190,7 +2518,7 @@
|
|
|
2190
2518
|
}
|
|
2191
2519
|
|
|
2192
2520
|
.bw-body {
|
|
2193
|
-
padding: 0 20px;
|
|
2521
|
+
padding: 0 20px 24px;
|
|
2194
2522
|
}
|
|
2195
2523
|
|
|
2196
2524
|
.bw-footer {
|
|
@@ -2217,17 +2545,38 @@
|
|
|
2217
2545
|
|
|
2218
2546
|
.bw-slot {
|
|
2219
2547
|
min-height: 40px;
|
|
2220
|
-
padding: 12px
|
|
2548
|
+
padding: 12px 12px;
|
|
2221
2549
|
font-size: 12px;
|
|
2222
2550
|
}
|
|
2223
2551
|
|
|
2224
2552
|
.bw-field input,
|
|
2225
2553
|
.bw-field textarea {
|
|
2226
|
-
|
|
2554
|
+
/* 16px on mobile prevents iOS Safari's auto-zoom-on-focus,
|
|
2555
|
+
which otherwise leaves the form stuck zoomed-in mid-flow. */
|
|
2556
|
+
font-size: 16px;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
.bw-field label {
|
|
2560
|
+
/* Bumped from 12px to 13px so labels read clearly without
|
|
2561
|
+
crowding the 16px input below them. */
|
|
2562
|
+
font-size: 13px;
|
|
2227
2563
|
}
|
|
2228
2564
|
|
|
2229
2565
|
.bw-summary-row {
|
|
2230
|
-
font-size:
|
|
2566
|
+
font-size: 13px;
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
.bw-slot {
|
|
2570
|
+
/* Time pills are a primary tap target — bump from 13px
|
|
2571
|
+
(footnote-tier) to 15px (subheadline). 3-col mobile grid
|
|
2572
|
+
still fits "10:30 a. m." comfortably at 15px. */
|
|
2573
|
+
font-size: 15px;
|
|
2574
|
+
padding: 13px 12px;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
.bw-cal-weekdays span {
|
|
2578
|
+
/* Day-of-week header bumped from 12px to 13px for readability. */
|
|
2579
|
+
font-size: 13px;
|
|
2231
2580
|
}
|
|
2232
2581
|
}
|
|
2233
2582
|
|
|
@@ -2287,6 +2636,14 @@
|
|
|
2287
2636
|
}
|
|
2288
2637
|
|
|
2289
2638
|
@media (min-width: 1025px) {
|
|
2639
|
+
/* Lock the desktop frame to a stable height so it doesn't snap to a
|
|
2640
|
+
shorter fit-content size when the widget swaps into the success
|
|
2641
|
+
view (.bw-done has less content than the normal flow). Picked to
|
|
2642
|
+
comfortably fit the calendar + slots + form layout. */
|
|
2643
|
+
.bw {
|
|
2644
|
+
min-height: 720px;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2290
2647
|
/* Desktop body grid: cols sit at the top of their cell instead of
|
|
2291
2648
|
stretching to the row's max content height — the visual empty
|
|
2292
2649
|
space below the cal-card and right-pane goes away. */
|
|
@@ -2334,13 +2691,28 @@
|
|
|
2334
2691
|
max-height: var(--bw-cal-h, 480px);
|
|
2335
2692
|
}
|
|
2336
2693
|
|
|
2337
|
-
.bw-pane--slots
|
|
2694
|
+
.bw-pane--slots,
|
|
2695
|
+
.bw-pane--details {
|
|
2696
|
+
/* Lock both panes in the body's right column to the same max
|
|
2697
|
+
height as the calendar so the body row's intrinsic height
|
|
2698
|
+
doesn't change between viewState 'slots' and 'details'. Without
|
|
2699
|
+
this, the form pane grows taller than the slots pane and
|
|
2700
|
+
.bw-content's grid-stack cell jumps in height on step 2. */
|
|
2338
2701
|
max-height: var(--bw-cal-h, 480px);
|
|
2339
2702
|
overflow: hidden;
|
|
2340
2703
|
display: flex;
|
|
2341
2704
|
flex-direction: column;
|
|
2342
2705
|
}
|
|
2343
2706
|
|
|
2707
|
+
/* Sticky "Available times" heading on desktop. Lives as a sibling
|
|
2708
|
+
of .bw-slots-desktop (the scroller) so it doesn't scroll with
|
|
2709
|
+
the time pills. Sits flush at the top of the right column. */
|
|
2710
|
+
.bw-pane--slots .bw-slots-heading--sticky {
|
|
2711
|
+
flex: 0 0 auto;
|
|
2712
|
+
margin-bottom: 4px;
|
|
2713
|
+
padding-bottom: 8px;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2344
2716
|
.bw-pane--slots .bw-slots-desktop {
|
|
2345
2717
|
flex: 1;
|
|
2346
2718
|
min-height: 0;
|
|
@@ -2361,9 +2733,16 @@
|
|
|
2361
2733
|
);
|
|
2362
2734
|
scrollbar-width: thin;
|
|
2363
2735
|
scrollbar-color: var(--bw-border) transparent;
|
|
2736
|
+
/* Reserve space for the scrollbar so it never overlays the
|
|
2737
|
+
pills' right edge. Combined with the right padding below this
|
|
2738
|
+
gives the bar its own lane regardless of whether content
|
|
2739
|
+
overflows. */
|
|
2740
|
+
scrollbar-gutter: stable;
|
|
2364
2741
|
/* Padding so first/last pills sit inside the fade zone, matching
|
|
2365
|
-
the services scroller pattern.
|
|
2742
|
+
the services scroller pattern. Right padding gives the pills
|
|
2743
|
+
breathing room from the scrollbar lane. */
|
|
2366
2744
|
padding-block: 12px 16px;
|
|
2745
|
+
padding-right: 6px;
|
|
2367
2746
|
}
|
|
2368
2747
|
|
|
2369
2748
|
.bw-pane--slots .bw-slots-desktop::-webkit-scrollbar {
|
|
@@ -2432,8 +2811,11 @@
|
|
|
2432
2811
|
display: none;
|
|
2433
2812
|
}
|
|
2434
2813
|
|
|
2814
|
+
/* Slots-mobile is now its own card frame on step 2 (combined with
|
|
2815
|
+
.bw-mobile-card class). Override default display: none so the
|
|
2816
|
+
card chrome shows; .bw-mobile-card already sets flex column. */
|
|
2435
2817
|
.bw-slots-mobile {
|
|
2436
|
-
display:
|
|
2818
|
+
display: flex;
|
|
2437
2819
|
}
|
|
2438
2820
|
|
|
2439
2821
|
/* Details view is desktop-only; mobile uses the existing step-4
|
|
@@ -2449,14 +2831,14 @@
|
|
|
2449
2831
|
wrapper for steps 1/2 since col--left stays visible across
|
|
2450
2832
|
them) so animations don't re-fire when the active col is shared
|
|
2451
2833
|
between adjacent steps. */
|
|
2452
|
-
/* 4-step flow
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
.bw-body[data-mobile-step='1'] .bw-
|
|
2457
|
-
.bw-body[data-mobile-step='2'] .bw-
|
|
2458
|
-
.bw-body[data-mobile-step='3'] .bw-col--
|
|
2459
|
-
.bw-body[data-mobile-step='4'] .bw-col--
|
|
2834
|
+
/* 4-step flow. Step 1 shares bw-col--left with no other step.
|
|
2835
|
+
Steps 3 + 4 both live in bw-col--right but differ in content,
|
|
2836
|
+
so we re-fire the enter animation on data-mobile-step changes
|
|
2837
|
+
between them. */
|
|
2838
|
+
.bw-body[data-mobile-step='1'] .bw-col--left,
|
|
2839
|
+
.bw-body[data-mobile-step='2'] .bw-col--center,
|
|
2840
|
+
.bw-body[data-mobile-step='3'] .bw-col--right,
|
|
2841
|
+
.bw-body[data-mobile-step='4'] .bw-col--review {
|
|
2460
2842
|
animation: bw-mobile-step-in 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
2461
2843
|
}
|
|
2462
2844
|
|
|
@@ -2553,6 +2935,24 @@
|
|
|
2553
2935
|
flex-direction: column;
|
|
2554
2936
|
gap: 16px;
|
|
2555
2937
|
padding-right: 8px;
|
|
2938
|
+
/* Sticky so the booking summary stays alongside the form when
|
|
2939
|
+
it gets long (e.g. paid services with payment block, or many
|
|
2940
|
+
intake fields). `align-self: start` keeps the grid cell from
|
|
2941
|
+
stretching to row height, which would block sticky behavior.
|
|
2942
|
+
Host sites can override --bw-sticky-summary-top to clear a
|
|
2943
|
+
fixed top nav — encomiendas' nav is 72px so a host override
|
|
2944
|
+
to ~96px makes sense; the default 24px works for hosts with
|
|
2945
|
+
no fixed nav. */
|
|
2946
|
+
position: sticky;
|
|
2947
|
+
top: var(--bw-sticky-summary-top, 24px);
|
|
2948
|
+
align-self: start;
|
|
2949
|
+
/* Cap height so very-tall summaries (long service description,
|
|
2950
|
+
all reschedule rows visible) don't overflow the viewport;
|
|
2951
|
+
internal scroll if needed. */
|
|
2952
|
+
max-height: calc(100svh - var(--bw-sticky-summary-top, 24px) - 24px);
|
|
2953
|
+
overflow-y: auto;
|
|
2954
|
+
scrollbar-width: thin;
|
|
2955
|
+
scrollbar-color: var(--bw-border) transparent;
|
|
2556
2956
|
}
|
|
2557
2957
|
|
|
2558
2958
|
.bw-details-eyebrow {
|