o2o-castad-frontend/src/styles/ssulbox-castad.css
Haewon Kam 0ca2140b98 feat: 썰박스 DS 통일과 Poster to Video 통합, 진입 화면 네비게이션 개편
썰박스를 ADO2 디자인 시스템으로 맞추고, 포스터 파이프라인 두 종을 파이프라인
탭에 추가한다. 탭이 4개가 되면서 진입 화면 네비게이션도 함께 손봤다.

## 썰박스 DS 통일
시나리오별 팔레트(조선=파랑·삼국지=빨강·그리스=녹·오디세이=청)와 이모지, 앰버
강조색(--color-ssul-accent)을 걷어냈다. 선택 표시는 castad 표준 문법(민트 테두리
+ 민트 틴트), 스피너는 퍼플, CTA는 퍼플 단색으로 통일했다. 색으로 구분하던 것을
없앤 대신 상태(선택)만 색으로 남긴다.

## Poster to Video 통합 (내부 시연 범위)
- 무빙 포스터(F1): 업로드 → 분석 → **검수 게이트** → 생성 → 완성 5단계 위저드.
  검수에서 나레이션·모션·메타태그를 포스터와 대조해 고치고 승인해야 렌더가 이어진다.
- 포스터 스타일링(F2): 위저드가 아닌 한 화면 도구. 여러 스타일을 갈아 끼우며
  비교하는 작업이라 단계를 나누면 왕복만 늘어난다.

P2V 서버(:8010)를 castad 백엔드가 아니라 **직접 호출**한다. 검증된 scripts를
subprocess로 감싼 단일 워커라 castad 백엔드에 흡수하려면 실행 환경(Higgsfield
CLI·ffmpeg·모델 키)을 통째로 이식해야 하기 때문이다. 호출은 utils/p2vApi.ts 한
파일에 격리했다 — 대외 공개 시 castad 백엔드에 /p2v/* 프록시를 세우고 카카오 인증·
크레딧 차감을 얹을 때 갈아끼울 지점이 여기 하나다.

인증은 단일 팀 키(P2V_ACCESS_KEY)다. 다른 오리진이라 쿠키가 실리지 않아 정적
산출물(영상·이미지·다운로드)은 p2vFile()이 ?key= 로 태운다.

F2는 영화 포스터 레퍼런스가 저작권 자산이라 **결과물의 외부 공개·상업 사용을
금한다**(퍼블릭 도메인 명화 제외). 화면 상단 고지 + 템플릿별 배포 등급 배지 +
내부 전용을 고른 순간에만 뜨는 경고로 표시한다.

## 진입 화면 네비게이션
- 밑줄 탭 → 아이콘 타일 + 기능명. 탭이 4개가 되자 "무빙 포스터"와 "포스터 스타일링"이
  글자로는 갈라지지 않아, 형태로 구분되게 했다. 아이콘은 ADO2 Design System 규칙
  (24×24 · fill none · stroke currentColor 2 · round)을 따르고 색은 넣지 않는다.
- 고른 기능의 한 줄 설명을 탭 아래에 붙였다.
- 간격 체계를 8px 그리드로 다시 잡았다(묶음 안 8~12 / 묶음 사이 20 / 섹션 28~32).
  이전에는 전부 10~24px이라 근접성으로 구조가 읽히지 않아 답답했다. 타일 간격은
  터치 타겟 최소 8dp를 지킨다.

## 버그 수정
- 진입 화면 세로 넘침 잘림: justify-content:center + overflow:hidden 조합이라
  콘텐츠가 길어지면 위로 넘친 부분에 스크롤로도 닿을 수 없었다(무빙 포스터에서
  로고 밴드 47px 잘림). 자식의 margin:auto 0 중앙정렬로 바꿔 해소.
- 사라진 P2V 잡: 404를 문구가 아니라 상태 코드로 판정한다(P2vNotFoundError).
  서버에서 지워진 잡 ID가 남으면 빈 진행 화면에서 멈췄다.
- 검수 화면 모바일: 좌우 여백 0(부모 위저드 컨테이너에 패딩이 없다) → 16px 부여.
  대조할 포스터를 편집 위로 올리고, 승인 CTA를 하단 탭바 위에 sticky로 고정.
- 한글 단어 중간 줄바꿈("애니메이/션") → word-break: keep-all.
- 입력·안내·제출 박스 높이를 48px로 통일.

## 카피
가게→업체, 크롤링→수집으로 어휘 통일.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:55:06 +09:00

494 lines
13 KiB
CSS

/* =====================================================
썰박스 — castad 통합용 신규 컴포넌트 스타일
ssulbox.css 는 원본에서 스크립트로 변환한 파일이라 직접 수정하지 않는다.
castad 안에서 새로 만든 화면(SsulCreateForm 등)의 스타일은 여기에 둔다.
모든 규칙은 원본과 동일하게 `.ssul-scope` 안에 가둔다.
===================================================== */
/* ssulbox.css 의 `.ssul-scope` 는 원본에서 전체 화면 셸(`#app`)이었던 탓에
`overflow: hidden` 을 갖는다. castad 안에서는 셸이 아니라 폼·화면 컨테이너로
쓰므로 그 클리핑이 **요소 바깥으로 그려지는 것들을 잘라 먹는다** —
선택 카드의 링(테두리 바깥 1px)이 좌우에서 잘리고, 버튼 hover 글로우도 잘린다.
생성물 파일은 직접 고치지 않으므로 여기서 되돌린다(0,2,0 이라 이긴다). */
.ssul-scope.ssul-create,
.ssul-scope.ssul-making {
overflow: visible;
}
/* ── 생성 폼 ─────────────────────────────────────── */
.ssul-scope.ssul-create {
width: 100%;
display: flex;
flex-direction: column;
color: var(--color-text-white);
}
/* 간격은 generation-flow.css 상단의 체계를 따른다 (묶음 안 8~12 / 묶음 사이 20 / 섹션 32) */
.ssul-create__step {
align-self: flex-start;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--color-mint);
/* 라벨과 그 라벨이 설명하는 것은 붙여야 한 덩어리로 읽힌다 */
margin-bottom: 12px;
}
/* 2단계 라벨. 강조를 1단계에만 두어 시선 순서를 만든다 */
.ssul-create__step.muted {
font-weight: 600;
color: var(--color-text-gray-400);
}
/* ── 시나리오 2x2 그리드 ─────────────────────────── */
.ssul-create__scenarios {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
width: 100%;
/* STEP 1 → STEP 2 는 주제가 바뀌는 자리라 섹션 간격 */
margin-bottom: 32px;
}
.ssul-scn {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
padding: 16px 14px;
/* 테두리 두께는 모든 상태에서 1px 로 고정한다. 선택 시 굵기를 바꾸면
border-box 안쪽 폭이 줄어 글자가 흔들린다 — 굵기는 box-shadow 링으로 더한다 */
border: 1px solid var(--color-border-white-10);
border-radius: 14px;
background: var(--color-bg-card);
color: inherit;
font-family: inherit;
text-align: left;
cursor: pointer;
overflow: hidden;
transition: border-color var(--transition-normal), transform var(--transition-normal);
}
.ssul-scn:hover {
border-color: rgba(255, 255, 255, 0.25);
transform: translateY(-1px);
}
/* 선택 표시 — castad .btn-select.selected 와 동일한 문법(민트 테두리 + 민트 틴트) */
.ssul-scn.active {
border-color: var(--color-mint);
background: var(--color-mint-10);
/* 링을 **테두리와 같은 색**으로 둔다. 색이 다르면 안쪽 테두리와 바깥 링이
갈라져 두 줄로 보인다. 같은 색이면 1px 테두리 + 1px 링이 이어져
2px 한 줄로 읽히고, 테두리 두께를 바꾸지 않아 글자도 흔들리지 않는다. */
box-shadow: 0 0 0 1px var(--color-mint);
}
.ssul-scn.active .ssul-scn__desc {
color: rgba(255, 255, 255, 0.92);
}
.ssul-scn__name {
position: relative;
font-size: 14px;
font-weight: 700;
}
.ssul-scn__desc {
position: relative;
font-size: 12px;
line-height: 1.4;
color: var(--color-text-gray-400);
}
/* ── 업장 검색 (ADO2 식 자동완성) ─────────────────── */
/* position: relative 는 드롭다운(.ssul-places)의 기준점이다 */
.ssul-create__search {
position: relative;
display: flex;
gap: 8px;
width: 100%;
/* 입력과 바로 아래 안내는 한 덩어리 — 붙여 둔다 */
margin-bottom: 10px;
}
/* 이 폼의 가로 박스(입력·안내·선택결과·제출)는 높이를 48px 로 맞춘다.
제각각이면(입력 46 / 안내 38 / 버튼 48) 세로로 쌓였을 때 리듬이 깨져
정렬이 틀어진 것처럼 보인다. 48px 은 CTA·터치 타겟 기준값이다. */
.ssul-create__input {
flex: 1;
min-width: 0;
min-height: 48px;
padding: 8px 16px;
border: none;
border-radius: 999px;
background: #fff;
color: #1f2937;
font-family: inherit;
font-size: 14px;
font-weight: 600;
letter-spacing: -0.006em;
}
.ssul-create__input::placeholder {
color: #9ca3af;
font-weight: 500;
}
.ssul-create__search-btn {
flex: none;
padding: 0 18px;
border: 1px solid var(--color-border-white-10);
border-radius: 999px;
background: var(--color-bg-card-inner);
color: var(--color-text-white);
font-family: inherit;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: opacity var(--transition-normal);
}
.ssul-create__search-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* ── 검색 결과 / 선택 / 안내 ─────────────────────── */
/* 입력창 아래로 겹쳐 뜨는 자동완성 드롭다운.
문서 흐름에 넣으면 타이핑할 때마다 아래 요소(비용·버튼)가 밀려 흔들린다. */
.ssul-places {
position: absolute;
top: calc(100% + 6px);
left: 0;
right: 0;
z-index: 20;
max-height: 260px;
overflow-y: auto;
margin: 0;
padding: 0;
list-style: none;
border: 1px solid var(--color-border-white-10);
border-radius: 12px;
background: var(--color-bg-card);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
.ssul-places li + li {
border-top: 1px solid var(--color-border-white-10);
}
.ssul-places button {
display: flex;
flex-direction: column;
gap: 3px;
width: 100%;
padding: 11px 14px;
border: none;
background: var(--color-bg-card);
color: inherit;
font-family: inherit;
text-align: left;
cursor: pointer;
}
.ssul-places button:hover {
background: var(--color-bg-card-inner);
}
.ssul-places b {
font-size: 14px;
font-weight: 700;
}
.ssul-places span {
font-size: 12px;
color: var(--color-text-gray-400);
}
/* 안내(.ssul-hint)와 같은 자리에 번갈아 나오므로 아래 간격도 같아야 한다 */
.ssul-picked {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
/* 안내(.ssul-hint)와 같은 자리에 번갈아 나오므로 하한을 맞춘다.
업체명 + 주소 2줄이라 실제로는 이보다 커지는데, 그건 내용이 늘어난 것이라 괜찮다 */
min-height: 48px;
padding: 11px 14px;
margin-bottom: 28px;
border: 1.5px solid var(--color-mint);
border-radius: 12px;
background: var(--color-mint-10);
}
.ssul-picked__body {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
flex: 1;
}
.ssul-picked__body b {
font-size: 14px;
font-weight: 700;
}
.ssul-picked__body span {
font-size: 12px;
color: var(--color-text-gray-400);
}
.ssul-hint {
/* 높이를 입력·제출과 같은 48px 로. 글이 길어 두 줄이 되면 그때만 늘어난다 */
display: flex;
align-items: center;
width: 100%;
min-height: 48px;
padding: 10px 14px;
/* 안내 다음은 비용·제출 묶음이라 섹션 간격을 준다 */
margin-bottom: 28px;
border-radius: 12px;
background: var(--color-bg-card-inner);
font-size: 12px;
line-height: 1.5;
color: var(--color-text-gray-300);
}
.ssul-hint.ok {
color: var(--color-mint);
}
/* 시나리오 확인 안내 — 앞에 점을 찍어 상태 표시임을 드러낸다 */
.ssul-hint.dot {
display: flex;
align-items: center;
gap: 8px;
color: var(--color-text-gray-300);
}
.ssul-hint.dot::before {
content: '';
flex: none;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--color-mint);
}
.ssul-hint.warn {
color: #ffb4a8;
}
/* ── 비용 고지 + 제출 ────────────────────────────── */
/* 비용 고지와 제출 버튼은 한 덩어리 — 누르기 직전에 읽어야 하므로 붙여 둔다 */
.ssul-create__cost {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0 4px;
margin-bottom: 12px;
font-size: 12px;
color: var(--color-text-gray-400);
}
.ssul-create__cost-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-weight: 700;
color: var(--color-mint);
}
.ssul-create__coin {
font-size: 14px;
line-height: 1;
}
/* castad .btn-cta 와 같은 문법 — 퍼플은 페이지에서 이 CTA 한 곳만 쓴다 */
.ssul-create__submit {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 48px;
border: none;
border-radius: 999px;
background: var(--color-purple);
color: var(--color-text-white);
font-family: inherit;
font-size: 16px;
font-weight: 700;
cursor: pointer;
box-shadow: var(--shadow-purple);
transition: background-color var(--transition-normal);
}
.ssul-create__submit:hover:not(:disabled) {
background: var(--color-purple-hover);
}
.ssul-create__submit:disabled {
opacity: 0.4;
cursor: default;
}
/* ── 생성 진행 화면 ──────────────────────────────── */
.ssul-scope.ssul-making {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 420px;
/* ssulbox.css 의 베이스 `.ssul-scope { height:100% }` (원본 전체화면 셸 규칙)가
여기까지 상속되어 이 박스 자체가 부모 높이를 꽉 채워버린다. 그러면 아래
margin:auto 가 나눠 가질 여유 공간이 0 이 되어 컨텐츠가 위로 쏠린다.
height:auto 로 되돌려 컨텐츠 크기만큼만 차지하게 해야 margin:auto 가 뜻대로
작동한다(부모 .wizard-page-container 가 height:100% flex column). */
height: auto;
/* `0 auto` 면 수평만 중앙이라 스텝퍼 바로 아래에 붙고 세로 여백이 전부
아래로 몰린다. `auto` 는 상하 여백도 균등 분배해 스텝퍼~바닥 사이
중앙에 놓인다. */
margin: auto;
padding: 48px 20px;
text-align: center;
gap: 10px;
color: var(--color-text-white);
}
.ssul-making__scenario {
font-size: 20px;
font-weight: 700;
color: var(--color-mint);
}
.ssul-making__title {
margin: 0 0 24px;
font-size: 24px;
font-weight: 700;
letter-spacing: -0.015em;
}
.ssul-making__title.warn {
color: #ffb4a8;
}
/* 진행 스피너 — 원본 LoadingSection 의 회전 요소만 가져왔다.
색은 ADO2 생성 스피너 표준(퍼플)을 따른다.
`spin` 키프레임은 castad 전역에 이미 있어 재사용한다(새로 만들면 3번째 중복). */
.ssul-making__spinner {
position: relative;
flex: none;
width: 150px;
height: 150px;
margin: 4px 0 26px;
}
/* 트랙(전체 원) */
.ssul-making__spinner::before {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
border: 7px solid var(--color-border-white-10);
}
/* 회전하는 호 — 3/4 만 칠해 원본 SVG 의 형태를 따른다 */
.ssul-making__spinner::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
border: 7px solid transparent;
border-top-color: var(--color-purple);
border-right-color: var(--color-purple);
border-bottom-color: var(--color-purple);
animation: spin 1.1s linear infinite;
}
.ssul-making__bar {
width: 100%;
height: 6px;
border-radius: 999px;
margin-top: 20px;
background: var(--color-bg-card-inner);
overflow: hidden;
}
.ssul-making__fill {
height: 100%;
border-radius: 999px;
background: var(--color-mint);
transition: width 400ms ease;
}
.ssul-making__meta {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin-top: 10px;
font-size: 16px;
color: var(--color-text-gray-300);
}
.ssul-making__pct {
font-variant-numeric: tabular-nums;
font-weight: 700;
}
.ssul-making__desc {
margin: 22px 0 0;
font-size: 14px;
line-height: 1.6;
color: var(--color-text-gray-400);
}
.ssul-making__refund {
margin: 8px 0 0;
font-size: 14px;
color: var(--color-mint);
}
.ssul-making__btn {
margin-top: 28px;
padding: 12px 24px;
border: 1px solid var(--color-border-white-10);
border-radius: 999px;
background: var(--color-bg-card);
color: var(--color-text-white);
font-family: inherit;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: border-color var(--transition-normal);
}
.ssul-making__btn:hover {
border-color: rgba(255, 255, 255, 0.3);
}
/* 진행바·스피너 애니메이션은 모션 민감 사용자에게 끈다.
스피너는 회전을 멈추면 3/4 호가 그대로 남아 정적 표시로 읽힌다. */
@media (prefers-reduced-motion: reduce) {
.ssul-making__fill {
transition: none;
}
.ssul-making__spinner::after {
animation: none;
}
}
/* 썰박스 뷰어 전용 스타일은 두지 않는다 —
SsulViewerModal 이 castad `video-detail-*` 클래스를 그대로 재사용해
통합 목록에서 어떤 카드를 열든 같은 모양이 나오게 했다. */
/* 결과 화면 스타일은 없다 — SsulResultContent 가 ADO2 완료 화면과 같은
`CompletionView`(comp2-* 클래스)를 쓴다(2026-08-10 통일). */