이 목업은 payload 가 없어 프리렌더 재굽기 대상이 아니다 — `index.html` 한 장이 유일본이고 자산이 지워지면 사람이 되돌려 넣어야 한다(CLAUDE.md 의 ★★ 함정). 지금까지 그 한 장을 이 맥에서만 만들고 있었다. 다른 사람이 이어받을 수 있도록 재료를 전부 올린다. `build/index.html` 은 생성물이라 이그노어 그대로다 — `patch_stay.py` 로 다시 나온다. - build_itinerary.py: 테마 21개 일정 생성. 좌표에서 이동시간을 계산하고(도보 4km/h, 1.5km 초과는 차 25km/h + 주차 5분) 입·퇴실·끼니 창을 맞춘다. 규칙 14종 감사가 **빌드 안**에 있어 하나라도 어기면 payload 를 쓰지 않고 멈춘다 — 검사가 빌드 밖에 있던 동안 뼈대를 고칠 때마다 안 보는 규칙이 생겼다(2026-09-11 REVIEW) - build_story.py: 노래 25곡·인물 57명. 사진은 위키백과 문서 pageimages 만 믿는다 (이름 검색은 동명이인을 끌고 온다 — 이수현→걸그룹, 박성현→골퍼) - patch_stay.py: 캐치프레이즈 100개·자작곡 5곡·객실 사진(A동 12/B동 10)을 넣고 payload 를 갈아 끼운 뒤 inject.css/js 를 `</body>` 앞에 주입해 index.html 을 짠다 - inject.js/css: React 가 다시 그려도 살아남아야 하는 다섯 가지(캐치프레이즈 순환· 헤더 미니 플레이어·지도 링크·사진 저작자 표시·카로셀 제어). `#root` 밖에 둔다 - audit-all.mjs / rails-test.mjs: 실제 브라우저로 34종 검사, 레일 13개 자동 넘김 전수 - README.md: 이어받는 사람이 먼저 읽는 문서. 배포·함정·§7 "내가 틀렸던 6가지" - PROMPTS.md / TEXT.md / REVIEW-2026-09-11.md: 문구 생성 프롬프트 · 전체 텍스트 · 검수 보고 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
330 lines
12 KiB
CSS
330 lines
12 KiB
CSS
/* /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: 11.5px;
|
|
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.55; 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: 12px; font-weight: 700; letter-spacing: 0.06em; }
|
|
.w4d-head-n { flex: 1 1 auto; font-size: 11px; 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: 13.5px;
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.w4d-sub {
|
|
display: block;
|
|
margin-top: 2px;
|
|
overflow: hidden;
|
|
font-size: 11px;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
opacity: 0.55;
|
|
}
|
|
.w4d-time {
|
|
flex: 0 0 auto;
|
|
font-size: 11px;
|
|
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: 12px;
|
|
line-height: 1.9;
|
|
}
|
|
#w4d-lyrics-box[hidden] { display: none; }
|
|
|
|
/* ★ 이용 정보 맨 아래 가로선을 지운다 (2026-09-10 사장님 지시).
|
|
<hr> 이 아니라 바로 뒤 '예약은 아래로 받습니다' 블록의 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: 10px; opacity: 0.55; }
|
|
|
|
/* ★ 모바일 헤더에서 전화 단추를 접는다 (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: var(--tpl-accent, #bf2f1b);
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* ── 캐치프레이즈 등장 ────────────────────────────────────────────
|
|
★ 줄 전체를 페이드하지 않고 **단어가 마스크 안에서 올라온다.**
|
|
한 줄짜리 문장은 통째로 흐려지면 '바뀌었다'만 보이고 문장이 안 읽힌다 —
|
|
왼쪽부터 차례로 서면 눈이 첫 단어에서 읽기를 시작한다.
|
|
★ 나갈 때는 반대로 위로 빠지며 흐려진다. 들어오는 방향과 나가는 방향이 같으면
|
|
두 문장이 같은 자리에서 겹쳐 보인다.
|
|
★ 자리 흔들림: 처음 구워진 줄 높이를 min-height 로 박아 둔다(스크립트가 잰다).
|
|
문장 길이가 달라 한 줄↔두 줄이 오갈 때 히어로 아래가 튀는 것을 막는다. */
|
|
.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: 13px;
|
|
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-11, 사장님: "날씨 애니메이션 지워줘")
|
|
* 그림 자체는 남는다. `prefers-reduced-motion` 에서 보이는 것과 같은 상태다
|
|
* (weather.css 가 "멈춘 상태도 그림으로 성립해야 한다"는 전제로 그려져 있다).
|
|
* 그림까지 없애려면 아래 두 줄의 주석을 푼다.
|
|
*/
|
|
#weather .w4-sky::before,
|
|
#weather .w4-sky::after {
|
|
animation: none !important;
|
|
}
|
|
/* #weather .w4-sky { display: none; } */
|