@roottale/cms-renderer-next 0.48.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 (36) hide show
  1. package/CHANGELOG.md +56 -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/concerns-client.d.ts +1 -1
  12. package/dist/expertise-client.d.ts +1 -1
  13. package/dist/expertise-client.js +1 -1
  14. package/dist/hero-carousel-client.js +1 -1
  15. package/dist/routes.d.ts +334 -20
  16. package/dist/routes.js +249 -68
  17. package/dist/routes.js.map +1 -1
  18. package/dist/seo-asset-hosts.d.ts +36 -0
  19. package/dist/seo-asset-hosts.js +391 -0
  20. package/dist/seo-asset-hosts.js.map +1 -0
  21. package/dist/server.d.ts +23 -241
  22. package/dist/server.js +6 -6
  23. package/dist/{site-config-Dicv57oY.d.ts → site-config-DNpvlqA5.d.ts} +201 -201
  24. package/dist/spaces-client.d.ts +1 -1
  25. package/dist/team-expanding-stage-client.d.ts +1 -1
  26. package/dist/team-expanding-stage-client.js +1 -1
  27. package/dist/templates.d.ts +64 -25
  28. package/dist/templates.js +13 -6
  29. package/dist/templates.js.map +1 -1
  30. package/package.json +7 -2
  31. package/dist/chunk-6MFHA555.js.map +0 -1
  32. package/dist/chunk-FZK3EXYZ.js +0 -103
  33. package/dist/chunk-FZK3EXYZ.js.map +0 -1
  34. package/dist/chunk-LIBSVC4M.js +0 -3642
  35. package/dist/chunk-LIBSVC4M.js.map +0 -1
  36. package/dist/chunk-OMXEU4LS.js.map +0 -1
@@ -0,0 +1,106 @@
1
+ "use client";
2
+
3
+ // src/templates/team-expanding-stage-client.tsx
4
+ import { useState } from "react";
5
+ import { useReducedMotion } from "motion/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function memberInitials(name) {
8
+ const parts = name.trim().split(/\s+/).filter(Boolean);
9
+ if (parts.length === 0) return "?";
10
+ const initials = parts.slice(0, 2).map((part) => part.at(0) ?? "").join("");
11
+ return initials || "?";
12
+ }
13
+ function TeamExpandingStageClient({
14
+ members
15
+ }) {
16
+ const [activeIndex, setActiveIndex] = useState(0);
17
+ const reducedMotion = useReducedMotion();
18
+ if (members.length === 0) return null;
19
+ const activate = (index) => setActiveIndex(index);
20
+ const onItemKeyDown = (event, index) => {
21
+ if ((event.key === "Enter" || event.key === " ") && index !== activeIndex) {
22
+ event.preventDefault();
23
+ activate(index);
24
+ }
25
+ };
26
+ return /* @__PURE__ */ jsx(
27
+ "ul",
28
+ {
29
+ className: "rt-tpl-team-expanding-stage",
30
+ "data-reduced-motion": reducedMotion ? "true" : "false",
31
+ children: members.map((member, index) => {
32
+ const active = index === activeIndex;
33
+ return (
34
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- hover 는 마우스 보조 경로다(키보드는 아래 onFocus 가 받는다).
35
+ /* @__PURE__ */ jsxs(
36
+ "li",
37
+ {
38
+ className: "rt-tpl-team-expanding-item",
39
+ "data-active": active ? "true" : "false",
40
+ tabIndex: 0,
41
+ onMouseEnter: () => activate(index),
42
+ onFocus: () => activate(index),
43
+ onClick: (event) => {
44
+ if (!active) event.preventDefault();
45
+ activate(index);
46
+ },
47
+ onKeyDown: (event) => onItemKeyDown(event, index),
48
+ children: [
49
+ /* @__PURE__ */ jsx("span", { className: "rt-tpl-team-expanding-index", "aria-hidden": "true", children: String(index + 1).padStart(2, "0") }),
50
+ member.photoSrc ? /* @__PURE__ */ jsx(
51
+ "img",
52
+ {
53
+ className: "rt-tpl-team-expanding-photo",
54
+ src: member.photoSrc,
55
+ alt: member.photoAlt ?? "",
56
+ loading: "lazy"
57
+ }
58
+ ) : /* @__PURE__ */ jsx(
59
+ "div",
60
+ {
61
+ className: "rt-tpl-team-expanding-photo rt-tpl-team-expanding-photo--initials",
62
+ "aria-hidden": "true",
63
+ children: memberInitials(member.name)
64
+ }
65
+ ),
66
+ /* @__PURE__ */ jsxs("div", { className: "rt-tpl-team-expanding-name", "aria-hidden": "true", children: [
67
+ /* @__PURE__ */ jsx("b", { children: member.name }),
68
+ /* @__PURE__ */ jsx("span", { children: member.title })
69
+ ] }),
70
+ /* @__PURE__ */ jsxs("div", { className: "rt-tpl-team-expanding-detail", children: [
71
+ /* @__PURE__ */ jsx("p", { className: "rt-tpl-team-expanding-role", children: member.title }),
72
+ /* @__PURE__ */ jsx("h3", { className: "rt-tpl-team-name", children: member.name }),
73
+ member.specialties ? /* @__PURE__ */ jsx(
74
+ "ul",
75
+ {
76
+ className: "rt-tpl-team-specialties",
77
+ "aria-label": `${member.name} \uC804\uBB38 \uBD84\uC57C`,
78
+ children: member.specialties.map((specialty) => /* @__PURE__ */ jsx("li", { children: specialty }, specialty))
79
+ }
80
+ ) : null,
81
+ member.credentials ? /* @__PURE__ */ jsx("ul", { className: "rt-tpl-team-credentials", children: member.credentials.map((credential) => /* @__PURE__ */ jsx("li", { children: credential }, credential)) }) : null,
82
+ member.bio ? /* @__PURE__ */ jsx("p", { className: "rt-tpl-team-bio", children: member.bio }) : null,
83
+ member.href ? /* @__PURE__ */ jsx(
84
+ "a",
85
+ {
86
+ className: "rt-tpl-team-cta",
87
+ href: member.href,
88
+ tabIndex: active ? 0 : -1,
89
+ children: member.ctaLabel ?? "\uD504\uB85C\uD544 \uBCF4\uAE30"
90
+ }
91
+ ) : null
92
+ ] })
93
+ ]
94
+ },
95
+ member.name
96
+ )
97
+ );
98
+ })
99
+ }
100
+ );
101
+ }
102
+
103
+ export {
104
+ TeamExpandingStageClient
105
+ };
106
+ //# sourceMappingURL=chunk-4RQ3SEDL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates/team-expanding-stage-client.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type KeyboardEvent, type ReactElement } from \"react\";\nimport { useReducedMotion } from \"motion/react\";\n\nimport type { TeamSection } from \"./site-config.js\";\n\ntype TeamMember = TeamSection[\"props\"][\"members\"][number];\n\nfunction memberInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n const initials = parts\n .slice(0, 2)\n .map((part) => part.at(0) ?? \"\")\n .join(\"\");\n return initials || \"?\";\n}\n\n/**\n * `team` `expanding-stage` variant 의 실모션 client island (D3 크래프트).\n * chambarun `.staff-stage`/`.staff-panel` 스펙(mockup/assets/site.js·site.css)\n * 이식 — hover/focus/tap 한 인물이 확장되며 자격 정보가 드러난다.\n *\n * 현재 `sections.tsx` `TemplateTeamExpandingStage` 는 이 컴포넌트를 아직\n * import 하지 않는다(정적 격자만 렌더) — `sections.tsx` 는 이 작업 소유 밖이라\n * 와이어링은 별도 보고 대상(README/보고 참조). 이 파일은 그 자리에 그대로\n * 끼워 넣을 수 있도록 `members` 하나만 받는 독립 컴포넌트로 설계했다.\n *\n * 접근성(계약, DESIGN.md \"Medical Staff Cards\" 원칙 확장):\n * - 각 패널은 `tabIndex=0` 컨테이너(hover/focus/click 시 확장) — chambarun\n * 원본과 동일하게 명시적 role 은 두지 않는다(내부에 실제 CTA 링크가 있어\n * `role=\"button\"` 을 얹으면 중첩 인터랙티브 요소가 된다).\n * - 자격정보·경력·CTA 는 접힌 상태에서도 DOM 에 그대로 존재한다(opacity 로만\n * 가림) — 스크린리더는 hover 없이 전체 내용을 읽을 수 있다(\"decorative hover\n * layers do not replace content\").\n * - CTA 링크는 비활성 패널일 때 `tabIndex=-1` — 보이지 않는 링크가 키보드\n * 포커스를 가로채지 않는다(chambarun 원본에는 없던, React 상태로만 가능한\n * 개선).\n * - `prefers-reduced-motion: reduce` → 전환 자체가 CSS 에서 제거된다\n * (templates.css, `@media (prefers-reduced-motion: no-preference)` 안에만\n * transition 선언 — ADR-0073 K3 C1 과 동일 관행). `data-reduced-motion`\n * 속성은 시각 회귀 테스트/QA 스크린샷 식별용.\n */\nexport function TeamExpandingStageClient({\n members,\n}: {\n readonly members: readonly TeamMember[];\n}): ReactElement | null {\n const [activeIndex, setActiveIndex] = useState(0);\n const reducedMotion = useReducedMotion();\n\n if (members.length === 0) return null;\n\n const activate = (index: number) => setActiveIndex(index);\n\n const onItemKeyDown = (event: KeyboardEvent<HTMLLIElement>, index: number) => {\n if ((event.key === \"Enter\" || event.key === \" \") && index !== activeIndex) {\n event.preventDefault();\n activate(index);\n }\n };\n\n return (\n <ul\n className=\"rt-tpl-team-expanding-stage\"\n data-reduced-motion={reducedMotion ? \"true\" : \"false\"}\n >\n {members.map((member, index) => {\n const active = index === activeIndex;\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- hover 는 마우스 보조 경로다(키보드는 아래 onFocus 가 받는다).\n <li\n key={member.name}\n className=\"rt-tpl-team-expanding-item\"\n data-active={active ? \"true\" : \"false\"}\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- 키보드 사용자가 팀원 카드를 넘겨보려면 초점을 받아야 한다(WCAG 2.1.1).\n tabIndex={0}\n onMouseEnter={() => activate(index)}\n onFocus={() => activate(index)}\n onClick={(event) => {\n // 첫 탭(hover/focus 없이 바로 터치)은 확장만 — CTA 로 즉시\n // 이동하지 않는다. 이미 활성(=두 번째 탭)이면 기본 클릭 동작\n // (링크 없는 li 자체는 아무 것도 안 함, 내부 CTA 는 그대로 이동).\n if (!active) event.preventDefault();\n activate(index);\n }}\n onKeyDown={(event) => onItemKeyDown(event, index)}\n >\n <span className=\"rt-tpl-team-expanding-index\" aria-hidden=\"true\">\n {String(index + 1).padStart(2, \"0\")}\n </span>\n {member.photoSrc ? (\n <img\n className=\"rt-tpl-team-expanding-photo\"\n src={member.photoSrc}\n alt={member.photoAlt ?? \"\"}\n loading=\"lazy\"\n />\n ) : (\n <div\n className=\"rt-tpl-team-expanding-photo rt-tpl-team-expanding-photo--initials\"\n aria-hidden=\"true\"\n >\n {memberInitials(member.name)}\n </div>\n )}\n <div className=\"rt-tpl-team-expanding-name\" aria-hidden=\"true\">\n <b>{member.name}</b>\n <span>{member.title}</span>\n </div>\n <div className=\"rt-tpl-team-expanding-detail\">\n <p className=\"rt-tpl-team-expanding-role\">{member.title}</p>\n <h3 className=\"rt-tpl-team-name\">{member.name}</h3>\n {member.specialties ? (\n <ul\n className=\"rt-tpl-team-specialties\"\n aria-label={`${member.name} 전문 분야`}\n >\n {member.specialties.map((specialty) => (\n <li key={specialty}>{specialty}</li>\n ))}\n </ul>\n ) : null}\n {member.credentials ? (\n <ul className=\"rt-tpl-team-credentials\">\n {member.credentials.map((credential) => (\n <li key={credential}>{credential}</li>\n ))}\n </ul>\n ) : null}\n {member.bio ? <p className=\"rt-tpl-team-bio\">{member.bio}</p> : null}\n {member.href ? (\n <a\n className=\"rt-tpl-team-cta\"\n href={member.href}\n tabIndex={active ? 0 : -1}\n >\n {member.ctaLabel ?? \"프로필 보기\"}\n </a>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":"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","names":[]}
@@ -236,6 +236,7 @@ function HeroCarouselViewport({
236
236
  }
237
237
  emblaApi?.scrollTo(index, reducedMotion);
238
238
  },
239
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- 위와 같은 이유(안정화된 메서드만 의존).
239
240
  [isCurtain, curtain.goTo, emblaApi, reducedMotion]
240
241
  );
241
242
  const onKeyDown = useCallback3(
@@ -262,153 +263,156 @@ function HeroCarouselViewport({
262
263
  onSelect: scrollTo,
263
264
  orientation: "horizontal"
264
265
  });
265
- return /* @__PURE__ */ jsxs2(
266
- "div",
267
- {
268
- ref: rootRef,
269
- className: "rt-tpl-hero-carousel",
270
- role: "region",
271
- "aria-roledescription": "carousel",
272
- "aria-label": "\uBC30\uACBD \uC774\uBBF8\uC9C0 \uCE90\uB7EC\uC140",
273
- tabIndex: 0,
274
- onKeyDown,
275
- "data-reduced-motion": reducedMotion ? "true" : "false",
276
- "data-scrim": scrim ?? "soft",
277
- "data-transition": transition,
278
- onMouseEnter: autoplayGate.rootHandlers.onMouseEnter,
279
- onMouseLeave: autoplayGate.rootHandlers.onMouseLeave,
280
- onFocus: autoplayGate.rootHandlers.onFocus,
281
- onBlur: autoplayGate.rootHandlers.onBlur,
282
- children: [
283
- isCurtain ? /* @__PURE__ */ jsx3(
284
- CurtainTrack,
285
- {
286
- slides,
287
- selectedIndex: curtain.selectedIndex,
288
- leavingIndex: curtain.leavingIndex
289
- }
290
- ) : /* @__PURE__ */ jsx3(
291
- SlideTrack,
292
- {
293
- viewportRef: emblaRef,
294
- slides,
295
- selectedIndex: emblaSelectedIndex
296
- }
297
- ),
298
- /* @__PURE__ */ jsx3(
299
- "button",
300
- {
301
- type: "button",
302
- className: "rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--prev",
303
- "aria-label": "\uC774\uC804 \uC2AC\uB77C\uC774\uB4DC",
304
- onClick: scrollPrev,
305
- children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx3("path", { d: "M15 5 8 12l7 7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
306
- }
307
- ),
308
- /* @__PURE__ */ jsx3(
309
- "button",
310
- {
311
- type: "button",
312
- className: "rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--next",
313
- "aria-label": "\uB2E4\uC74C \uC2AC\uB77C\uC774\uB4DC",
314
- onClick: scrollNext,
315
- children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx3("path", { d: "m9 5 7 7-7 7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
316
- }
317
- ),
318
- hasSlidePanel && selectedSlide ? /* @__PURE__ */ jsxs2(
319
- "div",
320
- {
321
- className: "rt-tpl-hero-carousel-slide-panel",
322
- children: [
323
- selectedSlide.eyebrow ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-eyebrow", children: selectedSlide.eyebrow }) : null,
324
- selectedSlide.title ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-title", children: selectedSlide.title }) : null,
325
- selectedSlide.subtitle ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-subtitle", children: selectedSlide.subtitle }) : null,
326
- selectedSlide.description ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-desc", children: selectedSlide.description }) : null,
327
- selectedSlide.metrics && selectedSlide.metrics.length > 0 ? /* @__PURE__ */ jsx3("dl", { className: "rt-tpl-hero-carousel-slide-metrics", children: selectedSlide.metrics.map((metric) => /* @__PURE__ */ jsxs2("div", { children: [
328
- /* @__PURE__ */ jsx3("dt", { children: metric.label }),
329
- /* @__PURE__ */ jsxs2("dd", { children: [
330
- metric.value,
331
- metric.unit ? /* @__PURE__ */ jsx3("span", { children: metric.unit }) : null
332
- ] }),
333
- metric.source ? /* @__PURE__ */ jsx3("dd", { children: metric.source }) : null
334
- ] }, `${metric.value}-${metric.label}`)) }) : null,
335
- selectedSlide.primaryCta || selectedSlide.secondaryCta ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-slide-actions", children: [
336
- selectedSlide.primaryCta ? /* @__PURE__ */ jsx3(
337
- "a",
338
- {
339
- className: "rt-tpl-btn rt-tpl-btn--primary",
340
- href: selectedSlide.primaryCta.href,
341
- "data-track": "cta_hero",
342
- children: selectedSlide.primaryCta.label
343
- }
344
- ) : null,
345
- selectedSlide.secondaryCta ? /* @__PURE__ */ jsx3(
346
- "a",
347
- {
348
- className: "rt-tpl-btn rt-tpl-btn--secondary",
349
- href: selectedSlide.secondaryCta.href,
350
- "data-track": "cta_hero",
351
- children: selectedSlide.secondaryCta.label
352
- }
353
- ) : null
354
- ] }) : null
355
- ]
356
- },
357
- isCurtain ? `panel-${selectedIndex}` : "panel"
358
- ) : null,
359
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-controls", children: [
266
+ return (
267
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- WAI-ARIA 캐러셀 패턴. region 이 좌우 키·포인터를 받고 실제 이동은 안쪽 컨트롤이 담당한다.
268
+ /* @__PURE__ */ jsxs2(
269
+ "div",
270
+ {
271
+ ref: rootRef,
272
+ className: "rt-tpl-hero-carousel",
273
+ role: "region",
274
+ "aria-roledescription": "carousel",
275
+ "aria-label": "\uBC30\uACBD \uC774\uBBF8\uC9C0 \uCE90\uB7EC\uC140",
276
+ tabIndex: 0,
277
+ onKeyDown,
278
+ "data-reduced-motion": reducedMotion ? "true" : "false",
279
+ "data-scrim": scrim ?? "soft",
280
+ "data-transition": transition,
281
+ onMouseEnter: autoplayGate.rootHandlers.onMouseEnter,
282
+ onMouseLeave: autoplayGate.rootHandlers.onMouseLeave,
283
+ onFocus: autoplayGate.rootHandlers.onFocus,
284
+ onBlur: autoplayGate.rootHandlers.onBlur,
285
+ children: [
286
+ isCurtain ? /* @__PURE__ */ jsx3(
287
+ CurtainTrack,
288
+ {
289
+ slides,
290
+ selectedIndex: curtain.selectedIndex,
291
+ leavingIndex: curtain.leavingIndex
292
+ }
293
+ ) : /* @__PURE__ */ jsx3(
294
+ SlideTrack,
295
+ {
296
+ viewportRef: emblaRef,
297
+ slides,
298
+ selectedIndex: emblaSelectedIndex
299
+ }
300
+ ),
360
301
  /* @__PURE__ */ jsx3(
361
- "div",
302
+ "button",
362
303
  {
363
- className: hasTabLabels ? "rt-tpl-hero-carousel-dots rt-tpl-hero-carousel-dots--tabs" : "rt-tpl-hero-carousel-dots",
364
- role: "tablist",
365
- "aria-label": "\uC2AC\uB77C\uC774\uB4DC \uC120\uD0DD",
366
- onKeyDown: onTablistKeyDown,
367
- children: slides.map((_slide, index) => /* @__PURE__ */ jsx3(
368
- "button",
369
- {
370
- ...getTabProps(index),
371
- type: "button",
372
- role: "tab",
373
- className: "rt-tpl-hero-carousel-dot",
374
- "aria-selected": index === selectedIndex,
375
- "aria-label": `${index + 1}\uBC88 \uC2AC\uB77C\uC774\uB4DC\uB85C \uC774\uB3D9`,
376
- "data-active": index === selectedIndex ? "true" : "false",
377
- onClick: () => scrollTo(index),
378
- children: hasTabLabels ? /* @__PURE__ */ jsxs2(Fragment, { children: [
379
- /* @__PURE__ */ jsx3("span", { children: _slide.tabLabel ?? `${index + 1}` }),
380
- _slide.tabMeta ? /* @__PURE__ */ jsx3("small", { children: _slide.tabMeta }) : null
381
- ] }) : null
382
- },
383
- index
384
- ))
304
+ type: "button",
305
+ className: "rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--prev",
306
+ "aria-label": "\uC774\uC804 \uC2AC\uB77C\uC774\uB4DC",
307
+ onClick: scrollPrev,
308
+ children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx3("path", { d: "M15 5 8 12l7 7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
385
309
  }
386
310
  ),
387
- /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-controls-right", children: [
388
- showPlayPause ? /* @__PURE__ */ jsx3(
389
- "button",
311
+ /* @__PURE__ */ jsx3(
312
+ "button",
313
+ {
314
+ type: "button",
315
+ className: "rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--next",
316
+ "aria-label": "\uB2E4\uC74C \uC2AC\uB77C\uC774\uB4DC",
317
+ onClick: scrollNext,
318
+ children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx3("path", { d: "m9 5 7 7-7 7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
319
+ }
320
+ ),
321
+ hasSlidePanel && selectedSlide ? /* @__PURE__ */ jsxs2(
322
+ "div",
323
+ {
324
+ className: "rt-tpl-hero-carousel-slide-panel",
325
+ children: [
326
+ selectedSlide.eyebrow ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-eyebrow", children: selectedSlide.eyebrow }) : null,
327
+ selectedSlide.title ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-title", children: selectedSlide.title }) : null,
328
+ selectedSlide.subtitle ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-subtitle", children: selectedSlide.subtitle }) : null,
329
+ selectedSlide.description ? /* @__PURE__ */ jsx3("p", { className: "rt-tpl-hero-carousel-slide-desc", children: selectedSlide.description }) : null,
330
+ selectedSlide.metrics && selectedSlide.metrics.length > 0 ? /* @__PURE__ */ jsx3("dl", { className: "rt-tpl-hero-carousel-slide-metrics", children: selectedSlide.metrics.map((metric) => /* @__PURE__ */ jsxs2("div", { children: [
331
+ /* @__PURE__ */ jsx3("dt", { children: metric.label }),
332
+ /* @__PURE__ */ jsxs2("dd", { children: [
333
+ metric.value,
334
+ metric.unit ? /* @__PURE__ */ jsx3("span", { children: metric.unit }) : null
335
+ ] }),
336
+ metric.source ? /* @__PURE__ */ jsx3("dd", { children: metric.source }) : null
337
+ ] }, `${metric.value}-${metric.label}`)) }) : null,
338
+ selectedSlide.primaryCta || selectedSlide.secondaryCta ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-slide-actions", children: [
339
+ selectedSlide.primaryCta ? /* @__PURE__ */ jsx3(
340
+ "a",
341
+ {
342
+ className: "rt-tpl-btn rt-tpl-btn--primary",
343
+ href: selectedSlide.primaryCta.href,
344
+ "data-track": "cta_hero",
345
+ children: selectedSlide.primaryCta.label
346
+ }
347
+ ) : null,
348
+ selectedSlide.secondaryCta ? /* @__PURE__ */ jsx3(
349
+ "a",
350
+ {
351
+ className: "rt-tpl-btn rt-tpl-btn--secondary",
352
+ href: selectedSlide.secondaryCta.href,
353
+ "data-track": "cta_hero",
354
+ children: selectedSlide.secondaryCta.label
355
+ }
356
+ ) : null
357
+ ] }) : null
358
+ ]
359
+ },
360
+ isCurtain ? `panel-${selectedIndex}` : "panel"
361
+ ) : null,
362
+ /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-controls", children: [
363
+ /* @__PURE__ */ jsx3(
364
+ "div",
390
365
  {
391
- type: "button",
392
- className: "rt-tpl-hero-carousel-playpause",
393
- "aria-pressed": autoplayGate.userPaused,
394
- "aria-label": autoplayGate.userPaused ? "\uC790\uB3D9 \uC804\uD658 \uC7AC\uC0DD" : "\uC790\uB3D9 \uC804\uD658 \uC77C\uC2DC\uC815\uC9C0",
395
- onClick: autoplayGate.togglePaused,
396
- children: autoplayGate.userPaused ? /* @__PURE__ */ jsx3(PlayIcon, {}) : /* @__PURE__ */ jsx3(PauseIcon, {})
366
+ className: hasTabLabels ? "rt-tpl-hero-carousel-dots rt-tpl-hero-carousel-dots--tabs" : "rt-tpl-hero-carousel-dots",
367
+ role: "tablist",
368
+ "aria-label": "\uC2AC\uB77C\uC774\uB4DC \uC120\uD0DD",
369
+ onKeyDown: onTablistKeyDown,
370
+ children: slides.map((_slide, index) => /* @__PURE__ */ jsx3(
371
+ "button",
372
+ {
373
+ ...getTabProps(index),
374
+ type: "button",
375
+ role: "tab",
376
+ className: "rt-tpl-hero-carousel-dot",
377
+ "aria-selected": index === selectedIndex,
378
+ "aria-label": `${index + 1}\uBC88 \uC2AC\uB77C\uC774\uB4DC\uB85C \uC774\uB3D9`,
379
+ "data-active": index === selectedIndex ? "true" : "false",
380
+ onClick: () => scrollTo(index),
381
+ children: hasTabLabels ? /* @__PURE__ */ jsxs2(Fragment, { children: [
382
+ /* @__PURE__ */ jsx3("span", { children: _slide.tabLabel ?? `${index + 1}` }),
383
+ _slide.tabMeta ? /* @__PURE__ */ jsx3("small", { children: _slide.tabMeta }) : null
384
+ ] }) : null
385
+ },
386
+ index
387
+ ))
397
388
  }
398
- ) : null,
399
- /* @__PURE__ */ jsxs2("p", { className: "rt-tpl-hero-carousel-counter", "aria-hidden": "true", children: [
400
- /* @__PURE__ */ jsx3("span", { className: "rt-tpl-hero-carousel-counter-current", children: selectedIndex + 1 }),
401
- "/",
402
- /* @__PURE__ */ jsx3("span", { className: "rt-tpl-hero-carousel-counter-total", children: slides.length })
389
+ ),
390
+ /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-carousel-controls-right", children: [
391
+ showPlayPause ? /* @__PURE__ */ jsx3(
392
+ "button",
393
+ {
394
+ type: "button",
395
+ className: "rt-tpl-hero-carousel-playpause",
396
+ "aria-pressed": autoplayGate.userPaused,
397
+ "aria-label": autoplayGate.userPaused ? "\uC790\uB3D9 \uC804\uD658 \uC7AC\uC0DD" : "\uC790\uB3D9 \uC804\uD658 \uC77C\uC2DC\uC815\uC9C0",
398
+ onClick: autoplayGate.togglePaused,
399
+ children: autoplayGate.userPaused ? /* @__PURE__ */ jsx3(PlayIcon, {}) : /* @__PURE__ */ jsx3(PauseIcon, {})
400
+ }
401
+ ) : null,
402
+ /* @__PURE__ */ jsxs2("p", { className: "rt-tpl-hero-carousel-counter", "aria-hidden": "true", children: [
403
+ /* @__PURE__ */ jsx3("span", { className: "rt-tpl-hero-carousel-counter-current", children: selectedIndex + 1 }),
404
+ "/",
405
+ /* @__PURE__ */ jsx3("span", { className: "rt-tpl-hero-carousel-counter-total", children: slides.length })
406
+ ] })
403
407
  ] })
404
408
  ] })
405
- ] })
406
- ]
407
- }
409
+ ]
410
+ }
411
+ )
408
412
  );
409
413
  }
410
414
 
411
415
  export {
412
416
  HeroCarouselViewport
413
417
  };
414
- //# sourceMappingURL=chunk-OMXEU4LS.js.map
418
+ //# sourceMappingURL=chunk-QYNEOFL7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates/hero-carousel-client.tsx","../src/templates/hero-carousel-curtain.tsx","../src/templates/hero-carousel-hooks.ts","../src/templates/hero-carousel-slide-track.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * `hero` `carousel` variant — 배경 사진 전환 메커니즘만 담당하는 client\n * island (M6 서버 우선: 인터랙션 최소 단위 격리). 카피 오버레이(h1/subtitle/\n * eyebrow/CTA)는 `TemplateHero`(RSC, sections.tsx)가 서버에서 그린다 — 이\n * 컴포넌트는 배경 슬라이드 + 도트 + 화살표 + \"N/M\" 카운터만 렌더한다.\n *\n * 빌드 계약: 본 파일은 tsup 의 독립 entry(`hero-carousel-client`)로 번들되고\n * `esbuild-plugin-preserve-directives` 가 \"use client\" 지시어를 해당 청크\n * 최상단에 보존한다 — `templates.js`(서버 전용, 나머지 4개 hero variant +\n * rich-text/services/faq/cta-banner/trust/contact) 로 병합되지 않는다\n * (tsup.config.ts 참고).\n *\n * 모션 규율(M5 + ADR-0086 P2 D3/D4, ds-rule-pack.md):\n * - `transition`: \"slide\"(기본, embla 가로 스크롤) | \"curtain\"(clip-path 와이프\n * — chambarun 이식, `hero-carousel-curtain.tsx`). variant 는 그대로\n * \"carousel\" 하나 — \"variant=구조, motion=인상\" 규칙(신규 variant 금지).\n * - `prefers-reduced-motion: reduce` → autoplay 강제 off + 전환은 즉시(embla\n * `jump=true` 무전환 / curtain 은 leaving 단계 생략) + Ken Burns·신 스윕·카피\n * 스태거 없음.\n * - autoplay 정지 사유 4종(D4): hover, focus-within, pause/재생 버튼, Page\n * Visibility API(탭 비활성). 합성은 `hero-carousel-hooks.ts`\n * `useAutoplayGate`.\n * - 좌/우 화살표 키보드 이동(포커스가 캐러셀 안에 있을 때), 도트는\n * `role=\"tablist\"`/`role=\"tab\"` + roving tabindex(방향키/Home/End, D4) —\n * 공용 `useRovingTablist`(`expertise-client.tsx` 와 동일 계약 재사용).\n * - WAI-ARIA carousel 패턴: 컨테이너 `role=\"region\"` +\n * `aria-roledescription=\"carousel\"`, 슬라이드별 `aria-roledescription=\"slide\"`\n * + `aria-label=\"Slide N of M\"`.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactElement } from \"react\";\nimport useEmblaCarousel from \"embla-carousel-react\";\n\nimport { useCurtainCarousel, CurtainTrack } from \"./hero-carousel-curtain.js\";\nimport { useAutoplayGate, usePrefersReducedMotion } from \"./hero-carousel-hooks.js\";\nimport { SlideTrack } from \"./hero-carousel-slide-track.js\";\nimport type { HeroCarouselViewportProps } from \"./hero-carousel-types.js\";\nimport { useRovingTablist } from \"./use-roving-tablist.js\";\n\nexport type {\n HeroCarouselSlideInput,\n HeroCarouselTransition,\n HeroCarouselViewportProps,\n} from \"./hero-carousel-types.js\";\n\nfunction PauseIcon(): ReactElement {\n return (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" fill=\"currentColor\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nfunction PlayIcon(): ReactElement {\n return (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M8 5v14l11-7z\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nexport function HeroCarouselViewport({\n slides,\n autoplay,\n intervalMs,\n scrim,\n transition = \"slide\",\n}: HeroCarouselViewportProps) {\n const isCurtain = transition === \"curtain\";\n const reducedMotion = usePrefersReducedMotion();\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n const [emblaRef, emblaApi] = useEmblaCarousel({\n loop: true,\n duration: reducedMotion ? 1 : 20,\n });\n const [emblaSelectedIndex, setEmblaSelectedIndex] = useState(0);\n const curtain = useCurtainCarousel(slides.length, reducedMotion);\n\n const onEmblaSelect = useCallback(() => {\n if (!emblaApi) return;\n setEmblaSelectedIndex(emblaApi.selectedScrollSnap());\n }, [emblaApi]);\n\n useEffect(() => {\n if (!emblaApi) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- 캐러셀 인스턴스가 준비된 뒤 현재 위치를 한 번 읽어 맞춘다(이후는 select 이벤트가 갱신). 외부 라이브러리 상태 구독이다.\n onEmblaSelect();\n emblaApi.on(\"select\", onEmblaSelect);\n emblaApi.on(\"reInit\", onEmblaSelect);\n return () => {\n emblaApi.off(\"select\", onEmblaSelect);\n emblaApi.off(\"reInit\", onEmblaSelect);\n };\n }, [emblaApi, onEmblaSelect]);\n\n const selectedIndex = isCurtain ? curtain.selectedIndex : emblaSelectedIndex;\n\n const autoplayGate = useAutoplayGate({ autoplay, reducedMotion, rootRef });\n\n useEffect(() => {\n if (!autoplayGate.shouldAutoplay) return;\n if (!isCurtain && !emblaApi) return;\n const id = window.setInterval(() => {\n if (isCurtain) curtain.next();\n else emblaApi?.scrollNext();\n }, intervalMs);\n return () => window.clearInterval(id);\n // curtain 객체 전체가 아니라 쓰는 메서드만 의존성에 둔다 — 메서드는\n // useCallback 으로 안정화돼 있고(hero-carousel-curtain.tsx), 객체는 매 렌더\n // 새로 만들어져 전체를 넣으면 타이머가 매번 다시 걸린다.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoplayGate.shouldAutoplay, intervalMs, isCurtain, curtain.next, emblaApi]);\n\n const scrollPrev = useCallback(() => {\n if (isCurtain) {\n curtain.prev();\n return;\n }\n emblaApi?.scrollPrev(reducedMotion);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- 위와 같은 이유(안정화된 메서드만 의존).\n }, [isCurtain, curtain.prev, emblaApi, reducedMotion]);\n\n const scrollNext = useCallback(() => {\n if (isCurtain) {\n curtain.next();\n return;\n }\n emblaApi?.scrollNext(reducedMotion);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- 위와 같은 이유(안정화된 메서드만 의존).\n }, [isCurtain, curtain.next, emblaApi, reducedMotion]);\n\n const scrollTo = useCallback(\n (index: number) => {\n if (isCurtain) {\n curtain.goTo(index);\n return;\n }\n emblaApi?.scrollTo(index, reducedMotion);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- 위와 같은 이유(안정화된 메서드만 의존).\n [isCurtain, curtain.goTo, emblaApi, reducedMotion],\n );\n\n const onKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext],\n );\n\n const selectedSlide = slides[selectedIndex];\n const hasSlidePanel = Boolean(\n selectedSlide &&\n (selectedSlide.eyebrow ||\n selectedSlide.title ||\n selectedSlide.subtitle ||\n selectedSlide.description ||\n selectedSlide.primaryCta ||\n selectedSlide.secondaryCta ||\n (selectedSlide.metrics && selectedSlide.metrics.length > 0)),\n );\n const hasTabLabels = slides.some((slide) => slide.tabLabel || slide.tabMeta);\n const showPlayPause = autoplay && !reducedMotion;\n\n const { getTabProps, onKeyDown: onTablistKeyDown } = useRovingTablist<HTMLButtonElement>({\n count: slides.length,\n selectedIndex,\n onSelect: scrollTo,\n orientation: \"horizontal\",\n });\n\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- WAI-ARIA 캐러셀 패턴. region 이 좌우 키·포인터를 받고 실제 이동은 안쪽 컨트롤이 담당한다.\n <div\n ref={rootRef}\n className=\"rt-tpl-hero-carousel\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n aria-label=\"배경 이미지 캐러셀\"\n tabIndex={0}\n onKeyDown={onKeyDown}\n data-reduced-motion={reducedMotion ? \"true\" : \"false\"}\n data-scrim={scrim ?? \"soft\"}\n data-transition={transition}\n onMouseEnter={autoplayGate.rootHandlers.onMouseEnter}\n onMouseLeave={autoplayGate.rootHandlers.onMouseLeave}\n onFocus={autoplayGate.rootHandlers.onFocus}\n onBlur={autoplayGate.rootHandlers.onBlur}\n >\n {isCurtain ? (\n <CurtainTrack\n slides={slides}\n selectedIndex={curtain.selectedIndex}\n leavingIndex={curtain.leavingIndex}\n />\n ) : (\n <SlideTrack\n viewportRef={emblaRef}\n slides={slides}\n selectedIndex={emblaSelectedIndex}\n />\n )}\n\n <button\n type=\"button\"\n className=\"rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--prev\"\n aria-label=\"이전 슬라이드\"\n onClick={scrollPrev}\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M15 5 8 12l7 7\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"rt-tpl-hero-carousel-arrow rt-tpl-hero-carousel-arrow--next\"\n aria-label=\"다음 슬라이드\"\n onClick={scrollNext}\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"m9 5 7 7-7 7\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n\n {hasSlidePanel && selectedSlide ? (\n <div\n key={isCurtain ? `panel-${selectedIndex}` : \"panel\"}\n className=\"rt-tpl-hero-carousel-slide-panel\"\n >\n {selectedSlide.eyebrow ? (\n <p className=\"rt-tpl-hero-carousel-slide-eyebrow\">\n {selectedSlide.eyebrow}\n </p>\n ) : null}\n {selectedSlide.title ? (\n <p className=\"rt-tpl-hero-carousel-slide-title\">\n {selectedSlide.title}\n </p>\n ) : null}\n {selectedSlide.subtitle ? (\n <p className=\"rt-tpl-hero-carousel-slide-subtitle\">\n {selectedSlide.subtitle}\n </p>\n ) : null}\n {selectedSlide.description ? (\n <p className=\"rt-tpl-hero-carousel-slide-desc\">\n {selectedSlide.description}\n </p>\n ) : null}\n {selectedSlide.metrics && selectedSlide.metrics.length > 0 ? (\n <dl className=\"rt-tpl-hero-carousel-slide-metrics\">\n {selectedSlide.metrics.map((metric) => (\n <div key={`${metric.value}-${metric.label}`}>\n <dt>{metric.label}</dt>\n <dd>\n {metric.value}\n {metric.unit ? <span>{metric.unit}</span> : null}\n </dd>\n {metric.source ? <dd>{metric.source}</dd> : null}\n </div>\n ))}\n </dl>\n ) : null}\n {selectedSlide.primaryCta || selectedSlide.secondaryCta ? (\n <div className=\"rt-tpl-hero-carousel-slide-actions\">\n {selectedSlide.primaryCta ? (\n <a\n className=\"rt-tpl-btn rt-tpl-btn--primary\"\n href={selectedSlide.primaryCta.href}\n data-track=\"cta_hero\"\n >\n {selectedSlide.primaryCta.label}\n </a>\n ) : null}\n {selectedSlide.secondaryCta ? (\n <a\n className=\"rt-tpl-btn rt-tpl-btn--secondary\"\n href={selectedSlide.secondaryCta.href}\n data-track=\"cta_hero\"\n >\n {selectedSlide.secondaryCta.label}\n </a>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"rt-tpl-hero-carousel-controls\">\n {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus -- WAI-ARIA 탭 패턴(roving tabindex). 초점은 개별 인디케이터가 갖고 컨테이너는 키 이벤트만 위임받는다. */}\n <div\n className={\n hasTabLabels\n ? \"rt-tpl-hero-carousel-dots rt-tpl-hero-carousel-dots--tabs\"\n : \"rt-tpl-hero-carousel-dots\"\n }\n role=\"tablist\"\n aria-label=\"슬라이드 선택\"\n onKeyDown={onTablistKeyDown}\n >\n {slides.map((_slide, index) => (\n <button\n key={index}\n {...getTabProps(index)}\n type=\"button\"\n role=\"tab\"\n className=\"rt-tpl-hero-carousel-dot\"\n aria-selected={index === selectedIndex}\n aria-label={`${index + 1}번 슬라이드로 이동`}\n data-active={index === selectedIndex ? \"true\" : \"false\"}\n onClick={() => scrollTo(index)}\n >\n {hasTabLabels ? (\n <>\n <span>{_slide.tabLabel ?? `${index + 1}`}</span>\n {_slide.tabMeta ? <small>{_slide.tabMeta}</small> : null}\n </>\n ) : null}\n </button>\n ))}\n </div>\n <div className=\"rt-tpl-hero-carousel-controls-right\">\n {showPlayPause ? (\n <button\n type=\"button\"\n className=\"rt-tpl-hero-carousel-playpause\"\n aria-pressed={autoplayGate.userPaused}\n aria-label={\n autoplayGate.userPaused ? \"자동 전환 재생\" : \"자동 전환 일시정지\"\n }\n onClick={autoplayGate.togglePaused}\n >\n {autoplayGate.userPaused ? <PlayIcon /> : <PauseIcon />}\n </button>\n ) : null}\n <p className=\"rt-tpl-hero-carousel-counter\" aria-hidden=\"true\">\n <span className=\"rt-tpl-hero-carousel-counter-current\">\n {selectedIndex + 1}\n </span>\n /\n <span className=\"rt-tpl-hero-carousel-counter-total\">{slides.length}</span>\n </p>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * `curtain` transition — chambarun 이식(ADR-0086 P2 D3).\n * 모션 스펙 원본: chambarun mockup `assets/site.css` `@keyframes curtainReveal`\n * (clip-path 와이프) + `.curtain-sheen`(신 스윕) + `.hero-copy>*` 스태거, 엔진은\n * `assets/site.js` 36-85행 `show(n)`(is-active/is-leaving 상태 머신, 980ms 뒤\n * is-leaving 정리). 타이밍·이징·클립 경로는 원본 값을 그대로 유지하고 색은\n * 하드코딩 헥스 대신 `--rt-*` 토큰으로 치환했다(CSS, `templates.css` 참고).\n *\n * embla 를 쓰지 않는다 — embla 는 가로 스크롤 트랙 물리 이동이 전제라\n * clip-path 와이프(절대배치 스택)와 구조가 다르다. `hero-carousel-client.tsx`\n * 400줄 상한 분할.\n */\nimport { useCallback, useRef, useState } from \"react\";\nimport type { ReactElement } from \"react\";\n\nimport type { HeroCarouselSlideInput } from \"./hero-carousel-types.js\";\n\n/** chambarun site.js — is-leaving 클래스 정리 타이밍(커튼 리빌 .92s + 여유). */\nconst LEAVING_CLEAR_MS = 980;\n\nexport interface CurtainCarouselController {\n readonly selectedIndex: number;\n readonly leavingIndex: number | null;\n readonly goTo: (index: number) => void;\n readonly next: () => void;\n readonly prev: () => void;\n}\n\n/**\n * `curtain` 전용 인덱스 상태 머신 — chambarun `show(n)` 이식. `reducedMotion`\n * 이면 leaving 단계를 만들지 않는다(D4 — 커튼 대신 즉시 전환).\n */\nexport function useCurtainCarousel(\n slideCount: number,\n reducedMotion: boolean,\n): CurtainCarouselController {\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [leavingIndex, setLeavingIndex] = useState<number | null>(null);\n const selectedRef = useRef(0);\n const runRef = useRef(0);\n const clearTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const goTo = useCallback(\n (nextRaw: number) => {\n if (slideCount < 1) return;\n const nextIndex = ((nextRaw % slideCount) + slideCount) % slideCount;\n const prevIndex = selectedRef.current;\n if (nextIndex === prevIndex) return;\n selectedRef.current = nextIndex;\n setSelectedIndex(nextIndex);\n clearTimeout(clearTimerRef.current);\n if (reducedMotion) {\n setLeavingIndex(null);\n return;\n }\n setLeavingIndex(prevIndex);\n runRef.current += 1;\n const runId = runRef.current;\n clearTimerRef.current = setTimeout(() => {\n if (runId === runRef.current) setLeavingIndex(null);\n }, LEAVING_CLEAR_MS);\n },\n [slideCount, reducedMotion],\n );\n\n const next = useCallback(() => goTo(selectedRef.current + 1), [goTo]);\n const prev = useCallback(() => goTo(selectedRef.current - 1), [goTo]);\n\n return { selectedIndex, leavingIndex, goTo, next, prev };\n}\n\nexport interface CurtainTrackProps {\n readonly slides: readonly HeroCarouselSlideInput[];\n readonly selectedIndex: number;\n readonly leavingIndex: number | null;\n}\n\n/**\n * 절대배치 스택 트랙(chambarun `.slide{position:absolute;inset:0}`) — 각\n * 슬라이드가 `data-active`/`data-leaving` 로 clip-path 와이프·오버랩을\n * 제어한다(CSS `templates.css` `[data-transition=\"curtain\"]` 스코프). 활성\n * 슬라이드에만 신 스윕 엘리먼트를 렌더(비활성 슬라이드는 DOM 비용 절감).\n */\nexport function CurtainTrack({\n slides,\n selectedIndex,\n leavingIndex,\n}: CurtainTrackProps): ReactElement {\n return (\n <div className=\"rt-tpl-hero-carousel-viewport\">\n <div className=\"rt-tpl-hero-carousel-track\">\n {slides.map((slide, index) => {\n const active = index === selectedIndex;\n const leaving = index === leavingIndex;\n return (\n <div\n key={`${slide.image}-${index}`}\n className=\"rt-tpl-hero-carousel-slide\"\n aria-roledescription=\"slide\"\n aria-label={`Slide ${index + 1} of ${slides.length}`}\n aria-hidden={active ? undefined : true}\n data-active={active ? \"true\" : \"false\"}\n data-leaving={leaving ? \"true\" : \"false\"}\n >\n <img\n className=\"rt-tpl-hero-carousel-img\"\n src={slide.image}\n alt={slide.alt}\n data-active={active ? \"true\" : \"false\"}\n loading={index === 0 ? \"eager\" : \"lazy\"}\n />\n {active ? (\n <span className=\"rt-tpl-hero-carousel-sheen\" aria-hidden=\"true\" />\n ) : null}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * `hero` `carousel` 공유 훅 — reduced-motion·document visibility·autoplay\n * 정지 사유 합성(D4 접근성 계약, ADR-0086 P2). `hero-carousel-client.tsx` 400줄\n * 상한 분할.\n */\nimport { useCallback, useState } from \"react\";\nimport type { FocusEvent, RefObject } from \"react\";\nimport { useEffect } from \"react\";\n\nexport function usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false);\n useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n setReduced(query.matches);\n const onChange = (event: MediaQueryListEvent) => setReduced(event.matches);\n query.addEventListener(\"change\", onChange);\n return () => query.removeEventListener(\"change\", onChange);\n }, []);\n return reduced;\n}\n\nexport function useDocumentVisible(): boolean {\n const [visible, setVisible] = useState(true);\n useEffect(() => {\n const onVisibility = () => setVisible(document.visibilityState === \"visible\");\n onVisibility();\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => document.removeEventListener(\"visibilitychange\", onVisibility);\n }, []);\n return visible;\n}\n\nexport interface AutoplayGate {\n /** 실제 autoplay 인터벌을 걸어도 되는지 — 모든 정지 사유의 합성 결과. */\n readonly shouldAutoplay: boolean;\n /** pause/재생 버튼이 반영하는 사용자 의도(수동 일시정지). */\n readonly userPaused: boolean;\n readonly togglePaused: () => void;\n /** 루트 엘리먼트에 그대로 스프레드 — hover/focus-within 일시정지. */\n readonly rootHandlers: {\n readonly onMouseEnter: () => void;\n readonly onMouseLeave: () => void;\n readonly onFocus: () => void;\n readonly onBlur: (event: FocusEvent<HTMLElement>) => void;\n };\n}\n\n/**\n * autoplay pause 사유 4종(D4): hover, focus-within, 사용자 버튼, 문서 비가시.\n * `reducedMotion` 은 autoplay 자체를 아예 시작하지 않는 별도 계약이라 이 훅\n * 밖(`autoplay && !reducedMotion`)에서 이미 걸러지지만, 재확인 차원에서 인자로\n * 받아 합성식에도 포함한다.\n */\nexport function useAutoplayGate(params: {\n readonly autoplay: boolean;\n readonly reducedMotion: boolean;\n readonly rootRef: RefObject<HTMLElement | null>;\n}): AutoplayGate {\n const { autoplay, reducedMotion, rootRef } = params;\n const tabVisible = useDocumentVisible();\n const [hovered, setHovered] = useState(false);\n const [focusedWithin, setFocusedWithin] = useState(false);\n const [userPaused, setUserPaused] = useState(false);\n\n const onBlur = useCallback(\n (event: FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget as Node | null;\n if (!next || !rootRef.current?.contains(next)) {\n setFocusedWithin(false);\n }\n },\n [rootRef],\n );\n\n const shouldAutoplay =\n autoplay &&\n !reducedMotion &&\n tabVisible &&\n !userPaused &&\n !hovered &&\n !focusedWithin;\n\n return {\n shouldAutoplay,\n userPaused,\n togglePaused: () => setUserPaused((value) => !value),\n rootHandlers: {\n onMouseEnter: () => setHovered(true),\n onMouseLeave: () => setHovered(false),\n onFocus: () => setFocusedWithin(true),\n onBlur,\n },\n };\n}\n","\"use client\";\n\n/**\n * `slide` transition(기본값) 트랙 — 기존 embla 가로 스크롤 배경 전환, 회귀 없음.\n * `hero-carousel-client.tsx` 400줄 상한 분할(`curtain` transition 은\n * `hero-carousel-curtain.tsx`).\n */\nimport type { ReactElement } from \"react\";\nimport type { EmblaViewportRefType } from \"embla-carousel-react\";\n\nimport type { HeroCarouselSlideInput } from \"./hero-carousel-types.js\";\n\nexport interface SlideTrackProps {\n readonly viewportRef: EmblaViewportRefType;\n readonly slides: readonly HeroCarouselSlideInput[];\n readonly selectedIndex: number;\n}\n\nexport function SlideTrack({\n viewportRef,\n slides,\n selectedIndex,\n}: SlideTrackProps): ReactElement {\n return (\n <div className=\"rt-tpl-hero-carousel-viewport\" ref={viewportRef}>\n <div className=\"rt-tpl-hero-carousel-track\">\n {slides.map((slide, index) => (\n <div\n key={`${slide.image}-${index}`}\n className=\"rt-tpl-hero-carousel-slide\"\n aria-roledescription=\"slide\"\n aria-label={`Slide ${index + 1} of ${slides.length}`}\n aria-hidden={index === selectedIndex ? undefined : true}\n >\n <img\n className=\"rt-tpl-hero-carousel-img\"\n src={slide.image}\n alt={slide.alt}\n data-active={index === selectedIndex ? \"true\" : \"false\"}\n loading={index === 0 ? \"eager\" : \"lazy\"}\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useCallback","useState","jsx","jsx","jsxs","useRef","useState","useCallback","useEffect"]}
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as ConcernsSection } from './site-config-Dicv57oY.js';
2
+ import { C as ConcernsSection } from './site-config-DNpvlqA5.js';
3
3
  import 'zod';
4
4
 
5
5
  type ConcernRegion = ConcernsSection["props"]["regions"][number];
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { E as ExpertiseSection } from './site-config-Dicv57oY.js';
2
+ import { E as ExpertiseSection } from './site-config-DNpvlqA5.js';
3
3
  import 'zod';
4
4
 
5
5
  type ExpertiseItem = ExpertiseSection["props"]["items"][number];
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  ExpertiseTabStage
4
- } from "./chunk-6MFHA555.js";
4
+ } from "./chunk-27RT6B3O.js";
5
5
  import "./chunk-YLSQAT6B.js";
6
6
  export {
7
7
  ExpertiseTabStage
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  HeroCarouselViewport
4
- } from "./chunk-OMXEU4LS.js";
4
+ } from "./chunk-QYNEOFL7.js";
5
5
  import "./chunk-YLSQAT6B.js";
6
6
  export {
7
7
  HeroCarouselViewport