From 82aa56fa401eb20fa9ed55fe40f372199562b03f Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Sat, 12 Sep 2026 15:21:31 +0900 Subject: [PATCH] =?UTF-8?q?chore(supporters):=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=EB=92=A4=20=ED=85=9C=ED=94=8C=EB=A6=BF=20=EC=9E=AC=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(=EC=A3=BC=EB=B3=80=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8,=20=EC=98=81=EC=96=B4=20=ED=99=88=20?= =?UTF-8?q?=EC=95=88=EC=A0=84=20=EA=B8=B0=EB=B3=B8=EA=B0=92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정본 supporters/ 와 템플릿의 페이지 집합이 같아졌다. /stay·/en/stay·/en·/plan·/en/plan 이 양쪽에 모두 있다. 검증 - 뷰 34페이지, 게이트 41/41, check.mjs 오류 0건. - 빈 데이터 템플릿 16페이지, 영어 홈에 병원 고유 사실 0건, 문장 성립. - 내보내기 종료코드 0, tsc 0 에러. Co-Authored-By: Claude Opus 5 (1M context) --- templates/supporters-astro/src/data/site.json | 6 +- .../supporters-astro/src/layouts/Base.astro | 2 + templates/supporters-astro/src/lib/i18n.ts | 5 +- .../supporters-astro/src/pages/sitemap.xml.ts | 2 + .../supporters-astro/src/styles/global.css | 177 ++++++++++++++++++ 5 files changed, 189 insertions(+), 3 deletions(-) diff --git a/templates/supporters-astro/src/data/site.json b/templates/supporters-astro/src/data/site.json index 2790d81..ec8c774 100644 --- a/templates/supporters-astro/src/data/site.json +++ b/templates/supporters-astro/src/data/site.json @@ -24,5 +24,9 @@ "sponsorNotice": "", "ga4MeasurementId": "", "_comment_ga4": "GA4 측정 ID(G-XXXX). 비우면 태그를 넣지 않는다. 키 이벤트 이름은 data/ai_channels.json conversion_events 와 같다.", - "heroImageAltEn": "" + "heroImageAltEn": "", + "indexNowKey": "", + "googleSiteVerification": "", + "naverSiteVerification": "", + "_comment_publish": "indexNowKey 는 워커 data 단계가 자동 생성(public/.txt). googleSiteVerification·naverSiteVerification 은 각 콘솔의 HTML 태그 content 값을 사람이 넣는다(briefs//clinic.json site 로 전달 가능). 발행 단계는 scripts/publish_site.mjs." } diff --git a/templates/supporters-astro/src/layouts/Base.astro b/templates/supporters-astro/src/layouts/Base.astro index 299a8a0..8691472 100644 --- a/templates/supporters-astro/src/layouts/Base.astro +++ b/templates/supporters-astro/src/layouts/Base.astro @@ -75,6 +75,7 @@ document.addEventListener('click',function(e){var a=e.target.closest&&e.target.c {u.nav_posts} {u.nav_videos} {u.nav_news} + {u.nav_stay} {u.nav_visit} {u.nav_plan} {u.nav_about} @@ -82,6 +83,7 @@ document.addEventListener('click',function(e){var a=e.target.closest&&e.target.c ) : ( <> {u.nav_home} + {u.nav_stay} {u.nav_plan} {u.nav_visit} {u.nav_clinic} diff --git a/templates/supporters-astro/src/lib/i18n.ts b/templates/supporters-astro/src/lib/i18n.ts index 68536d9..181a8ec 100644 --- a/templates/supporters-astro/src/lib/i18n.ts +++ b/templates/supporters-astro/src/lib/i18n.ts @@ -6,6 +6,7 @@ export type Lang = 'ko' | 'en'; const PAIRS: [string, string][] = [ ['/', '/en'], ['/plan', '/en/plan'], + ['/stay', '/en/stay'], ]; export function switchHref(pathname: string, to: Lang): string { const p = pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, '').replace(/\/$/, '') || '/'; @@ -21,7 +22,7 @@ export const UI: Record> = { ko: { sample: '샘플 사이트입니다. 정식 공개 전입니다.', brand: '서포터즈', - nav_posts: '상담 전 질문', nav_videos: '의료진 영상', nav_news: '뉴스룸', nav_visit: '방문 안내', nav_plan: '회복 일정', nav_about: '매체 소개', + nav_posts: '상담 전 질문', nav_videos: '의료진 영상', nav_news: '뉴스룸', nav_stay: '주변 안내', nav_visit: '방문 안내', nav_plan: '회복 일정', nav_about: '매체 소개', cta: '상담 예약', back: '이전 페이지', backAria: '이전 페이지로 돌아가기', totop: '맨 위로', switchLabel: 'EN', switchAria: 'Switch to English', @@ -33,7 +34,7 @@ export const UI: Record> = { en: { sample: 'Sample site. Not yet public.', brand: 'Supporters', - nav_home: 'Home', nav_plan: 'Recovery Planner', nav_visit: 'Getting Here', nav_clinic: 'Clinic Facts', nav_korean: '한국어 전체 보기', + nav_home: 'Home', nav_stay: 'Nearby', nav_plan: 'Recovery Planner', nav_visit: 'Getting Here', nav_clinic: 'Clinic Facts', nav_korean: '한국어 전체 보기', cta: 'Book a consultation', back: 'Back', backAria: 'Go back to the previous page', totop: 'Back to top', switchLabel: 'KO', switchAria: '한국어로 보기', diff --git a/templates/supporters-astro/src/pages/sitemap.xml.ts b/templates/supporters-astro/src/pages/sitemap.xml.ts index 5a6ffd9..574b8bf 100644 --- a/templates/supporters-astro/src/pages/sitemap.xml.ts +++ b/templates/supporters-astro/src/pages/sitemap.xml.ts @@ -12,6 +12,8 @@ export const GET: APIRoute = async ({ site }) => { { loc: `${base}/clinic`, lastmod: today }, { loc: `${base}/visit`, lastmod: today }, { loc: `${base}/en`, lastmod: today }, + { loc: `${base}/stay`, lastmod: today }, + { loc: `${base}/en/stay`, lastmod: today }, { loc: `${base}/plan`, lastmod: today }, { loc: `${base}/en/plan`, lastmod: today }, { loc: `${base}/newsroom`, lastmod: today }, diff --git a/templates/supporters-astro/src/styles/global.css b/templates/supporters-astro/src/styles/global.css index aaed710..39b3144 100644 --- a/templates/supporters-astro/src/styles/global.css +++ b/templates/supporters-astro/src/styles/global.css @@ -280,3 +280,180 @@ td { color: var(--slate-700); } .site-header .nav a { margin-left: 0; flex: none; font-size: 0.9rem; } } +/* ── /en/stay ── */ +.stay { padding-bottom: 4rem; } +.stay .lede { color: var(--slate-600, #475569); margin-top: 0.6rem; } +.stay section { margin-top: 3.4rem; } +.stay .sec-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } +.stay .sec-head h2 { margin: 0; } +.stay .upd { font-size: 0.8rem; color: var(--slate-500, #64748B); } +.stay .sub { color: var(--slate-600, #475569); margin: 0.4rem 0 1rem; } +.stay .src { font-size: 0.84rem; color: var(--slate-500, #64748B); margin-top: 1rem; } +.stay .chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.2rem 0 1.6rem; } +.stay .chip { + border: 1px solid var(--line, #E2E8F0); background: #fff; border-radius: 999px; + padding: 0.36rem 0.85rem; font: inherit; font-size: 0.86rem; color: var(--slate-600, #475569); + cursor: pointer; transition: background 0.15s, color 0.15s, border-color 0.15s; +} +.stay .chip b { font-weight: 700; opacity: 0.55; margin-left: 0.25em; } +.stay .chip:hover { border-color: var(--primary-900, #0A1128); } +.stay .chip.on { background: var(--primary-900, #0A1128); border-color: var(--primary-900, #0A1128); color: #fff; } +.stay .chip.on b { opacity: 0.7; } +.stay .grp { margin-bottom: 2.2rem; } +.stay .grp h3 { margin: 0 0 0.9rem; } +.stay .grp h3 small { font-weight: 400; font-size: 0.82rem; color: var(--slate-500, #64748B); margin-left: 0.45em; } +.stay .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); gap: 1rem; } +.stay .card { + background: #fff; border: 1px solid var(--line, #E2E8F0); border-radius: 14px; + overflow: hidden; display: flex; flex-direction: column; + box-shadow: 3px 4px 12px rgba(0, 0, 0, 0.05); +} +.stay .card .thumb { position: relative; height: 148px; background: #EEF1F6; display: flex; align-items: center; justify-content: center; } +.stay .card .thumb img { width: 100%; height: 148px; object-fit: cover; display: block; } +.stay .card .nophoto { + font-size: 1.02rem; font-weight: 700; color: var(--slate-500, #64748B); + padding: 0 1rem; text-align: center; line-height: 1.35; word-break: keep-all; +} +.stay .card .badge { + position: absolute; left: 0.6rem; bottom: 0.6rem; + background: rgba(10, 17, 40, 0.82); color: #fff; border-radius: 6px; + padding: 0.18rem 0.45rem; font-size: 0.74rem; font-weight: 600; +} +.stay .card .badge em { font-style: normal; opacity: 0.7; margin-left: 0.25em; } +.stay .card .badge.mono { letter-spacing: 0.06em; } +.stay .card .body { padding: 0.85rem 0.95rem 1rem; display: flex; flex-direction: column; flex: 1; } +.stay .card h4 { margin: 0 0 0.35rem; font-size: 0.98rem; } +.stay .card .when { font-size: 0.8rem; color: var(--slate-600, #475569); margin: 0 0 0.4rem; } +.stay .card .desc { font-size: 0.86rem; color: var(--slate-600, #475569); margin: 0 0 0.5rem; } +.stay .card .addr { font-size: 0.78rem; color: var(--slate-500, #64748B); margin: 0 0 0.6rem; } +.stay .card .go { font-size: 0.82rem; margin-top: auto; } +.stay .route { border: 1px solid var(--line, #E2E8F0); border-radius: 16px; padding: 1.3rem 1.4rem; margin-bottom: 1.4rem; background: #fff; } +.stay .route-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; } +.stay .route-head h3 { margin: 0; } +.stay .route .who { font-size: 0.84rem; color: var(--slate-500, #64748B); margin: 0.15rem 0 0; } +.stay .route .clock { font-size: 0.86rem; font-weight: 600; color: var(--primary-900, #0A1128); white-space: nowrap; } +.stay .route .blurb { color: var(--slate-600, #475569); margin: 0.7rem 0 1rem; } +.stay .route .map { width: 100%; height: auto; display: block; border-radius: 12px; } +.stay .route .maphint { font-size: 0.76rem; color: var(--slate-500, #64748B); margin: 0.4rem 0 1rem; } +.stay .timeline { list-style: none; padding: 0; margin: 0; } +.stay .timeline .move { font-size: 0.8rem; color: var(--slate-500, #64748B); margin: 0.35rem 0 0.35rem 1.05rem; } +.stay .timeline .stop { display: flex; gap: 0.8rem; align-items: flex-start; } +.stay .timeline .n { + flex: none; width: 26px; height: 26px; border-radius: 50%; + background: var(--primary-900, #0A1128); color: #fff; + font-size: 0.8rem; font-weight: 700; display: grid; place-items: center; margin-top: 0.1rem; +} +.stay .timeline .stop-t { font-size: 0.8rem; color: var(--slate-500, #64748B); } +.stay .timeline h4 { margin: 0.1rem 0 0.2rem; font-size: 0.98rem; } +.stay .timeline p { margin: 0; font-size: 0.86rem; color: var(--slate-600, #475569); } +.stay .botnote { margin-top: 1.6rem; } +/* 시술 후 관리 안내(병원 원문 인용). 동선 카드(.route)·정차(.stop) 룩을 그대로 쓰고 접이식 동작만 더한다. 새 색·크기 없음. */ +.stay details.recovery > summary.btn { list-style: none; cursor: pointer; } +.stay details.recovery > summary.btn::-webkit-details-marker { display: none; } +.stay details.recovery > summary.btn .hide { display: none; } +.stay details.recovery[open] > summary.btn .show { display: none; } +.stay details.recovery[open] > summary.btn .hide { display: inline; } +.stay details.recovery[open] > summary.btn { margin-bottom: 1.1rem; } +.stay details.recovery > .timeline { margin-top: 0.2rem; } +.stay .recovery .timeline > li { border-top: 1px solid var(--line, #E2E8F0); padding: 0.7rem 0; } +.stay .recovery .timeline > li:first-child { border-top: 0; padding-top: 0; } +.stay .recovery summary { cursor: pointer; list-style: none; } +.stay .recovery summary::-webkit-details-marker { display: none; } +.stay .recovery summary .stop { display: flex; } +.stay .recovery summary h4 { margin: 0.1rem 0 0; } +.stay .recovery details[open] summary h4 { text-decoration: underline; text-underline-offset: 0.2em; text-decoration-color: var(--slate-200, #E2E8F0); } +.stay .recovery details > ul { margin: 0.7rem 0 0.5rem 2.2rem; padding-left: 1.1rem; } +.stay .recovery details > ul li { font-size: 0.9rem; color: var(--slate-700, #334155); margin-bottom: 0.35rem; } +.stay .recovery details > p { margin: 0 0 0 2.2rem; font-size: 0.84rem; color: var(--slate-500, #64748B); } +@media (max-width: 640px) { .cards { grid-template-columns: 1fr; } } + +/* 섹션 점프. 머리말 아래에 붙어 스크롤을 따라온다. 헤더(68px) 아래에 걸리게 top 을 맞춘다. */ +.stay .jump { + position: sticky; top: 68px; z-index: 9; + display: flex; flex-wrap: wrap; gap: 0.45rem; + margin: 1.4rem 0 2.4rem; + padding: 0.62rem 0.68rem; + background: rgba(255, 255, 255, 0.58); + backdrop-filter: blur(16px) saturate(1.5); + -webkit-backdrop-filter: blur(16px) saturate(1.5); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 18px; + box-shadow: 0 8px 28px rgba(10, 17, 40, 0.09), inset 0 1px 0 rgba(255, 255, 255, 0.7); +} +.stay .jump a { + display: inline-flex; align-items: center; gap: 0.42rem; + padding: 0.46rem 0.9rem; + border-radius: 999px; + font-size: 0.86rem; font-weight: 600; letter-spacing: -0.005em; + color: var(--primary-900, #0A1128); + text-decoration: none; white-space: nowrap; + background: #fff; + border: 1px solid var(--line, #E2E8F0); + box-shadow: 0 1px 2px rgba(10, 17, 40, 0.06); + transition: transform 0.14s ease, box-shadow 0.14s ease, background 0.14s, border-color 0.14s, color 0.14s; +} +.stay .jump a:hover { + text-decoration: none; + transform: translateY(-1px); + border-color: #C5CBF5; + box-shadow: 0 4px 12px rgba(10, 17, 40, 0.12); +} +.stay .jump a:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(10, 17, 40, 0.08); } +.stay .jump a b { + font-weight: 700; font-size: 0.72rem; line-height: 1; + padding: 0.2rem 0.4rem; border-radius: 999px; + background: #EFF0FF; color: #3A3F7C; +} +/* 지금 보고 있는 섹션 */ +.stay .jump a.on { + background: linear-gradient(to right, #4F1DA1, #021341); + border-color: transparent; color: #fff; + box-shadow: 0 4px 14px rgba(79, 29, 161, 0.32); +} +.stay .jump a.on b { background: rgba(255, 255, 255, 0.22); color: #fff; } +/* 점프로 이동했을 때 제목이 스티키 바에 가리지 않게 */ +.stay section, .stay .grp { scroll-margin-top: 152px; } +@media (max-width: 640px) { + .stay .jump { top: 60px; gap: 0.35rem; padding: 0.5rem; border-radius: 14px; } + .stay .jump a { padding: 0.4rem 0.72rem; font-size: 0.8rem; } +} + +/* 필터 칩도 눌리는 것처럼 보이게 */ +.stay .chip { + box-shadow: 0 1px 2px rgba(10, 17, 40, 0.05); + transition: transform 0.14s ease, box-shadow 0.14s ease, background 0.15s, color 0.15s, border-color 0.15s; +} +.stay .chip:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(10, 17, 40, 0.1); } +.stay .chip:active { transform: translateY(0); } +.stay .chip.on { box-shadow: 0 4px 14px rgba(10, 17, 40, 0.22); } + +/* 오늘 날씨. 값을 못 받으면 hidden 이라 아예 그려지지 않는다. */ +.stay .wx { + display: flex; align-items: center; justify-content: space-between; + gap: 1.2rem; flex-wrap: wrap; + margin-top: 1.6rem; padding: 1rem 1.2rem; + background: linear-gradient(to right, #fff3eb, #e4cfff, #f5f9ff); + border-radius: 16px; +} +.stay .wx-now { display: flex; align-items: center; gap: 0.9rem; } +.stay .wx-temp { font-family: 'Playfair Display', serif; font-size: 2.5rem; font-weight: 700; line-height: 1; color: var(--primary-900, #0A1128); } +.stay .wx-unit { font-size: 1.1rem; vertical-align: 0.9rem; margin-left: 0.06em; opacity: 0.6; } +.stay .wx-cond { font-weight: 700; color: var(--primary-900, #0A1128); } +.stay .wx-meta { font-size: 0.8rem; color: var(--slate-600, #475569); margin-top: 0.15rem; } +.stay .wx-note { margin: 0; font-size: 0.88rem; color: var(--slate-700, #334155); max-width: 30rem; } + +/* 동선 지도. OSM 타일을 좌표로 깔고 그 위에 핀을 얹는다. 출처 표기는 OSM 정책상 필수다. */ +.stay .route .map { + position: relative; width: 100%; max-width: 680px; height: 340px; + border-radius: 12px; overflow: hidden; background: #E8EDF3; + border: 1px solid var(--line, #E2E8F0); +} +.stay .route .map-tiles { position: absolute; inset: 0; } +.stay .route .map-tiles .tile { position: absolute; width: 256px; height: 256px; background-size: 256px 256px; background-repeat: no-repeat; } +.stay .route .map-pins { position: absolute; inset: 0; width: 100%; height: 100%; } +.stay .route .map-credit { + position: absolute; right: 0; bottom: 0; + background: rgba(255, 255, 255, 0.82); color: var(--slate-600, #475569); + font-size: 0.66rem; padding: 0.1rem 0.35rem; border-top-left-radius: 6px; +} +