@rubytech/create-sitedesk-code 0.1.402 → 0.1.405

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 (71) hide show
  1. package/dist/__tests__/launchd-plist.test.js +13 -1
  2. package/dist/__tests__/neo4j-darwin-dedicated.test.js +52 -0
  3. package/dist/__tests__/neo4j-datadir.test.js +8 -7
  4. package/dist/__tests__/neo4j-teardown.test.js +17 -1
  5. package/dist/index.js +183 -9
  6. package/dist/launchd-plist.js +21 -0
  7. package/dist/neo4j-darwin-dedicated.js +39 -0
  8. package/dist/neo4j-datadir.js +9 -5
  9. package/dist/neo4j-teardown.js +15 -0
  10. package/dist/uninstall.js +34 -8
  11. package/package.json +1 -1
  12. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  13. package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +19 -2
  14. package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +12 -12
  15. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  16. package/payload/premium-plugins/sitedesk/BUNDLE.md +6 -4
  17. package/payload/premium-plugins/sitedesk/PLUGIN.md +2 -2
  18. package/payload/premium-plugins/sitedesk/agents/sitedesk--media-producer.md +18 -7
  19. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/.claude-plugin/plugin.json +1 -1
  20. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/PLUGIN.md +30 -26
  21. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/README.md +26 -36
  22. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/commands/make-media.md +2 -2
  23. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/SKILL.md +52 -75
  24. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/SKILL.md +52 -120
  25. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/sources.md +17 -52
  26. package/payload/server/public/assets/{AdminLoginScreens-rlk263u9.js → AdminLoginScreens-D36zz_SY.js} +1 -1
  27. package/payload/server/public/assets/AdminShell-DH6Lcj-d.js +1 -0
  28. package/payload/server/public/assets/{Checkbox-Chz6bfy-.js → Checkbox-54KtArF3.js} +1 -1
  29. package/payload/server/public/assets/{Transcript-Bsx-bGgY.js → Transcript-BCpiXt4J.js} +1 -1
  30. package/payload/server/public/assets/{admin-BnZH758N.js → admin-C0FCHcJj.js} +1 -1
  31. package/payload/server/public/assets/{browser-CIrfyZWe.js → browser-bKeZ5iLx.js} +1 -1
  32. package/payload/server/public/assets/{calendar-D6Z8Kmrn.js → calendar-BDPBArFA.js} +1 -1
  33. package/payload/server/public/assets/chat-Cp_1ebzU.js +1 -0
  34. package/payload/server/public/assets/chevron-left-S7Nk-se6.js +1 -0
  35. package/payload/server/public/assets/data-BshlTgIW.js +1 -0
  36. package/payload/server/public/assets/{graph-XZJZHzvv.js → graph-Dvru9CW9.js} +1 -1
  37. package/payload/server/public/assets/{graph-labels-Cabqmf35.js → graph-labels-DLhR6Rit.js} +1 -1
  38. package/payload/server/public/assets/{maximize-2-DPe-Noig.js → maximize-2-CyPKq7HZ.js} +1 -1
  39. package/payload/server/public/assets/{operator-BM_CDyCb.js → operator-Ql5iQXB-.js} +1 -1
  40. package/payload/server/public/assets/page-ClJOW2fk.js +32 -0
  41. package/payload/server/public/assets/{page-E7I5sEVt.js → page-YsTtZsn6.js} +1 -1
  42. package/payload/server/public/assets/{public-CrdWCSk2.js → public-BlDxwcmS.js} +1 -1
  43. package/payload/server/public/assets/{rotate-ccw-Dwtj0NJ7.js → rotate-ccw-DSynJPZX.js} +1 -1
  44. package/payload/server/public/assets/useSubAccountSwitcher-CJelLQK-.css +1 -0
  45. package/payload/server/public/browser.html +4 -4
  46. package/payload/server/public/calendar.html +5 -5
  47. package/payload/server/public/chat.html +9 -9
  48. package/payload/server/public/data.html +8 -8
  49. package/payload/server/public/graph.html +9 -9
  50. package/payload/server/public/index.html +11 -11
  51. package/payload/server/public/operator.html +11 -11
  52. package/payload/server/public/public.html +9 -9
  53. package/payload/server/server.js +34 -32
  54. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/brand-extract/SKILL.md +0 -86
  55. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/SKILL.md +0 -75
  56. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/references/intake-and-questions.md +0 -74
  57. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/placeholders.md +0 -96
  58. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/sections.md +0 -48
  59. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/index.html +0 -1004
  60. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/llms.txt +0 -32
  61. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/pii-strip.md +0 -64
  62. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/og.html +0 -92
  63. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/social-posts.md +0 -47
  64. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/socials.html +0 -87
  65. package/payload/server/public/assets/AdminShell-C5vWTy2n.js +0 -1
  66. package/payload/server/public/assets/chat-h7SytZHx.js +0 -1
  67. package/payload/server/public/assets/chevron-left-DSTeS1_g.js +0 -1
  68. package/payload/server/public/assets/data-CL7CyX1C.js +0 -1
  69. package/payload/server/public/assets/page-CMIbXzSg.js +0 -32
  70. package/payload/server/public/assets/useSubAccountSwitcher-BH-YjP0H.css +0 -1
  71. /package/payload/server/public/assets/{useSubAccountSwitcher-BVCMAnWK.js → useSubAccountSwitcher-DGKQxXDD.js} +0 -0
@@ -1,1004 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en-GB">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>{{ COMPANY_NAME }} · {{ SECTOR_LABEL }} · Est. {{ ESTABLISHED_YEAR }} · {{ REGION }}</title>
7
- <meta name="description" content="{{ META_DESCRIPTION }}">
8
- <link rel="canonical" href="{{ WEBSITE_URL }}">
9
- <link rel="icon" href="favicon.png" type="image/png">
10
- <meta property="og:type" content="website">
11
- <meta property="og:site_name" content="{{ COMPANY_LEGAL_NAME }}">
12
- <meta property="og:title" content="{{ COMPANY_NAME }} · {{ SECTOR_LABEL }} · Est. {{ ESTABLISHED_YEAR }} · {{ REGION }}">
13
- <meta property="og:description" content="{{ META_DESCRIPTION }}">
14
- <meta property="og:url" content="{{ WEBSITE_URL }}">
15
- <meta property="og:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.jpg">
16
- <meta property="og:image:width" content="1200">
17
- <meta property="og:image:height" content="628">
18
- <meta name="twitter:card" content="summary_large_image">
19
- <meta name="twitter:title" content="{{ COMPANY_NAME }} · {{ SECTOR_LABEL }} · Est. {{ ESTABLISHED_YEAR }}">
20
- <meta name="twitter:description" content="{{ META_DESCRIPTION_SHORT }}">
21
- <meta name="twitter:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.jpg">
22
- <link rel="preconnect" href="https://fonts.googleapis.com">
23
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
24
- <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=Lora:ital,wght@0,400;0,500;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
25
- <style>
26
- :root {
27
- /* Brand tokens — substituted per client by the brand-extract step.
28
- Names are stable so all component CSS resolves regardless of brand. */
29
- --paper: {{ PAPER_100 }}; /* paper-100 · white surface (dominant body) */
30
- --paper-2: {{ PAPER_200 }}; /* paper-200 · page-bg tint (alternating bands, image cells, hover) */
31
- --ink: {{ INK_900 }}; /* ink-900 · primary text, headings, dark surfaces */
32
- --ink-2: {{ INK_700 }}; /* ink-700 · secondary text */
33
- --slate: {{ INK_400 }}; /* ink-400 · muted / captions */
34
- --rule: {{ RULE_100 }}; /* rule-100 · borders / dividers */
35
- --rule-fine: {{ RULE_100 }};
36
- --gold: {{ ACCENT_TEXT }}; /* copper, deepened for legible accent text (eyebrows, links, em) */
37
- --gold-soft: {{ ACCENT }}; /* brand accent — decorative tone */
38
- --copper: {{ ACCENT }}; /* brand accent */
39
- --copper-soft:{{ ACCENT_SOFT }}; /* soft tint behind accent text (tag chip, quote ground) */
40
- --brand-grey: {{ BRAND_GREY }}; /* {{ COMPANY_SHORT }} monogram / wordmark grey */
41
-
42
- --serif: 'Cormorant Garamond', 'EB Garamond', Garamond, serif;
43
- --body-serif: 'Lora', 'Source Serif Pro', Georgia, serif;
44
- --sans: 'Inter', system-ui, -apple-system, sans-serif;
45
- }
46
-
47
- * { margin: 0; padding: 0; box-sizing: border-box; }
48
- html { font-size: 16px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; scroll-behavior: smooth; }
49
- body {
50
- font-family: var(--body-serif);
51
- background: var(--paper);
52
- color: var(--ink);
53
- line-height: 1.65;
54
- }
55
- img { display: block; max-width: 100%; height: auto; }
56
- a { color: inherit; }
57
-
58
- /* ───────── TOP NAV ───────── */
59
- .topnav {
60
- position: relative;
61
- z-index: 50;
62
- height: 112px;
63
- display: flex; align-items: center; justify-content: space-between;
64
- padding: 0 32px;
65
- background: var(--paper);
66
- border-bottom: 1px solid var(--rule);
67
- }
68
- .burger {
69
- display: none; background: transparent; border: none;
70
- padding: 10px; cursor: pointer; margin-left: -10px;
71
- }
72
- .burger span {
73
- display: block; width: 22px; height: 1.5px; background: var(--ink);
74
- margin: 4px 0; transition: transform 250ms ease, opacity 250ms ease;
75
- }
76
- .burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
77
- .burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
78
- .burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }
79
-
80
- .drawer {
81
- position: fixed; top: 0; right: 0; bottom: 0;
82
- width: min(360px, 86vw);
83
- background: var(--paper);
84
- box-shadow: -2px 0 36px rgba(42,45,56,0.18);
85
- transform: translateX(100%);
86
- transition: transform 320ms ease;
87
- z-index: 60; padding: 64px 28px 28px;
88
- display: flex; flex-direction: column; gap: 18px; overflow-y: auto;
89
- }
90
- .drawer.open { transform: translateX(0); }
91
- .drawer-close {
92
- position: absolute; top: 16px; right: 18px;
93
- width: 36px; height: 36px; background: transparent; border: none;
94
- font-size: 32px; line-height: 1; color: var(--ink); cursor: pointer;
95
- }
96
- .drawer-nav { display: flex; flex-direction: column; gap: 4px; }
97
- .drawer-nav a {
98
- font-family: var(--sans); font-size: 12px; font-weight: 500;
99
- letter-spacing: 0.18em; text-transform: uppercase;
100
- color: var(--ink-2); text-decoration: none;
101
- padding: 14px 0; border-bottom: 1px solid var(--rule);
102
- }
103
- .drawer-nav a:hover { color: var(--gold); }
104
- .drawer-nav a.drawer-cta {
105
- margin-top: 12px; padding: 14px 18px;
106
- background: var(--ink); color: var(--paper);
107
- text-align: center; border-bottom: none;
108
- }
109
- .drawer-nav a.drawer-cta:hover { background: var(--copper); color: #fff; }
110
- .drawer-foot {
111
- margin-top: auto; padding-top: 24px; border-top: 1px solid var(--rule);
112
- font-family: var(--body-serif); font-size: 13px; color: var(--slate); line-height: 1.55;
113
- }
114
- .drawer-foot a {
115
- color: var(--ink-2); text-decoration: underline;
116
- text-decoration-color: rgba(232,146,78,0.5); text-underline-offset: 3px;
117
- font-variant-numeric: tabular-nums;
118
- }
119
- .drawer-backdrop {
120
- position: fixed; inset: 0; background: rgba(42,45,56,0.45);
121
- opacity: 0; pointer-events: none; transition: opacity 320ms ease; z-index: 55;
122
- }
123
- .drawer-backdrop.open { opacity: 1; pointer-events: auto; }
124
- body.drawer-open { overflow: hidden; }
125
-
126
- .topnav .left, .topnav .right {
127
- display: flex; align-items: center; gap: 26px;
128
- font-family: var(--sans); font-size: 11px; font-weight: 500;
129
- letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-2);
130
- }
131
- .topnav .left a, .topnav .right a { text-decoration: none; }
132
- .topnav .left a:hover, .topnav .right a:hover { color: var(--gold); }
133
- .topnav .center {
134
- position: absolute; left: 50%; top: 50%;
135
- transform: translate(-50%, -50%); display: flex; align-items: center;
136
- }
137
- .topnav .center img { height: 84px; width: auto; }
138
-
139
- /* ───────── HERO — 10-slide rotator with Ken-Burns (JS-driven .active) ───────── */
140
- .hero {
141
- position: relative; width: 100%; height: 80vh; min-height: 580px;
142
- overflow: hidden; background: var(--ink);
143
- }
144
- .hero-stage { position: absolute; inset: 0; overflow: hidden; }
145
- .hero-slide {
146
- position: absolute; inset: 0; opacity: 0;
147
- transition: opacity 1000ms ease; will-change: opacity;
148
- }
149
- .hero-slide.active { opacity: 1; }
150
- .hero-slide img {
151
- width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%;
152
- transform: scale(1.05); transform-origin: 50% 50%; will-change: transform;
153
- }
154
- /* the visible slide gets a slow push-in for the time it is on screen */
155
- .hero-slide.active img { animation: heroKb 7s ease-out forwards; }
156
- @keyframes heroKb { from { transform: scale(1.05); } to { transform: scale(1.16); } }
157
- /* subtle scrim so the info-card edge reads against any slide */
158
- .hero::after {
159
- content: ''; position: absolute; inset: 0; pointer-events: none;
160
- background: linear-gradient(90deg, rgba(42,45,56,0.34) 0%, rgba(42,45,56,0.05) 42%, rgba(42,45,56,0) 70%);
161
- }
162
- @media (prefers-reduced-motion: reduce) {
163
- .hero-slide { transition: none; }
164
- .hero-slide.active img { animation: none; transform: none; }
165
- }
166
-
167
- .info-card {
168
- position: absolute; left: 6vw; bottom: 7vh; z-index: 2;
169
- background: rgba(255, 255, 255, 0.97);
170
- padding: 22px 28px 24px; width: 392px;
171
- border-top: 3px solid var(--copper);
172
- box-shadow: 0 8px 36px rgba(42,45,56,0.20);
173
- }
174
- .info-card .tag {
175
- display: inline-block; font-family: var(--sans);
176
- font-size: 9px; font-weight: 600; letter-spacing: 0.32em; text-transform: uppercase;
177
- color: var(--gold); margin-bottom: 12px;
178
- }
179
- .info-card .title {
180
- font-family: var(--serif); font-style: italic; font-weight: 400;
181
- font-size: 36px; line-height: 1.04; color: var(--ink); margin-bottom: 10px;
182
- }
183
- .info-card .address {
184
- font-family: var(--body-serif); font-size: 14px; color: var(--ink-2);
185
- line-height: 1.5; margin-bottom: 14px;
186
- }
187
- .info-card .tagline {
188
- font-family: var(--serif); font-style: italic; font-size: 20px;
189
- color: var(--gold); margin-bottom: 4px; letter-spacing: 0.005em;
190
- }
191
- .info-card .meta {
192
- font-family: var(--body-serif); font-size: 12.5px; color: var(--slate);
193
- line-height: 1.55; margin-bottom: 14px;
194
- }
195
- .info-card .specs {
196
- display: flex; flex-wrap: nowrap; gap: 0; padding: 10px 0; margin-bottom: 14px;
197
- border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
198
- font-family: var(--sans); font-size: 9px; font-weight: 600;
199
- letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2);
200
- font-variant-numeric: tabular-nums; justify-content: space-between; align-items: baseline;
201
- }
202
- .info-card .specs span { white-space: nowrap; flex: 0 0 auto; }
203
- .info-card .specs span + span::before { content: "·"; color: var(--copper); margin: 0 7px; }
204
- .info-card .links {
205
- display: flex; flex-wrap: nowrap; gap: 16px;
206
- font-family: var(--sans); font-size: 9.5px; font-weight: 500;
207
- letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 16px;
208
- }
209
- .info-card .links a {
210
- text-decoration: none; border-bottom: 1px solid var(--gold-soft);
211
- padding-bottom: 2px; white-space: nowrap; transition: color 200ms;
212
- }
213
- .info-card .links a:hover { color: var(--gold); }
214
- .info-card .cta {
215
- display: block; width: 100%; padding: 13px 16px;
216
- background: var(--ink); color: var(--paper);
217
- font-family: var(--sans); font-size: 11px; font-weight: 500;
218
- letter-spacing: 0.26em; text-transform: uppercase; text-align: center;
219
- text-decoration: none; border: none; cursor: pointer; transition: background 200ms;
220
- }
221
- .info-card .cta:hover { background: var(--copper); }
222
-
223
- /* ───────── STICKY SUB-NAV ───────── */
224
- .subnav {
225
- /* fixed (not sticky): a reveal-on-scroll bar. position:fixed + translateY(-100%)
226
- moves it genuinely above the viewport, so there is nothing to overlap. This
227
- also avoids the Safari/WebKit bug where transform on a sticky element promotes
228
- it to its own compositing layer and breaks the z-index overlap-hide. */
229
- position: fixed; top: 0; left: 0; right: 0; z-index: 40;
230
- background: var(--paper); border-bottom: 1px solid var(--rule);
231
- padding: 14px 32px; display: flex; align-items: center; justify-content: space-between;
232
- transform: translateY(-100%); transition: transform 300ms ease;
233
- }
234
- .subnav.visible { transform: translateY(0); }
235
- .subnav .left { font-family: var(--serif); font-size: 18px; color: var(--ink); }
236
- .subnav .left .price {
237
- color: var(--slate); font-size: 11px; font-family: var(--sans);
238
- letter-spacing: 0.16em; text-transform: uppercase; margin-left: 14px;
239
- }
240
- .subnav .right { display: flex; align-items: center; gap: 14px; }
241
- .subnav .right a {
242
- font-family: var(--sans); font-size: 10px; font-weight: 500;
243
- letter-spacing: 0.20em; text-transform: uppercase; color: var(--ink-2);
244
- text-decoration: none; padding: 6px 10px; border: 1px solid var(--rule);
245
- }
246
- .subnav .right a:hover { background: var(--paper-2); }
247
- .subnav .right a.cta { background: var(--ink); color: var(--paper); border-color: var(--ink); }
248
- .subnav .right a.cta:hover { background: var(--copper); border-color: var(--copper); }
249
-
250
- /* ───────── BODY SECTIONS ───────── */
251
- .body-block { max-width: 720px; margin: 0 auto; padding: 72px 32px 28px; }
252
- .body-block.short { padding-bottom: 16px; }
253
- .body-block h2 {
254
- font-family: var(--serif); font-style: italic; font-weight: 400;
255
- font-size: 27px; line-height: 1.2; color: var(--ink); margin-bottom: 18px; letter-spacing: -0.005em;
256
- }
257
- .body-block h2::before {
258
- content: ''; display: block; width: 40px; height: 2px;
259
- background: var(--copper); margin-bottom: 18px;
260
- }
261
- .body-block p {
262
- font-family: var(--body-serif); font-size: 15.5px; line-height: 1.75;
263
- color: var(--ink-2); letter-spacing: 0.005em;
264
- }
265
- .body-block p + p { margin-top: 16px; }
266
- .body-block p strong { color: var(--ink); font-weight: 500; }
267
- .body-block p em { color: var(--gold); font-style: italic; }
268
- .body-block p:first-of-type::first-letter {
269
- font-family: var(--serif); font-weight: 400; font-size: 3.6em; line-height: 0.85;
270
- float: left; margin: 0.08em 0.12em -0.04em 0; color: var(--ink); letter-spacing: -0.02em;
271
- }
272
- .section-eyebrow {
273
- font-family: var(--sans); font-size: 10px; font-weight: 600;
274
- letter-spacing: 0.36em; text-transform: uppercase; color: var(--gold); margin-bottom: 14px;
275
- }
276
-
277
- /* ───────── PHOTO GRID ───────── */
278
- .photo-grid {
279
- max-width: 1100px; margin: 32px auto; padding: 0 32px;
280
- display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
281
- }
282
- .photo-grid .cell {
283
- position: relative; overflow: hidden; aspect-ratio: 4/3; background: var(--paper-2);
284
- }
285
- .photo-grid .cell img { width: 100%; height: 100%; object-fit: cover; transition: transform 600ms ease; }
286
- .photo-grid .cell:hover img { transform: scale(1.03); }
287
-
288
- .photo-full { margin: 36px 0; }
289
- .photo-full img { width: 100%; height: auto; max-height: 78vh; object-fit: cover; }
290
-
291
- /* ───────── TESTIMONIAL BAND (repurposed feature band) ───────── */
292
- .testimonial { background: var(--ink); color: var(--paper); padding: 84px 32px; margin-top: 40px; }
293
- .testimonial .inner {
294
- max-width: 1040px; margin: 0 auto;
295
- display: grid; grid-template-columns: 0.85fr 1fr; gap: 56px; align-items: center;
296
- }
297
- .testimonial .home-img img {
298
- width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block;
299
- box-shadow: 0 16px 48px rgba(0,0,0,0.38); border-top: 3px solid var(--copper);
300
- }
301
- .testimonial .person {
302
- display: flex; align-items: center; gap: 14px;
303
- margin-top: 26px; padding-top: 22px; border-top: 1px solid rgba(255,255,255,0.18);
304
- }
305
- .testimonial .person img {
306
- width: 62px; height: 62px; border-radius: 50%; object-fit: cover;
307
- object-position: 50% 35%; flex: 0 0 auto; border: 2px solid var(--copper);
308
- }
309
- .testimonial .person .who { display: flex; flex-direction: column; line-height: 1.25; }
310
- .testimonial .person .pn { font-family: var(--serif); font-style: italic; font-size: 20px; color: #fff; }
311
- .testimonial .person .pr {
312
- font-family: var(--sans); font-size: 10px; font-weight: 600;
313
- letter-spacing: 0.22em; text-transform: uppercase; color: var(--copper); margin-top: 3px;
314
- }
315
- .testimonial .meta .eyebrow {
316
- font-family: var(--sans); font-size: 10px; font-weight: 600;
317
- letter-spacing: 0.36em; text-transform: uppercase; color: var(--copper); margin-bottom: 20px;
318
- }
319
- .testimonial blockquote {
320
- font-family: var(--serif); font-style: italic; font-weight: 400;
321
- font-size: 27px; line-height: 1.34; color: var(--paper); margin: 0 0 22px;
322
- }
323
- .testimonial blockquote .hl { color: var(--copper); }
324
- .testimonial .attrib {
325
- font-family: var(--sans); font-size: 11px; font-weight: 500;
326
- letter-spacing: 0.18em; text-transform: uppercase; color: rgba(255,255,255,0.72);
327
- }
328
- .testimonial .attrib span { color: var(--copper); }
329
-
330
- /* ───────── FOUNDER ───────── */
331
- .founder { background: var(--paper-2); padding: 84px 32px; }
332
- .founder .inner {
333
- max-width: 1100px; margin: 0 auto;
334
- display: grid; grid-template-columns: 0.8fr 1fr; gap: 56px; align-items: center;
335
- }
336
- .founder .portrait img {
337
- width: 100%; max-width: 420px; height: auto; display: block;
338
- box-shadow: 0 16px 48px rgba(42,45,56,0.22); border-top: 3px solid var(--copper);
339
- }
340
- .founder .portrait .cap {
341
- display: block; margin-top: 12px; font-family: var(--sans);
342
- font-size: 10px; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; color: var(--brand-grey);
343
- }
344
- .founder .words h2 {
345
- font-family: var(--serif); font-style: italic; font-size: 28px; color: var(--ink); margin-bottom: 18px;
346
- }
347
- .founder .words h2::before { content: ''; display: block; width: 40px; height: 2px; background: var(--copper); margin-bottom: 18px; }
348
- .founder .words p { font-family: var(--body-serif); font-size: 15.5px; line-height: 1.75; color: var(--ink-2); }
349
- .founder .words p + p { margin-top: 14px; }
350
- .founder .words blockquote {
351
- font-family: var(--serif); font-style: italic; font-size: 21px; line-height: 1.4;
352
- color: var(--ink); margin: 22px 0; padding-left: 20px; border-left: 3px solid var(--copper);
353
- }
354
- .founder .words .ethos { font-family: var(--body-serif); font-style: italic; font-size: 14.5px; color: var(--slate); }
355
- .founder-extra {
356
- max-width: 1100px; margin: 44px auto 0;
357
- display: grid; grid-template-columns: 1.4fr 1fr; gap: 20px;
358
- align-items: end;
359
- }
360
- .founder-extra figure { margin: 0; display: flex; flex-direction: column; }
361
- .founder-extra img { width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block; }
362
- .founder-extra figcaption { margin-top: 10px; font-family: var(--body-serif); font-style: italic; font-size: 12.5px; color: var(--slate); }
363
- .site-updates { max-width: 1100px; margin: 56px auto 0; }
364
- .site-updates .eyebrow {
365
- font-family: var(--sans); font-size: 10px; font-weight: 600; letter-spacing: 0.36em;
366
- text-transform: uppercase; color: var(--gold); text-align: center; margin-bottom: 24px;
367
- }
368
- .site-updates .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
369
- .site-updates .card {
370
- background: var(--paper); border: 1px solid var(--rule); padding: 24px 24px 26px;
371
- display: flex; flex-direction: column;
372
- }
373
- .site-updates .card .loc {
374
- font-family: var(--sans); font-size: 10px; font-weight: 600; letter-spacing: 0.2em;
375
- text-transform: uppercase; color: var(--copper); margin-bottom: 10px;
376
- }
377
- .site-updates .card p {
378
- font-family: var(--body-serif); font-size: 13.5px; line-height: 1.6; color: var(--ink-2);
379
- margin-top: auto;
380
- }
381
-
382
- /* ───────── SERVICES ───────── */
383
- .services { max-width: 1100px; margin: 76px auto 32px; padding: 0 32px; }
384
- .services h2 {
385
- font-family: var(--serif); font-style: italic; font-size: 27px; color: var(--ink);
386
- margin-bottom: 8px; text-align: center;
387
- }
388
- .services .lede {
389
- text-align: center; font-family: var(--body-serif); font-size: 14.5px;
390
- color: var(--ink-2); max-width: 560px; margin: 0 auto 36px;
391
- }
392
- .services h2::after {
393
- content: ''; display: block; width: 40px; height: 2px; background: var(--copper); margin: 14px auto 0;
394
- }
395
- .services .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
396
- .services .svc { background: var(--paper); padding: 30px 26px 32px; }
397
- .services .svc .num {
398
- font-family: var(--sans); font-size: 10px; font-weight: 600;
399
- letter-spacing: 0.28em; color: var(--copper); margin-bottom: 14px;
400
- }
401
- .services .svc h3 {
402
- font-family: var(--serif); font-style: italic; font-weight: 500;
403
- font-size: 21px; color: var(--ink); margin-bottom: 10px;
404
- }
405
- .services .svc p { font-family: var(--body-serif); font-size: 13.5px; line-height: 1.6; color: var(--ink-2); }
406
-
407
- /* ───────── KEY FACTS ───────── */
408
- .key-facts { max-width: 1100px; margin: 76px auto 32px; padding: 0 32px; }
409
- .key-facts h2 {
410
- font-family: var(--serif); font-style: italic; font-size: 27px; color: var(--ink);
411
- margin-bottom: 24px; text-align: center;
412
- }
413
- .key-facts h2::after {
414
- content: ''; display: block; width: 40px; height: 2px; background: var(--copper); margin: 14px auto 0;
415
- }
416
- .key-facts .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px 36px; font-variant-numeric: tabular-nums; }
417
- .key-facts .row { border-bottom: 1px solid var(--rule); padding: 12px 0 14px; }
418
- .key-facts .row .k {
419
- font-family: var(--sans); font-size: 9px; font-weight: 600;
420
- letter-spacing: 0.28em; text-transform: uppercase; color: var(--slate); margin-bottom: 5px;
421
- }
422
- .key-facts .row .v { font-family: var(--body-serif); font-size: 14.5px; color: var(--ink); }
423
- .key-facts .footnote {
424
- grid-column: 1 / -1; margin-top: 18px; font-family: var(--body-serif);
425
- font-style: italic; font-size: 12px; color: var(--slate); text-align: center;
426
- }
427
-
428
- /* ───────── SCROLL TO TOP ───────── */
429
- .to-top {
430
- position: fixed; right: 26px; bottom: 26px; z-index: 45;
431
- width: 48px; height: 48px; border-radius: 50%;
432
- background: var(--ink); color: #fff; border: none; cursor: pointer;
433
- display: flex; align-items: center; justify-content: center;
434
- box-shadow: 0 6px 22px rgba(42,45,56,0.30);
435
- opacity: 0; visibility: hidden; transform: translateY(10px);
436
- transition: opacity 240ms ease, transform 240ms ease, visibility 240ms, background 200ms;
437
- }
438
- .to-top.show { opacity: 1; visibility: visible; transform: translateY(0); }
439
- .to-top:hover { background: var(--copper); }
440
- .to-top svg { width: 18px; height: 18px; display: block; }
441
- @media (max-width: 760px) { .to-top { right: 16px; bottom: 16px; } }
442
-
443
- /* ───────── FAQ ───────── */
444
- .faq { max-width: 760px; margin: 76px auto 16px; padding: 0 32px; }
445
- .faq > h2 {
446
- font-family: var(--serif); font-style: italic; font-size: 27px; color: var(--ink);
447
- margin-bottom: 28px; text-align: center;
448
- }
449
- .faq > h2::after { content: ''; display: block; width: 40px; height: 2px; background: var(--copper); margin: 14px auto 0; }
450
- .faq .qa { border-top: 1px solid var(--rule); padding: 22px 0; }
451
- .faq .qa:last-child { border-bottom: 1px solid var(--rule); }
452
- .faq .qa h3 {
453
- font-family: var(--serif); font-style: italic; font-weight: 500;
454
- font-size: 20px; color: var(--ink); margin-bottom: 8px;
455
- }
456
- .faq .qa p { font-family: var(--body-serif); font-size: 15px; line-height: 1.7; color: var(--ink-2); }
457
-
458
- /* ───────── CTA STRIP ───────── */
459
- .cta-strip { background: var(--copper); color: #fff; text-align: center; padding: 72px 32px; margin-top: 72px; }
460
- .cta-strip .eyebrow {
461
- font-family: var(--sans); font-size: 10px; font-weight: 600;
462
- letter-spacing: 0.36em; text-transform: uppercase; color: rgba(255,255,255,0.85); margin-bottom: 16px;
463
- }
464
- .cta-strip h2 {
465
- font-family: var(--serif); font-style: italic; font-weight: 400;
466
- font-size: 38px; margin-bottom: 30px; color: #fff;
467
- }
468
- .cta-strip .cta-row { display: flex; gap: 18px; justify-content: center; align-items: center; flex-wrap: wrap; }
469
- .cta-strip .cta-row a {
470
- font-family: var(--sans); font-size: 11px; font-weight: 500;
471
- letter-spacing: 0.24em; text-transform: uppercase; color: #fff; text-decoration: none;
472
- padding: 13px 24px; border: 1px solid rgba(255,255,255,0.55); transition: background 200ms, color 200ms, border-color 200ms;
473
- font-variant-numeric: tabular-nums;
474
- }
475
- .cta-strip .cta-row a:hover { background: rgba(255,255,255,0.14); }
476
- .cta-strip .cta-row a.solid { background: var(--ink); color: #fff; border-color: var(--ink); }
477
- .cta-strip .cta-row a.solid:hover { background: #fff; color: var(--ink); border-color: #fff; }
478
-
479
- /* ───────── FOOTER ───────── */
480
- footer { background: var(--paper); padding: 56px 32px 32px; border-top: 1px solid var(--rule); }
481
- footer .inner {
482
- max-width: 1100px; margin: 0 auto; display: flex;
483
- justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 40px;
484
- }
485
- footer .agent { font-family: var(--body-serif); font-size: 13.5px; color: var(--ink-2); line-height: 1.7; max-width: 320px; }
486
- footer .agent .name { font-family: var(--serif); font-size: 19px; color: var(--ink); margin-bottom: 4px; }
487
- footer .agent .name em { font-style: italic; color: var(--gold); }
488
- footer .agent .label {
489
- font-family: var(--sans); font-size: 9px; font-weight: 600;
490
- letter-spacing: 0.32em; text-transform: uppercase; color: var(--slate); margin-bottom: 10px;
491
- }
492
- footer .agent a {
493
- color: var(--ink-2); text-decoration: underline; text-decoration-color: rgba(232,146,78,0.5);
494
- text-underline-offset: 3px; font-variant-numeric: tabular-nums;
495
- }
496
- footer .socials { display: flex; gap: 18px; margin-top: 14px; }
497
- footer .socials a {
498
- font-family: var(--sans); font-size: 10px; font-weight: 600;
499
- letter-spacing: 0.18em; text-transform: uppercase; color: var(--brand-grey); text-decoration: none;
500
- }
501
- footer .socials a:hover { color: var(--gold); }
502
- footer .legal {
503
- font-family: var(--body-serif); font-style: italic; font-size: 11.5px;
504
- color: var(--slate); line-height: 1.6; max-width: 560px;
505
- align-self: flex-end;
506
- }
507
- footer .colophon {
508
- width: 100%; border-top: 1px solid var(--rule); padding-top: 18px; margin-top: 28px;
509
- display: flex; justify-content: space-between; align-items: center;
510
- font-family: var(--sans); font-size: 10px; font-weight: 500;
511
- letter-spacing: 0.20em; text-transform: uppercase; color: var(--slate);
512
- }
513
- footer .colophon img { height: 60px; width: auto; opacity: 0.9; }
514
-
515
- /* ───────── RESPONSIVE ───────── */
516
- /* Nav collapses to a hamburger early: a wide centred logo lockup needs room,
517
- so the deep links would otherwise collide with it well above 960px. Tune this
518
- width to the logo's rendered width if the lockup is much narrower or wider. */
519
- @media (max-width: 1240px) {
520
- .burger { display: block; }
521
- .topnav .left, .topnav .right { display: none; }
522
- .topnav .center { left: auto; right: auto; transform: none; position: static; margin-left: auto; margin-right: auto; }
523
- }
524
- @media (max-width: 960px) {
525
- .testimonial .inner { grid-template-columns: 1fr; gap: 36px; }
526
- .testimonial .home-img { max-width: 460px; margin: 0 auto; }
527
- .services .grid { grid-template-columns: 1fr 1fr; }
528
- .founder .inner { grid-template-columns: 1fr; gap: 32px; }
529
- .founder .portrait img { max-width: 360px; margin: 0 auto; }
530
- .site-updates .grid { grid-template-columns: 1fr; }
531
- }
532
- @media (max-width: 760px) {
533
- .hero { height: auto; min-height: 0; display: flex; flex-direction: column; background: var(--paper); }
534
- .hero::after { display: none; }
535
- .hero-stage { position: relative; width: 100%; aspect-ratio: 4/3; flex: 0 0 auto; background: var(--ink); }
536
- .info-card {
537
- position: static; left: auto; right: auto; bottom: auto; width: auto; margin: 0;
538
- padding: 26px 24px 28px; box-shadow: none; border-top: 3px solid var(--copper); background: var(--paper);
539
- }
540
- .info-card .title { font-size: 32px; }
541
- .info-card .specs { font-size: 9px; }
542
- .info-card .links { font-size: 10px; }
543
- .photo-grid { grid-template-columns: 1fr; gap: 12px; }
544
- .key-facts .grid { grid-template-columns: 1fr 1fr; }
545
- .subnav .right a:not(.cta) { display: none; }
546
- .body-block { padding: 52px 24px 24px; }
547
- .topnav { padding: 0 18px; height: 88px; }
548
- .topnav .center img { height: 60px; }
549
- .testimonial { padding: 60px 24px; }
550
- .testimonial blockquote { font-size: 23px; }
551
- .founder { padding: 56px 24px; }
552
- .founder-extra { grid-template-columns: 1fr; }
553
- .cta-strip h2 { font-size: 30px; }
554
- footer .colophon { flex-direction: column; align-items: flex-start; gap: 12px; }
555
- }
556
- @media (max-width: 440px) {
557
- .hero-stage { aspect-ratio: 5/4; }
558
- .key-facts .grid { grid-template-columns: 1fr; }
559
- .services .grid { grid-template-columns: 1fr; }
560
- .subnav .left .price { display: none; }
561
- }
562
- </style>
563
- <script type="application/ld+json">
564
- {
565
- "@context": "https://schema.org",
566
- "@type": "{{ SCHEMA_TYPE }}",
567
- "name": "{{ COMPANY_NAME }}",
568
- "legalName": "{{ COMPANY_LEGAL_NAME }}",
569
- "url": "{{ WEBSITE_URL }}",
570
- "image": "{{ WEBSITE_URL }}images/hero-01.webp",
571
- "logo": "{{ WEBSITE_URL }}images/logo.png",
572
- "slogan": "{{ TAGLINE }}",
573
- "description": "{{ SCHEMA_DESCRIPTION }}",
574
- "foundingDate": "{{ ESTABLISHED_YEAR }}",
575
- "telephone": "{{ PHONE_INTL }}",
576
- "email": "{{ EMAIL }}",
577
- "identifier": { "@type": "PropertyValue", "name": "UK company number", "value": "{{ COMPANY_NUMBER }}" },
578
- "employee": { "@type": "Person", "name": "{{ FOUNDER_NAME }}" },
579
- "address": {
580
- "@type": "PostalAddress",
581
- "streetAddress": "{{ STREET_ADDRESS }}",
582
- "addressLocality": "{{ POST_TOWN }}",
583
- "addressRegion": "{{ COUNTY }}",
584
- "postalCode": "{{ POSTCODE }}",
585
- "addressCountry": "GB"
586
- },
587
- "areaServed": [
588
- { "@type": "AdministrativeArea", "name": "{{ COUNTY }}" },
589
- { "@type": "AdministrativeArea", "name": "{{ REGION_2 }}" }
590
- ],
591
- "sameAs": [
592
- "{{ FACEBOOK_URL }}",
593
- "{{ INSTAGRAM_URL }}",
594
- "{{ TIKTOK_URL }}"
595
- ],
596
- "knowsAbout": ["{{ SERVICE_1_NAME }}", "{{ SERVICE_2_NAME }}", "{{ SERVICE_3_NAME }}", "{{ SERVICE_4_NAME }}", "{{ SERVICE_5_NAME }}", "{{ SERVICE_6_NAME }}"],
597
- "hasOfferCatalog": {
598
- "@type": "OfferCatalog",
599
- "name": "{{ OFFER_CATALOG_NAME }}",
600
- "itemListElement": [
601
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_1_NAME }}", "description": "{{ SERVICE_1_DESC }}" } },
602
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_2_NAME }}", "description": "{{ SERVICE_2_DESC }}" } },
603
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_3_NAME }}", "description": "{{ SERVICE_3_DESC }}" } },
604
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_4_NAME }}", "description": "{{ SERVICE_4_DESC }}" } },
605
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_5_NAME }}", "description": "{{ SERVICE_5_DESC }}" } },
606
- { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "{{ SERVICE_6_NAME }}", "description": "{{ SERVICE_6_DESC }}" } }
607
- ]
608
- }
609
- }
610
- </script>
611
- <script type="application/ld+json">
612
- {
613
- "@context": "https://schema.org",
614
- "@type": "FAQPage",
615
- "mainEntity": [
616
- { "@type": "Question", "name": "{{ FAQ_1_Q }}",
617
- "acceptedAnswer": { "@type": "Answer", "text": "{{ FAQ_1_A }}" } },
618
- { "@type": "Question", "name": "{{ FAQ_2_Q }}",
619
- "acceptedAnswer": { "@type": "Answer", "text": "{{ FAQ_2_A }}" } },
620
- { "@type": "Question", "name": "{{ FAQ_3_Q }}",
621
- "acceptedAnswer": { "@type": "Answer", "text": "{{ FAQ_3_A }}" } },
622
- { "@type": "Question", "name": "{{ FAQ_4_Q }}",
623
- "acceptedAnswer": { "@type": "Answer", "text": "{{ FAQ_4_A }}" } },
624
- { "@type": "Question", "name": "{{ FAQ_5_Q }}",
625
- "acceptedAnswer": { "@type": "Answer", "text": "{{ FAQ_5_A }}" } }
626
- ]
627
- }
628
- </script>
629
- </head>
630
- <body>
631
-
632
- <!-- ════════════════ TOP NAV ════════════════ -->
633
- <nav class="topnav">
634
- <button class="burger" aria-label="Open menu" aria-expanded="false" aria-controls="drawer">
635
- <span></span><span></span><span></span>
636
- </button>
637
- <div class="left">
638
- <a href="#story">Our story</a>
639
- <a href="#build">What we build</a>
640
- <a href="#craft">Our craft</a>
641
- <a href="#founder">{{ FOUNDER_SECTION_LABEL }}</a>
642
- </div>
643
- <div class="center">
644
- <img src="images/logo.png" alt="{{ COMPANY_NAME }}, {{ SECTOR_LABEL }}, est. {{ ESTABLISHED_YEAR }}">
645
- </div>
646
- <div class="right">
647
- <a href="#words">Reviews</a>
648
- <a href="#services">Services</a>
649
- <a href="mailto:{{ EMAIL }}?subject=Project%20enquiry">Enquire</a>
650
- </div>
651
- </nav>
652
-
653
- <!-- Mobile drawer -->
654
- <div class="drawer" id="drawer" aria-hidden="true">
655
- <button class="drawer-close" aria-label="Close menu">&times;</button>
656
- <nav class="drawer-nav">
657
- <a href="#story">Our story</a>
658
- <a href="#build">What we build</a>
659
- <a href="#craft">Our craft</a>
660
- <a href="#founder">{{ FOUNDER_SECTION_LABEL }}</a>
661
- <a href="#words">Reviews</a>
662
- <a href="#services">Services</a>
663
- <a href="#facts">{{ WHY_HEADLINE }}</a>
664
- <a href="tel:{{ PHONE_TEL }}">Call {{ PHONE_DISPLAY }}</a>
665
- <a href="{{ INSTAGRAM_URL }}" target="_blank" rel="noopener">Instagram</a>
666
- <a href="{{ FACEBOOK_URL }}" target="_blank" rel="noopener">Facebook</a>
667
- <a href="mailto:{{ EMAIL }}?subject=Project%20enquiry" class="drawer-cta">Request a callback</a>
668
- </nav>
669
- <div class="drawer-foot">
670
- {{ COMPANY_LEGAL_NAME }} · {{ LOCALITY }}, {{ COUNTY }}<br>
671
- <a href="tel:{{ PHONE_TEL }}">{{ PHONE_DISPLAY }}</a> · <a href="mailto:{{ EMAIL }}">{{ EMAIL }}</a>
672
- </div>
673
- </div>
674
-
675
- <!-- ════════════════ STICKY SUB-NAV ════════════════ -->
676
- <nav class="subnav" id="subnav">
677
- <div class="left">
678
- {{ COMPANY_NAME }}
679
- <span class="price">{{ SECTOR_LABEL }} · Est. {{ ESTABLISHED_YEAR }}</span>
680
- </div>
681
- <div class="right">
682
- <a href="#craft">Our craft</a>
683
- <a href="#services">Services</a>
684
- <a href="#words">Reviews</a>
685
- <a href="mailto:{{ EMAIL }}?subject=Project%20enquiry" class="cta">Get in touch</a>
686
- </div>
687
- </nav>
688
-
689
- <!-- ════════════════ HERO ════════════════ -->
690
- <section class="hero">
691
- <div class="hero-stage" aria-label="{{ COMPANY_NAME }}, views of our work" role="img">
692
- <div class="hero-slide active"><img src="images/hero-01.webp" alt="A family home with a glazed garden-room extension and bi-fold doors onto the lawn, built by {{ COMPANY_NAME }}"></div>
693
- <div class="hero-slide"><img src="images/hero-02.webp" alt="A bright open-plan living and kitchen extension lit by twin roof lanterns"></div>
694
- <div class="hero-slide"><img src="images/hero-03.webp" alt="A finished kitchen with a central island, shaker units and quartz worktops"></div>
695
- <div class="hero-slide"><img src="images/hero-04.webp" alt="A finished reception room with a large new window over the garden"></div>
696
- <div class="hero-slide"><img src="images/hero-05.webp" alt="New sandstone steps and landscaping completing a home's approach"></div>
697
- <div class="hero-slide"><img src="images/hero-06.webp" alt="An aerial view of a major rear extension under way on a period home"></div>
698
- <div class="hero-slide"><img src="images/hero-07.webp" alt="A new room framed out beneath its exposed timber roof structure"></div>
699
- <div class="hero-slide"><img src="images/hero-08.webp" alt="A contemporary clad building with a standing-seam roof"></div>
700
- <div class="hero-slide"><img src="images/hero-09.webp" alt="The {{ COMPANY_NAME }} team at work on the rear of a brick home"></div>
701
- <div class="hero-slide"><img src="images/hero-10.webp" alt="A rear extension taking shape in blockwork, ready for its roof"></div>
702
- </div>
703
- <div class="info-card">
704
- <span class="tag">Est. {{ ESTABLISHED_YEAR }} · {{ COUNTY }}</span>
705
- <h1 class="title">{{ COMPANY_NAME }}</h1>
706
- <div class="address">{{ HERO_STRAPLINE }}</div>
707
- <div class="tagline">{{ TAGLINE }}</div>
708
- <div class="meta">{{ HERO_META }}</div>
709
- <div class="specs">
710
- <span>{{ HERO_SPEC_1 }}</span>
711
- <span>{{ HERO_SPEC_2 }}</span>
712
- <span>{{ HERO_SPEC_3 }}</span>
713
- <span>{{ HERO_SPEC_4 }}</span>
714
- </div>
715
- <div class="links">
716
- <a href="#build">Our work</a>
717
- <a href="#words">Reviews</a>
718
- <a href="#services">Services</a>
719
- </div>
720
- <a href="mailto:{{ EMAIL }}?subject=Project%20enquiry" class="cta">Request a callback</a>
721
- </div>
722
- </section>
723
-
724
- <!-- ════════════════ MAIN BODY ════════════════ -->
725
- <main>
726
-
727
- <!-- OUR STORY -->
728
- <article class="body-block" id="story">
729
- <div class="section-eyebrow">Since {{ ESTABLISHED_YEAR }}</div>
730
- <h2>{{ STORY_HEADLINE }}</h2>
731
- <p>{{ STORY_PARA_1 }}</p>
732
- <p>{{ STORY_PARA_2 }}</p>
733
- </article>
734
-
735
- <!-- PHOTO GRID 1 — finished work -->
736
- <div class="photo-grid">
737
- <div class="cell"><img src="images/grid-01.webp" alt="New sandstone steps, a retaining wall and planting completing a home's approach"></div>
738
- <div class="cell"><img src="images/grid-02.webp" alt="A contemporary clad building with a standing-seam roof, nearing completion"></div>
739
- <div class="cell"><img src="images/grid-03.webp" alt="A new room framed out, its timber roof structure exposed before lining"></div>
740
- <div class="cell"><img src="images/grid-04.webp" alt="The {{ COMPANY_NAME }} team at work on the rear of a brick home"></div>
741
- </div>
742
-
743
- <!-- WHAT WE BUILD -->
744
- <article class="body-block" id="build">
745
- <div class="section-eyebrow">What we build</div>
746
- <h2>{{ BUILD_HEADLINE }}</h2>
747
- <p>{{ BUILD_PARA_1 }}</p>
748
- <p>{{ BUILD_PARA_2 }}</p>
749
- <p>{{ BUILD_PARA_3 }}</p>
750
- </article>
751
-
752
- <!-- PHOTO GRID 2 — interiors & finishes -->
753
- <div class="photo-grid">
754
- <div class="cell"><img src="images/grid-05.webp" alt="A structural steel beam set in to open up the back of a house for an extension"></div>
755
- <div class="cell"><img src="images/grid-06.webp" alt="Stripping and re-laying an old clay-tiled roof from the scaffold"></div>
756
- <div class="cell"><img src="images/grid-07.webp" alt="A rear extension taking shape in blockwork, ready for its roof"></div>
757
- <div class="cell"><img src="images/grid-08.webp" alt="A gable rebuilt and re-roofed, with the {{ COMPANY_NAME }} board on site"></div>
758
- </div>
759
-
760
- <!-- OUR CRAFT -->
761
- <article class="body-block" id="craft">
762
- <div class="section-eyebrow">Ground to finish</div>
763
- <h2>{{ CRAFT_HEADLINE }}</h2>
764
- <p>{{ CRAFT_PARA_1 }}</p>
765
- <p>{{ CRAFT_PARA_2 }}</p>
766
- <p>{{ CRAFT_PARA_3 }}</p>
767
- </article>
768
-
769
- <!-- FULL-BLEED CRAFT MOMENT -->
770
- <div class="photo-full">
771
- <img src="images/fullbleed-01.webp" alt="An aerial view of a major rear extension under way on a period home, by {{ COMPANY_NAME }}">
772
- </div>
773
-
774
- <!-- PHOTO GRID 4 — process, groundworks to roof -->
775
- <div class="photo-grid">
776
- <div class="cell"><img src="images/grid-09.webp" alt="The groundworks crew at work beside an existing home"></div>
777
- <div class="cell"><img src="images/grid-10.webp" alt="The team raising an insulated timber-frame wall on a new build"></div>
778
- <div class="cell"><img src="images/grid-11.webp" alt="Underfloor-heating pipes laid out during first fix"></div>
779
- <div class="cell"><img src="images/grid-12.webp" alt="A freshly poured floor screed in a room under renovation"></div>
780
- </div>
781
-
782
- </main>
783
-
784
- <!-- ════════════════ FOUNDER / TEAM LEAD ════════════════ -->
785
- <section class="founder" id="founder">
786
- <div class="inner">
787
- <div class="portrait">
788
- <img src="images/founder-portrait.webp" alt="{{ FOUNDER_NAME }} of {{ COMPANY_NAME }}, on site in front of the company hoarding">
789
- <span class="cap">{{ FOUNDER_NAME }} · {{ COMPANY_NAME }}</span>
790
- </div>
791
- <div class="words">
792
- <div class="section-eyebrow">{{ FOUNDER_SECTION_LABEL }}</div>
793
- <h2>{{ FOUNDER_HEADLINE }}</h2>
794
- <p>{{ FOUNDER_INTRO }}</p>
795
- <blockquote>{{ FOUNDER_QUOTE }}</blockquote>
796
- <p class="ethos">{{ FOUNDER_ETHOS }}</p>
797
- </div>
798
- </div>
799
- <div class="founder-extra">
800
- <figure>
801
- <img src="images/feature-project.webp" alt="{{ FEATURE_PROJECT_ALT }}">
802
- <figcaption>{{ FEATURE_PROJECT_CAPTION }}</figcaption>
803
- </figure>
804
- <figure>
805
- <img src="images/gift.webp" alt="{{ GIFT_ALT }}">
806
- <figcaption>{{ GIFT_CAPTION }}</figcaption>
807
- </figure>
808
- </div>
809
- <div class="site-updates">
810
- <div class="eyebrow">{{ UPDATES_LABEL }}</div>
811
- <div class="grid">
812
- <div class="card">
813
- <div class="loc">{{ UPDATE_1_LOC }}</div>
814
- <p>{{ UPDATE_1_TEXT }}</p>
815
- </div>
816
- <div class="card">
817
- <div class="loc">{{ UPDATE_2_LOC }}</div>
818
- <p>{{ UPDATE_2_TEXT }}</p>
819
- </div>
820
- <div class="card">
821
- <div class="loc">{{ UPDATE_3_LOC }}</div>
822
- <p>{{ UPDATE_3_TEXT }}</p>
823
- </div>
824
- </div>
825
- </div>
826
- </section>
827
-
828
- <!-- ════════════════ TESTIMONIAL BAND ════════════════ -->
829
- <section class="testimonial" id="words">
830
- <div class="inner">
831
- <div class="home-img">
832
- <img src="images/testimonial.webp" alt="A bespoke kitchen with a large island, built and fitted by {{ COMPANY_NAME }}">
833
- </div>
834
- <div class="meta">
835
- <div class="eyebrow">In their words</div>
836
- <blockquote>{{ TESTIMONIAL_QUOTE }}</blockquote>
837
- <div class="attrib">{{ TESTIMONIAL_ATTRIB }} · <span>{{ TESTIMONIAL_CONTEXT }}</span></div>
838
- <div class="person">
839
- <img src="images/founder-avatar.webp" alt="{{ FOUNDER_NAME }} of {{ COMPANY_NAME }} on site">
840
- <div class="who"><span class="pn">{{ FOUNDER_NAME }}</span><span class="pr">{{ COMPANY_NAME }}</span></div>
841
- </div>
842
- </div>
843
- </div>
844
- </section>
845
-
846
- <!-- ════════════════ SERVICES ════════════════ -->
847
- <section class="services" id="services">
848
- <h2>{{ SERVICES_HEADLINE }}</h2>
849
- <p class="lede">{{ SERVICES_LEDE }}</p>
850
- <div class="grid">
851
- <div class="svc"><div class="num">01</div><h3>{{ SERVICE_1_NAME }}</h3><p>{{ SERVICE_1_DESC }}</p></div>
852
- <div class="svc"><div class="num">02</div><h3>{{ SERVICE_2_NAME }}</h3><p>{{ SERVICE_2_DESC }}</p></div>
853
- <div class="svc"><div class="num">03</div><h3>{{ SERVICE_3_NAME }}</h3><p>{{ SERVICE_3_DESC }}</p></div>
854
- <div class="svc"><div class="num">04</div><h3>{{ SERVICE_4_NAME }}</h3><p>{{ SERVICE_4_DESC }}</p></div>
855
- <div class="svc"><div class="num">05</div><h3>{{ SERVICE_5_NAME }}</h3><p>{{ SERVICE_5_DESC }}</p></div>
856
- <div class="svc"><div class="num">06</div><h3>{{ SERVICE_6_NAME }}</h3><p>{{ SERVICE_6_DESC }}</p></div>
857
- </div>
858
- </section>
859
-
860
- <!-- ════════════════ WHY-US / KEY FACTS ════════════════ -->
861
- <section class="key-facts" id="facts">
862
- <h2>{{ WHY_HEADLINE }}</h2>
863
- <div class="grid">
864
- <div class="row"><div class="k">Established</div><div class="v">{{ ESTABLISHED_YEAR }}</div></div>
865
- <div class="row"><div class="k">{{ FACT_1_KEY }}</div><div class="v">{{ FACT_1_VALUE }}</div></div>
866
- <div class="row"><div class="k">{{ FACT_2_KEY }}</div><div class="v">{{ FACT_2_VALUE }}</div></div>
867
- <div class="row"><div class="k">Coverage</div><div class="v">{{ REGION }}</div></div>
868
- <div class="row"><div class="k">Based</div><div class="v">{{ LOCALITY }}, {{ COUNTY }}</div></div>
869
- <div class="row"><div class="k">{{ FACT_3_KEY }}</div><div class="v">{{ FACT_3_VALUE }}</div></div>
870
- <div class="row"><div class="k">{{ FACT_4_KEY }}</div><div class="v">{{ FACT_4_VALUE }}</div></div>
871
- <div class="row"><div class="k">{{ FACT_5_KEY }}</div><div class="v">{{ FACT_5_VALUE }}</div></div>
872
- <div class="row"><div class="k">{{ FACT_6_KEY }}</div><div class="v">{{ FACT_6_VALUE }}</div></div>
873
- <div class="footnote">{{ FACTS_FOOTNOTE }}</div>
874
- </div>
875
- </section>
876
-
877
- <!-- ════════════════ FAQ (answer-first, AEO lift targets) ════════════════ -->
878
- <section class="faq" id="faq">
879
- <h2>Frequently asked questions</h2>
880
- <div class="qa">
881
- <h3>{{ FAQ_1_Q }}</h3>
882
- <p>{{ FAQ_1_A }}</p>
883
- </div>
884
- <div class="qa">
885
- <h3>{{ FAQ_2_Q }}</h3>
886
- <p>{{ FAQ_2_A }}</p>
887
- </div>
888
- <div class="qa">
889
- <h3>{{ FAQ_3_Q }}</h3>
890
- <p>{{ FAQ_3_A }}</p>
891
- </div>
892
- <div class="qa">
893
- <h3>{{ FAQ_4_Q }}</h3>
894
- <p>{{ FAQ_4_A }}</p>
895
- </div>
896
- <div class="qa">
897
- <h3>{{ FAQ_5_Q }}</h3>
898
- <p>{{ FAQ_5_A }}</p>
899
- </div>
900
- </section>
901
-
902
- <!-- ════════════════ CTA STRIP ════════════════ -->
903
- <section class="cta-strip">
904
- <div class="eyebrow">{{ TAGLINE }}</div>
905
- <h2>{{ CTA_HEADLINE }}</h2>
906
- <div class="cta-row">
907
- <a href="tel:{{ PHONE_TEL }}">{{ PHONE_DISPLAY }}</a>
908
- <a href="tel:{{ MOBILE_TEL }}">{{ MOBILE_DISPLAY }}</a>
909
- <a href="mailto:{{ EMAIL }}?subject=Project%20enquiry" class="solid">Request a callback</a>
910
- </div>
911
- </section>
912
-
913
- <!-- ════════════════ FOOTER ════════════════ -->
914
- <footer>
915
- <div class="inner">
916
- <div class="agent">
917
- <div class="label">{{ FOOTER_LABEL }}</div>
918
- <div class="name"><em>{{ COMPANY_NAME }}</em></div>
919
- {{ STREET_ADDRESS }},<br>
920
- {{ POST_TOWN }}, {{ COUNTY }}, {{ POSTCODE }}<br>
921
- <a href="tel:{{ PHONE_TEL }}">{{ PHONE_DISPLAY }}</a> · <a href="tel:{{ MOBILE_TEL }}">{{ MOBILE_DISPLAY }}</a><br>
922
- <a href="mailto:{{ EMAIL }}">{{ EMAIL }}</a>
923
- <div class="socials">
924
- <a href="{{ FACEBOOK_URL }}" target="_blank" rel="noopener">Facebook</a>
925
- <a href="{{ INSTAGRAM_URL }}" target="_blank" rel="noopener">Instagram</a>
926
- <a href="{{ TIKTOK_URL }}" target="_blank" rel="noopener">TikTok</a>
927
- </div>
928
- </div>
929
- <p class="legal">{{ FOOTER_LEGAL }}</p>
930
- <div class="colophon">
931
- <img src="images/icon.png" alt="{{ COMPANY_SHORT }} monogram">
932
- <span>© {{ COPYRIGHT_YEAR }} {{ COMPANY_LEGAL_NAME }} · {{ TAGLINE }}</span>
933
- </div>
934
- </div>
935
- </footer>
936
-
937
- <button class="to-top" id="toTop" aria-label="Back to top">
938
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>
939
- </button>
940
-
941
- <script>
942
- // Hero rotator — cycle the .active slide (skipped for reduced-motion users)
943
- (function() {
944
- const slides = [...document.querySelectorAll('.hero-slide')];
945
- if (slides.length < 2) return;
946
- if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
947
- let i = 0;
948
- setInterval(() => {
949
- slides[i].classList.remove('active');
950
- i = (i + 1) % slides.length;
951
- slides[i].classList.add('active');
952
- }, 5200);
953
- })();
954
-
955
- // Back to top
956
- (function() {
957
- const btn = document.getElementById('toTop');
958
- if (!btn) return;
959
- function upd() { (window.scrollY > 600) ? btn.classList.add('show') : btn.classList.remove('show'); }
960
- window.addEventListener('scroll', upd, { passive: true }); upd();
961
- btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
962
- })();
963
-
964
- // Sticky sub-nav after the hero scrolls past
965
- (function() {
966
- const subnav = document.getElementById('subnav');
967
- const hero = document.querySelector('.hero');
968
- function update() {
969
- const heroBottom = hero.getBoundingClientRect().bottom;
970
- if (heroBottom < 0) subnav.classList.add('visible');
971
- else subnav.classList.remove('visible');
972
- }
973
- window.addEventListener('scroll', update, { passive: true });
974
- update();
975
- })();
976
-
977
- // Hamburger / drawer
978
- (function() {
979
- const burger = document.querySelector('.burger');
980
- const drawer = document.getElementById('drawer');
981
- if (!burger || !drawer) return;
982
- const backdrop = document.createElement('div');
983
- backdrop.className = 'drawer-backdrop';
984
- document.body.appendChild(backdrop);
985
- function open() {
986
- drawer.classList.add('open'); backdrop.classList.add('open');
987
- document.body.classList.add('drawer-open');
988
- burger.setAttribute('aria-expanded', 'true'); drawer.setAttribute('aria-hidden', 'false');
989
- }
990
- function close() {
991
- drawer.classList.remove('open'); backdrop.classList.remove('open');
992
- document.body.classList.remove('drawer-open');
993
- burger.setAttribute('aria-expanded', 'false'); drawer.setAttribute('aria-hidden', 'true');
994
- }
995
- burger.addEventListener('click', () => { drawer.classList.contains('open') ? close() : open(); });
996
- backdrop.addEventListener('click', close);
997
- document.querySelector('.drawer-close').addEventListener('click', close);
998
- drawer.querySelectorAll('a').forEach(a => a.addEventListener('click', close));
999
- document.addEventListener('keydown', e => { if (e.key === 'Escape' && drawer.classList.contains('open')) close(); });
1000
- })();
1001
- </script>
1002
-
1003
- </body>
1004
- </html>