srcdev-nuxt-components 9.3.7 → 9.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.claude/hooks/check-component-update.sh +30 -1
  2. package/.claude/skills/components/breadcrumb.md +49 -0
  3. package/.claude/skills/components/content-docs.md +1 -1
  4. package/.claude/skills/components/service-detail.md +181 -0
  5. package/.claude/skills/components/services-card-grid.md +9 -9
  6. package/.claude/skills/components/services-card.md +82 -30
  7. package/.claude/skills/index.md +3 -1
  8. package/.claude/skills/qa-panel.md +2 -2
  9. package/.vscode/srcdev-component-breadcrumb.code-snippets +14 -0
  10. package/.vscode/srcdev-component-service-detail.code-snippets +59 -0
  11. package/.vscode/srcdev-component-services-card.code-snippets +108 -0
  12. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +94 -0
  13. package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +32 -0
  14. package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +87 -0
  15. package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +87 -0
  16. package/app/components/01.atoms/navigation/breadcrumb/tests/__snapshots__/Breadcrumb.spec.ts.snap +13 -0
  17. package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +234 -0
  18. package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +536 -0
  19. package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +306 -0
  20. package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +314 -0
  21. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +109 -0
  22. package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +129 -0
  23. package/app/components/03.organisms/services/services-card/ServicesCard.vue +153 -39
  24. package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +65 -0
  25. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +82 -0
  26. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +13 -5
  27. package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +3 -7
  28. package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +126 -2
  29. package/app/components/03.organisms/services/services-grids/stories/ServicesSectionGrid.stories.ts +6 -0
  30. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +51 -27
  31. package/app/types/components/breadcrumb.ts +4 -0
  32. package/app/types/components/index.ts +1 -0
  33. package/package.json +2 -2
  34. package/app/pages/auto-grid.vue +0 -328
  35. package/app/pages/banner-video.vue +0 -238
  36. package/app/pages/forms/examples/buttons/index.vue +0 -259
  37. package/app/pages/forms/examples/material/checkbox-radio-panels.vue +0 -244
  38. package/app/pages/forms/examples/material/cssbattle.vue +0 -60
  39. package/app/pages/forms/examples/material/text-fields.vue +0 -910
  40. package/app/pages/grid-stack.vue +0 -252
  41. package/app/pages/index.vue +0 -127
  42. package/app/pages/page-hero-highlights.vue +0 -204
  43. package/app/pages/samaritan.vue +0 -327
  44. package/app/pages/typography/hero-text.vue +0 -97
  45. package/app/pages/typography/index.vue +0 -39
  46. package/app/pages/typography/page-body.vue +0 -171
  47. package/app/pages/typography/page-heading.vue +0 -76
  48. package/app/pages/typography/page-link.vue +0 -103
  49. package/app/pages/ui/accordian.vue +0 -211
  50. package/app/pages/ui/anchor-scroll-tab-navigation.vue +0 -191
  51. package/app/pages/ui/anchor-scroll.vue +0 -367
  52. package/app/pages/ui/animated-svg-text.vue +0 -85
  53. package/app/pages/ui/block-decorators.vue +0 -142
  54. package/app/pages/ui/card-core.vue +0 -82
  55. package/app/pages/ui/carousel-basic.vue +0 -292
  56. package/app/pages/ui/carousel-flip.vue +0 -449
  57. package/app/pages/ui/carousel-infinite.vue +0 -260
  58. package/app/pages/ui/clipped-panels.vue +0 -101
  59. package/app/pages/ui/contact-section.vue +0 -323
  60. package/app/pages/ui/container-glow.vue +0 -106
  61. package/app/pages/ui/content-width.vue +0 -126
  62. package/app/pages/ui/deep-expanding-menu.vue +0 -138
  63. package/app/pages/ui/display-avatar.vue +0 -253
  64. package/app/pages/ui/display-banner.vue +0 -78
  65. package/app/pages/ui/display-chip.vue +0 -367
  66. package/app/pages/ui/display-details.vue +0 -171
  67. package/app/pages/ui/display-dialog.vue +0 -344
  68. package/app/pages/ui/display-pill.vue +0 -396
  69. package/app/pages/ui/display-prompt.vue +0 -284
  70. package/app/pages/ui/display-toast.vue +0 -311
  71. package/app/pages/ui/expanding-panel.vue +0 -326
  72. package/app/pages/ui/glowing-border.vue +0 -111
  73. package/app/pages/ui/layout-content-docs.vue +0 -72
  74. package/app/pages/ui/layout-grid-a.vue +0 -96
  75. package/app/pages/ui/layout-grid-b.vue +0 -132
  76. package/app/pages/ui/magnetic-navigation.vue +0 -50
  77. package/app/pages/ui/marquee-scroller.vue +0 -99
  78. package/app/pages/ui/mask-element.vue +0 -104
  79. package/app/pages/ui/masonry-columns.vue +0 -55
  80. package/app/pages/ui/masonry-grid-ordered-experiment.vue +0 -79
  81. package/app/pages/ui/masonry-grid-ordered.vue +0 -56
  82. package/app/pages/ui/masonry-grid-sorted.vue +0 -55
  83. package/app/pages/ui/masonry-grid.vue +0 -55
  84. package/app/pages/ui/navigation/navigation-horizontal.vue +0 -484
  85. package/app/pages/ui/navigation/site-navigation/about.vue +0 -36
  86. package/app/pages/ui/navigation/site-navigation/contact.vue +0 -39
  87. package/app/pages/ui/navigation/site-navigation/index.vue +0 -38
  88. package/app/pages/ui/navigation/site-navigation/portfolio.vue +0 -43
  89. package/app/pages/ui/navigation/site-navigation/services.vue +0 -42
  90. package/app/pages/ui/navigation/tab-navigation.vue +0 -493
  91. package/app/pages/ui/page-row.vue +0 -127
  92. package/app/pages/ui/price-list.vue +0 -65
  93. package/app/pages/ui/profile-section.vue +0 -142
  94. package/app/pages/ui/qr-code/[componentName].vue +0 -218
  95. package/app/pages/ui/rotating-carousel.vue +0 -154
  96. package/app/pages/ui/scroll-reveal-image.vue +0 -483
  97. package/app/pages/ui/section-parallax.vue +0 -54
  98. package/app/pages/ui/services/colour-finder.vue +0 -43
  99. package/app/pages/ui/services/services-cards.vue +0 -57
  100. package/app/pages/ui/services/services-section/[slug].vue +0 -61
  101. package/app/pages/ui/services/services-sections-compact.vue +0 -64
  102. package/app/pages/ui/settings.vue +0 -54
  103. package/app/pages/ui/simple-grid.vue +0 -55
  104. package/app/pages/ui/slider-gallery.vue +0 -261
  105. package/app/pages/ui/tabs-y.vue +0 -107
  106. package/app/pages/ui/tabs.vue +0 -189
  107. package/app/pages/ui/tooltips.vue +0 -104
  108. package/app/pages/ui/wipe-away-vertical.vue +0 -129
@@ -0,0 +1,536 @@
1
+ <template>
2
+ <component :is="tag" class="service-detail" :class="[elementClasses]" :aria-labelledby="ariaLabelledby">
3
+ <GridStack tag="div" class="service-detail__hero">
4
+ <template #image>
5
+ <PageRow tag="div" :variant="heroImageVariant" class="service-detail__hero-image-row">
6
+ <NuxtImg
7
+ :src="serviceData.image"
8
+ :alt="serviceData.title"
9
+ :loading="imageLoading"
10
+ :fetchpriority="imageFetchPriority"
11
+ class="service-detail__hero-image"
12
+ />
13
+ </PageRow>
14
+ </template>
15
+ <template #content>
16
+ <PageRow tag="div" :variant="heroContentVariant" class="service-detail__hero-overlay">
17
+ <div class="service-detail__hero-content">
18
+ <Breadcrumb :items="resolvedBreadcrumbItems" class="service-detail__breadcrumb" />
19
+ <EyebrowText font-size="large" :text-content="serviceData.subtitle" />
20
+ <HeroText
21
+ :id="headingId"
22
+ :tag="headerTag"
23
+ font-size="display"
24
+ :text-content="[{ text: serviceData.title, styleClass: 'normal' }]"
25
+ :style-class-passthrough="['mb-20']"
26
+ />
27
+ <div class="service-detail__hero-pills">
28
+ <DisplayPill :label="serviceData.duration" size="md" variant="neutral" />
29
+ <DisplayPill :label="`From ${serviceData.price}`" size="md" variant="neutral" />
30
+ </div>
31
+ </div>
32
+ </PageRow>
33
+ </template>
34
+ </GridStack>
35
+
36
+ <PageRow tag="div" :variant="bodyVariant" class="service-detail__body-row">
37
+ <div class="service-detail__body">
38
+ <div class="service-detail__main">
39
+ <p class="page-body-normal">{{ serviceData.longDescription }}</p>
40
+
41
+ <HeroText
42
+ :tag="subheadingTag"
43
+ axis="horizontal"
44
+ font-size="subheading"
45
+ :text-content="serviceData.heroHeading"
46
+ :style-class-passthrough="['mb-20']"
47
+ />
48
+ <p class="page-body-normal">{{ serviceData.whatIsIt }}</p>
49
+
50
+ <HeroText
51
+ :tag="subheadingTag"
52
+ axis="horizontal"
53
+ font-size="subheading"
54
+ :text-content="[{ text: processHeading, styleClass: 'normal' }]"
55
+ :style-class-passthrough="['mb-20']"
56
+ />
57
+ <StepperList
58
+ tag="ol"
59
+ indicator-alignment="top"
60
+ indicator-variant="circle"
61
+ :connected="true"
62
+ :item-count="serviceData.process.length"
63
+ class="service-detail__process"
64
+ >
65
+ <template v-for="(step, i) in serviceData.process" :key="i" #[`item-${i}`]>
66
+ <p class="page-body-normal">{{ step }}</p>
67
+ </template>
68
+ </StepperList>
69
+
70
+ <HeroText
71
+ :tag="subheadingTag"
72
+ axis="horizontal"
73
+ font-size="subheading"
74
+ :text-content="[{ text: idealForHeading, styleClass: 'normal' }]"
75
+ :style-class-passthrough="['mb-20']"
76
+ />
77
+ <ul class="service-detail__ideal-for">
78
+ <li v-for="(item, i) in serviceData.idealFor" :key="i" class="service-detail__ideal-for-item">
79
+ <Icon name="mdi:diamond-stone" class="service-detail__ideal-for-icon" />
80
+ <p class="page-body-normal">{{ item }}</p>
81
+ </li>
82
+ </ul>
83
+
84
+ <HeroText
85
+ :tag="subheadingTag"
86
+ axis="horizontal"
87
+ font-size="subheading"
88
+ :text-content="[{ text: maintenanceHeading, styleClass: 'normal' }]"
89
+ :style-class-passthrough="['mb-20']"
90
+ />
91
+ <p class="page-body-normal">{{ serviceData.maintenance }}</p>
92
+
93
+ <HeroText
94
+ :tag="subheadingTag"
95
+ axis="horizontal"
96
+ font-size="subheading"
97
+ :text-content="[{ text: faqsHeading, styleClass: 'normal' }]"
98
+ :style-class-passthrough="['mb-20']"
99
+ />
100
+ <div class="service-detail__faqs">
101
+ <div v-for="(faq, i) in serviceData.faqs" :key="i" class="service-detail__faq">
102
+ <h3 class="service-detail__faq-question">{{ faq.question }}</h3>
103
+ <p class="page-body-normal">{{ faq.answer }}</p>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <aside class="service-detail__sidebar">
109
+ <GlassPanel :style-class-passthrough="['service-detail__booking-card']">
110
+ <p class="service-detail__sidebar-label">{{ bookingHeading }}</p>
111
+
112
+ <div class="service-detail__sidebar-row">
113
+ <span>{{ priceLabel }}</span>
114
+ <span>{{ serviceData.price }}</span>
115
+ </div>
116
+ <div class="service-detail__sidebar-row">
117
+ <span>{{ durationLabel }}</span>
118
+ <span>{{ serviceData.duration }}</span>
119
+ </div>
120
+ <div v-if="location" class="service-detail__sidebar-row">
121
+ <span>{{ locationLabel }}</span>
122
+ <span>{{ location }}</span>
123
+ </div>
124
+
125
+ <div v-if="$slots['book-cta']" class="service-detail__sidebar-row book-cta">
126
+ <slot name="book-cta" :service-data="serviceData"></slot>
127
+ </div>
128
+
129
+ <div class="service-detail__sidebar-note">
130
+ <slot name="sidebar-note"></slot>
131
+ </div>
132
+ </GlassPanel>
133
+
134
+ <div v-if="relatedServices.length" class="service-detail__related">
135
+ <p class="service-detail__sidebar-label">{{ relatedServicesHeading }}</p>
136
+
137
+ <div class="service-detail__related-items">
138
+ <div
139
+ v-for="(related, i) in relatedServices"
140
+ :key="related.slug"
141
+ class="service-detail__related-item-slot"
142
+ >
143
+ <slot name="related-service" :service="related" :index="i">
144
+ <div class="service-detail__related-item">
145
+ <NuxtImg
146
+ :src="related.image"
147
+ :alt="related.title"
148
+ loading="lazy"
149
+ class="service-detail__related-image"
150
+ />
151
+ <div class="service-detail__related-details">
152
+ <p class="service-detail__related-title">{{ related.title }}</p>
153
+ <p class="service-detail__related-price">{{ related.price }}</p>
154
+ </div>
155
+ </div>
156
+ </slot>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </aside>
161
+ </div>
162
+ </PageRow>
163
+
164
+ <PageRow tag="div" :variant="finalCtaVariant" class="service-detail__final-cta-row">
165
+ <div class="service-detail__final-cta">
166
+ <div class="service-detail__final-cta-copy">
167
+ <HeroText
168
+ :tag="subheadingTag"
169
+ axis="horizontal"
170
+ font-size="subheading"
171
+ :text-content="[{ text: finalCtaHeading, styleClass: 'normal' }]"
172
+ :style-class-passthrough="['mbs-0', 'mbe-8']"
173
+ />
174
+ <p class="page-body-normal">{{ finalCtaBody }}</p>
175
+ </div>
176
+ <slot name="final-cta" :service-data="serviceData"></slot>
177
+ </div>
178
+ </PageRow>
179
+ </component>
180
+ </template>
181
+
182
+ <script setup lang="ts">
183
+ import type { Service } from "~/types/types.services";
184
+ import type { BreadcrumbItem } from "~/types/components/breadcrumb";
185
+
186
+ interface Props {
187
+ tag?: "div" | "section" | "article" | "main";
188
+ headerTag?: "h1" | "h2" | "h3";
189
+ subheadingTag?: "h2" | "h3";
190
+ serviceData: Service;
191
+ breadcrumbItems?: BreadcrumbItem[];
192
+ heroImageVariant?: "full" | "popout" | "content" | "inset-content";
193
+ heroContentVariant?: "full" | "popout" | "content" | "inset-content";
194
+ bodyVariant?: "full" | "popout" | "content" | "inset-content";
195
+ finalCtaVariant?: "full" | "popout" | "content" | "inset-content";
196
+ processHeading?: string;
197
+ idealForHeading?: string;
198
+ maintenanceHeading?: string;
199
+ faqsHeading?: string;
200
+ bookingHeading?: string;
201
+ priceLabel?: string;
202
+ durationLabel?: string;
203
+ locationLabel?: string;
204
+ location?: string;
205
+ relatedServicesHeading?: string;
206
+ relatedServices?: Service[];
207
+ finalCtaHeading?: string;
208
+ finalCtaBody?: string;
209
+ imageLoading?: "eager" | "lazy";
210
+ imageFetchPriority?: "high" | "auto" | "low";
211
+ styleClassPassthrough?: string | string[];
212
+ }
213
+
214
+ const props = withDefaults(defineProps<Props>(), {
215
+ tag: "div",
216
+ headerTag: "h1",
217
+ subheadingTag: "h2",
218
+ breadcrumbItems: undefined,
219
+ heroImageVariant: "full",
220
+ heroContentVariant: "content",
221
+ bodyVariant: "content",
222
+ finalCtaVariant: "content",
223
+ processHeading: "The Process",
224
+ idealForHeading: "Ideal For",
225
+ maintenanceHeading: "Aftercare & Maintenance",
226
+ faqsHeading: "Frequently Asked Questions",
227
+ bookingHeading: "Book This Service",
228
+ priceLabel: "Price",
229
+ durationLabel: "Duration",
230
+ locationLabel: "Location",
231
+ location: undefined,
232
+ relatedServicesHeading: "You May Also Like",
233
+ relatedServices: () => [],
234
+ finalCtaHeading: "Ready to book your appointment?",
235
+ finalCtaBody: "Get in touch to book your appointment.",
236
+ imageLoading: "eager",
237
+ imageFetchPriority: "high",
238
+ styleClassPassthrough: () => [],
239
+ });
240
+
241
+ const { headingId, ariaLabelledby } = useAriaLabelledById(() => props.tag);
242
+
243
+ // Falls back to a plain, non-linked breadcrumb built from the service's own category/title
244
+ // when the consumer doesn't pass real routes — routing is always the consumer's decision.
245
+ const resolvedBreadcrumbItems = computed<BreadcrumbItem[]>(
246
+ () => props.breadcrumbItems ?? [{ label: props.serviceData.category }, { label: props.serviceData.title }]
247
+ );
248
+
249
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
250
+
251
+ watch(
252
+ () => props.styleClassPassthrough,
253
+ () => {
254
+ resetElementClasses(props.styleClassPassthrough);
255
+ }
256
+ );
257
+ </script>
258
+
259
+ <style lang="css">
260
+ @layer components {
261
+ .service-detail {
262
+ container-type: inline-size;
263
+ container-name: service-detail;
264
+
265
+ .service-detail__hero {
266
+ border-radius: var(--service-detail-hero-border-radius, 0.8rem);
267
+ overflow: hidden;
268
+ height: var(--service-detail-hero-min-height-mobile, 32rem);
269
+
270
+ @container (width >= 768px) {
271
+ height: var(--service-detail-hero-min-height-tablet, 36rem);
272
+ }
273
+
274
+ @container (width >= 1024px) {
275
+ height: var(--service-detail-hero-min-height-desktop, 42rem);
276
+ }
277
+
278
+ .grid-stack__layer {
279
+ height: 100%;
280
+ overflow: hidden;
281
+
282
+ .service-detail__hero-image-row,
283
+ .service-detail__hero-overlay {
284
+ height: 100%;
285
+ }
286
+ }
287
+
288
+ .service-detail__hero-image {
289
+ display: block;
290
+ width: 100%;
291
+ height: 100%;
292
+ object-fit: cover;
293
+ object-position: var(--service-detail-hero-image-position-small, bottom);
294
+
295
+ @container (width >= 768px) {
296
+ object-fit: contain;
297
+ object-position: var(--service-detail-hero-image-position-medium, center);
298
+ }
299
+ }
300
+
301
+ .service-detail__hero-overlay {
302
+ display: grid;
303
+ height: 100%;
304
+ align-content: var(--service-detail-hero-content-align, end);
305
+ padding-block: var(--service-detail-hero-padding, 3.2rem);
306
+ background: var(
307
+ --service-detail-hero-scrim,
308
+ linear-gradient(0deg, rgb(0 0 0 / 70%) 0%, rgb(0 0 0 / 10%) 60%, transparent 100%)
309
+ );
310
+
311
+ .service-detail__hero-content {
312
+ .service-detail__breadcrumb {
313
+ --breadcrumb-colour: var(--service-detail-hero-text-colour, white);
314
+ margin-block-end: var(--service-detail-breadcrumb-margin-block-end, 0.8rem);
315
+ }
316
+
317
+ .eyebrow-text,
318
+ .hero-text {
319
+ color: var(--service-detail-hero-text-colour, white);
320
+ }
321
+
322
+ .service-detail__hero-pills {
323
+ display: flex;
324
+ flex-wrap: wrap;
325
+ gap: var(--service-detail-hero-pills-gap, 0.8rem);
326
+
327
+ --theme-pill-bg: var(--service-detail-hero-pill-bg, transparent);
328
+ --theme-pill-color: var(--service-detail-hero-text-colour, white);
329
+ --theme-pill-border-color: var(--service-detail-hero-pill-border-colour, currentColor);
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ .service-detail__body {
336
+ display: grid;
337
+ grid-template-columns: 1fr;
338
+ gap: var(--service-detail-body-gap, 3rem);
339
+ margin-block-start: var(--service-detail-body-margin-block-start, 3rem);
340
+
341
+ @container (width >= 900px) {
342
+ grid-template-columns: minmax(0, 1fr) minmax(28rem, 34rem);
343
+ align-items: start;
344
+ }
345
+
346
+ .service-detail__main {
347
+ min-inline-size: 0;
348
+ display: flex;
349
+ flex-direction: column;
350
+ gap: var(--service-detail-main-section-gap, 2.4rem);
351
+ }
352
+
353
+ .service-detail__process {
354
+ --stepper-list-gap: var(--service-detail-process-step-gap, 1.6rem);
355
+ --stepper-list-padding-block: var(--service-detail-process-step-padding-block, 1.6rem);
356
+ --stepper-list-connector-color: var(--service-detail-process-divider-colour, currentColor);
357
+ --stepper-list-counter-circle-text: var(--service-detail-process-index-colour, var(--colour-text-accent));
358
+ --stepper-list-counter-circle-border: var(--service-detail-process-index-colour, var(--colour-text-accent));
359
+ --stepper-list-counter-font-size: var(--service-detail-process-index-font-size, 1.4rem);
360
+ }
361
+
362
+ .service-detail__ideal-for {
363
+ list-style: none;
364
+ margin: 0;
365
+ padding: 0;
366
+ display: grid;
367
+ grid-template-columns: 1fr;
368
+ gap: var(--service-detail-ideal-for-gap, 1.2rem);
369
+
370
+ @container (width >= 500px) {
371
+ grid-template-columns: 1fr 1fr;
372
+ }
373
+
374
+ .service-detail__ideal-for-item {
375
+ display: flex;
376
+ align-items: flex-start;
377
+ gap: var(--service-detail-ideal-for-item-gap, 1rem);
378
+ padding: var(--service-detail-ideal-for-item-padding, 1.6rem);
379
+ background-color: var(--service-detail-ideal-for-item-background, var(--slate-09));
380
+ border: 1px solid var(--service-detail-ideal-for-item-border-colour, var(--slate-06));
381
+ border-radius: var(--service-detail-ideal-for-item-border-radius, 0.4rem);
382
+
383
+ .service-detail__ideal-for-icon {
384
+ flex-shrink: 0;
385
+ width: var(--service-detail-ideal-for-icon-size, 1.6rem);
386
+ height: var(--service-detail-ideal-for-icon-size, 1.6rem);
387
+ color: var(--service-detail-ideal-for-icon-colour, var(--colour-text-accent));
388
+ transform: var(--service-detail-ideal-for-icon-transform, translateY(0.4rem));
389
+ }
390
+
391
+ p {
392
+ margin: 0;
393
+ }
394
+ }
395
+ }
396
+
397
+ .service-detail__faqs {
398
+ display: flex;
399
+ flex-direction: column;
400
+
401
+ .service-detail__faq {
402
+ padding-block: var(--service-detail-faq-padding-block, 1.6rem);
403
+ border-block-end: 1px solid var(--service-detail-faq-divider-colour, currentColor);
404
+
405
+ &:first-child {
406
+ padding-block-start: 0;
407
+ }
408
+ &:last-child {
409
+ border-block-end: none;
410
+ }
411
+
412
+ .service-detail__faq-question {
413
+ margin: 0 0 var(--service-detail-faq-question-margin-block-end, 0.8rem);
414
+ font-size: var(--service-detail-faq-question-font-size, 1.6rem);
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ .service-detail__sidebar {
421
+ display: flex;
422
+ flex-direction: column;
423
+ gap: var(--service-detail-sidebar-gap, 2rem);
424
+ position: sticky;
425
+ top: var(--service-detail-sidebar-sticky-offset, 2rem);
426
+
427
+ .service-detail__sidebar-label {
428
+ margin: 0 0 var(--service-detail-sidebar-label-margin-block-end, 1.6rem);
429
+ font-size: var(--service-detail-sidebar-label-font-size, 1.2rem);
430
+ text-transform: uppercase;
431
+ letter-spacing: var(--service-detail-sidebar-label-letter-spacing, 0.05em);
432
+ opacity: var(--service-detail-muted-opacity, 0.7);
433
+ }
434
+
435
+ .service-detail__booking-card {
436
+ padding: var(--service-detail-booking-card-padding, 2.4rem);
437
+ }
438
+
439
+ .service-detail__sidebar-row {
440
+ display: flex;
441
+ justify-content: space-between;
442
+ gap: var(--service-detail-sidebar-row-gap, 1rem);
443
+ padding-block: var(--service-detail-sidebar-row-padding-block, 1rem);
444
+ border-block-start: 1px solid var(--service-detail-sidebar-row-divider-colour, currentColor);
445
+
446
+ &.book-cta {
447
+ padding-block: var(--service-detail-sidebar-row-book-cta-padding-block, 2rem);
448
+ border-block-start: none;
449
+ justify-content: var(--service-detail-sidebar-row-book-cta-justify-content, end);
450
+ }
451
+
452
+ span:first-child {
453
+ text-transform: uppercase;
454
+ font-size: var(--service-detail-sidebar-row-label-font-size, 1.2rem);
455
+ opacity: var(--service-detail-muted-opacity, 0.7);
456
+ }
457
+ }
458
+
459
+ .service-detail__sidebar-note {
460
+ margin-block-start: var(--service-detail-sidebar-note-margin-block-start, 1.2rem);
461
+ font-size: var(--service-detail-sidebar-note-font-size, 1.2rem);
462
+ opacity: var(--service-detail-muted-opacity, 0.7);
463
+
464
+ &:empty {
465
+ display: none;
466
+ }
467
+ }
468
+
469
+ .service-detail__related {
470
+ .service-detail__related-items {
471
+ display: grid;
472
+ gap: var(--service-detail-related-items-gap, 1rem);
473
+
474
+ .service-detail__related-item-slot {
475
+ background-color: var(--service-detail-related-item-background, var(--slate-09));
476
+ padding: var(--service-detail-related-item-padding, 1rem);
477
+ border: 1px solid var(--service-detail-related-item-border-colour, var(--slate-06));
478
+ border-radius: var(--service-detail-related-item-border-radius, 0.4rem);
479
+
480
+ .service-detail__related-item {
481
+ display: grid;
482
+ grid-template-columns: auto 1fr;
483
+ gap: var(--service-detail-related-item-gap, 1.2rem);
484
+ align-items: center;
485
+
486
+ .service-detail__related-image {
487
+ width: var(--service-detail-related-image-size, 5.6rem);
488
+ height: var(--service-detail-related-image-size, 5.6rem);
489
+ border-radius: var(--service-detail-related-image-border-radius, 0.4rem);
490
+ object-fit: cover;
491
+ display: block;
492
+ }
493
+
494
+ .service-detail__related-title,
495
+ .service-detail__related-price {
496
+ margin: 0;
497
+ }
498
+
499
+ .service-detail__related-price {
500
+ margin-block-start: var(--service-detail-related-price-margin-block-start, 0.6rem);
501
+ font-size: var(--service-detail-related-price-font-size, 1.2rem);
502
+ opacity: var(--service-detail-muted-opacity, 0.7);
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }
509
+
510
+ .service-detail__final-cta-row {
511
+ .service-detail__final-cta {
512
+ display: flex;
513
+ flex-direction: column;
514
+ gap: var(--service-detail-final-cta-gap, 1.6rem);
515
+ align-items: flex-start;
516
+ justify-content: space-between;
517
+ margin-block-start: var(--service-detail-final-cta-margin-block-start, 3.2rem);
518
+ padding-block: var(--service-detail-final-cta-padding-block, 3.2rem 3.2rem);
519
+ border-block-start: 1px solid var(--service-detail-final-cta-divider-colour, currentColor);
520
+
521
+ @container (width >= 700px) {
522
+ flex-direction: row;
523
+ align-items: center;
524
+ }
525
+
526
+ .service-detail__final-cta-copy {
527
+ p {
528
+ margin: 0;
529
+ opacity: var(--service-detail-muted-opacity, 0.7);
530
+ }
531
+ }
532
+ }
533
+ }
534
+ }
535
+ }
536
+ </style>