@brandlab-digital/site-next 3.14.0 → 3.15.1

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 (148) hide show
  1. package/dist/analytics.d.ts +1 -1
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/analytics.js +2 -0
  4. package/dist/analytics.js.map +1 -1
  5. package/dist/image-runtime.js +6 -1
  6. package/dist/image-runtime.js.map +1 -1
  7. package/dist/navigation/constants.d.ts +11 -2
  8. package/dist/navigation/constants.d.ts.map +1 -1
  9. package/dist/navigation/constants.js +11 -2
  10. package/dist/navigation/constants.js.map +1 -1
  11. package/dist/navigation/index.d.ts +1 -1
  12. package/dist/navigation/index.d.ts.map +1 -1
  13. package/dist/navigation/index.js +1 -1
  14. package/dist/navigation/index.js.map +1 -1
  15. package/dist/navigation/use-back-to-top.d.ts.map +1 -1
  16. package/dist/navigation/use-back-to-top.js +12 -4
  17. package/dist/navigation/use-back-to-top.js.map +1 -1
  18. package/dist/navigation/use-header-metrics.d.ts +1 -1
  19. package/dist/navigation/use-header-metrics.js +1 -1
  20. package/dist/navigation/use-header-metrics.js.map +1 -1
  21. package/dist/navigation/use-sticky-call-bar.d.ts +2 -1
  22. package/dist/navigation/use-sticky-call-bar.d.ts.map +1 -1
  23. package/dist/navigation/use-sticky-call-bar.js +11 -2
  24. package/dist/navigation/use-sticky-call-bar.js.map +1 -1
  25. package/dist/next-sharp.d.ts +14 -9
  26. package/dist/next-sharp.d.ts.map +1 -1
  27. package/dist/next-sharp.js +26 -25
  28. package/dist/next-sharp.js.map +1 -1
  29. package/dist/ui-kit/brandlab-skin.d.ts +26 -0
  30. package/dist/ui-kit/brandlab-skin.d.ts.map +1 -1
  31. package/dist/ui-kit/brandlab-skin.js.map +1 -1
  32. package/dist/ui-kit/prefetch.d.ts +19 -10
  33. package/dist/ui-kit/prefetch.d.ts.map +1 -1
  34. package/dist/ui-kit/prefetch.js +23 -10
  35. package/dist/ui-kit/prefetch.js.map +1 -1
  36. package/dist/ui-kit/primitives/index.d.ts +1 -1
  37. package/dist/ui-kit/primitives/index.d.ts.map +1 -1
  38. package/dist/ui-kit/primitives/index.js.map +1 -1
  39. package/dist/ui-kit/primitives/links.js +1 -1
  40. package/dist/ui-kit/primitives/links.js.map +1 -1
  41. package/dist/ui-kit/primitives/lists.d.ts +9 -1
  42. package/dist/ui-kit/primitives/lists.d.ts.map +1 -1
  43. package/dist/ui-kit/primitives/lists.js +1 -1
  44. package/dist/ui-kit/primitives/lists.js.map +1 -1
  45. package/dist/ui-kit/primitives/quote-button.d.ts +25 -2
  46. package/dist/ui-kit/primitives/quote-button.d.ts.map +1 -1
  47. package/dist/ui-kit/primitives/quote-button.js +25 -3
  48. package/dist/ui-kit/primitives/quote-button.js.map +1 -1
  49. package/dist/ui-kit/repeated-slots.d.ts +32 -0
  50. package/dist/ui-kit/repeated-slots.d.ts.map +1 -1
  51. package/dist/ui-kit/repeated-slots.js +16 -0
  52. package/dist/ui-kit/repeated-slots.js.map +1 -1
  53. package/dist/ui-kit/sections/area-map.d.ts +64 -0
  54. package/dist/ui-kit/sections/area-map.d.ts.map +1 -0
  55. package/dist/ui-kit/sections/area-map.js +142 -0
  56. package/dist/ui-kit/sections/area-map.js.map +1 -0
  57. package/dist/ui-kit/sections/before-after.d.ts +59 -0
  58. package/dist/ui-kit/sections/before-after.d.ts.map +1 -0
  59. package/dist/ui-kit/sections/before-after.js +42 -0
  60. package/dist/ui-kit/sections/before-after.js.map +1 -0
  61. package/dist/ui-kit/sections/council-rules.d.ts +64 -0
  62. package/dist/ui-kit/sections/council-rules.d.ts.map +1 -0
  63. package/dist/ui-kit/sections/council-rules.js +46 -0
  64. package/dist/ui-kit/sections/council-rules.js.map +1 -0
  65. package/dist/ui-kit/sections/coverage.d.ts +53 -0
  66. package/dist/ui-kit/sections/coverage.d.ts.map +1 -0
  67. package/dist/ui-kit/sections/coverage.js +57 -0
  68. package/dist/ui-kit/sections/coverage.js.map +1 -0
  69. package/dist/ui-kit/sections/emergency-band.d.ts +60 -0
  70. package/dist/ui-kit/sections/emergency-band.d.ts.map +1 -0
  71. package/dist/ui-kit/sections/emergency-band.js +43 -0
  72. package/dist/ui-kit/sections/emergency-band.js.map +1 -0
  73. package/dist/ui-kit/sections/gallery.d.ts +60 -0
  74. package/dist/ui-kit/sections/gallery.d.ts.map +1 -0
  75. package/dist/ui-kit/sections/gallery.js +39 -0
  76. package/dist/ui-kit/sections/gallery.js.map +1 -0
  77. package/dist/ui-kit/sections/hero.d.ts +5 -0
  78. package/dist/ui-kit/sections/hero.d.ts.map +1 -1
  79. package/dist/ui-kit/sections/hero.js +1 -1
  80. package/dist/ui-kit/sections/hero.js.map +1 -1
  81. package/dist/ui-kit/sections/index.d.ts +14 -1
  82. package/dist/ui-kit/sections/index.d.ts.map +1 -1
  83. package/dist/ui-kit/sections/index.js +14 -1
  84. package/dist/ui-kit/sections/index.js.map +1 -1
  85. package/dist/ui-kit/sections/licences.d.ts +72 -0
  86. package/dist/ui-kit/sections/licences.d.ts.map +1 -0
  87. package/dist/ui-kit/sections/licences.js +46 -0
  88. package/dist/ui-kit/sections/licences.js.map +1 -0
  89. package/dist/ui-kit/sections/local-facts.d.ts +78 -0
  90. package/dist/ui-kit/sections/local-facts.d.ts.map +1 -0
  91. package/dist/ui-kit/sections/local-facts.js +32 -0
  92. package/dist/ui-kit/sections/local-facts.js.map +1 -0
  93. package/dist/ui-kit/sections/nearby-areas.d.ts +50 -0
  94. package/dist/ui-kit/sections/nearby-areas.d.ts.map +1 -0
  95. package/dist/ui-kit/sections/nearby-areas.js +49 -0
  96. package/dist/ui-kit/sections/nearby-areas.js.map +1 -0
  97. package/dist/ui-kit/sections/nearby-jobs.d.ts +66 -0
  98. package/dist/ui-kit/sections/nearby-jobs.d.ts.map +1 -0
  99. package/dist/ui-kit/sections/nearby-jobs.js +49 -0
  100. package/dist/ui-kit/sections/nearby-jobs.js.map +1 -0
  101. package/dist/ui-kit/sections/projects.d.ts +79 -0
  102. package/dist/ui-kit/sections/projects.d.ts.map +1 -0
  103. package/dist/ui-kit/sections/projects.js +69 -0
  104. package/dist/ui-kit/sections/projects.js.map +1 -0
  105. package/dist/ui-kit/sections/quote-checklist.d.ts +43 -0
  106. package/dist/ui-kit/sections/quote-checklist.d.ts.map +1 -0
  107. package/dist/ui-kit/sections/quote-checklist.js +19 -0
  108. package/dist/ui-kit/sections/quote-checklist.js.map +1 -0
  109. package/dist/ui-kit/sections/related-services.d.ts +41 -0
  110. package/dist/ui-kit/sections/related-services.d.ts.map +1 -0
  111. package/dist/ui-kit/sections/related-services.js +52 -0
  112. package/dist/ui-kit/sections/related-services.js.map +1 -0
  113. package/dist/ui-kit/sections/reviews-band.d.ts +11 -0
  114. package/dist/ui-kit/sections/reviews-band.d.ts.map +1 -1
  115. package/dist/ui-kit/sections/reviews-band.js +11 -0
  116. package/dist/ui-kit/sections/reviews-band.js.map +1 -1
  117. package/dist/ui-kit/sections/services-grid.js +1 -1
  118. package/dist/ui-kit/sections/services-grid.js.map +1 -1
  119. package/dist/ui-kit/sections/work-photos.d.ts +15 -0
  120. package/dist/ui-kit/sections/work-photos.d.ts.map +1 -0
  121. package/dist/ui-kit/sections/work-photos.js +24 -0
  122. package/dist/ui-kit/sections/work-photos.js.map +1 -0
  123. package/dist/ui-kit/shell/back-to-top.js +1 -1
  124. package/dist/ui-kit/shell/back-to-top.js.map +1 -1
  125. package/dist/ui-kit/shell/current-link.d.ts +21 -5
  126. package/dist/ui-kit/shell/current-link.d.ts.map +1 -1
  127. package/dist/ui-kit/shell/current-link.js +16 -5
  128. package/dist/ui-kit/shell/current-link.js.map +1 -1
  129. package/dist/ui-kit/shell/drawer-client.js +1 -1
  130. package/dist/ui-kit/shell/drawer-client.js.map +1 -1
  131. package/dist/ui-kit/shell/intent-prefetch.d.ts.map +1 -1
  132. package/dist/ui-kit/shell/intent-prefetch.js +3 -2
  133. package/dist/ui-kit/shell/intent-prefetch.js.map +1 -1
  134. package/dist/ui-kit/shell/mobile-call-bar.d.ts +6 -1
  135. package/dist/ui-kit/shell/mobile-call-bar.d.ts.map +1 -1
  136. package/dist/ui-kit/shell/mobile-call-bar.js +6 -3
  137. package/dist/ui-kit/shell/mobile-call-bar.js.map +1 -1
  138. package/dist/ui-kit/shell/mobile-drawer.js +2 -2
  139. package/dist/ui-kit/shell/mobile-drawer.js.map +1 -1
  140. package/dist/ui-kit/shell/shell-quote.d.ts +10 -0
  141. package/dist/ui-kit/shell/shell-quote.d.ts.map +1 -0
  142. package/dist/ui-kit/shell/shell-quote.js +13 -0
  143. package/dist/ui-kit/shell/shell-quote.js.map +1 -0
  144. package/dist/ui-kit/shell/site-header.d.ts +1 -1
  145. package/dist/ui-kit/shell/site-header.js +4 -4
  146. package/dist/ui-kit/shell/site-header.js.map +1 -1
  147. package/dist/ui-kit/structure.css +410 -7
  148. package/package.json +2 -2
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `Gallery` (`gallery`, 3.15.0): a grid of genuine job photos, on the gallery page (eight or more) or as a short
3
+ * section on a money page. Server component, no client JS: every photo renders through `MediaFrame`, so the sizing
4
+ * rule (a fluid `vw` size or a fixed `displayWidth`, never `sizes="148px"`) and the provenance hook are the library's.
5
+ *
6
+ * Locked:
7
+ * - genuine photos only: a stock or AI image fails the build. An AI illustration is supporting media with its
8
+ * "Illustration" label elsewhere, never in an "our work" gallery (F13);
9
+ * - every photo loads lazily (no preload): a gallery is never the page's main image;
10
+ * - a photo may link its project page; those links never prefetch (a gallery can hold dozens);
11
+ * - no photo twice, and no default visible copy: captions ("{what}, {suburb}, {month year}") come from the site.
12
+ */
13
+ import type { ReactNode } from 'react';
14
+ import type { BrandlabMediaRatio } from '@brandlab-digital/site-core/media';
15
+ import { type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order';
16
+ import { type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js';
17
+ import { type UiMedia } from '../primitives/media-frame.js';
18
+ import type { RepeatedSlotOf } from '../repeated-slots.js';
19
+ import { type SkinEntry } from '../skin.js';
20
+ import type { UiLink } from '../types.js';
21
+ /** Rendered once per photo: styled from the skin stylesheet, never by class. The frames are `[data-bl="media"]`. */
22
+ export type GalleryRepeatedSlot = RepeatedSlotOf<'gallery'>;
23
+ export type GallerySlot = SectionSlot | SectionHeadingSlot | 'list' | 'more' | GalleryRepeatedSlot;
24
+ export interface GalleryLayout {
25
+ /** Columns on wide screens. Defaults to 3. */
26
+ readonly columns?: 2 | 3 | 4;
27
+ /** Crop every photo to one ratio, so the grid lines up. Left out, each photo keeps its own shape. */
28
+ readonly ratio?: BrandlabMediaRatio;
29
+ }
30
+ export type GallerySkin = SkinEntry<GallerySlot, never, GalleryLayout, GalleryRepeatedSlot>;
31
+ /** One photo of the work. */
32
+ export interface GalleryPhoto {
33
+ /** A stable slug (the media id), rendered as `data-item`. */
34
+ readonly id: string;
35
+ /** A genuine photo from the media manifest. */
36
+ readonly media: UiMedia;
37
+ /** "{what}, {suburb}, {month year}", only when the photo really shows that job. */
38
+ readonly caption?: ReactNode;
39
+ /** The job's project page, when it has one. Never prefetched. */
40
+ readonly href?: string;
41
+ }
42
+ export interface GalleryProps extends GallerySkin {
43
+ /** The H2, for example "Our recent tree work". */
44
+ readonly title: ReactNode;
45
+ readonly eyebrow?: ReactNode;
46
+ readonly lead?: ReactNode;
47
+ readonly photos: readonly GalleryPhoto[];
48
+ /** A link after the grid, for example the full gallery. Never prefetched. */
49
+ readonly more?: UiLink;
50
+ /** The photos' `sizes`: a fluid `vw` string, or a number for a fixed CSS width (thumbnails). Defaults to one column. */
51
+ readonly imageSizes?: string | number;
52
+ /** Defaults to `gallery-heading`. */
53
+ readonly headingId?: string;
54
+ /** Set by `SectionSequence`. */
55
+ readonly tone?: SectionTone;
56
+ }
57
+ /** The section's guard for `SectionSequence`: skipped when there is no genuine photo to show. */
58
+ export declare function galleryReady({ photos }: Pick<GalleryProps, 'photos'>): BrandlabSectionReadiness;
59
+ export declare function Gallery({ title, eyebrow, lead, photos, more, imageSizes, headingId, tone, layout, classes }: GalleryProps): import("react/jsx-runtime").JSX.Element | null;
60
+ //# sourceMappingURL=gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/gallery.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAC3E,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,2CAA2C,CAAA;AACxG,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE9H,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,oHAAoH;AACpH,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;AAC3D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,mBAAmB,CAAA;AAClG,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC5B,qGAAqG;IACrG,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAA;CACpC;AACD,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,mBAAmB,CAAC,CAAA;AAE3F,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,wHAAwH;IACxH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrC,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B;AAED,iGAAiG;AACjG,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG,wBAAwB,CAE/F;AAgBD,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,YAAY,kDAiC7I"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SECTION_READY } from '@brandlab-digital/site-core/section-order';
3
+ import { Section, SectionHeading } from '../primitives/layout.js';
4
+ import { Anchor } from '../primitives/links.js';
5
+ import { MediaFrame } from '../primitives/media-frame.js';
6
+ import { repeatedSlot, slot } from '../skin.js';
7
+ import { assertGenuineWork, frameSizing, gridImageSizes, isInternalPath } from './work-photos.js';
8
+ /** The section's guard for `SectionSequence`: skipped when there is no genuine photo to show. */
9
+ export function galleryReady({ photos }) {
10
+ return photos.length > 0 ? SECTION_READY : { render: false, reason: 'no-genuine-photos' };
11
+ }
12
+ function assertPhotos(photos) {
13
+ const ids = new Set();
14
+ const sources = new Map();
15
+ for (const photo of photos) {
16
+ if (!photo.id || ids.has(photo.id))
17
+ throw new Error(`Gallery: every photo needs its own id ("${photo.id}" is empty or repeated).`);
18
+ ids.add(photo.id);
19
+ assertGenuineWork('Gallery', photo.media, `"${photo.id}"`);
20
+ const other = sources.get(photo.media.src);
21
+ if (other)
22
+ throw new Error(`Gallery: "${photo.id}" and "${other}" are the same photo (${photo.media.src}).`);
23
+ sources.set(photo.media.src, photo.id);
24
+ if (photo.href !== undefined && !isInternalPath(photo.href))
25
+ throw new Error(`Gallery: "${photo.id}" links ${photo.href}; a photo links its project page on this site.`);
26
+ }
27
+ }
28
+ export function Gallery({ title, eyebrow, lead, photos, more, imageSizes, headingId = 'gallery-heading', tone, layout, classes }) {
29
+ if (!galleryReady({ photos }).render)
30
+ return null;
31
+ assertPhotos(photos);
32
+ const columns = layout?.columns ?? 3;
33
+ const sizing = frameSizing(imageSizes ?? gridImageSizes(columns));
34
+ return (_jsxs(Section, { id: "gallery", labelledBy: headingId, tone: tone, classes: classes, hooks: { 'data-columns': String(columns), 'data-ratio': layout?.ratio }, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), _jsx("ul", { role: "list", ...slot(classes, 'list'), children: photos.map((photo) => {
35
+ const frame = _jsx(MediaFrame, { media: photo.media, ratio: layout?.ratio, caption: photo.caption, ...sizing });
36
+ return (_jsx("li", { ...repeatedSlot('item'), "data-item": photo.id, children: photo.href ? (_jsx(Anchor, { href: photo.href, prefetch: false, hooks: { 'data-slot': 'link' }, children: frame })) : (frame) }, photo.id));
37
+ }) }), more ? (_jsx("p", { ...slot(classes, 'more'), children: _jsx(Anchor, { href: more.href, prefetch: false, hooks: {}, children: more.label }) })) : null] }));
38
+ }
39
+ //# sourceMappingURL=gallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/gallery.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Gallery` (`gallery`, 3.15.0): a grid of genuine job photos, on the gallery page (eight or more) or as a short\n * section on a money page. Server component, no client JS: every photo renders through `MediaFrame`, so the sizing\n * rule (a fluid `vw` size or a fixed `displayWidth`, never `sizes=\"148px\"`) and the provenance hook are the library's.\n *\n * Locked:\n * - genuine photos only: a stock or AI image fails the build. An AI illustration is supporting media with its\n * \"Illustration\" label elsewhere, never in an \"our work\" gallery (F13);\n * - every photo loads lazily (no preload): a gallery is never the page's main image;\n * - a photo may link its project page; those links never prefetch (a gallery can hold dozens);\n * - no photo twice, and no default visible copy: captions (\"{what}, {suburb}, {month year}\") come from the site.\n */\nimport type { ReactNode } from 'react'\nimport type { BrandlabMediaRatio } from '@brandlab-digital/site-core/media'\nimport { SECTION_READY, type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order'\nimport { Section, SectionHeading, type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js'\nimport { Anchor } from '../primitives/links.js'\nimport { MediaFrame, type UiMedia } from '../primitives/media-frame.js'\nimport type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, slot, type SkinEntry } from '../skin.js'\nimport type { UiLink } from '../types.js'\nimport { assertGenuineWork, frameSizing, gridImageSizes, isInternalPath } from './work-photos.js'\n\n/** Rendered once per photo: styled from the skin stylesheet, never by class. The frames are `[data-bl=\"media\"]`. */\nexport type GalleryRepeatedSlot = RepeatedSlotOf<'gallery'>\nexport type GallerySlot = SectionSlot | SectionHeadingSlot | 'list' | 'more' | GalleryRepeatedSlot\nexport interface GalleryLayout {\n /** Columns on wide screens. Defaults to 3. */\n readonly columns?: 2 | 3 | 4\n /** Crop every photo to one ratio, so the grid lines up. Left out, each photo keeps its own shape. */\n readonly ratio?: BrandlabMediaRatio\n}\nexport type GallerySkin = SkinEntry<GallerySlot, never, GalleryLayout, GalleryRepeatedSlot>\n\n/** One photo of the work. */\nexport interface GalleryPhoto {\n /** A stable slug (the media id), rendered as `data-item`. */\n readonly id: string\n /** A genuine photo from the media manifest. */\n readonly media: UiMedia\n /** \"{what}, {suburb}, {month year}\", only when the photo really shows that job. */\n readonly caption?: ReactNode\n /** The job's project page, when it has one. Never prefetched. */\n readonly href?: string\n}\n\nexport interface GalleryProps extends GallerySkin {\n /** The H2, for example \"Our recent tree work\". */\n readonly title: ReactNode\n readonly eyebrow?: ReactNode\n readonly lead?: ReactNode\n readonly photos: readonly GalleryPhoto[]\n /** A link after the grid, for example the full gallery. Never prefetched. */\n readonly more?: UiLink\n /** The photos' `sizes`: a fluid `vw` string, or a number for a fixed CSS width (thumbnails). Defaults to one column. */\n readonly imageSizes?: string | number\n /** Defaults to `gallery-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\n/** The section's guard for `SectionSequence`: skipped when there is no genuine photo to show. */\nexport function galleryReady({ photos }: Pick<GalleryProps, 'photos'>): BrandlabSectionReadiness {\n return photos.length > 0 ? SECTION_READY : { render: false, reason: 'no-genuine-photos' }\n}\n\nfunction assertPhotos(photos: readonly GalleryPhoto[]): void {\n const ids = new Set<string>()\n const sources = new Map<string, string>()\n for (const photo of photos) {\n if (!photo.id || ids.has(photo.id)) throw new Error(`Gallery: every photo needs its own id (\"${photo.id}\" is empty or repeated).`)\n ids.add(photo.id)\n assertGenuineWork('Gallery', photo.media, `\"${photo.id}\"`)\n const other = sources.get(photo.media.src)\n if (other) throw new Error(`Gallery: \"${photo.id}\" and \"${other}\" are the same photo (${photo.media.src}).`)\n sources.set(photo.media.src, photo.id)\n if (photo.href !== undefined && !isInternalPath(photo.href)) throw new Error(`Gallery: \"${photo.id}\" links ${photo.href}; a photo links its project page on this site.`)\n }\n}\n\nexport function Gallery({ title, eyebrow, lead, photos, more, imageSizes, headingId = 'gallery-heading', tone, layout, classes }: GalleryProps) {\n if (!galleryReady({ photos }).render) return null\n assertPhotos(photos)\n const columns = layout?.columns ?? 3\n const sizing = frameSizing(imageSizes ?? gridImageSizes(columns))\n return (\n <Section id=\"gallery\" labelledBy={headingId} tone={tone} classes={classes} hooks={{ 'data-columns': String(columns), 'data-ratio': layout?.ratio }}>\n <SectionHeading id={headingId} title={title} eyebrow={eyebrow} lead={lead} classes={classes} />\n <ul role=\"list\" {...slot(classes, 'list')}>\n {photos.map((photo) => {\n const frame = <MediaFrame media={photo.media} ratio={layout?.ratio} caption={photo.caption} {...sizing} />\n return (\n <li key={photo.id} {...repeatedSlot('item')} data-item={photo.id}>\n {photo.href ? (\n <Anchor href={photo.href} prefetch={false} hooks={{ 'data-slot': 'link' }}>\n {frame}\n </Anchor>\n ) : (\n frame\n )}\n </li>\n )\n })}\n </ul>\n {more ? (\n <p {...slot(classes, 'more')}>\n <Anchor href={more.href} prefetch={false} hooks={{}}>\n {more.label}\n </Anchor>\n </p>\n ) : null}\n </Section>\n )\n}\n"]}
@@ -54,6 +54,11 @@ export interface HeroLayout {
54
54
  readonly height?: 'standard' | 'tall';
55
55
  /** The trust checklist's columns (default 2). */
56
56
  readonly checklistColumns?: 1 | 2;
57
+ /**
58
+ * The checklist's ticks (3.15.0): `icon` (the default) is the sprite's check; `rule` ships no svg, only a mark the
59
+ * stylesheet draws as a short rule (`TickList`'s `layout.tick`), about 100 bytes less a fact.
60
+ */
61
+ readonly checklistTick?: 'icon' | 'rule';
57
62
  }
58
63
  export type HeroSkin = SkinEntry<HeroSlot, HeroVariant, HeroLayout>;
59
64
  /** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */
@@ -1 +1 @@
1
- {"version":3,"file":"hero.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hero.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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"}
@@ -53,6 +53,6 @@ export function Hero(props) {
53
53
  'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),
54
54
  'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,
55
55
  'data-form': form ? '' : undefined,
56
- }, children: [_jsxs("div", { ...slot(classes, 'content'), children: [crumbs ? _jsx("div", { ...slot(classes, 'crumbs'), children: crumbs }) : null, eyebrow ? _jsx("p", { ...slot(classes, 'eyebrow'), children: eyebrow }) : null, _jsx("h1", { id: headingId, ...slot(classes, 'h1'), children: title }), _jsx("p", { ...slot(classes, 'summary'), children: summary }), _jsxs("div", { ...slot(classes, 'actions'), "data-bl-call-path": "", "data-primary": primary, children: [_jsx(PhoneLink, { phone: phone, placement: "hero", label: phoneLabel, showIcon: true, variant: "button", classes: { root: classes?.phone } }), _jsx(QuoteButton, { label: quote.label, href: quote.href ?? `#${HERO_FORM_ID}`, placement: "hero", ctaId: "quote-hero", variant: primary === 'quote' ? 'primary' : 'secondary', classes: { root: classes?.quote } })] }), checklist.length > 0 ? (_jsx("div", { ...slot(classes, 'checklist'), children: _jsx(TickList, { items: checklist, layout: { columns: layout?.checklistColumns ?? 2 } }) })) : null, hasGoogleRating(rating) ? _jsx(RatingChip, { reviews: rating, classes: { root: classes?.rating } }) : null, badges ? _jsx("div", { ...slot(classes, 'badges'), children: badges }) : null] }), _jsxs("div", { ...slot(classes, 'media'), children: [_jsx(MediaFrame, { media: media, ratio: "4:3", sizes: variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw', caption: caption, priority: true, classes: { caption: classes?.caption } }), variant === 'full-bleed' ? _jsx("div", { ...slot(classes, 'scrim'), "aria-hidden": "true" }) : null] }), form ? _jsx("div", { ...slot(classes, 'form'), children: form }) : null] }));
56
+ }, children: [_jsxs("div", { ...slot(classes, 'content'), children: [crumbs ? _jsx("div", { ...slot(classes, 'crumbs'), children: crumbs }) : null, eyebrow ? _jsx("p", { ...slot(classes, 'eyebrow'), children: eyebrow }) : null, _jsx("h1", { id: headingId, ...slot(classes, 'h1'), children: title }), _jsx("p", { ...slot(classes, 'summary'), children: summary }), _jsxs("div", { ...slot(classes, 'actions'), "data-bl-call-path": "", "data-primary": primary, children: [_jsx(PhoneLink, { phone: phone, placement: "hero", label: phoneLabel, showIcon: true, variant: "button", classes: { root: classes?.phone } }), _jsx(QuoteButton, { label: quote.label, href: quote.href ?? `#${HERO_FORM_ID}`, placement: "hero", ctaId: "quote-hero", variant: primary === 'quote' ? 'primary' : 'secondary', classes: { root: classes?.quote } })] }), checklist.length > 0 ? (_jsx("div", { ...slot(classes, 'checklist'), children: _jsx(TickList, { items: checklist, layout: { columns: layout?.checklistColumns ?? 2, ...(layout?.checklistTick ? { tick: layout.checklistTick } : {}) } }) })) : null, hasGoogleRating(rating) ? _jsx(RatingChip, { reviews: rating, classes: { root: classes?.rating } }) : null, badges ? _jsx("div", { ...slot(classes, 'badges'), children: badges }) : null] }), _jsxs("div", { ...slot(classes, 'media'), children: [_jsx(MediaFrame, { media: media, ratio: "4:3", sizes: variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw', caption: caption, priority: true, classes: { caption: classes?.caption } }), variant === 'full-bleed' ? _jsx("div", { ...slot(classes, 'scrim'), "aria-hidden": "true" }) : null] }), form ? _jsx("div", { ...slot(classes, 'form'), children: form }) : null] }));
57
57
  }
58
58
  //# sourceMappingURL=hero.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hero.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Hero` (S1, `hero`, 3.14.0): the first screen of the home, service and service-in-suburb pages. Server component;\n * it wins the click and starts the enquiry. Light by default: every colour comes from the site's tokens and skin.\n *\n * In source order, which is the phone order: breadcrumbs (service and location pages), an optional eyebrow, the H1\n * (once, as plain text; on a location page the built \"{Service} {Suburb}\" string), one offer line of 25 words or fewer,\n * the two actions, the trust checklist (up to six verified facts, two columns), Google's feed rating, partner badges,\n * the photo and then the site's quote form. On desktop the form sits beside the text (`split`, the photo on the other side) or over the photo\n * as a card (`full-bleed`, under a text-side scrim the skin colours).\n *\n * Locked:\n * - the actions are the phone link (`data-phone-location=\"hero\"`, icon on the left) then the quote button\n * (`lead_cta_click` placement `hero`, id `quote-hero`): the same phone-first order as the header, the call bar and the\n * CTA band (K6). Job mode only moves the primary styling (`data-primary`), never the order;\n * - the actions are the page's call path (`data-bl-call-path`), so the phone call bar stays hidden while they are on\n * screen and shows once they scroll out (F6);\n * - with a form, the quote button links to it (`#quote-hero`, the form's own id) and the form is in the hero, so C1 sees\n * the H1, a call and a quote action in the first phone screen and the form in the first desktop screen;\n * - the photo is the page's largest image: preloaded, high priority, never lazy. It must be real work or licensed stock\n * (an AI illustration is supporting only, F13), and a job caption needs a genuine photo;\n * - no review quote, and no visible words the site doesn't pass.\n *\n * `family` is the page family (`data-family`): `home` takes no breadcrumbs and is `tall` by default; `service` and\n * `location` take breadcrumbs. A skin styles families apart through `[data-family]`; the variant is the site's one\n * hero structure.\n */\nimport type { ReactNode } from 'react'\nimport { SITE_JOB_MODES, type SiteJobMode } from '@brandlab-digital/site-core/strategy'\nimport { Section, type SectionTone } from '../primitives/layout.js'\nimport { PhoneLink } from '../primitives/links.js'\nimport { TickList, type TickItem } from '../primitives/lists.js'\nimport { MediaFrame, type UiMedia } from '../primitives/media-frame.js'\nimport { QuoteButton } from '../primitives/quote-button.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiPhone } from '../types.js'\nimport { RatingChip, hasGoogleRating, type RatingChipData } from './rating-chip.js'\n\nexport const HERO_VARIANTS = ['split', 'full-bleed'] as const\nexport type HeroVariant = (typeof HERO_VARIANTS)[number]\n\n/**\n * The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all\n * take the `location` hero), so this stays a hero-level grouping.\n */\nexport const HERO_FAMILIES = ['home', 'service', 'location'] as const\nexport type HeroFamily = (typeof HERO_FAMILIES)[number]\n\n/**\n * A service's job mode (v2 §2.2), as the site's strategy decision card records it (`SiteJobMode` in\n * `site-core/strategy`): which action is styled primary. Never the order.\n */\nexport const HERO_JOB_MODES = SITE_JOB_MODES\nexport type HeroJobMode = SiteJobMode\n\nexport type HeroSlot =\n | 'root'\n | 'inner'\n | 'content'\n | 'crumbs'\n | 'eyebrow'\n | 'h1'\n | 'summary'\n | 'actions'\n | 'phone'\n | 'quote'\n | 'checklist'\n | 'rating'\n | 'badges'\n | 'media'\n | 'caption'\n | 'scrim'\n | 'form'\n\nexport interface HeroLayout {\n /** `split`: the photo's side on desktop (default `end`). */\n readonly mediaSide?: 'start' | 'end'\n /** Default `tall` on the home page, `standard` elsewhere. */\n readonly height?: 'standard' | 'tall'\n /** The trust checklist's columns (default 2). */\n readonly checklistColumns?: 1 | 2\n}\n\nexport type HeroSkin = SkinEntry<HeroSlot, HeroVariant, HeroLayout>\n\n/** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */\nexport const HERO_FORM_ID = 'quote-hero'\n/** The most facts the trust checklist holds (v2 S1: three to six). */\nexport const HERO_CHECKLIST_MAX = 6\n/** The offer line's word limit (v2 §9.2). */\nexport const HERO_SUMMARY_MAX_WORDS = 25\n\nexport interface HeroProps extends HeroSkin {\n readonly family: HeroFamily\n /** The H1 text: the recorded H1 on live pages, the title builder's on new ones (\"Tree Removal Castle Hill\"). */\n readonly title: string\n /** The one offer line under the H1, 25 words or fewer. */\n readonly summary: string\n /** A short label above the H1. */\n readonly eyebrow?: string\n /** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). Service and location pages only. */\n readonly breadcrumbs?: ReactNode\n /** The trust checklist: verified facts only (licence, insurance, rating source, years), at most six. */\n readonly checklist: readonly TickItem[]\n /**\n * Google's average and total from the reviews feed (`prepareBusinessProfileReviews`), shown as the shared\n * `RatingChip` (`data-bl=\"rating-chip\"`, the `rating` class on its root, like `phone` and `quote`) after the\n * checklist, linked to the Business Profile. Never typed by hand: without a feed average and a total of 1 or more,\n * nothing renders.\n */\n readonly rating?: RatingChipData | null\n /** Partner badges or logos held and verified (a `LogoStrip`). */\n readonly badges?: ReactNode\n /** The best genuine photo of this page's service; on a location page the nearest job's. From `media.ts`. */\n readonly media: UiMedia\n /** \"{what}, {suburb}, {month year}\" for a genuine job photo, for example on a location page. */\n readonly caption?: string\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call\". */\n readonly phoneLabel?: string\n /**\n * The site's recorded quote label (C5). `href` defaults to the hero's own form (`#quote-hero`); without a form it\n * is required (for example `/contact#quote`).\n */\n readonly quote: { readonly label: string; readonly href?: string }\n /** The site's bound quote form: `<QuoteForm formId=\"quote-hero\" />`. Required on money pages (C1). */\n readonly form?: ReactNode\n /** The page's service job mode (default `planned`): `urgent` styles the phone link primary. */\n readonly jobMode?: HeroJobMode\n /** The H1's id (default `hero-title`). */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n /** Signature art, rendered `aria-hidden` (Superior's waterline, Absolute's grass edge). */\n readonly decoration?: ReactNode\n}\n\nconst words = (text: string) => text.trim().split(/\\s+/).filter(Boolean).length\n\nfunction assertHero({ title, summary, checklist, media, caption, quote, form }: HeroProps): void {\n const problems: string[] = []\n if (title.trim() === '') problems.push('the H1 is empty')\n if (words(summary) > HERO_SUMMARY_MAX_WORDS) problems.push(`the offer line has ${words(summary)} words (${HERO_SUMMARY_MAX_WORDS} at most)`)\n if (checklist.length > HERO_CHECKLIST_MAX) problems.push(`the checklist has ${checklist.length} facts (${HERO_CHECKLIST_MAX} at most)`)\n if (media.provenance === 'illustrative-ai') problems.push(`${media.src} is an AI illustration; the hero shows real work or licensed stock`)\n if (caption && media.provenance !== 'genuine') problems.push(`a job caption needs a genuine photo (${media.src} is ${media.provenance})`)\n if (!form && !quote.href) problems.push('without a form, quote.href must name the quote form, for example /contact#quote')\n if (problems.length > 0) throw new Error(`Hero \"${title}\": ${problems.join('; ')}.`)\n}\n\nexport function Hero(props: HeroProps) {\n assertHero(props)\n const {\n family,\n title,\n summary,\n eyebrow,\n breadcrumbs,\n checklist,\n rating,\n badges,\n media,\n caption,\n phone,\n phoneLabel,\n quote,\n form,\n jobMode = 'planned',\n headingId = 'hero-title',\n tone,\n decoration,\n variant = 'split',\n layout,\n classes,\n } = props\n const primary = jobMode === 'urgent' ? 'phone' : 'quote'\n const crumbs = family === 'home' ? null : breadcrumbs\n return (\n <Section\n id=\"hero\"\n labelledBy={headingId}\n tone={tone}\n variant={variant}\n classes={classes}\n decoration={decoration}\n phonePlacement=\"hero\"\n hooks={{\n 'data-family': family,\n 'data-job-mode': jobMode,\n 'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),\n 'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,\n 'data-form': form ? '' : undefined,\n }}\n >\n <div {...slot(classes, 'content')}>\n {crumbs ? <div {...slot(classes, 'crumbs')}>{crumbs}</div> : null}\n {eyebrow ? <p {...slot(classes, 'eyebrow')}>{eyebrow}</p> : null}\n <h1 id={headingId} {...slot(classes, 'h1')}>\n {title}\n </h1>\n <p {...slot(classes, 'summary')}>{summary}</p>\n <div {...slot(classes, 'actions')} data-bl-call-path=\"\" data-primary={primary}>\n <PhoneLink phone={phone} placement=\"hero\" label={phoneLabel} showIcon variant=\"button\" classes={{ root: classes?.phone }} />\n <QuoteButton\n label={quote.label}\n href={quote.href ?? `#${HERO_FORM_ID}`}\n placement=\"hero\"\n ctaId=\"quote-hero\"\n variant={primary === 'quote' ? 'primary' : 'secondary'}\n classes={{ root: classes?.quote }}\n />\n </div>\n {checklist.length > 0 ? (\n <div {...slot(classes, 'checklist')}>\n <TickList items={checklist} layout={{ columns: layout?.checklistColumns ?? 2 }} />\n </div>\n ) : null}\n {hasGoogleRating(rating) ? <RatingChip reviews={rating} classes={{ root: classes?.rating }} /> : null}\n {badges ? <div {...slot(classes, 'badges')}>{badges}</div> : null}\n </div>\n <div {...slot(classes, 'media')}>\n <MediaFrame\n media={media}\n ratio=\"4:3\"\n sizes={variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw'}\n caption={caption}\n priority\n classes={{ caption: classes?.caption }}\n />\n {variant === 'full-bleed' ? <div {...slot(classes, 'scrim')} aria-hidden=\"true\" /> : null}\n </div>\n {form ? <div {...slot(classes, 'form')}>{form}</div> : null}\n </Section>\n )\n}\n"]}
1
+ {"version":3,"file":"hero.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":";AA2BA,OAAO,EAAE,cAAc,EAAoB,MAAM,sCAAsC,CAAA;AACvF,OAAO,EAAE,OAAO,EAAoB,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAiB,MAAM,wBAAwB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAgB,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAuB,MAAM,kBAAkB,CAAA;AAEnF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,YAAY,CAAU,CAAA;AAG7D;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAA;AAGrE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAsC5C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAA;AACxC,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAA;AACnC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAA;AA+CxC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;AAE/E,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAa;IACvF,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,sBAAsB;QAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,OAAO,CAAC,WAAW,sBAAsB,WAAW,CAAC,CAAA;IAC5I,IAAI,SAAS,CAAC,MAAM,GAAG,kBAAkB;QAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,SAAS,CAAC,MAAM,WAAW,kBAAkB,WAAW,CAAC,CAAA;IACvI,IAAI,KAAK,CAAC,UAAU,KAAK,iBAAiB;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,oEAAoE,CAAC,CAAA;IAC3I,IAAI,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,wCAAwC,KAAK,CAAC,GAAG,OAAO,KAAK,CAAC,UAAU,GAAG,CAAC,CAAA;IACzI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,QAAQ,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAA;IAC1H,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtF,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,UAAU,CAAC,KAAK,CAAC,CAAA;IACjB,MAAM,EACJ,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,IAAI,EACJ,OAAO,GAAG,SAAS,EACnB,SAAS,GAAG,YAAY,EACxB,IAAI,EACJ,UAAU,EACV,OAAO,GAAG,OAAO,EACjB,MAAM,EACN,OAAO,GACR,GAAG,KAAK,CAAA;IACT,MAAM,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACxD,MAAM,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAA;IACrD,OAAO,CACL,MAAC,OAAO,IACN,EAAE,EAAC,MAAM,EACT,UAAU,EAAE,SAAS,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAC,MAAM,EACrB,KAAK,EAAE;YACL,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,OAAO;YACxB,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;YAC1E,iBAAiB,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YACjF,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;SACnC,aAED,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,aAC9B,MAAM,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,MAAM,GAAO,CAAC,CAAC,CAAC,IAAI,EAChE,OAAO,CAAC,CAAC,CAAC,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,EAChE,aAAI,EAAE,EAAE,SAAS,KAAM,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,YACvC,KAAK,GACH,EACL,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,EAC9C,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,uBAAoB,EAAE,kBAAe,OAAO,aAC3E,KAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,EAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,QAAC,OAAO,EAAC,QAAQ,EAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAI,EAC5H,KAAC,WAAW,IACV,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,YAAY,EAAE,EACtC,SAAS,EAAC,MAAM,EAChB,KAAK,EAAC,YAAY,EAClB,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EACtD,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GACjC,IACE,EACL,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACtB,iBAAS,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,YACjC,KAAC,QAAQ,IAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAI,GAChJ,CACP,CAAC,CAAC,CAAC,IAAI,EACP,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,EACpG,MAAM,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,MAAM,GAAO,CAAC,CAAC,CAAC,IAAI,IAC7D,EACN,kBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,aAC7B,KAAC,UAAU,IACT,KAAK,EAAE,KAAK,EACZ,KAAK,EAAC,KAAK,EACX,KAAK,EAAE,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC,EAC7E,OAAO,EAAE,OAAO,EAChB,QAAQ,QACR,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GACtC,EACD,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAc,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,IACrF,EACL,IAAI,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,YAAG,IAAI,GAAO,CAAC,CAAC,CAAC,IAAI,IACnD,CACX,CAAA;AACH,CAAC","sourcesContent":["/**\n * `Hero` (S1, `hero`, 3.14.0): the first screen of the home, service and service-in-suburb pages. Server component;\n * it wins the click and starts the enquiry. Light by default: every colour comes from the site's tokens and skin.\n *\n * In source order, which is the phone order: breadcrumbs (service and location pages), an optional eyebrow, the H1\n * (once, as plain text; on a location page the built \"{Service} {Suburb}\" string), one offer line of 25 words or fewer,\n * the two actions, the trust checklist (up to six verified facts, two columns), Google's feed rating, partner badges,\n * the photo and then the site's quote form. On desktop the form sits beside the text (`split`, the photo on the other side) or over the photo\n * as a card (`full-bleed`, under a text-side scrim the skin colours).\n *\n * Locked:\n * - the actions are the phone link (`data-phone-location=\"hero\"`, icon on the left) then the quote button\n * (`lead_cta_click` placement `hero`, id `quote-hero`): the same phone-first order as the header, the call bar and the\n * CTA band (K6). Job mode only moves the primary styling (`data-primary`), never the order;\n * - the actions are the page's call path (`data-bl-call-path`), so the phone call bar stays hidden while they are on\n * screen and shows once they scroll out (F6);\n * - with a form, the quote button links to it (`#quote-hero`, the form's own id) and the form is in the hero, so C1 sees\n * the H1, a call and a quote action in the first phone screen and the form in the first desktop screen;\n * - the photo is the page's largest image: preloaded, high priority, never lazy. It must be real work or licensed stock\n * (an AI illustration is supporting only, F13), and a job caption needs a genuine photo;\n * - no review quote, and no visible words the site doesn't pass.\n *\n * `family` is the page family (`data-family`): `home` takes no breadcrumbs and is `tall` by default; `service` and\n * `location` take breadcrumbs. A skin styles families apart through `[data-family]`; the variant is the site's one\n * hero structure.\n */\nimport type { ReactNode } from 'react'\nimport { SITE_JOB_MODES, type SiteJobMode } from '@brandlab-digital/site-core/strategy'\nimport { Section, type SectionTone } from '../primitives/layout.js'\nimport { PhoneLink } from '../primitives/links.js'\nimport { TickList, type TickItem } from '../primitives/lists.js'\nimport { MediaFrame, type UiMedia } from '../primitives/media-frame.js'\nimport { QuoteButton } from '../primitives/quote-button.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiPhone } from '../types.js'\nimport { RatingChip, hasGoogleRating, type RatingChipData } from './rating-chip.js'\n\nexport const HERO_VARIANTS = ['split', 'full-bleed'] as const\nexport type HeroVariant = (typeof HERO_VARIANTS)[number]\n\n/**\n * The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all\n * take the `location` hero), so this stays a hero-level grouping.\n */\nexport const HERO_FAMILIES = ['home', 'service', 'location'] as const\nexport type HeroFamily = (typeof HERO_FAMILIES)[number]\n\n/**\n * A service's job mode (v2 §2.2), as the site's strategy decision card records it (`SiteJobMode` in\n * `site-core/strategy`): which action is styled primary. Never the order.\n */\nexport const HERO_JOB_MODES = SITE_JOB_MODES\nexport type HeroJobMode = SiteJobMode\n\nexport type HeroSlot =\n | 'root'\n | 'inner'\n | 'content'\n | 'crumbs'\n | 'eyebrow'\n | 'h1'\n | 'summary'\n | 'actions'\n | 'phone'\n | 'quote'\n | 'checklist'\n | 'rating'\n | 'badges'\n | 'media'\n | 'caption'\n | 'scrim'\n | 'form'\n\nexport interface HeroLayout {\n /** `split`: the photo's side on desktop (default `end`). */\n readonly mediaSide?: 'start' | 'end'\n /** Default `tall` on the home page, `standard` elsewhere. */\n readonly height?: 'standard' | 'tall'\n /** The trust checklist's columns (default 2). */\n readonly checklistColumns?: 1 | 2\n /**\n * The checklist's ticks (3.15.0): `icon` (the default) is the sprite's check; `rule` ships no svg, only a mark the\n * stylesheet draws as a short rule (`TickList`'s `layout.tick`), about 100 bytes less a fact.\n */\n readonly checklistTick?: 'icon' | 'rule'\n}\n\nexport type HeroSkin = SkinEntry<HeroSlot, HeroVariant, HeroLayout>\n\n/** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */\nexport const HERO_FORM_ID = 'quote-hero'\n/** The most facts the trust checklist holds (v2 S1: three to six). */\nexport const HERO_CHECKLIST_MAX = 6\n/** The offer line's word limit (v2 §9.2). */\nexport const HERO_SUMMARY_MAX_WORDS = 25\n\nexport interface HeroProps extends HeroSkin {\n readonly family: HeroFamily\n /** The H1 text: the recorded H1 on live pages, the title builder's on new ones (\"Tree Removal Castle Hill\"). */\n readonly title: string\n /** The one offer line under the H1, 25 words or fewer. */\n readonly summary: string\n /** A short label above the H1. */\n readonly eyebrow?: string\n /** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). Service and location pages only. */\n readonly breadcrumbs?: ReactNode\n /** The trust checklist: verified facts only (licence, insurance, rating source, years), at most six. */\n readonly checklist: readonly TickItem[]\n /**\n * Google's average and total from the reviews feed (`prepareBusinessProfileReviews`), shown as the shared\n * `RatingChip` (`data-bl=\"rating-chip\"`, the `rating` class on its root, like `phone` and `quote`) after the\n * checklist, linked to the Business Profile. Never typed by hand: without a feed average and a total of 1 or more,\n * nothing renders.\n */\n readonly rating?: RatingChipData | null\n /** Partner badges or logos held and verified (a `LogoStrip`). */\n readonly badges?: ReactNode\n /** The best genuine photo of this page's service; on a location page the nearest job's. From `media.ts`. */\n readonly media: UiMedia\n /** \"{what}, {suburb}, {month year}\" for a genuine job photo, for example on a location page. */\n readonly caption?: string\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call\". */\n readonly phoneLabel?: string\n /**\n * The site's recorded quote label (C5). `href` defaults to the hero's own form (`#quote-hero`); without a form it\n * is required (for example `/contact#quote`).\n */\n readonly quote: { readonly label: string; readonly href?: string }\n /** The site's bound quote form: `<QuoteForm formId=\"quote-hero\" />`. Required on money pages (C1). */\n readonly form?: ReactNode\n /** The page's service job mode (default `planned`): `urgent` styles the phone link primary. */\n readonly jobMode?: HeroJobMode\n /** The H1's id (default `hero-title`). */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n /** Signature art, rendered `aria-hidden` (Superior's waterline, Absolute's grass edge). */\n readonly decoration?: ReactNode\n}\n\nconst words = (text: string) => text.trim().split(/\\s+/).filter(Boolean).length\n\nfunction assertHero({ title, summary, checklist, media, caption, quote, form }: HeroProps): void {\n const problems: string[] = []\n if (title.trim() === '') problems.push('the H1 is empty')\n if (words(summary) > HERO_SUMMARY_MAX_WORDS) problems.push(`the offer line has ${words(summary)} words (${HERO_SUMMARY_MAX_WORDS} at most)`)\n if (checklist.length > HERO_CHECKLIST_MAX) problems.push(`the checklist has ${checklist.length} facts (${HERO_CHECKLIST_MAX} at most)`)\n if (media.provenance === 'illustrative-ai') problems.push(`${media.src} is an AI illustration; the hero shows real work or licensed stock`)\n if (caption && media.provenance !== 'genuine') problems.push(`a job caption needs a genuine photo (${media.src} is ${media.provenance})`)\n if (!form && !quote.href) problems.push('without a form, quote.href must name the quote form, for example /contact#quote')\n if (problems.length > 0) throw new Error(`Hero \"${title}\": ${problems.join('; ')}.`)\n}\n\nexport function Hero(props: HeroProps) {\n assertHero(props)\n const {\n family,\n title,\n summary,\n eyebrow,\n breadcrumbs,\n checklist,\n rating,\n badges,\n media,\n caption,\n phone,\n phoneLabel,\n quote,\n form,\n jobMode = 'planned',\n headingId = 'hero-title',\n tone,\n decoration,\n variant = 'split',\n layout,\n classes,\n } = props\n const primary = jobMode === 'urgent' ? 'phone' : 'quote'\n const crumbs = family === 'home' ? null : breadcrumbs\n return (\n <Section\n id=\"hero\"\n labelledBy={headingId}\n tone={tone}\n variant={variant}\n classes={classes}\n decoration={decoration}\n phonePlacement=\"hero\"\n hooks={{\n 'data-family': family,\n 'data-job-mode': jobMode,\n 'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),\n 'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,\n 'data-form': form ? '' : undefined,\n }}\n >\n <div {...slot(classes, 'content')}>\n {crumbs ? <div {...slot(classes, 'crumbs')}>{crumbs}</div> : null}\n {eyebrow ? <p {...slot(classes, 'eyebrow')}>{eyebrow}</p> : null}\n <h1 id={headingId} {...slot(classes, 'h1')}>\n {title}\n </h1>\n <p {...slot(classes, 'summary')}>{summary}</p>\n <div {...slot(classes, 'actions')} data-bl-call-path=\"\" data-primary={primary}>\n <PhoneLink phone={phone} placement=\"hero\" label={phoneLabel} showIcon variant=\"button\" classes={{ root: classes?.phone }} />\n <QuoteButton\n label={quote.label}\n href={quote.href ?? `#${HERO_FORM_ID}`}\n placement=\"hero\"\n ctaId=\"quote-hero\"\n variant={primary === 'quote' ? 'primary' : 'secondary'}\n classes={{ root: classes?.quote }}\n />\n </div>\n {checklist.length > 0 ? (\n <div {...slot(classes, 'checklist')}>\n <TickList items={checklist} layout={{ columns: layout?.checklistColumns ?? 2, ...(layout?.checklistTick ? { tick: layout.checklistTick } : {}) }} />\n </div>\n ) : null}\n {hasGoogleRating(rating) ? <RatingChip reviews={rating} classes={{ root: classes?.rating }} /> : null}\n {badges ? <div {...slot(classes, 'badges')}>{badges}</div> : null}\n </div>\n <div {...slot(classes, 'media')}>\n <MediaFrame\n media={media}\n ratio=\"4:3\"\n sizes={variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw'}\n caption={caption}\n priority\n classes={{ caption: classes?.caption }}\n />\n {variant === 'full-bleed' ? <div {...slot(classes, 'scrim')} aria-hidden=\"true\" /> : null}\n </div>\n {form ? <div {...slot(classes, 'form')}>{form}</div> : null}\n </Section>\n )\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js';
2
- export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, splitReviewText, type ReviewsBandHeadingProps, type ReviewsBandItemProps, type ReviewsBandLayout, type ReviewsBandProps, type ReviewsBandRootProps, type ReviewsBandSkin, type ReviewsBandSlot, type ReviewsBandSummaryProps, type ReviewsBandVariant, } from './reviews-band.js';
2
+ export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_SKIP_REASON, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, reviewsBandReady, splitReviewText, type ReviewsBandHeadingProps, type ReviewsBandItemProps, type ReviewsBandLayout, type ReviewsBandProps, type ReviewsBandRootProps, type ReviewsBandSkin, type ReviewsBandSlot, type ReviewsBandSummaryProps, type ReviewsBandVariant, } from './reviews-band.js';
3
3
  export { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand, type CTABandLayout, type CTABandProps, type CTABandSkin, type CTABandSlot, type CTABandVariant } from './cta-band.js';
4
4
  export { SITE_FOOTER_VARIANTS, SiteFooter, type SiteFooterColumn, type SiteFooterLicence, type SiteFooterProps, type SiteFooterSkin, type SiteFooterSlot, type SiteFooterVariant, } from './site-footer.js';
5
5
  export { Accordion, type AccordionItem, type AccordionProps, type AccordionSkin, type AccordionSlot } from './accordion.js';
@@ -12,4 +12,17 @@ export { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type Conta
12
12
  export { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js';
13
13
  export { HERO_CHECKLIST_MAX, HERO_FAMILIES, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_VARIANTS, Hero, type HeroFamily, type HeroJobMode, type HeroLayout, type HeroProps, type HeroSkin, type HeroSlot, type HeroVariant, } from './hero.js';
14
14
  export { SERVICES_GRID_CARD_LIMIT, SERVICES_GRID_VARIANTS, ServiceCard, ServicesGrid, defaultImageSizes, type ServiceCardData, type ServiceCardProps, type ServiceCardSlot, type ServicesGridLayout, type ServicesGridProps, type ServicesGridRepeatedSlot, type ServicesGridSkin, type ServicesGridSlot, type ServicesGridVariant, } from './services-grid.js';
15
+ export { CouncilRecordLines, CouncilRules, councilFeeLine, councilRecordReady, councilRulesReady, formatCouncilFeeAmount, type CouncilRecordSlot, type CouncilRuleWording, type CouncilRulesProps, type CouncilRulesRepeatedSlot, type CouncilRulesSkin, type CouncilRulesSlot, } from './council-rules.js';
16
+ export { LOCAL_FACTS_VARIANTS, LocalFacts, localFactsReady, type LocalFact, type LocalFactsPermit, type LocalFactsProps, type LocalFactsRepeatedSlot, type LocalFactsSkin, type LocalFactsSlot, type LocalFactsVariant, } from './local-facts.js';
17
+ export { NEARBY_JOBS_VARIANTS, NearbyJobs, nearbyJobsReady, type NearbyJobsLayout, type NearbyJobsProps, type NearbyJobsRepeatedSlot, type NearbyJobsSkin, type NearbyJobsSlot, type NearbyJobsVariant, } from './nearby-jobs.js';
18
+ export { NEARBY_AREAS_DEFAULT, NEARBY_AREAS_LIMIT, NEARBY_AREAS_VARIANTS, NearbyAreas, nearbyAreaLinks, nearbyAreasReady, type NearbyAreasLayout, type NearbyAreasProps, type NearbyAreasSkin, type NearbyAreasSlot, type NearbyAreasVariant, } from './nearby-areas.js';
19
+ export { AREA_MAP_VARIANTS, AreaMap, areaMapPlaces, areaMapReady, type AreaMapData, type AreaMapLayout, type AreaMapProps, type AreaMapSkin, type AreaMapSlot, type AreaMapVariant, } from './area-map.js';
20
+ export { COVERAGE_VARIANTS, Coverage, coverageHubLabel, coverageReady, type CoverageGroup, type CoverageHub, type CoverageLayout, type CoverageProps, type CoverageRepeatedSlot, type CoverageSkin, type CoverageSlot, type CoverageVariant, } from './coverage.js';
21
+ export { RELATED_SERVICES_LIMIT, RELATED_SERVICES_VARIANTS, RelatedServices, relatedServicesReady, type RelatedServicesLayout, type RelatedServicesProps, type RelatedServicesRepeatedSlot, type RelatedServicesSkin, type RelatedServicesSlot, type RelatedServicesVariant, } from './related-services.js';
22
+ export { PROJECTS_CARD_LIMIT, Projects, projectCardFromJob, projectsReady, type ProjectCardData, type ProjectsLayout, type ProjectsProps, type ProjectsSkin, type ProjectsSlot, } from './projects.js';
23
+ export { Gallery, galleryReady, type GalleryLayout, type GalleryPhoto, type GalleryProps, type GalleryRepeatedSlot, type GallerySkin, type GallerySlot, } from './gallery.js';
24
+ export { BeforeAfter, beforeAfterReady, type BeforeAfterLayout, type BeforeAfterPair, type BeforeAfterProps, type BeforeAfterRepeatedSlot, type BeforeAfterSkin, type BeforeAfterSlot, } from './before-after.js';
25
+ export { LICENCES_VARIANTS, Licences, licencesContent, licencesReady, type LicencesBusiness, type LicencesLabels, type LicencesProps, type LicencesRepeatedSlot, type LicencesSkin, type LicencesSlot, type LicencesVariant, } from './licences.js';
26
+ export { QUOTE_CHECKLIST_MAX, QuoteChecklist, type QuoteChecklistLayout, type QuoteChecklistProps, type QuoteChecklistSkin, type QuoteChecklistSlot, } from './quote-checklist.js';
27
+ export { EmergencyBand, emergencyBandReady, renderableAfterHours, type EmergencyBandBusiness, type EmergencyBandLayout, type EmergencyBandProps, type EmergencyBandSkin, type EmergencyBandSlot, } from './emergency-band.js';
15
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACnJ,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAC/K,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC3H,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACxI,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC/H,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAC3J,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAChJ,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClI,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  export { hasGoogleRating, RatingChip } from './rating-chip.js';
2
- export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, splitReviewText, } from './reviews-band.js';
2
+ export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_SKIP_REASON, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, reviewsBandReady, splitReviewText, } from './reviews-band.js';
3
3
  export { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand } from './cta-band.js';
4
4
  export { SITE_FOOTER_VARIANTS, SiteFooter, } from './site-footer.js';
5
5
  export { Accordion } from './accordion.js';
@@ -12,4 +12,17 @@ export { ContactBlock } from './contact-block.js';
12
12
  export { SectionSequence, serialiseSectionManifest } from './section-sequence.js';
13
13
  export { HERO_CHECKLIST_MAX, HERO_FAMILIES, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_VARIANTS, Hero, } from './hero.js';
14
14
  export { SERVICES_GRID_CARD_LIMIT, SERVICES_GRID_VARIANTS, ServiceCard, ServicesGrid, defaultImageSizes, } from './services-grid.js';
15
+ export { CouncilRecordLines, CouncilRules, councilFeeLine, councilRecordReady, councilRulesReady, formatCouncilFeeAmount, } from './council-rules.js';
16
+ export { LOCAL_FACTS_VARIANTS, LocalFacts, localFactsReady, } from './local-facts.js';
17
+ export { NEARBY_JOBS_VARIANTS, NearbyJobs, nearbyJobsReady, } from './nearby-jobs.js';
18
+ export { NEARBY_AREAS_DEFAULT, NEARBY_AREAS_LIMIT, NEARBY_AREAS_VARIANTS, NearbyAreas, nearbyAreaLinks, nearbyAreasReady, } from './nearby-areas.js';
19
+ export { AREA_MAP_VARIANTS, AreaMap, areaMapPlaces, areaMapReady, } from './area-map.js';
20
+ export { COVERAGE_VARIANTS, Coverage, coverageHubLabel, coverageReady, } from './coverage.js';
21
+ export { RELATED_SERVICES_LIMIT, RELATED_SERVICES_VARIANTS, RelatedServices, relatedServicesReady, } from './related-services.js';
22
+ export { PROJECTS_CARD_LIMIT, Projects, projectCardFromJob, projectsReady, } from './projects.js';
23
+ export { Gallery, galleryReady, } from './gallery.js';
24
+ export { BeforeAfter, beforeAfterReady, } from './before-after.js';
25
+ export { LICENCES_VARIANTS, Licences, licencesContent, licencesReady, } from './licences.js';
26
+ export { QUOTE_CHECKLIST_MAX, QuoteChecklist, } from './quote-checklist.js';
27
+ export { EmergencyBand, emergencyBandReady, renderableAfterHours, } from './emergency-band.js';
15
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAuF,MAAM,kBAAkB,CAAA;AACnJ,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,GAUhB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,EAAkG,MAAM,eAAe,CAAA;AAC/K,OAAO,EACL,oBAAoB,EACpB,UAAU,GAOX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAmF,MAAM,gBAAgB,CAAA;AAC3H,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAgF,MAAM,WAAW,CAAA;AACxI,OAAO,EAAE,UAAU,EAAoF,MAAM,kBAAkB,CAAA;AAC/H,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAMhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,eAAe,GAQhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAA0F,MAAM,kBAAkB,CAAA;AAC3J,OAAO,EAAE,YAAY,EAAiG,MAAM,oBAAoB,CAAA;AAChJ,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAmD,MAAM,uBAAuB,CAAA;AAClI,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,IAAI,GAQL,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,YAAY,EACZ,iBAAiB,GAUlB,MAAM,oBAAoB,CAAA","sourcesContent":["export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js'\nexport {\n REVIEWS_BAND_HEADING_ID,\n REVIEWS_BAND_VARIANTS,\n ReviewsBand,\n ReviewsBandHeading,\n ReviewsBandItem,\n ReviewsBandList,\n ReviewsBandRoot,\n ReviewsBandSummary,\n splitReviewText,\n type ReviewsBandHeadingProps,\n type ReviewsBandItemProps,\n type ReviewsBandLayout,\n type ReviewsBandProps,\n type ReviewsBandRootProps,\n type ReviewsBandSkin,\n type ReviewsBandSlot,\n type ReviewsBandSummaryProps,\n type ReviewsBandVariant,\n} from './reviews-band.js'\nexport { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand, type CTABandLayout, type CTABandProps, type CTABandSkin, type CTABandSlot, type CTABandVariant } from './cta-band.js'\nexport {\n SITE_FOOTER_VARIANTS,\n SiteFooter,\n type SiteFooterColumn,\n type SiteFooterLicence,\n type SiteFooterProps,\n type SiteFooterSkin,\n type SiteFooterSlot,\n type SiteFooterVariant,\n} from './site-footer.js'\nexport { Accordion, type AccordionItem, type AccordionProps, type AccordionSkin, type AccordionSlot } from './accordion.js'\nexport { FAQS_VARIANTS, FAQs, faqsReady, type FAQItem, type FAQsProps, type FAQsSkin, type FAQsSlot, type FAQsVariant } from './faqs.js'\nexport { PriceTable, type PriceLabels, type PriceTableProps, type PriceTableSkin, type PriceTableSlot } from './price-table.js'\nexport {\n PRICE_GUIDE_VARIANTS,\n PriceGuide,\n priceGuideReady,\n type PriceFactor,\n type PriceGuideProps,\n type PriceGuideSkin,\n type PriceGuideSlot,\n type PriceGuideVariant,\n} from './price-guide.js'\nexport {\n PROOF_STRIP_MINIMUM,\n PROOF_STRIP_VARIANTS,\n ProofStrip,\n proofStripReady,\n type ProofFactItem,\n type ProofItem,\n type ProofRatingItem,\n type ProofStripProps,\n type ProofStripSkin,\n type ProofStripSlot,\n type ProofStripVariant,\n} from './proof-strip.js'\nexport { PAGE_HEADER_VARIANTS, PageHeader, type PageHeaderProps, type PageHeaderSkin, type PageHeaderSlot, type PageHeaderVariant } from './page-header.js'\nexport { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type ContactBlockSlot, type ContactDetailLines } from './contact-block.js'\nexport { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js'\nexport {\n HERO_CHECKLIST_MAX,\n HERO_FAMILIES,\n HERO_FORM_ID,\n HERO_JOB_MODES,\n HERO_SUMMARY_MAX_WORDS,\n HERO_VARIANTS,\n Hero,\n type HeroFamily,\n type HeroJobMode,\n type HeroLayout,\n type HeroProps,\n type HeroSkin,\n type HeroSlot,\n type HeroVariant,\n} from './hero.js'\nexport {\n SERVICES_GRID_CARD_LIMIT,\n SERVICES_GRID_VARIANTS,\n ServiceCard,\n ServicesGrid,\n defaultImageSizes,\n type ServiceCardData,\n type ServiceCardProps,\n type ServiceCardSlot,\n type ServicesGridLayout,\n type ServicesGridProps,\n type ServicesGridRepeatedSlot,\n type ServicesGridSkin,\n type ServicesGridSlot,\n type ServicesGridVariant,\n} from './services-grid.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAuF,MAAM,kBAAkB,CAAA;AACnJ,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,GAUhB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,EAAkG,MAAM,eAAe,CAAA;AAC/K,OAAO,EACL,oBAAoB,EACpB,UAAU,GAOX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAmF,MAAM,gBAAgB,CAAA;AAC3H,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAgF,MAAM,WAAW,CAAA;AACxI,OAAO,EAAE,UAAU,EAAoF,MAAM,kBAAkB,CAAA;AAC/H,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAMhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,eAAe,GAQhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAA0F,MAAM,kBAAkB,CAAA;AAC3J,OAAO,EAAE,YAAY,EAAiG,MAAM,oBAAoB,CAAA;AAChJ,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAmD,MAAM,uBAAuB,CAAA;AAClI,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,IAAI,GAQL,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,YAAY,EACZ,iBAAiB,GAUlB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GAOvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAQhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAOhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,gBAAgB,GAMjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,YAAY,GAOb,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,gBAAgB,EAChB,aAAa,GASd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,GAOrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,kBAAkB,EAClB,aAAa,GAMd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,OAAO,EACP,YAAY,GAOb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,WAAW,EACX,gBAAgB,GAOjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,aAAa,GAQd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,mBAAmB,EACnB,cAAc,GAKf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,oBAAoB,GAMrB,MAAM,qBAAqB,CAAA","sourcesContent":["export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js'\nexport {\n REVIEWS_BAND_HEADING_ID,\n REVIEWS_BAND_SKIP_REASON,\n REVIEWS_BAND_VARIANTS,\n ReviewsBand,\n ReviewsBandHeading,\n ReviewsBandItem,\n ReviewsBandList,\n ReviewsBandRoot,\n ReviewsBandSummary,\n reviewsBandReady,\n splitReviewText,\n type ReviewsBandHeadingProps,\n type ReviewsBandItemProps,\n type ReviewsBandLayout,\n type ReviewsBandProps,\n type ReviewsBandRootProps,\n type ReviewsBandSkin,\n type ReviewsBandSlot,\n type ReviewsBandSummaryProps,\n type ReviewsBandVariant,\n} from './reviews-band.js'\nexport { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand, type CTABandLayout, type CTABandProps, type CTABandSkin, type CTABandSlot, type CTABandVariant } from './cta-band.js'\nexport {\n SITE_FOOTER_VARIANTS,\n SiteFooter,\n type SiteFooterColumn,\n type SiteFooterLicence,\n type SiteFooterProps,\n type SiteFooterSkin,\n type SiteFooterSlot,\n type SiteFooterVariant,\n} from './site-footer.js'\nexport { Accordion, type AccordionItem, type AccordionProps, type AccordionSkin, type AccordionSlot } from './accordion.js'\nexport { FAQS_VARIANTS, FAQs, faqsReady, type FAQItem, type FAQsProps, type FAQsSkin, type FAQsSlot, type FAQsVariant } from './faqs.js'\nexport { PriceTable, type PriceLabels, type PriceTableProps, type PriceTableSkin, type PriceTableSlot } from './price-table.js'\nexport {\n PRICE_GUIDE_VARIANTS,\n PriceGuide,\n priceGuideReady,\n type PriceFactor,\n type PriceGuideProps,\n type PriceGuideSkin,\n type PriceGuideSlot,\n type PriceGuideVariant,\n} from './price-guide.js'\nexport {\n PROOF_STRIP_MINIMUM,\n PROOF_STRIP_VARIANTS,\n ProofStrip,\n proofStripReady,\n type ProofFactItem,\n type ProofItem,\n type ProofRatingItem,\n type ProofStripProps,\n type ProofStripSkin,\n type ProofStripSlot,\n type ProofStripVariant,\n} from './proof-strip.js'\nexport { PAGE_HEADER_VARIANTS, PageHeader, type PageHeaderProps, type PageHeaderSkin, type PageHeaderSlot, type PageHeaderVariant } from './page-header.js'\nexport { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type ContactBlockSlot, type ContactDetailLines } from './contact-block.js'\nexport { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js'\nexport {\n HERO_CHECKLIST_MAX,\n HERO_FAMILIES,\n HERO_FORM_ID,\n HERO_JOB_MODES,\n HERO_SUMMARY_MAX_WORDS,\n HERO_VARIANTS,\n Hero,\n type HeroFamily,\n type HeroJobMode,\n type HeroLayout,\n type HeroProps,\n type HeroSkin,\n type HeroSlot,\n type HeroVariant,\n} from './hero.js'\nexport {\n SERVICES_GRID_CARD_LIMIT,\n SERVICES_GRID_VARIANTS,\n ServiceCard,\n ServicesGrid,\n defaultImageSizes,\n type ServiceCardData,\n type ServiceCardProps,\n type ServiceCardSlot,\n type ServicesGridLayout,\n type ServicesGridProps,\n type ServicesGridRepeatedSlot,\n type ServicesGridSkin,\n type ServicesGridSlot,\n type ServicesGridVariant,\n} from './services-grid.js'\nexport {\n CouncilRecordLines,\n CouncilRules,\n councilFeeLine,\n councilRecordReady,\n councilRulesReady,\n formatCouncilFeeAmount,\n type CouncilRecordSlot,\n type CouncilRuleWording,\n type CouncilRulesProps,\n type CouncilRulesRepeatedSlot,\n type CouncilRulesSkin,\n type CouncilRulesSlot,\n} from './council-rules.js'\nexport {\n LOCAL_FACTS_VARIANTS,\n LocalFacts,\n localFactsReady,\n type LocalFact,\n type LocalFactsPermit,\n type LocalFactsProps,\n type LocalFactsRepeatedSlot,\n type LocalFactsSkin,\n type LocalFactsSlot,\n type LocalFactsVariant,\n} from './local-facts.js'\nexport {\n NEARBY_JOBS_VARIANTS,\n NearbyJobs,\n nearbyJobsReady,\n type NearbyJobsLayout,\n type NearbyJobsProps,\n type NearbyJobsRepeatedSlot,\n type NearbyJobsSkin,\n type NearbyJobsSlot,\n type NearbyJobsVariant,\n} from './nearby-jobs.js'\nexport {\n NEARBY_AREAS_DEFAULT,\n NEARBY_AREAS_LIMIT,\n NEARBY_AREAS_VARIANTS,\n NearbyAreas,\n nearbyAreaLinks,\n nearbyAreasReady,\n type NearbyAreasLayout,\n type NearbyAreasProps,\n type NearbyAreasSkin,\n type NearbyAreasSlot,\n type NearbyAreasVariant,\n} from './nearby-areas.js'\nexport {\n AREA_MAP_VARIANTS,\n AreaMap,\n areaMapPlaces,\n areaMapReady,\n type AreaMapData,\n type AreaMapLayout,\n type AreaMapProps,\n type AreaMapSkin,\n type AreaMapSlot,\n type AreaMapVariant,\n} from './area-map.js'\nexport {\n COVERAGE_VARIANTS,\n Coverage,\n coverageHubLabel,\n coverageReady,\n type CoverageGroup,\n type CoverageHub,\n type CoverageLayout,\n type CoverageProps,\n type CoverageRepeatedSlot,\n type CoverageSkin,\n type CoverageSlot,\n type CoverageVariant,\n} from './coverage.js'\nexport {\n RELATED_SERVICES_LIMIT,\n RELATED_SERVICES_VARIANTS,\n RelatedServices,\n relatedServicesReady,\n type RelatedServicesLayout,\n type RelatedServicesProps,\n type RelatedServicesRepeatedSlot,\n type RelatedServicesSkin,\n type RelatedServicesSlot,\n type RelatedServicesVariant,\n} from './related-services.js'\nexport {\n PROJECTS_CARD_LIMIT,\n Projects,\n projectCardFromJob,\n projectsReady,\n type ProjectCardData,\n type ProjectsLayout,\n type ProjectsProps,\n type ProjectsSkin,\n type ProjectsSlot,\n} from './projects.js'\nexport {\n Gallery,\n galleryReady,\n type GalleryLayout,\n type GalleryPhoto,\n type GalleryProps,\n type GalleryRepeatedSlot,\n type GallerySkin,\n type GallerySlot,\n} from './gallery.js'\nexport {\n BeforeAfter,\n beforeAfterReady,\n type BeforeAfterLayout,\n type BeforeAfterPair,\n type BeforeAfterProps,\n type BeforeAfterRepeatedSlot,\n type BeforeAfterSkin,\n type BeforeAfterSlot,\n} from './before-after.js'\nexport {\n LICENCES_VARIANTS,\n Licences,\n licencesContent,\n licencesReady,\n type LicencesBusiness,\n type LicencesLabels,\n type LicencesProps,\n type LicencesRepeatedSlot,\n type LicencesSkin,\n type LicencesSlot,\n type LicencesVariant,\n} from './licences.js'\nexport {\n QUOTE_CHECKLIST_MAX,\n QuoteChecklist,\n type QuoteChecklistLayout,\n type QuoteChecklistProps,\n type QuoteChecklistSkin,\n type QuoteChecklistSlot,\n} from './quote-checklist.js'\nexport {\n EmergencyBand,\n emergencyBandReady,\n renderableAfterHours,\n type EmergencyBandBusiness,\n type EmergencyBandLayout,\n type EmergencyBandProps,\n type EmergencyBandSkin,\n type EmergencyBandSlot,\n} from './emergency-band.js'\n"]}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `Licences` (`licences`, 3.15.0): the business's credentials as a ruled ledger, read straight from the site's business
3
+ * facts (`site-core/business`), so the page can only say what the evidence allows. Server component.
4
+ *
5
+ * Variants (matrix part B):
6
+ * - `licence` (licensed trades): each current licence as the register shows it (its name, number, licensee, classes
7
+ * with their conditions and the register link), then the insurance lines;
8
+ * - `qualifications` (trades with no licence, such as tree work): the qualifications as the business may state them,
9
+ * without a person's name ("AQF Level 3-qualified arborist"), then the insurance lines.
10
+ *
11
+ * Locked:
12
+ * - licences come only from a verified `registration.licences` fact whose evidence allows website copy and has not
13
+ * expired, less any expired licence (`renderableLicences`); a licence number is never typed in by hand;
14
+ * - qualifications and insurance come only through `renderableCredentials(credentials, 'website-copy')`: evidenced
15
+ * for the surface, not expired, and no policy past its end date. The amount is `formatInsuranceAmount`'s ("$20 million");
16
+ * - each line carries its K12-slots hook (`data-bl-slot="licence" | "qualifications" | "insurance"`); a line the
17
+ * variant expects but the evidence doesn't allow is left out and listed in `data-bl-slot-skip` with its reason;
18
+ * with nothing to show, the section renders nothing;
19
+ * - no default visible copy: the row labels, cover names and register link text are the site's (`labels`). A line
20
+ * with no label fails the build rather than render unlabelled.
21
+ */
22
+ import type { ReactNode } from 'react';
23
+ import { type BrandlabBusiness, type BrandlabInsuranceCover, type BrandlabLicence, type BrandlabRenderableCredentials } from '@brandlab-digital/site-core/business';
24
+ import { type BrandlabSectionReadiness, type BrandlabSectionSlotSkip } from '@brandlab-digital/site-core/section-order';
25
+ import { type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js';
26
+ import type { RepeatedSlotOf } from '../repeated-slots.js';
27
+ import { type SkinEntry } from '../skin.js';
28
+ export declare const LICENCES_VARIANTS: readonly ["licence", "qualifications"];
29
+ export type LicencesVariant = (typeof LICENCES_VARIANTS)[number];
30
+ /** Rendered once per line: styled from the skin stylesheet, never by class. */
31
+ export type LicencesRepeatedSlot = RepeatedSlotOf<'licences'>;
32
+ export type LicencesSlot = SectionSlot | SectionHeadingSlot | 'list' | LicencesRepeatedSlot;
33
+ export type LicencesSkin = SkinEntry<LicencesSlot, LicencesVariant, Record<never, never>, LicencesRepeatedSlot>;
34
+ /** The site's words for the ledger. Each is needed only when its line renders. */
35
+ export interface LicencesLabels {
36
+ /** The qualifications row's label, for example "Qualifications". */
37
+ readonly qualifications?: string;
38
+ /** The insurance row's label, for example "Insurance". */
39
+ readonly insurance?: string;
40
+ /** Each cover's name, for example `{ 'public-liability': 'Public liability' }`. */
41
+ readonly covers?: Readonly<Partial<Record<BrandlabInsuranceCover, string>>>;
42
+ /** The register link's text, for example "Check the register". Left out, no register link renders. */
43
+ readonly register?: string;
44
+ }
45
+ /** The business facts the ledger reads: `business.registration` and `business.credentials`. */
46
+ export type LicencesBusiness = Pick<BrandlabBusiness, 'registration' | 'credentials'>;
47
+ export interface LicencesProps extends LicencesSkin {
48
+ /** The H2, for example "Qualified and insured". */
49
+ readonly title: ReactNode;
50
+ readonly eyebrow?: ReactNode;
51
+ readonly lead?: ReactNode;
52
+ readonly business: LicencesBusiness;
53
+ readonly labels: LicencesLabels;
54
+ /** The day the page is built (tests pin it). */
55
+ readonly asOf?: Date;
56
+ /** Defaults to `licences-heading`. */
57
+ readonly headingId?: string;
58
+ /** Set by `SectionSequence`. */
59
+ readonly tone?: SectionTone;
60
+ }
61
+ interface LedgerContent {
62
+ readonly licences: readonly BrandlabLicence[];
63
+ readonly credentials: BrandlabRenderableCredentials;
64
+ readonly skips: readonly BrandlabSectionSlotSkip[];
65
+ }
66
+ /** What the evidence lets the variant show today, and the expected lines it has to leave out. */
67
+ export declare function licencesContent(business: LicencesBusiness, variant: LicencesVariant, asOf?: Date): LedgerContent;
68
+ /** The section's guard for `SectionSequence`: skipped when the evidence allows no line at all. */
69
+ export declare function licencesReady({ business, variant, asOf }: Pick<LicencesProps, 'business' | 'variant' | 'asOf'>): BrandlabSectionReadiness;
70
+ export declare function Licences({ title, eyebrow, lead, business, labels, asOf, headingId, tone, variant, classes }: LicencesProps): import("react/jsx-runtime").JSX.Element | null;
71
+ export {};
72
+ //# sourceMappingURL=licences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licences.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/licences.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { formatInsuranceAmount, renderableCredentials, renderableLicences, resolveVerifiedFactForSurface, } from '@brandlab-digital/site-core/business';
3
+ import { SECTION_READY, SECTION_SLOT_ATTRIBUTE, SECTION_SLOT_SKIP_ATTRIBUTE, formatSectionSlotSkips, } from '@brandlab-digital/site-core/section-order';
4
+ import { Section, SectionHeading } from '../primitives/layout.js';
5
+ import { Anchor } from '../primitives/links.js';
6
+ import { repeatedSlot, slot } from '../skin.js';
7
+ export const LICENCES_VARIANTS = ['licence', 'qualifications'];
8
+ /** What the evidence lets the variant show today, and the expected lines it has to leave out. */
9
+ export function licencesContent(business, variant, asOf = new Date()) {
10
+ const credentials = renderableCredentials(business.credentials, 'website-copy', asOf);
11
+ const licences = variant === 'licence' ? renderableLicences(resolveVerifiedFactForSurface(business.registration.licences, 'website-copy', asOf) ?? [], asOf) : [];
12
+ const skips = [];
13
+ if (variant === 'licence' && licences.length === 0)
14
+ skips.push({ slot: 'licence', reason: 'no-current-licence' });
15
+ if (variant === 'qualifications' && credentials.qualifications.length === 0)
16
+ skips.push({ slot: 'qualifications', reason: 'no-evidenced-qualification' });
17
+ if (variant === 'qualifications' && credentials.insurance.length === 0)
18
+ skips.push({ slot: 'insurance', reason: 'no-current-insurance' });
19
+ return { licences, credentials: variant === 'qualifications' ? credentials : { ...credentials, qualifications: [] }, skips };
20
+ }
21
+ function hasLines({ licences, credentials }) {
22
+ return licences.length + credentials.qualifications.length + credentials.insurance.length > 0;
23
+ }
24
+ /** The section's guard for `SectionSequence`: skipped when the evidence allows no line at all. */
25
+ export function licencesReady({ business, variant = 'licence', asOf }) {
26
+ return hasLines(licencesContent(business, variant, asOf)) ? SECTION_READY : { render: false, reason: 'no-evidenced-credentials' };
27
+ }
28
+ function required(value, name) {
29
+ if (!value?.trim())
30
+ throw new Error(`Licences: labels.${name} is needed to show this line, in the site's words.`);
31
+ return value;
32
+ }
33
+ function LicenceLine({ licence, registerLabel }) {
34
+ return (_jsxs("div", { ...repeatedSlot('row'), [SECTION_SLOT_ATTRIBUTE]: 'licence', children: [_jsx("dt", { ...repeatedSlot('label'), children: licence.name }), _jsxs("dd", { ...repeatedSlot('value'), children: [_jsx("span", { ...repeatedSlot('number'), children: licence.identifier }), licence.licensee ? (_jsxs(_Fragment, { children: [' ', _jsx("span", { ...repeatedSlot('holder'), children: licence.licensee })] })) : null] }), licence.classes && licence.classes.length > 0 ? (_jsx("dd", { ...repeatedSlot('classes'), children: _jsx("ul", { role: "list", children: licence.classes.map((entry) => (_jsxs("li", { ...repeatedSlot('class'), children: [entry.name, entry.conditions && entry.conditions.length > 0 ? `: ${entry.conditions.join('; ')}` : null] }, entry.name))) }) })) : null, licence.registerUrl && registerLabel ? (_jsx("dd", { ...repeatedSlot('register'), children: _jsx(Anchor, { href: licence.registerUrl, prefetch: false, hooks: {}, children: registerLabel }) })) : null] }));
35
+ }
36
+ export function Licences({ title, eyebrow, lead, business, labels, asOf, headingId = 'licences-heading', tone, variant = 'licence', classes }) {
37
+ const content = licencesContent(business, variant, asOf);
38
+ if (!hasLines(content))
39
+ return null;
40
+ const { licences, credentials, skips } = content;
41
+ const qualificationsLabel = credentials.qualifications.length > 0 ? required(labels.qualifications, 'qualifications') : undefined;
42
+ const insuranceLabel = credentials.insurance.length > 0 ? required(labels.insurance, 'insurance') : undefined;
43
+ const coverName = (cover) => required(labels.covers?.[cover], `covers['${cover}']`);
44
+ return (_jsxs(Section, { id: "licences", labelledBy: headingId, tone: tone, variant: variant, classes: classes, hooks: skips.length > 0 ? { [SECTION_SLOT_SKIP_ATTRIBUTE]: formatSectionSlotSkips(skips) } : undefined, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), _jsxs("dl", { ...slot(classes, 'list'), children: [licences.map((licence) => (_jsx(LicenceLine, { licence: licence, registerLabel: labels.register }, `${licence.name}-${licence.identifier}`))), qualificationsLabel ? (_jsxs("div", { ...repeatedSlot('row'), [SECTION_SLOT_ATTRIBUTE]: 'qualifications', children: [_jsx("dt", { ...repeatedSlot('label'), children: qualificationsLabel }), credentials.qualifications.map((entry) => (_jsx("dd", { ...repeatedSlot('value'), children: entry.title }, entry.title)))] })) : null, insuranceLabel ? (_jsxs("div", { ...repeatedSlot('row'), [SECTION_SLOT_ATTRIBUTE]: 'insurance', children: [_jsx("dt", { ...repeatedSlot('label'), children: insuranceLabel }), credentials.insurance.map((entry) => (_jsxs("dd", { ...repeatedSlot('value'), children: [_jsx("span", { ...repeatedSlot('cover'), children: coverName(entry.cover) }), ' ', _jsx("span", { ...repeatedSlot('amount'), children: formatInsuranceAmount(entry.amountAud) })] }, `${entry.cover}-${entry.amountAud}`)))] })) : null] })] }));
45
+ }
46
+ //# sourceMappingURL=licences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licences.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/licences.tsx"],"names":[],"mappings":";AAsBA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,6BAA6B,GAK9B,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,GAGvB,MAAM,2CAA2C,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,cAAc,EAA+D,MAAM,yBAAyB,CAAA;AAC9H,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAE/C,OAAO,EAAE,YAAY,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAE/D,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,gBAAgB,CAAU,CAAA;AA2CvE,iGAAiG;AACjG,MAAM,UAAU,eAAe,CAAC,QAA0B,EAAE,OAAwB,EAAE,OAAa,IAAI,IAAI,EAAE;IAC3G,MAAM,WAAW,GAAG,qBAAqB,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,EAAE,IAAI,CAAC,CAAA;IACrF,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,6BAA6B,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACjK,MAAM,KAAK,GAA8B,EAAE,CAAA;IAC3C,IAAI,OAAO,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAA;IACjH,IAAI,OAAO,KAAK,gBAAgB,IAAI,WAAW,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC,CAAA;IACzJ,IAAI,OAAO,KAAK,gBAAgB,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC,CAAA;IACzI,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,KAAK,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;AAC9H,CAAC;AAED,SAAS,QAAQ,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAiB;IACxD,OAAO,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,cAAc,CAAC,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAA;AAC/F,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAwD;IACzH,OAAO,QAAQ,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAA;AACnI,CAAC;AAED,SAAS,QAAQ,CAAC,KAAyB,EAAE,IAAY;IACvD,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,oDAAoD,CAAC,CAAA;IACjH,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,EAAmE;IAC9G,OAAO,CACL,kBAAS,YAAY,CAAC,KAAK,CAAC,EAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,aACrE,gBAAQ,YAAY,CAAC,OAAO,CAAC,YAAG,OAAO,CAAC,IAAI,GAAM,EAClD,iBAAQ,YAAY,CAAC,OAAO,CAAC,aAC3B,kBAAU,YAAY,CAAC,QAAQ,CAAC,YAAG,OAAO,CAAC,UAAU,GAAQ,EAC5D,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAClB,8BACG,GAAG,EACJ,kBAAU,YAAY,CAAC,QAAQ,CAAC,YAAG,OAAO,CAAC,QAAQ,GAAQ,IAC1D,CACJ,CAAC,CAAC,CAAC,IAAI,IACL,EACJ,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAC/C,gBAAQ,YAAY,CAAC,SAAS,CAAC,YAC7B,aAAI,IAAI,EAAC,MAAM,YACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC9B,iBAAyB,YAAY,CAAC,OAAO,CAAC,aAC3C,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAFrF,KAAK,CAAC,IAAI,CAGd,CACN,CAAC,GACC,GACF,CACN,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,WAAW,IAAI,aAAa,CAAC,CAAC,CAAC,CACtC,gBAAQ,YAAY,CAAC,UAAU,CAAC,YAC9B,KAAC,MAAM,IAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,YAC1D,aAAa,GACP,GACN,CACN,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAiB;IAC1J,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IACxD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IACnC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAChD,MAAM,mBAAmB,GAAG,WAAW,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjI,MAAM,cAAc,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7G,MAAM,SAAS,GAAG,CAAC,KAA6B,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,KAAK,IAAI,CAAC,CAAA;IAC3G,OAAO,CACL,MAAC,OAAO,IACN,EAAE,EAAC,UAAU,EACb,UAAU,EAAE,SAAS,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,2BAA2B,CAAC,EAAE,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,aAEtG,KAAC,cAAc,IAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAI,EAC/F,iBAAQ,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,aAC1B,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CACzB,KAAC,WAAW,IAA+C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,QAAQ,IAAzF,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE,CAAsD,CAChH,CAAC,EACD,mBAAmB,CAAC,CAAC,CAAC,CACrB,kBAAS,YAAY,CAAC,KAAK,CAAC,EAAQ,CAAC,sBAAsB,CAAC,EAAE,gBAAgB,aAC5E,gBAAQ,YAAY,CAAC,OAAO,CAAC,YAAG,mBAAmB,GAAM,EACxD,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACzC,gBAA0B,YAAY,CAAC,OAAO,CAAC,YAC5C,KAAK,CAAC,KAAK,IADL,KAAK,CAAC,KAAK,CAEf,CACN,CAAC,IACE,CACP,CAAC,CAAC,CAAC,IAAI,EACP,cAAc,CAAC,CAAC,CAAC,CAChB,kBAAS,YAAY,CAAC,KAAK,CAAC,EAAQ,CAAC,sBAAsB,CAAC,EAAE,WAAW,aACvE,gBAAQ,YAAY,CAAC,OAAO,CAAC,YAAG,cAAc,GAAM,EACnD,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACpC,iBAAkD,YAAY,CAAC,OAAO,CAAC,aACrE,kBAAU,YAAY,CAAC,OAAO,CAAC,YAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,GAAQ,EAAC,GAAG,EACpE,kBAAU,YAAY,CAAC,QAAQ,CAAC,YAAG,qBAAqB,CAAC,KAAK,CAAC,SAAS,CAAC,GAAQ,KAF1E,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,EAAE,CAGvC,CACN,CAAC,IACE,CACP,CAAC,CAAC,CAAC,IAAI,IACL,IACG,CACX,CAAA;AACH,CAAC","sourcesContent":["/**\n * `Licences` (`licences`, 3.15.0): the business's credentials as a ruled ledger, read straight from the site's business\n * facts (`site-core/business`), so the page can only say what the evidence allows. Server component.\n *\n * Variants (matrix part B):\n * - `licence` (licensed trades): each current licence as the register shows it (its name, number, licensee, classes\n * with their conditions and the register link), then the insurance lines;\n * - `qualifications` (trades with no licence, such as tree work): the qualifications as the business may state them,\n * without a person's name (\"AQF Level 3-qualified arborist\"), then the insurance lines.\n *\n * Locked:\n * - licences come only from a verified `registration.licences` fact whose evidence allows website copy and has not\n * expired, less any expired licence (`renderableLicences`); a licence number is never typed in by hand;\n * - qualifications and insurance come only through `renderableCredentials(credentials, 'website-copy')`: evidenced\n * for the surface, not expired, and no policy past its end date. The amount is `formatInsuranceAmount`'s (\"$20 million\");\n * - each line carries its K12-slots hook (`data-bl-slot=\"licence\" | \"qualifications\" | \"insurance\"`); a line the\n * variant expects but the evidence doesn't allow is left out and listed in `data-bl-slot-skip` with its reason;\n * with nothing to show, the section renders nothing;\n * - no default visible copy: the row labels, cover names and register link text are the site's (`labels`). A line\n * with no label fails the build rather than render unlabelled.\n */\nimport type { ReactNode } from 'react'\nimport {\n formatInsuranceAmount,\n renderableCredentials,\n renderableLicences,\n resolveVerifiedFactForSurface,\n type BrandlabBusiness,\n type BrandlabInsuranceCover,\n type BrandlabLicence,\n type BrandlabRenderableCredentials,\n} from '@brandlab-digital/site-core/business'\nimport {\n SECTION_READY,\n SECTION_SLOT_ATTRIBUTE,\n SECTION_SLOT_SKIP_ATTRIBUTE,\n formatSectionSlotSkips,\n type BrandlabSectionReadiness,\n type BrandlabSectionSlotSkip,\n} from '@brandlab-digital/site-core/section-order'\nimport { Section, SectionHeading, type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js'\nimport { Anchor } from '../primitives/links.js'\nimport type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, slot, type SkinEntry } from '../skin.js'\n\nexport const LICENCES_VARIANTS = ['licence', 'qualifications'] as const\nexport type LicencesVariant = (typeof LICENCES_VARIANTS)[number]\n/** Rendered once per line: styled from the skin stylesheet, never by class. */\nexport type LicencesRepeatedSlot = RepeatedSlotOf<'licences'>\nexport type LicencesSlot = SectionSlot | SectionHeadingSlot | 'list' | LicencesRepeatedSlot\nexport type LicencesSkin = SkinEntry<LicencesSlot, LicencesVariant, Record<never, never>, LicencesRepeatedSlot>\n\n/** The site's words for the ledger. Each is needed only when its line renders. */\nexport interface LicencesLabels {\n /** The qualifications row's label, for example \"Qualifications\". */\n readonly qualifications?: string\n /** The insurance row's label, for example \"Insurance\". */\n readonly insurance?: string\n /** Each cover's name, for example `{ 'public-liability': 'Public liability' }`. */\n readonly covers?: Readonly<Partial<Record<BrandlabInsuranceCover, string>>>\n /** The register link's text, for example \"Check the register\". Left out, no register link renders. */\n readonly register?: string\n}\n\n/** The business facts the ledger reads: `business.registration` and `business.credentials`. */\nexport type LicencesBusiness = Pick<BrandlabBusiness, 'registration' | 'credentials'>\n\nexport interface LicencesProps extends LicencesSkin {\n /** The H2, for example \"Qualified and insured\". */\n readonly title: ReactNode\n readonly eyebrow?: ReactNode\n readonly lead?: ReactNode\n readonly business: LicencesBusiness\n readonly labels: LicencesLabels\n /** The day the page is built (tests pin it). */\n readonly asOf?: Date\n /** Defaults to `licences-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\ninterface LedgerContent {\n readonly licences: readonly BrandlabLicence[]\n readonly credentials: BrandlabRenderableCredentials\n readonly skips: readonly BrandlabSectionSlotSkip[]\n}\n\n/** What the evidence lets the variant show today, and the expected lines it has to leave out. */\nexport function licencesContent(business: LicencesBusiness, variant: LicencesVariant, asOf: Date = new Date()): LedgerContent {\n const credentials = renderableCredentials(business.credentials, 'website-copy', asOf)\n const licences = variant === 'licence' ? renderableLicences(resolveVerifiedFactForSurface(business.registration.licences, 'website-copy', asOf) ?? [], asOf) : []\n const skips: BrandlabSectionSlotSkip[] = []\n if (variant === 'licence' && licences.length === 0) skips.push({ slot: 'licence', reason: 'no-current-licence' })\n if (variant === 'qualifications' && credentials.qualifications.length === 0) skips.push({ slot: 'qualifications', reason: 'no-evidenced-qualification' })\n if (variant === 'qualifications' && credentials.insurance.length === 0) skips.push({ slot: 'insurance', reason: 'no-current-insurance' })\n return { licences, credentials: variant === 'qualifications' ? credentials : { ...credentials, qualifications: [] }, skips }\n}\n\nfunction hasLines({ licences, credentials }: LedgerContent): boolean {\n return licences.length + credentials.qualifications.length + credentials.insurance.length > 0\n}\n\n/** The section's guard for `SectionSequence`: skipped when the evidence allows no line at all. */\nexport function licencesReady({ business, variant = 'licence', asOf }: Pick<LicencesProps, 'business' | 'variant' | 'asOf'>): BrandlabSectionReadiness {\n return hasLines(licencesContent(business, variant, asOf)) ? SECTION_READY : { render: false, reason: 'no-evidenced-credentials' }\n}\n\nfunction required(value: string | undefined, name: string): string {\n if (!value?.trim()) throw new Error(`Licences: labels.${name} is needed to show this line, in the site's words.`)\n return value\n}\n\nfunction LicenceLine({ licence, registerLabel }: { licence: BrandlabLicence; registerLabel: string | undefined }) {\n return (\n <div {...repeatedSlot('row')} {...{ [SECTION_SLOT_ATTRIBUTE]: 'licence' }}>\n <dt {...repeatedSlot('label')}>{licence.name}</dt>\n <dd {...repeatedSlot('value')}>\n <span {...repeatedSlot('number')}>{licence.identifier}</span>\n {licence.licensee ? (\n <>\n {' '}\n <span {...repeatedSlot('holder')}>{licence.licensee}</span>\n </>\n ) : null}\n </dd>\n {licence.classes && licence.classes.length > 0 ? (\n <dd {...repeatedSlot('classes')}>\n <ul role=\"list\">\n {licence.classes.map((entry) => (\n <li key={entry.name} {...repeatedSlot('class')}>\n {entry.name}\n {entry.conditions && entry.conditions.length > 0 ? `: ${entry.conditions.join('; ')}` : null}\n </li>\n ))}\n </ul>\n </dd>\n ) : null}\n {licence.registerUrl && registerLabel ? (\n <dd {...repeatedSlot('register')}>\n <Anchor href={licence.registerUrl} prefetch={false} hooks={{}}>\n {registerLabel}\n </Anchor>\n </dd>\n ) : null}\n </div>\n )\n}\n\nexport function Licences({ title, eyebrow, lead, business, labels, asOf, headingId = 'licences-heading', tone, variant = 'licence', classes }: LicencesProps) {\n const content = licencesContent(business, variant, asOf)\n if (!hasLines(content)) return null\n const { licences, credentials, skips } = content\n const qualificationsLabel = credentials.qualifications.length > 0 ? required(labels.qualifications, 'qualifications') : undefined\n const insuranceLabel = credentials.insurance.length > 0 ? required(labels.insurance, 'insurance') : undefined\n const coverName = (cover: BrandlabInsuranceCover) => required(labels.covers?.[cover], `covers['${cover}']`)\n return (\n <Section\n id=\"licences\"\n labelledBy={headingId}\n tone={tone}\n variant={variant}\n classes={classes}\n hooks={skips.length > 0 ? { [SECTION_SLOT_SKIP_ATTRIBUTE]: formatSectionSlotSkips(skips) } : undefined}\n >\n <SectionHeading id={headingId} title={title} eyebrow={eyebrow} lead={lead} classes={classes} />\n <dl {...slot(classes, 'list')}>\n {licences.map((licence) => (\n <LicenceLine key={`${licence.name}-${licence.identifier}`} licence={licence} registerLabel={labels.register} />\n ))}\n {qualificationsLabel ? (\n <div {...repeatedSlot('row')} {...{ [SECTION_SLOT_ATTRIBUTE]: 'qualifications' }}>\n <dt {...repeatedSlot('label')}>{qualificationsLabel}</dt>\n {credentials.qualifications.map((entry) => (\n <dd key={entry.title} {...repeatedSlot('value')}>\n {entry.title}\n </dd>\n ))}\n </div>\n ) : null}\n {insuranceLabel ? (\n <div {...repeatedSlot('row')} {...{ [SECTION_SLOT_ATTRIBUTE]: 'insurance' }}>\n <dt {...repeatedSlot('label')}>{insuranceLabel}</dt>\n {credentials.insurance.map((entry) => (\n <dd key={`${entry.cover}-${entry.amountAud}`} {...repeatedSlot('value')}>\n <span {...repeatedSlot('cover')}>{coverName(entry.cover)}</span>{' '}\n <span {...repeatedSlot('amount')}>{formatInsuranceAmount(entry.amountAud)}</span>\n </dd>\n ))}\n </div>\n ) : null}\n </dl>\n </Section>\n )\n}\n"]}