@asksable/site-connector 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/booking-widget.d.ts +75 -1
- package/dist/booking-widget.d.ts.map +1 -1
- package/dist/booking-widget.js +987 -130
- package/dist/booking-widget.js.map +1 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +56 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/styles.css +1673 -91
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -1
package/dist/styles.css
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
.bw {
|
|
2
|
+
/* Theme tokens — overridable per host site (color + font). */
|
|
2
3
|
--bw-font: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
3
4
|
--bw-bg: #ffffff;
|
|
4
5
|
--bw-text: #1a1a1a;
|
|
5
6
|
--bw-text-secondary: #6b6b6b;
|
|
6
|
-
|
|
7
|
+
/* Bumped from #999 (2.85:1, fails WCAG AA) to #737373 (~4.74:1,
|
|
8
|
+
passes AA for normal text). Still distinct from --bw-text-secondary
|
|
9
|
+
(#6b6b6b, 5.74:1) so the visual hierarchy holds. */
|
|
10
|
+
--bw-text-muted: #737373;
|
|
7
11
|
--bw-border: #e8e8e8;
|
|
8
12
|
--bw-border-light: #f0f0f0;
|
|
9
|
-
--bw-radius: 8px;
|
|
10
|
-
--bw-radius-lg: 12px;
|
|
11
13
|
--bw-primary: #1a1a1a;
|
|
12
14
|
--bw-primary-text: #ffffff;
|
|
13
15
|
--bw-hover: #f8f8f8;
|
|
14
16
|
--bw-error-bg: #fef2f2;
|
|
15
17
|
--bw-error-text: #b91c1c;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/* Structural tokens — Sable design rhythm; tenants generally
|
|
19
|
+
don't override these. Mirrors the radius scale documented in
|
|
20
|
+
productdesign.md (cards 12/16, inputs 22, buttons 28). */
|
|
21
|
+
--bw-radius-xs: 4px;
|
|
22
|
+
--bw-radius-sm: 8px;
|
|
23
|
+
--bw-radius-md: 12px;
|
|
24
|
+
--bw-radius: 12px;
|
|
25
|
+
--bw-radius-lg: 16px;
|
|
26
|
+
--bw-radius-xl: 22px;
|
|
27
|
+
--bw-radius-pill: 9999px;
|
|
28
|
+
--bw-shadow: 0 1px 2px rgba(15, 15, 15, 0.04), 0 8px 24px -12px rgba(15, 15, 15, 0.08);
|
|
29
|
+
--bw-shadow-badge: 0 1px 4px rgba(15, 15, 15, 0.12);
|
|
30
|
+
--bw-shadow-lift: 0 1px 2px rgba(15, 15, 15, 0.06), 0 12px 32px -16px rgba(15, 15, 15, 0.12);
|
|
31
|
+
--bw-ease: cubic-bezier(0.16, 1, 0.3, 1);
|
|
32
|
+
--bw-duration: 220ms;
|
|
18
33
|
position: relative;
|
|
19
34
|
display: flex;
|
|
20
35
|
flex-direction: column;
|
|
@@ -22,9 +37,20 @@
|
|
|
22
37
|
background: var(--bw-bg);
|
|
23
38
|
color: var(--bw-text);
|
|
24
39
|
font-family: var(--bw-font);
|
|
40
|
+
font-feature-settings: "ss01", "cv11";
|
|
41
|
+
-webkit-font-smoothing: antialiased;
|
|
42
|
+
-moz-osx-font-smoothing: grayscale;
|
|
43
|
+
text-wrap: pretty;
|
|
25
44
|
overflow: hidden;
|
|
26
45
|
}
|
|
27
46
|
|
|
47
|
+
.bw h1,
|
|
48
|
+
.bw h2,
|
|
49
|
+
.bw h3,
|
|
50
|
+
.bw h4 {
|
|
51
|
+
text-wrap: balance;
|
|
52
|
+
}
|
|
53
|
+
|
|
28
54
|
.bw,
|
|
29
55
|
.bw * {
|
|
30
56
|
box-sizing: border-box;
|
|
@@ -89,7 +115,11 @@
|
|
|
89
115
|
}
|
|
90
116
|
|
|
91
117
|
.bw-skel--slot {
|
|
92
|
-
height:
|
|
118
|
+
/* Default mobile pill height: matches .bw-slot { padding: 12px 0;
|
|
119
|
+
font-size: 13px } ≈ 44px. Desktop overrides this below to match
|
|
120
|
+
the taller .bw-slots-desktop .bw-slot { padding: 14px 16px } pill. */
|
|
121
|
+
height: 44px;
|
|
122
|
+
border-radius: var(--bw-radius);
|
|
93
123
|
}
|
|
94
124
|
|
|
95
125
|
.bw-skel-list {
|
|
@@ -114,6 +144,176 @@
|
|
|
114
144
|
padding: 0 24px 24px;
|
|
115
145
|
}
|
|
116
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
|
+
|
|
117
317
|
.bw-loading,
|
|
118
318
|
.bw-empty {
|
|
119
319
|
display: flex;
|
|
@@ -174,7 +374,11 @@
|
|
|
174
374
|
|
|
175
375
|
.bw-body {
|
|
176
376
|
display: grid;
|
|
177
|
-
|
|
377
|
+
/* Left col widened from 22% to 28% so the service rows have room
|
|
378
|
+
to render the duration + description on their secondary line
|
|
379
|
+
without crunching. The middle (calendar) absorbs the difference;
|
|
380
|
+
the right (slots/details) stays at 22%. */
|
|
381
|
+
grid-template-columns: 28% 1fr 22%;
|
|
178
382
|
gap: 32px;
|
|
179
383
|
padding: 32px 48px 48px;
|
|
180
384
|
min-height: 0;
|
|
@@ -203,8 +407,8 @@
|
|
|
203
407
|
|
|
204
408
|
.bw-label {
|
|
205
409
|
display: block;
|
|
206
|
-
margin-bottom:
|
|
207
|
-
font-size:
|
|
410
|
+
margin-bottom: 14px;
|
|
411
|
+
font-size: 14px;
|
|
208
412
|
font-weight: 500;
|
|
209
413
|
color: var(--bw-text);
|
|
210
414
|
}
|
|
@@ -215,6 +419,35 @@
|
|
|
215
419
|
background: var(--bw-border);
|
|
216
420
|
}
|
|
217
421
|
|
|
422
|
+
/* Service groups — categories. We use generous vertical rhythm
|
|
423
|
+
(28px gap) and a small caption-style heading instead of a hard
|
|
424
|
+
divider line, so the category break reads through whitespace +
|
|
425
|
+
typographic shift, not via a blend-in 1px border. */
|
|
426
|
+
.bw-svc-group {
|
|
427
|
+
display: flex;
|
|
428
|
+
flex-direction: column;
|
|
429
|
+
gap: 2px;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.bw-svc-group + .bw-svc-group {
|
|
433
|
+
margin-top: 28px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/* Category heading: smaller than the section labels (14/500) but
|
|
437
|
+
heavier weight so the section break still reads cleanly. The
|
|
438
|
+
wider top gap above (28px between groups) carries most of the
|
|
439
|
+
visual separation. */
|
|
440
|
+
.bw-svc-category {
|
|
441
|
+
margin: 0 0 12px;
|
|
442
|
+
padding: 0;
|
|
443
|
+
font-size: 13px;
|
|
444
|
+
font-weight: 600;
|
|
445
|
+
line-height: 1.4;
|
|
446
|
+
letter-spacing: 0.01em;
|
|
447
|
+
color: var(--bw-text-secondary);
|
|
448
|
+
text-transform: none;
|
|
449
|
+
}
|
|
450
|
+
|
|
218
451
|
.bw-staff-dropdown {
|
|
219
452
|
position: relative;
|
|
220
453
|
margin-bottom: 10px;
|
|
@@ -223,20 +456,30 @@
|
|
|
223
456
|
.bw-staff-trigger {
|
|
224
457
|
display: flex;
|
|
225
458
|
align-items: center;
|
|
226
|
-
gap:
|
|
459
|
+
gap: 12px;
|
|
227
460
|
width: 100%;
|
|
228
|
-
padding:
|
|
229
|
-
border:
|
|
461
|
+
padding: 10px 14px;
|
|
462
|
+
border: 1px solid var(--bw-border);
|
|
230
463
|
border-radius: var(--bw-radius);
|
|
231
464
|
background: transparent;
|
|
232
465
|
cursor: pointer;
|
|
233
466
|
text-align: left;
|
|
234
|
-
transition: border-color
|
|
467
|
+
transition: border-color var(--bw-duration) var(--bw-ease),
|
|
468
|
+
box-shadow var(--bw-duration) var(--bw-ease),
|
|
469
|
+
background var(--bw-duration) var(--bw-ease);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.bw-staff-trigger:hover {
|
|
473
|
+
border-color: var(--bw-text-secondary);
|
|
235
474
|
}
|
|
236
475
|
|
|
237
|
-
.bw-staff-trigger:
|
|
238
|
-
.bw-staff-trigger.has-value {
|
|
476
|
+
.bw-staff-trigger:focus-visible {
|
|
239
477
|
border-color: var(--bw-primary);
|
|
478
|
+
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.bw-staff-trigger[disabled] {
|
|
482
|
+
cursor: default;
|
|
240
483
|
}
|
|
241
484
|
|
|
242
485
|
.bw-staff-trigger-content,
|
|
@@ -252,6 +495,7 @@
|
|
|
252
495
|
}
|
|
253
496
|
|
|
254
497
|
.bw-staff-avatar {
|
|
498
|
+
position: relative;
|
|
255
499
|
display: flex;
|
|
256
500
|
flex-shrink: 0;
|
|
257
501
|
align-items: center;
|
|
@@ -261,6 +505,15 @@
|
|
|
261
505
|
border-radius: 999px;
|
|
262
506
|
background: var(--bw-border-light);
|
|
263
507
|
color: var(--bw-text-secondary);
|
|
508
|
+
overflow: hidden;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.bw-staff-avatar-img {
|
|
512
|
+
position: absolute;
|
|
513
|
+
inset: 0;
|
|
514
|
+
width: 100%;
|
|
515
|
+
height: 100%;
|
|
516
|
+
object-fit: cover;
|
|
264
517
|
}
|
|
265
518
|
|
|
266
519
|
.bw-staff-avatar svg,
|
|
@@ -299,6 +552,9 @@
|
|
|
299
552
|
font-weight: 500;
|
|
300
553
|
line-height: 1.3;
|
|
301
554
|
color: var(--bw-text);
|
|
555
|
+
white-space: nowrap;
|
|
556
|
+
overflow: hidden;
|
|
557
|
+
text-overflow: ellipsis;
|
|
302
558
|
}
|
|
303
559
|
|
|
304
560
|
.bw-staff-trigger-desc,
|
|
@@ -333,8 +589,9 @@
|
|
|
333
589
|
z-index: 9;
|
|
334
590
|
display: flex;
|
|
335
591
|
flex-direction: column;
|
|
592
|
+
gap: 2px;
|
|
336
593
|
width: 100%;
|
|
337
|
-
padding:
|
|
594
|
+
padding: 6px;
|
|
338
595
|
border: 1px solid var(--bw-border);
|
|
339
596
|
border-radius: var(--bw-radius);
|
|
340
597
|
background: var(--bw-bg);
|
|
@@ -353,10 +610,11 @@
|
|
|
353
610
|
width: 100%;
|
|
354
611
|
padding: 10px 12px;
|
|
355
612
|
border: 0;
|
|
356
|
-
border-radius:
|
|
613
|
+
border-radius: var(--bw-radius-sm);
|
|
357
614
|
background: transparent;
|
|
358
615
|
cursor: pointer;
|
|
359
616
|
text-align: left;
|
|
617
|
+
transition: background var(--bw-duration) var(--bw-ease);
|
|
360
618
|
}
|
|
361
619
|
|
|
362
620
|
.bw-staff-option:hover,
|
|
@@ -364,52 +622,131 @@
|
|
|
364
622
|
background: var(--bw-hover);
|
|
365
623
|
}
|
|
366
624
|
|
|
625
|
+
/* When the picker is in expanded mode (full list visible), let it
|
|
626
|
+
stretch to fill the column so the scroll list extends down to the
|
|
627
|
+
bottom of the left column instead of capping at an arbitrary
|
|
628
|
+
height. Detected via :has() — the .bw-svc-scroll only renders in
|
|
629
|
+
expanded mode; in collapsed mode the picker contains only the
|
|
630
|
+
selected card, which sizes to its content (so the provider section
|
|
631
|
+
below sits flush, not pushed to the bottom). */
|
|
632
|
+
.bw-service-picker:has(.bw-svc-scroll) {
|
|
633
|
+
display: flex;
|
|
634
|
+
flex-direction: column;
|
|
635
|
+
flex: 1;
|
|
636
|
+
min-height: 0;
|
|
637
|
+
}
|
|
638
|
+
|
|
367
639
|
.bw-svc-scroll {
|
|
368
|
-
display:
|
|
369
|
-
|
|
640
|
+
display: flex;
|
|
641
|
+
flex-direction: column;
|
|
642
|
+
flex: 1;
|
|
643
|
+
min-height: 0;
|
|
370
644
|
}
|
|
371
645
|
|
|
372
646
|
.bw-svc-scroll-wrap {
|
|
647
|
+
position: relative;
|
|
648
|
+
flex: 1;
|
|
373
649
|
min-height: 0;
|
|
374
|
-
max-height: 320px;
|
|
375
650
|
overflow-y: auto;
|
|
651
|
+
/* Soft fade at the top + bottom so the scroll edge looks
|
|
652
|
+
intentional instead of cutting service rows in half. Matches
|
|
653
|
+
the editorial "warm paper, not cold glass" feel — depth from
|
|
654
|
+
softness, not a hard line. */
|
|
655
|
+
-webkit-mask-image: linear-gradient(
|
|
656
|
+
to bottom,
|
|
657
|
+
transparent 0,
|
|
658
|
+
#000 18px,
|
|
659
|
+
#000 calc(100% - 18px),
|
|
660
|
+
transparent 100%
|
|
661
|
+
);
|
|
662
|
+
mask-image: linear-gradient(
|
|
663
|
+
to bottom,
|
|
664
|
+
transparent 0,
|
|
665
|
+
#000 18px,
|
|
666
|
+
#000 calc(100% - 18px),
|
|
667
|
+
transparent 100%
|
|
668
|
+
);
|
|
669
|
+
scroll-padding-block: 16px;
|
|
670
|
+
/* Native scrollbar tightened so it doesn't dominate the
|
|
671
|
+
softened edge. Webkit-only; Firefox uses default. */
|
|
672
|
+
scrollbar-width: thin;
|
|
673
|
+
scrollbar-color: var(--bw-border) transparent;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.bw-svc-scroll-wrap::-webkit-scrollbar {
|
|
677
|
+
width: 6px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.bw-svc-scroll-wrap::-webkit-scrollbar-thumb {
|
|
681
|
+
border-radius: var(--bw-radius-pill);
|
|
682
|
+
background: var(--bw-border);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.bw-svc-scroll-wrap::-webkit-scrollbar-thumb:hover {
|
|
686
|
+
background: var(--bw-text-muted);
|
|
376
687
|
}
|
|
377
688
|
|
|
378
689
|
.bw-svc-scroll-inner {
|
|
379
690
|
display: flex;
|
|
380
691
|
flex-direction: column;
|
|
381
|
-
padding-
|
|
692
|
+
padding-block: 12px 24px;
|
|
382
693
|
}
|
|
383
694
|
|
|
695
|
+
/* Service row — refactored to fix the narrow-width hierarchy issues:
|
|
696
|
+
- Top-aligned items so price reads with the first line of the name
|
|
697
|
+
- Name allowed to wrap with text-wrap: pretty (no orphans)
|
|
698
|
+
- Description line-clamps to 2 lines instead of single-line ellipsis
|
|
699
|
+
- Concentric inner radius (row 12px - 14px padding around 20px check
|
|
700
|
+
leaves the optical center on the first line of text) */
|
|
384
701
|
.bw-svc-row {
|
|
385
702
|
display: flex;
|
|
386
|
-
align-items:
|
|
387
|
-
gap:
|
|
703
|
+
align-items: flex-start;
|
|
704
|
+
gap: 12px;
|
|
388
705
|
width: 100%;
|
|
389
|
-
padding:
|
|
706
|
+
padding: 14px;
|
|
390
707
|
border: 0;
|
|
391
|
-
border-radius:
|
|
708
|
+
border-radius: var(--bw-radius);
|
|
392
709
|
background: transparent;
|
|
393
710
|
cursor: pointer;
|
|
394
711
|
text-align: left;
|
|
395
|
-
transition: background
|
|
712
|
+
transition: background var(--bw-duration) var(--bw-ease),
|
|
713
|
+
box-shadow var(--bw-duration) var(--bw-ease),
|
|
714
|
+
transform var(--bw-duration) var(--bw-ease);
|
|
396
715
|
}
|
|
397
716
|
|
|
398
717
|
.bw-svc-row:hover {
|
|
399
718
|
background: var(--bw-hover);
|
|
400
719
|
}
|
|
401
720
|
|
|
721
|
+
.bw-svc-row:focus-visible {
|
|
722
|
+
background: var(--bw-hover);
|
|
723
|
+
box-shadow: inset 0 0 0 1.5px var(--bw-text);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.bw-svc-row:active {
|
|
727
|
+
transform: scale(0.98);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/* Checkbox: aligned to first line of name (1.4 line-height on 14px =
|
|
731
|
+
~20px line, matches check height so they share a baseline). */
|
|
402
732
|
.bw-check {
|
|
403
733
|
display: flex;
|
|
404
734
|
flex-shrink: 0;
|
|
405
735
|
align-items: center;
|
|
406
736
|
justify-content: center;
|
|
407
|
-
width:
|
|
408
|
-
height:
|
|
409
|
-
|
|
410
|
-
border
|
|
737
|
+
width: 20px;
|
|
738
|
+
height: 20px;
|
|
739
|
+
margin-top: 1px;
|
|
740
|
+
border: 1.5px solid var(--bw-border);
|
|
741
|
+
border-radius: var(--bw-radius-xs);
|
|
411
742
|
color: transparent;
|
|
412
|
-
transition:
|
|
743
|
+
transition: border-color var(--bw-duration) var(--bw-ease),
|
|
744
|
+
background var(--bw-duration) var(--bw-ease),
|
|
745
|
+
color var(--bw-duration) var(--bw-ease);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.bw-svc-row:hover .bw-check {
|
|
749
|
+
border-color: var(--bw-text-secondary);
|
|
413
750
|
}
|
|
414
751
|
|
|
415
752
|
.bw-check.is-checked {
|
|
@@ -419,38 +756,156 @@
|
|
|
419
756
|
}
|
|
420
757
|
|
|
421
758
|
.bw-check--lg {
|
|
422
|
-
width:
|
|
423
|
-
height:
|
|
424
|
-
|
|
759
|
+
width: 22px;
|
|
760
|
+
height: 22px;
|
|
761
|
+
margin-top: 0;
|
|
762
|
+
border-radius: var(--bw-radius-xs);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/* Service thumbnail (Fresha/Square pattern). Replaces the checkbox
|
|
766
|
+
slot when a service has an image. Selected state shows a small
|
|
767
|
+
primary-colored badge in the bottom-right corner with the same
|
|
768
|
+
check icon — keeps the affordance consistent with image-less
|
|
769
|
+
rows. Concentric radius (md = 12px) matches surrounding chrome. */
|
|
770
|
+
.bw-svc-image {
|
|
771
|
+
position: relative;
|
|
772
|
+
flex-shrink: 0;
|
|
773
|
+
width: 80px;
|
|
774
|
+
height: 80px;
|
|
775
|
+
margin-top: 0;
|
|
776
|
+
border-radius: var(--bw-radius-md);
|
|
777
|
+
overflow: hidden;
|
|
778
|
+
background: var(--bw-border-light);
|
|
779
|
+
outline: 1px solid rgba(0, 0, 0, 0.06);
|
|
780
|
+
outline-offset: -1px;
|
|
781
|
+
transition: outline-color var(--bw-duration) var(--bw-ease);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.bw-svc-image img {
|
|
785
|
+
display: block;
|
|
786
|
+
width: 100%;
|
|
787
|
+
height: 100%;
|
|
788
|
+
object-fit: cover;
|
|
789
|
+
object-position: center;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.bw-svc-row:hover .bw-svc-image {
|
|
793
|
+
outline-color: rgba(0, 0, 0, 0.12);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.bw-svc-image.is-checked {
|
|
797
|
+
outline-color: var(--bw-primary);
|
|
798
|
+
outline-width: 1.5px;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.bw-svc-image-badge {
|
|
802
|
+
position: absolute;
|
|
803
|
+
right: 4px;
|
|
804
|
+
bottom: 4px;
|
|
805
|
+
display: flex;
|
|
806
|
+
align-items: center;
|
|
807
|
+
justify-content: center;
|
|
808
|
+
width: 18px;
|
|
809
|
+
height: 18px;
|
|
810
|
+
border-radius: var(--bw-radius-pill);
|
|
811
|
+
background: var(--bw-primary);
|
|
812
|
+
color: var(--bw-primary-text);
|
|
813
|
+
box-shadow: var(--bw-shadow-badge);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.bw-svc-image-badge svg {
|
|
817
|
+
width: 11px;
|
|
818
|
+
height: 11px;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.bw-svc-image--lg {
|
|
822
|
+
width: 96px;
|
|
823
|
+
height: 96px;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/* Meta footer row: groups duration + price on a single line under
|
|
827
|
+
the description with a dot separator. Replaces the old top-right
|
|
828
|
+
floating price so the row reads as a tidy stack instead of an
|
|
829
|
+
L-shape. */
|
|
830
|
+
.bw-svc-meta-row {
|
|
831
|
+
display: inline-flex;
|
|
832
|
+
align-items: baseline;
|
|
833
|
+
gap: 8px;
|
|
834
|
+
margin-top: 4px;
|
|
835
|
+
font-variant-numeric: tabular-nums;
|
|
836
|
+
color: var(--bw-text-secondary);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.bw-svc-meta-row .bw-svc-meta {
|
|
840
|
+
margin: 0;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
.bw-svc-meta-row .bw-svc-price {
|
|
844
|
+
margin: 0;
|
|
845
|
+
flex-shrink: 0;
|
|
846
|
+
color: var(--bw-text);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.bw-svc-meta-dot {
|
|
850
|
+
color: var(--bw-text-muted);
|
|
851
|
+
user-select: none;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.bw-svc-info {
|
|
855
|
+
display: flex;
|
|
856
|
+
flex-direction: column;
|
|
857
|
+
gap: 4px;
|
|
858
|
+
min-width: 0;
|
|
859
|
+
flex: 1 1 auto;
|
|
425
860
|
}
|
|
426
861
|
|
|
862
|
+
/* Name: 14px, allowed to wrap, pretty wrap to avoid one-word
|
|
863
|
+
orphans, hard cap at 2 lines so very long names truncate
|
|
864
|
+
cleanly instead of dominating the row. */
|
|
427
865
|
.bw-svc-name {
|
|
428
|
-
font-size:
|
|
429
|
-
font-weight:
|
|
430
|
-
line-height: 1.
|
|
866
|
+
font-size: 14px;
|
|
867
|
+
font-weight: 600;
|
|
868
|
+
line-height: 1.35;
|
|
869
|
+
letter-spacing: -0.005em;
|
|
431
870
|
color: var(--bw-text);
|
|
871
|
+
text-wrap: pretty;
|
|
872
|
+
display: -webkit-box;
|
|
873
|
+
-webkit-line-clamp: 2;
|
|
874
|
+
-webkit-box-orient: vertical;
|
|
875
|
+
overflow: hidden;
|
|
432
876
|
}
|
|
433
877
|
|
|
878
|
+
/* Meta + description sit together as a soft secondary line. Meta
|
|
879
|
+
(duration) renders inline, then description picks up after a
|
|
880
|
+
middle dot. Wrapping is allowed; we line-clamp to 2 so multi-
|
|
881
|
+
sentence descriptions don't blow up the row height. */
|
|
434
882
|
.bw-svc-meta {
|
|
435
|
-
font-size:
|
|
436
|
-
line-height: 1.
|
|
437
|
-
color: var(--bw-text-
|
|
883
|
+
font-size: 13px;
|
|
884
|
+
line-height: 1.45;
|
|
885
|
+
color: var(--bw-text-secondary);
|
|
886
|
+
font-variant-numeric: tabular-nums;
|
|
438
887
|
}
|
|
439
888
|
|
|
440
889
|
.bw-svc-desc {
|
|
441
|
-
font-size:
|
|
442
|
-
line-height: 1.
|
|
443
|
-
color: var(--bw-text-
|
|
444
|
-
|
|
445
|
-
text-overflow: ellipsis;
|
|
446
|
-
white-space: nowrap;
|
|
890
|
+
font-size: 13px;
|
|
891
|
+
line-height: 1.45;
|
|
892
|
+
color: var(--bw-text-secondary);
|
|
893
|
+
text-wrap: pretty;
|
|
447
894
|
}
|
|
448
895
|
|
|
896
|
+
/* Price: same scale as name so the first-line baseline locks them
|
|
897
|
+
together optically. Regular weight (not 600) so price doesn't
|
|
898
|
+
compete for attention with the name — name leads, price reads
|
|
899
|
+
as a quiet endcap. */
|
|
449
900
|
.bw-svc-price {
|
|
450
901
|
flex-shrink: 0;
|
|
902
|
+
margin-top: 1px;
|
|
451
903
|
font-size: 14px;
|
|
452
|
-
font-weight:
|
|
904
|
+
font-weight: 500;
|
|
905
|
+
line-height: 1.35;
|
|
453
906
|
color: var(--bw-text);
|
|
907
|
+
font-variant-numeric: tabular-nums;
|
|
908
|
+
letter-spacing: -0.005em;
|
|
454
909
|
}
|
|
455
910
|
|
|
456
911
|
.bw-step-2-heading {
|
|
@@ -486,7 +941,7 @@
|
|
|
486
941
|
}
|
|
487
942
|
|
|
488
943
|
.bw-svc-row--full .bw-svc-meta {
|
|
489
|
-
font-size:
|
|
944
|
+
font-size: 13px;
|
|
490
945
|
}
|
|
491
946
|
|
|
492
947
|
.bw-col--center {
|
|
@@ -503,9 +958,10 @@
|
|
|
503
958
|
flex: 1;
|
|
504
959
|
flex-direction: column;
|
|
505
960
|
gap: 24px;
|
|
506
|
-
padding: 32px;
|
|
961
|
+
padding: 24px 32px 32px;
|
|
507
962
|
border: 1px solid var(--bw-border);
|
|
508
|
-
border-radius: var(--bw-radius);
|
|
963
|
+
border-radius: var(--bw-radius-lg);
|
|
964
|
+
background: var(--bw-bg);
|
|
509
965
|
}
|
|
510
966
|
|
|
511
967
|
.bw-cal-prompt,
|
|
@@ -518,19 +974,235 @@
|
|
|
518
974
|
color: var(--bw-text-muted);
|
|
519
975
|
}
|
|
520
976
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
977
|
+
/* Transition the service-picker swap (full list <-> selected card)
|
|
978
|
+
and the provider section's reveal. Each branch animates in on
|
|
979
|
+
mount via the shared bw-slots-fade-in keyframe so the picker
|
|
980
|
+
feels like a settling state-machine, not a hard swap. */
|
|
981
|
+
.bw-svc-selected,
|
|
982
|
+
.bw-svc-scroll,
|
|
983
|
+
.bw-provider-section {
|
|
984
|
+
animation: bw-slots-fade-in 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
524
985
|
}
|
|
525
986
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
987
|
+
@media (prefers-reduced-motion: reduce) {
|
|
988
|
+
.bw-svc-selected,
|
|
989
|
+
.bw-svc-scroll,
|
|
990
|
+
.bw-provider-section {
|
|
991
|
+
animation: none;
|
|
992
|
+
}
|
|
530
993
|
}
|
|
531
994
|
|
|
532
|
-
.
|
|
533
|
-
|
|
995
|
+
/* Service picker — collapsed selected state. Once a service is
|
|
996
|
+
picked, the long list collapses to a read-only card showing just
|
|
997
|
+
the selected service plus a "Change" link. Below this card the
|
|
998
|
+
provider section reveals (per the service-first flow). The card
|
|
999
|
+
reuses .bw-svc-row chrome but with --readonly disabling hover/cursor. */
|
|
1000
|
+
.bw-svc-selected {
|
|
1001
|
+
display: flex;
|
|
1002
|
+
flex-direction: column;
|
|
1003
|
+
gap: 12px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.bw-svc-selected-header {
|
|
1007
|
+
display: flex;
|
|
1008
|
+
align-items: baseline;
|
|
1009
|
+
justify-content: space-between;
|
|
1010
|
+
gap: 12px;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.bw-svc-selected-header .bw-label {
|
|
1014
|
+
margin-bottom: 0;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
.bw-svc-change {
|
|
1018
|
+
padding: 0;
|
|
1019
|
+
border: 0;
|
|
1020
|
+
background: transparent;
|
|
1021
|
+
color: var(--bw-text-secondary);
|
|
1022
|
+
font-size: 13px;
|
|
1023
|
+
font-weight: 500;
|
|
1024
|
+
cursor: pointer;
|
|
1025
|
+
transition: color var(--bw-duration) var(--bw-ease);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.bw-svc-change:hover {
|
|
1029
|
+
color: var(--bw-text);
|
|
1030
|
+
text-decoration: underline;
|
|
1031
|
+
text-underline-offset: 3px;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.bw-svc-row--readonly {
|
|
1035
|
+
cursor: default;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
.bw-svc-row--readonly:hover {
|
|
1039
|
+
background: transparent;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
.bw-svc-row--readonly:hover .bw-check {
|
|
1043
|
+
border-color: var(--bw-primary);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/* === Payment panel (design proposal) ===
|
|
1047
|
+
Renders inside the details form when paymentMode === 'full' or
|
|
1048
|
+
'deposit'. Two stacks: a soft cararra-tinted summary card with
|
|
1049
|
+
line items + total + charge breakdown (mirrors the reference
|
|
1050
|
+
pattern but in Sable's tone), then a card-details slot for the
|
|
1051
|
+
eventual Stripe PaymentElement, then a small "secured by Stripe"
|
|
1052
|
+
reassurance line. Stub fields are placeholders only — real impl
|
|
1053
|
+
replaces .bw-pay-card-slot's children with <PaymentElement />. */
|
|
1054
|
+
.bw-pay {
|
|
1055
|
+
display: flex;
|
|
1056
|
+
flex-direction: column;
|
|
1057
|
+
gap: 18px;
|
|
1058
|
+
margin-top: 4px;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.bw-pay-summary {
|
|
1062
|
+
display: flex;
|
|
1063
|
+
flex-direction: column;
|
|
1064
|
+
padding: 18px 20px;
|
|
1065
|
+
border: 1px solid var(--bw-border);
|
|
1066
|
+
border-radius: var(--bw-radius-lg);
|
|
1067
|
+
background: var(--bw-border-light);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.bw-pay-summary-header {
|
|
1071
|
+
margin-bottom: 14px;
|
|
1072
|
+
font-size: 13px;
|
|
1073
|
+
font-weight: 600;
|
|
1074
|
+
color: var(--bw-text);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.bw-pay-summary-row {
|
|
1078
|
+
display: flex;
|
|
1079
|
+
align-items: baseline;
|
|
1080
|
+
justify-content: space-between;
|
|
1081
|
+
gap: 16px;
|
|
1082
|
+
padding: 4px 0;
|
|
1083
|
+
font-size: 13px;
|
|
1084
|
+
line-height: 1.45;
|
|
1085
|
+
color: var(--bw-text);
|
|
1086
|
+
font-variant-numeric: tabular-nums;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.bw-pay-summary-row--muted {
|
|
1090
|
+
color: var(--bw-text-secondary);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
.bw-pay-summary-row--total {
|
|
1094
|
+
font-weight: 600;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.bw-pay-summary-row--strong {
|
|
1098
|
+
margin-top: 2px;
|
|
1099
|
+
font-size: 14px;
|
|
1100
|
+
font-weight: 600;
|
|
1101
|
+
color: var(--bw-text);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
.bw-pay-summary-val {
|
|
1105
|
+
flex-shrink: 0;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
.bw-pay-summary-divider {
|
|
1109
|
+
height: 1px;
|
|
1110
|
+
margin: 10px 0;
|
|
1111
|
+
background: var(--bw-border);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.bw-pay-card {
|
|
1115
|
+
display: flex;
|
|
1116
|
+
flex-direction: column;
|
|
1117
|
+
gap: 10px;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
.bw-pay-card .bw-label {
|
|
1121
|
+
margin-bottom: 0;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/* Card slot: a soft surface where Stripe's PaymentElement will
|
|
1125
|
+
mount. Until then, render a static stub of the fields so the
|
|
1126
|
+
spacing + chrome can be validated. */
|
|
1127
|
+
.bw-pay-card-slot {
|
|
1128
|
+
padding: 14px;
|
|
1129
|
+
border: 1px solid var(--bw-border);
|
|
1130
|
+
border-radius: var(--bw-radius-lg);
|
|
1131
|
+
background: var(--bw-bg);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
.bw-pay-card-stub {
|
|
1135
|
+
display: flex;
|
|
1136
|
+
flex-direction: column;
|
|
1137
|
+
gap: 12px;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
.bw-pay-card-stub-row {
|
|
1141
|
+
display: flex;
|
|
1142
|
+
flex-direction: column;
|
|
1143
|
+
gap: 4px;
|
|
1144
|
+
padding: 10px 12px;
|
|
1145
|
+
border: 1px solid var(--bw-border-light);
|
|
1146
|
+
border-radius: var(--bw-radius);
|
|
1147
|
+
background: var(--bw-border-light);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.bw-pay-card-stub-label {
|
|
1151
|
+
font-size: 11px;
|
|
1152
|
+
font-weight: 500;
|
|
1153
|
+
color: var(--bw-text-muted);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
.bw-pay-card-stub-value {
|
|
1157
|
+
font-size: 13px;
|
|
1158
|
+
color: var(--bw-text-secondary);
|
|
1159
|
+
font-variant-numeric: tabular-nums;
|
|
1160
|
+
letter-spacing: 0.04em;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.bw-pay-card-stub-grid {
|
|
1164
|
+
display: grid;
|
|
1165
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
1166
|
+
gap: 8px;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.bw-pay-secure {
|
|
1170
|
+
margin: 0;
|
|
1171
|
+
font-size: 12px;
|
|
1172
|
+
color: var(--bw-text-muted);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/* Empty-state when no service is selected. Centered in the slots
|
|
1176
|
+
column so the right pane reads as "waiting for input" instead
|
|
1177
|
+
of just blank. Used in both desktop (right column) and mobile
|
|
1178
|
+
(inline under the calendar) since slotsArea renders in both. */
|
|
1179
|
+
.bw-slots-empty {
|
|
1180
|
+
display: flex;
|
|
1181
|
+
flex: 1;
|
|
1182
|
+
align-items: center;
|
|
1183
|
+
justify-content: center;
|
|
1184
|
+
min-height: 120px;
|
|
1185
|
+
margin: 0;
|
|
1186
|
+
padding: 24px 12px;
|
|
1187
|
+
font-size: 14px;
|
|
1188
|
+
line-height: 1.5;
|
|
1189
|
+
color: var(--bw-text-secondary);
|
|
1190
|
+
text-align: center;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.bw-cal-prompt {
|
|
1194
|
+
padding-top: 8px;
|
|
1195
|
+
text-align: center;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
.bw-cal-header {
|
|
1199
|
+
display: flex;
|
|
1200
|
+
align-items: center;
|
|
1201
|
+
justify-content: space-between;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
.bw-month-dropdown {
|
|
1205
|
+
position: relative;
|
|
534
1206
|
}
|
|
535
1207
|
|
|
536
1208
|
.bw-month-btn {
|
|
@@ -609,35 +1281,42 @@
|
|
|
609
1281
|
.bw-cal-grid {
|
|
610
1282
|
display: grid;
|
|
611
1283
|
grid-template-columns: repeat(7, 1fr);
|
|
1284
|
+
column-gap: 6px;
|
|
612
1285
|
}
|
|
613
1286
|
|
|
614
1287
|
.bw-cal-weekdays {
|
|
615
1288
|
text-align: center;
|
|
1289
|
+
margin-bottom: 4px;
|
|
616
1290
|
}
|
|
617
1291
|
|
|
618
1292
|
.bw-cal-weekdays span {
|
|
619
1293
|
padding: 6px 0;
|
|
620
|
-
font-size:
|
|
621
|
-
font-weight:
|
|
1294
|
+
font-size: 11px;
|
|
1295
|
+
font-weight: 600;
|
|
1296
|
+
letter-spacing: 0.08em;
|
|
1297
|
+
text-transform: uppercase;
|
|
622
1298
|
color: var(--bw-text-muted);
|
|
623
1299
|
}
|
|
624
1300
|
|
|
625
1301
|
.bw-cal-grid {
|
|
626
|
-
gap: 6px
|
|
1302
|
+
gap: 6px;
|
|
627
1303
|
}
|
|
628
1304
|
|
|
629
1305
|
.bw-cal-day {
|
|
1306
|
+
position: relative;
|
|
630
1307
|
display: flex;
|
|
631
1308
|
align-items: center;
|
|
632
1309
|
justify-content: center;
|
|
633
1310
|
height: 48px;
|
|
634
1311
|
border: 0;
|
|
635
|
-
border-radius:
|
|
1312
|
+
border-radius: var(--bw-radius-sm);
|
|
636
1313
|
background: transparent;
|
|
637
1314
|
color: var(--bw-text);
|
|
638
1315
|
cursor: default;
|
|
639
1316
|
font-size: 13px;
|
|
640
|
-
|
|
1317
|
+
font-variant-numeric: tabular-nums;
|
|
1318
|
+
transition: background var(--bw-duration) var(--bw-ease),
|
|
1319
|
+
color var(--bw-duration) var(--bw-ease);
|
|
641
1320
|
}
|
|
642
1321
|
|
|
643
1322
|
.bw-cal-day.is-outside {
|
|
@@ -662,6 +1341,26 @@
|
|
|
662
1341
|
font-weight: 600;
|
|
663
1342
|
}
|
|
664
1343
|
|
|
1344
|
+
/* Dot under today's number — subtle reference indicator. Hidden when
|
|
1345
|
+
the day is the active selection (the filled background already
|
|
1346
|
+
communicates focus). */
|
|
1347
|
+
.bw-cal-day.is-today::after {
|
|
1348
|
+
content: '';
|
|
1349
|
+
position: absolute;
|
|
1350
|
+
bottom: 6px;
|
|
1351
|
+
left: 50%;
|
|
1352
|
+
width: 4px;
|
|
1353
|
+
height: 4px;
|
|
1354
|
+
border-radius: var(--bw-radius-pill);
|
|
1355
|
+
background: var(--bw-text-muted);
|
|
1356
|
+
transform: translateX(-50%);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.bw-cal-day.is-today.is-selected::after {
|
|
1360
|
+
background: var(--bw-primary-text);
|
|
1361
|
+
opacity: 0.7;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
665
1364
|
.bw-cal-day.is-blocked,
|
|
666
1365
|
.bw-cal-day.is-disabled {
|
|
667
1366
|
color: var(--bw-border);
|
|
@@ -674,6 +1373,48 @@
|
|
|
674
1373
|
margin-top: 8px;
|
|
675
1374
|
}
|
|
676
1375
|
|
|
1376
|
+
/* Wrapper that re-mounts whenever service/staff/date/loading state
|
|
1377
|
+
changes (key-driven from JSX). The wrapper itself fades, and each
|
|
1378
|
+
slot pill staggers in via --bw-slot-i set inline so the list
|
|
1379
|
+
reads as "settling into place" instead of popping. */
|
|
1380
|
+
.bw-slots-fade {
|
|
1381
|
+
animation: bw-slots-fade-in 220ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.bw-slots-fade .bw-slot {
|
|
1385
|
+
animation: bw-slot-stagger-in 260ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
1386
|
+
animation-delay: calc(var(--bw-slot-i, 0) * 22ms);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
@keyframes bw-slots-fade-in {
|
|
1390
|
+
from {
|
|
1391
|
+
opacity: 0;
|
|
1392
|
+
transform: translateY(4px);
|
|
1393
|
+
}
|
|
1394
|
+
to {
|
|
1395
|
+
opacity: 1;
|
|
1396
|
+
transform: translateY(0);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
@keyframes bw-slot-stagger-in {
|
|
1401
|
+
from {
|
|
1402
|
+
opacity: 0;
|
|
1403
|
+
transform: translateY(6px) scale(0.98);
|
|
1404
|
+
}
|
|
1405
|
+
to {
|
|
1406
|
+
opacity: 1;
|
|
1407
|
+
transform: translateY(0) scale(1);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1412
|
+
.bw-slots-fade,
|
|
1413
|
+
.bw-slots-fade .bw-slot {
|
|
1414
|
+
animation: none;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
677
1418
|
.bw-slot {
|
|
678
1419
|
padding: 12px 0;
|
|
679
1420
|
border: 1px solid var(--bw-border);
|
|
@@ -683,12 +1424,21 @@
|
|
|
683
1424
|
text-align: center;
|
|
684
1425
|
white-space: nowrap;
|
|
685
1426
|
font-size: 13px;
|
|
1427
|
+
font-variant-numeric: tabular-nums;
|
|
686
1428
|
color: var(--bw-text);
|
|
687
|
-
transition:
|
|
1429
|
+
transition: border-color var(--bw-duration) var(--bw-ease),
|
|
1430
|
+
background var(--bw-duration) var(--bw-ease),
|
|
1431
|
+
color var(--bw-duration) var(--bw-ease),
|
|
1432
|
+
transform var(--bw-duration) var(--bw-ease);
|
|
688
1433
|
}
|
|
689
1434
|
|
|
690
1435
|
.bw-slot:hover {
|
|
691
|
-
border-color: var(--bw-text);
|
|
1436
|
+
border-color: var(--bw-text-secondary);
|
|
1437
|
+
background: var(--bw-hover);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.bw-slot:active {
|
|
1441
|
+
transform: scale(0.98);
|
|
692
1442
|
}
|
|
693
1443
|
|
|
694
1444
|
.bw-slot.is-active {
|
|
@@ -741,23 +1491,45 @@
|
|
|
741
1491
|
.bw-field input,
|
|
742
1492
|
.bw-field textarea {
|
|
743
1493
|
width: 100%;
|
|
744
|
-
padding:
|
|
1494
|
+
padding: 11px 16px;
|
|
745
1495
|
border: 1px solid var(--bw-border);
|
|
746
|
-
border-radius:
|
|
1496
|
+
border-radius: var(--bw-radius-md);
|
|
747
1497
|
background: transparent;
|
|
748
1498
|
color: var(--bw-text);
|
|
749
1499
|
font-size: 13px;
|
|
750
|
-
|
|
1500
|
+
font-family: inherit;
|
|
1501
|
+
transition: border-color var(--bw-duration) var(--bw-ease),
|
|
1502
|
+
box-shadow var(--bw-duration) var(--bw-ease);
|
|
751
1503
|
}
|
|
752
1504
|
|
|
753
1505
|
.bw-field textarea {
|
|
754
|
-
min-height:
|
|
1506
|
+
min-height: 96px;
|
|
755
1507
|
resize: none;
|
|
756
1508
|
}
|
|
757
1509
|
|
|
1510
|
+
.bw-field input:hover,
|
|
1511
|
+
.bw-field textarea:hover {
|
|
1512
|
+
border-color: var(--bw-text-secondary);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
758
1515
|
.bw-field input:focus,
|
|
759
1516
|
.bw-field textarea:focus {
|
|
760
1517
|
border-color: var(--bw-primary);
|
|
1518
|
+
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
.bw-field.is-invalid input,
|
|
1522
|
+
.bw-field.is-invalid textarea,
|
|
1523
|
+
.bw-field.is-invalid select {
|
|
1524
|
+
border-color: var(--bw-error-text);
|
|
1525
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--bw-error-text) 12%, transparent);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
.bw-field-error {
|
|
1529
|
+
margin-top: -4px;
|
|
1530
|
+
color: var(--bw-error-text);
|
|
1531
|
+
font-size: 12px;
|
|
1532
|
+
line-height: 1.35;
|
|
761
1533
|
}
|
|
762
1534
|
|
|
763
1535
|
.bw-field input::placeholder,
|
|
@@ -777,25 +1549,53 @@
|
|
|
777
1549
|
color: var(--bw-text-muted);
|
|
778
1550
|
}
|
|
779
1551
|
|
|
1552
|
+
.bw-submit-help {
|
|
1553
|
+
border: 1px solid color-mix(in srgb, var(--bw-error-text) 24%, var(--bw-border));
|
|
1554
|
+
border-radius: 10px;
|
|
1555
|
+
background: color-mix(in srgb, var(--bw-error-text) 8%, var(--bw-bg));
|
|
1556
|
+
color: var(--bw-text);
|
|
1557
|
+
padding: 10px 12px;
|
|
1558
|
+
font-size: 12px;
|
|
1559
|
+
line-height: 1.45;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.bw-submit-help-title {
|
|
1563
|
+
display: block;
|
|
1564
|
+
font-weight: 650;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
.bw-submit-help ul {
|
|
1568
|
+
margin: 6px 0 0;
|
|
1569
|
+
padding-left: 18px;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
.bw-submit-help li + li {
|
|
1573
|
+
margin-top: 2px;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
780
1576
|
.bw-required {
|
|
781
1577
|
color: var(--bw-error-text);
|
|
782
1578
|
font-weight: 400;
|
|
783
1579
|
}
|
|
784
1580
|
|
|
785
1581
|
.bw-summary {
|
|
786
|
-
padding
|
|
1582
|
+
padding: 18px;
|
|
1583
|
+
border: 1px solid var(--bw-border);
|
|
1584
|
+
border-radius: var(--bw-radius-lg);
|
|
1585
|
+
background: var(--bw-bg);
|
|
787
1586
|
}
|
|
788
1587
|
|
|
789
1588
|
.bw-summary-title {
|
|
790
1589
|
display: block;
|
|
791
|
-
margin-bottom:
|
|
792
|
-
font-size:
|
|
1590
|
+
margin-bottom: 14px;
|
|
1591
|
+
font-size: 13px;
|
|
793
1592
|
font-weight: 600;
|
|
1593
|
+
letter-spacing: -0.005em;
|
|
794
1594
|
color: var(--bw-text);
|
|
795
1595
|
}
|
|
796
1596
|
|
|
797
1597
|
.bw-summary-rows {
|
|
798
|
-
gap:
|
|
1598
|
+
gap: 10px;
|
|
799
1599
|
}
|
|
800
1600
|
|
|
801
1601
|
.bw-summary-row {
|
|
@@ -804,6 +1604,8 @@
|
|
|
804
1604
|
justify-content: space-between;
|
|
805
1605
|
gap: 16px;
|
|
806
1606
|
font-size: 13px;
|
|
1607
|
+
line-height: 1.45;
|
|
1608
|
+
font-variant-numeric: tabular-nums;
|
|
807
1609
|
}
|
|
808
1610
|
|
|
809
1611
|
.bw-summary-row span:first-child {
|
|
@@ -848,6 +1650,63 @@
|
|
|
848
1650
|
line-height: 1.4;
|
|
849
1651
|
}
|
|
850
1652
|
|
|
1653
|
+
.bw-service-warning {
|
|
1654
|
+
display: flex;
|
|
1655
|
+
gap: 14px;
|
|
1656
|
+
padding: 18px;
|
|
1657
|
+
border: 2px solid #ef4444;
|
|
1658
|
+
border-radius: var(--bw-radius-lg);
|
|
1659
|
+
background: #fef2f2;
|
|
1660
|
+
color: #7f1d1d;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
.bw-service-warning-icon {
|
|
1664
|
+
display: flex;
|
|
1665
|
+
flex: 0 0 auto;
|
|
1666
|
+
align-items: center;
|
|
1667
|
+
justify-content: center;
|
|
1668
|
+
width: 42px;
|
|
1669
|
+
height: 42px;
|
|
1670
|
+
border-radius: 999px;
|
|
1671
|
+
background: #fee2e2;
|
|
1672
|
+
color: #b91c1c;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
.bw-service-warning-icon svg {
|
|
1676
|
+
width: 24px;
|
|
1677
|
+
height: 24px;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
.bw-service-warning-copy {
|
|
1681
|
+
display: flex;
|
|
1682
|
+
min-width: 0;
|
|
1683
|
+
flex-direction: column;
|
|
1684
|
+
gap: 6px;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
.bw-service-warning-title {
|
|
1688
|
+
font-size: 16px;
|
|
1689
|
+
font-weight: 800;
|
|
1690
|
+
line-height: 1.25;
|
|
1691
|
+
color: #7f1d1d;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
.bw-service-warning p {
|
|
1695
|
+
margin: 0;
|
|
1696
|
+
font-size: 13px;
|
|
1697
|
+
line-height: 1.5;
|
|
1698
|
+
color: #991b1b;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
.bw-service-warning-link {
|
|
1702
|
+
align-self: flex-start;
|
|
1703
|
+
font-size: 13px;
|
|
1704
|
+
font-weight: 800;
|
|
1705
|
+
color: #7f1d1d;
|
|
1706
|
+
text-decoration: underline;
|
|
1707
|
+
text-underline-offset: 3px;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
851
1710
|
.bw-confirm-btn,
|
|
852
1711
|
.bw-footer-next,
|
|
853
1712
|
.bw-btn-primary {
|
|
@@ -859,21 +1718,31 @@
|
|
|
859
1718
|
background: var(--bw-primary);
|
|
860
1719
|
color: var(--bw-primary-text);
|
|
861
1720
|
cursor: pointer;
|
|
862
|
-
transition: opacity
|
|
1721
|
+
transition: opacity var(--bw-duration) var(--bw-ease),
|
|
1722
|
+
transform var(--bw-duration) var(--bw-ease),
|
|
1723
|
+
box-shadow var(--bw-duration) var(--bw-ease);
|
|
863
1724
|
}
|
|
864
1725
|
|
|
865
1726
|
.bw-confirm-btn {
|
|
866
1727
|
width: 100%;
|
|
867
|
-
height:
|
|
868
|
-
border-radius: var(--bw-radius);
|
|
1728
|
+
height: 52px;
|
|
1729
|
+
border-radius: var(--bw-radius-pill);
|
|
869
1730
|
font-size: 15px;
|
|
870
1731
|
font-weight: 500;
|
|
1732
|
+
letter-spacing: -0.005em;
|
|
871
1733
|
}
|
|
872
1734
|
|
|
873
1735
|
.bw-confirm-btn:hover:not(:disabled),
|
|
874
1736
|
.bw-footer-next:hover:not(:disabled),
|
|
875
1737
|
.bw-btn-primary:hover {
|
|
876
|
-
opacity: 0.
|
|
1738
|
+
opacity: 0.92;
|
|
1739
|
+
box-shadow: var(--bw-shadow-lift);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
.bw-confirm-btn:active:not(:disabled),
|
|
1743
|
+
.bw-footer-next:active:not(:disabled),
|
|
1744
|
+
.bw-btn-primary:active {
|
|
1745
|
+
transform: scale(0.99);
|
|
877
1746
|
}
|
|
878
1747
|
|
|
879
1748
|
.bw-confirm-btn:disabled,
|
|
@@ -903,6 +1772,24 @@
|
|
|
903
1772
|
color: var(--bw-primary-text);
|
|
904
1773
|
}
|
|
905
1774
|
|
|
1775
|
+
/* Cancelled-state disc: neutral background instead of the celebratory
|
|
1776
|
+
primary fill. Tone is informational, not affirmative — we don't
|
|
1777
|
+
want to "celebrate" a cancellation with a brand-color check badge. */
|
|
1778
|
+
.bw-done-icon--muted {
|
|
1779
|
+
background: var(--bw-border-light);
|
|
1780
|
+
color: var(--bw-text-secondary);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/* Override the shared 16px sizing for icons rendered inside chrome
|
|
1784
|
+
(.bw-done-icon, .bw-staff-avatar, etc.) — the success view's check
|
|
1785
|
+
reads small inside the 56px disc, so bump the glyph to 24px so it
|
|
1786
|
+
anchors the moment cleanly. */
|
|
1787
|
+
.bw-done-icon svg {
|
|
1788
|
+
width: 24px;
|
|
1789
|
+
height: 24px;
|
|
1790
|
+
stroke-width: 2;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
906
1793
|
.bw-done-title {
|
|
907
1794
|
margin: 0;
|
|
908
1795
|
font-size: 28px;
|
|
@@ -927,8 +1814,52 @@
|
|
|
927
1814
|
}
|
|
928
1815
|
|
|
929
1816
|
@media (max-width: 1024px) {
|
|
1817
|
+
/* Lock mobile widget to a fixed 90vh height so the frame doesn't
|
|
1818
|
+
bounce between steps. Step 2 (calendar + slot list) is the
|
|
1819
|
+
tallest natural step and fits at 90vh on typical phones; step 1
|
|
1820
|
+
and step 3 fill the same height with the active col content at
|
|
1821
|
+
top + footer pinned at bottom. The body becomes the scroll
|
|
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. */
|
|
930
1825
|
.bw {
|
|
931
|
-
min-height:
|
|
1826
|
+
min-height: 90vh;
|
|
1827
|
+
max-height: 90vh;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
/* Lock header/footer in the column so they never shrink under
|
|
1831
|
+
content pressure. Flex children default to flex-shrink: 1, which
|
|
1832
|
+
would let a tall body squeeze the title/buttons. Force them to
|
|
1833
|
+
size to their content and stay put — the body in between is the
|
|
1834
|
+
only thing that scrolls. */
|
|
1835
|
+
.bw-mobile-header,
|
|
1836
|
+
.bw-header,
|
|
1837
|
+
.bw-footer {
|
|
1838
|
+
flex: 0 0 auto;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
.bw-body {
|
|
1842
|
+
overflow-y: auto;
|
|
1843
|
+
-webkit-overflow-scrolling: touch;
|
|
1844
|
+
overscroll-behavior: contain;
|
|
1845
|
+
/* Soft fade at both edges of the scroll area so content reads as
|
|
1846
|
+
scrollable past the header above and the footer below instead
|
|
1847
|
+
of cut off. Padding inside the body keeps the first/last rows
|
|
1848
|
+
from sitting right at the fade line. */
|
|
1849
|
+
-webkit-mask-image: linear-gradient(
|
|
1850
|
+
to bottom,
|
|
1851
|
+
transparent 0,
|
|
1852
|
+
#000 24px,
|
|
1853
|
+
#000 calc(100% - 24px),
|
|
1854
|
+
transparent 100%
|
|
1855
|
+
);
|
|
1856
|
+
mask-image: linear-gradient(
|
|
1857
|
+
to bottom,
|
|
1858
|
+
transparent 0,
|
|
1859
|
+
#000 24px,
|
|
1860
|
+
#000 calc(100% - 24px),
|
|
1861
|
+
transparent 100%
|
|
1862
|
+
);
|
|
932
1863
|
}
|
|
933
1864
|
|
|
934
1865
|
.bw-skel-desktop {
|
|
@@ -953,11 +1884,12 @@
|
|
|
953
1884
|
|
|
954
1885
|
.bw-header {
|
|
955
1886
|
padding: 20px 24px 20px;
|
|
1887
|
+
border-bottom: 1px solid var(--bw-border-light);
|
|
956
1888
|
}
|
|
957
1889
|
|
|
958
1890
|
.bw-header-row {
|
|
959
1891
|
display: flex;
|
|
960
|
-
align-items:
|
|
1892
|
+
align-items: flex-start;
|
|
961
1893
|
justify-content: space-between;
|
|
962
1894
|
gap: 16px;
|
|
963
1895
|
}
|
|
@@ -969,6 +1901,11 @@
|
|
|
969
1901
|
justify-content: flex-end;
|
|
970
1902
|
gap: 8px;
|
|
971
1903
|
width: 116px;
|
|
1904
|
+
/* Align dots with the title's first cap-height when the title
|
|
1905
|
+
wraps to multiple lines (e.g. "Reschedule Eric Lan's
|
|
1906
|
+
appointment"). Without this the dots would sit on the row's
|
|
1907
|
+
top edge above the title's optical line. */
|
|
1908
|
+
margin-top: 10px;
|
|
972
1909
|
}
|
|
973
1910
|
|
|
974
1911
|
.bw-dot {
|
|
@@ -1018,40 +1955,60 @@
|
|
|
1018
1955
|
display: none;
|
|
1019
1956
|
}
|
|
1020
1957
|
|
|
1958
|
+
/* Mobile 4-step flow:
|
|
1959
|
+
Step 1: services (bw-col--left, bw-step-2 only — provider hidden)
|
|
1960
|
+
Step 2: provider (bw-col--left, bw-step-1 only — services hidden)
|
|
1961
|
+
Step 3: calendar + slots (bw-col--center)
|
|
1962
|
+
Step 4: form (bw-col--right) */
|
|
1021
1963
|
.bw-body[data-mobile-step="1"] .bw-col--left,
|
|
1022
1964
|
.bw-body[data-mobile-step="2"] .bw-col--left,
|
|
1023
1965
|
.bw-body[data-mobile-step="3"] .bw-col--center,
|
|
1024
1966
|
.bw-body[data-mobile-step="4"] .bw-col--right {
|
|
1025
1967
|
display: flex;
|
|
1968
|
+
padding-top: 16px;
|
|
1969
|
+
padding-bottom: 20px;
|
|
1026
1970
|
}
|
|
1027
1971
|
|
|
1028
1972
|
.bw-body[data-mobile-step="4"] .bw-right-inner {
|
|
1029
1973
|
padding-bottom: 20px;
|
|
1030
1974
|
}
|
|
1031
1975
|
|
|
1976
|
+
/* Step 1: hide the desktop service-picker (inside bw-step-1) and
|
|
1977
|
+
hide the entire bw-step-1 wrapper. Show bw-step-2 (full-width
|
|
1978
|
+
services list). */
|
|
1032
1979
|
.bw-body[data-mobile-step="1"] .bw-step-1 {
|
|
1033
|
-
display: flex;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
.bw-body[data-mobile-step="1"] .bw-step-2 {
|
|
1037
1980
|
display: none;
|
|
1038
1981
|
}
|
|
1039
1982
|
|
|
1040
|
-
.bw-body[data-mobile-step="1"] .bw-
|
|
1983
|
+
.bw-body[data-mobile-step="1"] .bw-step-2 {
|
|
1984
|
+
display: flex;
|
|
1985
|
+
flex-direction: column;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
.bw-body[data-mobile-step="1"] .bw-step-2-heading,
|
|
1989
|
+
.bw-body[data-mobile-step="1"] .bw-step-2-divider {
|
|
1041
1990
|
display: none;
|
|
1042
1991
|
}
|
|
1043
1992
|
|
|
1993
|
+
/* Step 2: provider only. Show bw-step-1 (which renders the
|
|
1994
|
+
provider section conditional on selectedService — guaranteed
|
|
1995
|
+
true at step 2). Hide bw-step-2 services + the desktop
|
|
1996
|
+
service-picker stub inside bw-step-1. */
|
|
1044
1997
|
.bw-body[data-mobile-step="2"] .bw-step-1 {
|
|
1045
|
-
display:
|
|
1998
|
+
display: flex;
|
|
1999
|
+
flex: 0 0 auto;
|
|
1046
2000
|
}
|
|
1047
2001
|
|
|
1048
2002
|
.bw-body[data-mobile-step="2"] .bw-step-2 {
|
|
1049
|
-
display:
|
|
1050
|
-
flex-direction: column;
|
|
2003
|
+
display: none;
|
|
1051
2004
|
}
|
|
1052
2005
|
|
|
1053
|
-
.bw-body[data-mobile-step="2"] .bw-step-
|
|
1054
|
-
|
|
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 */
|
|
1055
2012
|
display: none;
|
|
1056
2013
|
}
|
|
1057
2014
|
|
|
@@ -1078,6 +2035,67 @@
|
|
|
1078
2035
|
margin: 4px 0;
|
|
1079
2036
|
}
|
|
1080
2037
|
|
|
2038
|
+
/* Mobile typography bumps. Per productdesign.md typography scale
|
|
2039
|
+
mobile column: list-row primary reads as body-s (13px) at the
|
|
2040
|
+
smallest, body-m (15px) for drawer-style titles, caption-l (12px)
|
|
2041
|
+
for list-row secondary. The widget is essentially a one-screen
|
|
2042
|
+
booking surface, so primary read targets get a one-step bump on
|
|
2043
|
+
mobile so they stay accessible at arm's length. */
|
|
2044
|
+
.bw-svc-name,
|
|
2045
|
+
.bw-svc-row--full .bw-svc-name,
|
|
2046
|
+
.bw-svc-price {
|
|
2047
|
+
font-size: 15px;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
.bw-svc-meta,
|
|
2051
|
+
.bw-svc-row--full .bw-svc-meta,
|
|
2052
|
+
.bw-svc-desc {
|
|
2053
|
+
font-size: 15px;
|
|
2054
|
+
line-height: 1.5;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
.bw-svc-category {
|
|
2058
|
+
font-size: 14px;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
.bw-staff-trigger-name,
|
|
2062
|
+
.bw-staff-option-name {
|
|
2063
|
+
font-size: 15px;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
.bw-staff-trigger-desc,
|
|
2067
|
+
.bw-staff-option-desc {
|
|
2068
|
+
font-size: 12px;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
.bw-staff-initials {
|
|
2072
|
+
font-size: 13px;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
/* Mobile thumbnails: bump both the inline (left col) and the
|
|
2076
|
+
full-width (mobile step 1) variants up one notch. The mobile
|
|
2077
|
+
full-width row has more horizontal breathing room and the
|
|
2078
|
+
thumbnail gets to anchor the row visually. */
|
|
2079
|
+
.bw-svc-image {
|
|
2080
|
+
width: 88px;
|
|
2081
|
+
height: 88px;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
.bw-svc-image--lg {
|
|
2085
|
+
width: 104px;
|
|
2086
|
+
height: 104px;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
.bw-svc-image-badge {
|
|
2090
|
+
width: 20px;
|
|
2091
|
+
height: 20px;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
.bw-svc-image-badge svg {
|
|
2095
|
+
width: 12px;
|
|
2096
|
+
height: 12px;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
1081
2099
|
.bw-form .bw-summary,
|
|
1082
2100
|
.bw-confirm-btn {
|
|
1083
2101
|
display: none;
|
|
@@ -1132,6 +2150,25 @@
|
|
|
1132
2150
|
font-size: 15px;
|
|
1133
2151
|
font-weight: 500;
|
|
1134
2152
|
}
|
|
2153
|
+
|
|
2154
|
+
.bw-footer-payment-slot {
|
|
2155
|
+
display: flex;
|
|
2156
|
+
flex: 1;
|
|
2157
|
+
min-width: 0;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
.bw-footer-payment-slot:empty {
|
|
2161
|
+
display: none;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
.bw-footer-payment-slot .bw-confirm-btn {
|
|
2165
|
+
display: flex;
|
|
2166
|
+
flex: 1;
|
|
2167
|
+
height: 48px;
|
|
2168
|
+
border-radius: var(--bw-radius-lg);
|
|
2169
|
+
font-size: 15px;
|
|
2170
|
+
font-weight: 500;
|
|
2171
|
+
}
|
|
1135
2172
|
}
|
|
1136
2173
|
|
|
1137
2174
|
@media (max-width: 480px) {
|
|
@@ -1144,7 +2181,7 @@
|
|
|
1144
2181
|
gap: 6px;
|
|
1145
2182
|
}
|
|
1146
2183
|
|
|
1147
|
-
.bw-body[data-mobile-step="
|
|
2184
|
+
.bw-body[data-mobile-step="3"] .bw-right-inner {
|
|
1148
2185
|
padding-bottom: 16px;
|
|
1149
2186
|
}
|
|
1150
2187
|
|
|
@@ -1193,3 +2230,548 @@
|
|
|
1193
2230
|
font-size: 12px;
|
|
1194
2231
|
}
|
|
1195
2232
|
}
|
|
2233
|
+
|
|
2234
|
+
/* ===========================================================================
|
|
2235
|
+
Desktop-only right-pane swap (slots → details) and sizing fixes.
|
|
2236
|
+
Mobile is intentionally untouched here — the pane wrappers exist in the
|
|
2237
|
+
DOM on mobile but receive no styling, so they collapse into transparent
|
|
2238
|
+
block containers and the form / cal-card / step-1 keep their original
|
|
2239
|
+
mobile flex behavior from commit 6ca438f.
|
|
2240
|
+
=========================================================================== */
|
|
2241
|
+
|
|
2242
|
+
/* Slot pill structural styling that's safe on both breakpoints because
|
|
2243
|
+
the .bw-slots-desktop wrapper is hidden on mobile (display: none below)
|
|
2244
|
+
and the .bw-slots-mobile wrapper is the only path that renders the
|
|
2245
|
+
inline 4-up grid on mobile via display: contents. */
|
|
2246
|
+
.bw-slots-mobile {
|
|
2247
|
+
display: none;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
.bw-slots-heading {
|
|
2251
|
+
display: flex;
|
|
2252
|
+
align-items: baseline;
|
|
2253
|
+
justify-content: space-between;
|
|
2254
|
+
margin-bottom: 14px;
|
|
2255
|
+
font-size: 14px;
|
|
2256
|
+
font-weight: 500;
|
|
2257
|
+
color: var(--bw-text);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
.bw-slots-count {
|
|
2261
|
+
font-size: 12px;
|
|
2262
|
+
color: var(--bw-text-muted);
|
|
2263
|
+
font-variant-numeric: tabular-nums;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
.bw-back-btn {
|
|
2267
|
+
display: inline-flex;
|
|
2268
|
+
align-items: center;
|
|
2269
|
+
gap: 6px;
|
|
2270
|
+
margin-bottom: 16px;
|
|
2271
|
+
padding: 6px 0;
|
|
2272
|
+
background: transparent;
|
|
2273
|
+
border: 0;
|
|
2274
|
+
font-size: 12px;
|
|
2275
|
+
color: var(--bw-text-muted);
|
|
2276
|
+
cursor: pointer;
|
|
2277
|
+
transition: color var(--bw-duration) var(--bw-ease);
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
.bw-back-btn:hover {
|
|
2281
|
+
color: var(--bw-text);
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
.bw-back-btn svg {
|
|
2285
|
+
width: 14px;
|
|
2286
|
+
height: 14px;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
@media (min-width: 1025px) {
|
|
2290
|
+
/* Desktop body grid: cols sit at the top of their cell instead of
|
|
2291
|
+
stretching to the row's max content height — the visual empty
|
|
2292
|
+
space below the cal-card and right-pane goes away. */
|
|
2293
|
+
.bw-body {
|
|
2294
|
+
align-items: start;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
/* Desktop cal-card hugs its grid + timezone instead of stretching
|
|
2298
|
+
to fill the row, and the timezone sits flush below the day grid
|
|
2299
|
+
(no auto margin pushing it down). */
|
|
2300
|
+
.bw-cal-card {
|
|
2301
|
+
flex: 0 0 auto;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
.bw-timezone {
|
|
2305
|
+
margin-top: 0;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
/* Slots column on desktop: vertical pill list above the form, not
|
|
2309
|
+
a 4-up grid below the calendar. */
|
|
2310
|
+
.bw-slots-desktop .bw-time-slots {
|
|
2311
|
+
grid-template-columns: 1fr;
|
|
2312
|
+
gap: 8px;
|
|
2313
|
+
margin-top: 0;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
.bw-slots-desktop .bw-slot {
|
|
2317
|
+
padding: 14px 16px;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
/* Match the skeleton pill dimensions to the real desktop pill so
|
|
2321
|
+
the load → loaded swap doesn't reflow the column. Real pill =
|
|
2322
|
+
14px padding + 13px font + line-height ≈ 50px tall. */
|
|
2323
|
+
.bw-slots-desktop .bw-skel--slot {
|
|
2324
|
+
height: 50px;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
/* --bw-cal-h is set on the .bw root via ResizeObserver in the
|
|
2328
|
+
widget — it tracks the live height of the .bw-cal-card in the
|
|
2329
|
+
middle column. Use it to cap the services list (left) and the
|
|
2330
|
+
slots list (right) so all three columns share the same visual
|
|
2331
|
+
height. Both internal scrollers fade their top + bottom edges
|
|
2332
|
+
so cut-off content reads as scrollable, not clipped. */
|
|
2333
|
+
.bw-svc-scroll-wrap {
|
|
2334
|
+
max-height: var(--bw-cal-h, 480px);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
.bw-pane--slots {
|
|
2338
|
+
max-height: var(--bw-cal-h, 480px);
|
|
2339
|
+
overflow: hidden;
|
|
2340
|
+
display: flex;
|
|
2341
|
+
flex-direction: column;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
.bw-pane--slots .bw-slots-desktop {
|
|
2345
|
+
flex: 1;
|
|
2346
|
+
min-height: 0;
|
|
2347
|
+
overflow-y: auto;
|
|
2348
|
+
-webkit-mask-image: linear-gradient(
|
|
2349
|
+
to bottom,
|
|
2350
|
+
transparent 0,
|
|
2351
|
+
#000 18px,
|
|
2352
|
+
#000 calc(100% - 18px),
|
|
2353
|
+
transparent 100%
|
|
2354
|
+
);
|
|
2355
|
+
mask-image: linear-gradient(
|
|
2356
|
+
to bottom,
|
|
2357
|
+
transparent 0,
|
|
2358
|
+
#000 18px,
|
|
2359
|
+
#000 calc(100% - 18px),
|
|
2360
|
+
transparent 100%
|
|
2361
|
+
);
|
|
2362
|
+
scrollbar-width: thin;
|
|
2363
|
+
scrollbar-color: var(--bw-border) transparent;
|
|
2364
|
+
/* Padding so first/last pills sit inside the fade zone, matching
|
|
2365
|
+
the services scroller pattern. */
|
|
2366
|
+
padding-block: 12px 16px;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
.bw-pane--slots .bw-slots-desktop::-webkit-scrollbar {
|
|
2370
|
+
width: 6px;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
.bw-pane--slots .bw-slots-desktop::-webkit-scrollbar-thumb {
|
|
2374
|
+
border-radius: var(--bw-radius-pill);
|
|
2375
|
+
background: var(--bw-border);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
.bw-pane--slots .bw-slots-desktop::-webkit-scrollbar-thumb:hover {
|
|
2379
|
+
background: var(--bw-text-muted);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
/* Right column two-step stage: slots pane → (crossfade) → details
|
|
2383
|
+
pane. Both panes share the stage area; the hidden one is taken
|
|
2384
|
+
out of normal flow so the column sizes only to the active pane.
|
|
2385
|
+
Animation timing matches Sable's productdesign popup rule:
|
|
2386
|
+
250ms expo ease-out on enter, 150ms standard ease-out on exit. */
|
|
2387
|
+
.bw-right-stage {
|
|
2388
|
+
position: relative;
|
|
2389
|
+
display: flex;
|
|
2390
|
+
flex-direction: column;
|
|
2391
|
+
min-height: 0;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
.bw-pane {
|
|
2395
|
+
transition:
|
|
2396
|
+
opacity 250ms cubic-bezier(0.16, 1, 0.3, 1),
|
|
2397
|
+
transform 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
2398
|
+
will-change: opacity, transform;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
.bw-pane.is-active {
|
|
2402
|
+
position: relative;
|
|
2403
|
+
opacity: 1;
|
|
2404
|
+
transform: scale(1);
|
|
2405
|
+
pointer-events: auto;
|
|
2406
|
+
z-index: 1;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
.bw-pane.is-hidden {
|
|
2410
|
+
position: absolute;
|
|
2411
|
+
inset: 0;
|
|
2412
|
+
opacity: 0;
|
|
2413
|
+
transform: scale(0.97);
|
|
2414
|
+
pointer-events: none;
|
|
2415
|
+
transition-duration: 150ms;
|
|
2416
|
+
transition-timing-function: ease-out;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
/* Mobile-only flips for the new pane wrappers: only enough to hide
|
|
2421
|
+
the desktop slots column copy and force the slots-mobile wrapper
|
|
2422
|
+
to render its children inline (the original 4-up grid below the
|
|
2423
|
+
calendar). The pane--details wrapper has no styling on mobile, so
|
|
2424
|
+
it's a transparent block container and the form renders exactly
|
|
2425
|
+
like it did pre-pane-restructure. */
|
|
2426
|
+
@media (max-width: 1024px) {
|
|
2427
|
+
.bw-pane--slots {
|
|
2428
|
+
display: none;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
.bw-back-btn {
|
|
2432
|
+
display: none;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
.bw-slots-mobile {
|
|
2436
|
+
display: contents;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
/* Details view is desktop-only; mobile uses the existing step-4
|
|
2440
|
+
right column for the form. */
|
|
2441
|
+
.bw-details-view {
|
|
2442
|
+
display: none;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
/* Mobile step transitions: when a step becomes active, the relevant
|
|
2446
|
+
content fades + slides up subtly. Same 250ms expo ease-out timing
|
|
2447
|
+
as desktop transitions so the product feels consistent across
|
|
2448
|
+
breakpoints. We target the step-specific content (not the col
|
|
2449
|
+
wrapper for steps 1/2 since col--left stays visible across
|
|
2450
|
+
them) so animations don't re-fire when the active col is shared
|
|
2451
|
+
between adjacent steps. */
|
|
2452
|
+
/* 4-step flow: steps 1+2 both share .bw-col--left, so we target
|
|
2453
|
+
the inner content (bw-step-2 for services, bw-step-1 for
|
|
2454
|
+
provider) so each step transitions independently. Steps 3+4
|
|
2455
|
+
have dedicated cols. */
|
|
2456
|
+
.bw-body[data-mobile-step='1'] .bw-step-2,
|
|
2457
|
+
.bw-body[data-mobile-step='2'] .bw-step-1,
|
|
2458
|
+
.bw-body[data-mobile-step='3'] .bw-col--center,
|
|
2459
|
+
.bw-body[data-mobile-step='4'] .bw-col--right {
|
|
2460
|
+
animation: bw-mobile-step-in 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
@keyframes bw-mobile-step-in {
|
|
2464
|
+
from {
|
|
2465
|
+
opacity: 0;
|
|
2466
|
+
transform: translateY(6px);
|
|
2467
|
+
}
|
|
2468
|
+
to {
|
|
2469
|
+
opacity: 1;
|
|
2470
|
+
transform: translateY(0);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
/* Desktop-only details view: 2-column summary | form layout shown
|
|
2476
|
+
when viewState === 'details'. Mobile keeps the step-based flow
|
|
2477
|
+
(bw-details-view is display: none on mobile, set above). */
|
|
2478
|
+
.bw-details-view {
|
|
2479
|
+
display: none;
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
/* The .bw-content wrapper holds bw-body and bw-details-view. On
|
|
2483
|
+
mobile it's a transparent flex column passthrough. On desktop it
|
|
2484
|
+
becomes a positioning context so the inactive view can overlay
|
|
2485
|
+
during the crossfade without contributing its height. */
|
|
2486
|
+
.bw-content {
|
|
2487
|
+
display: flex;
|
|
2488
|
+
flex-direction: column;
|
|
2489
|
+
flex: 1;
|
|
2490
|
+
min-height: 0;
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
@media (min-width: 1025px) {
|
|
2494
|
+
/* Stack the two panes in the same grid cell so they share space,
|
|
2495
|
+
but keep the inactive pane out of normal flow. Otherwise the
|
|
2496
|
+
services/calendar step reserves the much taller details form
|
|
2497
|
+
before the user has selected a time. */
|
|
2498
|
+
.bw-content {
|
|
2499
|
+
position: relative;
|
|
2500
|
+
display: grid;
|
|
2501
|
+
grid-template-areas: 'stack';
|
|
2502
|
+
align-items: start;
|
|
2503
|
+
flex: 0 1 auto;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
.bw-body,
|
|
2507
|
+
.bw-details-view {
|
|
2508
|
+
grid-area: stack;
|
|
2509
|
+
transition:
|
|
2510
|
+
opacity 250ms cubic-bezier(0.16, 1, 0.3, 1),
|
|
2511
|
+
transform 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
2512
|
+
will-change: opacity, transform;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
.bw-body {
|
|
2516
|
+
position: relative;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
.bw-details-view {
|
|
2520
|
+
position: absolute;
|
|
2521
|
+
inset: 0;
|
|
2522
|
+
display: grid;
|
|
2523
|
+
grid-template-columns: 5fr 6fr;
|
|
2524
|
+
gap: 56px;
|
|
2525
|
+
padding: 32px 48px 48px;
|
|
2526
|
+
max-width: 1440px;
|
|
2527
|
+
width: 100%;
|
|
2528
|
+
margin: 0 auto;
|
|
2529
|
+
/* Default hidden state — overlaid on top of body. */
|
|
2530
|
+
opacity: 0;
|
|
2531
|
+
transform: scale(0.99);
|
|
2532
|
+
pointer-events: none;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
.bw[data-view-state='details'] .bw-body {
|
|
2536
|
+
position: absolute;
|
|
2537
|
+
inset: 0;
|
|
2538
|
+
opacity: 0;
|
|
2539
|
+
transform: scale(0.99);
|
|
2540
|
+
pointer-events: none;
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
.bw[data-view-state='details'] .bw-details-view {
|
|
2544
|
+
position: relative;
|
|
2545
|
+
inset: auto;
|
|
2546
|
+
opacity: 1;
|
|
2547
|
+
transform: scale(1);
|
|
2548
|
+
pointer-events: auto;
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
.bw-details-summary {
|
|
2552
|
+
display: flex;
|
|
2553
|
+
flex-direction: column;
|
|
2554
|
+
gap: 16px;
|
|
2555
|
+
padding-right: 8px;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
.bw-details-eyebrow {
|
|
2559
|
+
margin: 0;
|
|
2560
|
+
font-size: 12px;
|
|
2561
|
+
font-weight: 500;
|
|
2562
|
+
letter-spacing: 0.04em;
|
|
2563
|
+
color: var(--bw-text-muted);
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
.bw-details-service {
|
|
2567
|
+
margin: 0;
|
|
2568
|
+
font-size: 24px;
|
|
2569
|
+
font-weight: 500;
|
|
2570
|
+
letter-spacing: -0.4px;
|
|
2571
|
+
line-height: 1.2;
|
|
2572
|
+
color: var(--bw-text);
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
.bw-details-desc {
|
|
2576
|
+
margin: 0;
|
|
2577
|
+
font-size: 13px;
|
|
2578
|
+
line-height: 1.55;
|
|
2579
|
+
color: var(--bw-text-secondary);
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
.bw-details-meta {
|
|
2583
|
+
display: flex;
|
|
2584
|
+
flex-direction: column;
|
|
2585
|
+
gap: 14px;
|
|
2586
|
+
margin-top: 8px;
|
|
2587
|
+
padding-top: 20px;
|
|
2588
|
+
border-top: 1px solid var(--bw-border-light);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
.bw-details-meta-row {
|
|
2592
|
+
display: flex;
|
|
2593
|
+
align-items: flex-start;
|
|
2594
|
+
gap: 12px;
|
|
2595
|
+
font-size: 13px;
|
|
2596
|
+
color: var(--bw-text);
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
.bw-details-meta-icon {
|
|
2600
|
+
display: inline-flex;
|
|
2601
|
+
flex-shrink: 0;
|
|
2602
|
+
align-items: center;
|
|
2603
|
+
justify-content: center;
|
|
2604
|
+
width: 18px;
|
|
2605
|
+
height: 18px;
|
|
2606
|
+
color: var(--bw-text-muted);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
.bw-details-meta-icon svg {
|
|
2610
|
+
width: 14px;
|
|
2611
|
+
height: 14px;
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
.bw-details-meta-text {
|
|
2615
|
+
display: flex;
|
|
2616
|
+
flex-direction: column;
|
|
2617
|
+
gap: 2px;
|
|
2618
|
+
min-width: 0;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
.bw-details-meta-label {
|
|
2622
|
+
font-size: 11px;
|
|
2623
|
+
color: var(--bw-text-muted);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
.bw-details-meta-value {
|
|
2627
|
+
font-size: 13px;
|
|
2628
|
+
color: var(--bw-text);
|
|
2629
|
+
font-variant-numeric: tabular-nums;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
.bw-details-meta-row--strike .bw-details-meta-value {
|
|
2633
|
+
text-decoration: line-through;
|
|
2634
|
+
opacity: 0.55;
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
.bw-details-hold {
|
|
2638
|
+
color: var(--bw-text);
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
.bw-details-form {
|
|
2642
|
+
display: flex;
|
|
2643
|
+
flex-direction: column;
|
|
2644
|
+
gap: 16px;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
/* Back link sits at the top of the summary pane, in the eyebrow
|
|
2648
|
+
position — replacing the previous static "Reschedule" / "Booking"
|
|
2649
|
+
label as the first thing the user reads on the left. The static
|
|
2650
|
+
eyebrow still renders just below as the section label for the
|
|
2651
|
+
summary stack. */
|
|
2652
|
+
.bw-details-summary .bw-back-btn--details {
|
|
2653
|
+
align-self: flex-start;
|
|
2654
|
+
margin-bottom: 16px;
|
|
2655
|
+
padding: 0;
|
|
2656
|
+
color: var(--bw-text-secondary);
|
|
2657
|
+
font-size: 14px;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
.bw-details-summary .bw-back-btn--details:hover {
|
|
2661
|
+
color: var(--bw-text);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
.bw-form--details {
|
|
2665
|
+
gap: 18px;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
/* Confirm sits alone at the bottom-right of the form column. */
|
|
2669
|
+
.bw-form--details .bw-confirm-btn {
|
|
2670
|
+
align-self: flex-end;
|
|
2671
|
+
margin-top: 8px;
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
.bw-intake {
|
|
2676
|
+
display: flex;
|
|
2677
|
+
flex-direction: column;
|
|
2678
|
+
gap: 14px;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
.bw-intake-section {
|
|
2682
|
+
display: flex;
|
|
2683
|
+
flex-direction: column;
|
|
2684
|
+
gap: 10px;
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
.bw-intake-title {
|
|
2688
|
+
font-size: 12px;
|
|
2689
|
+
font-weight: 700;
|
|
2690
|
+
color: var(--bw-text);
|
|
2691
|
+
text-transform: uppercase;
|
|
2692
|
+
letter-spacing: 0.08em;
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
.bw-field--wide {
|
|
2696
|
+
grid-column: 1 / -1;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
.bw .bw-field select {
|
|
2700
|
+
width: 100%;
|
|
2701
|
+
min-height: 40px;
|
|
2702
|
+
border: 1px solid var(--bw-border);
|
|
2703
|
+
border-radius: 10px;
|
|
2704
|
+
background: var(--bw-bg);
|
|
2705
|
+
color: var(--bw-text);
|
|
2706
|
+
padding: 0 12px;
|
|
2707
|
+
font: inherit;
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
.bw-help {
|
|
2711
|
+
margin: 4px 0 0;
|
|
2712
|
+
font-size: 12px;
|
|
2713
|
+
color: var(--bw-text-muted);
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
.bw-checkbox-field label {
|
|
2717
|
+
display: flex;
|
|
2718
|
+
align-items: flex-start;
|
|
2719
|
+
gap: 8px;
|
|
2720
|
+
font-size: 13px;
|
|
2721
|
+
line-height: 1.4;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
.bw-checkbox-field input {
|
|
2725
|
+
width: 16px;
|
|
2726
|
+
height: 16px;
|
|
2727
|
+
min-width: 16px;
|
|
2728
|
+
flex: 0 0 16px;
|
|
2729
|
+
margin-top: 1px;
|
|
2730
|
+
padding: 0;
|
|
2731
|
+
accent-color: var(--bw-primary);
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
.bw-repeatable {
|
|
2735
|
+
display: flex;
|
|
2736
|
+
flex-direction: column;
|
|
2737
|
+
gap: 10px;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
.bw-repeatable-item {
|
|
2741
|
+
display: flex;
|
|
2742
|
+
flex-direction: column;
|
|
2743
|
+
gap: 10px;
|
|
2744
|
+
border: 1px solid var(--bw-border);
|
|
2745
|
+
border-radius: 12px;
|
|
2746
|
+
padding: 12px;
|
|
2747
|
+
background: color-mix(in srgb, var(--bw-bg) 85%, transparent);
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
.bw-repeatable-head {
|
|
2751
|
+
display: flex;
|
|
2752
|
+
align-items: center;
|
|
2753
|
+
justify-content: space-between;
|
|
2754
|
+
gap: 10px;
|
|
2755
|
+
font-size: 13px;
|
|
2756
|
+
font-weight: 700;
|
|
2757
|
+
color: var(--bw-text);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
.bw-link-btn,
|
|
2761
|
+
.bw-secondary-btn {
|
|
2762
|
+
border: 0;
|
|
2763
|
+
background: transparent;
|
|
2764
|
+
color: var(--bw-primary);
|
|
2765
|
+
font: inherit;
|
|
2766
|
+
font-size: 13px;
|
|
2767
|
+
font-weight: 700;
|
|
2768
|
+
cursor: pointer;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
.bw-secondary-btn {
|
|
2772
|
+
align-self: flex-start;
|
|
2773
|
+
border: 1px solid var(--bw-border);
|
|
2774
|
+
border-radius: 999px;
|
|
2775
|
+
padding: 8px 12px;
|
|
2776
|
+
background: var(--bw-bg);
|
|
2777
|
+
}
|