/* /s/stay 목업 주입분 — 헤더 미니 플레이어 · 캐치프레이즈 전환. ★ 색·테두리·서체를 전부 템플릿 변수(--tpl-*)에서 가져온다. 값을 박으면 템플릿을 바꿀 때 플레이어만 옛 색으로 남는다. ★ 플레이어는 헤더 **안**이다. 하단 바는 모바일 탭바(fixed bottom:0 · 61px)를 덮었고, 상단 바는 헤더와 두 줄이 됐다. 헤더 안이면 화면에서 차지하는 줄이 늘지 않는다. */ /* ── 헤더에 꽂는 아이콘 세 개 ─────────────────────────────────────── */ #w4d-mini { display: inline-flex; align-items: center; gap: 2px; margin-right: 6px; padding-right: 8px; border-right: 1px solid var(--tpl-border, #bcb49e); } #w4d-mini button { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; color: var(--tpl-text, #1b1a15); background: transparent; border: 0; border-radius: var(--tpl-radius, 0); cursor: pointer; opacity: 0.75; } #w4d-mini button:hover { opacity: 1; background: rgb(27 26 21 / 0.06); } #w4d-mini svg { width: 16px; height: 16px; } /* 지금 나오는 곡명. 좁은 화면에서는 헤더에 자리가 없어 접는다 — 실측(390px): 상호 115px + 오른쪽 묶음 94px 이라 남는 폭이 55px 뿐이다. 그 화면에서는 목록을 열면 재생 중인 줄에 곡명과 시간이 있다. */ #w4d-now { max-width: 150px; overflow: hidden; font-size: 14px; font-weight: 700; white-space: nowrap; text-overflow: ellipsis; color: var(--tpl-accent, #bf2f1b); padding-right: 2px; } #w4d-mini[data-playing='0'] #w4d-now { opacity: 0.85; font-weight: 400; } @media (max-width: 860px) { #w4d-now { display: none; } } /* 카세트 — 단추가 아니라 표지다. 이 자리가 무엇인지 한눈에 말해 준다. */ #w4d-tape { display: inline-flex; align-items: center; padding: 0 4px 0 2px; color: var(--tpl-text, #1b1a15); opacity: 0.8; } #w4d-tape svg { width: 20px; height: 20px; } #w4d-mini[data-playing='1'] #w4d-tape { color: var(--tpl-accent, #bf2f1b); opacity: 1; } /* 재생 중에는 릴이 돈다 — 소리가 나고 있다는 것을 글자 없이 말한다. */ #w4d-mini[data-playing='1'] .w4d-reel { transform-box: fill-box; transform-origin: center; animation: w4d-reel 2.6s linear infinite; } @keyframes w4d-reel { to { transform: rotate(360deg); } } @media (prefers-reduced-motion: reduce) { .w4d-reel { animation: none !important; } } /* 재생 중일 때만 강조색 — 헤더에 곡명을 안 두므로 색이 유일한 '지금 나온다' 표시다. */ #w4d-mini button[data-playing='1'] { color: var(--tpl-accent, #bf2f1b); opacity: 1; } @media (max-width: 400px) { #w4d-mini { margin-right: 2px; padding-right: 4px; } #w4d-mini button { width: 27px; height: 27px; } } /* ── 목록 패널 (헤더 아래에 떠 있다) ────────────────────────────── ★ 좁은 화면에서는 좌우 8px 만 남기고 펼친다. 320px 고정이었을 때 390px 화면에서 제목이 두 줄로 접히고 시간이 잘렸다(2026-09-11 사장님: "모바일 대응이 안 되어 있음"). */ #w4d-panel { position: fixed; z-index: 45; /* 사이트 헤더가 z-40 이라 그 위 */ width: 344px; max-width: calc(100vw - 16px); max-height: min(64vh, 520px); display: flex; flex-direction: column; overflow: hidden; font-family: var(--tpl-font-body, serif); color: var(--tpl-text, #1b1a15); background: var(--tpl-card, #efe7d3); border: var(--tpl-border-width, 2px) solid var(--tpl-primary, #1b1a15); box-shadow: var(--tpl-shadow, 4px 4px 0 rgb(27 26 21 / 0.16)); } #w4d-panel[hidden] { display: none; } @media (max-width: 640px) { #w4d-panel { width: auto; } } #w4d-panel .w4d-head { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; padding: 11px 8px 11px 14px; border-bottom: var(--tpl-border-width, 2px) solid var(--tpl-primary, #1b1a15); } .w4d-head-t { font-size: 15px; font-weight: 700; letter-spacing: 0.06em; } .w4d-head-n { flex: 1 1 auto; font-size: 14px; opacity: 0.5; } #w4d-panel .w4d-head button { display: inline-flex; width: 28px; height: 28px; flex: 0 0 auto; align-items: center; justify-content: center; color: inherit; background: transparent; border: 0; cursor: pointer; opacity: 0.6; } #w4d-panel .w4d-head button:hover { opacity: 1; } #w4d-panel .w4d-head svg { width: 14px; height: 14px; } #w4d-panel ol { flex: 1 1 auto; margin: 0; padding: 4px 0; overflow-y: auto; list-style: none; -webkit-overflow-scrolling: touch; } /* 한 줄 = 판 + 제목/가수 + 시간. 48px 아래로 내리지 않는다 — 손가락으로 누르는 자리다. */ .w4d-item { display: flex; width: 100%; min-height: 52px; align-items: center; gap: 11px; padding: 8px 14px; color: inherit; background: transparent; border: 0; font: inherit; text-align: left; cursor: pointer; } .w4d-item + .w4d-item { border-top: 1px dashed var(--tpl-border, #bcb49e); } .w4d-item:hover { background: rgb(27 26 21 / 0.05); } .w4d-item[aria-current='true'] { background: rgb(27 26 21 / 0.06); } .w4d-item[aria-current='true'] .w4d-t { color: var(--tpl-accent, #bf2f1b); } /* 판 자리. 재생 중인 줄에서는 판 대신 막대가 뛴다. */ .w4d-mark { position: relative; flex: 0 0 26px; height: 26px; } .w4d-disc-sm { position: absolute; inset: 0; border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--w4d-lbl, #bf2f1b) 0 34%, transparent 34%), repeating-radial-gradient(circle at 50% 50%, rgb(255 255 255 / 0.12) 0 1px, transparent 1px 3px), #14110f; } .w4d-disc-sm::after { content: ''; position: absolute; inset: 45%; border-radius: 50%; background: var(--tpl-card, #efe7d3); } .w4d-eq { position: absolute; inset: 0; display: none; align-items: flex-end; justify-content: center; gap: 3px; padding-bottom: 4px; } .w4d-eq i { width: 3px; height: 6px; background: var(--tpl-accent, #bf2f1b); animation: w4d-eq 0.9s ease-in-out infinite; } .w4d-eq i:nth-child(2) { animation-delay: 0.18s; } .w4d-eq i:nth-child(3) { animation-delay: 0.36s; } @keyframes w4d-eq { 0%, 100% { height: 5px; } 50% { height: 17px; } } .w4d-item[data-playing='1'] .w4d-disc-sm { display: none; } .w4d-item[data-playing='1'] .w4d-eq { display: flex; } @media (prefers-reduced-motion: reduce) { .w4d-eq i { animation: none; height: 11px; } } .w4d-info { min-width: 0; flex: 1 1 auto; } .w4d-t { display: block; overflow: hidden; font-size: 15px; font-weight: 700; white-space: nowrap; text-overflow: ellipsis; } .w4d-sub { display: block; margin-top: 2px; overflow: hidden; font-size: 14px; white-space: nowrap; text-overflow: ellipsis; opacity: 0.85; } .w4d-time { flex: 0 0 auto; font-size: 14px; opacity: 0.6; font-variant-numeric: tabular-nums; } /* 가사 — 우리 곡만. 종이 위 인쇄물처럼 둔다. */ #w4d-lyrics-box { padding: 10px 12px 14px; border-top: 1px dashed var(--tpl-border, #bcb49e); white-space: pre-line; font-size: 15px; line-height: 1.9; } #w4d-lyrics-box[hidden] { display: none; } /* ★ 이용 정보 맨 아래 가로선을 지운다 (2026-09-10 사장님 지시).
이 아니라 바로 뒤 '예약은 아래로 받습니다' 블록의 border-t 다 — 마지막 항목 밑에 선이 한 줄 더 그어져 빈 칸이 하나 더 있는 것처럼 보인다. 항목 자체는 그대로 둔다. 마크업을 고치면 하이드레이션이 되돌리므로 CSS 로 끈다. */ #root div.border-line.flex.flex-col.gap-3.border-t.pt-6 { border-top-width: 0; } /* 사진 저작자 표시 — 출처 줄과 같은 크기, 한 단 더 옅게. */ .w4d-credit { margin-top: 2px; font-size: 14px; opacity: 0.85; } /* ★ 모바일 헤더에서 전화 단추를 접는다 (2026-09-11 사장님: "저 전화 아이콘은 빼도 되잖아"). 좁은 화면 헤더에 상호 + 플레이어 3개 + 전화 + 메뉴가 다 서서 난잡했다. ★ 전화가 사라지는 게 아니다 — 이 화면 폭에서는 **하단 탭바에 전화가 이미 있다** (실측: NAV fixed bottom:0 · 위치 · 전화 · 네이버 예약). 같은 것이 두 번 서 있던 것이다. ★ 넓은 화면에서는 탭바가 없으므로 그대로 둔다. */ @media (max-width: 860px) { #root header .shell > div:last-child > a[href^="tel:"] { display: none; } } /* 덧붙인 링크 — 본문 글자보다 눈에 띄되 강조색을 남발하지 않는다. */ .w4d-link { color: color-mix(in oklab, var(--tpl-accent, #bf2f1b) 62%, var(--tpl-text, #1b1a15)); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; } /* ── 캐치프레이즈 등장 ──────────────────────────────────────────── ★ 줄 전체를 페이드하지 않고 **단어가 마스크 안에서 올라온다.** 한 줄짜리 문장은 통째로 흐려지면 '바뀌었다'만 보이고 문장이 안 읽힌다 — 왼쪽부터 차례로 서면 눈이 첫 단어에서 읽기를 시작한다. ★ 나갈 때는 반대로 위로 빠지며 흐려진다. 들어오는 방향과 나가는 방향이 같으면 두 문장이 같은 자리에서 겹쳐 보인다. ★ 자리 흔들림: 처음 구워진 줄 높이를 min-height 로 박아 둔다(스크립트가 잰다). 문장 길이가 달라 한 줄↔두 줄이 오갈 때 히어로 아래가 튀는 것을 막는다. */ /* 대표 문구 아래에서 도는 줄 (2026-09-11). 대표 문구보다 한 단 작고 옅게 둔다 — 둘이 같은 크기·같은 농도면 어느 쪽이 숙소의 한 줄인지 안 읽힌다. */ .w4d-sub { margin-top: 0.5rem; font-size: var(--fs-sm, 0.9375rem); line-height: 1.6; opacity: 0.75; } .w4d-line { display: block; } .w4d-line--out { opacity: 0; transform: translateY(-5px); filter: blur(3px); transition: opacity 0.3s ease, transform 0.3s ease, filter 0.3s ease; } .w4d-line--in { opacity: 1; transform: none; filter: none; transition: opacity 0.3s ease, transform 0.3s ease, filter 0.3s ease; } /* 단어 한 칸 = 마스크. 안쪽 글자가 아래에서 올라온다. */ .w4d-w { display: inline-block; overflow: hidden; vertical-align: bottom; } .w4d-w > span { display: inline-block; transform: translateY(112%); opacity: 0; filter: blur(4px); } .w4d-line--in .w4d-w > span { animation: w4d-rise 0.62s cubic-bezier(0.16, 0.84, 0.28, 1) forwards; animation-delay: var(--w4d-d, 0ms); } @keyframes w4d-rise { from { transform: translateY(112%); opacity: 0; filter: blur(4px); } 60% { filter: blur(0); } to { transform: translateY(0); opacity: 1; filter: blur(0); } } @media (prefers-reduced-motion: reduce) { .w4d-line--out, .w4d-line--in { transition: none; opacity: 1; transform: none; filter: none; } .w4d-w > span { transform: none; opacity: 1; filter: none; animation: none; } } /* 가사 — 우리 곡만. 종이 위 인쇄물처럼 둔다. */ #w4d-lyrics-box { max-width: 1120px; margin: 0 auto; padding: 12px 16px 16px; border-top: 1px dashed var(--tpl-border, #bcb49e); white-space: pre-line; font-size: 15px; line-height: 1.9; max-height: 40vh; overflow-y: auto; } #w4d-lyrics-box[hidden] { display: none; } /* * 일정 정거장 번호 — 제목 글자와 세로 중심을 맞춘다. * * 번호 동그라미는 inline-grid 라 **안에 든 숫자(10px)의 베이스라인**이 제목(14px)의 * 베이스라인에 붙는다. 숫자가 17px 상자 한가운데 있으니 동그라미 중심은 그만큼 아래로 * 내려가고, 렌더러가 얹은 translate-y-px 가 1px 더 내린다. * 실측(1280·390 공통): 동그라미 중심이 제목 줄 중심보다 3.14px 아래. * 2px 올리면 0.14px — 반올림해서 눈에 안 걸리는 자리다. */ #itinerary ol li span.inline-grid.rounded-full { translate: 0 -2px; transform: none; } /* * ── 가요 다방 판꽂이 ────────────────────────────────────────────────────── * * ★ 되짚음 (2026-09-14 대표: "모바일 대응이 안 되어있는듯? 무슨 노래인지 모르잖아, * 모바일에서는 hover가 없으니까"). 맞는 지적이고, **아래는 내가 만든 회귀였다.** * 원래 렌더러(`SongsSection.tsx`)는 판 아래 곡명을 **항상** 보여준다 — * `{song.title}`, * 숨기는 코드가 없다. 그런데 같은 날 앞선 지적("LP판이 많아져서 보기가 이상하잖아", * 25곡이 `flex-wrap` 으로 3~9줄이 되던 문제)에 답하며 판을 겹쳐 꽂고 **곡명을 * hover 로만 띄우게** 고쳤다 — 데스크톱 벽은 없앴지만 손가락엔 hover 가 없으니 * 곡명이 아예 안 보이는 화면이 됐다. 데스크톱만 보고 고친 값이다. * ★ 다시 짠다 — **곡명은 항상 보인다. 줄은 접지 않고 가로 한 줄로만 편다.** * 판이 25곡이든 50곡이든 줄 수는 하나고, 안 보이는 나머지는 옆으로 스크롤한다. * 이 페이지의 다른 레일(주변 명소·인물 열전)이 이미 쓰는 것과 같은 해법이라 — * "hover 로 감추기" 는 애초에 필요 없던 처방이었다. * ★ 마크업을 고치지 않는다(React 가 되돌린다). `:has()` 로 판이 든 상자만 집는다. * 곡명 `` 은 원래도 절대배치가 아니라 `block` 이라 손대지 않는다 — 화면에 * 보이려고 위치를 옮길 필요가 없다, 그냥 숨기지만 않으면 된다. * ★ **화살표 + 실제 카로셀 라이브러리**(2026-09-14 대표: "옆으로 갈수있는 버튼도 주고 * slider.js 적용해줘"). `inject.js` `startSongsRail()` 이 이 판 목록을 감싸는 상자를 * 하나 만들어 `.slider-viewport` 로 표시하고(embla-carousel 코어를 그 위에 붙인다 — * "군산 읽기" 탭과 같은 라이브러리, `vendor/embla-carousel.umd.js`), 그 안에 원래 * 판 목록(:has() 로 잡는 이 상자)을 그대로 넣는다. **가로 스크롤·snap 은 이제 * `.slider-viewport` 쪽 몫이다** — 그 클래스가 이미 그 CSS 를 갖고 있어(전역 번들) * 여기서 다시 안 만든다. 이 규칙은 **안쪽 판 줄의 레이아웃(줄바꿈 금지·간격·판 크기)만** 잡는다. */ #songs div:has(> button > .w4-disc-mini) { flex-wrap: nowrap; justify-content: flex-start; gap: 14px; padding: 6px 4px 14px; /* 렌더러가 이 div 자체에 준 mt-4 를 지운다 — 지금은 이 div 가 `.slider-viewport` 안에 들어가 있어, 그 여백이 화살표 줄(위)이 아니라 스크롤 상자 안쪽 빈 공간이 됐다. 간격은 화살표 줄의 mt-4(inject.js `startSongsRail`)가 대신 준다. */ margin-top: 0; } #songs button:has(> .w4-disc-mini) { flex: 0 0 auto; width: 68px; /* 원래 96px(`w-24`) — 한 줄로만 펴니 그만큼은 줄여도 된다 */ transition: transform 0.16s ease; } #songs button:has(> .w4-disc-mini) > .w4-disc-mini { width: 56px; height: 56px; } /* 곡명 — 렌더러가 원래 그리는 그 글자다. 여기서는 줄어든 판 폭에 맞춰 여백만 좁힌다. */ #songs button:has(> .w4-disc-mini) > .w4-disc-mini + span { margin-top: 5px; font-size: 14px; } /* 고른 판만 살짝 들어 올린다 — "뽑아 든 판" 표시. 선택 자체(테두리)는 React 가 인라인 스타일로 이미 그린다(`boxShadow`); 여기선 그 위에 얹는 동작만 더한다. */ #songs button:has(> .w4-disc-mini):hover { transform: translateY(-4px); } #songs button[aria-pressed='true']:has(> .w4-disc-mini) { transform: translateY(-6px); } @media (prefers-reduced-motion: reduce) { #songs button:has(> .w4-disc-mini) { transition: none; } } /* `#songs .slider-viewport` 로 스코프한다 — `.slider-viewport` 를 전부 건드리면 "군산 읽기" 카로셀과 이 페이지 다른 실제 레일까지 같이 바뀐다. */ #songs .slider-viewport { -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 28px), transparent 100%); mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 28px), transparent 100%); } /* * ── 엽서 쓰기 (inject.js ⑥) ─────────────────────────────────────────────── * `#location` 과 `#festival` 사이의 **독립 섹션**이다(2026-09-14 대표 지시로 '오늘의 * 엽서' 탭에서 빠져나왔다). 다른 실제 섹션과 같은 껍데기(`.shell` · `--section-space` * 세로 여백)를 쓰고, 제목·부제도 `ItemSection` 이 쓰는 것과 같은 전역 클래스(`.h2` * `.measure`)라 이 섹션만 따로 CSS 를 두지 않는다 — 안쪽 위젯(캔버스·폼)만 여기서 잡는다. */ #w4d-pm-section { width: 100%; padding-block: var(--section-space, 64px); background: var(--tpl-surface, #fafafa); color: var(--tpl-text, #1b1a15); } .w4d-pm-layout { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; } .w4d-pm-canvas { /* ★ 300px 이던 것을 키웠다(2026-09-14 대표: "엽서 작아서 깨지는데?"). 실제로 그리는 건 1080×1080 이라 화질 문제는 아니지만, 미리보기가 작으면 안의 문구·소인이 안 읽혀 "제대로 안 나온 것" 처럼 보인다 — 미리보기 크기를 실물 크기에 맞춘다. */ width: 420px; max-width: 100%; aspect-ratio: 1 / 1; border: 1px solid var(--tpl-border, #bcb49e); background: var(--tpl-card, #efe7d3); } .w4d-pm-form { flex: 1 1 320px; min-width: 260px; } @media (max-width: 767px) { .w4d-pm-form { order: -1; } } /* 썸네일 줄 — `.slider-viewport`(뷰포트) 안에 `.slider-track`(트랙, 이 클래스 자체가 display:flex 를 준다)이 있다. 여기서는 간격·안쪽 여백만 얹는다(2026-09-14 대표: "이미지는 다 가져와서 그냥 카로셀로 보여주든가 하셈" — 9장 전체를 후보로 두면서 한 줄에 다 못 앉으니 카로셀이 됐다). */ .w4d-pm-thumbs-viewport { margin-bottom: 12px; } .w4d-pm-thumbs { gap: 8px; padding: 2px; } .w4d-pm-thumb { width: 52px; height: 52px; flex: 0 0 auto; padding: 0; overflow: hidden; border: 2px solid transparent; border-radius: 4px; cursor: pointer; } .w4d-pm-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; } .w4d-pm-thumb[aria-pressed='true'] { border-color: var(--tpl-accent, #bf2f1b); } .w4d-pm-text { width: 100%; box-sizing: border-box; padding: 10px 12px; font: inherit; font-family: var(--tpl-font-body, serif); font-size: 14px; line-height: 1.5; color: var(--tpl-text, #1b1a15); background: var(--tpl-surface, #fafafa); border: 1px solid var(--tpl-border, #bcb49e); resize: none; /* 2026-09-14 대표: "textarea 크기 못바꾸게" — 늘리면 옆 캔버스와 높이가 어긋난다 */ } .w4d-pm-count { margin: 4px 0 14px; font-size: 14px; text-align: right; opacity: 0.85; } .w4d-pm-actions { display: flex; flex-wrap: wrap; gap: 10px; } .w4d-pm-actions button { padding: 9px 16px; font-size: 15px; font-weight: 700; border: 1px solid var(--tpl-primary, #1b1a15); cursor: pointer; } .w4d-pm-share { background: var(--tpl-primary, #1b1a15); color: var(--tpl-bg, #fff); } .w4d-pm-save { background: transparent; color: var(--tpl-text, #1b1a15); } .w4d-pm-hint { margin: 10px 0 0; font-size: 14px; opacity: 0.6; } @media (max-width: 480px) { .w4d-pm-canvas { width: 100%; } } /* * ── 군산 읽기 (inject.js ③) ─────────────────────────────────────────────── * 렌더러에 없는 섹션이라 카드 내용 모양은 여기서 만든다. 색·서체는 전부 템플릿 변수에서 * 가져온다 — 값을 박으면 템플릿을 바꿀 때 이 섹션만 남의 집처럼 보인다(이 파일의 규약). * * ★ **카로셀 껍데기는 새로 만들지 않았다** — `.slider-viewport` · `.slider-track` 은 * 이 사이트가 이미 쓰는 클래스다(`site/src/lib/ui/Carousel.tsx`, 컴파일된 전역 CSS에 * 이미 있다). 여기서 정의하는 CSS 는 **카드 안 내용물**뿐이다. 점(dots)·간격도 그 컴포넌트가 * 쓰는 Tailwind 유틸리티(`h-1.5 rounded-full bg-current opacity-70` 등)를 그대로 쓴다. * ★ **포스트 하나씩** (2026-09-14 대표). 슬라이드 하나 = 꼭지 하나, 전체 폭을 다 쓴다 — * 3~4개를 욱여넣었더니 카드가 좁아져 본문이 한 줄로 접혔었다. */ /* '군산 이야기' 탭 하나로 접혀 들어왔다(2026-09-14) — 독립 섹션의 배경·제호는 없다. 나머지 다섯 탭 패널과 같은 폭(`.shell`)에, 위아래 여백만 얹는다. */ #w4d-reading-panel { padding: 26px 0 4px; color: var(--tpl-text, #1b1a15); font-family: var(--tpl-font-body, serif); } /* .w4d-read-kicker 삭제 — 2026-09-14 대표: "군산읽기만 타이틀이 없서 통일 시켜", 커스텀 캡션 대신 다른 네 탭과 같은 h2.h2 + p.measure 로 바꿨다(inject.js startReading()). */ /* ── 슬라이드 폭 ── `CarouselSlide` 의 관례와 같다: 모바일은 다음 장이 살짝 걸치게(86%), 데스크톱은 한 장이 크게 온다(560px 안팎). "다음 장이 걸쳐 보이는 것" 이 옆에 더 있다는 유일한 시각 신호라는 그 컴포넌트의 원칙을 그대로 따른다. */ .w4d-read-basis { width: 86%; } @media (min-width: 640px) { .w4d-read-basis { width: min(600px, 56%); } } /* ── 포스트 카드 — 슬라이드 하나 = 꼭지 하나. */ .w4d-read-card { height: 100%; padding: 18px 20px 16px; background: var(--tpl-card, #efe7d3); border: 1px solid var(--tpl-border, #bcb49e); border-radius: var(--tpl-radius, 0); } .w4d-read-page { margin: 0 0 8px; font-size: 14px; font-weight: 700; letter-spacing: 0.18em; color: color-mix(in oklab, var(--tpl-accent, #bf2f1b) 62%, var(--tpl-text, #1b1a15)); } .w4d-read-year { font-weight: 400; letter-spacing: 0.06em; opacity: 0.7; } .w4d-read-card h3 { margin: 0 0 10px; padding-bottom: 10px; border-bottom: 2px solid var(--tpl-primary, #1b1a15); font-family: var(--tpl-font-heading, var(--tpl-font-body, serif)); font-size: 21px; font-weight: 700; line-height: 1.3; } .w4d-read-body { margin: 0; font-size: 14.5px; line-height: 1.8; text-align: justify; word-break: keep-all; opacity: 0.88; } .w4d-read-src { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--tpl-border, #bcb49e); font-size: 14px; font-style: italic; opacity: 0.6; } .w4d-read-src a { color: inherit; text-decoration: underline; text-underline-offset: 2px; } /* .w4d-read-flag(확인필요 배지) 삭제 — 2026-09-14 대표: "확인필요 없애고 링크는 네이버 링크로", inject.js startReading() 도 같이 뺐다. */ @media (max-width: 640px) { .w4d-read-card h3 { font-size: 18px; } } /* ══ 사진 갤러리 쪽 넘김 (데스크톱만) ═══════════════════════════════════════ * ★ 왜 (2026-09-14 사장님: "사진갤러리 전체 렌더링 말고 페이징 좀, 너무 많아 데스크톱에서는") * 갤러리는 좁은 화면에서 한 장씩 미는 캐러셀이지만 넓은 화면에서는 4열 격자로 **전부** * 깔린다(`GallerySection.tsx` — `lg:grid lg:grid-cols-4`). 사진 37장이면 열 줄이 되어 * 페이지가 사진으로 끊긴다. * ★ 좁은 화면은 그대로 둔다 — 거기서는 한 장이 폭을 다 쓰는 캐러셀이라 이미 한 번에 하나다. * 그래서 숨김 규칙 전부가 `min-width: 1024px`(Tailwind `lg`) 안에 있다. * ★ 마크업을 지우지 않고 **속성으로만** 가린다. React 가 payload 로 다시 그리면 잘라낸 *
  • 는 돌아오지만, 그때는 주입 JS 가 속성을 다시 찍는다(inject.js ⑧). * 그리고 사진이 DOM 에 남아 있어야 alt 문장이 문서에 남는다 — 이미지 검색이 그걸 읽는다 * (같은 이유가 `GallerySection.tsx` 머리주석에도 있다). */ .w4d-gal-pager { display: none; } @media (min-width: 1024px) { #gallery ul[data-w4d-gal] > li[data-w4d-gal-on='0'] { display: none; } .w4d-gal-pager { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 18px; } .w4d-gal-pager button { min-width: 30px; height: 30px; padding: 0 8px; border: 1px solid var(--tpl-border, #bcb49e); border-radius: 8px; background: transparent; color: inherit; font-size: 15px; font-variant-numeric: tabular-nums; line-height: 1; cursor: pointer; transition: background-color 0.15s ease, opacity 0.15s ease; } .w4d-gal-pager button:hover:not(:disabled) { background: color-mix(in srgb, currentColor 8%, transparent); } .w4d-gal-pager button:disabled { opacity: 0.3; cursor: default; } .w4d-gal-pager button[aria-current='true'] { background: var(--tpl-primary, #1b1a15); border-color: var(--tpl-primary, #1b1a15); color: var(--tpl-bg, #fff); font-weight: 700; } .w4d-gal-total { margin-left: 8px; font-size: 14px; opacity: 0.85; } } /* ── 768~1023px 에서 하단 탭바가 푸터 마지막 줄을 덮는 것 막기 ───────────────── ★ 실측(2026-09-14 대표 스크린샷, 태블릿 폭 Edge): 푸터(SiteFooter.tsx)는 768px 부터 아래 여백을 줄이는데(pb-28 → md:pb-14), 하단 고정 탭바(MobileTabBar.tsx)는 1024px 이 되기 전엔 그대로 떠 있다(lg:hidden). 그래서 768~1023px 사이에서만 푸터 마지막 줄이 탭바에 깔린다 — 두 컴포넌트가 서로 다른 기준(md·lg)으로 "이제 모바일 아니다"를 판단해 생기는 폭이다. ★ 진짜 수정은 렌더러 쪽(`solution/site/src/sections/SiteFooter.tsx`)에서 `md:pb-14` 를 탭바와 같은 `lg:pb-14` 로 맞추는 것인데, 그건 발행 사이트 전부에 영향을 주는 변경이라 다시 굽고(`docker compose restart solution-prerender`) 전부 재발행(`republish_all.py`)해야 한다 — 이 목업만 먼저 막는다. */ @media (min-width: 768px) and (max-width: 1023.98px) { footer { padding-bottom: 7rem !important; } } #w4d-mini[data-armed="1"] #w4d-play { animation: w4d-breathe 2.4s ease-in-out infinite; } @keyframes w4d-breathe { 0%, 100% { opacity: 1 } 50% { opacity: .45 } } @media (prefers-reduced-motion: reduce) { #w4d-mini[data-armed="1"] #w4d-play { animation: none } } h3.border-line.mb-3.border-b.pb-2 { border-bottom: 0; padding-bottom: 0; } #units .rounded-md.border-line.tpl-border { border-radius: 999px; border-width: 0; padding-inline: 0.9rem; background-color: color-mix(in srgb, var(--tpl-text, #1b1a15) 8%, transparent); } /* 축제 각주 삭제 (2026-09-17 대표 지시) — 렌더러는 그대로 두고 시연본에서만 감춘다. */ #festival > div > p.text-muted:last-child { display: none; }