@roottale/cms-renderer-next 0.47.0 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/blog-filter-BKCGM0qd.d.ts +29 -0
  3. package/dist/{chunk-6MFHA555.js → chunk-27RT6B3O.js} +1 -1
  4. package/dist/chunk-27RT6B3O.js.map +1 -0
  5. package/dist/chunk-2QU4JU46.js +3927 -0
  6. package/dist/chunk-2QU4JU46.js.map +1 -0
  7. package/dist/chunk-4RQ3SEDL.js +106 -0
  8. package/dist/chunk-4RQ3SEDL.js.map +1 -0
  9. package/dist/{chunk-OMXEU4LS.js → chunk-QYNEOFL7.js} +140 -136
  10. package/dist/chunk-QYNEOFL7.js.map +1 -0
  11. package/dist/{chunk-FUA5H6JE.js → chunk-U2IM6OCQ.js} +46 -2
  12. package/dist/chunk-U2IM6OCQ.js.map +1 -0
  13. package/dist/cms-public.css +137 -18
  14. package/dist/concerns-client.d.ts +1 -1
  15. package/dist/expertise-client.d.ts +1 -1
  16. package/dist/expertise-client.js +1 -1
  17. package/dist/hero-carousel-client.js +1 -1
  18. package/dist/routes.d.ts +377 -21
  19. package/dist/routes.js +273 -106
  20. package/dist/routes.js.map +1 -1
  21. package/dist/seo-asset-hosts.d.ts +36 -0
  22. package/dist/seo-asset-hosts.js +391 -0
  23. package/dist/seo-asset-hosts.js.map +1 -0
  24. package/dist/server.d.ts +34 -216
  25. package/dist/server.js +7 -7
  26. package/dist/{site-config-Dicv57oY.d.ts → site-config-DNpvlqA5.d.ts} +201 -201
  27. package/dist/spaces-client.d.ts +1 -1
  28. package/dist/team-expanding-stage-client.d.ts +1 -1
  29. package/dist/team-expanding-stage-client.js +1 -1
  30. package/dist/templates.d.ts +64 -25
  31. package/dist/templates.js +14 -7
  32. package/dist/templates.js.map +1 -1
  33. package/package.json +7 -2
  34. package/dist/chunk-6MFHA555.js.map +0 -1
  35. package/dist/chunk-FUA5H6JE.js.map +0 -1
  36. package/dist/chunk-FZK3EXYZ.js +0 -103
  37. package/dist/chunk-FZK3EXYZ.js.map +0 -1
  38. package/dist/chunk-OMXEU4LS.js.map +0 -1
  39. package/dist/chunk-ZHXYCABN.js +0 -3584
  40. package/dist/chunk-ZHXYCABN.js.map +0 -1
@@ -1,3584 +0,0 @@
1
- import {
2
- BeforeAfterSlider
3
- } from "./chunk-U5AKACPN.js";
4
- import {
5
- ContactMapClickToLoad
6
- } from "./chunk-LEHHSI6G.js";
7
- import {
8
- ExpertiseTabStage
9
- } from "./chunk-6MFHA555.js";
10
- import {
11
- TeamExpandingStageClient
12
- } from "./chunk-FZK3EXYZ.js";
13
- import {
14
- SpacesStage
15
- } from "./chunk-K62RR4UQ.js";
16
- import {
17
- ConcernsFigureMap
18
- } from "./chunk-H46JUIHZ.js";
19
- import {
20
- AnimateOnScroll,
21
- CountUpStat,
22
- Marquee,
23
- StaggerGroup,
24
- StaggerItem
25
- } from "./chunk-ZL2NA3QT.js";
26
- import {
27
- LeadFormMeta
28
- } from "./chunk-VG6UUF4D.js";
29
- import {
30
- collectCategories,
31
- collectCategoriesWithCounts,
32
- collectCollectionCategories,
33
- defaultCategoryHref,
34
- filterPostsByCategory
35
- } from "./chunk-FUA5H6JE.js";
36
- import {
37
- SectionSchema
38
- } from "./chunk-UM37DOIK.js";
39
- import {
40
- HeroCarouselViewport
41
- } from "./chunk-OMXEU4LS.js";
42
-
43
- // src/server.tsx
44
- import {
45
- DEFAULT_BLOG_SETTINGS,
46
- fetchBlogSettings,
47
- fetchPost,
48
- fetchPosts,
49
- fetchTheme,
50
- jsonLdJson,
51
- resolvePostDisplay,
52
- selectRelatedPosts
53
- } from "@roottale/cms-client/server";
54
-
55
- // ../cms-i18n/src/index.ts
56
- var DICTS = {
57
- ko: {
58
- blogListEmpty: "\uC544\uC9C1 \uBC1C\uD589\uB41C \uAE00\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
59
- blogCategoriesEmpty: "\uC544\uC9C1 \uCE74\uD14C\uACE0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
60
- allCategoriesLabel: "\uC804\uCCB4",
61
- relatedPostsTitle: "\uAD00\uB828 \uAE00",
62
- postNotFound: "\uAE00\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
63
- pageNotFound: "\uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
64
- tableOfContentsTitle: "\uBAA9\uCC28"
65
- },
66
- en: {
67
- blogListEmpty: "No posts published yet.",
68
- blogCategoriesEmpty: "No categories yet.",
69
- allCategoriesLabel: "All",
70
- relatedPostsTitle: "Related posts",
71
- postNotFound: "Post not found.",
72
- pageNotFound: "Page not found.",
73
- tableOfContentsTitle: "Table of contents"
74
- }
75
- };
76
- function resolveLocale(locale) {
77
- return locale === "en" ? "en" : "ko";
78
- }
79
- function t(locale, key) {
80
- return DICTS[resolveLocale(locale)][key];
81
- }
82
-
83
- // src/server.tsx
84
- import {
85
- bindingSrcValue,
86
- bindingTextValue,
87
- boundFieldValue,
88
- extractFaqEntries,
89
- faqPageJsonLd,
90
- isAllowedEmbedSrc as isAllowedEmbedSrc2,
91
- postsForCollection,
92
- postsNotInAnyCollection,
93
- resolvePostCollection,
94
- resolvePostPath
95
- } from "@roottale/cms-core";
96
-
97
- // src/LeadForm.tsx
98
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
99
- var VERTICAL_LABEL = {
100
- consulting: "\uCEE8\uC124\uD305",
101
- medical: "\uC758\uB8CC",
102
- tax: "\uC138\uBB34",
103
- legal: "\uBC95\uB960"
104
- };
105
- var VERTICAL_OPTIONS = [
106
- { value: "consulting", label: "\uCEE8\uC124\uD305 / \uC77C\uBC18" },
107
- { value: "medical", label: "\uC758\uB8CC (\uBCD1\uC758\uC6D0\xB7\uCE58\uACFC\xB7\uD55C\uC758\uC6D0)" },
108
- { value: "tax", label: "\uC138\uBB34 (\uC138\uBB34\uC0AC\xB7\uD68C\uACC4\uC0AC)" },
109
- { value: "legal", label: "\uBC95\uB960 (\uBC95\uBB34\uBC95\uC778\xB7\uBCC0\uD638\uC0AC)" }
110
- ];
111
- function RootTaleLeadForm(props) {
112
- const {
113
- action,
114
- vertical,
115
- redirectUrl,
116
- heading,
117
- description,
118
- submitLabel = "\uC9C4\uB2E8 \uC2E0\uCCAD",
119
- turnstileSiteKey,
120
- className
121
- } = props;
122
- const formClass = ["rt-cms-lead-form", className].filter(Boolean).join(" ");
123
- const isMedical = vertical === "medical";
124
- return /* @__PURE__ */ jsxs(
125
- "form",
126
- {
127
- method: "post",
128
- action,
129
- "data-roottale-cms": "lead-form",
130
- className: formClass,
131
- children: [
132
- /* @__PURE__ */ jsx(LeadFormMeta, { formId: "contact" }),
133
- heading ? /* @__PURE__ */ jsx("h2", { className: "rt-cms-lead-heading", children: heading }) : null,
134
- description ? /* @__PURE__ */ jsx("p", { className: "rt-cms-lead-description", children: description }) : null,
135
- redirectUrl ? /* @__PURE__ */ jsx("input", { type: "hidden", name: "_redirect_url", value: redirectUrl }) : null,
136
- vertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
137
- /* @__PURE__ */ jsx("input", { type: "hidden", name: "vertical", value: vertical }),
138
- /* @__PURE__ */ jsxs("p", { className: "rt-cms-lead-vertical-label", children: [
139
- "\uBD84\uC57C: ",
140
- /* @__PURE__ */ jsx("strong", { children: VERTICAL_LABEL[vertical] })
141
- ] })
142
- ] }) : /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
143
- /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
144
- "\uBD84\uC57C ",
145
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
146
- ] }),
147
- /* @__PURE__ */ jsxs(
148
- "select",
149
- {
150
- name: "vertical",
151
- required: true,
152
- className: "rt-cms-field-input rt-cms-field-select",
153
- defaultValue: "",
154
- children: [
155
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "\uC120\uD0DD\uD558\uC138\uC694" }),
156
- VERTICAL_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
157
- ]
158
- }
159
- )
160
- ] }),
161
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
162
- /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
163
- "\uC774\uB984 ",
164
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
165
- ] }),
166
- /* @__PURE__ */ jsx(
167
- "input",
168
- {
169
- type: "text",
170
- name: "contact_name",
171
- required: true,
172
- autoComplete: "name",
173
- className: "rt-cms-field-input"
174
- }
175
- )
176
- ] }),
177
- isMedical ? (
178
- // 환자 문의에는 "사업체명" 개념이 없다 — 화면에는 노출하지 않되,
179
- // intake 가 vertical 불문 hard-required 로 검증하는 계약(위 파일
180
- // 헤더 코멘트 참조)을 만족시키기 위해 자리표시자만 hidden 전송.
181
- /* @__PURE__ */ jsx("input", { type: "hidden", name: "business_name", value: "-" })
182
- ) : /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
183
- /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
184
- "\uC0AC\uC5C5\uCCB4\uBA85 ",
185
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
186
- ] }),
187
- /* @__PURE__ */ jsx(
188
- "input",
189
- {
190
- type: "text",
191
- name: "business_name",
192
- required: true,
193
- autoComplete: "organization",
194
- className: "rt-cms-field-input"
195
- }
196
- )
197
- ] }),
198
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
199
- /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
200
- "\uC804\uD654\uBC88\uD638 ",
201
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
202
- ] }),
203
- /* @__PURE__ */ jsx(
204
- "input",
205
- {
206
- type: "tel",
207
- name: "phone",
208
- required: true,
209
- autoComplete: "tel",
210
- inputMode: "tel",
211
- pattern: "0\\d{1,2}-?\\d{3,4}-?\\d{4}",
212
- placeholder: "010-1234-5678",
213
- className: "rt-cms-field-input"
214
- }
215
- )
216
- ] }),
217
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
218
- /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
219
- "\uC774\uBA54\uC77C ",
220
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
221
- ] }),
222
- /* @__PURE__ */ jsx(
223
- "input",
224
- {
225
- type: "email",
226
- name: "email",
227
- required: true,
228
- autoComplete: "email",
229
- className: "rt-cms-field-input"
230
- }
231
- )
232
- ] }),
233
- isMedical ? /* @__PURE__ */ jsxs(Fragment, { children: [
234
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
235
- /* @__PURE__ */ jsx("span", { className: "rt-cms-field-label", children: "\uC0C1\uB2F4 \uD76C\uB9DD \uBD84\uC57C\xB7\uC99D\uC0C1 (\uC120\uD0DD)" }),
236
- /* @__PURE__ */ jsx(
237
- "input",
238
- {
239
- type: "text",
240
- name: "consultation_field",
241
- placeholder: "\uC608: \uBB34\uB98E \uD1B5\uC99D, \uC815\uAE30\uAC80\uC9C4",
242
- className: "rt-cms-field-input"
243
- }
244
- )
245
- ] }),
246
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
247
- /* @__PURE__ */ jsx("span", { className: "rt-cms-field-label", children: "\uBB38\uC758 \uB0B4\uC6A9 (\uC120\uD0DD)" }),
248
- /* @__PURE__ */ jsx(
249
- "textarea",
250
- {
251
- name: "message",
252
- rows: 4,
253
- placeholder: "\uC99D\uC0C1\uC774 \uC5B8\uC81C\uBD80\uD130 \uC788\uC5C8\uB294\uC9C0, \uAD81\uAE08\uD558\uC2E0 \uC810\uC744 \uC790\uC720\uB86D\uAC8C \uC801\uC5B4\uC8FC\uC138\uC694.",
254
- className: "rt-cms-field-input rt-cms-field-textarea"
255
- }
256
- )
257
- ] })
258
- ] }) : /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
259
- /* @__PURE__ */ jsx("span", { className: "rt-cms-field-label", children: "\uD604\uC7AC \uC0AC\uC774\uD2B8 URL (\uC120\uD0DD)" }),
260
- /* @__PURE__ */ jsx(
261
- "input",
262
- {
263
- type: "url",
264
- name: "current_site_url",
265
- placeholder: "https://",
266
- className: "rt-cms-field-input"
267
- }
268
- ),
269
- /* @__PURE__ */ jsx("span", { className: "rt-cms-field-hint", children: "\uC5C6\uC73C\uBA74 \uBE44\uC6CC\uB450\uC138\uC694." })
270
- ] }),
271
- /* @__PURE__ */ jsxs("label", { className: "rt-cms-lead-consent", children: [
272
- /* @__PURE__ */ jsx("input", { type: "checkbox", name: "privacy_consent", required: true }),
273
- /* @__PURE__ */ jsxs("span", { children: [
274
- /* @__PURE__ */ jsx("strong", { children: "(\uD544\uC218)" }),
275
- " \uAC1C\uC778\uC815\uBCF4 \uC218\uC9D1\xB7\uC774\uC6A9\uC5D0 \uB3D9\uC758\uD569\uB2C8\uB2E4. (\uC218\uC9D1 \uD56D\uBAA9:",
276
- " ",
277
- isMedical ? "\uC774\uB984\xB7\uC5F0\uB77D\uCC98\xB7\uC774\uBA54\uC77C\xB7\uC0C1\uB2F4 \uB0B4\uC6A9(\uC791\uC131 \uC2DC)\xB7\uC0AC\uC774\uD2B8 \uC774\uC6A9 \uAE30\uB85D(\uBC29\uBB38 \uD398\uC774\uC9C0\xB7\uC0C1\uD638\uC791\uC6A9)" : "\uC774\uB984\xB7\uC5F0\uB77D\uCC98\xB7\uC774\uBA54\uC77C\xB7\uC0AC\uC5C5\uCCB4\uBA85\xB7\uC0AC\uC774\uD2B8 \uC774\uC6A9 \uAE30\uB85D(\uBC29\uBB38 \uD398\uC774\uC9C0\xB7\uC0C1\uD638\uC791\uC6A9)",
278
- ". \uBCF4\uAD00 \uAE30\uAC04: \uBB38\uC758 \uC885\uB8CC \uD6C4 3\uB144)"
279
- ] })
280
- ] }),
281
- vertical === "medical" ? /* @__PURE__ */ jsxs("label", { className: "rt-cms-lead-consent", children: [
282
- /* @__PURE__ */ jsx(
283
- "input",
284
- {
285
- type: "checkbox",
286
- name: "overseas_transfer_consent",
287
- required: true
288
- }
289
- ),
290
- /* @__PURE__ */ jsxs("span", { children: [
291
- /* @__PURE__ */ jsx("strong", { children: "(\uD544\uC218, \uC758\uB8CC)" }),
292
- " \uC758\uB8CC PII \uC758 \uAD6D\uC678\uC774\uC804(\uBCF4\uAD00\xB7\uCC98\uB9AC)\uC5D0 \uB3D9\uC758\uD569\uB2C8\uB2E4. (\uC758\uB8CC\uBC95 \xA721\xB7\uAC1C\uC778\uC815\uBCF4\uBCF4\uD638\uBC95 \xA728 \u2014 ADR-0018)"
293
- ] })
294
- ] }) : null,
295
- turnstileSiteKey ? /* @__PURE__ */ jsxs(Fragment, { children: [
296
- /* @__PURE__ */ jsx(
297
- "script",
298
- {
299
- src: "https://challenges.cloudflare.com/turnstile/v0/api.js",
300
- async: true,
301
- defer: true
302
- }
303
- ),
304
- /* @__PURE__ */ jsx(
305
- "div",
306
- {
307
- className: "cf-turnstile",
308
- "data-sitekey": turnstileSiteKey,
309
- "data-action": "turnstile-spin-v1"
310
- }
311
- )
312
- ] }) : null,
313
- /* @__PURE__ */ jsx("div", { className: "rt-cms-lead-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "rt-cms-lead-submit", children: submitLabel }) })
314
- ]
315
- }
316
- );
317
- }
318
-
319
- // src/templates/sections.tsx
320
- import { isAllowedEmbedSrc, isAllowedMapEmbedSrc } from "@roottale/cms-core";
321
-
322
- // src/templates/metric-value.ts
323
- var LEADING_NUMBER_PATTERN = /^(\d[\d,]*)(\.(\d+))?/;
324
- function parseMetricValue(raw) {
325
- const match = LEADING_NUMBER_PATTERN.exec(raw);
326
- if (!match) {
327
- return null;
328
- }
329
- const [matched, integerPart, , fractionPart] = match;
330
- const integerLiteral = (integerPart ?? "").replace(/,/g, "");
331
- const numericLiteral = fractionPart ? `${integerLiteral}.${fractionPart}` : integerLiteral;
332
- const value = Number(numericLiteral);
333
- if (!Number.isFinite(value)) {
334
- return null;
335
- }
336
- return {
337
- value,
338
- decimals: fractionPart?.length ?? 0,
339
- suffix: raw.slice(matched.length)
340
- };
341
- }
342
-
343
- // src/templates/sections.tsx
344
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
345
- function sectionThemeStyle(theme) {
346
- if (!theme) return void 0;
347
- const style = {};
348
- for (const [key, value] of Object.entries(theme)) {
349
- if (key.startsWith("--")) style[key] = value;
350
- }
351
- return Object.keys(style).length > 0 ? style : void 0;
352
- }
353
- function CtaLink({
354
- cta,
355
- className,
356
- track
357
- }) {
358
- return /* @__PURE__ */ jsx2(
359
- "a",
360
- {
361
- className: className ?? "rt-tpl-btn rt-tpl-btn--primary",
362
- href: cta.href,
363
- "data-track": track,
364
- children: cta.label
365
- }
366
- );
367
- }
368
- function ctaTrackForHref(href) {
369
- return href.startsWith("tel:") ? "cta_banner" : "cta_hero";
370
- }
371
- function contactHrefTrack(href) {
372
- if (href.startsWith("tel:")) return "phone_click";
373
- if (href.startsWith("mailto:")) return "email_click";
374
- return void 0;
375
- }
376
- function TemplateHeroProof({
377
- proofMetrics
378
- }) {
379
- if (!proofMetrics || proofMetrics.items.length === 0) return null;
380
- return /* @__PURE__ */ jsx2(
381
- "div",
382
- {
383
- className: "rt-tpl-hero-proof",
384
- "data-position": proofMetrics.position,
385
- "aria-label": "\uC2E0\uB8B0 \uC9C0\uD45C",
386
- children: proofMetrics.items.map((metric) => /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-proof-item", children: [
387
- metric.imageSrc ? /* @__PURE__ */ jsx2(
388
- "img",
389
- {
390
- className: "rt-tpl-hero-proof-icon",
391
- src: metric.imageSrc,
392
- alt: metric.imageAlt ?? "",
393
- loading: "lazy"
394
- }
395
- ) : null,
396
- /* @__PURE__ */ jsxs2("span", { className: "rt-tpl-hero-proof-value", children: [
397
- metric.value,
398
- metric.unit ? /* @__PURE__ */ jsx2("span", { children: metric.unit }) : null
399
- ] }),
400
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-proof-label", children: metric.label }),
401
- metric.source ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-proof-source", children: metric.source }) : null
402
- ] }, `${metric.value}-${metric.label}`))
403
- }
404
- );
405
- }
406
- function TemplateHeroCarousel(section) {
407
- const {
408
- eyebrow,
409
- title,
410
- subtitle,
411
- cta,
412
- slides,
413
- autoplay,
414
- intervalMs,
415
- transition,
416
- proofMetrics,
417
- scrim
418
- } = section.props;
419
- const safeSlides = slides ?? [];
420
- return /* @__PURE__ */ jsxs2(
421
- "section",
422
- {
423
- "data-roottale-tpl": "hero",
424
- "data-variant": "carousel",
425
- id: section.id,
426
- className: "rt-tpl-section rt-tpl-hero rt-tpl-hero--carousel",
427
- style: sectionThemeStyle(section.theme),
428
- children: [
429
- safeSlides.length >= 2 ? /* @__PURE__ */ jsx2(
430
- HeroCarouselViewport,
431
- {
432
- slides: safeSlides,
433
- autoplay: autoplay ?? true,
434
- intervalMs: intervalMs ?? 6e3,
435
- scrim,
436
- transition
437
- }
438
- ) : null,
439
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-hero-carousel-overlay", children: [
440
- eyebrow ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-eyebrow", children: eyebrow }) : null,
441
- /* @__PURE__ */ jsx2("h1", { className: "rt-tpl-hero-title rt-tpl-hero-carousel-title", children: title.split("\n").map((line, i) => /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-title-line", children: line }, i)) }),
442
- subtitle ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-desc rt-tpl-hero-carousel-subtitle", children: subtitle }) : null,
443
- cta ? /* @__PURE__ */ jsx2(
444
- CtaLink,
445
- {
446
- cta,
447
- track: "cta_hero",
448
- className: "rt-tpl-btn rt-tpl-btn--primary rt-tpl-hero-carousel-cta"
449
- }
450
- ) : null,
451
- /* @__PURE__ */ jsx2(TemplateHeroProof, { proofMetrics })
452
- ] })
453
- ]
454
- }
455
- );
456
- }
457
- function TemplateHeroOverlayPanel(section) {
458
- const { eyebrow, title, description, imageSrc, imageAlt, cta, proofMetrics } = section.props;
459
- return /* @__PURE__ */ jsxs2(
460
- "section",
461
- {
462
- "data-roottale-tpl": "hero",
463
- "data-variant": "overlay-panel",
464
- id: section.id,
465
- className: "rt-tpl-section rt-tpl-hero rt-tpl-hero--overlay-panel",
466
- style: sectionThemeStyle(section.theme),
467
- children: [
468
- /* @__PURE__ */ jsx2(
469
- "img",
470
- {
471
- className: "rt-tpl-hero-overlay-img",
472
- src: imageSrc ?? "",
473
- alt: imageAlt ?? "",
474
- loading: "eager"
475
- }
476
- ),
477
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-hero-overlay-scrim", "aria-hidden": "true" }),
478
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-container rt-tpl-hero-overlay-inner", children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-overlay-panel", children: [
479
- eyebrow ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-eyebrow", children: eyebrow }) : null,
480
- /* @__PURE__ */ jsx2("h1", { className: "rt-tpl-hero-title", children: title.split("\n").map((line, i) => /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-title-line", children: line }, i)) }),
481
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-desc", children: description }) : null,
482
- cta ? /* @__PURE__ */ jsx2(CtaLink, { cta, track: "cta_hero" }) : null,
483
- /* @__PURE__ */ jsx2(TemplateHeroProof, { proofMetrics })
484
- ] }) })
485
- ]
486
- }
487
- );
488
- }
489
- function TemplateHero(section) {
490
- if (section.variant === "carousel") return TemplateHeroCarousel(section);
491
- if (section.variant === "overlay-panel")
492
- return TemplateHeroOverlayPanel(section);
493
- const { eyebrow, title, description, imageSrc, imageAlt, cta, proofMetrics } = section.props;
494
- const variant = section.variant;
495
- const showImage = variant !== "minimal" && imageSrc;
496
- return /* @__PURE__ */ jsx2(
497
- "section",
498
- {
499
- "data-roottale-tpl": "hero",
500
- "data-variant": variant,
501
- id: section.id,
502
- className: "rt-tpl-section rt-tpl-hero",
503
- style: sectionThemeStyle(section.theme),
504
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-hero-inner", children: [
505
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-copy", children: [
506
- eyebrow ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-eyebrow", children: eyebrow }) : null,
507
- /* @__PURE__ */ jsx2("h1", { className: "rt-tpl-hero-title", children: title.split("\n").map((line, i) => /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-title-line", children: line }, i)) }),
508
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-desc", children: description }) : null,
509
- cta ? /* @__PURE__ */ jsx2(CtaLink, { cta, track: "cta_hero" }) : null,
510
- /* @__PURE__ */ jsx2(TemplateHeroProof, { proofMetrics })
511
- ] }),
512
- showImage ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-hero-media", children: /* @__PURE__ */ jsx2(
513
- "img",
514
- {
515
- className: "rt-tpl-hero-img",
516
- src: imageSrc,
517
- alt: imageAlt ?? "",
518
- loading: "eager"
519
- }
520
- ) }) : null
521
- ] })
522
- }
523
- );
524
- }
525
- function TemplateRichText(section) {
526
- const { heading, body } = section.props;
527
- return /* @__PURE__ */ jsx2(
528
- "section",
529
- {
530
- "data-roottale-tpl": "rich-text",
531
- id: section.id,
532
- className: "rt-tpl-section rt-tpl-rich-text",
533
- style: sectionThemeStyle(section.theme),
534
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
535
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
536
- body.split("\n\n").map((para, i) => /* @__PURE__ */ jsx2("p", { className: "rt-tpl-rich-text-p", children: para }, i))
537
- ] })
538
- }
539
- );
540
- }
541
- function servicesIndexNum(i) {
542
- return String(i + 1).padStart(2, "0");
543
- }
544
- function ServicesItemMeta({
545
- item
546
- }) {
547
- const hasChips = Boolean(item.chips && item.chips.length > 0);
548
- const hasMeta = Boolean(item.meta && item.meta.length > 0);
549
- if (!hasChips && !hasMeta) return null;
550
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-services-meta-block", children: [
551
- hasChips ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-services-chips", "aria-label": `${item.title} \uD0DC\uADF8`, children: (item.chips ?? []).map((chip) => /* @__PURE__ */ jsx2("li", { children: chip }, chip)) }) : null,
552
- hasMeta ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-services-meta", "aria-label": `${item.title} \uBA54\uD0C0`, children: (item.meta ?? []).map((meta) => /* @__PURE__ */ jsx2("li", { children: meta }, meta)) }) : null
553
- ] });
554
- }
555
- function ServicesIcon({
556
- item
557
- }) {
558
- if (!item.iconSrc) return null;
559
- return /* @__PURE__ */ jsx2(
560
- "img",
561
- {
562
- className: "rt-tpl-services-icon",
563
- src: item.iconSrc,
564
- alt: item.iconAlt ?? "",
565
- loading: "lazy"
566
- }
567
- );
568
- }
569
- function TemplateServicesActionRail(section) {
570
- const { heading, description, items } = section.props;
571
- return /* @__PURE__ */ jsx2(
572
- "section",
573
- {
574
- "data-roottale-tpl": "services",
575
- "data-variant": "action-rail",
576
- id: section.id,
577
- className: "rt-tpl-section rt-tpl-services",
578
- style: sectionThemeStyle(section.theme),
579
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
580
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
581
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
582
- /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-services-action-rail", children: items.map((item, i) => {
583
- const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
584
- /* @__PURE__ */ jsx2(ServicesIcon, { item }),
585
- /* @__PURE__ */ jsxs2("span", { className: "rt-tpl-services-action-body", children: [
586
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-title", children: item.title }),
587
- item.subtitle ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-subtitle", children: item.subtitle }) : null,
588
- item.description ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-desc", children: item.description }) : null,
589
- /* @__PURE__ */ jsx2(ServicesItemMeta, { item }),
590
- item.ctaLabel ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-action-label", children: item.ctaLabel }) : null
591
- ] })
592
- ] });
593
- return /* @__PURE__ */ jsx2("li", { "data-kind": item.kind, children: item.href ? /* @__PURE__ */ jsx2(
594
- "a",
595
- {
596
- className: "rt-tpl-services-action-card",
597
- href: item.href,
598
- "data-track": "service_card",
599
- children: body
600
- }
601
- ) : /* @__PURE__ */ jsx2("div", { className: "rt-tpl-services-action-card", children: body }) }, `${item.title}-${i}`);
602
- }) })
603
- ] })
604
- }
605
- );
606
- }
607
- function TemplateServicesPhotoCards(section) {
608
- const { heading, description, items } = section.props;
609
- return /* @__PURE__ */ jsx2(
610
- "section",
611
- {
612
- "data-roottale-tpl": "services",
613
- "data-variant": "photo-cards",
614
- id: section.id,
615
- className: "rt-tpl-section rt-tpl-services",
616
- style: sectionThemeStyle(section.theme),
617
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
618
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
619
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
620
- /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-services-photo-list", children: items.map((item, i) => {
621
- const card = /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-services-photo-card", children: [
622
- /* @__PURE__ */ jsx2(
623
- "img",
624
- {
625
- className: "rt-tpl-services-photo-img",
626
- src: item.imageSrc ?? "",
627
- alt: item.imageAlt ?? "",
628
- loading: "lazy"
629
- }
630
- ),
631
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-services-photo-body", children: [
632
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-services-title", children: item.title }),
633
- item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-desc", children: item.description }) : null,
634
- item.ctaLabel ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-action-label", children: item.ctaLabel }) : null
635
- ] })
636
- ] });
637
- return /* @__PURE__ */ jsx2("li", { children: item.href ? /* @__PURE__ */ jsx2(
638
- "a",
639
- {
640
- className: "rt-tpl-services-photo-link",
641
- href: item.href,
642
- "data-track": "service_card",
643
- children: card
644
- }
645
- ) : card }, `${item.title}-${i}`);
646
- }) })
647
- ] })
648
- }
649
- );
650
- }
651
- function TemplateServicesIndexRows(section) {
652
- const { heading, description, items } = section.props;
653
- return /* @__PURE__ */ jsx2(
654
- "section",
655
- {
656
- "data-roottale-tpl": "services",
657
- "data-variant": "index-rows",
658
- id: section.id,
659
- className: "rt-tpl-section rt-tpl-services",
660
- style: sectionThemeStyle(section.theme),
661
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
662
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
663
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
664
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ol", className: "rt-tpl-services-index-list", children: items.map((item, i) => {
665
- const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
666
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-index-num", "aria-hidden": "true", children: servicesIndexNum(i) }),
667
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-services-index-body", children: [
668
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-services-index-title", children: item.title }),
669
- item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-index-desc", children: item.description }) : null
670
- ] })
671
- ] });
672
- return /* @__PURE__ */ jsx2(StaggerItem, { as: "li", className: "rt-tpl-services-index-row", children: item.href ? /* @__PURE__ */ jsx2(
673
- "a",
674
- {
675
- className: "rt-tpl-services-index-link",
676
- href: item.href,
677
- "data-track": "service_card",
678
- children: body
679
- }
680
- ) : /* @__PURE__ */ jsx2("div", { className: "rt-tpl-services-index-link", children: body }) }, i);
681
- }) })
682
- ] })
683
- }
684
- );
685
- }
686
- function TemplateServicesSplitFeatured(section) {
687
- const { heading, description, items } = section.props;
688
- const [featured, ...rest] = items;
689
- return /* @__PURE__ */ jsx2(
690
- "section",
691
- {
692
- "data-roottale-tpl": "services",
693
- "data-variant": "split-featured",
694
- id: section.id,
695
- className: "rt-tpl-section rt-tpl-services",
696
- style: sectionThemeStyle(section.theme),
697
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
698
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
699
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
700
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-services-split", children: [
701
- /* @__PURE__ */ jsx2(
702
- AnimateOnScroll,
703
- {
704
- as: "div",
705
- variant: "fade-up",
706
- className: "rt-tpl-services-split-featured",
707
- children: featured ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
708
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-services-split-featured-title", children: featured.title }),
709
- featured.subtitle ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-subtitle", children: featured.subtitle }) : null,
710
- featured.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-split-featured-desc", children: featured.description }) : null,
711
- /* @__PURE__ */ jsx2(ServicesItemMeta, { item: featured }),
712
- featured.href ? /* @__PURE__ */ jsx2(
713
- "a",
714
- {
715
- className: "rt-tpl-btn rt-tpl-btn--primary rt-tpl-services-split-featured-cta",
716
- href: featured.href,
717
- "data-track": "service_card",
718
- children: featured.ctaLabel ?? "\uC790\uC138\uD788 \uBCF4\uAE30"
719
- }
720
- ) : null
721
- ] }) : null
722
- }
723
- ),
724
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ol", className: "rt-tpl-services-split-list", children: rest.map((item, i) => {
725
- const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
726
- /* @__PURE__ */ jsx2("h4", { className: "rt-tpl-services-split-item-title", children: item.title }),
727
- item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-split-item-desc", children: item.description }) : null,
728
- /* @__PURE__ */ jsx2(ServicesItemMeta, { item })
729
- ] });
730
- return /* @__PURE__ */ jsx2(
731
- StaggerItem,
732
- {
733
- as: "li",
734
- className: "rt-tpl-services-split-item",
735
- children: item.href ? /* @__PURE__ */ jsx2(
736
- "a",
737
- {
738
- className: "rt-tpl-services-split-item-link",
739
- href: item.href,
740
- "data-track": "service_card",
741
- children: body
742
- }
743
- ) : body
744
- },
745
- i
746
- );
747
- }) })
748
- ] })
749
- ] })
750
- }
751
- );
752
- }
753
- function TemplateServices(section) {
754
- if (section.variant === "index-rows") return TemplateServicesIndexRows(section);
755
- if (section.variant === "split-featured")
756
- return TemplateServicesSplitFeatured(section);
757
- if (section.variant === "action-rail")
758
- return TemplateServicesActionRail(section);
759
- if (section.variant === "photo-cards")
760
- return TemplateServicesPhotoCards(section);
761
- const { heading, description, items } = section.props;
762
- return /* @__PURE__ */ jsx2(
763
- "section",
764
- {
765
- "data-roottale-tpl": "services",
766
- "data-variant": section.variant,
767
- id: section.id,
768
- className: "rt-tpl-section rt-tpl-services",
769
- style: sectionThemeStyle(section.theme),
770
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
771
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
772
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
773
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-services-list", children: items.map((item, i) => {
774
- const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
775
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-services-title", children: item.title }),
776
- item.subtitle ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-subtitle", children: item.subtitle }) : null,
777
- item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-desc", children: item.description }) : null,
778
- /* @__PURE__ */ jsx2(ServicesItemMeta, { item }),
779
- item.ctaLabel ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-services-action-label", children: item.ctaLabel }) : null
780
- ] });
781
- return /* @__PURE__ */ jsx2(StaggerItem, { as: "li", className: "rt-tpl-services-item", children: item.href ? /* @__PURE__ */ jsxs2(
782
- "a",
783
- {
784
- className: "rt-tpl-services-link",
785
- href: item.href,
786
- "data-track": "service_card",
787
- children: [
788
- /* @__PURE__ */ jsx2(ServicesIcon, { item }),
789
- body
790
- ]
791
- }
792
- ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
793
- /* @__PURE__ */ jsx2(ServicesIcon, { item }),
794
- body
795
- ] }) }, i);
796
- }) })
797
- ] })
798
- }
799
- );
800
- }
801
- function FooterBrand(section) {
802
- const { businessName, logo, description } = section.props;
803
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-brand", children: [
804
- logo ? /* @__PURE__ */ jsx2(
805
- "img",
806
- {
807
- className: "rt-tpl-footer-logo",
808
- src: logo.src,
809
- alt: logo.alt,
810
- loading: "lazy"
811
- }
812
- ) : null,
813
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-footer-name", children: businessName }),
814
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-footer-description", children: description }) : null
815
- ] });
816
- }
817
- function FooterContact({
818
- contactInfo
819
- }) {
820
- if (!contactInfo?.address && !contactInfo?.phone && !contactInfo?.email) {
821
- return null;
822
- }
823
- return /* @__PURE__ */ jsxs2("address", { className: "rt-tpl-footer-contact", children: [
824
- contactInfo.address ? /* @__PURE__ */ jsx2("span", { children: contactInfo.address }) : null,
825
- contactInfo.phone ? /* @__PURE__ */ jsx2(
826
- "a",
827
- {
828
- href: telHref(contactInfo.phone),
829
- "data-track": "phone_click",
830
- "data-track-placement": "footer",
831
- children: contactInfo.phone
832
- }
833
- ) : null,
834
- contactInfo.email ? /* @__PURE__ */ jsx2(
835
- "a",
836
- {
837
- href: `mailto:${contactInfo.email}`,
838
- "data-track": "email_click",
839
- "data-track-placement": "footer",
840
- children: contactInfo.email
841
- }
842
- ) : null
843
- ] });
844
- }
845
- function FooterLegal(section) {
846
- const { legalInfo, privacyHref, termsHref, copyright, socialLinks, credit } = section.props;
847
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-bottom", children: [
848
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-legal", children: [
849
- legalInfo ? /* @__PURE__ */ jsx2("p", { children: legalInfo }) : null,
850
- copyright ? /* @__PURE__ */ jsx2("p", { children: copyright }) : null
851
- ] }),
852
- /* @__PURE__ */ jsxs2("nav", { className: "rt-tpl-footer-policy", "aria-label": "\uBC95\uC801 \uACE0\uC9C0", children: [
853
- privacyHref ? /* @__PURE__ */ jsx2("a", { href: privacyHref, children: "\uAC1C\uC778\uC815\uBCF4\uCC98\uB9AC\uBC29\uCE68" }) : null,
854
- termsHref ? /* @__PURE__ */ jsx2("a", { href: termsHref, children: "\uC774\uC6A9\uC57D\uAD00" }) : null,
855
- socialLinks?.map((link) => /* @__PURE__ */ jsx2("a", { href: link.href, children: link.kind }, `${link.kind}-${link.href}`))
856
- ] }),
857
- credit === "hidden" ? null : /* @__PURE__ */ jsxs2("p", { className: "rt-tpl-footer-credit", children: [
858
- "Website by",
859
- " ",
860
- /* @__PURE__ */ jsx2("a", { href: "https://roottale.com", target: "_blank", rel: "noopener", children: "RootTale" })
861
- ] })
862
- ] });
863
- }
864
- function TemplateFooterColumns(section) {
865
- const { columns, contactInfo } = section.props;
866
- return /* @__PURE__ */ jsx2(
867
- "footer",
868
- {
869
- role: "contentinfo",
870
- "data-roottale-tpl": "footer",
871
- "data-variant": "columns",
872
- id: section.id,
873
- className: "rt-tpl-footer",
874
- style: sectionThemeStyle(section.theme),
875
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
876
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-columns", children: [
877
- /* @__PURE__ */ jsx2(FooterBrand, { ...section }),
878
- /* @__PURE__ */ jsx2("nav", { className: "rt-tpl-footer-nav", "aria-label": "\uD478\uD130 \uB9C1\uD06C", children: (columns ?? []).map((column) => /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-column", children: [
879
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-footer-column-title", children: column.title }),
880
- /* @__PURE__ */ jsx2("ul", { children: column.links.map((link) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2("a", { href: link.href, children: link.label }) }, `${column.title}-${link.label}-${link.href}`)) })
881
- ] }, column.title)) }),
882
- /* @__PURE__ */ jsx2(FooterContact, { contactInfo })
883
- ] }),
884
- /* @__PURE__ */ jsx2(FooterLegal, { ...section })
885
- ] })
886
- }
887
- );
888
- }
889
- function TemplateFooterMinimal(section) {
890
- return /* @__PURE__ */ jsx2(
891
- "footer",
892
- {
893
- role: "contentinfo",
894
- "data-roottale-tpl": "footer",
895
- "data-variant": "minimal",
896
- id: section.id,
897
- className: "rt-tpl-footer rt-tpl-footer--minimal",
898
- style: sectionThemeStyle(section.theme),
899
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
900
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-footer-minimal-row", children: [
901
- /* @__PURE__ */ jsx2(FooterBrand, { ...section }),
902
- /* @__PURE__ */ jsx2(FooterContact, { contactInfo: section.props.contactInfo })
903
- ] }),
904
- /* @__PURE__ */ jsx2(FooterLegal, { ...section })
905
- ] })
906
- }
907
- );
908
- }
909
- function TemplateFooter(section) {
910
- if (section.variant === "minimal") return TemplateFooterMinimal(section);
911
- return TemplateFooterColumns(section);
912
- }
913
- function faqItemsForJsonLd(section) {
914
- if (section.props.groups && section.props.groups.length > 0) {
915
- return section.props.groups.flatMap((group) => group.items);
916
- }
917
- return section.props.items ?? [];
918
- }
919
- function TemplateFaqAccordion(section) {
920
- const { heading, items, contact } = section.props;
921
- const list = items ?? [];
922
- return /* @__PURE__ */ jsxs2(
923
- "div",
924
- {
925
- className: section.variant === "with-contact" ? "rt-tpl-faq-with-contact" : void 0,
926
- children: [
927
- /* @__PURE__ */ jsxs2("div", { children: [
928
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
929
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-list", children: list.map((item, i) => /* @__PURE__ */ jsxs2("details", { className: "rt-tpl-faq-item", children: [
930
- /* @__PURE__ */ jsx2("summary", { className: "rt-tpl-faq-q", children: item.question }),
931
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-faq-a", children: item.answer })
932
- ] }, i)) })
933
- ] }),
934
- section.variant === "with-contact" && contact ? /* @__PURE__ */ jsxs2("aside", { className: "rt-tpl-faq-contact", "aria-label": "\uC0C1\uB2F4 \uBB38\uC758", children: [
935
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-faq-contact-title", children: contact.title }),
936
- contact.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-faq-contact-desc", children: contact.description }) : null,
937
- contact.phone ? /* @__PURE__ */ jsx2(
938
- "a",
939
- {
940
- className: "rt-tpl-faq-contact-phone",
941
- href: telHref(contact.phone),
942
- "data-track": "cta_call",
943
- children: contact.phone
944
- }
945
- ) : null,
946
- contact.cta ? /* @__PURE__ */ jsx2(
947
- CtaLink,
948
- {
949
- cta: contact.cta,
950
- track: ctaTrackForHref(contact.cta.href),
951
- className: "rt-tpl-btn rt-tpl-btn--primary rt-tpl-faq-contact-cta"
952
- }
953
- ) : null
954
- ] }) : null
955
- ]
956
- }
957
- );
958
- }
959
- function TemplateFaqTwoColumn(section) {
960
- const { heading, items, groups } = section.props;
961
- const hasGroups = Boolean(groups && groups.length > 0);
962
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
963
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
964
- hasGroups ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-groups", children: (groups ?? []).map((group) => /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-faq-group", children: [
965
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-faq-group-title", children: group.title }),
966
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-two-column-list", children: group.items.map((item, i) => /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-faq-open-item", children: [
967
- /* @__PURE__ */ jsx2("h4", { className: "rt-tpl-faq-open-q", children: item.question }),
968
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-faq-open-a", children: item.answer })
969
- ] }, `${group.title}-${i}`)) })
970
- ] }, group.title)) }) : /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-two-column-list", children: (items ?? []).map((item, i) => /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-faq-open-item", children: [
971
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-faq-open-q", children: item.question }),
972
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-faq-open-a", children: item.answer })
973
- ] }, i)) })
974
- ] });
975
- }
976
- function TemplateFaqContentAccordion(section) {
977
- const { heading, contentGroups } = section.props;
978
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
979
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
980
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-content-groups", children: (contentGroups ?? []).map((group) => /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-faq-content-group", children: [
981
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-faq-group-title", children: group.title }),
982
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-list", children: group.items.map((item) => /* @__PURE__ */ jsxs2("details", { className: "rt-tpl-faq-item", children: [
983
- /* @__PURE__ */ jsxs2("summary", { className: "rt-tpl-faq-q", children: [
984
- /* @__PURE__ */ jsx2("span", { children: item.title }),
985
- item.summary ? /* @__PURE__ */ jsx2("small", { children: item.summary }) : null
986
- ] }),
987
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-faq-a rt-tpl-faq-content-body", children: [
988
- /* @__PURE__ */ jsx2("p", { children: item.body }),
989
- item.signs && item.signs.length > 0 ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-faq-content-signs", children: item.signs.map((sign) => /* @__PURE__ */ jsx2("li", { children: sign }, sign)) }) : null,
990
- item.href ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-faq-content-link", href: item.href, children: "\uC790\uC138\uD788 \uBCF4\uAE30" }) : null
991
- ] })
992
- ] }, item.title)) })
993
- ] }, group.title)) })
994
- ] });
995
- }
996
- function TemplateFaq(section) {
997
- const { jsonLd } = section.props;
998
- const jsonLdItems = faqItemsForJsonLd(section);
999
- return /* @__PURE__ */ jsxs2(
1000
- "section",
1001
- {
1002
- "data-roottale-tpl": "faq",
1003
- "data-variant": section.variant,
1004
- id: section.id,
1005
- className: "rt-tpl-section rt-tpl-faq",
1006
- style: sectionThemeStyle(section.theme),
1007
- children: [
1008
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-container", children: section.variant === "content-accordion" ? TemplateFaqContentAccordion(section) : section.variant === "two-column" ? TemplateFaqTwoColumn(section) : TemplateFaqAccordion(section) }),
1009
- section.variant !== "content-accordion" && jsonLd && jsonLdItems.length > 0 ? /* @__PURE__ */ jsx2(
1010
- "script",
1011
- {
1012
- type: "application/ld+json",
1013
- dangerouslySetInnerHTML: {
1014
- __html: JSON.stringify({
1015
- "@context": "https://schema.org",
1016
- "@type": "FAQPage",
1017
- mainEntity: jsonLdItems.map((item) => ({
1018
- "@type": "Question",
1019
- name: item.question,
1020
- acceptedAnswer: { "@type": "Answer", text: item.answer }
1021
- }))
1022
- })
1023
- }
1024
- }
1025
- ) : null
1026
- ]
1027
- }
1028
- );
1029
- }
1030
- function TemplateCtaBannerPhotoOverlay(section) {
1031
- const { title, description, cta, image, imageAlt } = section.props;
1032
- return /* @__PURE__ */ jsx2(
1033
- "section",
1034
- {
1035
- "data-roottale-tpl": "cta-banner",
1036
- "data-variant": "photo-overlay",
1037
- id: section.id,
1038
- className: "rt-tpl-section rt-tpl-cta-banner rt-tpl-cta-banner--photo-overlay",
1039
- style: sectionThemeStyle(section.theme),
1040
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-cta-banner-photo", children: [
1041
- /* @__PURE__ */ jsx2(
1042
- "img",
1043
- {
1044
- className: "rt-tpl-cta-banner-photo-img",
1045
- src: image ?? "",
1046
- alt: imageAlt ?? "",
1047
- loading: "lazy"
1048
- }
1049
- ),
1050
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-cta-banner-photo-scrim", "aria-hidden": "true" }),
1051
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-cta-banner-photo-inner", children: [
1052
- /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-cta-banner-photo-title", children: title }),
1053
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-cta-banner-photo-desc", children: description }) : null,
1054
- cta ? /* @__PURE__ */ jsx2(
1055
- CtaLink,
1056
- {
1057
- cta,
1058
- track: "cta_banner",
1059
- className: "rt-tpl-btn rt-tpl-btn--primary rt-tpl-cta-banner-photo-cta"
1060
- }
1061
- ) : null
1062
- ] })
1063
- ] })
1064
- }
1065
- );
1066
- }
1067
- function TemplateCtaBanner(section) {
1068
- if (section.variant === "photo-overlay")
1069
- return TemplateCtaBannerPhotoOverlay(section);
1070
- const { title, description, cta } = section.props;
1071
- return /* @__PURE__ */ jsx2(
1072
- "section",
1073
- {
1074
- "data-roottale-tpl": "cta-banner",
1075
- "data-variant": section.variant,
1076
- id: section.id,
1077
- className: "rt-tpl-section rt-tpl-cta-banner",
1078
- style: sectionThemeStyle(section.theme),
1079
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-cta-banner-inner", children: [
1080
- /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-cta-banner-title", children: title }),
1081
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-cta-banner-desc", children: description }) : null,
1082
- cta ? /* @__PURE__ */ jsx2(CtaLink, { cta, track: "cta_banner" }) : null
1083
- ] })
1084
- }
1085
- );
1086
- }
1087
- function TrustMetricValue({ value }) {
1088
- const parsed = parseMetricValue(value);
1089
- if (!parsed) {
1090
- return /* @__PURE__ */ jsx2(Fragment2, { children: value });
1091
- }
1092
- return /* @__PURE__ */ jsx2(
1093
- CountUpStat,
1094
- {
1095
- value: parsed.value,
1096
- decimals: parsed.decimals,
1097
- suffix: parsed.suffix
1098
- }
1099
- );
1100
- }
1101
- function TrustCredentialIcon() {
1102
- return /* @__PURE__ */ jsxs2(
1103
- "svg",
1104
- {
1105
- className: "rt-cred__icon",
1106
- viewBox: "0 0 16 16",
1107
- "aria-hidden": "true",
1108
- focusable: "false",
1109
- children: [
1110
- /* @__PURE__ */ jsx2("path", { d: "M8 1.75 13 3.5v3.75c0 3-2 5.45-5 7-3-1.55-5-4-5-7V3.5z" }),
1111
- /* @__PURE__ */ jsx2("path", { d: "m5.75 8 1.45 1.45 3.2-3.4" })
1112
- ]
1113
- }
1114
- );
1115
- }
1116
- function TemplateTrust(section) {
1117
- const { heading, metrics, credentials, logos, marqueeLogos, chart, disclaimer } = section.props;
1118
- const chartMax = chart?.points.reduce((max, point) => Math.max(max, point.value), 0) ?? 0;
1119
- return /* @__PURE__ */ jsx2(
1120
- "section",
1121
- {
1122
- "data-roottale-tpl": "trust",
1123
- "data-variant": section.variant,
1124
- id: section.id,
1125
- className: "rt-tpl-section rt-tpl-trust",
1126
- style: sectionThemeStyle(section.theme),
1127
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-trust-inner", children: [
1128
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1129
- section.variant === "stat-band" ? (
1130
- // 수치 밴드 진입 시 fade-up(C8) + 지표 숫자 카운트업(ADR-0086 D3로
1131
- // 채택(구 룰 supersede, 2026-07-17)). reduce·SSR 시 <div> 그대로
1132
- // (마크업 불변) + CountUpStat 자체도 mount 전엔 최종값 정적 표시.
1133
- /* @__PURE__ */ jsx2(AnimateOnScroll, { as: "div", variant: "fade-up", className: "rt-tpl-trust-stat-band", children: (metrics ?? []).map((metric) => /* @__PURE__ */ jsxs2(
1134
- "div",
1135
- {
1136
- className: "rt-stat",
1137
- "data-variant": "band-item",
1138
- children: [
1139
- /* @__PURE__ */ jsxs2("span", { className: "rt-stat__value", children: [
1140
- /* @__PURE__ */ jsx2(TrustMetricValue, { value: metric.value }),
1141
- metric.unit ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__unit", children: metric.unit }) : null
1142
- ] }),
1143
- /* @__PURE__ */ jsx2("span", { className: "rt-stat__label", children: metric.label }),
1144
- metric.source ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__source", children: metric.source }) : null
1145
- ]
1146
- },
1147
- `${metric.value}-${metric.label}`
1148
- )) })
1149
- ) : null,
1150
- section.variant === "logo-cloud" ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-trust-logo-list", children: (logos ?? []).map((logo) => /* @__PURE__ */ jsx2("li", { className: "rt-tpl-trust-logo-item", children: logo.href ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-trust-logo-link", href: logo.href, children: /* @__PURE__ */ jsx2(
1151
- "img",
1152
- {
1153
- className: "rt-tpl-trust-logo",
1154
- src: logo.src,
1155
- alt: logo.alt,
1156
- loading: "lazy"
1157
- }
1158
- ) }) : /* @__PURE__ */ jsx2(
1159
- "img",
1160
- {
1161
- className: "rt-tpl-trust-logo",
1162
- src: logo.src,
1163
- alt: logo.alt,
1164
- loading: "lazy"
1165
- }
1166
- ) }, `${logo.src}-${logo.alt}`)) }) : null,
1167
- section.variant === "feature-grid" ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-trust-cred-list", children: (credentials ?? []).map((credential) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsxs2("span", { className: "rt-cred", "data-kind": credential.kind, children: [
1168
- /* @__PURE__ */ jsx2(TrustCredentialIcon, {}),
1169
- /* @__PURE__ */ jsx2("span", { className: "rt-cred__label", children: credential.name }),
1170
- credential.issuer ? /* @__PURE__ */ jsx2("span", { className: "rt-cred__issuer", children: credential.issuer }) : null
1171
- ] }) }, `${credential.name}-${credential.issuer ?? ""}`)) }) : null,
1172
- section.variant === "logo-marquee" ? (
1173
- // C8 Marquee 소비(직접 재구현 금지, C10 §1-3). 로고는 흑백/저채도
1174
- // 처리(CSS filter) — hover 시에만 원색 복원.
1175
- /* @__PURE__ */ jsx2(
1176
- Marquee,
1177
- {
1178
- className: "rt-tpl-trust-marquee",
1179
- ariaLabel: heading ?? "\uD30C\uD2B8\uB108/\uC778\uC99D \uB85C\uACE0",
1180
- children: (marqueeLogos ?? []).map(
1181
- (logo, i) => logo.href ? /* @__PURE__ */ jsx2(
1182
- "a",
1183
- {
1184
- className: "rt-tpl-trust-marquee-link",
1185
- href: logo.href,
1186
- children: /* @__PURE__ */ jsx2(
1187
- "img",
1188
- {
1189
- className: "rt-tpl-trust-marquee-logo",
1190
- src: logo.src,
1191
- alt: logo.alt,
1192
- loading: "lazy"
1193
- }
1194
- )
1195
- },
1196
- `${logo.alt}-${i}`
1197
- ) : /* @__PURE__ */ jsx2(
1198
- "img",
1199
- {
1200
- className: "rt-tpl-trust-marquee-logo",
1201
- src: logo.src,
1202
- alt: logo.alt,
1203
- loading: "lazy"
1204
- },
1205
- `${logo.alt}-${i}`
1206
- )
1207
- )
1208
- }
1209
- )
1210
- ) : null,
1211
- section.variant === "credential-list" ? (
1212
- // hairline 자격·이력·수상 리스트 — 연도는 tabular-nums, 위계는
1213
- // 작은 크기로(AI-slop #3 대체: 장식용 큰 숫자 금지).
1214
- /* @__PURE__ */ jsx2("ol", { className: "rt-tpl-trust-credential-list", children: (credentials ?? []).map((credential, i) => /* @__PURE__ */ jsxs2(
1215
- "li",
1216
- {
1217
- className: "rt-tpl-trust-credential-row",
1218
- children: [
1219
- credential.year ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-trust-credential-year", children: credential.year }) : null,
1220
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-trust-credential-name", children: credential.name }),
1221
- credential.issuer ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-trust-credential-issuer", children: credential.issuer }) : null
1222
- ]
1223
- },
1224
- `${credential.name}-${credential.year ?? i}`
1225
- )) })
1226
- ) : null,
1227
- section.variant === "dashboard" && chart ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-trust-dashboard", children: [
1228
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-trust-dashboard-chart", children: [
1229
- /* @__PURE__ */ jsxs2("div", { children: [
1230
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-trust-dashboard-title", children: chart.title }),
1231
- chart.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-trust-dashboard-desc", children: chart.description }) : null
1232
- ] }),
1233
- /* @__PURE__ */ jsx2(
1234
- "ul",
1235
- {
1236
- className: "rt-tpl-trust-dashboard-bars",
1237
- "data-chart-type": chart.type,
1238
- children: chart.points.map((point) => {
1239
- const percent = chartMax > 0 ? `${Math.max(4, Math.round(point.value / chartMax * 100))}%` : "4%";
1240
- return /* @__PURE__ */ jsxs2("li", { children: [
1241
- /* @__PURE__ */ jsx2("span", { children: point.label }),
1242
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-trust-dashboard-track", children: /* @__PURE__ */ jsx2(
1243
- "span",
1244
- {
1245
- className: "rt-tpl-trust-dashboard-bar",
1246
- style: { inlineSize: percent }
1247
- }
1248
- ) }),
1249
- /* @__PURE__ */ jsx2("strong", { children: point.display ?? String(point.value) })
1250
- ] }, point.label);
1251
- })
1252
- }
1253
- )
1254
- ] }),
1255
- /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-trust-dashboard-metrics", children: (metrics ?? []).map((metric) => /* @__PURE__ */ jsxs2("li", { className: "rt-stat", children: [
1256
- metric.imageSrc ? /* @__PURE__ */ jsx2(
1257
- "img",
1258
- {
1259
- className: "rt-tpl-trust-dashboard-icon",
1260
- src: metric.imageSrc,
1261
- alt: metric.imageAlt ?? "",
1262
- loading: "lazy"
1263
- }
1264
- ) : null,
1265
- /* @__PURE__ */ jsxs2("span", { className: "rt-stat__value", children: [
1266
- /* @__PURE__ */ jsx2(TrustMetricValue, { value: metric.value }),
1267
- metric.unit ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__unit", children: metric.unit }) : null
1268
- ] }),
1269
- /* @__PURE__ */ jsx2("span", { className: "rt-stat__label", children: metric.label }),
1270
- metric.source ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__source", children: metric.source }) : null
1271
- ] }, `${metric.value}-${metric.label}`)) })
1272
- ] }) : null,
1273
- disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-trust-disclaimer", children: disclaimer }) : null
1274
- ] })
1275
- }
1276
- );
1277
- }
1278
- function TemplateTestimonialsEditorialQuote(section) {
1279
- const { heading, description, items, disclaimer } = section.props;
1280
- const layout = items.length > 1 ? "stack" : "single";
1281
- return /* @__PURE__ */ jsx2(
1282
- "section",
1283
- {
1284
- "data-roottale-tpl": "testimonials",
1285
- "data-variant": "editorial-quote",
1286
- id: section.id,
1287
- className: "rt-tpl-section rt-tpl-testimonials",
1288
- style: sectionThemeStyle(section.theme),
1289
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1290
- /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }),
1291
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1292
- /* @__PURE__ */ jsx2(
1293
- StaggerGroup,
1294
- {
1295
- as: "ul",
1296
- className: `rt-tpl-testimonials-editorial-list rt-tpl-testimonials-editorial-list--${layout}`,
1297
- children: items.map((item, i) => /* @__PURE__ */ jsx2(
1298
- StaggerItem,
1299
- {
1300
- as: "li",
1301
- className: "rt-tpl-testimonials-editorial-item",
1302
- children: /* @__PURE__ */ jsxs2("blockquote", { className: "rt-tpl-testimonials-editorial-quote", children: [
1303
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-editorial-text", children: item.quote }),
1304
- /* @__PURE__ */ jsxs2("footer", { className: "rt-tpl-testimonials-editorial-footer", children: [
1305
- /* @__PURE__ */ jsx2("cite", { className: "rt-tpl-testimonials-editorial-author", children: item.author }),
1306
- item.context ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-editorial-context", children: item.context }) : null,
1307
- /* @__PURE__ */ jsx2(TestimonialMeta, { item })
1308
- ] }),
1309
- item.reply ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-reply", children: item.reply }) : null
1310
- ] })
1311
- },
1312
- `${item.author}-${i}`
1313
- ))
1314
- }
1315
- ),
1316
- disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-disclaimer", children: disclaimer }) : null
1317
- ] })
1318
- }
1319
- );
1320
- }
1321
- function TestimonialMeta({
1322
- item
1323
- }) {
1324
- const values = [
1325
- typeof item.rating === "number" ? `\uD3C9\uC810 ${item.rating.toFixed(1)}` : void 0,
1326
- item.source,
1327
- item.visitedAt,
1328
- item.item
1329
- ].filter((value) => Boolean(value));
1330
- if (values.length === 0) return null;
1331
- return /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-meta", children: values.map((value) => /* @__PURE__ */ jsx2("span", { children: value }, value)) });
1332
- }
1333
- function TemplateTestimonialsQuoteRail(section) {
1334
- const { heading, description, items, marquee, disclaimer } = section.props;
1335
- return /* @__PURE__ */ jsx2(
1336
- "section",
1337
- {
1338
- "data-roottale-tpl": "testimonials",
1339
- "data-variant": "quote-rail",
1340
- id: section.id,
1341
- className: "rt-tpl-section rt-tpl-testimonials",
1342
- style: sectionThemeStyle(section.theme),
1343
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1344
- /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }),
1345
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1346
- /* @__PURE__ */ jsx2(
1347
- AnimateOnScroll,
1348
- {
1349
- as: "div",
1350
- variant: "fade-up",
1351
- className: "rt-tpl-testimonials-rail-viewport",
1352
- children: /* @__PURE__ */ jsx2(
1353
- "ul",
1354
- {
1355
- className: marquee ? "rt-tpl-testimonials-rail rt-tpl-testimonials-rail--marquee" : "rt-tpl-testimonials-rail",
1356
- "data-marquee": marquee ? "true" : void 0,
1357
- children: items.map((item, i) => /* @__PURE__ */ jsx2(
1358
- "li",
1359
- {
1360
- className: "rt-tpl-testimonials-rail-item",
1361
- children: /* @__PURE__ */ jsxs2("blockquote", { className: "rt-tpl-testimonials-rail-quote", children: [
1362
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-rail-text", children: item.quote }),
1363
- /* @__PURE__ */ jsxs2("footer", { className: "rt-tpl-testimonials-rail-footer", children: [
1364
- /* @__PURE__ */ jsx2("cite", { className: "rt-tpl-testimonials-rail-author", children: item.author }),
1365
- item.context ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-rail-context", children: item.context }) : null,
1366
- /* @__PURE__ */ jsx2(TestimonialMeta, { item })
1367
- ] }),
1368
- item.reply ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-reply", children: item.reply }) : null
1369
- ] })
1370
- },
1371
- `${item.author}-${i}`
1372
- ))
1373
- }
1374
- )
1375
- }
1376
- ),
1377
- disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-disclaimer", children: disclaimer }) : null
1378
- ] })
1379
- }
1380
- );
1381
- }
1382
- function StarRating({ rating }) {
1383
- const clamped = Math.min(5, Math.max(0, rating));
1384
- const fill = `${(clamped / 5 * 100).toFixed(0)}%`;
1385
- return /* @__PURE__ */ jsxs2(
1386
- "span",
1387
- {
1388
- className: "rt-tpl-testimonials-stars",
1389
- role: "img",
1390
- "aria-label": `\uD3C9\uC810 5\uC810 \uB9CC\uC810\uC5D0 ${clamped.toFixed(1)}\uC810`,
1391
- style: { "--rt-testimonials-star-fill": fill },
1392
- children: [
1393
- /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "rt-tpl-testimonials-stars-base", children: "\u2605\u2605\u2605\u2605\u2605" }),
1394
- /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "rt-tpl-testimonials-stars-fill", children: "\u2605\u2605\u2605\u2605\u2605" })
1395
- ]
1396
- }
1397
- );
1398
- }
1399
- function TemplateTestimonialsStarCompact(section) {
1400
- const { heading, description, items, disclaimer } = section.props;
1401
- const rated = items.filter(
1402
- (item) => typeof item.rating === "number"
1403
- );
1404
- const average = rated.length > 0 ? rated.reduce((sum, item) => sum + item.rating, 0) / rated.length : void 0;
1405
- return /* @__PURE__ */ jsx2(
1406
- "section",
1407
- {
1408
- "data-roottale-tpl": "testimonials",
1409
- "data-variant": "star-compact",
1410
- id: section.id,
1411
- className: "rt-tpl-section rt-tpl-testimonials",
1412
- style: sectionThemeStyle(section.theme),
1413
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1414
- /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }),
1415
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1416
- average !== void 0 ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-testimonials-star-summary", children: [
1417
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-average", children: average.toFixed(1) }),
1418
- /* @__PURE__ */ jsx2(StarRating, { rating: average }),
1419
- /* @__PURE__ */ jsxs2("span", { className: "rt-tpl-testimonials-star-count", children: [
1420
- "\uD6C4\uAE30 ",
1421
- items.length,
1422
- "\uAC74"
1423
- ] })
1424
- ] }) : null,
1425
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-testimonials-star-list", children: items.map((item, i) => /* @__PURE__ */ jsx2(
1426
- StaggerItem,
1427
- {
1428
- as: "li",
1429
- className: "rt-tpl-testimonials-star-item",
1430
- children: /* @__PURE__ */ jsxs2("blockquote", { className: "rt-tpl-testimonials-star-quote", children: [
1431
- typeof item.rating === "number" ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-testimonials-star-head", children: [
1432
- /* @__PURE__ */ jsx2(StarRating, { rating: item.rating }),
1433
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-value", children: item.rating.toFixed(1) })
1434
- ] }) : null,
1435
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-star-text", children: item.quote }),
1436
- /* @__PURE__ */ jsxs2("footer", { className: "rt-tpl-testimonials-star-footer", children: [
1437
- /* @__PURE__ */ jsx2("cite", { className: "rt-tpl-testimonials-star-author", children: item.author }),
1438
- item.context ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-context", children: item.context }) : null,
1439
- /* @__PURE__ */ jsx2(StarCompactMeta, { item })
1440
- ] }),
1441
- item.reply ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-reply", children: item.reply }) : null
1442
- ] })
1443
- },
1444
- `${item.author}-${i}`
1445
- )) }),
1446
- disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-disclaimer", children: disclaimer }) : null
1447
- ] })
1448
- }
1449
- );
1450
- }
1451
- function StarCompactMeta({
1452
- item
1453
- }) {
1454
- const values = [item.source, item.visitedAt, item.item].filter(
1455
- (value) => Boolean(value)
1456
- );
1457
- if (values.length === 0) return null;
1458
- return /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-meta", children: values.map((value) => /* @__PURE__ */ jsx2("span", { children: value }, value)) });
1459
- }
1460
- function TemplateTestimonials(section) {
1461
- if (section.variant === "quote-rail")
1462
- return TemplateTestimonialsQuoteRail(section);
1463
- if (section.variant === "star-compact")
1464
- return TemplateTestimonialsStarCompact(section);
1465
- return TemplateTestimonialsEditorialQuote(section);
1466
- }
1467
- function memberInitials(name) {
1468
- const parts = name.trim().split(/\s+/).filter(Boolean);
1469
- if (parts.length === 0) return "?";
1470
- const initials = parts.slice(0, 2).map((part) => part.at(0) ?? "").join("");
1471
- return initials || "?";
1472
- }
1473
- function TeamMemberPhoto({
1474
- member,
1475
- featured = false
1476
- }) {
1477
- if (member.photoSrc) {
1478
- return /* @__PURE__ */ jsx2(
1479
- "img",
1480
- {
1481
- className: featured ? "rt-tpl-team-photo rt-tpl-team-photo--featured" : "rt-tpl-team-photo",
1482
- src: member.photoSrc,
1483
- alt: member.photoAlt ?? "",
1484
- loading: "lazy"
1485
- }
1486
- );
1487
- }
1488
- return /* @__PURE__ */ jsx2(
1489
- "div",
1490
- {
1491
- className: featured ? "rt-tpl-team-initials rt-tpl-team-initials--featured" : "rt-tpl-team-initials",
1492
- "aria-hidden": "true",
1493
- children: memberInitials(member.name)
1494
- }
1495
- );
1496
- }
1497
- function TeamMemberBody({
1498
- member
1499
- }) {
1500
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-team-body", children: [
1501
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-team-name", children: member.name }),
1502
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-team-title", children: member.title }),
1503
- member.specialties ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-team-specialties", "aria-label": `${member.name} \uC804\uBB38 \uBD84\uC57C`, children: member.specialties.map((specialty) => /* @__PURE__ */ jsx2("li", { children: specialty }, specialty)) }) : null,
1504
- member.credentials ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-team-credentials", children: member.credentials.map((credential) => /* @__PURE__ */ jsx2("li", { children: credential }, credential)) }) : null,
1505
- member.bio ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-team-bio", children: member.bio }) : null,
1506
- member.href ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-team-cta", href: member.href, children: member.ctaLabel ?? "\uD504\uB85C\uD544 \uBCF4\uAE30" }) : null
1507
- ] });
1508
- }
1509
- function TemplateTeamGrid(section) {
1510
- const { heading, description, members } = section.props;
1511
- return /* @__PURE__ */ jsx2(
1512
- "section",
1513
- {
1514
- "data-roottale-tpl": "team",
1515
- "data-variant": "grid",
1516
- id: section.id,
1517
- className: "rt-tpl-section rt-tpl-team",
1518
- style: sectionThemeStyle(section.theme),
1519
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1520
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1521
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1522
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-team-grid", children: members.map((member) => /* @__PURE__ */ jsxs2(StaggerItem, { as: "li", className: "rt-tpl-team-card", children: [
1523
- /* @__PURE__ */ jsx2(TeamMemberPhoto, { member }),
1524
- /* @__PURE__ */ jsx2(TeamMemberBody, { member })
1525
- ] }, member.name)) })
1526
- ] })
1527
- }
1528
- );
1529
- }
1530
- function TemplateTeamSpotlight(section) {
1531
- const { heading, description, members } = section.props;
1532
- const [featured, ...rest] = members;
1533
- return /* @__PURE__ */ jsx2(
1534
- "section",
1535
- {
1536
- "data-roottale-tpl": "team",
1537
- "data-variant": "spotlight",
1538
- id: section.id,
1539
- className: "rt-tpl-section rt-tpl-team",
1540
- style: sectionThemeStyle(section.theme),
1541
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1542
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1543
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1544
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-team-spotlight", children: [
1545
- featured ? /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-team-card rt-tpl-team-card--featured", children: [
1546
- /* @__PURE__ */ jsx2(TeamMemberPhoto, { member: featured, featured: true }),
1547
- /* @__PURE__ */ jsx2(TeamMemberBody, { member: featured })
1548
- ] }) : null,
1549
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-team-spotlight-list", children: rest.map((member) => /* @__PURE__ */ jsxs2(StaggerItem, { as: "li", className: "rt-tpl-team-card", children: [
1550
- /* @__PURE__ */ jsx2(TeamMemberPhoto, { member }),
1551
- /* @__PURE__ */ jsx2(TeamMemberBody, { member })
1552
- ] }, member.name)) })
1553
- ] })
1554
- ] })
1555
- }
1556
- );
1557
- }
1558
- function TemplateTeamExpandingStage(section) {
1559
- const { heading, description, members } = section.props;
1560
- return /* @__PURE__ */ jsx2(
1561
- "section",
1562
- {
1563
- "data-roottale-tpl": "team",
1564
- "data-variant": "expanding-stage",
1565
- id: section.id,
1566
- className: "rt-tpl-section rt-tpl-team",
1567
- style: sectionThemeStyle(section.theme),
1568
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1569
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1570
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1571
- /* @__PURE__ */ jsx2(TeamExpandingStageClient, { members })
1572
- ] })
1573
- }
1574
- );
1575
- }
1576
- function TemplateTeamRow(section) {
1577
- const { heading, description, members } = section.props;
1578
- return /* @__PURE__ */ jsx2(
1579
- "section",
1580
- {
1581
- "data-roottale-tpl": "team",
1582
- "data-variant": "row",
1583
- id: section.id,
1584
- className: "rt-tpl-section rt-tpl-team",
1585
- style: sectionThemeStyle(section.theme),
1586
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1587
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1588
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1589
- /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-team-row-list", children: members.map((member) => /* @__PURE__ */ jsx2(StaggerItem, { as: "li", className: "rt-tpl-team-row-item", children: /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-team-card rt-tpl-team-card--row", children: [
1590
- /* @__PURE__ */ jsx2(TeamMemberPhoto, { member }),
1591
- /* @__PURE__ */ jsx2(TeamMemberBody, { member })
1592
- ] }) }, member.name)) })
1593
- ] })
1594
- }
1595
- );
1596
- }
1597
- function TemplateTeam(section) {
1598
- if (section.variant === "expanding-stage")
1599
- return TemplateTeamExpandingStage(section);
1600
- if (section.variant === "row") return TemplateTeamRow(section);
1601
- if (section.variant === "spotlight") return TemplateTeamSpotlight(section);
1602
- return TemplateTeamGrid(section);
1603
- }
1604
- function telHref(phone) {
1605
- return `tel:${phone.replace(/[^\d+]/g, "")}`;
1606
- }
1607
- function ContactMap({ url }) {
1608
- if (isAllowedMapEmbedSrc(url) || isAllowedEmbedSrc(url)) {
1609
- return /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-map", children: /* @__PURE__ */ jsx2(
1610
- "iframe",
1611
- {
1612
- src: url,
1613
- title: "\uC624\uC2DC\uB294\uAE38 \uC9C0\uB3C4",
1614
- loading: "lazy",
1615
- referrerPolicy: "no-referrer-when-downgrade"
1616
- }
1617
- ) });
1618
- }
1619
- return /* @__PURE__ */ jsx2("p", { className: "rt-tpl-contact-map-fallback", children: /* @__PURE__ */ jsx2("a", { href: url, rel: "noopener noreferrer", target: "_blank", children: "\uAE38\uCC3E\uAE30" }) });
1620
- }
1621
- function ContactMapBlock({
1622
- map,
1623
- mapEmbedUrl
1624
- }) {
1625
- const embedUrl = map?.embedUrl ?? mapEmbedUrl;
1626
- const externalUrl = map?.externalUrl ?? embedUrl;
1627
- if (!embedUrl && !map?.facadeImageSrc && !externalUrl) return null;
1628
- const liveMap = Boolean(
1629
- embedUrl && (isAllowedMapEmbedSrc(embedUrl) || isAllowedEmbedSrc(embedUrl))
1630
- );
1631
- const clickToLoad = liveMap && embedUrl && (map?.mapLoad ?? "lazy") === "click";
1632
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-map-block", "data-provider": map?.provider, children: [
1633
- map?.facadeImageSrc && !liveMap ? /* @__PURE__ */ jsxs2("figure", { className: "rt-tpl-contact-map-facade", children: [
1634
- /* @__PURE__ */ jsx2(
1635
- "img",
1636
- {
1637
- src: map.facadeImageSrc,
1638
- alt: map.facadeImageAlt ?? "",
1639
- loading: "lazy"
1640
- }
1641
- ),
1642
- externalUrl ? /* @__PURE__ */ jsx2("figcaption", { children: /* @__PURE__ */ jsx2("a", { href: externalUrl, target: "_blank", rel: "noopener noreferrer", children: "\uC9C0\uB3C4 \uD06C\uAC8C \uBCF4\uAE30" }) }) : null
1643
- ] }) : null,
1644
- clickToLoad ? /* @__PURE__ */ jsx2(
1645
- ContactMapClickToLoad,
1646
- {
1647
- embedUrl,
1648
- externalUrl,
1649
- facadeImageSrc: map?.facadeImageSrc,
1650
- facadeImageAlt: map?.facadeImageAlt
1651
- }
1652
- ) : embedUrl ? /* @__PURE__ */ jsx2(ContactMap, { url: embedUrl }) : null
1653
- ] });
1654
- }
1655
- function ContactActions({
1656
- actions
1657
- }) {
1658
- if (!actions || actions.length === 0) return null;
1659
- return /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-contact-actions", children: actions.map((action) => {
1660
- const track = contactHrefTrack(action.href);
1661
- return /* @__PURE__ */ jsx2("li", { "data-kind": action.kind, children: /* @__PURE__ */ jsx2(
1662
- "a",
1663
- {
1664
- href: action.href,
1665
- "data-track": track,
1666
- "data-track-placement": track ? "contact_actions" : void 0,
1667
- children: action.label
1668
- }
1669
- ) }, `${action.label}-${action.href}`);
1670
- }) });
1671
- }
1672
- function TemplateContactChannelCards(section) {
1673
- const { heading, channels, map, mapEmbedUrl, actions } = section.props;
1674
- return /* @__PURE__ */ jsx2(
1675
- "section",
1676
- {
1677
- "data-roottale-tpl": "contact",
1678
- "data-variant": "channel-cards",
1679
- id: section.id,
1680
- className: "rt-tpl-section rt-tpl-contact",
1681
- style: sectionThemeStyle(section.theme),
1682
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1683
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1684
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-channel-layout", children: [
1685
- /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-contact-channel-list", children: (channels ?? []).map((channel) => {
1686
- const content = /* @__PURE__ */ jsxs2(Fragment2, { children: [
1687
- /* @__PURE__ */ jsxs2("span", { className: "rt-tpl-contact-channel-label", children: [
1688
- channel.iconSrc ? /* @__PURE__ */ jsx2(
1689
- "img",
1690
- {
1691
- className: "rt-tpl-contact-channel-icon",
1692
- src: channel.iconSrc,
1693
- alt: channel.iconAlt ?? "",
1694
- loading: "lazy"
1695
- }
1696
- ) : null,
1697
- channel.label
1698
- ] }),
1699
- channel.value ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-channel-value", children: channel.value }) : null,
1700
- channel.description ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-channel-desc", children: channel.description }) : null,
1701
- channel.href && channel.actionLabel ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-channel-action", children: channel.actionLabel }) : null
1702
- ] });
1703
- return /* @__PURE__ */ jsx2(
1704
- "li",
1705
- {
1706
- "data-kind": channel.kind,
1707
- children: channel.href ? /* @__PURE__ */ jsx2(
1708
- "a",
1709
- {
1710
- className: "rt-tpl-contact-channel-card",
1711
- href: channel.href,
1712
- "data-track": channel.href.startsWith("tel:") ? "cta_call" : channel.href.startsWith("mailto:") ? "email_click" : "cta_banner",
1713
- "data-track-placement": channel.href.startsWith("mailto:") ? "contact_channels" : void 0,
1714
- children: content
1715
- }
1716
- ) : /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-channel-card", children: content })
1717
- },
1718
- `${channel.label}-${channel.value ?? channel.href ?? ""}`
1719
- );
1720
- }) }),
1721
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-channel-side", children: [
1722
- /* @__PURE__ */ jsx2(ContactMapBlock, { map, mapEmbedUrl }),
1723
- /* @__PURE__ */ jsx2(ContactActions, { actions })
1724
- ] })
1725
- ] })
1726
- ] })
1727
- }
1728
- );
1729
- }
1730
- function ContactHoursSummary({
1731
- hours
1732
- }) {
1733
- if (!hours || hours.length === 0) return null;
1734
- return /* @__PURE__ */ jsxs2("table", { className: "rt-tpl-contact-visit-hours", children: [
1735
- /* @__PURE__ */ jsx2("caption", { className: "rt-tpl-sr-only", children: "\uC9C4\uB8CC\uC2DC\uAC04" }),
1736
- /* @__PURE__ */ jsx2("tbody", { children: hours.map((row) => /* @__PURE__ */ jsxs2("tr", { children: [
1737
- /* @__PURE__ */ jsx2("th", { scope: "row", children: row.day }),
1738
- /* @__PURE__ */ jsxs2("td", { children: [
1739
- /* @__PURE__ */ jsx2("span", { children: row.open }),
1740
- /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "-" }),
1741
- /* @__PURE__ */ jsx2("span", { children: row.close }),
1742
- row.note ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-hours-note", children: row.note }) : null
1743
- ] })
1744
- ] }, `${row.day}-${row.open}-${row.close}`)) })
1745
- ] });
1746
- }
1747
- function TemplateContactVisitBand(section) {
1748
- const { heading, phone, address, hours, map, mapEmbedUrl, actions } = section.props;
1749
- const mapAction = actions?.find((action) => action.kind === "map") ?? actions?.[0];
1750
- const ctaAction = actions?.find((action) => action.kind === "reserve" || action.kind === "call") ?? actions?.[1] ?? mapAction;
1751
- const phoneAction = phone ? { label: phone, href: telHref(phone) } : void 0;
1752
- return /* @__PURE__ */ jsx2(
1753
- "section",
1754
- {
1755
- "data-roottale-tpl": "contact",
1756
- "data-variant": "visit-band",
1757
- id: section.id,
1758
- className: "rt-tpl-section rt-tpl-contact",
1759
- style: sectionThemeStyle(section.theme),
1760
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1761
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1762
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-visit-band", children: [
1763
- /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-contact-visit-panel", "aria-label": "\uC9C4\uB8CC\uC2DC\uAC04", children: [
1764
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-contact-visit-title", children: "\uC9C4\uB8CC\uC2DC\uAC04" }),
1765
- /* @__PURE__ */ jsx2(ContactHoursSummary, { hours })
1766
- ] }),
1767
- /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-contact-visit-panel", "aria-label": "\uC624\uC2DC\uB294 \uAE38", children: [
1768
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-contact-visit-title", children: "\uC624\uC2DC\uB294 \uAE38" }),
1769
- /* @__PURE__ */ jsx2(ContactMapBlock, { map, mapEmbedUrl }),
1770
- address ? /* @__PURE__ */ jsx2("address", { className: "rt-tpl-contact-visit-address", children: address }) : null,
1771
- mapAction ? /* @__PURE__ */ jsx2(
1772
- "a",
1773
- {
1774
- className: "rt-tpl-contact-visit-link",
1775
- href: mapAction.href,
1776
- "data-track": contactHrefTrack(mapAction.href),
1777
- "data-track-placement": contactHrefTrack(mapAction.href) ? "visit" : void 0,
1778
- children: mapAction.label
1779
- }
1780
- ) : null
1781
- ] }),
1782
- /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-contact-visit-panel", "aria-label": "\uC804\uD654", children: [
1783
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-contact-visit-title", children: "\uC804\uD654" }),
1784
- phoneAction ? /* @__PURE__ */ jsx2(
1785
- "a",
1786
- {
1787
- className: "rt-tpl-contact-visit-phone",
1788
- href: phoneAction.href,
1789
- "data-track": "cta_call",
1790
- children: phoneAction.label
1791
- }
1792
- ) : null,
1793
- hours ? /* @__PURE__ */ jsx2(ContactHoursSummary, { hours: hours.slice(0, 2) }) : null,
1794
- ctaAction ? /* @__PURE__ */ jsx2(
1795
- "a",
1796
- {
1797
- className: "rt-tpl-btn rt-tpl-btn--primary",
1798
- href: ctaAction.href,
1799
- "data-track": contactHrefTrack(ctaAction.href),
1800
- "data-track-placement": contactHrefTrack(ctaAction.href) ? "visit" : void 0,
1801
- children: ctaAction.label
1802
- }
1803
- ) : null
1804
- ] })
1805
- ] })
1806
- ] })
1807
- }
1808
- );
1809
- }
1810
- function TemplateContact(section) {
1811
- if (section.variant === "channel-cards")
1812
- return TemplateContactChannelCards(section);
1813
- if (section.variant === "visit-band") return TemplateContactVisitBand(section);
1814
- const { heading, form, phone, address, hours, map, mapEmbedUrl, actions } = section.props;
1815
- return /* @__PURE__ */ jsx2(
1816
- "section",
1817
- {
1818
- "data-roottale-tpl": "contact",
1819
- "data-variant": section.variant,
1820
- id: section.id,
1821
- className: "rt-tpl-section rt-tpl-contact",
1822
- style: sectionThemeStyle(section.theme),
1823
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-contact-inner", children: [
1824
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-info", children: [
1825
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1826
- phone ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-contact-phone", href: telHref(phone), "data-track": "cta_call", children: phone }) : null,
1827
- address ? /* @__PURE__ */ jsx2("address", { className: "rt-tpl-contact-address", children: address }) : null,
1828
- hours ? /* @__PURE__ */ jsxs2("table", { className: "rt-tpl-contact-hours", children: [
1829
- /* @__PURE__ */ jsx2("caption", { className: "rt-tpl-sr-only", children: "\uC9C4\uB8CC\uC2DC\uAC04" }),
1830
- /* @__PURE__ */ jsx2("tbody", { children: hours.map((row) => /* @__PURE__ */ jsxs2("tr", { children: [
1831
- /* @__PURE__ */ jsx2("th", { scope: "row", children: row.day }),
1832
- /* @__PURE__ */ jsxs2("td", { children: [
1833
- /* @__PURE__ */ jsx2("span", { children: row.open }),
1834
- /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "-" }),
1835
- /* @__PURE__ */ jsx2("span", { children: row.close }),
1836
- row.note ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-hours-note", children: row.note }) : null
1837
- ] })
1838
- ] }, `${row.day}-${row.open}-${row.close}`)) })
1839
- ] }) : null,
1840
- /* @__PURE__ */ jsx2(ContactMapBlock, { map, mapEmbedUrl }),
1841
- /* @__PURE__ */ jsx2(ContactActions, { actions })
1842
- ] }),
1843
- form.enabled ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-form", children: /* @__PURE__ */ jsx2(RootTaleLeadForm, { action: "/api/inquiry", vertical: form.vertical }) }) : null
1844
- ] })
1845
- }
1846
- );
1847
- }
1848
- function renderPracticeHeader({
1849
- eyebrow,
1850
- heading,
1851
- description
1852
- }) {
1853
- if (!eyebrow && !heading && !description) return null;
1854
- return /* @__PURE__ */ jsxs2("header", { className: "rt-tpl-practice-header", children: [
1855
- eyebrow ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-practice-eyebrow", children: eyebrow }) : null,
1856
- heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
1857
- description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null
1858
- ] });
1859
- }
1860
- function renderHoursBadges(badges) {
1861
- if (!badges) return null;
1862
- const values = [
1863
- badges.lunch,
1864
- badges.evening,
1865
- badges.closed,
1866
- ...badges.extra ?? []
1867
- ].filter((value) => Boolean(value));
1868
- if (values.length === 0) return null;
1869
- return /* @__PURE__ */ jsx2("span", { className: "rt-tpl-practice-badges", children: values.map((value) => /* @__PURE__ */ jsx2("span", { className: "rt-tpl-practice-badge", children: value }, value)) });
1870
- }
1871
- function renderPracticeDisclaimer(disclaimer) {
1872
- return disclaimer ? /* @__PURE__ */ jsx2("aside", { className: "rt-tpl-practice-disclaimer", children: disclaimer }) : null;
1873
- }
1874
- function TemplateHoursCompact(section) {
1875
- const { eyebrow, heading, description, rows, note, disclaimer } = section.props;
1876
- return /* @__PURE__ */ jsx2(
1877
- "section",
1878
- {
1879
- "data-roottale-tpl": "hours",
1880
- "data-variant": "compact",
1881
- id: section.id,
1882
- className: "rt-tpl-section rt-tpl-practice rt-tpl-hours",
1883
- style: sectionThemeStyle(section.theme),
1884
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1885
- renderPracticeHeader({ eyebrow, heading, description }),
1886
- /* @__PURE__ */ jsx2("dl", { className: "rt-tpl-hours-compact-list", children: rows.map((row) => /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hours-compact-row", children: [
1887
- /* @__PURE__ */ jsx2("dt", { className: "rt-tpl-hours-compact-day", children: row.label }),
1888
- /* @__PURE__ */ jsxs2("dd", { className: "rt-tpl-hours-compact-body", children: [
1889
- /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hours-time", children: row.hours }),
1890
- row.lunchHours ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hours-muted", children: row.lunchHours }) : null,
1891
- renderHoursBadges(row.badges),
1892
- row.note ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hours-muted", children: row.note }) : null
1893
- ] })
1894
- ] }, row.day)) }),
1895
- note ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-practice-note", children: note }) : null,
1896
- renderPracticeDisclaimer(disclaimer)
1897
- ] })
1898
- }
1899
- );
1900
- }
1901
- function TemplateHours(section) {
1902
- if (section.variant === "compact") return TemplateHoursCompact(section);
1903
- const { eyebrow, heading, description, rows, note, disclaimer } = section.props;
1904
- return /* @__PURE__ */ jsx2(
1905
- "section",
1906
- {
1907
- "data-roottale-tpl": "hours",
1908
- "data-variant": section.variant,
1909
- id: section.id,
1910
- className: "rt-tpl-section rt-tpl-practice rt-tpl-hours",
1911
- style: sectionThemeStyle(section.theme),
1912
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1913
- renderPracticeHeader({ eyebrow, heading, description }),
1914
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-practice-table-wrap", children: /* @__PURE__ */ jsxs2("table", { className: "rt-tpl-practice-table", children: [
1915
- /* @__PURE__ */ jsx2("caption", { className: "rt-tpl-sr-only", children: "\uC601\uC5C5\uC2DC\uAC04" }),
1916
- /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
1917
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC694\uC77C" }),
1918
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC2DC\uAC04" }),
1919
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC810\uC2EC/\uD734\uAC8C" }),
1920
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC548\uB0B4" })
1921
- ] }) }),
1922
- /* @__PURE__ */ jsxs2("tbody", { children: [
1923
- rows.map((row) => /* @__PURE__ */ jsxs2("tr", { children: [
1924
- /* @__PURE__ */ jsx2("th", { scope: "row", className: "rt-tpl-practice-primary", children: row.label }),
1925
- /* @__PURE__ */ jsx2("td", { children: row.hours }),
1926
- /* @__PURE__ */ jsx2("td", { className: "rt-tpl-practice-muted", children: row.lunchHours ?? "\uBCC4\uB3C4 \uC548\uB0B4" }),
1927
- /* @__PURE__ */ jsxs2("td", { children: [
1928
- renderHoursBadges(row.badges),
1929
- row.note ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-practice-muted", children: row.note }) : null
1930
- ] })
1931
- ] }, row.day)),
1932
- note ? /* @__PURE__ */ jsx2("tr", { className: "rt-tpl-practice-table-note", children: /* @__PURE__ */ jsx2("td", { colSpan: 4, children: note }) }) : null
1933
- ] })
1934
- ] }) }),
1935
- renderPracticeDisclaimer(disclaimer)
1936
- ] })
1937
- }
1938
- );
1939
- }
1940
- function TemplatePricingCards(section) {
1941
- const { eyebrow, heading, description, groups, disclaimer } = section.props;
1942
- return /* @__PURE__ */ jsx2(
1943
- "section",
1944
- {
1945
- "data-roottale-tpl": "pricing",
1946
- "data-variant": "cards",
1947
- id: section.id,
1948
- className: "rt-tpl-section rt-tpl-practice rt-tpl-pricing",
1949
- style: sectionThemeStyle(section.theme),
1950
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1951
- renderPracticeHeader({ eyebrow, heading, description }),
1952
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-pricing-card-groups", children: groups.map((group) => /* @__PURE__ */ jsxs2("section", { className: "rt-tpl-pricing-card-group", children: [
1953
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-pricing-group-title", children: group.label }),
1954
- /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-pricing-card-list", children: group.items.map((item) => /* @__PURE__ */ jsxs2("li", { className: "rt-tpl-pricing-card", children: [
1955
- /* @__PURE__ */ jsxs2("div", { children: [
1956
- /* @__PURE__ */ jsx2("h4", { className: "rt-tpl-pricing-name", children: item.name }),
1957
- item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-pricing-desc", children: item.description }) : null
1958
- ] }),
1959
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-pricing-price", children: item.price }),
1960
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-pricing-condition", children: item.condition ?? "\uC790\uB8CC \uD655\uC778 \uD6C4 \uD655\uC815" })
1961
- ] }, `${group.label}-${item.name}`)) })
1962
- ] }, group.label)) }),
1963
- /* @__PURE__ */ jsx2("aside", { className: "rt-tpl-practice-disclaimer", children: disclaimer })
1964
- ] })
1965
- }
1966
- );
1967
- }
1968
- function TemplatePricing(section) {
1969
- if (section.variant === "cards") return TemplatePricingCards(section);
1970
- const { eyebrow, heading, description, groups, disclaimer } = section.props;
1971
- return /* @__PURE__ */ jsx2(
1972
- "section",
1973
- {
1974
- "data-roottale-tpl": "pricing",
1975
- "data-variant": section.variant,
1976
- id: section.id,
1977
- className: "rt-tpl-section rt-tpl-practice rt-tpl-pricing",
1978
- style: sectionThemeStyle(section.theme),
1979
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1980
- renderPracticeHeader({ eyebrow, heading, description }),
1981
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-practice-table-wrap", children: /* @__PURE__ */ jsxs2("table", { className: "rt-tpl-practice-table", children: [
1982
- /* @__PURE__ */ jsx2("caption", { className: "rt-tpl-sr-only", children: "\uC694\uAE08 \uC548\uB0B4" }),
1983
- /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
1984
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uD56D\uBAA9" }),
1985
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC124\uBA85" }),
1986
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uAC00\uACA9" }),
1987
- /* @__PURE__ */ jsx2("th", { scope: "col", children: "\uC870\uAC74" })
1988
- ] }) }),
1989
- /* @__PURE__ */ jsx2("tbody", { children: groups.flatMap((group) => [
1990
- /* @__PURE__ */ jsx2("tr", { className: "rt-tpl-pricing-group-row", children: /* @__PURE__ */ jsx2("td", { colSpan: 4, children: group.label }) }, `${group.label}-group`),
1991
- ...group.items.map((item) => /* @__PURE__ */ jsxs2("tr", { children: [
1992
- /* @__PURE__ */ jsx2("th", { scope: "row", className: "rt-tpl-practice-primary", children: item.name }),
1993
- /* @__PURE__ */ jsx2("td", { className: "rt-tpl-practice-muted", children: item.description ?? "\uC0C1\uB2F4 \uD6C4 \uBC94\uC704 \uD655\uC778" }),
1994
- /* @__PURE__ */ jsx2("td", { className: "rt-tpl-pricing-price", children: item.price }),
1995
- /* @__PURE__ */ jsx2("td", { className: "rt-tpl-practice-muted", children: item.condition ?? "\uC790\uB8CC \uD655\uC778 \uD6C4 \uD655\uC815" })
1996
- ] }, `${group.label}-${item.name}`))
1997
- ]) })
1998
- ] }) }),
1999
- /* @__PURE__ */ jsx2("aside", { className: "rt-tpl-practice-disclaimer", children: disclaimer })
2000
- ] })
2001
- }
2002
- );
2003
- }
2004
- function TemplateProcess(section) {
2005
- const { eyebrow, heading, description, items, connector, disclaimer } = section.props;
2006
- if (section.variant === "timeline-marked") {
2007
- return /* @__PURE__ */ jsx2(
2008
- "section",
2009
- {
2010
- "data-roottale-tpl": "process",
2011
- "data-variant": "timeline-marked",
2012
- id: section.id,
2013
- className: "rt-tpl-section rt-tpl-practice rt-tpl-process",
2014
- style: sectionThemeStyle(section.theme),
2015
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
2016
- renderPracticeHeader({ eyebrow, heading, description }),
2017
- /* @__PURE__ */ jsx2("ol", { className: "rt-tpl-process-marked-list", children: items.map((item, index) => {
2018
- const status = item.status ?? "complete";
2019
- const mark = item.mark ?? (status === "complete" ? "check" : "dot");
2020
- return /* @__PURE__ */ jsx2(
2021
- "li",
2022
- {
2023
- className: "rt-tpl-process-marked-item",
2024
- "data-status": status,
2025
- children: /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-process-marked-card", children: [
2026
- /* @__PURE__ */ jsx2("div", { className: "rt-tpl-process-marked-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("span", { "data-mark": mark }) }),
2027
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-process-marked-body", children: [
2028
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-marked-step", children: item.eyebrow ?? `step ${String(index + 1).padStart(2, "0")}` }),
2029
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-process-title", children: item.title }),
2030
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-desc", children: item.description }),
2031
- item.duration || item.statusLabel ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-duration", children: item.statusLabel ?? item.duration }) : null
2032
- ] })
2033
- ] })
2034
- },
2035
- `${index}-${item.title}`
2036
- );
2037
- }) }),
2038
- renderPracticeDisclaimer(disclaimer)
2039
- ] })
2040
- }
2041
- );
2042
- }
2043
- return /* @__PURE__ */ jsx2(
2044
- "section",
2045
- {
2046
- "data-roottale-tpl": "process",
2047
- "data-variant": section.variant,
2048
- id: section.id,
2049
- className: "rt-tpl-section rt-tpl-practice rt-tpl-process",
2050
- style: sectionThemeStyle(section.theme),
2051
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
2052
- renderPracticeHeader({ eyebrow, heading, description }),
2053
- /* @__PURE__ */ jsx2(
2054
- "ol",
2055
- {
2056
- className: "rt-tpl-process-list",
2057
- "data-connector": connector ?? "line",
2058
- children: items.map((item, index) => /* @__PURE__ */ jsx2("li", { className: "rt-tpl-process-item", children: /* @__PURE__ */ jsxs2("article", { className: "rt-tpl-process-content", children: [
2059
- item.imageSrc ? /* @__PURE__ */ jsx2(
2060
- "img",
2061
- {
2062
- className: "rt-tpl-process-image",
2063
- src: item.imageSrc,
2064
- alt: item.imageAlt ?? "",
2065
- loading: "lazy"
2066
- }
2067
- ) : null,
2068
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-step", children: item.eyebrow ?? `step ${String(index + 1).padStart(2, "0")}` }),
2069
- /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-process-title", children: item.title }),
2070
- /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-desc", children: item.description }),
2071
- item.duration ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-process-duration", children: item.duration }) : null
2072
- ] }) }, `${index}-${item.title}`))
2073
- }
2074
- ),
2075
- renderPracticeDisclaimer(disclaimer)
2076
- ] })
2077
- }
2078
- );
2079
- }
2080
- function TemplateBeforeAfterSideBySide(section) {
2081
- const { beforeImage, afterImage, beforeLabel, afterLabel } = section.props;
2082
- return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-before-after-pair", children: [
2083
- /* @__PURE__ */ jsxs2("figure", { className: "rt-tpl-before-after-figure", children: [
2084
- /* @__PURE__ */ jsx2(
2085
- "img",
2086
- {
2087
- className: "rt-tpl-before-after-img",
2088
- src: beforeImage.src,
2089
- alt: beforeImage.alt,
2090
- loading: "lazy"
2091
- }
2092
- ),
2093
- /* @__PURE__ */ jsx2("figcaption", { className: "rt-tpl-before-after-caption", children: beforeLabel })
2094
- ] }),
2095
- /* @__PURE__ */ jsxs2("figure", { className: "rt-tpl-before-after-figure", children: [
2096
- /* @__PURE__ */ jsx2(
2097
- "img",
2098
- {
2099
- className: "rt-tpl-before-after-img",
2100
- src: afterImage.src,
2101
- alt: afterImage.alt,
2102
- loading: "lazy"
2103
- }
2104
- ),
2105
- /* @__PURE__ */ jsx2("figcaption", { className: "rt-tpl-before-after-caption", children: afterLabel })
2106
- ] })
2107
- ] });
2108
- }
2109
- function TemplateBeforeAfter(section) {
2110
- const {
2111
- heading,
2112
- description,
2113
- beforeImage,
2114
- afterImage,
2115
- beforeLabel,
2116
- afterLabel,
2117
- disclaimer
2118
- } = section.props;
2119
- return /* @__PURE__ */ jsx2(
2120
- "section",
2121
- {
2122
- "data-roottale-tpl": "before-after",
2123
- "data-variant": section.variant,
2124
- id: section.id,
2125
- className: "rt-tpl-section rt-tpl-practice rt-tpl-before-after",
2126
- style: sectionThemeStyle(section.theme),
2127
- children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
2128
- renderPracticeHeader({ heading, description }),
2129
- section.variant === "side-by-side" ? /* @__PURE__ */ jsx2(TemplateBeforeAfterSideBySide, { ...section }) : /* @__PURE__ */ jsx2(
2130
- BeforeAfterSlider,
2131
- {
2132
- beforeImage,
2133
- afterImage,
2134
- beforeLabel,
2135
- afterLabel
2136
- }
2137
- ),
2138
- renderPracticeDisclaimer(disclaimer)
2139
- ] })
2140
- }
2141
- );
2142
- }
2143
-
2144
- // src/templates/professional-sections.tsx
2145
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2146
- function SectionHeader({
2147
- heading,
2148
- description
2149
- }) {
2150
- if (!heading && !description) return null;
2151
- return /* @__PURE__ */ jsxs3("header", { className: "rt-tpl-client-section-header", children: [
2152
- heading ? /* @__PURE__ */ jsx3("h2", { className: "rt-tpl-heading", children: heading }) : null,
2153
- description ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-section-desc", children: description }) : null
2154
- ] });
2155
- }
2156
- function TagList({
2157
- tags,
2158
- label
2159
- }) {
2160
- if (!tags || tags.length === 0) return null;
2161
- return /* @__PURE__ */ jsx3("ul", { className: "rt-tpl-client-tags", "aria-label": label, children: tags.map((tag) => /* @__PURE__ */ jsx3("li", { children: tag }, tag)) });
2162
- }
2163
- function TemplateExpertiseCards(section) {
2164
- const { heading, description, items } = section.props;
2165
- return /* @__PURE__ */ jsx3(
2166
- "section",
2167
- {
2168
- "data-roottale-tpl": "expertise",
2169
- "data-variant": "cards",
2170
- id: section.id,
2171
- className: "rt-tpl-section rt-tpl-expertise",
2172
- style: sectionThemeStyle(section.theme),
2173
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2174
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2175
- /* @__PURE__ */ jsx3("ul", { className: "rt-tpl-expertise-card-grid", children: items.map((item) => /* @__PURE__ */ jsxs3("li", { className: "rt-tpl-expertise-card", children: [
2176
- item.imageSrc ? /* @__PURE__ */ jsx3(
2177
- "img",
2178
- {
2179
- className: "rt-tpl-expertise-card-img",
2180
- src: item.imageSrc,
2181
- alt: item.imageAlt ?? "",
2182
- loading: "lazy"
2183
- }
2184
- ) : null,
2185
- /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-expertise-card-body", children: [
2186
- item.kicker ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-expertise-kicker", children: item.kicker }) : null,
2187
- /* @__PURE__ */ jsx3("h3", { className: "rt-tpl-expertise-title", children: item.title }),
2188
- /* @__PURE__ */ jsx3("p", { className: "rt-tpl-expertise-body", children: item.body }),
2189
- /* @__PURE__ */ jsx3(TagList, { tags: item.tags, label: `${item.title} \uD0DC\uADF8` }),
2190
- item.href ? /* @__PURE__ */ jsx3("a", { className: "rt-tpl-expertise-cta", href: item.href, children: item.ctaLabel ?? "\uC790\uC138\uD788 \uBCF4\uAE30" }) : null
2191
- ] })
2192
- ] }, item.id)) })
2193
- ] })
2194
- }
2195
- );
2196
- }
2197
- function TemplateExpertise(section) {
2198
- const { heading, description, items } = section.props;
2199
- if (section.variant === "cards") return TemplateExpertiseCards(section);
2200
- return /* @__PURE__ */ jsx3(
2201
- "section",
2202
- {
2203
- "data-roottale-tpl": "expertise",
2204
- "data-variant": "tab-stage",
2205
- id: section.id,
2206
- className: "rt-tpl-section rt-tpl-expertise",
2207
- style: sectionThemeStyle(section.theme),
2208
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2209
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2210
- /* @__PURE__ */ jsx3(ExpertiseTabStage, { items })
2211
- ] })
2212
- }
2213
- );
2214
- }
2215
- function TemplateSpacesMosaic(section) {
2216
- const { heading, description, spaces, disclaimer } = section.props;
2217
- return /* @__PURE__ */ jsx3(
2218
- "section",
2219
- {
2220
- "data-roottale-tpl": "spaces",
2221
- "data-variant": "mosaic",
2222
- id: section.id,
2223
- className: "rt-tpl-section rt-tpl-spaces",
2224
- style: sectionThemeStyle(section.theme),
2225
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2226
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2227
- /* @__PURE__ */ jsx3("ul", { className: "rt-tpl-spaces-mosaic", children: spaces.map((space) => /* @__PURE__ */ jsxs3("li", { className: "rt-tpl-spaces-tile", children: [
2228
- /* @__PURE__ */ jsx3("img", { src: space.imageSrc, alt: space.imageAlt, loading: "lazy" }),
2229
- /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-spaces-tile-caption", children: [
2230
- space.kicker ? /* @__PURE__ */ jsx3("span", { className: "rt-tpl-spaces-kicker", children: space.kicker }) : null,
2231
- /* @__PURE__ */ jsx3("h3", { children: space.title ?? space.label }),
2232
- space.description ? /* @__PURE__ */ jsx3("p", { children: space.description }) : null,
2233
- /* @__PURE__ */ jsx3(TagList, { tags: space.tags, label: `${space.label} \uD0DC\uADF8` })
2234
- ] })
2235
- ] }, space.id)) }),
2236
- disclaimer ? /* @__PURE__ */ jsx3("aside", { className: "rt-tpl-client-disclaimer", children: disclaimer }) : null
2237
- ] })
2238
- }
2239
- );
2240
- }
2241
- function TemplateSpaces(section) {
2242
- const { heading, description, spaces, disclaimer } = section.props;
2243
- if (section.variant === "mosaic") return TemplateSpacesMosaic(section);
2244
- return /* @__PURE__ */ jsx3(
2245
- "section",
2246
- {
2247
- "data-roottale-tpl": "spaces",
2248
- "data-variant": "stage",
2249
- id: section.id,
2250
- className: "rt-tpl-section rt-tpl-spaces",
2251
- style: sectionThemeStyle(section.theme),
2252
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2253
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2254
- /* @__PURE__ */ jsx3(SpacesStage, { spaces }),
2255
- disclaimer ? /* @__PURE__ */ jsx3("aside", { className: "rt-tpl-client-disclaimer", children: disclaimer }) : null
2256
- ] })
2257
- }
2258
- );
2259
- }
2260
- function TemplateConcernsList(section) {
2261
- const { heading, description, regions, disclaimer } = section.props;
2262
- return /* @__PURE__ */ jsx3(
2263
- "section",
2264
- {
2265
- "data-roottale-tpl": "concerns",
2266
- "data-variant": "list",
2267
- id: section.id,
2268
- className: "rt-tpl-section rt-tpl-concerns",
2269
- style: sectionThemeStyle(section.theme),
2270
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2271
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2272
- /* @__PURE__ */ jsx3("ul", { className: "rt-tpl-concerns-list", children: regions.map((region) => /* @__PURE__ */ jsxs3("li", { className: "rt-tpl-concerns-card", children: [
2273
- /* @__PURE__ */ jsx3("p", { className: "rt-tpl-concerns-label", children: region.label }),
2274
- /* @__PURE__ */ jsx3("h3", { className: "rt-tpl-concerns-title", children: region.title }),
2275
- /* @__PURE__ */ jsx3("p", { className: "rt-tpl-concerns-desc", children: region.description }),
2276
- region.items ? /* @__PURE__ */ jsx3("ul", { className: "rt-tpl-concerns-items", children: region.items.map((item) => /* @__PURE__ */ jsx3("li", { children: item }, item)) }) : null,
2277
- region.href ? /* @__PURE__ */ jsx3("a", { className: "rt-tpl-concerns-cta", href: region.href, children: region.ctaLabel ?? "\uC790\uC138\uD788 \uBCF4\uAE30" }) : null
2278
- ] }, region.id)) }),
2279
- /* @__PURE__ */ jsx3("aside", { className: "rt-tpl-client-disclaimer", children: disclaimer })
2280
- ] })
2281
- }
2282
- );
2283
- }
2284
- function TemplateConcerns(section) {
2285
- const {
2286
- heading,
2287
- description,
2288
- figureImage,
2289
- figureAlt,
2290
- regions,
2291
- disclaimer
2292
- } = section.props;
2293
- if (section.variant === "list") return TemplateConcernsList(section);
2294
- return /* @__PURE__ */ jsx3(
2295
- "section",
2296
- {
2297
- "data-roottale-tpl": "concerns",
2298
- "data-variant": "figure-map",
2299
- id: section.id,
2300
- className: "rt-tpl-section rt-tpl-concerns",
2301
- style: sectionThemeStyle(section.theme),
2302
- children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
2303
- /* @__PURE__ */ jsx3(SectionHeader, { heading, description }),
2304
- /* @__PURE__ */ jsx3(
2305
- ConcernsFigureMap,
2306
- {
2307
- figureImage: figureImage ?? "",
2308
- figureAlt: figureAlt ?? "",
2309
- regions
2310
- }
2311
- ),
2312
- /* @__PURE__ */ jsx3("aside", { className: "rt-tpl-client-disclaimer", children: disclaimer })
2313
- ] })
2314
- }
2315
- );
2316
- }
2317
-
2318
- // src/render-section-node.tsx
2319
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2320
- function warnSkippedCmsBound(sectionType, isDev) {
2321
- if (isDev()) {
2322
- console.warn(
2323
- `[cms-renderer-next] section \uB178\uB4DC sectionType=${sectionType} \uB294 apiKey \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC5B4 \uB80C\uB354 skip`
2324
- );
2325
- }
2326
- }
2327
- function renderSectionNode(node, key, context) {
2328
- const attrs = node.attrs ?? {};
2329
- const input = {
2330
- type: typeof attrs.sectionType === "string" ? attrs.sectionType : "",
2331
- variant: attrs.variant ?? void 0,
2332
- theme: attrs.theme ?? void 0,
2333
- props: attrs.props ?? {}
2334
- };
2335
- const parsed = SectionSchema.safeParse(input);
2336
- if (!parsed.success) {
2337
- if (context.isDev()) {
2338
- console.warn(
2339
- `[cms-renderer-next] section \uB178\uB4DC parse \uC2E4\uD328 (sectionType=${input.type}) \u2014 \uB80C\uB354 skip:`,
2340
- parsed.error.issues
2341
- );
2342
- }
2343
- return null;
2344
- }
2345
- const section = parsed.data;
2346
- switch (section.type) {
2347
- case "hero":
2348
- return /* @__PURE__ */ jsx4(TemplateHero, { ...section }, key);
2349
- case "rich-text":
2350
- return /* @__PURE__ */ jsx4(TemplateRichText, { ...section }, key);
2351
- case "services":
2352
- return /* @__PURE__ */ jsx4(TemplateServices, { ...section }, key);
2353
- case "faq":
2354
- return /* @__PURE__ */ jsx4(TemplateFaq, { ...section }, key);
2355
- case "footer":
2356
- return /* @__PURE__ */ jsx4(TemplateFooter, { ...section }, key);
2357
- case "cta-banner":
2358
- return /* @__PURE__ */ jsx4(TemplateCtaBanner, { ...section }, key);
2359
- case "trust":
2360
- return /* @__PURE__ */ jsx4(TemplateTrust, { ...section }, key);
2361
- case "hours":
2362
- return /* @__PURE__ */ jsx4(TemplateHours, { ...section }, key);
2363
- case "pricing":
2364
- return /* @__PURE__ */ jsx4(TemplatePricing, { ...section }, key);
2365
- case "process":
2366
- return /* @__PURE__ */ jsx4(TemplateProcess, { ...section }, key);
2367
- case "before-after":
2368
- return /* @__PURE__ */ jsx4(TemplateBeforeAfter, { ...section }, key);
2369
- case "testimonials":
2370
- return /* @__PURE__ */ jsx4(TemplateTestimonials, { ...section }, key);
2371
- case "team":
2372
- return /* @__PURE__ */ jsx4(TemplateTeam, { ...section }, key);
2373
- case "expertise":
2374
- return /* @__PURE__ */ jsx4(TemplateExpertise, { ...section }, key);
2375
- case "spaces":
2376
- return /* @__PURE__ */ jsx4(TemplateSpaces, { ...section }, key);
2377
- case "concerns":
2378
- return /* @__PURE__ */ jsx4(TemplateConcerns, { ...section }, key);
2379
- case "contact":
2380
- if (!context.cms) {
2381
- warnSkippedCmsBound(section.type, context.isDev);
2382
- return null;
2383
- }
2384
- return /* @__PURE__ */ jsx4(TemplateContact, { ...section }, key);
2385
- case "blog-list": {
2386
- if (!context.cms) {
2387
- warnSkippedCmsBound(section.type, context.isDev);
2388
- return null;
2389
- }
2390
- const s = section;
2391
- const base = s.props.blogBasePath ?? "/blog";
2392
- return /* @__PURE__ */ jsx4(
2393
- "section",
2394
- {
2395
- "data-roottale-tpl": "blog-list",
2396
- "data-roottale-cms": "list",
2397
- id: s.id,
2398
- className: "rt-tpl-section rt-tpl-blog-list",
2399
- style: sectionThemeStyle(s.theme),
2400
- children: /* @__PURE__ */ jsxs4("div", { className: "rt-tpl-container", children: [
2401
- s.props.heading ? /* @__PURE__ */ jsx4("h2", { className: "rt-tpl-heading", children: s.props.heading }) : null,
2402
- context.renderBlogList({
2403
- apiKey: context.cms.apiKey,
2404
- baseUrl: context.cms.baseUrl,
2405
- limit: s.props.limit,
2406
- emptyMessage: s.props.emptyMessage,
2407
- postHref: (post) => `${base}/${post.slug}`
2408
- })
2409
- ] })
2410
- },
2411
- key
2412
- );
2413
- }
2414
- case "lead-form": {
2415
- if (!context.cms) {
2416
- warnSkippedCmsBound(section.type, context.isDev);
2417
- return null;
2418
- }
2419
- const s = section;
2420
- return /* @__PURE__ */ jsx4(
2421
- "section",
2422
- {
2423
- "data-roottale-tpl": "lead-form",
2424
- "data-roottale-cms": "lead-form",
2425
- id: s.id,
2426
- className: "rt-tpl-section rt-tpl-lead-form",
2427
- style: sectionThemeStyle(s.theme),
2428
- children: /* @__PURE__ */ jsx4("div", { className: "rt-tpl-container", children: /* @__PURE__ */ jsx4(
2429
- RootTaleLeadForm,
2430
- {
2431
- action: s.props.action,
2432
- vertical: s.props.vertical,
2433
- heading: s.props.heading,
2434
- description: s.props.description,
2435
- submitLabel: s.props.submitLabel
2436
- }
2437
- ) })
2438
- },
2439
- key
2440
- );
2441
- }
2442
- default:
2443
- if (context.isDev()) {
2444
- console.warn(
2445
- `[cms-renderer-next] section \uB178\uB4DC sectionType=${section.type} \uB294 renderNode \uC704\uC784 \uB300\uC0C1\uC774 \uC544\uB2D8(cms-bound/\uBBF8\uC9C0) \u2014 skip`
2446
- );
2447
- }
2448
- return null;
2449
- }
2450
- }
2451
-
2452
- // src/PostCard.tsx
2453
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2454
- function firstCategoryName(terms) {
2455
- if (!terms) return null;
2456
- const cat = terms.find((t2) => t2.taxonomy === "category");
2457
- return cat ? cat.name : null;
2458
- }
2459
- function RootTalePostCard(props) {
2460
- const { post, href = defaultHref, display } = props;
2461
- const showDate = display ? display.showDate : true;
2462
- const showAuthor = display ? display.showAuthor : false;
2463
- const showImage = display?.showImage !== false;
2464
- const showCategory = display?.showCategory !== false;
2465
- const authorName = post.authorName;
2466
- const publishedDate = showDate ? formatPublishedDate(post.publishedAt) : "";
2467
- const views = typeof post.viewCount === "number" ? post.viewCount : null;
2468
- const imageUrl = showImage ? post.featuredImageUrl : null;
2469
- const category = showCategory ? firstCategoryName(post.terms) : null;
2470
- return /* @__PURE__ */ jsx5("article", { "data-roottale-cms": "card", className: "rt-cms-card", children: /* @__PURE__ */ jsxs5("a", { className: "rt-cms-card-link", href: href(post), children: [
2471
- imageUrl ? (
2472
- // eslint-disable-next-line @next/next/no-img-element -- SSR string URL, framework-agnostic
2473
- /* @__PURE__ */ jsx5(
2474
- "img",
2475
- {
2476
- className: "rt-cms-card-image",
2477
- src: imageUrl,
2478
- alt: "",
2479
- loading: "lazy",
2480
- decoding: "async"
2481
- }
2482
- )
2483
- ) : null,
2484
- category ? /* @__PURE__ */ jsx5("span", { className: "rt-cms-card-category", children: category }) : null,
2485
- publishedDate || showAuthor && authorName || views !== null ? /* @__PURE__ */ jsxs5("p", { className: "rt-cms-meta", children: [
2486
- publishedDate ? /* @__PURE__ */ jsx5("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "date", children: publishedDate }) : null,
2487
- showAuthor && authorName ? /* @__PURE__ */ jsxs5("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "author", children: [
2488
- "\uC791\uC131\uC790 ",
2489
- authorName
2490
- ] }) : null,
2491
- views !== null ? /* @__PURE__ */ jsxs5("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "views", children: [
2492
- "\uC870\uD68C ",
2493
- views.toLocaleString("ko-KR")
2494
- ] }) : null
2495
- ] }) : null,
2496
- /* @__PURE__ */ jsx5("h2", { className: "rt-cms-title", children: post.title }),
2497
- post.excerpt ? /* @__PURE__ */ jsx5("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null,
2498
- /* @__PURE__ */ jsx5("span", { className: "rt-cms-card-action", "aria-hidden": "true", children: "\uC77D\uAE30" })
2499
- ] }) });
2500
- }
2501
- function defaultHref(post) {
2502
- return `/blog/${post.slug}`;
2503
- }
2504
- function formatPublishedDate(iso) {
2505
- const d = new Date(iso);
2506
- if (Number.isNaN(d.getTime())) return "";
2507
- return new Intl.DateTimeFormat("ko-KR", {
2508
- year: "numeric",
2509
- month: "2-digit",
2510
- day: "2-digit"
2511
- }).format(d);
2512
- }
2513
-
2514
- // src/TableOfContents.tsx
2515
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2516
- function RootTaleTableOfContents(props) {
2517
- const { entries, locale, title = t(locale, "tableOfContentsTitle") } = props;
2518
- if (entries.length === 0) return null;
2519
- return /* @__PURE__ */ jsx6("nav", { "data-roottale-cms": "toc", className: "rt-cms-toc", "aria-label": title, children: /* @__PURE__ */ jsxs6("details", { className: "rt-cms-toc-details", open: true, children: [
2520
- /* @__PURE__ */ jsxs6("summary", { className: "rt-cms-toc-summary", children: [
2521
- title ? /* @__PURE__ */ jsx6("span", { className: "rt-cms-toc-title", children: title }) : null,
2522
- /* @__PURE__ */ jsx6("span", { className: "rt-cms-toc-toggle", "aria-hidden": "true" })
2523
- ] }),
2524
- /* @__PURE__ */ jsx6("ol", { className: "rt-cms-toc-list", children: entries.map((entry) => /* @__PURE__ */ jsx6(
2525
- "li",
2526
- {
2527
- className: `rt-cms-toc-item rt-cms-toc-level-${entry.level}`,
2528
- children: /* @__PURE__ */ jsx6("a", { href: `#${entry.id}`, children: entry.text })
2529
- },
2530
- entry.id
2531
- )) })
2532
- ] }) });
2533
- }
2534
-
2535
- // ../ui-react/dist/theme/index.js
2536
- import { jsx as _jsx } from "react/jsx-runtime";
2537
- function themeToCssVars(theme) {
2538
- return {
2539
- ...theme.colors?.primary ? { "--rt-color-primary": theme.colors.primary } : {},
2540
- ...theme.colors?.primaryForeground ? { "--rt-color-primary-foreground": theme.colors.primaryForeground } : {},
2541
- ...theme.colors?.secondary ? { "--rt-color-secondary": theme.colors.secondary } : {},
2542
- ...theme.colors?.secondaryForeground ? { "--rt-color-secondary-foreground": theme.colors.secondaryForeground } : {},
2543
- ...theme.colors?.accent ? { "--rt-color-accent": theme.colors.accent } : {},
2544
- ...theme.colors?.accentForeground ? { "--rt-color-accent-foreground": theme.colors.accentForeground } : {},
2545
- ...theme.colors?.background ? { "--rt-color-background": theme.colors.background } : {},
2546
- ...theme.colors?.foreground ? { "--rt-color-foreground": theme.colors.foreground } : {},
2547
- ...theme.colors?.muted ? { "--rt-color-muted": theme.colors.muted } : {},
2548
- ...theme.colors?.mutedForeground ? { "--rt-color-muted-foreground": theme.colors.mutedForeground } : {},
2549
- ...theme.colors?.destructive ? { "--rt-color-destructive": theme.colors.destructive } : {},
2550
- ...theme.colors?.destructiveForeground ? { "--rt-color-destructive-foreground": theme.colors.destructiveForeground } : {},
2551
- ...theme.colors?.border ? { "--rt-color-border": theme.colors.border } : {},
2552
- ...theme.colors?.input ? { "--rt-color-input": theme.colors.input } : {},
2553
- ...theme.colors?.ring ? { "--rt-color-ring": theme.colors.ring } : {},
2554
- ...theme.fonts?.body ? { "--rt-font-body": theme.fonts.body } : {},
2555
- ...theme.fonts?.display ? { "--rt-font-display": theme.fonts.display } : {},
2556
- ...theme.radius?.sm ? { "--rt-radius-sm": theme.radius.sm } : {},
2557
- ...theme.radius?.md ? { "--rt-radius-md": theme.radius.md } : {},
2558
- ...theme.radius?.lg ? { "--rt-radius-lg": theme.radius.lg } : {},
2559
- ...theme.radius?.xl ? { "--rt-radius-xl": theme.radius.xl } : {}
2560
- };
2561
- }
2562
-
2563
- // src/theme-css-vars.ts
2564
- function themeToCssVars2(theme) {
2565
- if (!theme) return null;
2566
- const vars = themeToCssVars(theme);
2567
- if (Object.keys(vars).length === 0) return null;
2568
- return vars;
2569
- }
2570
-
2571
- // src/toc.ts
2572
- function tocLevel(rawLevel) {
2573
- if (rawLevel < 1 || rawLevel > 4) return null;
2574
- if (rawLevel < 3) return 2;
2575
- return rawLevel === 3 ? 3 : 4;
2576
- }
2577
- function headingToId(text) {
2578
- return text.trim().toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2579
- }
2580
- function textOf(node) {
2581
- if (typeof node.text === "string") return node.text;
2582
- if (!Array.isArray(node.content)) return "";
2583
- return node.content.map(textOf).join("");
2584
- }
2585
- function extractToc(doc) {
2586
- const root = doc;
2587
- const content = Array.isArray(root.content) ? root.content : [];
2588
- const entries = [];
2589
- const idCounts = /* @__PURE__ */ new Map();
2590
- for (const node of content) {
2591
- if (node?.type !== "heading") continue;
2592
- const rawLevel = Number(node.attrs?.level ?? 2);
2593
- const level = tocLevel(rawLevel);
2594
- if (level === null) continue;
2595
- const text = textOf(node).trim();
2596
- if (!text) continue;
2597
- const base = headingToId(text) || "heading";
2598
- const count = idCounts.get(base) ?? 0;
2599
- const id = count === 0 ? base : `${base}-${count}`;
2600
- idCounts.set(base, count + 1);
2601
- entries.push({ level, text, id });
2602
- }
2603
- return entries;
2604
- }
2605
- function attachHeadingIds(doc, entries) {
2606
- const root = doc;
2607
- const content = Array.isArray(root.content) ? root.content : [];
2608
- let cursor = 0;
2609
- const nextContent = content.map((node) => {
2610
- if (node?.type !== "heading") return node;
2611
- const rawLevel = Number(node.attrs?.level ?? 2);
2612
- if (rawLevel < 1 || rawLevel > 4) return node;
2613
- if (!textOf(node).trim()) return node;
2614
- const entry = entries[cursor++];
2615
- if (!entry) return node;
2616
- return {
2617
- ...node,
2618
- attrs: { ...node.attrs ?? {}, id: entry.id }
2619
- };
2620
- });
2621
- return { ...root, content: nextContent };
2622
- }
2623
-
2624
- // src/FloatingCta.tsx
2625
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2626
- function RootTaleFloatingCta(props) {
2627
- const { buttons, position = "bottom-right" } = props;
2628
- if (!buttons || buttons.length === 0) return null;
2629
- return /* @__PURE__ */ jsx7(
2630
- "div",
2631
- {
2632
- "data-roottale-cms": "floating-cta",
2633
- className: `rt-cms-floating-cta rt-cms-floating-cta--${position}`,
2634
- children: buttons.map((btn, idx) => {
2635
- const isExternal = btn.type === "kakao" || btn.type === "custom";
2636
- return /* @__PURE__ */ jsxs7(
2637
- "a",
2638
- {
2639
- href: btn.href,
2640
- className: `rt-cms-floating-cta__btn rt-cms-floating-cta__btn--${btn.type}`,
2641
- target: isExternal ? "_blank" : void 0,
2642
- rel: isExternal ? "noopener noreferrer" : void 0,
2643
- children: [
2644
- /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "rt-cms-floating-cta__icon", children: btn.icon ?? defaultIcon(btn.type) }),
2645
- /* @__PURE__ */ jsx7("span", { className: "rt-cms-floating-cta__label", children: btn.label })
2646
- ]
2647
- },
2648
- `${btn.type}-${idx}`
2649
- );
2650
- })
2651
- }
2652
- );
2653
- }
2654
- function defaultIcon(type) {
2655
- switch (type) {
2656
- case "phone":
2657
- return "\u260E";
2658
- case "contact":
2659
- return "\u2709";
2660
- case "kakao":
2661
- return "\u{1F4AC}";
2662
- case "custom":
2663
- return "\u2192";
2664
- }
2665
- }
2666
-
2667
- // src/block-to-react.tsx
2668
- import {
2669
- applyBlockBindings,
2670
- createFieldBindingResolver,
2671
- sanitizeHtml
2672
- } from "@roottale/cms-core";
2673
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2674
- function isString(value) {
2675
- return typeof value === "string";
2676
- }
2677
- function htmlContent(html) {
2678
- return { __html: sanitizeHtml(html) };
2679
- }
2680
- function renderInner(blocks) {
2681
- return blocks.map((b, i) => renderBlock(b, i));
2682
- }
2683
- function renderBlock(block, key = 0) {
2684
- const dataBlockId = block._id;
2685
- switch (block.name) {
2686
- case "core/paragraph": {
2687
- const text = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
2688
- return /* @__PURE__ */ jsx8(
2689
- "p",
2690
- {
2691
- "data-block-id": dataBlockId,
2692
- dangerouslySetInnerHTML: htmlContent(text)
2693
- },
2694
- key
2695
- );
2696
- }
2697
- case "core/heading": {
2698
- const levelRaw = block.attributes.level;
2699
- const level = typeof levelRaw === "number" && levelRaw >= 2 && levelRaw <= 4 ? levelRaw : 2;
2700
- const text = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
2701
- const props = {
2702
- "data-block-id": dataBlockId,
2703
- dangerouslySetInnerHTML: htmlContent(text)
2704
- };
2705
- if (level === 2) return /* @__PURE__ */ jsx8("h2", { ...props }, key);
2706
- if (level === 3) return /* @__PURE__ */ jsx8("h3", { ...props }, key);
2707
- return /* @__PURE__ */ jsx8("h4", { ...props }, key);
2708
- }
2709
- case "core/image": {
2710
- const src = isString(block.attributes.url) ? block.attributes.url : "";
2711
- const alt = isString(block.attributes.alt) ? block.attributes.alt : "";
2712
- const caption = isString(block.attributes.caption) ? block.attributes.caption : null;
2713
- return /* @__PURE__ */ jsxs8("figure", { "data-block-id": dataBlockId, children: [
2714
- /* @__PURE__ */ jsx8("img", { src, alt, loading: "lazy" }),
2715
- caption && /* @__PURE__ */ jsx8("figcaption", { children: caption })
2716
- ] }, key);
2717
- }
2718
- case "core/list": {
2719
- const ordered = block.attributes.ordered === true;
2720
- const items = (block.innerBlocks ?? []).map(
2721
- (item, i) => item.rawHtml ? /* @__PURE__ */ jsx8(
2722
- "li",
2723
- {
2724
- dangerouslySetInnerHTML: htmlContent(item.rawHtml)
2725
- },
2726
- i
2727
- ) : /* @__PURE__ */ jsx8("li", { children: renderInner(item.innerBlocks ?? []) }, i)
2728
- );
2729
- return ordered ? /* @__PURE__ */ jsx8("ol", { "data-block-id": dataBlockId, children: items }, key) : /* @__PURE__ */ jsx8("ul", { "data-block-id": dataBlockId, children: items }, key);
2730
- }
2731
- case "core/quote": {
2732
- const inner = renderInner(block.innerBlocks ?? []);
2733
- const citation = isString(block.attributes.citation) ? block.attributes.citation : null;
2734
- return /* @__PURE__ */ jsxs8("blockquote", { "data-block-id": dataBlockId, children: [
2735
- inner,
2736
- citation && /* @__PURE__ */ jsx8("cite", { children: citation })
2737
- ] }, key);
2738
- }
2739
- case "core/code": {
2740
- const code = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
2741
- const lang = isString(block.attributes.language) ? block.attributes.language : void 0;
2742
- return /* @__PURE__ */ jsx8(
2743
- "pre",
2744
- {
2745
- "data-block-id": dataBlockId,
2746
- "data-language": lang,
2747
- children: /* @__PURE__ */ jsx8("code", { children: code })
2748
- },
2749
- key
2750
- );
2751
- }
2752
- case "core/columns": {
2753
- return /* @__PURE__ */ jsx8(
2754
- "div",
2755
- {
2756
- className: "rt-columns",
2757
- "data-block-id": dataBlockId,
2758
- children: renderInner(block.innerBlocks ?? [])
2759
- },
2760
- key
2761
- );
2762
- }
2763
- case "core/separator":
2764
- return /* @__PURE__ */ jsx8("hr", { "data-block-id": dataBlockId }, key);
2765
- case "core/spacer": {
2766
- const heightRaw = block.attributes.height;
2767
- const height = isString(heightRaw) ? heightRaw : "32px";
2768
- return /* @__PURE__ */ jsx8(
2769
- "div",
2770
- {
2771
- className: "rt-spacer",
2772
- "data-block-id": dataBlockId,
2773
- style: { height }
2774
- },
2775
- key
2776
- );
2777
- }
2778
- case "core/group": {
2779
- return /* @__PURE__ */ jsx8(
2780
- "div",
2781
- {
2782
- className: "rt-group",
2783
- "data-block-id": dataBlockId,
2784
- children: renderInner(block.innerBlocks ?? [])
2785
- },
2786
- key
2787
- );
2788
- }
2789
- default: {
2790
- const rawHtml = block.rawHtml;
2791
- return /* @__PURE__ */ jsx8(
2792
- "div",
2793
- {
2794
- className: "rt-unknown-block",
2795
- "data-block-id": dataBlockId,
2796
- "data-block-name": block.name,
2797
- "data-rawhtml-suppressed": rawHtml ? "true" : void 0,
2798
- children: renderInner(block.innerBlocks ?? [])
2799
- },
2800
- key
2801
- );
2802
- }
2803
- }
2804
- }
2805
- function renderBlocks(blocks) {
2806
- return blocks.map((b, i) => renderBlock(b, i));
2807
- }
2808
- function renderBoundBlocks(blocks, opts = {}) {
2809
- const resolver = opts.resolver ?? createFieldBindingResolver(opts.fields);
2810
- return renderBlocks(applyBlockBindings(blocks, resolver));
2811
- }
2812
-
2813
- // src/server.tsx
2814
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2815
- function authorImageRadiusCss(radius) {
2816
- if (radius === "circle") return "999px";
2817
- if (radius === "rounded") return "0.875rem";
2818
- return "0";
2819
- }
2820
- function RootTaleThemeProvider(props) {
2821
- const style = themeToCssVars2(props.theme ?? null);
2822
- if (!style) return /* @__PURE__ */ jsx9(Fragment3, { children: props.children });
2823
- return /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "theme-root", "data-rt-theme": "", style, children: props.children });
2824
- }
2825
- async function RootTaleBlogList(props) {
2826
- const {
2827
- apiKey,
2828
- baseUrl,
2829
- limit,
2830
- type = "post",
2831
- collection,
2832
- collections,
2833
- excludeCollections = false,
2834
- locale,
2835
- emptyMessage = t(locale, "blogListEmpty"),
2836
- showCategoryFilter = false,
2837
- activeCategory,
2838
- categoryHref = defaultCategoryHref,
2839
- allCategoriesLabel = t(locale, "allCategoriesLabel"),
2840
- author,
2841
- theme: themeProp
2842
- } = props;
2843
- if (collection && excludeCollections) {
2844
- throw new Error(
2845
- "RootTaleBlogList: `collection` \uACFC `excludeCollections` \uB97C \uB3D9\uC2DC\uC5D0 \uC904 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uD3EC\uC9C0\uD2F0\uBE0C/\uB124\uAC70\uD2F0\uBE0C \uC2A4\uCF54\uD504\uB294 \uBC30\uD0C0\uC801\uC785\uB2C8\uB2E4."
2846
- );
2847
- }
2848
- if (excludeCollections && !collections) {
2849
- throw new Error(
2850
- "RootTaleBlogList: `excludeCollections` \uB294 `collections` \uC640 \uD568\uAED8 \uC918\uC57C \uD569\uB2C8\uB2E4."
2851
- );
2852
- }
2853
- const postHref = props.postHref ?? (collections ? makeCollectionPostHref(collections) : defaultPostHref);
2854
- const [page, themeStyle, blogSettings] = await Promise.all([
2855
- fetchPosts({
2856
- apiKey,
2857
- baseUrl,
2858
- limit,
2859
- type,
2860
- ...author ? { author } : {},
2861
- // blog-list-collection-scope.md §2②(잔여 배선) — 포지티브 스코프도 서버가
2862
- // 이미 그 섹션 글만 걸러 fetch 하게 한다(limit 이 스트림 밖 글에 낭비되지
2863
- // 않음). 아래 `postsForCollection` 클라이언트 필터는 방어선으로 유지
2864
- // (판정 시맨틱 = `resolvePostCollection` 동일 정의).
2865
- ...collection && collections ? { collectionKey: collection } : {},
2866
- // blog-list-collection-scope.md §2② — 서버(api-core)가 이미 스트림 글을
2867
- // 걸러 fetch 하게 한다(limit 이 스트림 글에 낭비되지 않음). 아래
2868
- // `postsNotInAnyCollection` 클라이언트 필터는 방어선으로 유지.
2869
- ...excludeCollections && collections ? { excludeCollections: true } : {},
2870
- // ADR-0052 A안 (W4-6 PR C1) — 미지정 시 서버가 사이트 기본 locale 반환
2871
- // (하위호환, 기존 소비자 diff 0).
2872
- ...locale ? { locale } : {}
2873
- }),
2874
- resolveThemeStyle({ themeProp, apiKey, baseUrl }),
2875
- fetchBlogSettings({ apiKey, baseUrl }).catch(() => DEFAULT_BLOG_SETTINGS)
2876
- ]);
2877
- const items = collection && collections ? postsForCollection(page.items, collections, collection) : excludeCollections && collections ? postsNotInAnyCollection(page.items, collections) : page.items;
2878
- if (items.length === 0) {
2879
- return /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "list", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx9("p", { className: "rt-cms-empty", children: emptyMessage }) });
2880
- }
2881
- const categories = showCategoryFilter ? collectCategories(items) : [];
2882
- const visiblePosts = showCategoryFilter ? filterPostsByCategory(items, activeCategory) : items;
2883
- return /* @__PURE__ */ jsxs9("div", { "data-roottale-cms": "list", style: themeStyle ?? void 0, children: [
2884
- showCategoryFilter && categories.length > 0 ? /* @__PURE__ */ jsxs9("nav", { className: "rt-cms-filter", "aria-label": "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130", children: [
2885
- /* @__PURE__ */ jsx9(
2886
- "a",
2887
- {
2888
- className: "rt-cms-filter-chip",
2889
- href: categoryHref(null),
2890
- "data-active": !activeCategory ? "" : void 0,
2891
- "aria-current": !activeCategory ? "true" : void 0,
2892
- children: allCategoriesLabel
2893
- }
2894
- ),
2895
- categories.map((c) => /* @__PURE__ */ jsx9(
2896
- "a",
2897
- {
2898
- className: "rt-cms-filter-chip",
2899
- href: categoryHref(c.slug),
2900
- "data-active": activeCategory === c.slug ? "" : void 0,
2901
- "aria-current": activeCategory === c.slug ? "true" : void 0,
2902
- children: c.name
2903
- },
2904
- c.slug
2905
- ))
2906
- ] }) : null,
2907
- visiblePosts.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-empty", children: emptyMessage }) : /* @__PURE__ */ jsx9("ul", { className: "rt-cms-list", children: visiblePosts.map((post) => /* @__PURE__ */ jsx9("li", { className: "rt-cms-list-item", children: /* @__PURE__ */ jsx9(
2908
- RootTalePostCard,
2909
- {
2910
- post,
2911
- href: postHref,
2912
- display: resolvePostDisplay(post, blogSettings)
2913
- }
2914
- ) }, post.id)) })
2915
- ] });
2916
- }
2917
- async function RootTaleBlogCategories(props) {
2918
- const {
2919
- apiKey,
2920
- baseUrl,
2921
- limit,
2922
- collection,
2923
- collections,
2924
- categoryHref = (slug) => defaultCategoryHref(slug),
2925
- locale,
2926
- emptyMessage = t(locale, "blogCategoriesEmpty"),
2927
- theme: themeProp
2928
- } = props;
2929
- const [page, themeStyle] = await Promise.all([
2930
- fetchPosts({ apiKey, baseUrl, limit, type: "post", ...locale ? { locale } : {} }),
2931
- resolveThemeStyle({ themeProp, apiKey, baseUrl })
2932
- ]);
2933
- const categories = collection && collections ? collectCollectionCategories(page.items, collections, collection) : collectCategoriesWithCounts(page.items);
2934
- return /* @__PURE__ */ jsx9(
2935
- "nav",
2936
- {
2937
- "data-roottale-cms": "categories",
2938
- className: "rt-cms-categories",
2939
- "aria-label": "\uBE14\uB85C\uADF8 \uCE74\uD14C\uACE0\uB9AC",
2940
- style: themeStyle ?? void 0,
2941
- children: categories.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-empty", children: emptyMessage }) : /* @__PURE__ */ jsx9("ul", { className: "rt-cms-category-list", children: categories.map((category) => /* @__PURE__ */ jsx9("li", { className: "rt-cms-category-list-item", children: /* @__PURE__ */ jsxs9(
2942
- "a",
2943
- {
2944
- className: "rt-cms-category-link",
2945
- href: categoryHref(category.slug),
2946
- children: [
2947
- /* @__PURE__ */ jsx9("span", { className: "rt-cms-category-name", children: category.name }),
2948
- /* @__PURE__ */ jsx9("span", { className: "rt-cms-category-count", children: category.count })
2949
- ]
2950
- }
2951
- ) }, category.slug)) })
2952
- }
2953
- );
2954
- }
2955
- async function RootTaleBlogPost(props) {
2956
- const {
2957
- apiKey,
2958
- slugOrId,
2959
- baseUrl,
2960
- notFoundElement,
2961
- showTitle = true,
2962
- showTableOfContents,
2963
- tableOfContentsTitle,
2964
- relatedPostsCount = 0,
2965
- locale,
2966
- relatedPostsTitle = t(locale, "relatedPostsTitle"),
2967
- collections,
2968
- showFieldFacts = false,
2969
- theme: themeProp
2970
- } = props;
2971
- const [post, themeStyle, blogSettings] = await Promise.all([
2972
- fetchPost({ apiKey, slugOrId, baseUrl, ...locale ? { locale } : {} }),
2973
- resolveThemeStyle({ themeProp, apiKey, baseUrl }),
2974
- // 사이트 기본 표시 설정 — 실패해도 렌더는 진행 (안전 기본값).
2975
- fetchBlogSettings({ apiKey, baseUrl }).catch(() => DEFAULT_BLOG_SETTINGS)
2976
- ]);
2977
- if (!post) {
2978
- return notFoundElement ?? /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "post-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx9("p", { className: "rt-cms-empty", children: t(locale, "postNotFound") }) });
2979
- }
2980
- const display = resolvePostDisplay(post, blogSettings, {
2981
- showTableOfContents,
2982
- tableOfContentsTitle
2983
- });
2984
- const toc = display.showTableOfContents ? extractToc(post.bodyJson) : [];
2985
- const renderDoc = toc.length > 0 ? attachHeadingIds(post.bodyJson, toc) : post.bodyJson;
2986
- let related = [];
2987
- if (relatedPostsCount > 0) {
2988
- try {
2989
- const candidates = await fetchPosts({
2990
- apiKey,
2991
- baseUrl,
2992
- type: "post",
2993
- limit: 30,
2994
- // ADR-0052 A안 (W4-6 PR C1) — 같은 언어판 후보로만 좁힌다(ko 글이 en
2995
- // 페이지 관련 글에 섞이는 걸 방지).
2996
- ...locale ? { locale } : {}
2997
- });
2998
- related = selectRelatedPosts(post, candidates.items, relatedPostsCount);
2999
- } catch {
3000
- }
3001
- }
3002
- const relatedHref = props.postHref ?? (collections ? makeCollectionPostHref(collections) : defaultPostHref);
3003
- const authorName = post.authorName;
3004
- const authorImageUrl = post.authorImageUrl;
3005
- const postAuthorBio = post.authorBio;
3006
- const views = typeof post.viewCount === "number" ? post.viewCount : null;
3007
- const publishedDate = display.showDate ? formatPublishedDate2(post.publishedAt) : "";
3008
- const showTopAuthor = display.showAuthor && Boolean(authorName);
3009
- const displayAuthorName = authorName ?? display.authorProfileName;
3010
- const authorBio = postAuthorBio ?? display.authorProfileBio ?? display.authorCardDescription;
3011
- const authorImageUrlForCard = authorImageUrl ?? display.authorProfileImageUrl;
3012
- const authorImageAlt = `${displayAuthorName ?? ""} \uD504\uB85C\uD544 \uC0AC\uC9C4`.trim();
3013
- const authorImageStyle = {
3014
- borderRadius: authorImageRadiusCss(display.authorProfileImageRadius),
3015
- objectPosition: `${display.authorProfileImagePositionX}% ${display.authorProfileImagePositionY}%`
3016
- };
3017
- const showAuthorCard = display.showAuthorCard && Boolean(displayAuthorName);
3018
- return /* @__PURE__ */ jsxs9(
3019
- "article",
3020
- {
3021
- "data-roottale-cms": "post",
3022
- className: "rt-cms-article",
3023
- style: themeStyle ?? void 0,
3024
- children: [
3025
- /* @__PURE__ */ jsxs9("header", { className: "rt-cms-post-header", children: [
3026
- showTitle ? /* @__PURE__ */ jsx9("h1", { className: "rt-cms-title", children: post.title }) : null,
3027
- post.excerpt ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null,
3028
- publishedDate || showTopAuthor || views !== null ? /* @__PURE__ */ jsxs9("p", { className: "rt-cms-meta", children: [
3029
- publishedDate ? /* @__PURE__ */ jsx9("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "date", children: publishedDate }) : null,
3030
- showTopAuthor ? /* @__PURE__ */ jsxs9("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "author", children: [
3031
- "\uC791\uC131\uC790 ",
3032
- authorName
3033
- ] }) : null,
3034
- views !== null ? /* @__PURE__ */ jsxs9("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "views", children: [
3035
- "\uC870\uD68C ",
3036
- views.toLocaleString("ko-KR")
3037
- ] }) : null
3038
- ] }) : null
3039
- ] }),
3040
- renderArchiveRelated(post.archiveRelated, collections),
3041
- showFieldFacts ? renderFieldFacts(post) : null,
3042
- (() => {
3043
- const tocEl = toc.length > 0 ? /* @__PURE__ */ jsx9(RootTaleTableOfContents, { entries: toc, title: display.tocTitle }) : null;
3044
- const bodyEl = /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: /* @__PURE__ */ jsx9(
3045
- RenderTiptap,
3046
- {
3047
- doc: renderDoc,
3048
- fields: post.fields,
3049
- apiKey,
3050
- baseUrl
3051
- }
3052
- ) });
3053
- if (display.tocPosition === "sidebar" && tocEl) {
3054
- return /* @__PURE__ */ jsxs9("div", { className: "rt-cms-layout rt-cms-layout--toc-side", children: [
3055
- bodyEl,
3056
- /* @__PURE__ */ jsx9("aside", { className: "rt-cms-toc-side", children: tocEl })
3057
- ] });
3058
- }
3059
- return /* @__PURE__ */ jsxs9(Fragment3, { children: [
3060
- tocEl,
3061
- bodyEl
3062
- ] });
3063
- })(),
3064
- showAuthorCard && displayAuthorName ? /* @__PURE__ */ jsxs9("section", { className: "rt-cms-author-card", "aria-label": "\uC791\uC131\uC790", children: [
3065
- authorImageUrlForCard ? /* @__PURE__ */ jsx9(
3066
- "img",
3067
- {
3068
- className: "rt-cms-author-card__image",
3069
- src: authorImageUrlForCard,
3070
- alt: authorImageAlt,
3071
- loading: "lazy",
3072
- style: authorImageStyle
3073
- }
3074
- ) : /* @__PURE__ */ jsx9("div", { className: "rt-cms-author-card__avatar", "aria-hidden": "true", children: authorInitials(displayAuthorName) }),
3075
- /* @__PURE__ */ jsxs9("div", { className: "rt-cms-author-card__body", children: [
3076
- /* @__PURE__ */ jsx9("p", { className: "rt-cms-author-card__eyebrow", children: display.authorCardEyebrow }),
3077
- /* @__PURE__ */ jsx9("p", { className: "rt-cms-author-card__name", children: displayAuthorName }),
3078
- authorBio ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-author-card__description", children: authorBio }) : null
3079
- ] })
3080
- ] }) : null,
3081
- renderRelated(related, relatedHref, relatedPostsTitle),
3082
- renderPostCta(blogSettings.postCta),
3083
- renderFaqJsonLd(post.bodyJson)
3084
- ]
3085
- }
3086
- );
3087
- }
3088
- function renderArchiveRelated(groups, collections) {
3089
- const visible = groups.filter((group) => group.items.length > 0);
3090
- if (visible.length === 0) return null;
3091
- return /* @__PURE__ */ jsx9("aside", { className: "rt-archive-related", "aria-label": "\uC5F0\uAD00 \uC815\uBCF4", children: visible.map((group) => /* @__PURE__ */ jsxs9("section", { className: "rt-archive-related__group", children: [
3092
- /* @__PURE__ */ jsx9("h2", { className: "rt-archive-related__title", children: archiveGroupTitle(group) }),
3093
- /* @__PURE__ */ jsx9("ul", { className: "rt-archive-related__list", children: group.items.map((item) => /* @__PURE__ */ jsx9("li", { className: "rt-archive-related__item", children: /* @__PURE__ */ jsxs9(
3094
- "a",
3095
- {
3096
- className: "rt-archive-related__link",
3097
- href: archivePostHref(item.collectionKey, item.slug, collections),
3098
- children: [
3099
- /* @__PURE__ */ jsx9("span", { className: "rt-archive-related__item-title", children: item.title }),
3100
- item.excerpt ? /* @__PURE__ */ jsx9("span", { className: "rt-archive-related__item-excerpt", children: item.excerpt }) : null
3101
- ]
3102
- }
3103
- ) }, item.id)) })
3104
- ] }, group.collectionKey)) });
3105
- }
3106
- var FIELD_FACT_TYPES = /* @__PURE__ */ new Set([
3107
- "text",
3108
- "textarea",
3109
- "email",
3110
- "url",
3111
- "select",
3112
- "radio",
3113
- "button_group",
3114
- "number",
3115
- "range"
3116
- ]);
3117
- function fieldFactValue(raw, meta) {
3118
- if (raw === null || raw === void 0) return null;
3119
- if (typeof raw === "number") return raw.toLocaleString("ko-KR");
3120
- if (typeof raw !== "string" || raw.trim() === "") return null;
3121
- if (meta.choices) {
3122
- const choice = meta.choices.find((c) => c.value === raw);
3123
- return choice ? choice.label : raw;
3124
- }
3125
- return raw;
3126
- }
3127
- function renderFieldFacts(post) {
3128
- const fields = post.fields;
3129
- if (!fields) return null;
3130
- const rows = post.fieldsMeta.filter((meta) => FIELD_FACT_TYPES.has(meta.type)).flatMap((meta) => {
3131
- const value = fieldFactValue(fields[meta.name], meta);
3132
- return value === null ? [] : [{ meta, value }];
3133
- });
3134
- if (rows.length === 0) return null;
3135
- return /* @__PURE__ */ jsx9("aside", { className: "rt-archive-facts", "aria-label": "\uC0C1\uC138 \uC815\uBCF4", children: /* @__PURE__ */ jsx9("dl", { className: "rt-archive-facts__list", children: rows.map(({ meta, value }) => /* @__PURE__ */ jsxs9("div", { className: "rt-archive-facts__row", children: [
3136
- /* @__PURE__ */ jsx9("dt", { className: "rt-archive-facts__label", children: meta.label }),
3137
- /* @__PURE__ */ jsx9(
3138
- "dd",
3139
- {
3140
- className: meta.type === "textarea" ? "rt-archive-facts__value rt-archive-facts__value--multiline" : "rt-archive-facts__value",
3141
- children: value
3142
- }
3143
- )
3144
- ] }, meta.name)) }) });
3145
- }
3146
- function archiveGroupTitle(group) {
3147
- if (group.collectionKey === "conditions") {
3148
- return `\uC774 \uC99D\uC0C1\uACFC \uAD00\uB828\uB41C \uC9C8\uD658 ${group.items.length}`;
3149
- }
3150
- if (group.collectionKey === "treatments") {
3151
- return `\uAD00\uB828 \uCE58\uB8CC\uBC95 ${group.items.length}`;
3152
- }
3153
- if (group.collectionKey === "symptoms") {
3154
- return `\uAD00\uB828 \uC99D\uC0C1 ${group.items.length}`;
3155
- }
3156
- return `${group.label} ${group.items.length}`;
3157
- }
3158
- function archivePostHref(collectionKey, slug, collections) {
3159
- const collection = collections?.find((item) => item.key === collectionKey);
3160
- const basePath = collection?.basePath || defaultArchiveBasePath(collectionKey);
3161
- const trimmedBase = basePath.replace(/\/+$/, "");
3162
- if (collection?.detail === false) return trimmedBase;
3163
- return `${trimmedBase}/${slug}`;
3164
- }
3165
- function defaultArchiveBasePath(collectionKey) {
3166
- if (collectionKey === "symptoms") return "/symptoms";
3167
- if (collectionKey === "conditions") return "/conditions";
3168
- if (collectionKey === "treatments") return "/treatments";
3169
- return "/blog";
3170
- }
3171
- function renderPostCta(cta) {
3172
- if (!cta) return null;
3173
- const href = cta.buttonHref.trim();
3174
- const safe = href.startsWith("/") || href.startsWith("#") || /^(https?:|mailto:|tel:)/i.test(href);
3175
- if (!safe) return null;
3176
- const external = /^https?:/i.test(href);
3177
- return /* @__PURE__ */ jsxs9("aside", { "data-roottale-cms": "post-cta", className: "rt-cms-post-cta", children: [
3178
- cta.title ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-post-cta__title", children: cta.title }) : null,
3179
- cta.description ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-post-cta__desc", children: cta.description }) : null,
3180
- /* @__PURE__ */ jsx9(
3181
- "a",
3182
- {
3183
- className: "rt-cms-post-cta__btn",
3184
- href,
3185
- target: external ? "_blank" : void 0,
3186
- rel: external ? "noopener noreferrer" : void 0,
3187
- children: cta.buttonLabel
3188
- }
3189
- )
3190
- ] });
3191
- }
3192
- function renderRelated(posts, href, title) {
3193
- if (posts.length === 0) return null;
3194
- return /* @__PURE__ */ jsxs9("nav", { "data-roottale-cms": "related", className: "rt-cms-related", "aria-label": title, children: [
3195
- /* @__PURE__ */ jsx9("h2", { className: "rt-cms-related-title", children: title }),
3196
- /* @__PURE__ */ jsx9("ul", { className: "rt-cms-related-list", children: posts.map((p) => /* @__PURE__ */ jsx9("li", { className: "rt-cms-related-item", children: /* @__PURE__ */ jsxs9("a", { className: "rt-cms-related-link", href: href(p), children: [
3197
- p.featuredImageUrl ? /* @__PURE__ */ jsx9(
3198
- "img",
3199
- {
3200
- className: "rt-cms-related-thumb",
3201
- src: p.featuredImageUrl,
3202
- alt: "",
3203
- loading: "lazy"
3204
- }
3205
- ) : null,
3206
- /* @__PURE__ */ jsx9("span", { className: "rt-cms-related-post-title", children: p.title })
3207
- ] }) }, p.id)) })
3208
- ] });
3209
- }
3210
- function renderFaqJsonLd(doc) {
3211
- const ld = faqPageJsonLd(extractFaqEntries(doc));
3212
- if (!ld) return null;
3213
- return /* @__PURE__ */ jsx9(
3214
- "script",
3215
- {
3216
- type: "application/ld+json",
3217
- dangerouslySetInnerHTML: { __html: jsonLdJson(ld) }
3218
- }
3219
- );
3220
- }
3221
- function RootTaleFaqJsonLd({
3222
- doc
3223
- }) {
3224
- return renderFaqJsonLd(doc);
3225
- }
3226
- async function RootTalePage(props) {
3227
- const {
3228
- apiKey,
3229
- slugOrId,
3230
- baseUrl,
3231
- notFoundElement,
3232
- showTitle = true,
3233
- theme: themeProp,
3234
- locale
3235
- } = props;
3236
- const [post, themeStyle] = await Promise.all([
3237
- fetchPost({ apiKey, slugOrId, baseUrl, ...locale ? { locale } : {} }),
3238
- resolveThemeStyle({ themeProp, apiKey, baseUrl })
3239
- ]);
3240
- if (!post) {
3241
- return notFoundElement ?? /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "page-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx9("p", { className: "rt-cms-empty", children: t(locale, "pageNotFound") }) });
3242
- }
3243
- return /* @__PURE__ */ jsxs9(
3244
- "article",
3245
- {
3246
- "data-roottale-cms": "page",
3247
- className: "rt-cms-article",
3248
- style: themeStyle ?? void 0,
3249
- children: [
3250
- showTitle ? /* @__PURE__ */ jsx9("header", { className: "rt-cms-post-header", children: /* @__PURE__ */ jsx9("h1", { className: "rt-cms-title", children: post.title }) }) : null,
3251
- /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: /* @__PURE__ */ jsx9(
3252
- RenderTiptap,
3253
- {
3254
- doc: post.bodyJson,
3255
- fields: post.fields,
3256
- apiKey,
3257
- baseUrl
3258
- }
3259
- ) })
3260
- ]
3261
- }
3262
- );
3263
- }
3264
- function authorInitials(name) {
3265
- const trimmed = name.trim();
3266
- if (!trimmed) return "";
3267
- const parts = trimmed.split(/\s+/).filter(Boolean);
3268
- if (parts.length >= 2) {
3269
- return `${parts[0]?.[0] ?? ""}${parts[1]?.[0] ?? ""}`.toUpperCase();
3270
- }
3271
- return [...trimmed].slice(0, 2).join("").toUpperCase();
3272
- }
3273
- async function resolveThemeStyle(input) {
3274
- const { themeProp, apiKey, baseUrl } = input;
3275
- if (themeProp === null) return null;
3276
- if (themeProp !== void 0) return themeToCssVars2(themeProp);
3277
- try {
3278
- const fetched = await fetchTheme({ apiKey, baseUrl });
3279
- return themeToCssVars2(fetched);
3280
- } catch {
3281
- return null;
3282
- }
3283
- }
3284
- function defaultPostHref(post) {
3285
- return `/blog/${post.slug}`;
3286
- }
3287
- function makeCollectionPostHref(collections) {
3288
- return (post) => {
3289
- const path = resolvePostPath(post, collections);
3290
- if (path) return path;
3291
- const collection = resolvePostCollection(post, collections);
3292
- if (collection && collection.detail === false && collection.basePath) {
3293
- return collection.basePath;
3294
- }
3295
- return defaultPostHref(post);
3296
- };
3297
- }
3298
- function formatPublishedDate2(iso) {
3299
- const d = new Date(iso);
3300
- if (Number.isNaN(d.getTime())) return "";
3301
- return new Intl.DateTimeFormat("ko-KR", {
3302
- year: "numeric",
3303
- month: "2-digit",
3304
- day: "2-digit"
3305
- }).format(d);
3306
- }
3307
- function RenderTiptap({
3308
- doc,
3309
- fields,
3310
- apiKey,
3311
- baseUrl
3312
- }) {
3313
- const rawContent = doc.content;
3314
- const content = Array.isArray(rawContent) ? rawContent : [];
3315
- const hasToc = content.some((n) => n?.type === "tableOfContents");
3316
- const docRecord = doc;
3317
- const entries = hasToc ? extractToc(docRecord) : [];
3318
- const renderDoc = hasToc && entries.length > 0 ? attachHeadingIds(docRecord, entries) : docRecord;
3319
- const nodes = Array.isArray(renderDoc.content) ? renderDoc.content : content;
3320
- const cms = apiKey ? { apiKey, baseUrl } : null;
3321
- const context = {
3322
- fields: fields ?? null,
3323
- section: {
3324
- cms,
3325
- isDev: isDevEnv,
3326
- renderBlogList: (props) => /* @__PURE__ */ jsx9(RootTaleBlogList, { ...props })
3327
- }
3328
- };
3329
- return /* @__PURE__ */ jsx9(Fragment3, { children: nodes.map(
3330
- (node, i) => node?.type === "tableOfContents" ? /* @__PURE__ */ jsx9(
3331
- RootTaleTableOfContents,
3332
- {
3333
- entries,
3334
- title: typeof node.attrs?.title === "string" ? node.attrs.title : "\uBAA9\uCC28"
3335
- },
3336
- i
3337
- ) : renderNode(node, i, context)
3338
- ) });
3339
- }
3340
- function boundContentText(node, fields) {
3341
- return bindingTextValue(
3342
- boundFieldValue(node.attrs?.bindings, "content", fields)
3343
- );
3344
- }
3345
- function isSafeHref(value) {
3346
- if (typeof value !== "string") return false;
3347
- const v = value.trim();
3348
- if (v.length === 0) return false;
3349
- if (v.startsWith("/") || v.startsWith("#") || v.startsWith("?")) return true;
3350
- return /^(https?:|mailto:|tel:)/i.test(v);
3351
- }
3352
- function alignStyle(node) {
3353
- const align = node.attrs?.textAlign;
3354
- if (typeof align !== "string" || align === "" || align === "left") return void 0;
3355
- if (align === "center" || align === "right" || align === "justify") {
3356
- return { textAlign: align };
3357
- }
3358
- return void 0;
3359
- }
3360
- function tableCellSpans(node) {
3361
- const colspan = Number(node.attrs?.colspan);
3362
- const rowspan = Number(node.attrs?.rowspan);
3363
- return {
3364
- colSpan: Number.isFinite(colspan) && colspan > 1 ? colspan : void 0,
3365
- rowSpan: Number.isFinite(rowspan) && rowspan > 1 ? rowspan : void 0
3366
- };
3367
- }
3368
- function isDevEnv() {
3369
- const env = globalThis.process?.env ?? {};
3370
- return env.NODE_ENV !== "production";
3371
- }
3372
- function renderNode(node, key, context = {
3373
- fields: null,
3374
- section: {
3375
- cms: null,
3376
- isDev: isDevEnv,
3377
- renderBlogList: (props) => /* @__PURE__ */ jsx9(RootTaleBlogList, { ...props })
3378
- }
3379
- }) {
3380
- if (!node || typeof node !== "object") return null;
3381
- const children = Array.isArray(node.content) ? node.content.map((child, i) => renderNode(child, i, context)) : void 0;
3382
- switch (node.type) {
3383
- case "paragraph": {
3384
- const bound = boundContentText(node, context.fields);
3385
- return /* @__PURE__ */ jsx9("p", { style: alignStyle(node), children: bound ?? children }, key);
3386
- }
3387
- case "heading": {
3388
- const rawLevel = Number(node.attrs?.level ?? 2);
3389
- const level = Math.min(Math.max(Number.isFinite(rawLevel) ? rawLevel : 2, 2), 4);
3390
- const idAttr = node.attrs?.id;
3391
- const id = typeof idAttr === "string" && idAttr.length > 0 ? idAttr : void 0;
3392
- const style = alignStyle(node);
3393
- const inner = boundContentText(node, context.fields) ?? children;
3394
- if (level === 2) return /* @__PURE__ */ jsx9("h2", { id, style, children: inner }, key);
3395
- if (level === 3) return /* @__PURE__ */ jsx9("h3", { id, style, children: inner }, key);
3396
- return /* @__PURE__ */ jsx9("h4", { id, style, children: inner }, key);
3397
- }
3398
- case "bulletList":
3399
- return /* @__PURE__ */ jsx9("ul", { children }, key);
3400
- case "orderedList":
3401
- return /* @__PURE__ */ jsx9("ol", { children }, key);
3402
- case "listItem":
3403
- return /* @__PURE__ */ jsx9("li", { children }, key);
3404
- case "blockquote":
3405
- return /* @__PURE__ */ jsx9("blockquote", { children }, key);
3406
- case "codeBlock":
3407
- return /* @__PURE__ */ jsx9("pre", { children: /* @__PURE__ */ jsx9("code", { children }) }, key);
3408
- case "horizontalRule":
3409
- return /* @__PURE__ */ jsx9("hr", {}, key);
3410
- case "hardBreak":
3411
- return /* @__PURE__ */ jsx9("br", {}, key);
3412
- case "image": {
3413
- const boundSrc = bindingSrcValue(
3414
- boundFieldValue(node.attrs?.bindings, "src", context.fields)
3415
- );
3416
- const src = boundSrc ?? node.attrs?.src;
3417
- if (typeof src !== "string" || src.length === 0) return null;
3418
- const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
3419
- const title = typeof node.attrs?.title === "string" ? node.attrs.title : void 0;
3420
- const width = typeof node.attrs?.width === "number" && node.attrs.width > 0 ? node.attrs.width : void 0;
3421
- const height = typeof node.attrs?.height === "number" && node.attrs.height > 0 ? node.attrs.height : void 0;
3422
- return /* @__PURE__ */ jsx9(
3423
- "img",
3424
- {
3425
- src,
3426
- alt,
3427
- title,
3428
- width,
3429
- height,
3430
- loading: "lazy"
3431
- },
3432
- key
3433
- );
3434
- }
3435
- case "columns":
3436
- return /* @__PURE__ */ jsx9("div", { className: "rt-columns", children }, key);
3437
- case "column":
3438
- return /* @__PURE__ */ jsx9("div", { className: "rt-column", children }, key);
3439
- case "table":
3440
- return /* @__PURE__ */ jsx9("div", { className: "rt-table-wrap", children: /* @__PURE__ */ jsx9("table", { className: "rt-table", children: /* @__PURE__ */ jsx9("tbody", { children }) }) }, key);
3441
- case "tableRow":
3442
- return /* @__PURE__ */ jsx9("tr", { children }, key);
3443
- case "tableHeader": {
3444
- const { colSpan, rowSpan } = tableCellSpans(node);
3445
- return /* @__PURE__ */ jsx9("th", { colSpan, rowSpan, children }, key);
3446
- }
3447
- case "tableCell": {
3448
- const { colSpan, rowSpan } = tableCellSpans(node);
3449
- return /* @__PURE__ */ jsx9("td", { colSpan, rowSpan, children }, key);
3450
- }
3451
- case "embed":
3452
- return renderEmbed(node, key);
3453
- case "faq":
3454
- return /* @__PURE__ */ jsx9("div", { "data-roottale-cms": "faq", className: "rt-cms-faq", children }, key);
3455
- case "faqItem":
3456
- return /* @__PURE__ */ jsx9("details", { className: "rt-cms-faq-item", children }, key);
3457
- case "faqQuestion":
3458
- return /* @__PURE__ */ jsx9("summary", { className: "rt-cms-faq-q", children }, key);
3459
- case "faqAnswer":
3460
- return /* @__PURE__ */ jsx9("div", { className: "rt-cms-faq-a", children }, key);
3461
- case "tableOfContents":
3462
- return null;
3463
- case "section":
3464
- return renderSectionNode(node, key, context.section);
3465
- case "text":
3466
- return renderText(node, key);
3467
- default:
3468
- return children ? /* @__PURE__ */ jsx9("span", { children }, key) : null;
3469
- }
3470
- }
3471
- function renderEmbed(node, key) {
3472
- const src = node.attrs?.src;
3473
- if (isAllowedEmbedSrc2(src)) {
3474
- const provider = typeof node.attrs?.provider === "string" ? node.attrs.provider : void 0;
3475
- return /* @__PURE__ */ jsx9(
3476
- "div",
3477
- {
3478
- className: "rt-embed",
3479
- "data-provider": provider,
3480
- style: { aspectRatio: "16 / 9" },
3481
- children: /* @__PURE__ */ jsx9(
3482
- "iframe",
3483
- {
3484
- src,
3485
- title: "\uC784\uBCA0\uB4DC\uB41C \uC601\uC0C1",
3486
- loading: "lazy",
3487
- allowFullScreen: true,
3488
- referrerPolicy: "strict-origin-when-cross-origin",
3489
- allow: "accelerometer; clipboard-write; encrypted-media; picture-in-picture; web-share",
3490
- style: { width: "100%", height: "100%", border: 0 }
3491
- }
3492
- )
3493
- },
3494
- key
3495
- );
3496
- }
3497
- const orig = node.attrs?.originalUrl;
3498
- if (isSafeHref(orig)) {
3499
- return /* @__PURE__ */ jsx9("p", { className: "rt-embed-fallback", children: /* @__PURE__ */ jsx9("a", { href: orig, rel: "noopener noreferrer", target: "_blank", children: orig }) }, key);
3500
- }
3501
- return null;
3502
- }
3503
- function renderText(node, key) {
3504
- const text = node.text ?? "";
3505
- const marks = node.marks ?? [];
3506
- if (marks.length === 0) return text;
3507
- let element = text;
3508
- for (const mark of marks) {
3509
- element = applyMark(mark, element);
3510
- }
3511
- return /* @__PURE__ */ jsx9("span", { children: element }, key);
3512
- }
3513
- function applyMark(mark, child) {
3514
- switch (mark.type) {
3515
- case "bold":
3516
- return /* @__PURE__ */ jsx9("strong", { children: child });
3517
- case "italic":
3518
- return /* @__PURE__ */ jsx9("em", { children: child });
3519
- case "underline":
3520
- return /* @__PURE__ */ jsx9("u", { children: child });
3521
- case "strike":
3522
- return /* @__PURE__ */ jsx9("s", { children: child });
3523
- case "code":
3524
- return /* @__PURE__ */ jsx9("code", { children: child });
3525
- case "highlight": {
3526
- const color = mark.attrs?.color;
3527
- const style = typeof color === "string" && color.length > 0 ? { background: color } : void 0;
3528
- return /* @__PURE__ */ jsx9("mark", { style, children: child });
3529
- }
3530
- case "textStyle": {
3531
- const color = mark.attrs?.color;
3532
- const style = typeof color === "string" && color.length > 0 ? { color } : void 0;
3533
- if (!style) return /* @__PURE__ */ jsx9(Fragment3, { children: child });
3534
- return /* @__PURE__ */ jsx9("span", { style, children: child });
3535
- }
3536
- case "link": {
3537
- const raw = mark.attrs?.href;
3538
- const href = isSafeHref(raw) ? raw : "#";
3539
- return /* @__PURE__ */ jsx9("a", { href, rel: "noopener noreferrer", target: "_blank", children: child });
3540
- }
3541
- default:
3542
- return /* @__PURE__ */ jsx9("span", { children: child });
3543
- }
3544
- }
3545
-
3546
- export {
3547
- RootTaleLeadForm,
3548
- sectionThemeStyle,
3549
- TemplateHero,
3550
- TemplateRichText,
3551
- TemplateServices,
3552
- TemplateFooter,
3553
- TemplateFaq,
3554
- TemplateCtaBanner,
3555
- TemplateTrust,
3556
- TemplateTestimonials,
3557
- TemplateTeam,
3558
- TemplateContact,
3559
- TemplateHours,
3560
- TemplatePricing,
3561
- TemplateProcess,
3562
- TemplateBeforeAfter,
3563
- TemplateExpertise,
3564
- TemplateSpaces,
3565
- TemplateConcerns,
3566
- RootTalePostCard,
3567
- RootTaleTableOfContents,
3568
- themeToCssVars2 as themeToCssVars,
3569
- headingToId,
3570
- extractToc,
3571
- attachHeadingIds,
3572
- RootTaleFloatingCta,
3573
- renderBlock,
3574
- renderBlocks,
3575
- renderBoundBlocks,
3576
- RootTaleThemeProvider,
3577
- RootTaleBlogList,
3578
- RootTaleBlogCategories,
3579
- RootTaleBlogPost,
3580
- RootTaleFaqJsonLd,
3581
- RootTalePage,
3582
- RenderTiptap
3583
- };
3584
- //# sourceMappingURL=chunk-ZHXYCABN.js.map