@roottale/cms-renderer-next 0.42.1 → 0.44.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @roottale/cms-renderer-next
2
2
 
3
+ ## 0.44.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 353d437: `RootTaleBlogPost`에 `showTitle` 옵션을 추가했습니다. 페이지가 글 제목을 직접
8
+ 출력할 때 `showTitle={false}`를 넘기면 CMS의 중복 `<h1>`만 생략하고 나머지
9
+ 헤더 정보와 본문은 그대로 렌더합니다.
10
+
11
+ ## 0.43.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 4ec1a83: 콘텐츠 유형(Collections, ADR-0060)에 `detail?: boolean` 옵션 추가 — 기본 `true`(하위호환), `false`면 "목록 전용" 유형(예: 후기 — 목록에서 전문 노출, 글마다 별도 상세 URL 없음).
16
+ - `@roottale/cms-core`: `RouteCollection.detail` 필드. `resolvePostUrl`/`resolvePostPath` 가 `collection.detail === false` 인 글에 `null` 반환(소속 판정 `resolvePostCollection` 은 영향 없음 — 소속과 상세 URL 존재 여부는 별개).
17
+ - `@roottale/cms-client`: `fetchCollections()`(`CmsRouteCollection.detail`)가 공개 API의 새 `detail` 필드를 매핑(미전달 시 `true`).
18
+ - `@roottale/cms-renderer-next`: `createSitemapIndex`/`createSitemap`(기존 `resolvePostUrl` 경유라 자동 반영)·`createFeedRoute`(RSS 아이템에서 `detail:false` 스트림 글 제외)·`createLlmsTxtRoute`가 `detail:false` 스트림 글의 상세 URL 을 발행하지 않는다. `RootTaleBlogList`/관련 글 카드·아카이브 연관 위젯의 postHref 가 `detail:false` 스트림에서는 존재하지 않는 상세 경로 대신 그 유형의 목록 페이지(`basePath`)로 링크한다.
19
+
20
+ 어드민 "콘텐츠 유형" 편집기에 "글별 상세 페이지" 토글 추가(기본 켬). 코드 변경 없이 어드민에서 끄면 즉시 반영.
21
+
22
+ ### Patch Changes
23
+
24
+ - 63b2eb0: `RootTaleBlogList`의 포지티브 `collection` 스코프(`collection`+`collections`)가 `fetchPosts()`의 기존 `collectionKey` 옵션을 실어 보내도록 배선 — 공개 posts API가 `LIMIT` 전에 `collection_key=` 필터를 적용해, 다중 스트림 사이트(예: `/notice`)의 `limit` 슬롯이 다른 섹션 글에 낭비되던 페이지네이션 왜곡이 해소된다(`blog-list-collection-scope.md` §2② 잔여 배선, `excludeCollections`와 동일 패턴). 기존 클라이언트 측 `postsForCollection` 필터는 방어선으로 그대로 유지(동작 diff 0).
25
+ - Updated dependencies [4ec1a83]
26
+ - @roottale/cms-core@0.43.0
27
+ - @roottale/cms-client@0.43.0
28
+
3
29
  ## 0.42.1
4
30
 
5
31
  ### Patch Changes
package/README.md CHANGED
@@ -138,6 +138,10 @@ export default async function PostPage({
138
138
  }
139
139
  ```
140
140
 
141
+ `RootTaleBlogPost` renders the post title as `<h1>` by default. If the page
142
+ already renders that title, pass `showTitle={false}` to avoid a duplicate
143
+ heading. The excerpt, post metadata, and body remain visible.
144
+
141
145
  ## Lead form — `app/contact/page.tsx`
142
146
 
143
147
  ```tsx
@@ -61,7 +61,7 @@ var SECTION_VARIANTS = {
61
61
  pricing: ["table", "cards"],
62
62
  process: ["horizontal", "vertical", "timeline-marked"],
63
63
  "before-after": ["slider", "side-by-side"],
64
- testimonials: ["editorial-quote", "quote-rail"],
64
+ testimonials: ["editorial-quote", "quote-rail", "star-compact"],
65
65
  team: ["grid", "spotlight", "expanding-stage", "row"],
66
66
  expertise: ["tab-stage", "cards"],
67
67
  spaces: ["stage", "mosaic"],
@@ -654,6 +654,9 @@ var TestimonialsSectionSchema = SectionBase.extend({
654
654
  * `editorial-quote`(기본): 대형 인용 타이포 에디토리얼 — 1개면 중앙 정렬
655
655
  * 스테이지, 2개 이상이면 세로 스택(hairline 구분). `quote-rail`: 가로
656
656
  * 스크롤 레일(hairline 좌측 보더 컬럼, 균등 그림자 카드 금지).
657
+ * `star-compact`(W8-7): 별점 중심 컴팩트 그리드 — 평균 별점 요약 밴드 +
658
+ * hairline 행. 별점은 CSS 타이포 글리프(이모지·이미지 금지), 색은 전부
659
+ * 토큰(variant×토큰 직교, 채움 폭만 데이터 주도 커스텀 프로퍼티).
657
660
  */
658
661
  variant: z.enum(SECTION_VARIANTS.testimonials).default("editorial-quote"),
659
662
  props: z.object({
@@ -913,4 +916,4 @@ export {
913
916
  parseSiteConfig,
914
917
  defineSiteConfig
915
918
  };
916
- //# sourceMappingURL=chunk-NUUTTSGT.js.map
919
+ //# sourceMappingURL=chunk-6PDZSFVZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates/site-config.ts"],"sourcesContent":["/**\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\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 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\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 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 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":[]}
@@ -25,7 +25,7 @@ import {
25
25
  } from "./chunk-FUA5H6JE.js";
26
26
  import {
27
27
  SectionSchema
28
- } from "./chunk-NUUTTSGT.js";
28
+ } from "./chunk-6PDZSFVZ.js";
29
29
  import {
30
30
  HeroCarouselViewport
31
31
  } from "./chunk-F3G6RVIR.js";
@@ -80,6 +80,7 @@ import {
80
80
  isAllowedEmbedSrc as isAllowedEmbedSrc2,
81
81
  postsForCollection,
82
82
  postsNotInAnyCollection,
83
+ resolvePostCollection,
83
84
  resolvePostPath
84
85
  } from "@roottale/cms-core";
85
86
 
@@ -305,7 +306,7 @@ function RootTaleLeadForm(props) {
305
306
  }
306
307
 
307
308
  // src/templates/sections.tsx
308
- import { isAllowedEmbedSrc } from "@roottale/cms-core";
309
+ import { isAllowedEmbedSrc, isAllowedMapEmbedSrc } from "@roottale/cms-core";
309
310
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
310
311
  function sectionThemeStyle(theme) {
311
312
  if (!theme) return void 0;
@@ -1307,9 +1308,89 @@ function TemplateTestimonialsQuoteRail(section) {
1307
1308
  }
1308
1309
  );
1309
1310
  }
1311
+ function StarRating({ rating }) {
1312
+ const clamped = Math.min(5, Math.max(0, rating));
1313
+ const fill = `${(clamped / 5 * 100).toFixed(0)}%`;
1314
+ return /* @__PURE__ */ jsxs2(
1315
+ "span",
1316
+ {
1317
+ className: "rt-tpl-testimonials-stars",
1318
+ role: "img",
1319
+ "aria-label": `\uD3C9\uC810 5\uC810 \uB9CC\uC810\uC5D0 ${clamped.toFixed(1)}\uC810`,
1320
+ style: { "--rt-testimonials-star-fill": fill },
1321
+ children: [
1322
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "rt-tpl-testimonials-stars-base", children: "\u2605\u2605\u2605\u2605\u2605" }),
1323
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "rt-tpl-testimonials-stars-fill", children: "\u2605\u2605\u2605\u2605\u2605" })
1324
+ ]
1325
+ }
1326
+ );
1327
+ }
1328
+ function TemplateTestimonialsStarCompact(section) {
1329
+ const { heading, description, items, disclaimer } = section.props;
1330
+ const rated = items.filter(
1331
+ (item) => typeof item.rating === "number"
1332
+ );
1333
+ const average = rated.length > 0 ? rated.reduce((sum, item) => sum + item.rating, 0) / rated.length : void 0;
1334
+ return /* @__PURE__ */ jsx2(
1335
+ "section",
1336
+ {
1337
+ "data-roottale-tpl": "testimonials",
1338
+ "data-variant": "star-compact",
1339
+ id: section.id,
1340
+ className: "rt-tpl-section rt-tpl-testimonials",
1341
+ style: sectionThemeStyle(section.theme),
1342
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
1343
+ /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }),
1344
+ description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
1345
+ average !== void 0 ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-testimonials-star-summary", children: [
1346
+ /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-average", children: average.toFixed(1) }),
1347
+ /* @__PURE__ */ jsx2(StarRating, { rating: average }),
1348
+ /* @__PURE__ */ jsxs2("span", { className: "rt-tpl-testimonials-star-count", children: [
1349
+ "\uD6C4\uAE30 ",
1350
+ items.length,
1351
+ "\uAC74"
1352
+ ] })
1353
+ ] }) : null,
1354
+ /* @__PURE__ */ jsx2(StaggerGroup, { as: "ul", className: "rt-tpl-testimonials-star-list", children: items.map((item, i) => /* @__PURE__ */ jsx2(
1355
+ StaggerItem,
1356
+ {
1357
+ as: "li",
1358
+ className: "rt-tpl-testimonials-star-item",
1359
+ children: /* @__PURE__ */ jsxs2("blockquote", { className: "rt-tpl-testimonials-star-quote", children: [
1360
+ typeof item.rating === "number" ? /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-testimonials-star-head", children: [
1361
+ /* @__PURE__ */ jsx2(StarRating, { rating: item.rating }),
1362
+ /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-value", children: item.rating.toFixed(1) })
1363
+ ] }) : null,
1364
+ /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-star-text", children: item.quote }),
1365
+ /* @__PURE__ */ jsxs2("footer", { className: "rt-tpl-testimonials-star-footer", children: [
1366
+ /* @__PURE__ */ jsx2("cite", { className: "rt-tpl-testimonials-star-author", children: item.author }),
1367
+ item.context ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-context", children: item.context }) : null,
1368
+ /* @__PURE__ */ jsx2(StarCompactMeta, { item })
1369
+ ] }),
1370
+ item.reply ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-reply", children: item.reply }) : null
1371
+ ] })
1372
+ },
1373
+ `${item.author}-${i}`
1374
+ )) }),
1375
+ disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-testimonials-disclaimer", children: disclaimer }) : null
1376
+ ] })
1377
+ }
1378
+ );
1379
+ }
1380
+ function StarCompactMeta({
1381
+ item
1382
+ }) {
1383
+ const values = [item.source, item.visitedAt, item.item].filter(
1384
+ (value) => Boolean(value)
1385
+ );
1386
+ if (values.length === 0) return null;
1387
+ return /* @__PURE__ */ jsx2("span", { className: "rt-tpl-testimonials-star-meta", children: values.map((value) => /* @__PURE__ */ jsx2("span", { children: value }, value)) });
1388
+ }
1310
1389
  function TemplateTestimonials(section) {
1311
1390
  if (section.variant === "quote-rail")
1312
1391
  return TemplateTestimonialsQuoteRail(section);
1392
+ if (section.variant === "star-compact")
1393
+ return TemplateTestimonialsStarCompact(section);
1313
1394
  return TemplateTestimonialsEditorialQuote(section);
1314
1395
  }
1315
1396
  function memberInitials(name) {
@@ -1457,7 +1538,7 @@ function telHref(phone) {
1457
1538
  return `tel:${phone.replace(/[^\d+]/g, "")}`;
1458
1539
  }
1459
1540
  function ContactMap({ url }) {
1460
- if (isAllowedEmbedSrc(url)) {
1541
+ if (isAllowedMapEmbedSrc(url) || isAllowedEmbedSrc(url)) {
1461
1542
  return /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-map", children: /* @__PURE__ */ jsx2(
1462
1543
  "iframe",
1463
1544
  {
@@ -1477,8 +1558,11 @@ function ContactMapBlock({
1477
1558
  const embedUrl = map?.embedUrl ?? mapEmbedUrl;
1478
1559
  const externalUrl = map?.externalUrl ?? embedUrl;
1479
1560
  if (!embedUrl && !map?.facadeImageSrc && !externalUrl) return null;
1561
+ const liveMap = Boolean(
1562
+ embedUrl && (isAllowedMapEmbedSrc(embedUrl) || isAllowedEmbedSrc(embedUrl))
1563
+ );
1480
1564
  return /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-map-block", "data-provider": map?.provider, children: [
1481
- map?.facadeImageSrc ? /* @__PURE__ */ jsxs2("figure", { className: "rt-tpl-contact-map-facade", children: [
1565
+ map?.facadeImageSrc && !liveMap ? /* @__PURE__ */ jsxs2("figure", { className: "rt-tpl-contact-map-facade", children: [
1482
1566
  /* @__PURE__ */ jsx2(
1483
1567
  "img",
1484
1568
  {
@@ -2668,6 +2752,11 @@ async function RootTaleBlogList(props) {
2668
2752
  limit,
2669
2753
  type,
2670
2754
  ...author ? { author } : {},
2755
+ // blog-list-collection-scope.md §2②(잔여 배선) — 포지티브 스코프도 서버가
2756
+ // 이미 그 섹션 글만 걸러 fetch 하게 한다(limit 이 스트림 밖 글에 낭비되지
2757
+ // 않음). 아래 `postsForCollection` 클라이언트 필터는 방어선으로 유지
2758
+ // (판정 시맨틱 = `resolvePostCollection` 동일 정의).
2759
+ ...collection && collections ? { collectionKey: collection } : {},
2671
2760
  // blog-list-collection-scope.md §2② — 서버(api-core)가 이미 스트림 글을
2672
2761
  // 걸러 fetch 하게 한다(limit 이 스트림 글에 낭비되지 않음). 아래
2673
2762
  // `postsNotInAnyCollection` 클라이언트 필터는 방어선으로 유지.
@@ -2763,6 +2852,7 @@ async function RootTaleBlogPost(props) {
2763
2852
  slugOrId,
2764
2853
  baseUrl,
2765
2854
  notFoundElement,
2855
+ showTitle = true,
2766
2856
  showTableOfContents,
2767
2857
  tableOfContentsTitle,
2768
2858
  relatedPostsCount = 0,
@@ -2827,7 +2917,7 @@ async function RootTaleBlogPost(props) {
2827
2917
  style: themeStyle ?? void 0,
2828
2918
  children: [
2829
2919
  /* @__PURE__ */ jsxs9("header", { className: "rt-cms-post-header", children: [
2830
- /* @__PURE__ */ jsx9("h1", { className: "rt-cms-title", children: post.title }),
2920
+ showTitle ? /* @__PURE__ */ jsx9("h1", { className: "rt-cms-title", children: post.title }) : null,
2831
2921
  post.excerpt ? /* @__PURE__ */ jsx9("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null,
2832
2922
  publishedDate || showTopAuthor || views !== null ? /* @__PURE__ */ jsxs9("p", { className: "rt-cms-meta", children: [
2833
2923
  publishedDate ? /* @__PURE__ */ jsx9("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "date", children: publishedDate }) : null,
@@ -2962,7 +3052,9 @@ function archiveGroupTitle(group) {
2962
3052
  function archivePostHref(collectionKey, slug, collections) {
2963
3053
  const collection = collections?.find((item) => item.key === collectionKey);
2964
3054
  const basePath = collection?.basePath ?? defaultArchiveBasePath(collectionKey);
2965
- return `${basePath.replace(/\/+$/, "")}/${slug}`;
3055
+ const trimmedBase = basePath.replace(/\/+$/, "");
3056
+ if (collection?.detail === false) return trimmedBase;
3057
+ return `${trimmedBase}/${slug}`;
2966
3058
  }
2967
3059
  function defaultArchiveBasePath(collectionKey) {
2968
3060
  if (collectionKey === "symptoms") return "/symptoms";
@@ -3087,7 +3179,13 @@ function defaultPostHref(post) {
3087
3179
  return `/blog/${post.slug}`;
3088
3180
  }
3089
3181
  function makeCollectionPostHref(collections) {
3090
- return (post) => resolvePostPath(post, collections) ?? defaultPostHref(post);
3182
+ return (post) => {
3183
+ const path = resolvePostPath(post, collections);
3184
+ if (path) return path;
3185
+ const collection = resolvePostCollection(post, collections);
3186
+ if (collection && collection.detail === false) return collection.basePath;
3187
+ return defaultPostHref(post);
3188
+ };
3091
3189
  }
3092
3190
  function formatPublishedDate2(iso) {
3093
3191
  const d = new Date(iso);
@@ -3375,4 +3473,4 @@ export {
3375
3473
  RootTalePage,
3376
3474
  RenderTiptap
3377
3475
  };
3378
- //# sourceMappingURL=chunk-RSNUZ4BF.js.map
3476
+ //# sourceMappingURL=chunk-75LHSTKC.js.map