@roottale/cms-renderer-next 0.52.0 → 0.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +33 -4
  3. package/dist/chunk-GVKENK6H.js +75 -0
  4. package/dist/chunk-GVKENK6H.js.map +1 -0
  5. package/dist/{chunk-U2IM6OCQ.js → chunk-HM5VP7SR.js} +1 -1
  6. package/dist/{chunk-U2IM6OCQ.js.map → chunk-HM5VP7SR.js.map} +1 -1
  7. package/dist/chunk-KSPPYX22.js +14 -0
  8. package/dist/chunk-KSPPYX22.js.map +1 -0
  9. package/dist/cms-public.css +65 -0
  10. package/dist/exposure-client.d.ts +9 -0
  11. package/dist/exposure-client.js +8 -0
  12. package/dist/routes.d.ts +10 -1
  13. package/dist/routes.js +36 -16
  14. package/dist/routes.js.map +1 -1
  15. package/dist/section-runtime.d.ts +46 -0
  16. package/dist/section-runtime.js +9 -0
  17. package/dist/server.d.ts +49 -3
  18. package/dist/server.js +1764 -34
  19. package/dist/server.js.map +1 -1
  20. package/package.json +8 -24
  21. package/dist/before-after-client.d.ts +0 -15
  22. package/dist/before-after-client.js +0 -8
  23. package/dist/chunk-27RT6B3O.js +0 -93
  24. package/dist/chunk-27RT6B3O.js.map +0 -1
  25. package/dist/chunk-2QU4JU46.js +0 -3927
  26. package/dist/chunk-2QU4JU46.js.map +0 -1
  27. package/dist/chunk-4RQ3SEDL.js +0 -106
  28. package/dist/chunk-4RQ3SEDL.js.map +0 -1
  29. package/dist/chunk-6GQCFFHU.js +0 -540
  30. package/dist/chunk-6GQCFFHU.js.map +0 -1
  31. package/dist/chunk-H46JUIHZ.js +0 -79
  32. package/dist/chunk-H46JUIHZ.js.map +0 -1
  33. package/dist/chunk-K62RR4UQ.js +0 -53
  34. package/dist/chunk-K62RR4UQ.js.map +0 -1
  35. package/dist/chunk-LEHHSI6G.js +0 -64
  36. package/dist/chunk-LEHHSI6G.js.map +0 -1
  37. package/dist/chunk-QYNEOFL7.js +0 -418
  38. package/dist/chunk-QYNEOFL7.js.map +0 -1
  39. package/dist/chunk-U5AKACPN.js +0 -113
  40. package/dist/chunk-U5AKACPN.js.map +0 -1
  41. package/dist/chunk-UM37DOIK.js +0 -961
  42. package/dist/chunk-UM37DOIK.js.map +0 -1
  43. package/dist/chunk-YLSQAT6B.js +0 -59
  44. package/dist/chunk-YLSQAT6B.js.map +0 -1
  45. package/dist/chunk-ZL2NA3QT.js +0 -313
  46. package/dist/chunk-ZL2NA3QT.js.map +0 -1
  47. package/dist/concerns-client.d.ts +0 -12
  48. package/dist/concerns-client.js +0 -8
  49. package/dist/contact-map-client.d.ts +0 -27
  50. package/dist/contact-map-client.js +0 -8
  51. package/dist/contact-map-client.js.map +0 -1
  52. package/dist/expertise-client.d.ts +0 -16
  53. package/dist/expertise-client.js +0 -9
  54. package/dist/expertise-client.js.map +0 -1
  55. package/dist/hero-carousel-client.d.ts +0 -52
  56. package/dist/hero-carousel-client.js +0 -9
  57. package/dist/hero-carousel-client.js.map +0 -1
  58. package/dist/seo-checks.d.ts +0 -87
  59. package/dist/seo-checks.js +0 -27
  60. package/dist/seo-checks.js.map +0 -1
  61. package/dist/site-config-DNpvlqA5.d.ts +0 -3961
  62. package/dist/spaces-client.d.ts +0 -10
  63. package/dist/spaces-client.js +0 -8
  64. package/dist/spaces-client.js.map +0 -1
  65. package/dist/team-expanding-stage-client.d.ts +0 -35
  66. package/dist/team-expanding-stage-client.js +0 -8
  67. package/dist/team-expanding-stage-client.js.map +0 -1
  68. package/dist/templates.css +0 -4145
  69. package/dist/templates.d.ts +0 -171
  70. package/dist/templates.js +0 -291
  71. package/dist/templates.js.map +0 -1
  72. package/dist/theme-css-vars-CD3jcEkp.d.ts +0 -7
  73. package/dist/ui-react-motion-client.d.ts +0 -76
  74. package/dist/ui-react-motion-client.js +0 -16
  75. package/dist/ui-react-motion-client.js.map +0 -1
  76. package/dist/validate.d.ts +0 -104
  77. package/dist/validate.js +0 -1204
  78. package/dist/validate.js.map +0 -1
  79. /package/dist/{before-after-client.js.map → exposure-client.js.map} +0 -0
  80. /package/dist/{concerns-client.js.map → section-runtime.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/templates/site-config-version.ts","../src/templates/site-config.ts"],"sourcesContent":["/**\n * `SiteConfig` 계약 버전 (ADR-0086 D2 — 팩 ↔ renderer 호환성 계약).\n *\n * `site-config.ts` 는 이미 SIZE_OK 상한 근처(canonical SectionSchema\n * registry)라 버전 해석 로직은 별도 모듈에 두고 `site-config.ts` 에서\n * re-export 한다.\n *\n * 팩 manifest(`packages/assembly/src/pack-manifest.ts`)의\n * `siteConfigVersion` 필드가 이 버전과 비교 대상 — 팩이 요구하는 버전이\n * renderer 가 실제로 해석하는 버전보다 높으면 조립 전 fail-fast 해야 한다.\n */\n\n/** 현재 `SiteConfig` 스키마 버전. 계약 필드 추가 등 breaking 변경 시에만 올린다. */\nexport const SITE_CONFIG_SCHEMA_VERSION = 1;\n\n/** `schemaVersion` 도입(버전 1) 이전에 작성된 config 가 갖는 암묵 버전. 불변. */\nconst LEGACY_SITE_CONFIG_SCHEMA_VERSION = 1;\n\n/**\n * `config.schemaVersion` 해석 헬퍼 — 필드 미지정(기존 고객\n * `site.config.ts`, `schemaVersion` 도입 이전 작성분)은 버전 1 로 해석한다\n * (하위 호환, ADR-0086 D2). 현재 버전 상수가 아니라 리터럴 1 — 후일\n * `SITE_CONFIG_SCHEMA_VERSION` 이 올라도 미지정 config 는 계속 1 이다.\n */\nexport function resolveSiteConfigSchemaVersion(config: {\n schemaVersion?: number;\n}): number {\n return config.schemaVersion ?? LEGACY_SITE_CONFIG_SCHEMA_VERSION;\n}\n","/**\n * `@roottale/cms-renderer-next/templates` — canonical `SiteConfig` 스키마.\n *\n * ADR-0040 P2 + P0.5 RFC §① (`roottale-docs/docs/services/external-site-blockification.md`).\n *\n * 설계 원칙 (codex consult #1 반영):\n * - **계약 = `section type + variant + renderer registry`.** tier 는 스키마 필드가\n * 아니라 문서/거버넌스용 성숙도 메타데이터.\n * - **100% serializable** — 함수/JSX 금지 (Astro parity + admin preview 동일 경로).\n * escape hatch 는 config 안 함수가 아니라 `customSections` registry\n * (`SiteRenderer` 에 빌드 시 type→컴포넌트 주입).\n * - CMS-bound 섹션(`blog-list`/`lead-form`)은 `RootTaleBlogList`/`RootTaleLeadForm`\n * 으로 위임 — templates 는 cms-renderer 코어를 단방향 의존 (RFC §②).\n *\n * RFC §① 대비 구현 정정:\n * - `site.url` 은 optional — 스타터/외부 사이트가 `SITE_URL` env 로 런타임 해석하는\n * 기존 패턴(P4 스타터)과 충돌하므로 config 강제 대신 env fallback 허용.\n * - 빌트인에 `rich-text` 추가 (P4 스타터 기 운영 섹션 — \"동일 패턴\" 군).\n * allow: SIZE_OK — canonical SectionSchema registry이며 W1-2 계약이 이 파일 수정을 요구한다.\n */\nimport { z } from \"zod\";\n\nexport {\n SITE_CONFIG_SCHEMA_VERSION,\n resolveSiteConfigSchemaVersion,\n} from \"./site-config-version.js\";\n\nconst JsonValue: z.ZodType<unknown> = z.lazy(() =>\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.array(JsonValue),\n z.record(JsonValue),\n ]),\n);\n\nexport const NavItemSchema = z.object({\n label: z.string(),\n href: z.string(),\n children: z\n .array(z.object({ label: z.string(), href: z.string() }))\n .optional(),\n});\nexport type NavItem = z.infer<typeof NavItemSchema>;\n\nconst CtaSchema = z.object({ label: z.string(), href: z.string() });\nexport type SectionCta = z.infer<typeof CtaSchema>;\n\nconst MetricSchema = z.object({\n value: z.string(),\n unit: z.string().optional(),\n label: z.string(),\n source: z.string().optional(),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n});\n\n/**\n * 모든 섹션 공통 필드. `theme` 는 섹션 스코프 CSS 변수 override —\n * key 는 `--rt-*` custom property 이름, 값은 CSS 값.\n * 예) `{ \"--rt-color-primary\": \"#0B1F49\" }`\n */\nconst SectionBase = z.object({\n /** 안정적 anchor·dedup·preview 매핑용. 페이지 내 유일. */\n id: z.string().optional(),\n /** 빌트인 또는 customSections registry 키. kebab-case. */\n type: z.string(),\n /** 같은 type 의 비주얼 변형. 빌트인 type 은 enum 으로 좁힘. */\n variant: z.string().optional(),\n /** 섹션 스코프 토큰 override (`--rt-*` CSS 변수 → 값). */\n theme: z.record(z.string()).optional(),\n /** 섹션별 직렬화 props. */\n props: z.record(JsonValue).default({}),\n});\n\n// ── 빌트인 섹션 (props 좁힘 — 런타임 + 타입) ────────────────────────────────\n\nexport const SECTION_VARIANTS = {\n hero: [\"split\", \"image\", \"minimal\", \"carousel\", \"overlay-panel\"],\n services: [\n \"grid\",\n \"list\",\n \"index-rows\",\n \"split-featured\",\n \"action-rail\",\n \"photo-cards\",\n ],\n faq: [\"default\", \"two-column\", \"with-contact\", \"content-accordion\"],\n \"cta-banner\": [\"default\", \"photo-overlay\"],\n trust: [\n \"stat-band\",\n \"logo-cloud\",\n \"feature-grid\",\n \"logo-marquee\",\n \"credential-list\",\n \"dashboard\",\n ],\n contact: [\"form-map-5050\", \"split\", \"channel-cards\", \"visit-band\"],\n hours: [\"table\", \"compact\"],\n pricing: [\"table\", \"cards\"],\n process: [\"horizontal\", \"vertical\", \"timeline-marked\"],\n \"before-after\": [\"slider\", \"side-by-side\"],\n testimonials: [\"editorial-quote\", \"quote-rail\", \"star-compact\"],\n team: [\"grid\", \"spotlight\", \"expanding-stage\", \"row\"],\n expertise: [\"tab-stage\", \"cards\"],\n spaces: [\"stage\", \"mosaic\"],\n concerns: [\"figure-map\", \"list\"],\n footer: [\"columns\", \"minimal\"],\n} as const;\n\n/**\n * `carousel` variant 슬라이드 — 카피는 슬라이드마다 바뀌지 않는다(고정 오버레이).\n * 슬라이드는 배경 사진 전환만 담당 — `alt`/`caption` 모두 h1 이 아니다\n * (publish-gate h1 모델은 `props.title` 만 본다, W6-1).\n */\nconst HeroCarouselSlideSchema = z.object({\n image: z.string(),\n alt: z.string(),\n caption: z.string().optional(),\n eyebrow: z.string().optional(),\n title: z.string().optional(),\n subtitle: z.string().optional(),\n description: z.string().optional(),\n primaryCta: CtaSchema.optional(),\n secondaryCta: CtaSchema.optional(),\n tabLabel: z.string().optional(),\n tabMeta: z.string().optional(),\n metrics: z.array(MetricSchema).max(4).optional(),\n});\n\nconst HeroProofMetricsSchema = z.object({\n items: z.array(MetricSchema).min(1).max(4),\n position: z.enum([\"inline\", \"overlay\"]).default(\"inline\"),\n});\n\nexport const HeroSectionSchema = SectionBase.extend({\n type: z.literal(\"hero\"),\n variant: z.enum(SECTION_VARIANTS.hero).default(\"split\"),\n props: z.object({\n eyebrow: z.string().optional(),\n /** `\\n` 으로 줄바꿈. */\n title: z.string(),\n description: z.string().optional(),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n cta: CtaSchema.optional(),\n /** `carousel` 전용 — 고정 카피 아래 배경만 전환되는 슬라이드. 2-8개. */\n slides: z.array(HeroCarouselSlideSchema).min(2).max(8).optional(),\n /** `carousel` 전용 — 부제(슬라이드와 무관한 고정 카피). */\n subtitle: z.string().optional(),\n /** `carousel` 전용. 기본 true — `useReducedMotion` 시 렌더러가 강제 off. */\n autoplay: z.boolean().optional(),\n /** `carousel` 전용, ms. 기본 6000. */\n intervalMs: z.number().min(3000).max(10000).optional(),\n /**\n * `carousel` 전용 배경 전환 모션(ADR-0086 P2 D3). 기본 \"slide\"(기존 embla\n * 가로 스크롤). \"curtain\" = clip-path 와이프 + 신 스윕(chambarun 이식).\n * variant 는 그대로 \"carousel\" — 신규 variant 아님(\"variant=구조,\n * motion=인상\" 규칙).\n */\n transition: z.enum([\"slide\", \"curtain\"]).optional(),\n proofMetrics: HeroProofMetricsSchema.optional(),\n scrim: z.enum([\"none\", \"soft\", \"strong\"]).optional(),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant === \"overlay-panel\") {\n if (!section.props.imageSrc) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"imageSrc\"],\n message: \"overlay-panel hero 는 imageSrc 가 필수입니다.\",\n });\n }\n if (!section.props.imageAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"imageAlt\"],\n message: \"overlay-panel hero 는 imageAlt 가 필수입니다.\",\n });\n }\n }\n if (section.variant !== \"carousel\") return;\n if (section.props.title.length < 1 || section.props.title.length > 80) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"title\"],\n message: \"carousel hero 제목은 1-80자여야 합니다.\",\n });\n }\n if (!section.props.slides || section.props.slides.length < 2) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"slides\"],\n message: \"carousel hero 는 slides 2-8개가 필요합니다.\",\n });\n }\n});\n\nexport const RichTextSectionSchema = SectionBase.extend({\n type: z.literal(\"rich-text\"),\n props: z.object({\n heading: z.string().optional(),\n /** `\\n\\n` 으로 문단 분리. */\n body: z.string(),\n }),\n});\n\nexport const ServicesSectionSchema = SectionBase.extend({\n type: z.literal(\"services\"),\n /**\n * C10 §1-2 — `index-rows`(hairline 인덱스 행) · `split-featured`(비대칭\n * 5/7, 대표 서비스 1개 + 나머지 컴팩트 리스트) 추가. AI-slop 금지 #1/#2\n * (아이콘 그리드·균등 카드) 대체 문법.\n */\n variant: z.enum(SECTION_VARIANTS.services).default(\"grid\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n items: z\n .array(\n z.object({\n title: z.string(),\n subtitle: z.string().optional(),\n description: z.string().optional(),\n href: z.string().optional(),\n iconSrc: z.string().optional(),\n iconAlt: z.string().optional(),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n kind: z.string().optional(),\n ctaLabel: z.string().optional(),\n chips: z.array(z.string()).max(6).optional(),\n meta: z.array(z.string()).max(8).optional(),\n }),\n )\n .min(1)\n .max(12),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant === \"split-featured\" && section.props.items.length < 2) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\"],\n message:\n \"split-featured 는 대표 서비스 1개 + 나머지 최소 1개, 총 2개 이상의 items 가 필요합니다.\",\n });\n }\n section.props.items.forEach((item, index) => {\n if (item.iconSrc && !item.iconAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\", index, \"iconAlt\"],\n message: \"iconSrc 가 있으면 iconAlt 가 필요합니다.\",\n });\n }\n if (section.variant === \"photo-cards\" && !item.imageSrc) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\", index, \"imageSrc\"],\n message: \"photo-cards 서비스 항목은 imageSrc 가 필요합니다.\",\n });\n }\n if (section.variant === \"photo-cards\" && !item.imageAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\", index, \"imageAlt\"],\n message: \"photo-cards 서비스 항목은 imageAlt 가 필요합니다.\",\n });\n }\n });\n});\n\nconst FooterLogoSchema = z.object({\n src: z.string(),\n alt: z.string(),\n});\n\nconst FooterLinkSchema = z.object({\n label: z.string(),\n href: z.string(),\n});\n\nconst FooterColumnSchema = z.object({\n title: z.string(),\n links: z.array(FooterLinkSchema).min(1).max(12),\n});\n\nconst FooterContactInfoSchema = z.object({\n address: z.string().optional(),\n phone: z.string().optional(),\n email: z.string().optional(),\n});\n\nconst FooterSocialLinkSchema = z.object({\n kind: z.string(),\n href: z.string(),\n});\n\nexport const FooterSectionSchema = SectionBase.extend({\n type: z.literal(\"footer\"),\n variant: z.enum(SECTION_VARIANTS.footer).default(\"columns\"),\n props: z.object({\n businessName: z.string(),\n logo: FooterLogoSchema.optional(),\n description: z.string().optional(),\n columns: z.array(FooterColumnSchema).max(6).optional(),\n contactInfo: FooterContactInfoSchema.optional(),\n legalInfo: z.string().optional(),\n privacyHref: z.string().optional(),\n termsHref: z.string().optional(),\n socialLinks: z.array(FooterSocialLinkSchema).max(8).optional(),\n copyright: z.string().optional(),\n /** 'hidden' 은 화이트라벨 plan gate 통과 시만 유효 (미지정 = 'default' 렌더). tenant 커스터마이즈 대상 아님 — 에디터 폼에 노출하지 않는다. */\n credit: z.enum([\"default\", \"hidden\"]).optional(),\n }),\n}).superRefine((section, ctx) => {\n if (\n section.variant === \"columns\" &&\n (!section.props.columns || section.props.columns.length === 0)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"columns\"],\n message: \"columns footer 는 columns 1개 이상이 필요합니다.\",\n });\n }\n});\n\nconst FaqItemSchema = z.object({ question: z.string(), answer: z.string() });\n\nconst FaqGroupSchema = z.object({\n title: z.string(),\n items: z.array(FaqItemSchema).min(1).max(12),\n});\n\nconst FaqContactSchema = z.object({\n title: z.string(),\n description: z.string().optional(),\n phone: z.string().optional(),\n cta: CtaSchema.optional(),\n});\n\nconst FaqContentItemSchema = z.object({\n title: z.string(),\n summary: z.string().optional(),\n body: z.string(),\n signs: z.array(z.string()).max(8).optional(),\n href: z.string().optional(),\n});\n\nconst FaqContentGroupSchema = z.object({\n title: z.string(),\n items: z.array(FaqContentItemSchema).min(1).max(12),\n});\n\nexport const FaqSectionSchema = SectionBase.extend({\n type: z.literal(\"faq\"),\n variant: z.enum(SECTION_VARIANTS.faq).default(\"default\"),\n props: z.object({\n heading: z.string().optional(),\n items: z.array(FaqItemSchema).min(1).max(20).optional(),\n /** `two-column` 전용 — 그룹 헤더 + 항목. 없으면 기존 flat items 를 사용한다. */\n groups: z.array(FaqGroupSchema).min(1).max(6).optional(),\n /** `with-contact` 전용 — 우측 sticky 상담 유도 카드. */\n contact: FaqContactSchema.optional(),\n /** `content-accordion` 전용 — FAQPage가 아닌 질환/서비스 설명 accordion. */\n contentGroups: z.array(FaqContentGroupSchema).min(1).max(6).optional(),\n /** FAQPage JSON-LD 주입 (기본 true — SEO 3 pillars). 페이지에 FAQ 섹션이 여럿이면 하나만 true 로. */\n jsonLd: z.boolean().default(true),\n }),\n}).superRefine((section, ctx) => {\n const hasContentGroups = Boolean(\n section.props.contentGroups && section.props.contentGroups.length > 0,\n );\n if (section.variant === \"content-accordion\") {\n if (!hasContentGroups) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"contentGroups\"],\n message: \"content-accordion faq sections require contentGroups\",\n });\n }\n return;\n }\n const hasItems = Boolean(section.props.items && section.props.items.length > 0);\n const hasGroups = Boolean(section.props.groups && section.props.groups.length > 0);\n if (!hasItems && !hasGroups) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\"],\n message: \"faq sections require items or groups\",\n });\n }\n if (section.variant === \"with-contact\" && !section.props.contact) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"contact\"],\n message: \"with-contact faq sections require contact\",\n });\n }\n});\n\nexport const CtaBannerSectionSchema = SectionBase.extend({\n type: z.literal(\"cta-banner\"),\n /** C10 §1-5 — `photo-overlay`: 풀블리드 사진 + 다크 오버레이 극단 대비 CTA. */\n variant: z.enum(SECTION_VARIANTS[\"cta-banner\"]).default(\"default\"),\n props: z.object({\n title: z.string(),\n description: z.string().optional(),\n cta: CtaSchema.optional(),\n /** `photo-overlay` 전용 — 필수(사진 없으면 렌더 거부). */\n image: z.string().optional(),\n imageAlt: z.string().optional(),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant !== \"photo-overlay\") return;\n if (!section.props.image) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"image\"],\n message: \"photo-overlay cta-banner 는 image 가 필수입니다.\",\n });\n }\n if (!section.props.imageAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"imageAlt\"],\n message: \"photo-overlay cta-banner 는 imageAlt 가 필수입니다.\",\n });\n }\n});\n\n/** → `RootTaleBlogList` 위임 (cms-bound). */\nexport const BlogListSectionSchema = SectionBase.extend({\n type: z.literal(\"blog-list\"),\n props: z.object({\n heading: z.string().optional(),\n limit: z.number().int().positive().optional(),\n /** 포스트 링크 베이스. 기본 `/blog`. */\n blogBasePath: z.string().optional(),\n emptyMessage: z.string().optional(),\n }),\n});\n\n/** → `RootTaleLeadForm` 위임 (cms-bound). */\nexport const LeadFormSectionSchema = SectionBase.extend({\n type: z.literal(\"lead-form\"),\n props: z.object({\n /** 사이트 자체 server 엔드포인트 (예: `/api/inquiry`). intake API 직접 지정 금지. */\n action: z.string(),\n vertical: z.enum([\"consulting\", \"medical\", \"tax\", \"legal\"]).optional(),\n heading: z.string().optional(),\n description: z.string().optional(),\n submitLabel: z.string().optional(),\n }),\n});\n\nconst TrustMetricSchema = MetricSchema;\n\nconst TrustCredentialSchema = z.object({\n name: z.string(),\n issuer: z.string().optional(),\n kind: z.enum([\"license\", \"education\", \"association\"]).optional(),\n image: z.string().optional(),\n /** `credential-list` 전용 — hairline 행의 tabular 연도. */\n year: z.string().optional(),\n});\n\nconst TrustLogoSchema = z.object({\n src: z.string(),\n alt: z.string(),\n href: z.string().optional(),\n});\n\nconst LegacyTrustLogoSchema = z\n .object({\n image: z.string(),\n alt: z.string(),\n href: z.string().optional(),\n })\n .transform((logo) => ({\n src: logo.image,\n alt: logo.alt,\n ...(logo.href ? { href: logo.href } : {}),\n }));\n\n/** logo-cloud/logo-marquee 공통 슬롯. legacy `{image, alt, href?}` 도 `{src, alt, href?}` 로 정규화한다. */\nconst TrustLogoSlotSchema = z.union([TrustLogoSchema, LegacyTrustLogoSchema]);\n\nconst TrustChartSchema = z.object({\n type: z.enum([\"bar\", \"line\"]).default(\"bar\"),\n title: z.string(),\n description: z.string().optional(),\n points: z\n .array(\n z.object({\n label: z.string(),\n value: z.number(),\n display: z.string().optional(),\n }),\n )\n .min(1)\n .max(12),\n});\n\nexport const TrustSectionSchema = SectionBase.extend({\n type: z.literal(\"trust\"),\n /**\n * C10 §1-3/1-4 — `logo-marquee`(C8 Marquee 소비, 흑백/저채도 로고 스트립) ·\n * `credential-list`(연도 tabular + hairline 자격 리스트) 추가.\n */\n variant: z.enum(SECTION_VARIANTS.trust).default(\"stat-band\"),\n props: z.object({\n heading: z.string().optional(),\n metrics: z.array(TrustMetricSchema).min(2).max(4).optional(),\n credentials: z.array(TrustCredentialSchema).max(8).optional(),\n logos: z.array(TrustLogoSlotSchema).max(8).optional(),\n /** `logo-marquee` 전용 — logo-cloud 와 동일한 `{src, alt, href?}` 슬롯. 최소 3개. */\n marqueeLogos: z.array(TrustLogoSlotSchema).min(3).max(16).optional(),\n chart: TrustChartSchema.optional(),\n disclaimer: z.string().optional(),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant === \"stat-band\" && !section.props.metrics) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"metrics\"],\n message: \"stat-band trust sections require 2-4 metrics\",\n });\n }\n if (section.variant === \"logo-cloud\" && !section.props.logos) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"logos\"],\n message: \"logo-cloud trust sections require logos\",\n });\n }\n if (\n (section.variant === \"feature-grid\" || section.variant === \"credential-list\") &&\n !section.props.credentials\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"credentials\"],\n message: `${section.variant} trust sections require credentials`,\n });\n }\n if (section.variant === \"logo-marquee\" && !section.props.marqueeLogos) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"marqueeLogos\"],\n message: \"logo-marquee trust sections require marqueeLogos (min 3)\",\n });\n }\n if (section.variant === \"dashboard\" && !section.props.chart) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"chart\"],\n message: \"dashboard trust sections require chart\",\n });\n }\n});\n\nconst ContactHoursSchema = z.object({\n day: z.string(),\n open: z.string(),\n close: z.string(),\n note: z.string().optional(),\n});\n\nconst ContactChannelSchema = z.object({\n label: z.string(),\n description: z.string().optional(),\n value: z.string().optional(),\n href: z.string().optional(),\n actionLabel: z.string().optional(),\n iconSrc: z.string().optional(),\n iconAlt: z.string().optional(),\n kind: z.string().optional(),\n});\n\nconst ContactMapSchema = z.object({\n facadeImageSrc: z.string().optional(),\n facadeImageAlt: z.string().optional(),\n provider: z.string().optional(),\n embedUrl: z.string().optional(),\n externalUrl: z.string().optional(),\n lat: z.number().optional(),\n lng: z.number().optional(),\n /**\n * 지도 로드 모드(ADR-0086 P2 D3, chambarun `.map-facade` 클릭-투-로드 이식).\n * 기본 \"lazy\"(기존 동작 그대로 — 실지도면 즉시 iframe `loading=\"lazy\"`).\n * \"click\" 은 정적 프리뷰(주소·지도 열기 버튼)만 먼저 그리고, 클릭 시에만\n * iframe 을 마운트한다(외부 지도 스크립트/네트워크 요청을 사용자 의도\n * 전까지 미루는 성능·프라이버시 절충 — additive, 기존 사이트는 영향 없음).\n */\n mapLoad: z.enum([\"lazy\", \"click\"]).optional(),\n});\n\nconst ContactActionSchema = z.object({\n label: z.string(),\n href: z.string(),\n kind: z.string().optional(),\n});\n\nexport const ContactSectionSchema = SectionBase.extend({\n type: z.literal(\"contact\"),\n variant: z.enum(SECTION_VARIANTS.contact).default(\"form-map-5050\"),\n props: z.object({\n heading: z.string().optional(),\n form: z.object({\n enabled: z.boolean(),\n vertical: z.enum([\"medical\", \"tax\", \"legal\", \"consulting\"]).optional(),\n }),\n phone: z.string().optional(),\n address: z.string().optional(),\n hours: z.array(ContactHoursSchema).max(8).optional(),\n mapEmbedUrl: z.string().optional(),\n map: ContactMapSchema.optional(),\n actions: z.array(ContactActionSchema).max(6).optional(),\n /** `channel-cards` 전용 — 카드당 하나의 행동. */\n channels: z.array(ContactChannelSchema).min(3).max(6).optional(),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant !== \"channel-cards\") return;\n if (!section.props.channels) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"channels\"],\n message: \"channel-cards contact sections require 3-6 channels\",\n });\n }\n section.props.channels?.forEach((channel, index) => {\n if (channel.iconSrc && !channel.iconAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"channels\", index, \"iconAlt\"],\n message: \"iconSrc 가 있으면 iconAlt 가 필요합니다.\",\n });\n }\n });\n});\n\nconst PracticeDaySchema = z.enum([\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\",\n \"sunday\",\n]);\n\nconst HoursBadgeSlotsSchema = z.object({\n lunch: z.string().optional(),\n evening: z.string().optional(),\n closed: z.string().optional(),\n extra: z.array(z.string()).max(4).optional(),\n});\n\nconst HoursRowSchema = z.object({\n day: PracticeDaySchema,\n label: z.string(),\n hours: z.string(),\n lunchHours: z.string().optional(),\n badges: HoursBadgeSlotsSchema.optional(),\n note: z.string().optional(),\n});\n\nexport const HoursSectionSchema = SectionBase.extend({\n type: z.literal(\"hours\"),\n variant: z.enum(SECTION_VARIANTS.hours).default(\"table\"),\n props: z.object({\n eyebrow: z.string().optional(),\n heading: z.string().optional(),\n description: z.string().optional(),\n rows: z.array(HoursRowSchema).min(1).max(14),\n note: z.string().optional(),\n disclaimer: z.string().optional(),\n }),\n});\n\nconst PricingItemSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n price: z.string(),\n condition: z.string().optional(),\n});\n\nconst PricingGroupSchema = z.object({\n label: z.string(),\n items: z.array(PricingItemSchema).min(1).max(12),\n});\n\nexport const PricingSectionSchema = SectionBase.extend({\n type: z.literal(\"pricing\"),\n variant: z.enum(SECTION_VARIANTS.pricing).default(\"table\"),\n props: z.object({\n eyebrow: z.string().optional(),\n heading: z.string().optional(),\n description: z.string().optional(),\n groups: z.array(PricingGroupSchema).min(1).max(6),\n disclaimer: z.string().min(1),\n }),\n});\n\nconst ProcessTimelineItemSchema = z.object({\n eyebrow: z.string().optional(),\n title: z.string(),\n description: z.string(),\n duration: z.string().optional(),\n status: z.enum([\"complete\", \"current\", \"pending\"]).optional(),\n statusLabel: z.string().optional(),\n mark: z.enum([\"check\", \"dot\", \"none\"]).optional(),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n});\n\nexport const ProcessSectionSchema = SectionBase.extend({\n type: z.literal(\"process\"),\n variant: z.enum(SECTION_VARIANTS.process).default(\"horizontal\"),\n props: z.object({\n eyebrow: z.string().optional(),\n heading: z.string().optional(),\n description: z.string().optional(),\n items: z.array(ProcessTimelineItemSchema).min(2).max(8),\n connector: z.enum([\"line\", \"rail\"]).optional(),\n disclaimer: z.string().optional(),\n }),\n}).superRefine((section, ctx) => {\n section.props.items.forEach((item, index) => {\n if (item.imageSrc && !item.imageAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"items\", index, \"imageAlt\"],\n message: \"imageSrc 가 있으면 imageAlt 가 필요합니다.\",\n });\n }\n });\n});\n\nconst BeforeAfterImageSchema = z.object({\n src: z.string(),\n alt: z.string(),\n});\n\nexport const BeforeAfterSectionSchema = SectionBase.extend({\n type: z.literal(\"before-after\"),\n variant: z.enum(SECTION_VARIANTS[\"before-after\"]).default(\"slider\"),\n props: z.object({\n beforeImage: BeforeAfterImageSchema,\n afterImage: BeforeAfterImageSchema,\n beforeLabel: z.string().default(\"시공 전\"),\n afterLabel: z.string().default(\"시공 후\"),\n heading: z.string().optional(),\n description: z.string().optional(),\n disclaimer: z.string().optional(),\n }),\n});\n\n/** `quote-rail` 전용 hairline 인용 카드 하한 — 스크롤 레일이 자연스러운 최소치. */\nconst TestimonialItemSchema = z.object({\n quote: z.string().min(1).max(280),\n author: z.string().min(1),\n /** 케이스 라벨(예: \"허리디스크 · 비수술 치료 3개월\"). */\n context: z.string().optional(),\n rating: z.number().min(0).max(5).optional(),\n source: z.string().optional(),\n visitedAt: z.string().optional(),\n item: z.string().optional(),\n reply: z.string().optional(),\n});\n\n/**\n * C11 — 후기(testimonials) 섹션. 의료 vertical 치료경험담 광고는 규제 대상\n * (의료법 §56 ②4, `@roottale/audit-medical` MED-007) — `disclaimer` 슬롯은\n * 법정 고지문 전용, 스토리 fixture 카피는 심의-안전 톤으로 작성 의무\n * (roottale-docs `premium-component-backlog.md` C11).\n */\nexport const TestimonialsSectionSchema = SectionBase.extend({\n type: z.literal(\"testimonials\"),\n /**\n * `editorial-quote`(기본): 대형 인용 타이포 에디토리얼 — 1개면 중앙 정렬\n * 스테이지, 2개 이상이면 세로 스택(hairline 구분). `quote-rail`: 가로\n * 스크롤 레일(hairline 좌측 보더 컬럼, 균등 그림자 카드 금지).\n * `star-compact`(W8-7): 별점 중심 컴팩트 그리드 — 평균 별점 요약 밴드 +\n * hairline 행. 별점은 CSS 타이포 글리프(이모지·이미지 금지), 색은 전부\n * 토큰(variant×토큰 직교, 채움 폭만 데이터 주도 커스텀 프로퍼티).\n */\n variant: z.enum(SECTION_VARIANTS.testimonials).default(\"editorial-quote\"),\n props: z.object({\n heading: z.string(),\n description: z.string().optional(),\n items: z.array(TestimonialItemSchema).min(1).max(12),\n marquee: z.boolean().optional(),\n /** 법정 고지 슬롯(\"개인 경험이며 효과는 다를 수 있습니다\" 류). */\n disclaimer: z.string().optional(),\n }),\n});\n\nconst TeamMemberSchema = z\n .object({\n name: z.string().min(1),\n title: z.string().min(1),\n photoSrc: z.string().optional(),\n photoAlt: z.string().optional(),\n specialties: z.array(z.string().min(1)).min(1).max(6).optional(),\n credentials: z.array(z.string().min(1)).min(1).max(8).optional(),\n bio: z.string().max(360).optional(),\n href: z.string().optional(),\n ctaLabel: z.string().optional(),\n })\n .superRefine((member, ctx) => {\n if (member.photoSrc && !member.photoAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"photoAlt\"],\n message: \"photoSrc 가 있으면 photoAlt 가 필요합니다.\",\n });\n }\n });\n\nexport const TeamSectionSchema = SectionBase.extend({\n type: z.literal(\"team\"),\n variant: z.enum(SECTION_VARIANTS.team).default(\"grid\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n members: z.array(TeamMemberSchema).min(2).max(8),\n }),\n});\n\nconst ExpertiseItemSchema = z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n kicker: z.string().optional(),\n title: z.string().min(1),\n body: z.string().min(1),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n tags: z.array(z.string().min(1)).max(8).optional(),\n href: z.string().optional(),\n ctaLabel: z.string().optional(),\n })\n .superRefine((item, ctx) => {\n if (item.imageSrc && !item.imageAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"imageAlt\"],\n message: \"imageSrc 가 있으면 imageAlt 가 필요합니다.\",\n });\n }\n });\n\nexport const ExpertiseSectionSchema = SectionBase.extend({\n type: z.literal(\"expertise\"),\n variant: z.enum(SECTION_VARIANTS.expertise).default(\"tab-stage\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n items: z.array(ExpertiseItemSchema).min(2).max(6),\n }),\n});\n\nconst SpaceItemSchema = z.object({\n id: z.string().min(1),\n label: z.string().min(1),\n kicker: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n imageSrc: z.string().min(1),\n imageAlt: z.string().min(1),\n tags: z.array(z.string().min(1)).max(8).optional(),\n});\n\nexport const SpacesSectionSchema = SectionBase.extend({\n type: z.literal(\"spaces\"),\n variant: z.enum(SECTION_VARIANTS.spaces).default(\"stage\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n spaces: z.array(SpaceItemSchema).min(2).max(8),\n disclaimer: z.string().optional(),\n }),\n});\n\nconst ConcernRegionSchema = z.object({\n id: z.string().min(1),\n label: z.string().min(1),\n x: z.number().min(0).max(100).optional(),\n y: z.number().min(0).max(100).optional(),\n title: z.string().min(1),\n description: z.string().min(1),\n items: z.array(z.string().min(1)).max(8).optional(),\n href: z.string().optional(),\n ctaLabel: z.string().optional(),\n});\n\nexport const ConcernsSectionSchema = SectionBase.extend({\n type: z.literal(\"concerns\"),\n variant: z.enum(SECTION_VARIANTS.concerns).default(\"figure-map\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n figureImage: z.string().optional(),\n figureAlt: z.string().optional(),\n regions: z.array(ConcernRegionSchema).min(3).max(10),\n disclaimer: z.string().min(1),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant !== \"figure-map\") return;\n if (!section.props.figureImage) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"figureImage\"],\n message: \"figure-map concerns 는 figureImage 가 필요합니다.\",\n });\n }\n if (!section.props.figureAlt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"figureAlt\"],\n message: \"figure-map concerns 는 figureAlt 가 필요합니다.\",\n });\n }\n section.props.regions.forEach((region, index) => {\n if (typeof region.x !== \"number\") {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"regions\", index, \"x\"],\n message: \"figure-map concerns region 은 x 좌표가 필요합니다.\",\n });\n }\n if (typeof region.y !== \"number\") {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"regions\", index, \"y\"],\n message: \"figure-map concerns region 은 y 좌표가 필요합니다.\",\n });\n }\n });\n});\n\n/** customSections 용 fallback — `type` 자유, props 는 직렬화만 강제. */\nexport const CustomSectionSchema = SectionBase;\n\nexport const BUILTIN_SECTION_SCHEMAS: Record<string, z.ZodTypeAny> = {\n hero: HeroSectionSchema,\n \"rich-text\": RichTextSectionSchema,\n services: ServicesSectionSchema,\n faq: FaqSectionSchema,\n \"cta-banner\": CtaBannerSectionSchema,\n \"blog-list\": BlogListSectionSchema,\n \"lead-form\": LeadFormSectionSchema,\n trust: TrustSectionSchema,\n contact: ContactSectionSchema,\n hours: HoursSectionSchema,\n pricing: PricingSectionSchema,\n process: ProcessSectionSchema,\n \"before-after\": BeforeAfterSectionSchema,\n testimonials: TestimonialsSectionSchema,\n team: TeamSectionSchema,\n expertise: ExpertiseSectionSchema,\n spaces: SpacesSectionSchema,\n concerns: ConcernsSectionSchema,\n footer: FooterSectionSchema,\n};\n\n/** 빌트인 type 목록 (renderer registry 와 1:1). */\nexport const BUILTIN_SECTION_TYPES = Object.keys(\n BUILTIN_SECTION_SCHEMAS,\n) as readonly string[];\n\nexport type HeroSection = z.output<typeof HeroSectionSchema>;\nexport type RichTextSection = z.output<typeof RichTextSectionSchema>;\nexport type ServicesSection = z.output<typeof ServicesSectionSchema>;\nexport type FaqSection = z.output<typeof FaqSectionSchema>;\nexport type CtaBannerSection = z.output<typeof CtaBannerSectionSchema>;\nexport type BlogListSection = z.output<typeof BlogListSectionSchema>;\nexport type LeadFormSection = z.output<typeof LeadFormSectionSchema>;\nexport type TrustSection = z.output<typeof TrustSectionSchema>;\nexport type ContactSection = z.output<typeof ContactSectionSchema>;\nexport type HoursSection = z.output<typeof HoursSectionSchema>;\nexport type PricingSection = z.output<typeof PricingSectionSchema>;\nexport type ProcessSection = z.output<typeof ProcessSectionSchema>;\nexport type BeforeAfterSection = z.output<typeof BeforeAfterSectionSchema>;\nexport type TestimonialsSection = z.output<typeof TestimonialsSectionSchema>;\nexport type TeamSection = z.output<typeof TeamSectionSchema>;\nexport type ExpertiseSection = z.output<typeof ExpertiseSectionSchema>;\nexport type SpacesSection = z.output<typeof SpacesSectionSchema>;\nexport type ConcernsSection = z.output<typeof ConcernsSectionSchema>;\nexport type FooterSection = z.output<typeof FooterSectionSchema>;\nexport type CustomSection = z.output<typeof CustomSectionSchema>;\n\n/** 파싱 결과 섹션 (defaults 적용 후). */\nexport type Section =\n | HeroSection\n | RichTextSection\n | ServicesSection\n | FaqSection\n | CtaBannerSection\n | BlogListSection\n | LeadFormSection\n | TrustSection\n | ContactSection\n | HoursSection\n | PricingSection\n | ProcessSection\n | BeforeAfterSection\n | TestimonialsSection\n | TeamSection\n | ExpertiseSection\n | SpacesSection\n | ConcernsSection\n | FooterSection\n | CustomSection;\n\n/** config 작성용 섹션 (defaults 생략 가능). `site.config.ts` 에서 이 타입 사용. */\nexport type SectionInput =\n | z.input<typeof HeroSectionSchema>\n | z.input<typeof RichTextSectionSchema>\n | z.input<typeof ServicesSectionSchema>\n | z.input<typeof FaqSectionSchema>\n | z.input<typeof CtaBannerSectionSchema>\n | z.input<typeof BlogListSectionSchema>\n | z.input<typeof LeadFormSectionSchema>\n | z.input<typeof TrustSectionSchema>\n | z.input<typeof ContactSectionSchema>\n | z.input<typeof HoursSectionSchema>\n | z.input<typeof PricingSectionSchema>\n | z.input<typeof ProcessSectionSchema>\n | z.input<typeof BeforeAfterSectionSchema>\n | z.input<typeof TestimonialsSectionSchema>\n | z.input<typeof TeamSectionSchema>\n | z.input<typeof ExpertiseSectionSchema>\n | z.input<typeof SpacesSectionSchema>\n | z.input<typeof ConcernsSectionSchema>\n | z.input<typeof FooterSectionSchema>\n | z.input<typeof CustomSectionSchema>;\n\n/**\n * 섹션 스키마 — 빌트인 type 이면 해당 스키마로 *강제* 검증 (union fallthrough 로\n * 빌트인 오타이핑이 CustomSection 으로 조용히 통과하는 것을 차단), 미지 type 은\n * CustomSection 으로 통과 (렌더러가 customSections registry 로 해석).\n */\nexport const SectionSchema: z.ZodType<Section, z.ZodTypeDef, SectionInput> = z\n .object({ type: z.string() })\n .passthrough()\n .transform((section, ctx) => {\n const schema = BUILTIN_SECTION_SCHEMAS[section.type] ?? CustomSectionSchema;\n const result = schema.safeParse(section);\n if (!result.success) {\n for (const issue of result.error.issues) ctx.addIssue(issue);\n return z.NEVER;\n }\n return result.data as Section;\n }) as unknown as z.ZodType<Section, z.ZodTypeDef, SectionInput>;\n\n/**\n * 콘텐츠 Collection (ADR-0060) — 같은 글 풀(post)을 스트림으로 분리(공지 게시판 /\n * 블로그 그리드 등). `routes.ts` 의 `RouteCollection` 의 타입드·직렬화 상위집합 —\n * 한 category slug 는 정확히 하나의 collection 에 속해야 한다(다대일 = 라우팅 모호).\n * `layout` 은 레지스트리 키(open string) — 신규 레이아웃 추가 시 스키마 불변.\n * 선언 순서 = 라우팅 우선순위(`resolvePostCollection`).\n */\nexport const CollectionSchema = z.object({\n /** 안정 식별자 (라우트/가드/파생 키). */\n key: z.string(),\n /** 작성자 카드·nav 라벨. */\n label: z.string(),\n /** 라우트 base path (예: \"/notice\" | \"/blog\"). 앞 슬래시 포함. */\n basePath: z.string(),\n /** 레이아웃 레지스트리 키 (board=게시판, grid=카드 그리드, …). 미지 키는 렌더러 fallback. */\n layout: z.string().default(\"grid\"),\n /** 이 스트림에 속하는 category slug 들. */\n categories: z.array(z.string()).default([]),\n /** RSS 피드 포함. */\n feed: z.boolean().default(false),\n /** `${basePath}/categories/{slug}` 아카이브 생성. */\n archives: z.boolean().default(false),\n /** 글별 동적 OG. */\n og: z.boolean().default(false),\n /** 작성 UX: 하위 카테고리 1개 필수(미선택 발행 차단). */\n requireSubcategory: z.boolean().default(false),\n /** 작성 UX: 상단고정 토글 노출. */\n pinning: z.boolean().default(false),\n /** nav/카드 아이콘 키 (선택). */\n icon: z.string().optional(),\n});\nexport type Collection = z.output<typeof CollectionSchema>;\n\nexport const SiteConfigSchema = z.object({\n /**\n * `SiteConfig` 계약 버전 (ADR-0086 D2) — 팩 manifest 의 `siteConfigVersion`\n * 과 비교해 팩↔renderer 호환성을 표현한다. optional — 하위 호환(기존 고객\n * `site.config.ts` 에는 이 필드가 없다). 미지정은 버전 1 로 해석한다\n * (`resolveSiteConfigSchemaVersion`).\n */\n schemaVersion: z.number().int().positive().optional(),\n site: z.object({\n /** canonical origin. 생략 시 소비측이 `SITE_URL` env 등으로 해석. */\n url: z.string().url().optional(),\n title: z.string(),\n description: z.string().optional(),\n locale: z.string().default(\"ko-KR\"),\n og: z.object({ image: z.string().optional() }).optional(),\n }),\n /**\n * 콘텐츠 스트림 분리 (ADR-0060). 비우면 단일 /blog(레거시 동작). 선언 순서 =\n * 라우팅 우선순위. `routes.ts` 팩토리에 `collections` 로 전달해 sitemap/feed/\n * 아카이브를 스트림별로 파생.\n */\n collections: z.array(CollectionSchema).default([]),\n /** 사이트 전역 토큰 override (`--rt-*` CSS 변수 → 값). 비우면 admin /design SSoT. */\n theme: z.record(z.string()).default({}),\n /**\n * 테마 방향(Theme Directions, `@roottale/tokens` 프리셋 id — 예: \"d1-editorial\").\n * root layout 이 `<html data-rt-direction=\"…\">` 로 부여해\n * `@roottale/tokens/theme-directions.css` 의 `[data-rt-direction=\"…\"]` 규칙을\n * 활성화한다(roottale-docs `docs/design/theme-directions.md`). 값은 문자열로만\n * 검증한다 — `@roottale/cms-renderer-next` 는 `@roottale/tokens` 를 직접\n * 의존하지 않으므로 유효 id 집합은 `@roottale/tokens/theme-directions` 의\n * `ThemeDirectionId` 를 참조해 `site.config.ts` 작성 시점에 타입으로 좁힌다.\n * 미지정 시 속성 자체가 안 붙어 완전히 무변화(기존 동작).\n */\n direction: z.string().optional(),\n nav: z.array(NavItemSchema).default([]),\n /** path(\"/\", \"/services\", …) → 섹션 배열. */\n pages: z.record(z.array(SectionSchema)),\n});\n\nexport type SiteConfig = z.output<typeof SiteConfigSchema>;\n/** config 작성용 (defaults 생략 가능). */\nexport type SiteConfigInput = Omit<\n z.input<typeof SiteConfigSchema>,\n \"pages\"\n> & { pages: Record<string, SectionInput[]> };\n\n/** config 검증 + defaults 적용. 실패 시 ZodError throw (빌드 타임 fail-fast 용). */\nexport function parseSiteConfig(input: unknown): SiteConfig {\n return SiteConfigSchema.parse(input);\n}\n\n/**\n * `site.config.ts` 작성 헬퍼 — 타입 추론 + 즉시 검증.\n *\n * ```ts\n * import { defineSiteConfig } from \"@roottale/cms-renderer-next/templates\";\n * export default defineSiteConfig({ site: { title: \"…\" }, pages: { \"/\": [ … ] } });\n * ```\n */\nexport function defineSiteConfig(input: SiteConfigInput): SiteConfig {\n return parseSiteConfig(input);\n}\n"],"mappings":"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","names":[]}
@@ -1,59 +0,0 @@
1
- "use client";
2
-
3
- // src/templates/use-roving-tablist.ts
4
- import { useCallback, useRef } from "react";
5
- function nextRovingIndex(key, currentIndex, count, orientation = "both") {
6
- if (count <= 0) return null;
7
- const horizontal = orientation !== "vertical";
8
- const vertical = orientation !== "horizontal";
9
- const wrap = (index) => (index % count + count) % count;
10
- switch (key) {
11
- case "ArrowRight":
12
- return horizontal ? wrap(currentIndex + 1) : null;
13
- case "ArrowLeft":
14
- return horizontal ? wrap(currentIndex - 1) : null;
15
- case "ArrowDown":
16
- return vertical ? wrap(currentIndex + 1) : null;
17
- case "ArrowUp":
18
- return vertical ? wrap(currentIndex - 1) : null;
19
- case "Home":
20
- return count > 0 ? 0 : null;
21
- case "End":
22
- return count > 0 ? count - 1 : null;
23
- default:
24
- return null;
25
- }
26
- }
27
- function useRovingTablist({
28
- count,
29
- selectedIndex,
30
- onSelect,
31
- orientation = "both"
32
- }) {
33
- const tabRefs = useRef([]);
34
- const getTabProps = useCallback(
35
- (index) => ({
36
- tabIndex: index === selectedIndex ? 0 : -1,
37
- ref: (el) => {
38
- tabRefs.current[index] = el;
39
- }
40
- }),
41
- [selectedIndex]
42
- );
43
- const onKeyDown = useCallback(
44
- (event) => {
45
- const nextIndex = nextRovingIndex(event.key, selectedIndex, count, orientation);
46
- if (nextIndex === null || nextIndex === selectedIndex) return;
47
- event.preventDefault();
48
- onSelect(nextIndex);
49
- tabRefs.current[nextIndex]?.focus();
50
- },
51
- [selectedIndex, count, orientation, onSelect]
52
- );
53
- return { getTabProps, onKeyDown };
54
- }
55
-
56
- export {
57
- useRovingTablist
58
- };
59
- //# sourceMappingURL=chunk-YLSQAT6B.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/templates/use-roving-tablist.ts"],"sourcesContent":["\"use client\";\n\n/**\n * WAI-ARIA APG `tablist` 키보드 계약(roving tabindex, 단일 tab stop) 공용 로직\n * (D4). `role=\"tablist\"` 컨테이너 + `role=\"tab\"` 버튼 N개로 이뤄진 어떤\n * client 컴포넌트에서도 재사용한다(현재 소비자: `expertise-client.tsx`) —\n * 파일별 중복 구현 대신 이 hook 한 곳에서. 별도 tsup client entry 는 두지\n * 않는다 — 소비하는 client 컴포넌트의 청크에 그대로 인라인되며, 그 컴포넌트가\n * 이미 \"use client\" 경계를 갖고 있으므로 문제 없다(hero-carousel-client 류의\n * 독립 entry 는 서버 전용 번들과의 분리가 목적인데, 이 hook 은 항상 client\n * 컴포넌트에서만 import 되므로 그 문제가 생기지 않는다).\n *\n * 계약:\n * - Arrow(orientation 에 따라 Left/Right 그리고|또는 Up/Down) → 이전/다음 탭.\n * activation 방식은 automatic(포커스 이동 = 선택 이동, 별도 Enter 불필요).\n * - Home/End → 처음/마지막 탭.\n * - 선택된 탭만 tabIndex=0, 나머지는 -1 — Tab 키는 tablist 진입·이탈에 한\n * 번씩만 소비하고 내부 이동은 화살표키로.\n *\n * `nextRovingIndex` 는 DOM/React 의존이 없는 순수 함수 — 키보드 매핑 자체는\n * 이 함수 하나로 단위 테스트한다(`use-roving-tablist.test.ts`).\n */\nimport { useCallback, useRef, type KeyboardEvent } from \"react\";\n\nexport type RovingTablistOrientation = \"horizontal\" | \"vertical\" | \"both\";\n\nexport function nextRovingIndex(\n key: string,\n currentIndex: number,\n count: number,\n orientation: RovingTablistOrientation = \"both\",\n): number | null {\n if (count <= 0) return null;\n const horizontal = orientation !== \"vertical\";\n const vertical = orientation !== \"horizontal\";\n const wrap = (index: number) => ((index % count) + count) % count;\n\n switch (key) {\n case \"ArrowRight\":\n return horizontal ? wrap(currentIndex + 1) : null;\n case \"ArrowLeft\":\n return horizontal ? wrap(currentIndex - 1) : null;\n case \"ArrowDown\":\n return vertical ? wrap(currentIndex + 1) : null;\n case \"ArrowUp\":\n return vertical ? wrap(currentIndex - 1) : null;\n case \"Home\":\n return count > 0 ? 0 : null;\n case \"End\":\n return count > 0 ? count - 1 : null;\n default:\n return null;\n }\n}\n\nexport interface UseRovingTablistOptions {\n readonly count: number;\n readonly selectedIndex: number;\n readonly onSelect: (index: number) => void;\n /** 기본 `both` — Left/Right·Up/Down 전부 허용(레이아웃이 반응형으로 축·행을\n * 오갈 수 있는 렌더러 특성상 관대하게 둔다). 필요 시 한 축으로 좁힐 수 있다. */\n readonly orientation?: RovingTablistOrientation;\n}\n\nexport interface RovingTabProps<T extends HTMLElement> {\n readonly tabIndex: 0 | -1;\n readonly ref: (el: T | null) => void;\n}\n\nexport interface UseRovingTablistResult<T extends HTMLElement> {\n readonly getTabProps: (index: number) => RovingTabProps<T>;\n readonly onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n}\n\nexport function useRovingTablist<T extends HTMLElement = HTMLElement>({\n count,\n selectedIndex,\n onSelect,\n orientation = \"both\",\n}: UseRovingTablistOptions): UseRovingTablistResult<T> {\n const tabRefs = useRef<(T | null)[]>([]);\n\n const getTabProps = useCallback(\n (index: number): RovingTabProps<T> => ({\n tabIndex: index === selectedIndex ? 0 : -1,\n ref: (el: T | null) => {\n tabRefs.current[index] = el;\n },\n }),\n [selectedIndex],\n );\n\n const onKeyDown = useCallback(\n (event: KeyboardEvent<HTMLElement>) => {\n const nextIndex = nextRovingIndex(event.key, selectedIndex, count, orientation);\n if (nextIndex === null || nextIndex === selectedIndex) return;\n event.preventDefault();\n onSelect(nextIndex);\n tabRefs.current[nextIndex]?.focus();\n },\n [selectedIndex, count, orientation, onSelect],\n );\n\n return { getTabProps, onKeyDown };\n}\n"],"mappings":";AAsBA,SAAS,aAAa,cAAkC;AAIjD,SAAS,gBACd,KACA,cACA,OACA,cAAwC,QACzB;AACf,MAAI,SAAS,EAAG,QAAO;AACvB,QAAM,aAAa,gBAAgB;AACnC,QAAM,WAAW,gBAAgB;AACjC,QAAM,OAAO,CAAC,WAAoB,QAAQ,QAAS,SAAS;AAE5D,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO,aAAa,KAAK,eAAe,CAAC,IAAI;AAAA,IAC/C,KAAK;AACH,aAAO,aAAa,KAAK,eAAe,CAAC,IAAI;AAAA,IAC/C,KAAK;AACH,aAAO,WAAW,KAAK,eAAe,CAAC,IAAI;AAAA,IAC7C,KAAK;AACH,aAAO,WAAW,KAAK,eAAe,CAAC,IAAI;AAAA,IAC7C,KAAK;AACH,aAAO,QAAQ,IAAI,IAAI;AAAA,IACzB,KAAK;AACH,aAAO,QAAQ,IAAI,QAAQ,IAAI;AAAA,IACjC;AACE,aAAO;AAAA,EACX;AACF;AAqBO,SAAS,iBAAsD;AAAA,EACpE;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAuD;AACrD,QAAM,UAAU,OAAqB,CAAC,CAAC;AAEvC,QAAM,cAAc;AAAA,IAClB,CAAC,WAAsC;AAAA,MACrC,UAAU,UAAU,gBAAgB,IAAI;AAAA,MACxC,KAAK,CAAC,OAAiB;AACrB,gBAAQ,QAAQ,KAAK,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,UAAsC;AACrC,YAAM,YAAY,gBAAgB,MAAM,KAAK,eAAe,OAAO,WAAW;AAC9E,UAAI,cAAc,QAAQ,cAAc,cAAe;AACvD,YAAM,eAAe;AACrB,eAAS,SAAS;AAClB,cAAQ,QAAQ,SAAS,GAAG,MAAM;AAAA,IACpC;AAAA,IACA,CAAC,eAAe,OAAO,aAAa,QAAQ;AAAA,EAC9C;AAEA,SAAO,EAAE,aAAa,UAAU;AAClC;","names":[]}
@@ -1,313 +0,0 @@
1
- "use client";
2
-
3
- // ../ui-react/src/components/scroll/AnimateOnScroll.tsx
4
- import {
5
- useEffect,
6
- createElement,
7
- useState
8
- } from "react";
9
- import { motion, useReducedMotion } from "motion/react";
10
-
11
- // ../ui-react/src/components/motion/variants.ts
12
- var ENTRANCE_EASE = [0, 0, 0.2, 1];
13
- var ENTRANCE_DURATION = 0.5;
14
- var STAGGER_STEP = 0.08;
15
- var DEFAULT_VIEWPORT_MARGIN = "-80px";
16
- var entranceTransition = {
17
- duration: ENTRANCE_DURATION,
18
- ease: ENTRANCE_EASE
19
- };
20
- var ENTRANCE_KEYFRAMES = {
21
- "fade-up": { hidden: { opacity: 0, y: 16 }, visible: { opacity: 1, y: 0 } },
22
- fade: { hidden: { opacity: 0 }, visible: { opacity: 1 } },
23
- "scale-in": { hidden: { opacity: 0, scale: 0.98 }, visible: { opacity: 1, scale: 1 } }
24
- };
25
- function withEntranceTransition(keyframes) {
26
- return {
27
- hidden: keyframes.hidden,
28
- visible: { ...keyframes.visible, transition: entranceTransition }
29
- };
30
- }
31
- var fadeUp = withEntranceTransition(ENTRANCE_KEYFRAMES["fade-up"]);
32
- var fade = withEntranceTransition(ENTRANCE_KEYFRAMES.fade);
33
- var scaleIn = withEntranceTransition(ENTRANCE_KEYFRAMES["scale-in"]);
34
- var ENTRANCE_VARIANTS = {
35
- "fade-up": fadeUp,
36
- fade,
37
- "scale-in": scaleIn
38
- };
39
- function staggerContainer(step = STAGGER_STEP) {
40
- return {
41
- hidden: {},
42
- visible: { transition: { staggerChildren: step } }
43
- };
44
- }
45
- var staggerItem = fadeUp;
46
-
47
- // ../ui-react/src/components/scroll/AnimateOnScroll.tsx
48
- import { jsx } from "react/jsx-runtime";
49
- function AnimateOnScroll({
50
- children,
51
- className,
52
- style,
53
- variant = "fade-up",
54
- as = "div",
55
- once = true,
56
- margin = DEFAULT_VIEWPORT_MARGIN
57
- }) {
58
- const reducedMotion = useReducedMotion();
59
- const [mounted, setMounted] = useState(false);
60
- useEffect(() => setMounted(true), []);
61
- const supportsInView = mounted && typeof window !== "undefined" && typeof window.IntersectionObserver !== "undefined";
62
- if (reducedMotion || !mounted || !supportsInView) {
63
- return createElement(as, { className, style }, children);
64
- }
65
- const MotionTag = motion[as];
66
- return /* @__PURE__ */ jsx(
67
- MotionTag,
68
- {
69
- className,
70
- style,
71
- initial: "hidden",
72
- whileInView: "visible",
73
- viewport: { once, margin },
74
- variants: ENTRANCE_VARIANTS[variant],
75
- children
76
- }
77
- );
78
- }
79
-
80
- // ../ui-react/dist/components/kinetic/CountUpStat.js
81
- import { createElement as createElement2, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
82
- import { useInView, useReducedMotion as useReducedMotion2 } from "motion/react";
83
- var DEFAULT_LOCALE = "ko-KR";
84
- var DEFAULT_DECIMALS = 0;
85
- var DEFAULT_THRESHOLD = 0.4;
86
- var LONG_DURATION_THRESHOLD = 1e3;
87
- var SHORT_DURATION_MS = 1400;
88
- var LONG_DURATION_MS = 2e3;
89
- function easeOutQuart(progress) {
90
- return 1 - (1 - progress) ** 4;
91
- }
92
- function defaultDurationFor(value) {
93
- return Math.abs(value) >= LONG_DURATION_THRESHOLD ? LONG_DURATION_MS : SHORT_DURATION_MS;
94
- }
95
- function formatWithLocale(value, decimals, locale) {
96
- return new Intl.NumberFormat(locale, {
97
- minimumFractionDigits: decimals,
98
- maximumFractionDigits: decimals
99
- }).format(value);
100
- }
101
- function CountUpStat({ value, prefix = "", suffix = "", decimals = DEFAULT_DECIMALS, locale = DEFAULT_LOCALE, formatter, duration, easing = easeOutQuart, threshold = DEFAULT_THRESHOLD, as = "span", className, style }) {
102
- const reducedMotion = useReducedMotion2();
103
- const [mounted, setMounted] = useState2(false);
104
- const ref = useRef(null);
105
- const inView = useInView(ref, { once: true, amount: threshold });
106
- const [displayValue, setDisplayValue] = useState2(0);
107
- useEffect2(() => setMounted(true), []);
108
- const format = useMemo(() => formatter ?? ((current) => formatWithLocale(current, decimals, locale)), [formatter, decimals, locale]);
109
- useEffect2(() => {
110
- if (reducedMotion || !mounted || !inView) {
111
- return;
112
- }
113
- const durationMs = Math.max(0, duration ?? defaultDurationFor(value));
114
- if (durationMs === 0) {
115
- setDisplayValue(value);
116
- return;
117
- }
118
- const startedAt = performance.now();
119
- let frameId = 0;
120
- function tick(now) {
121
- const progress = Math.min((now - startedAt) / durationMs, 1);
122
- setDisplayValue(value * easing(progress));
123
- if (progress < 1) {
124
- frameId = requestAnimationFrame(tick);
125
- } else {
126
- setDisplayValue(value);
127
- }
128
- }
129
- frameId = requestAnimationFrame(tick);
130
- return () => cancelAnimationFrame(frameId);
131
- }, [duration, easing, inView, mounted, reducedMotion, value]);
132
- const finalText = `${prefix}${format(value)}${suffix}`;
133
- const wrapperStyle = { fontVariantNumeric: "tabular-nums", ...style };
134
- const showAnimated = !reducedMotion && mounted;
135
- if (!showAnimated) {
136
- return createElement2(as, { ref, className, style: wrapperStyle }, finalText);
137
- }
138
- return createElement2(as, { ref, "aria-label": finalText, className, style: wrapperStyle }, createElement2("span", { "aria-hidden": true }, `${prefix}${format(displayValue)}${suffix}`));
139
- }
140
-
141
- // ../ui-react/src/components/motion/Marquee.tsx
142
- import {
143
- useEffect as useEffect3,
144
- useRef as useRef2,
145
- useState as useState3
146
- } from "react";
147
- import {
148
- motion as motion2,
149
- useAnimationFrame,
150
- useMotionValue,
151
- useReducedMotion as useReducedMotion3,
152
- useTransform
153
- } from "motion/react";
154
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
155
- function Marquee({
156
- children,
157
- className,
158
- speedSec = 24,
159
- gap = "2rem",
160
- pauseOnHover = true,
161
- ariaLabel,
162
- direction = "left"
163
- }) {
164
- const reducedMotion = useReducedMotion3();
165
- const [mounted, setMounted] = useState3(false);
166
- const [paused, setPaused] = useState3(false);
167
- const [fillCount, setFillCount] = useState3(1);
168
- const wrapperRef = useRef2(null);
169
- const measureRef = useRef2(null);
170
- useEffect3(() => setMounted(true), []);
171
- useEffect3(() => {
172
- const wrapper = wrapperRef.current;
173
- const measure = measureRef.current;
174
- if (!wrapper || !measure || typeof ResizeObserver === "undefined") return;
175
- const calculate = () => {
176
- const containerWidth = wrapper.offsetWidth;
177
- const setWidth = measure.offsetWidth;
178
- if (setWidth === 0) return;
179
- setFillCount(Math.max(1, Math.ceil(containerWidth / setWidth)));
180
- };
181
- calculate();
182
- const ro = new ResizeObserver(calculate);
183
- ro.observe(wrapper);
184
- return () => ro.disconnect();
185
- }, [children, gap, mounted, reducedMotion]);
186
- const x = useMotionValue(direction === "right" ? -50 : 0);
187
- const transform = useTransform(x, (v) => `translateX(${v}%)`);
188
- useEffect3(() => {
189
- x.set(direction === "right" ? -50 : 0);
190
- }, [direction, x]);
191
- useAnimationFrame((_t, delta) => {
192
- if (reducedMotion || paused) return;
193
- const percentPerMs = 50 / (speedSec * 1e3);
194
- const sign = direction === "right" ? 1 : -1;
195
- let next = x.get() + sign * percentPerMs * delta;
196
- if (direction === "right") {
197
- if (next >= 0) next -= 50;
198
- } else if (next <= -50) {
199
- next += 50;
200
- }
201
- x.set(next);
202
- });
203
- const staticStyle = {
204
- display: "flex",
205
- flexWrap: "wrap",
206
- alignItems: "center",
207
- gap
208
- };
209
- if (reducedMotion || !mounted) {
210
- return /* @__PURE__ */ jsx2("div", { className, "aria-label": ariaLabel, style: staticStyle, children });
211
- }
212
- const copyStyle = {
213
- display: "flex",
214
- alignItems: "center",
215
- gap,
216
- flexShrink: 0,
217
- paddingInlineEnd: gap
218
- };
219
- const reps = Array.from({ length: fillCount }, (_, i) => i);
220
- return /* @__PURE__ */ jsxs(
221
- "div",
222
- {
223
- ref: wrapperRef,
224
- className,
225
- "aria-label": ariaLabel,
226
- style: { overflow: "hidden", inlineSize: "100%" },
227
- onMouseEnter: pauseOnHover ? () => setPaused(true) : void 0,
228
- onMouseLeave: pauseOnHover ? () => setPaused(false) : void 0,
229
- children: [
230
- /* @__PURE__ */ jsx2(
231
- "div",
232
- {
233
- ref: measureRef,
234
- "data-marquee-measure": "",
235
- "aria-hidden": true,
236
- style: { ...copyStyle, position: "absolute", visibility: "hidden", pointerEvents: "none" },
237
- children
238
- }
239
- ),
240
- /* @__PURE__ */ jsxs(
241
- motion2.div,
242
- {
243
- "data-marquee-track": "",
244
- style: { display: "flex", inlineSize: "max-content", willChange: "transform", transform },
245
- children: [
246
- /* @__PURE__ */ jsx2("div", { style: { display: "flex" }, "data-marquee-block": "a", children: reps.map((i) => /* @__PURE__ */ jsx2("div", { style: copyStyle, "aria-hidden": i > 0 ? true : void 0, children }, `a-${i}`)) }),
247
- /* @__PURE__ */ jsx2("div", { style: { display: "flex" }, "data-marquee-block": "b", "aria-hidden": true, children: reps.map((i) => /* @__PURE__ */ jsx2("div", { style: copyStyle, children }, `b-${i}`)) })
248
- ]
249
- }
250
- )
251
- ]
252
- }
253
- );
254
- }
255
-
256
- // ../ui-react/src/components/scroll/StaggerGroup.tsx
257
- import {
258
- useEffect as useEffect4,
259
- createElement as createElement3,
260
- useState as useState4
261
- } from "react";
262
- import { motion as motion3, useReducedMotion as useReducedMotion4 } from "motion/react";
263
- import { jsx as jsx3 } from "react/jsx-runtime";
264
- function StaggerGroup({
265
- children,
266
- className,
267
- style,
268
- as = "div",
269
- staggerChildren = STAGGER_STEP,
270
- once = true,
271
- margin = DEFAULT_VIEWPORT_MARGIN
272
- }) {
273
- const reducedMotion = useReducedMotion4();
274
- const [mounted, setMounted] = useState4(false);
275
- useEffect4(() => setMounted(true), []);
276
- const supportsInView = mounted && typeof window !== "undefined" && typeof window.IntersectionObserver !== "undefined";
277
- if (reducedMotion || !mounted || !supportsInView) {
278
- return createElement3(as, { className, style }, children);
279
- }
280
- const MotionTag = motion3[as];
281
- return /* @__PURE__ */ jsx3(
282
- MotionTag,
283
- {
284
- className,
285
- style,
286
- initial: "hidden",
287
- whileInView: "visible",
288
- viewport: { once, margin },
289
- variants: staggerContainer(staggerChildren),
290
- children
291
- }
292
- );
293
- }
294
- function StaggerItem({ children, className, style, as = "div" }) {
295
- const reducedMotion = useReducedMotion4();
296
- const [mounted, setMounted] = useState4(false);
297
- useEffect4(() => setMounted(true), []);
298
- const supportsInView = mounted && typeof window !== "undefined" && typeof window.IntersectionObserver !== "undefined";
299
- if (reducedMotion || !mounted || !supportsInView) {
300
- return createElement3(as, { className, style }, children);
301
- }
302
- const MotionTag = motion3[as];
303
- return /* @__PURE__ */ jsx3(MotionTag, { className, style, variants: staggerItem, children });
304
- }
305
-
306
- export {
307
- AnimateOnScroll,
308
- CountUpStat,
309
- Marquee,
310
- StaggerGroup,
311
- StaggerItem
312
- };
313
- //# sourceMappingURL=chunk-ZL2NA3QT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../ui-react/src/components/scroll/AnimateOnScroll.tsx","../../ui-react/src/components/motion/variants.ts","../../ui-react/dist/components/kinetic/CountUpStat.js","../../ui-react/src/components/motion/Marquee.tsx","../../ui-react/src/components/scroll/StaggerGroup.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * `AnimateOnScroll` — whileInView 진입 애니메이션 래퍼(ddd `scroll`).\n *\n * 모션 규율(M5, ds-rule-pack.md):\n * - `useReducedMotion()` true → 애니메이션 없이 즉시 표시.\n * - **마운트 전(SSR·하이드레이션 전)에는 initial=\"hidden\" 을 걸지 않는다** —\n * 서버 마크업은 항상 최종(보이는) 상태로 렌더한다. JS 미로드·모션 실패 시에도\n * 콘텐츠가 보여야 하는 계약(M6)을 지키기 위함. 진입 애니메이션은 하이드레이션\n * 이후에만 활성화되며, 이 컴포넌트를 쓰는 섹션은 통상 fold 아래라 \"보였다가\n * 숨는\" 플래시가 사용자에게 노출되지 않는다.\n * - **`IntersectionObserver` 가 없는 환경(구형 브라우저·테스트 스텁 삭제)도\n * 동일 계약을 지킨다.** motion/react 의 `whileInView` Feature 는 마운트 시\n * 무조건 `new IntersectionObserver(...)` 를 호출하므로, 그 생성자가 없으면\n * 관찰이 아예 시작되지 않아 `initial=\"hidden\"` 상태(opacity 0)에서 영원히\n * 멈춘다 — motion 라이브러리 자체는 이 마운트 경로에 폴백이 없다. 여기서\n * `IntersectionObserver` 존재 여부를 직접 확인해 없으면 reduced-motion과\n * 동일하게 최종 상태로 렌더한다(M6).\n *\n * variant(구조 상이 ≥2, M4-f): `fade-up` · `fade` · `scale-in` — 정의는\n * `../motion/variants`(C5, 손 복사 금지) 한 곳에서 import.\n */\nimport {\n useEffect,\n createElement,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport {\n DEFAULT_VIEWPORT_MARGIN,\n ENTRANCE_VARIANTS,\n type EntranceVariant,\n} from \"../motion/variants\";\n\ntype AnimateTag = \"div\" | \"section\" | \"article\" | \"li\" | \"span\";\n\nexport interface AnimateOnScrollProps {\n readonly children?: ReactNode;\n readonly className?: string;\n readonly style?: CSSProperties;\n /** 진입 애니메이션 종류(구조 상이). 기본 `fade-up`. */\n readonly variant?: EntranceVariant;\n /** 렌더할 시맨틱 엘리먼트(래퍼 div 추가 없이 그 자리를 대체). 기본 `div`. */\n readonly as?: AnimateTag;\n /** 한 번만 트리거(재진입 시 재생 안 함). 기본 true. */\n readonly once?: boolean;\n /** whileInView viewport root margin. 기본 `-80px`. */\n readonly margin?: string;\n}\n\nexport function AnimateOnScroll({\n children,\n className,\n style,\n variant = \"fade-up\",\n as = \"div\",\n once = true,\n margin = DEFAULT_VIEWPORT_MARGIN,\n}: AnimateOnScrollProps) {\n const reducedMotion = useReducedMotion();\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n\n // `mounted` already gates the SSR/pre-hydration pass, so this check only\n // ever runs client-side — safe to read `window` synchronously here.\n const supportsInView =\n mounted && typeof window !== \"undefined\" && typeof window.IntersectionObserver !== \"undefined\";\n\n if (reducedMotion || !mounted || !supportsInView) {\n return createElement(as, { className, style }, children);\n }\n\n const MotionTag = motion[as] as typeof motion.div;\n return (\n <MotionTag\n className={className}\n style={style}\n initial=\"hidden\"\n whileInView=\"visible\"\n viewport={{ once, margin }}\n variants={ENTRANCE_VARIANTS[variant]}\n >\n {children}\n </MotionTag>\n );\n}\n","/**\n * 공유 모션 variants — C5(생성물 손 복사 금지)의 정신을 따라 모든 `scroll`/`motion`\n * 프리미티브가 이 한 파일에서 variants 를 파생한다. 각 컴포넌트가 hidden/visible\n * keyframe 객체를 손으로 복사하지 않는다(ds-rule-pack.md C5).\n *\n * 타이밍 출처(C1 — 임의값 대신 토큰 참조):\n * - `ENTRANCE_EASE` = DTCG `--ease-out`(packages/tokens/src/motion.dtcg.json:\n * `cubic-bezier(0, 0, 0.2, 1)`)과 동일 곡선. 등장(entrance)에 쓰는 표준 감속.\n * - `ENTRANCE_DURATION`(0.5s)·`STAGGER_STEP`(0.08s) = 앰비언트 등장 스케일.\n * motion.dtcg 의 100~200ms 는 INP 예산이 걸린 마이크로 인터랙션\n * (hover/focus/button)용 cap 이며(M5), 스크롤 진입 리빌은 입력 지연(INP)\n * 경로가 아니라 그 cap 대상이 아니다 — 별도 앰비언트 스케일로 문서화한다.\n */\nimport type { Variants } from \"motion/react\";\n\n/** = DTCG `--ease-out` (packages/tokens/src/motion.dtcg.json). */\nexport const ENTRANCE_EASE = [0, 0, 0.2, 1] as const;\n/** 앰비언트 등장 지속(초). INP cap(200ms) 대상 아님 — 상단 주석 참조. */\nexport const ENTRANCE_DURATION = 0.5;\n/** 자식 순차 지연(초). */\nexport const STAGGER_STEP = 0.08;\n/** whileInView viewport 기본 margin — 뷰포트 하단 80px 전 트리거. */\nexport const DEFAULT_VIEWPORT_MARGIN = \"-80px\";\n\nconst entranceTransition = {\n duration: ENTRANCE_DURATION,\n ease: ENTRANCE_EASE,\n} as const;\n\nexport type EntranceVariant = \"fade-up\" | \"fade\" | \"scale-in\";\n\n/** 모션 유무와 관계없이 공유하는 진입 시작/완료 상태의 단일 원본. */\nexport const ENTRANCE_KEYFRAMES = {\n \"fade-up\": { hidden: { opacity: 0, y: 16 }, visible: { opacity: 1, y: 0 } },\n fade: { hidden: { opacity: 0 }, visible: { opacity: 1 } },\n \"scale-in\": { hidden: { opacity: 0, scale: 0.98 }, visible: { opacity: 1, scale: 1 } },\n} as const;\n\nfunction withEntranceTransition(keyframes: (typeof ENTRANCE_KEYFRAMES)[EntranceVariant]): Variants {\n return {\n hidden: keyframes.hidden,\n visible: { ...keyframes.visible, transition: entranceTransition },\n };\n}\n\n/** fade + 상승(구조: 이동 O). */\nexport const fadeUp: Variants = withEntranceTransition(ENTRANCE_KEYFRAMES[\"fade-up\"]);\n\n/** fade only(구조: 이동 X). */\nexport const fade: Variants = withEntranceTransition(ENTRANCE_KEYFRAMES.fade);\n\n/** subtle scale(구조: 축소→기본, AI-slop #10 준수 — 0.98 미만 스케일 금지). */\nexport const scaleIn: Variants = withEntranceTransition(ENTRANCE_KEYFRAMES[\"scale-in\"]);\n\n/** `AnimateOnScroll` 의 3 구조 상이 variant(M4-f) — 여기서만 정의. */\nexport const ENTRANCE_VARIANTS: Record<EntranceVariant, Variants> = {\n \"fade-up\": fadeUp,\n fade,\n \"scale-in\": scaleIn,\n};\n\n/** stagger 컨테이너 — 자식 진입을 `step` 간격으로 순차 트리거. */\nexport function staggerContainer(step: number = STAGGER_STEP): Variants {\n return {\n hidden: {},\n visible: { transition: { staggerChildren: step } },\n };\n}\n\n/** stagger 자식 — 컨테이너의 hidden/visible 라벨을 상속받아 fadeUp 으로 진입. */\nexport const staggerItem: Variants = fadeUp;\n","\"use client\";\n/**\n * `CountUpStat` — 뷰포트 진입 시 목표값까지 rAF 카운트업(ddd `kinetic`).\n *\n * 스펙 원본(ADR-0086 P2-크래프트 D3 — chambarun 목업 자체 구현 채택):\n * - `clients/chambarun/mockup/assets/site.js` 89~106행 — `IntersectionObserver`\n * 진입(threshold .4) 1회 트리거 → `requestAnimationFrame` 루프 + ease-out\n * quartic(`1-(1-t)**4`)으로 감속하며 목표값에 안착. 큰 값(≥1000)은 더 오래\n * (2000ms), 작은 값은 1400ms — 이 기본 지속시간 산식을 그대로 이식했다.\n * - `clients/chambarun/DESIGN.md` proof/counter 계약 — \"뷰포트 진입 1회 실행,\n * 이징 곡선\", `prefers-reduced-motion` 즉시 최종값.\n *\n * 접근성(정적 텍스트 + 시각 애니메이션 분리, aria-live 미사용):\n * 카운트가 진행되는 동안에도 outer 엘리먼트의 `aria-label` 은 항상 완성된\n * 최종 문자열이라 스크린리더는 애니메이션 중간값을 절대 읽지 않는다. 실제로\n * 숫자가 올라가는 시각 표현은 `aria-hidden` 내부 span 이 전담한다 —\n * `kinetic/utils.ts` 의 `animatedTextElement`/`staticTextElement` 와 동일한\n * 원칙이나, 이 컴포넌트는 `useInView` 관찰 대상 `ref` 가 필요해 그 헬퍼를\n * 그대로 재사용하지 않고 동일 패턴을 직접 구현한다.\n *\n * 마운트 게이팅은 `RevealText`/`ScrambleText`/`ScrollRevealText` 와 동일 원칙\n * (M6) — SSR·하이드레이션 전·모션 실패 시에도 항상 최종값이 보인다.\n *\n * CLS 방지: `font-variant-numeric: tabular-nums` 는 상속 속성이라 outer\n * wrapper 에 한 번만 걸어도 내부 텍스트까지 자릿수 폭이 고정된다(ui-css 클래스\n * 신설 불필요 — kinetic 형제 컴포넌트들도 전부 인라인 스타일만 쓰는 관습).\n */\nimport { createElement, useEffect, useMemo, useRef, useState, } from \"react\";\nimport { useInView, useReducedMotion } from \"motion/react\";\nconst DEFAULT_LOCALE = \"ko-KR\";\nconst DEFAULT_DECIMALS = 0;\nconst DEFAULT_THRESHOLD = 0.4;\n/** 스펙 원본 site.js 100행 — 목표값이 이 값 이상이면 더 오래 카운트한다. */\nconst LONG_DURATION_THRESHOLD = 1000;\nconst SHORT_DURATION_MS = 1400;\nconst LONG_DURATION_MS = 2000;\nfunction easeOutQuart(progress) {\n return 1 - (1 - progress) ** 4;\n}\nfunction defaultDurationFor(value) {\n return Math.abs(value) >= LONG_DURATION_THRESHOLD ? LONG_DURATION_MS : SHORT_DURATION_MS;\n}\nfunction formatWithLocale(value, decimals, locale) {\n return new Intl.NumberFormat(locale, {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(value);\n}\nexport function CountUpStat({ value, prefix = \"\", suffix = \"\", decimals = DEFAULT_DECIMALS, locale = DEFAULT_LOCALE, formatter, duration, easing = easeOutQuart, threshold = DEFAULT_THRESHOLD, as = \"span\", className, style, }) {\n const reducedMotion = useReducedMotion();\n const [mounted, setMounted] = useState(false);\n const ref = useRef(null);\n const inView = useInView(ref, { once: true, amount: threshold });\n const [displayValue, setDisplayValue] = useState(0);\n useEffect(() => setMounted(true), []);\n const format = useMemo(() => formatter ?? ((current) => formatWithLocale(current, decimals, locale)), [formatter, decimals, locale]);\n useEffect(() => {\n if (reducedMotion || !mounted || !inView) {\n return;\n }\n const durationMs = Math.max(0, duration ?? defaultDurationFor(value));\n if (durationMs === 0) {\n setDisplayValue(value);\n return;\n }\n const startedAt = performance.now();\n let frameId = 0;\n function tick(now) {\n const progress = Math.min((now - startedAt) / durationMs, 1);\n setDisplayValue(value * easing(progress));\n if (progress < 1) {\n frameId = requestAnimationFrame(tick);\n }\n else {\n setDisplayValue(value);\n }\n }\n frameId = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frameId);\n }, [duration, easing, inView, mounted, reducedMotion, value]);\n const finalText = `${prefix}${format(value)}${suffix}`;\n const wrapperStyle = { fontVariantNumeric: \"tabular-nums\", ...style };\n // `ref` 는 두 분기 모두에서 항상 붙인다 — reduced-motion/pre-mount 분기에서만\n // 빠뜨리면 `useInView` 내부 effect 가 최초 커밋 시 `ref.current` 를 null 로\n // 캡처한 뒤 deps(ref 객체 자체는 항상 동일 identity)가 안 바뀌어 재구독을\n // 영영 못 하는 버그가 된다(mounted 전환 후에도 IntersectionObserver 자체가\n // 생성되지 않음). 항상 붙여서 최초 커밋부터 관찰이 성립하게 한다.\n const showAnimated = !reducedMotion && mounted;\n if (!showAnimated) {\n return createElement(as, { ref, className, style: wrapperStyle }, finalText);\n }\n return createElement(as, { ref, \"aria-label\": finalText, className, style: wrapperStyle }, createElement(\"span\", { \"aria-hidden\": true }, `${prefix}${format(displayValue)}${suffix}`));\n}\n","\"use client\";\n\n/**\n * `Marquee` — 수평 무한 루프(ddd `motion`). 로고 클라우드·신뢰 배지 띠용.\n *\n * 모션 규율(M5·AI-slop):\n * - `useReducedMotion()` true 또는 마운트 전 → **정지**. 스크롤 없이\n * `flex-wrap` 으로 항목 전체를 한 번에 노출(콘텐츠 손실 없음, M6).\n * - 진행 방식 = `useAnimationFrame` 로 매 프레임 균일 이동(선형). hover 시\n * 일시정지(pause-on-hover) — 정지/재개가 위치 점프 없이 이어진다(연속\n * `useMotionValue` 갱신이라 `direction` 전환 외에는 값이 리셋되지 않음).\n * - 과한 효과 금지(AI-slop #10) — scale·회전 없음, 등속 이동만.\n *\n * 트랙은 자식을 2벌(\"block\")로 나눠 -50%(또는 `direction=\"right\"` 는\n * 0 ↔ -50% 반대 위상) 이동 시 이음매 없이 순환한다. 각 block 내부는\n * `fillCount` 만큼 자식 세트를 반복 렌더 — 1세트 폭이 컨테이너보다 좁으면\n * (예: 로고 3개짜리 마퀴) 루프 중간에 빈틈이 생기는 문제를 막는다.\n * `fillCount` 는 ResizeObserver 로 컨테이너 폭 ÷ 1세트 실측 폭(올림)을 매\n * 리사이즈마다 재계산(vibe-design-starter-kit `MarqueeContainer` 참조,\n * 단 이 컴포넌트의 rAF/모션값 엔진은 그대로 유지). SSR 또는\n * `ResizeObserver` 부재 환경은 `fillCount=1`(기존 2벌 렌더)로 폴백한다.\n */\nimport {\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport {\n motion,\n useAnimationFrame,\n useMotionValue,\n useReducedMotion,\n useTransform,\n} from \"motion/react\";\n\nexport interface MarqueeProps {\n readonly children?: ReactNode;\n readonly className?: string;\n /** 한 바퀴(자식 1벌 폭) 이동에 걸리는 시간(초). 기본 24. */\n readonly speedSec?: number;\n /** 항목 간격(CSS length). 기본 `2rem`(구조 간격 — 브랜드 토큰 아님). */\n readonly gap?: string;\n /** hover 시 일시정지. 기본 true. */\n readonly pauseOnHover?: boolean;\n /** 스크린리더용 그룹 레이블. */\n readonly ariaLabel?: string;\n /** 이동 방향. 기본 `left`(기존 동작 불변). `right` 는 반대 방향 이중 마퀴 패턴용. */\n readonly direction?: \"left\" | \"right\";\n}\n\nexport function Marquee({\n children,\n className,\n speedSec = 24,\n gap = \"2rem\",\n pauseOnHover = true,\n ariaLabel,\n direction = \"left\",\n}: MarqueeProps) {\n const reducedMotion = useReducedMotion();\n const [mounted, setMounted] = useState(false);\n const [paused, setPaused] = useState(false);\n // 자식 1세트 반복 횟수. 1 = 기존 동작(block 당 1벌, 총 2벌).\n const [fillCount, setFillCount] = useState(1);\n const wrapperRef = useRef<HTMLDivElement | null>(null);\n const measureRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => setMounted(true), []);\n\n // 동적 채움: 컨테이너 폭 대비 1세트 실측 폭으로 반복 횟수 계산.\n // SSR·ResizeObserver 부재 환경은 이 effect 가 그대로 no-op → fillCount=1 유지.\n useEffect(() => {\n const wrapper = wrapperRef.current;\n const measure = measureRef.current;\n if (!wrapper || !measure || typeof ResizeObserver === \"undefined\") return;\n\n const calculate = () => {\n const containerWidth = wrapper.offsetWidth;\n const setWidth = measure.offsetWidth;\n if (setWidth === 0) return;\n setFillCount(Math.max(1, Math.ceil(containerWidth / setWidth)));\n };\n\n calculate();\n const ro = new ResizeObserver(calculate);\n ro.observe(wrapper);\n return () => ro.disconnect();\n // mounted·reducedMotion 은 wrapper/measure DOM 이 실제로 존재하는 렌더를 잡기\n // 위한 트리거다(정적 폴백 렌더에서는 refs 가 비어 있어 no-op) — children/gap\n // 변경 시 재측정도 필요하므로 넷 다 의존성에 둔다.\n }, [children, gap, mounted, reducedMotion]);\n\n const x = useMotionValue(direction === \"right\" ? -50 : 0);\n const transform = useTransform(x, (v) => `translateX(${v}%)`);\n\n // direction 전환 시에만 위상을 리셋(정상 정지/재개 흐름과는 무관).\n useEffect(() => {\n x.set(direction === \"right\" ? -50 : 0);\n }, [direction, x]);\n\n useAnimationFrame((_t, delta) => {\n if (reducedMotion || paused) return;\n const percentPerMs = 50 / (speedSec * 1000); // 자식 1벌 = 트랙의 50%\n const sign = direction === \"right\" ? 1 : -1;\n let next = x.get() + sign * percentPerMs * delta;\n if (direction === \"right\") {\n if (next >= 0) next -= 50; // 이음매 없는 순환(반대 위상)\n } else if (next <= -50) {\n next += 50; // 이음매 없는 순환\n }\n x.set(next);\n });\n\n const staticStyle: CSSProperties = {\n display: \"flex\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n gap,\n };\n if (reducedMotion || !mounted) {\n return (\n <div className={className} aria-label={ariaLabel} style={staticStyle}>\n {children}\n </div>\n );\n }\n\n const copyStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap,\n flexShrink: 0,\n paddingInlineEnd: gap,\n };\n const reps = Array.from({ length: fillCount }, (_, i) => i);\n\n return (\n <div\n ref={wrapperRef}\n className={className}\n aria-label={ariaLabel}\n style={{ overflow: \"hidden\", inlineSize: \"100%\" }}\n onMouseEnter={pauseOnHover ? () => setPaused(true) : undefined}\n onMouseLeave={pauseOnHover ? () => setPaused(false) : undefined}\n >\n {/* 측정 전용: 자식 1세트의 실제 폭(gap 포함) — 화면 비노출, fillCount 계산에만 사용 */}\n <div\n ref={measureRef}\n data-marquee-measure=\"\"\n aria-hidden\n style={{ ...copyStyle, position: \"absolute\", visibility: \"hidden\", pointerEvents: \"none\" }}\n >\n {children}\n </div>\n <motion.div\n data-marquee-track=\"\"\n style={{ display: \"flex\", inlineSize: \"max-content\", willChange: \"transform\", transform }}\n >\n <div style={{ display: \"flex\" }} data-marquee-block=\"a\">\n {reps.map((i) => (\n <div key={`a-${i}`} style={copyStyle} aria-hidden={i > 0 ? true : undefined}>\n {children}\n </div>\n ))}\n </div>\n <div style={{ display: \"flex\" }} data-marquee-block=\"b\" aria-hidden>\n {reps.map((i) => (\n <div key={`b-${i}`} style={copyStyle}>\n {children}\n </div>\n ))}\n </div>\n </motion.div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * `StaggerGroup` + `StaggerItem` — 자식 순차 진입(ddd `scroll`). 카드 그리드용.\n *\n * `StaggerGroup` 이 컨테이너(초기 hidden→whileInView visible)를, 각 `StaggerItem`\n * 은 컨테이너의 애니메이션 라벨을 상속받아 `STAGGER_STEP` 간격으로 진입한다.\n * variants 정의는 `../motion/variants` 한 곳(C5). 모션 규율은 `AnimateOnScroll`\n * 와 동일 — reduce·마운트 전에는 애니메이션 없이 시맨틱 엘리먼트를 그대로 렌더\n * (래퍼 div 추가 없음 → SSR 마크업 불변, M6). `IntersectionObserver` 가 없는\n * 환경(구형 브라우저·일부 헤드리스/캡처 도구)도 `AnimateOnScroll` 과 동일하게\n * 최종(보이는) 상태로 렌더한다 — motion/react 의 `whileInView` 는 그 생성자가\n * 없으면 관찰 자체가 시작되지 않아 `initial=\"hidden\"` 에서 영원히 멈춘다.\n */\nimport {\n useEffect,\n createElement,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport {\n DEFAULT_VIEWPORT_MARGIN,\n STAGGER_STEP,\n staggerContainer,\n staggerItem,\n} from \"../motion/variants\";\n\ntype GroupTag = \"div\" | \"ul\" | \"ol\" | \"section\";\ntype ItemTag = \"div\" | \"li\" | \"article\";\n\nexport interface StaggerGroupProps {\n readonly children?: ReactNode;\n readonly className?: string;\n readonly style?: CSSProperties;\n /** 렌더할 시맨틱 컨테이너 엘리먼트. 기본 `div`. */\n readonly as?: GroupTag;\n /** 자식 순차 지연(초). 기본 0.08. */\n readonly staggerChildren?: number;\n readonly once?: boolean;\n readonly margin?: string;\n}\n\nexport function StaggerGroup({\n children,\n className,\n style,\n as = \"div\",\n staggerChildren = STAGGER_STEP,\n once = true,\n margin = DEFAULT_VIEWPORT_MARGIN,\n}: StaggerGroupProps) {\n const reducedMotion = useReducedMotion();\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n\n // `mounted` 가 이미 SSR/하이드레이션-전 패스를 게이트하므로, 아래 체크는\n // 클라이언트에서만 실행된다 — `window` 동기 참조가 안전하다.\n const supportsInView =\n mounted && typeof window !== \"undefined\" && typeof window.IntersectionObserver !== \"undefined\";\n\n if (reducedMotion || !mounted || !supportsInView) {\n return createElement(as, { className, style }, children);\n }\n\n const MotionTag = motion[as] as typeof motion.div;\n return (\n <MotionTag\n className={className}\n style={style}\n initial=\"hidden\"\n whileInView=\"visible\"\n viewport={{ once, margin }}\n variants={staggerContainer(staggerChildren)}\n >\n {children}\n </MotionTag>\n );\n}\n\nexport interface StaggerItemProps {\n readonly children?: ReactNode;\n readonly className?: string;\n readonly style?: CSSProperties;\n /** 렌더할 시맨틱 아이템 엘리먼트. 기본 `div`. */\n readonly as?: ItemTag;\n}\n\nexport function StaggerItem({ children, className, style, as = \"div\" }: StaggerItemProps) {\n const reducedMotion = useReducedMotion();\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n\n const supportsInView =\n mounted && typeof window !== \"undefined\" && typeof window.IntersectionObserver !== \"undefined\";\n\n if (reducedMotion || !mounted || !supportsInView) {\n return createElement(as, { className, style }, children);\n }\n\n // initial/whileInView 없음 — 부모 StaggerGroup 의 hidden/visible 라벨을 상속.\n const MotionTag = motion[as] as typeof motion.div;\n return (\n <MotionTag className={className} style={style} variants={staggerItem}>\n {children}\n </MotionTag>\n );\n}\n"],"mappings":";AAuBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,QAAQ,wBAAwB;;;ACdlC,IAAM,gBAAgB,CAAC,GAAG,GAAG,KAAK,CAAC;AAEnC,IAAM,oBAAoB;AAE1B,IAAM,eAAe;AAErB,IAAM,0BAA0B;AAEvC,IAAM,qBAAqB;AAAA,EACzB,UAAU;AAAA,EACV,MAAM;AACR;AAKO,IAAM,qBAAqB;AAAA,EAChC,WAAW,EAAE,QAAQ,EAAE,SAAS,GAAG,GAAG,GAAG,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE,EAAE;AAAA,EAC1E,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,SAAS,EAAE,EAAE;AAAA,EACxD,YAAY,EAAE,QAAQ,EAAE,SAAS,GAAG,OAAO,KAAK,GAAG,SAAS,EAAE,SAAS,GAAG,OAAO,EAAE,EAAE;AACvF;AAEA,SAAS,uBAAuB,WAAmE;AACjG,SAAO;AAAA,IACL,QAAQ,UAAU;AAAA,IAClB,SAAS,EAAE,GAAG,UAAU,SAAS,YAAY,mBAAmB;AAAA,EAClE;AACF;AAGO,IAAM,SAAmB,uBAAuB,mBAAmB,SAAS,CAAC;AAG7E,IAAM,OAAiB,uBAAuB,mBAAmB,IAAI;AAGrE,IAAM,UAAoB,uBAAuB,mBAAmB,UAAU,CAAC;AAG/E,IAAM,oBAAuD;AAAA,EAClE,WAAW;AAAA,EACX;AAAA,EACA,YAAY;AACd;AAGO,SAAS,iBAAiB,OAAe,cAAwB;AACtE,SAAO;AAAA,IACL,QAAQ,CAAC;AAAA,IACT,SAAS,EAAE,YAAY,EAAE,iBAAiB,KAAK,EAAE;AAAA,EACnD;AACF;AAGO,IAAM,cAAwB;;;ADOjC;AAxBG,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AACX,GAAyB;AACvB,QAAM,gBAAgB,iBAAiB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,YAAU,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AAIpC,QAAM,iBACJ,WAAW,OAAO,WAAW,eAAe,OAAO,OAAO,yBAAyB;AAErF,MAAI,iBAAiB,CAAC,WAAW,CAAC,gBAAgB;AAChD,WAAO,cAAc,IAAI,EAAE,WAAW,MAAM,GAAG,QAAQ;AAAA,EACzD;AAEA,QAAM,YAAY,OAAO,EAAE;AAC3B,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,SAAQ;AAAA,MACR,aAAY;AAAA,MACZ,UAAU,EAAE,MAAM,OAAO;AAAA,MACzB,UAAU,kBAAkB,OAAO;AAAA,MAElC;AAAA;AAAA,EACH;AAEJ;;;AE7DA,SAAS,iBAAAA,gBAAe,aAAAC,YAAW,SAAS,QAAQ,YAAAC,iBAAiB;AACrE,SAAS,WAAW,oBAAAC,yBAAwB;AAC5C,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAE1B,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,SAAS,aAAa,UAAU;AAC5B,SAAO,KAAK,IAAI,aAAa;AACjC;AACA,SAAS,mBAAmB,OAAO;AAC/B,SAAO,KAAK,IAAI,KAAK,KAAK,0BAA0B,mBAAmB;AAC3E;AACA,SAAS,iBAAiB,OAAO,UAAU,QAAQ;AAC/C,SAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,IACjC,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,EAC3B,CAAC,EAAE,OAAO,KAAK;AACnB;AACO,SAAS,YAAY,EAAE,OAAO,SAAS,IAAI,SAAS,IAAI,WAAW,kBAAkB,SAAS,gBAAgB,WAAW,UAAU,SAAS,cAAc,YAAY,mBAAmB,KAAK,QAAQ,WAAW,MAAO,GAAG;AAC9N,QAAM,gBAAgBA,kBAAiB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAID,UAAS,KAAK;AAC5C,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,SAAS,UAAU,KAAK,EAAE,MAAM,MAAM,QAAQ,UAAU,CAAC;AAC/D,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,CAAC;AAClD,EAAAD,WAAU,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AACpC,QAAM,SAAS,QAAQ,MAAM,cAAc,CAAC,YAAY,iBAAiB,SAAS,UAAU,MAAM,IAAI,CAAC,WAAW,UAAU,MAAM,CAAC;AACnI,EAAAA,WAAU,MAAM;AACZ,QAAI,iBAAiB,CAAC,WAAW,CAAC,QAAQ;AACtC;AAAA,IACJ;AACA,UAAM,aAAa,KAAK,IAAI,GAAG,YAAY,mBAAmB,KAAK,CAAC;AACpE,QAAI,eAAe,GAAG;AAClB,sBAAgB,KAAK;AACrB;AAAA,IACJ;AACA,UAAM,YAAY,YAAY,IAAI;AAClC,QAAI,UAAU;AACd,aAAS,KAAK,KAAK;AACf,YAAM,WAAW,KAAK,KAAK,MAAM,aAAa,YAAY,CAAC;AAC3D,sBAAgB,QAAQ,OAAO,QAAQ,CAAC;AACxC,UAAI,WAAW,GAAG;AACd,kBAAU,sBAAsB,IAAI;AAAA,MACxC,OACK;AACD,wBAAgB,KAAK;AAAA,MACzB;AAAA,IACJ;AACA,cAAU,sBAAsB,IAAI;AACpC,WAAO,MAAM,qBAAqB,OAAO;AAAA,EAC7C,GAAG,CAAC,UAAU,QAAQ,QAAQ,SAAS,eAAe,KAAK,CAAC;AAC5D,QAAM,YAAY,GAAG,MAAM,GAAG,OAAO,KAAK,CAAC,GAAG,MAAM;AACpD,QAAM,eAAe,EAAE,oBAAoB,gBAAgB,GAAG,MAAM;AAMpE,QAAM,eAAe,CAAC,iBAAiB;AACvC,MAAI,CAAC,cAAc;AACf,WAAOD,eAAc,IAAI,EAAE,KAAK,WAAW,OAAO,aAAa,GAAG,SAAS;AAAA,EAC/E;AACA,SAAOA,eAAc,IAAI,EAAE,KAAK,cAAc,WAAW,WAAW,OAAO,aAAa,GAAGA,eAAc,QAAQ,EAAE,eAAe,KAAK,GAAG,GAAG,MAAM,GAAG,OAAO,YAAY,CAAC,GAAG,MAAM,EAAE,CAAC;AAC1L;;;ACtEA;AAAA,EACE,aAAAI;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAGK;AACP;AAAA,EACE,UAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,OACK;AAwFD,gBAAAC,MAiCA,YAjCA;AAvEC,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,MAAM;AAAA,EACN,eAAe;AAAA,EACf;AAAA,EACA,YAAY;AACd,GAAiB;AACf,QAAM,gBAAgBD,kBAAiB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAIF,UAAS,KAAK;AAC5C,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAE1C,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,CAAC;AAC5C,QAAM,aAAaD,QAA8B,IAAI;AACrD,QAAM,aAAaA,QAA8B,IAAI;AAErD,EAAAD,WAAU,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AAIpC,EAAAA,WAAU,MAAM;AACd,UAAM,UAAU,WAAW;AAC3B,UAAM,UAAU,WAAW;AAC3B,QAAI,CAAC,WAAW,CAAC,WAAW,OAAO,mBAAmB,YAAa;AAEnE,UAAM,YAAY,MAAM;AACtB,YAAM,iBAAiB,QAAQ;AAC/B,YAAM,WAAW,QAAQ;AACzB,UAAI,aAAa,EAAG;AACpB,mBAAa,KAAK,IAAI,GAAG,KAAK,KAAK,iBAAiB,QAAQ,CAAC,CAAC;AAAA,IAChE;AAEA,cAAU;AACV,UAAM,KAAK,IAAI,eAAe,SAAS;AACvC,OAAG,QAAQ,OAAO;AAClB,WAAO,MAAM,GAAG,WAAW;AAAA,EAI7B,GAAG,CAAC,UAAU,KAAK,SAAS,aAAa,CAAC;AAE1C,QAAM,IAAI,eAAe,cAAc,UAAU,MAAM,CAAC;AACxD,QAAM,YAAY,aAAa,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI;AAG5D,EAAAA,WAAU,MAAM;AACd,MAAE,IAAI,cAAc,UAAU,MAAM,CAAC;AAAA,EACvC,GAAG,CAAC,WAAW,CAAC,CAAC;AAEjB,oBAAkB,CAAC,IAAI,UAAU;AAC/B,QAAI,iBAAiB,OAAQ;AAC7B,UAAM,eAAe,MAAM,WAAW;AACtC,UAAM,OAAO,cAAc,UAAU,IAAI;AACzC,QAAI,OAAO,EAAE,IAAI,IAAI,OAAO,eAAe;AAC3C,QAAI,cAAc,SAAS;AACzB,UAAI,QAAQ,EAAG,SAAQ;AAAA,IACzB,WAAW,QAAQ,KAAK;AACtB,cAAQ;AAAA,IACV;AACA,MAAE,IAAI,IAAI;AAAA,EACZ,CAAC;AAED,QAAM,cAA6B;AAAA,IACjC,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,EACF;AACA,MAAI,iBAAiB,CAAC,SAAS;AAC7B,WACE,gBAAAK,KAAC,SAAI,WAAsB,cAAY,WAAW,OAAO,aACtD,UACH;AAAA,EAEJ;AAEA,QAAM,YAA2B;AAAA,IAC/B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,kBAAkB;AAAA,EACpB;AACA,QAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC;AAE1D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,cAAY;AAAA,MACZ,OAAO,EAAE,UAAU,UAAU,YAAY,OAAO;AAAA,MAChD,cAAc,eAAe,MAAM,UAAU,IAAI,IAAI;AAAA,MACrD,cAAc,eAAe,MAAM,UAAU,KAAK,IAAI;AAAA,MAGtD;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,wBAAqB;AAAA,YACrB,eAAW;AAAA,YACX,OAAO,EAAE,GAAG,WAAW,UAAU,YAAY,YAAY,UAAU,eAAe,OAAO;AAAA,YAExF;AAAA;AAAA,QACH;AAAA,QACA;AAAA,UAACF,QAAO;AAAA,UAAP;AAAA,YACC,sBAAmB;AAAA,YACnB,OAAO,EAAE,SAAS,QAAQ,YAAY,eAAe,YAAY,aAAa,UAAU;AAAA,YAExF;AAAA,8BAAAE,KAAC,SAAI,OAAO,EAAE,SAAS,OAAO,GAAG,sBAAmB,KACjD,eAAK,IAAI,CAAC,MACT,gBAAAA,KAAC,SAAmB,OAAO,WAAW,eAAa,IAAI,IAAI,OAAO,QAC/D,YADO,KAAK,CAAC,EAEhB,CACD,GACH;AAAA,cACA,gBAAAA,KAAC,SAAI,OAAO,EAAE,SAAS,OAAO,GAAG,sBAAmB,KAAI,eAAW,MAChE,eAAK,IAAI,CAAC,MACT,gBAAAA,KAAC,SAAmB,OAAO,WACxB,YADO,KAAK,CAAC,EAEhB,CACD,GACH;AAAA;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACnKA;AAAA,EACE,aAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,YAAAC;AAAA,OAGK;AACP,SAAS,UAAAC,SAAQ,oBAAAC,yBAAwB;AA+CrC,gBAAAC,YAAA;AAxBG,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,KAAK;AAAA,EACL,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,SAAS;AACX,GAAsB;AACpB,QAAM,gBAAgBC,kBAAiB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,KAAK;AAC5C,EAAAC,WAAU,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AAIpC,QAAM,iBACJ,WAAW,OAAO,WAAW,eAAe,OAAO,OAAO,yBAAyB;AAErF,MAAI,iBAAiB,CAAC,WAAW,CAAC,gBAAgB;AAChD,WAAOC,eAAc,IAAI,EAAE,WAAW,MAAM,GAAG,QAAQ;AAAA,EACzD;AAEA,QAAM,YAAYC,QAAO,EAAE;AAC3B,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,SAAQ;AAAA,MACR,aAAY;AAAA,MACZ,UAAU,EAAE,MAAM,OAAO;AAAA,MACzB,UAAU,iBAAiB,eAAe;AAAA,MAEzC;AAAA;AAAA,EACH;AAEJ;AAUO,SAAS,YAAY,EAAE,UAAU,WAAW,OAAO,KAAK,MAAM,GAAqB;AACxF,QAAM,gBAAgBC,kBAAiB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,KAAK;AAC5C,EAAAC,WAAU,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AAEpC,QAAM,iBACJ,WAAW,OAAO,WAAW,eAAe,OAAO,OAAO,yBAAyB;AAErF,MAAI,iBAAiB,CAAC,WAAW,CAAC,gBAAgB;AAChD,WAAOC,eAAc,IAAI,EAAE,WAAW,MAAM,GAAG,QAAQ;AAAA,EACzD;AAGA,QAAM,YAAYC,QAAO,EAAE;AAC3B,SACE,gBAAAL,KAAC,aAAU,WAAsB,OAAc,UAAU,aACtD,UACH;AAEJ;","names":["createElement","useEffect","useState","useReducedMotion","useEffect","useRef","useState","motion","useReducedMotion","jsx","useEffect","createElement","useState","motion","useReducedMotion","jsx","useReducedMotion","useState","useEffect","createElement","motion"]}
@@ -1,12 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as ConcernsSection } from './site-config-DNpvlqA5.js';
3
- import 'zod';
4
-
5
- type ConcernRegion = ConcernsSection["props"]["regions"][number];
6
- declare function ConcernsFigureMap({ figureImage, figureAlt, regions, }: {
7
- readonly figureImage: string;
8
- readonly figureAlt: string;
9
- readonly regions: readonly ConcernRegion[];
10
- }): react_jsx_runtime.JSX.Element | null;
11
-
12
- export { ConcernsFigureMap };