feat(system,ui): message-template admin (V48) + empty-table demo seed (V49) + Nifty design/token alignment & public card visuals

- system/message: sys_message template CRUD + {key} render API (ADMIN-gated, render for authed)
- V48: sys_message table + 36 seeded templates (9 codes x ko/en/zh/ja), idempotent upsert
- V49: backfill 12 empty domain tables (render_job, cms_media, logistics_*, mail_log,
  edm_followup, webhook_*, login_history, sys_role_menu, audit_log); idempotent guards.
  followup ON CONFLICT targets business key (event_id, lead_id) to skip pre-seeded rows
- frontend: Nifty token/typography sweep (--fs-*/--fw-*), public e-card 3:4 media + cover fit,
  footer copy update across ko/en/zh/ja

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zio 2026-07-12 21:40:38 +09:00
parent 3077cb04f5
commit 276293ebe7
66 changed files with 1613 additions and 690 deletions

View File

@ -82,6 +82,8 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL
| 2026-07-11 | 구현 하네스 초기 구성 — 전문 에이전트 6종 + kintex-impl-orchestrator + 구현 백로그 | 전체 | 범용 에이전트만 존재 → 스택 확정 후 실제 구현 조율 팀 구성 | | 2026-07-11 | 구현 하네스 초기 구성 — 전문 에이전트 6종 + kintex-impl-orchestrator + 구현 백로그 | 전체 | 범용 에이전트만 존재 → 스택 확정 후 실제 구현 조율 팀 구성 |
| 2026-07-11 | **v2.0 재구성** — 자동전시시스템 확장(PLANNING v2.0). 신규 에이전트 11종(아키텍트 5·공통 1·도메인 5·AI 1) + 오케스트레이터·백로그 v2.0(Phase A 아키텍처→B 공통레이어→C 부스코어→D 도메인→E 배포) | 전체 | 스코프 확장(전시 전기능·경영분석·CMS·관리자·역할분리·공사 옥션·WISE 공통레이어·AI Claude 전환) | | 2026-07-11 | **v2.0 재구성** — 자동전시시스템 확장(PLANNING v2.0). 신규 에이전트 11종(아키텍트 5·공통 1·도메인 5·AI 1) + 오케스트레이터·백로그 v2.0(Phase A 아키텍처→B 공통레이어→C 부스코어→D 도메인→E 배포) | 전체 | 스코프 확장(전시 전기능·경영분석·CMS·관리자·역할분리·공사 옥션·WISE 공통레이어·AI Claude 전환) |
| 2026-07-11 | WISE 웹 기능 레퍼런스 강화 — kintex-frontend-dev에 uiws frontend(pages 공통 업무기능) 차용 원칙, 오케스트레이터에 웹 기능 화면 WISE 패턴·Stitch 디자인 경유·모바일 경계 명시 + 로스터에 kintex-mobile-dev(경계) 추가 | kintex-frontend-dev·kintex-impl-orchestrator | "웹도 wise 기능 참고" 요청 | | 2026-07-11 | WISE 웹 기능 레퍼런스 강화 — kintex-frontend-dev에 uiws frontend(pages 공통 업무기능) 차용 원칙, 오케스트레이터에 웹 기능 화면 WISE 패턴·Stitch 디자인 경유·모바일 경계 명시 + 로스터에 kintex-mobile-dev(경계) 추가 | kintex-frontend-dev·kintex-impl-orchestrator | "웹도 wise 기능 참고" 요청 |
| 2026-07-12 | **세션 대량 구현·배포** — visitor 메인(AI 관람 도우미 홈·기능타일·연월 브리핑, 근거=크롤 적재 DB)·3-트랙 관문/서브홈·역할별 랜딩+메뉴 게이트(plan_role_routing)·공개 AI/캘린더 API(V43)·정산 데이터 정렬(V47)·포스터 이미지 빌드내장(V46)·성능 인덱스(V44)·아바타 API(V45). AI 토큰최소화 기획(PLANNING v3.4 §8A)·데이터표준(FK최소화·공통코드·view/mview·배치, data.md)·Open SSO+HR 연동 설계(sso-hr-integration.md). 배포 커밋 214b6df→3077cb0, health 게이트 통과 | 전체 | 소유자 라이브 피드백 연속 반영. **전 지시 로그: `docs/OWNER_FEEDBACK.md`(권위)** |
| 2026-07-12 | **선행 게이트 해소·검증 교훈** — G1 나노바나나 Gemini 라이브·G2 kintex.zioinfo.co.kr:8021 CI/CD 라이브. 배포 전 필수: **Flyway 마이그레이션 라이브 dry-run(BEGIN…ROLLBACK)**·시크릿 fail-fast 프로파일 확인·파일단위 커밋(공유 트리 교차 방지)·health 게이트. Stitch 불안정 시 design.md 스펙 직접 구현(문서화 폴백) | 전체·kintex-devops-dev | 다수 배포 롤백/캐시/교차커밋 사고 후 표준화 |
--- ---
@ -95,6 +97,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL
| 날짜 | 변경 내용 | 대상 | 사유 | | 날짜 | 변경 내용 | 대상 | 사유 |
|------|----------|------|------| |------|----------|------|------|
| 2026-07-12 | 초기 구성(analyst·crawler 에이전트 + 오케스트레이터) | 전체 | "크롤링/벤치마킹 하네스 생성" 요청 — COEX 3-트랙 재구성·행사 크롤 정례화 | | 2026-07-12 | 초기 구성(analyst·crawler 에이전트 + 오케스트레이터) | 전체 | "크롤링/벤치마킹 하네스 생성" 요청 — COEX 3-트랙 재구성·행사 크롤 정례화 |
| 2026-07-12 | **모바일 앱 벤치마킹**(전시·공연 앱 다운로드·평점 상위순) → `docs/analysis/mobile-benchmark-exhibition-performance.md` + 백로그 12항목. 핵심: 다운로드↔만족도 상반(티켓팅 빅3 저평점) → MMCA·Eventbrite·DICE·Fever 롤모델. **관람객 정보 크롤→DB 적재**(V43 visitor_guide·transport, 포스터 34장 V46) | kintex-benchmark-analyst·kintex-crawler-dev | "모바일은 전시·공연 앱 벤치마킹 다운로드·좋아요순" + "크롤링해서 db 저장" |
--- ---
@ -108,6 +111,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL
| 날짜 | 변경 내용 | 대상 | 사유 | | 날짜 | 변경 내용 | 대상 | 사유 |
|------|----------|------|------| |------|----------|------|------|
| 2026-07-12 | 초기 구성(kintex-testdata-dev 신규 + wise-ui/menu-ia/qa 재사용 + 오케스트레이터) | 전체 | 소유자 지시 — 상세→메인 복귀 부재·전면 리뉴얼·테스트 데이터/공통코드/프로그램 채움 | | 2026-07-12 | 초기 구성(kintex-testdata-dev 신규 + wise-ui/menu-ia/qa 재사용 + 오케스트레이터) | 전체 | 소유자 지시 — 상세→메인 복귀 부재·전면 리뉴얼·테스트 데이터/공통코드/프로그램 채움 |
| 2026-07-12 | **코엑스풍 공개사이트 리뉴얼**(design.md v2.4 §3C-V 비주얼·모션 — 시네마틱 히어로·스크롤 리빌·카운트업·포스터 카드 리프트/줌·오토스크롤, prefers-reduced-motion·CLS 0) + **행사스코프 데이터 정렬**(V47 e-2026-live 폐루프·V49 잔여 보강). ★교훈: 화면이 비는 진짜 원인=시드가 데모 해소행사(workspaces[0]=start_date 최소)에 안 묶임 | kintex-frontend-dev·kintex-testdata-dev·designer | "코엑스처럼 지금 착수"·"정산 데이터 없음"·"모든 테이블 데이터" |
--- ---
@ -122,6 +126,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL
|------|----------|------|------| |------|----------|------|------|
| 2026-07-12 | 초기 구성(에이전트 2 신규 + kintex-qa 재사용 + 오케스트레이터) | 전체 | 소유자 강한 피드백("전부 WISE대로 안 되어 있다" — 시스템관리 메뉴 실종·햄버거/아코디언 부재·캘린더·대시보드·로고) | | 2026-07-12 | 초기 구성(에이전트 2 신규 + kintex-qa 재사용 + 오케스트레이터) | 전체 | 소유자 강한 피드백("전부 WISE대로 안 되어 있다" — 시스템관리 메뉴 실종·햄버거/아코디언 부재·캘린더·대시보드·로고) |
| 2026-07-12 | 메뉴 IA 전담 추가 — kintex-menu-ia-dev + kintex-menu-recompose 스킬(표준 카테고리 v1·라우트↔메뉴 정합 절차), 오케스트레이터 Phase 2.5 편입 | agents·skills | "카테고리별 메뉴 재구성 하네스" 요청 — 별도 오케스트레이터 대신 중복 회피 확장 | | 2026-07-12 | 메뉴 IA 전담 추가 — kintex-menu-ia-dev + kintex-menu-recompose 스킬(표준 카테고리 v1·라우트↔메뉴 정합 절차), 오케스트레이터 Phase 2.5 편입 | agents·skills | "카테고리별 메뉴 재구성 하네스" 요청 — 별도 오케스트레이터 대신 중복 회피 확장 |
| 2026-07-12 | **Nifty 디자인 시스템 전면 정렬** — 카드 Nifty 타입(`.kx-card` subtle 그림자·hover·`--flush`/`__media`/`__header`/`__body`/`__footer` 구조)·타이포(`--fs-micro`/`--fs-nano`·`--fw-*` 토큰·공용 `.kx-page__title`/`.kx-section__title`·font-size 179+weight 415 토큰화)·그리드(`.kx-table` Nifty Advanced header로 6화면 수렴). **캘린더 Nifty**(옅은 격자·연노랑 today·소프트 이벤트칩)·**파란버튼 흰글자**(`--color-on-accent` 다크대응)·favicon 정정. 기준: `docs/DESIGN_SYSTEM_NIFTY.md`·`_workspace/audit_typography_grid.md` | kintex-frontend-dev·kintex-qa | 소유자 반복 강피드백("Nifty 카드/그리드/폰트 안 맞음"·"파란버튼 흰글자"·"달력 Nifty") |
--- ---
@ -138,3 +143,4 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL
|------|----------|------|------| |------|----------|------|------|
| 2026-07-11 | 초기 구성 — kintex-mobile-dev 신규 + 오케스트레이터(재사용: designer·backend-dev·qa·devops-dev·reviewer). WISE 모바일 레퍼런스·Stitch 디자인 경유 반영 | 전체 | 모바일 앱 트랙 전담 하네스 부재("하네스 생성" + "wise 모바일 참고" + "디자인은 스티치" 요청) | | 2026-07-11 | 초기 구성 — kintex-mobile-dev 신규 + 오케스트레이터(재사용: designer·backend-dev·qa·devops-dev·reviewer). WISE 모바일 레퍼런스·Stitch 디자인 경유 반영 | 전체 | 모바일 앱 트랙 전담 하네스 부재("하네스 생성" + "wise 모바일 참고" + "디자인은 스티치" 요청) |
| 2026-07-11 | **앱 타깃 2개 확정 반영** — 코드베이스 1(mobile/) + 배포 타깃 2(①운영 B2B: 2FA 필수·사내 QR ②관람객 B2C: 스토어 공개·간편가입·게스트). 계정은 단일 통합+가입 트랙 분리(PLANNING v3.1) | kintex-mobile-dev·오케스트레이터 | 소유자 확정 — 회원가입 관람객 포함 질의 → 하이브리드(계정 통합·앱 분리) 채택 | | 2026-07-11 | **앱 타깃 2개 확정 반영** — 코드베이스 1(mobile/) + 배포 타깃 2(①운영 B2B: 2FA 필수·사내 QR ②관람객 B2C: 스토어 공개·간편가입·게스트). 계정은 단일 통합+가입 트랙 분리(PLANNING v3.1) | kintex-mobile-dev·오케스트레이터 | 소유자 확정 — 회원가입 관람객 포함 질의 → 하이브리드(계정 통합·앱 분리) 채택 |
| 2026-07-12 | **내정보(WISE)+생체인식(expo-local-authentication)+프로필 사진(expo-image-picker)** + **앱 위변조 방지·시큐어코딩 보안 체크리스트**(루트/탈옥·무결성·Hermes+R8·화면캡처·cleartext·권한최소, `docs/security/mobile-security-*.md`) + **다국어(react-i18next)·역할별 랜딩 패리티** 착수 | kintex-mobile-dev·kintex-qa | "모바일 내정보 WISE+생체+사진"·"앱 위변조방지·시큐어코딩·보안체크"·"모바일도 동일 로직"·"웹 4개국어인데 모바일은?" |

184
docs/OWNER_FEEDBACK.md Normal file
View File

@ -0,0 +1,184 @@
# 소유자 피드백·지시 로그 (KINTEX AI 전시·행사시스템)
> 이번 세션에서 소유자가 지시한 모든 항목을 전수 기록한다. 상태: ✅완료(배포) · 🚚배포중 · ⏳작업중 · 📋대기.
> 이 파일을 기준으로 누락 없이 처리한다. 새 지시는 여기에 추가한다.
> 최종 갱신: 2026-07-12
---
## 1. 디자인 시스템 (Nifty / WISE) — 최우선 반복 지시
| # | 지시 | 상태 | 비고 |
|---|------|------|------|
| 1.1 | **Nifty 카드 타입** 적용 (https://preview.themeon.net/nifty/ui-elements/cards/) — 라운딩·그림자·헤더/바디/푸터 구조·보더 톤 | 📋대기 | `.kxp-ecard`는 8px 라운딩 있으나 Nifty 카드 구조 전면 정렬 필요. 다음 패스 |
| 1.2 | **그리드는 Nifty Advanced table headers**로 통일 (tables/static-tables) | ⏳작업중 | 정본 `.kx-table` 존재, 미준수 6화면 수렴 (감사완료) |
| 1.3 | **Tabulator 기능** 추가 (tables/tabulator — 정렬·필터·페이지·CSV) | 📋대기 | task #32 |
| 1.4 | **상세페이지 = Nifty blog** 스타일 (blog-apps/blog) | 📋대기 | task #33 |
| 1.5 | **버튼 = Nifty buttons**, **Dropdowns**, **컴포넌트**, **List Group**, **Typography/Modals/Offcanvas** = ui-elements 전체 채택 | ⏳부분 | Offcanvas 완료, 나머지 정렬 진행 |
| 1.6 | **Nifty 스타일 학습 md 생성** | ✅완료 | `docs/DESIGN_SYSTEM_NIFTY.md`·`docs/analysis/nifty-design-refs.md` |
| 1.7 | **아이콘은 선(line/stroke) SVG**로 직접 (이모지 금지) | ✅원칙 | 전 신규화면 준수 |
| 1.8 | **UI는 WISE(UIWS) 문자 그대로** — 셸·메뉴·캘린더 자체 재해석 금지 | ✅원칙 | 메모리 [[ui-follow-wise-strictly]] |
| 1.9 | **폰트를 페이지마다 안 맞춤** — 전 화면 타이포 일관화 | ⏳작업중 | 감사완료(하드코딩 262건/36파일). `--fs-micro`·공용 `.kx-page__title`·weight 토큰 신설로 정렬. 다음 패스 |
| 1.10 | **모든 경계선 엷은 회색** | ⏳부분 | 구조 보더는 neutral-200(#e4e7ec) 사용 중. 의미색·hover는 유지, 지정화면 정렬 |
| 1.11 | **좌우 2단 폭 50:50**, 전 화면 **공백 없는 반응형**(풀블리드) | ✅원칙 | task #39 스윕 대기 |
| 1.12 | **top 설정 아이콘** 추가 + **설정 레이아웃 커스터마이저**(offcanvas 테마/레이아웃/색상) | ⏳부분 | 설정 아이콘 완료, 커스터마이저 task #43 |
## 2. 캘린더
| # | 지시 | 상태 |
|---|------|------|
| 2.1 | 캘린더를 **Nifty app-views/calendar**처럼 (옅은 회색 격자·연한 today·가벼운 이벤트 칩) | 🚚배포중 | 큰 캘린더 재스타일 완료(격자 neutral-100·today 연노랑·소프트 칩), 배포 중 |
| 2.2 | **공휴일 붉은색 + 일자 옆 공휴일명** (2050년까지 데이터) | 🚚배포중 | 큰 캘린더+미니 달력 배선. 7월은 공휴일 없음(8/15·9월 추석 표시) |
| 2.3 | **달력 경계선 모두 회색** | 🚚배포중 | 미니 달력 셀 회색 테두리 추가 |
| 2.4 | 일정 **월/주/일** 뷰 (WISE CalendarView) | ✅완료 |
| 2.5 | 전시 일정 50:50 비율·달력 날짜 위로(하단 공백 제거) | ✅완료 |
## 3. 공개사이트 / 메인 / visitor
| # | 지시 | 상태 |
|---|------|------|
| 3.1 | **첫 화면 = 제품 소개** (미인증 루트는 히어로 랜딩, 로그인 폼만 노출 금지) | ✅완료 | 메모리 [[product-identity-first]] |
| 3.2 | 제품명 **'KINTEX AI 전시·행사시스템'** | ✅완료 |
| 3.3 | **visitor 메인 = 세련된 홈페이지** (AI 관람 도우미 중심) | 🚚배포중 | AiAssistant·기능타일·연월 브리핑·쇼케이스 구현 |
| 3.4 | 메인에 **AI 기능 전면 노출** (AI가 뭘 하는지 한눈에) | 🚚배포중 | AI 기능 쇼케이스 6타일 |
| 3.5 | AI가 답하는 정보는 **크롤링해서 DB 저장** (실시간 외부호출 X) | ✅완료 | V43 visitor_guide·transport, 공개 AI API 근거응답 |
| 3.6 | **AI는 전시·행사정보 학습** (근거 기반) | ✅완료 | grounding + 인용, 환각 차단 |
| 3.7 | **AI가 연간·월간 전시·행사 기획정보 제시** | 🚚배포중 | AiPlanningBriefing(연/월 토글) |
| 3.8 | 비지터가 **가장 알고 싶은 정보를 AI로 쉽게** | ✅설계 | 관람 도우미 예시칩·구조화 카드 |
| 3.9 | 코엑스 분석 → **visitor/business/agency 3-트랙** 분리 | ✅완료 | 관문 + 3 서브홈 + TrackSwitcher |
| 3.10 | **"코엑스처럼" 전면 리뉴얼** (히어로 배경·애니메이션·비주얼) | 🚚배포중 | design.md v2.4 §3C-V, 코엑스풍 비주얼·모션 구현 |
| 3.11 | 메인 카드가 **밋밋한 파란 placeholder** (포스터 이미지 없음) | ✅완료 | V46 실제 포스터 이미지로 교체(배포됨) |
| 3.12 | **메인 카드 이미지 크기 안 맞음**(세로 포스터가 16:11 가로 프레임에 늘어남/잘림) | ⏳수정됨 | 포스터 실측 세로 0.67~0.8 → 카드 미디어 3:4 프레임 + `.kxp-ecard__poster` `object-fit:cover`(왜곡 제거). 다음 배포 |
| 3.13 | **메인 히어로 이미지 5장 회전(슬라이드)** | ⏳착수 | 크롤러가 KINTEX 전시장/시설 히어로 이미지 5장 `public/media/hero/` 확보 → 프론트 히어로 회전 UI(배포 후) |
## 4. 역할 · 네비게이션
| # | 지시 | 상태 |
|---|------|------|
| 4.1 | **로그인 사용자 구분으로 메인페이지 + 메뉴 그룹 결정** (디폴트 visitor, 로그인 후 business/admin/agency) | 🚚배포중 | plan_role_routing 구현(admin→/admin·agency→/contractor·visitor→/visitor·ops/business→/home) + 역할별 메뉴 게이트 |
| 4.2 | **각 트랙 메인페이지 Stitch 디자인 의뢰** | ✅부분 | Stitch 2회 실패 → design.md 스펙 직접 구현(문서화 폴백) |
| 4.3 | **모바일 앱도 동일 로직** | 📋대기 | 웹 완료 후 모바일 반영 |
| 4.4 | **상세→메인 복귀·브레드크럼·404** 내비게이션 | ⏳ | renewal 하네스 |
## 5. 데이터 / 시드
| # | 지시 | 상태 |
|---|------|------|
| 5.1 | **모든 테이블에 데이터** (50~100건) | ✅완료 | V42 대량시드(40테이블 2,535행) |
| 5.2 | **정산·결제 데이터 없음** — 행사스코프 정렬 | 🚚배포중 | V47: 데모 해소행사 `e-2026-live`에 정산 폐루프+옥션+관람객+리드+캠페인 |
| 5.3 | 테스트 데이터 **더 현실적으로** | ✅완료 | V39·V42 실감형 |
| 5.4 | 행사정보 **크롤링해서 데이터 넣기** | ✅완료 | 크롤 시드 |
| 5.5 | **연간·월간 전시 기획정보**도 적재 | ✅완료 | V43 event 생성열·v_event_calendar 뷰 |
| 5.6 | 포스터 이미지 **다 다운받아 빌드 내장** | 🚚배포중 | V46 34장 `/media/posters/` |
## 6. 정산·도메인 화면
| # | 지시 | 상태 |
|---|------|------|
| 6.1 | **정산·결제 패딩 없음** | 🚚배포중 | `.kx-settle` 패딩 추가 |
| 6.2 | **참가업체 관리가 홈으로 감** — 전용 화면 신설 | 📋대기 | task #41 |
| 6.3 | 부서 관리 = **WISE 조직도** | ✅완료 |
| 6.4 | 결재관리 **반려·재상신·결재선** (WISE) | ✅완료 |
| 6.5 | 통합검색에 **행사·전시 포함** | ✅완료 |
| 6.6 | 시스템관리 **메시지 관리**({} 플레이스홀더+샘플) | 📋대기 | task #35 |
| 6.7 | 물류허브·서류/마일스톤·CMS·일정 = WISE | ✅완료 |
| 6.8 | **그룹권한(역할↔메뉴) 메뉴** 존재 확인 | ✅확인 | RoleAdminPage + RoleMenuAdminPage |
## 7. 버튼 · 색상 · 대비
| # | 지시 | 상태 |
|---|------|------|
| 7.1 | **파란 배경 버튼 = 흰 글자** | ✅완료 | `--color-on-accent`(다크에서도 흰색). 라이브 반영(캐시 새로고침 필요) |
| 7.2 | 다크모드 색상배경 위 **흰 글자 어두워지는 버그** 전수 수정 | ✅완료 | 77곳 on-accent. +통계밴드 2곳 추가수정(배포중) |
| 7.3 | 회색 버튼은 옅은 ghost — 흰 글자 아님(솔리드 진회색만 흰글자) | ✅확인 |
## 8. 파비콘 · 브랜드
| # | 지시 | 상태 |
|---|------|------|
| 8.1 | **KINTEX favicon** 추가 (홈페이지 크롤) | ✅완료 | 리포 favicon.ico가 톰캣 아이콘이던 것 규명→KINTEX .ico 재생성(v3) |
| 8.2 | **브랜드 밑에 영어 병기** | ✅완료 | "AI Exhibition & Event System" |
## 9. 모바일
| # | 지시 | 상태 |
|---|------|------|
| 9.1 | **내정보 (WISE 참조) + 생체인식 + 사진등록** | ✅완료 | app/profile, expo-local-authentication, expo-image-picker. 아바타 API(백엔드) 완료 |
| 9.2 | **전시·공연 앱 벤치마킹** (다운로드·좋아요 상위순) | ✅완료 | docs/analysis/mobile-benchmark-exhibition-performance.md + 백로그 12항목 |
| 9.3 | **앱 위변조 방지 + 시큐어 코딩 + 보안 체크리스트 통과** | ✅완료 | 루트/탈옥·무결성·Hermes+R8·화면캡처·cleartext·권한최소. 체크리스트/감사 문서 |
| 9.4 | **다국어** — 웹 4개국어 완비, 모바일은 한국어(백로그) | ⏳ | 웹 ✅(ko/en/zh/ja), 모바일 i18n 미도입(MB-11) |
## 10. AI
| # | 지시 | 상태 |
|---|------|------|
| 10.1 | **AI 기능 쉽게 사용 + 토큰 최소화** | ✅완료 | PLANNING v3.4 §8A(결정론 라우팅·소형모델 우선·RAG축소·캐싱·집계는 SQL·측정지표) |
| 10.2 | AI 기능이 **하나도 구현 안 된 것 같다 → 구현** | ✅완료 | 공개 AI 관람 도우미 라이브(grounded=true, Claude) |
| 10.3 | 나노바나나 유료? | ✅답변 | Google Gemini 유료 |
## 11. 데이터 아키텍처 (표준)
| # | 지시 | 상태 |
|---|------|------|
| 11.1 | **FK 최소화, 공통코드로 관리** | ✅표준 | docs/architecture/data.md §10 (소프트참조·공통코드) |
| 11.2 | **필요한 인덱스 생성** | 🚚배포중 | V44 성능 인덱스 8건(핫패스) — 이미 배포됨 |
| 11.3 | **view/mview/function 고려** | ✅표준 | data.md §11 |
| 11.4 | **자동화 배치·프로시저 고려** | ✅표준 | data.md §13 배치 카탈로그 |
| 11.5 | **ten_id/tenant_id PK 맨 앞 복합키** | ✅원칙 | 메모리 [[tenant-id-pk-standard]] |
## 12. 인증 / SSO / HR
| # | 지시 | 상태 |
|---|------|------|
| 12.1 | **직원 인사(조직) 정보는 API 방식** | ✅설계 | docs/architecture/sso-hr-integration.md (HrDirectoryClient 어댑터+스냅샷) |
| 12.2 | **Open SSO 도입** | ✅설계 | Keycloak OIDC(PKCE)+SAML, 기존 JWT+2FA 공존(브로커) |
## 13. 평면도 / 기타
| # | 지시 | 상태 |
|---|------|------|
| 13.1 | 도면 안 열림 / AI 시각화 어디감 | ✅원인 | 403(행사 미가입) → 데모계정 멤버십 해소. DWG는 SVG캔버스+JPG 렌더(DWG 바이너리는 좌표소스) |
| 13.2 | ReRoomAI 기능 (나노바나나 image-to-image) | ⏳ | visualizer 트랙 |
---
## 미해결·대기 요약 (다음 우선 처리)
1. **Nifty 디자인 시스템 전면 정렬** — 카드(ui-elements/cards)·폰트(--fs-micro·공용 제목클래스)·그리드(.kx-table 6화면) 일괄 (1.1·1.2·1.9)
2. **참가업체 관리 전용 화면** (6.2 / task #41)
3. **메시지 관리** (6.6 / task #35)
4. **Tabulator**·**상세 Nifty blog**·**설정 커스터마이저** (1.3·1.4·1.12 / task #32·33·43)
5. **전 화면 공백없는 반응형 스윕** (1.11 / task #39)
6. **모바일 역할 랜딩·다국어** (4.3·9.4)
7. **상세→메인 내비게이션** (4.4)
---
## 처리 로그 (내가 처리·배포한 것 — 2026-07-12 세션)
**배포 완료 (라이브):** 커밋 214b6df → 6e95ac6 → 87f310f/55e27b3 → 3077cb0
- V42 대량시드(40테이블 2,535행) · V43 관람객 가이드/연월 캘린더 뷰 · V44 성능 인덱스 8건 · V45 아바타 컬럼 · V46 포스터 로컬화(73건) · V47 e-2026-live 정산 폐루프
- 공개 AI 관람 도우미 API(grounded·Claude) · 연월 캘린더 API · 3-트랙 관문/서브홈 · 역할별 랜딩+메뉴 게이트
- 미니/큰 캘린더 공휴일 표시·회색 테두리·Nifty 톤 · 코엑스풍 공개사이트 비주얼(히어로·모션·통계밴드) · 실제 포스터 이미지
- 파란버튼 흰글자(`--color-on-accent`) · 다크모드 흰글자 77곳 · favicon(톰캣→KINTEX v3) · 2FA 방패 아이콘 · 브랜드 영문병기 · 정산 패딩
**구현 완료 (미배포 — 다음 배포 대기):**
- **Nifty 디자인 시스템 정렬**(카드 `.kx-card` Nifty 구조·`--fs-micro`/`--fw-*` 토큰·공용 제목클래스·테이블 `.kx-table` 6화면 수렴, font 179+weight 415 토큰화)
- **카드 이미지 3:4 프레임 + object-fit cover**(왜곡/잘림 제거)
- **메시지 관리 백엔드**(V48 sys_message + 렌더 API + 36샘플)
- **통계밴드 흰글자 2곳** · **푸터 "2026 © KINTEX..."** 4개국어
**병렬 진행 중:** 모바일 다국어+역할랜딩 · 잔여 데이터 보강(V49) · 산출물 문서
**설계·표준 문서 산출:** PLANNING v3.4(AI 토큰최소화 §8A) · design.md v2.4(공개 비주얼·모션) · docs/architecture/data.md(FK최소화·공통코드·view/mview·배치) · sso-hr-integration.md(Open SSO+HR) · DESIGN_SYSTEM_NIFTY.md · mobile-security-*.md · mobile-benchmark-*.md
## 산출물(문서) 상태
| 산출물 | 타이밍 | 상태 |
|--------|--------|------|
| 개발계획서 | 개발 전/중 | 📋 생성 착수(PLANNING v3.4 기반) |
| 설계서(아키텍처·ERD·화면·API) | 개발 중 | 📋 생성 착수(design.md v2.4·architecture·backend 계약 기반) |
| 사용자 지침서 | **완성+QA 후**(최종 메뉴 반영) | ⏳ UI 정렬 안정화 후 — 지금 생성 시 재작업(정책: 최종 1회) |
| 운영자 지침서 | **완성+QA 후** | ⏳ 동일 |
> 정책([[deliverables-update-policy]]): deliverables는 최초 1회 + 최종 1회. 개발계획서·설계서는 지금 생성 가능(내용 안정), 지침서는 현재 UI 정렬 파동이 끝난 뒤 최종 메뉴 기준으로 1회 생성.

View File

@ -0,0 +1,103 @@
package com.zioinfo.kintex.system.message;
import com.zioinfo.kintex.auth.EventAccessGuard;
import com.zioinfo.kintex.auth.KintexPrincipal;
import com.zioinfo.kintex.common.ApiResponse;
import com.zioinfo.kintex.common.audit.Audited;
import com.zioinfo.kintex.system.SystemAccessGuard;
import jakarta.validation.Valid;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 메시지 관리 API. 관리(CRUD) ADMIN(시스템관리 게이트), 렌더는 인증 사용자 공개.
* - GET /api/admin/messages 목록(category/locale 필터) [ADMIN]
* - GET /api/admin/messages/{code} 코드별 로케일 목록 [ADMIN]
* - POST /api/admin/messages 생성/수정(upsert) [ADMIN]
* - PUT /api/admin/messages/{code} 수정(upsert) [ADMIN]
* - DELETE /api/admin/messages/{code} 삭제(locale 옵션) [ADMIN]
* - POST /api/messages/{code}/render {params:{...}} 치환 문자열 [인증]
*/
@RestController
public class MessageController {
private final MessageService service;
private final EventAccessGuard authGuard;
private final SystemAccessGuard adminGuard;
public MessageController(MessageService service, EventAccessGuard authGuard, SystemAccessGuard adminGuard) {
this.service = service;
this.authGuard = authGuard;
this.adminGuard = adminGuard;
}
@GetMapping("/api/admin/messages")
public ApiResponse<List<MessageDto>> list(@AuthenticationPrincipal KintexPrincipal principal,
@RequestParam(required = false) String category,
@RequestParam(required = false) String locale) {
adminGuard.requireAdmin(principal);
return ApiResponse.ok(service.list(principal.tenantId(), category, locale));
}
@GetMapping("/api/admin/messages/{code}")
public ApiResponse<List<MessageDto>> byCode(@AuthenticationPrincipal KintexPrincipal principal,
@PathVariable String code) {
adminGuard.requireAdmin(principal);
return ApiResponse.ok(service.byCode(principal.tenantId(), code));
}
@Audited(action = "SYS_MESSAGE_SAVE", targetType = "sys_message")
@PostMapping("/api/admin/messages")
public ApiResponse<Void> create(@AuthenticationPrincipal KintexPrincipal principal,
@Valid @RequestBody MessageDto dto) {
adminGuard.requireAdmin(principal);
service.save(principal.tenantId(), dto);
return ApiResponse.ok(null);
}
@Audited(action = "SYS_MESSAGE_SAVE", targetType = "sys_message")
@PutMapping("/api/admin/messages/{code}")
public ApiResponse<Void> update(@AuthenticationPrincipal KintexPrincipal principal,
@PathVariable String code, @Valid @RequestBody MessageDto dto) {
adminGuard.requireAdmin(principal);
MessageDto merged = new MessageDto(code, dto.locale(), dto.category(), dto.title(),
dto.template(), dto.useYn(), null);
service.save(principal.tenantId(), merged);
return ApiResponse.ok(null);
}
@Audited(action = "SYS_MESSAGE_DELETE", targetType = "sys_message")
@DeleteMapping("/api/admin/messages/{code}")
public ApiResponse<Void> delete(@AuthenticationPrincipal KintexPrincipal principal,
@PathVariable String code,
@RequestParam(required = false) String locale) {
adminGuard.requireAdmin(principal);
service.delete(principal.tenantId(), code, locale);
return ApiResponse.ok(null);
}
/** 렌더 — {params:{name:..,eventName:..}} 를 받아 template 치환 결과 반환. */
@PostMapping("/api/messages/{code}/render")
public ApiResponse<RenderResult> render(@AuthenticationPrincipal KintexPrincipal principal,
@PathVariable String code,
@RequestParam(required = false) String locale,
@RequestBody(required = false) RenderRequest body) {
authGuard.require(principal);
Map<String, Object> params = body == null || body.params() == null ? Map.of() : body.params();
String loc = (locale != null && !locale.isBlank()) ? locale
: (body != null ? body.locale() : null);
String text = service.render(principal.tenantId(), code, loc, params);
return ApiResponse.ok(new RenderResult(code, loc, text));
}
/** 렌더 요청 본문 — params 는 {key}→값 매핑. locale 은 쿼리 우선, 없으면 본문. */
public record RenderRequest(String locale, Map<String, Object> params) {
}
/** 렌더 결과. */
public record RenderResult(String code, String locale, String text) {
}
}

View File

@ -0,0 +1,17 @@
package com.zioinfo.kintex.system.message;
import jakarta.validation.constraints.NotBlank;
/**
* 메시지 템플릿(sys_message 이식) 알림/이메일/통지 문구를 코드·로케일별로 관리.
* template {name}·{eventName} 중괄호 플레이스홀더를 포함하며, 렌더 파라미터로 치환된다.
*/
public record MessageDto(
@NotBlank String msgCode,
String locale,
@NotBlank String category,
@NotBlank String title,
@NotBlank String template,
String useYn,
String updatedAt) {
}

View File

@ -0,0 +1,51 @@
package com.zioinfo.kintex.system.message;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* 메시지 템플릿 매퍼(sys_message). 필터는 null-safe(값이 없으면 조건 무시) &lt;script&gt; 미사용.
*/
@Mapper
public interface MessageMapper {
@Select("SELECT msg_code AS \"msgCode\", locale, category, title, template, use_yn AS \"useYn\", "
+ "to_char(updated_at,'YYYY-MM-DD\"T\"HH24:MI:SS') AS \"updatedAt\" FROM sys_message "
+ "WHERE tenant_id = #{tenantId} "
+ "AND (#{category} IS NULL OR category = #{category}) "
+ "AND (#{locale} IS NULL OR locale = #{locale}) "
+ "ORDER BY category, msg_code, locale")
List<Map<String, Object>> findAll(@Param("tenantId") String tenantId,
@Param("category") String category,
@Param("locale") String locale);
/** 코드로 로케일 목록 조회(관리 상세). */
@Select("SELECT msg_code AS \"msgCode\", locale, category, title, template, use_yn AS \"useYn\", "
+ "to_char(updated_at,'YYYY-MM-DD\"T\"HH24:MI:SS') AS \"updatedAt\" FROM sys_message "
+ "WHERE tenant_id = #{tenantId} AND msg_code = #{msgCode} ORDER BY locale")
List<Map<String, Object>> findByCode(@Param("tenantId") String tenantId,
@Param("msgCode") String msgCode);
/** 렌더용 단건 조회 — 정확 로케일 우선, 없으면 ko 폴백은 서비스에서 처리. */
@Select("SELECT msg_code AS \"msgCode\", locale, category, title, template, use_yn AS \"useYn\" "
+ "FROM sys_message WHERE tenant_id = #{tenantId} AND msg_code = #{msgCode} "
+ "AND locale = #{locale} AND use_yn = 'Y'")
Map<String, Object> findOne(@Param("tenantId") String tenantId,
@Param("msgCode") String msgCode,
@Param("locale") String locale);
@Insert("INSERT INTO sys_message (tenant_id, msg_code, locale, category, title, template, use_yn) "
+ "VALUES (#{tenantId}, #{msgCode}, COALESCE(#{locale},'ko'), #{category}, #{title}, #{template}, "
+ "COALESCE(#{useYn},'Y')) "
+ "ON CONFLICT (tenant_id, msg_code, locale) DO UPDATE SET category=EXCLUDED.category, "
+ "title=EXCLUDED.title, template=EXCLUDED.template, use_yn=EXCLUDED.use_yn, updated_at=now()")
int upsert(Map<String, Object> p);
@Delete("DELETE FROM sys_message WHERE tenant_id = #{tenantId} AND msg_code = #{msgCode} "
+ "AND (#{locale} IS NULL OR locale = #{locale})")
int delete(@Param("tenantId") String tenantId,
@Param("msgCode") String msgCode,
@Param("locale") String locale);
}

View File

@ -0,0 +1,112 @@
package com.zioinfo.kintex.system.message;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 메시지 템플릿 조회/관리 + 렌더링.
* <p>렌더: template {key} params[key] 치환한다. 파라미터가 없으면 원본 {key} 유지하고(안전),
* 값이 null 이면 문자열로 치환한다. 알림/이메일(W10)·통지에서 {@link #render}/{@link #renderTemplate} 재사용.
*/
@Service
public class MessageService {
/** {key} — 영문/숫자/밑줄/점만 허용(중괄호 오탐 방지). */
private static final Pattern PLACEHOLDER = Pattern.compile("\\{([a-zA-Z0-9_.]+)}");
private static final String DEFAULT_TENANT = "KINTEX";
private static final String DEFAULT_LOCALE = "ko";
private final MessageMapper mapper;
public MessageService(MessageMapper mapper) {
this.mapper = mapper;
}
public List<MessageDto> list(String tenantId, String category, String locale) {
return mapper.findAll(tenant(tenantId), blankToNull(category), blankToNull(locale))
.stream().map(MessageService::toDto).toList();
}
public List<MessageDto> byCode(String tenantId, String msgCode) {
return mapper.findByCode(tenant(tenantId), msgCode).stream().map(MessageService::toDto).toList();
}
public void save(String tenantId, MessageDto dto) {
Map<String, Object> p = new HashMap<>();
p.put("tenantId", tenant(tenantId));
p.put("msgCode", dto.msgCode());
p.put("locale", blankToNull(dto.locale()));
p.put("category", dto.category());
p.put("title", dto.title());
p.put("template", dto.template());
p.put("useYn", dto.useYn());
mapper.upsert(p);
}
/** locale 이 null 이면 해당 코드의 전 로케일 삭제. */
public void delete(String tenantId, String msgCode, String locale) {
mapper.delete(tenant(tenantId), msgCode, blankToNull(locale));
}
/**
* 코드+로케일 템플릿을 조회해 파라미터로 치환한 최종 문자열을 반환.
* 요청 로케일이 없으면 ko 폴백, 그래도 없으면 문자열.
*/
public String render(String tenantId, String msgCode, String locale, Map<String, Object> params) {
String loc = (locale == null || locale.isBlank()) ? DEFAULT_LOCALE : locale;
Map<String, Object> row = mapper.findOne(tenant(tenantId), msgCode, loc);
if (row == null && !DEFAULT_LOCALE.equals(loc)) {
row = mapper.findOne(tenant(tenantId), msgCode, DEFAULT_LOCALE);
}
if (row == null) {
return "";
}
return renderTemplate(String.valueOf(row.get("template")), params);
}
/** template 의 {key} 를 params 로 치환(파라미터 없으면 원본 유지, null 이면 빈 문자열). */
public String renderTemplate(String template, Map<String, Object> params) {
if (template == null || template.isEmpty()) {
return template == null ? "" : template;
}
Map<String, Object> safe = params == null ? Map.of() : params;
Matcher m = PLACEHOLDER.matcher(template);
StringBuilder sb = new StringBuilder();
while (m.find()) {
String key = m.group(1);
String replacement;
if (safe.containsKey(key)) {
Object v = safe.get(key);
replacement = v == null ? "" : String.valueOf(v);
} else {
replacement = m.group(0); // 원본 {key} 유지
}
m.appendReplacement(sb, Matcher.quoteReplacement(replacement));
}
m.appendTail(sb);
return sb.toString();
}
private static String tenant(String tenantId) {
return (tenantId == null || tenantId.isBlank()) ? DEFAULT_TENANT : tenantId;
}
private static String blankToNull(String s) {
return (s == null || s.isBlank()) ? null : s;
}
private static MessageDto toDto(Map<String, Object> r) {
return new MessageDto(
str(r.get("msgCode")), str(r.get("locale")), str(r.get("category")),
str(r.get("title")), str(r.get("template")), str(r.get("useYn")), str(r.get("updatedAt")));
}
private static String str(Object o) {
return o == null ? null : String.valueOf(o);
}
}

View File

@ -0,0 +1,115 @@
-- V48 시스템관리 '메시지 관리' — 메시지 템플릿 리소스 + 샘플 전량 시드 (#35)
-- 목표: 알림/이메일/통지 문구를 코드·로케일별 템플릿({name}·{eventName} 등 중괄호 플레이스홀더)으로 중앙 관리.
-- 렌더 API가 template 의 {key} 를 파라미터로 치환해 최종 문자열을 만든다(코드 계층).
-- 불변: V1~V47 파일 불변. 본 마이그레이션은 순증(additive)·멱등(ON CONFLICT)만 수행한다.
-- 테넌트 표준(V31): tenant_id DEFAULT 'KINTEX', 복합 PK 선두 (tenant_id, msg_code, locale).
-- 보안 불변(§0-3): 템플릿은 문구만 저장 — 비밀번호·시크릿·PII 실값 미저장(치환 파라미터는 런타임 전달).
-- ============================================================================
-- 1) 테이블 — 메시지 템플릿
-- ============================================================================
CREATE TABLE IF NOT EXISTS sys_message (
tenant_id varchar(40) NOT NULL DEFAULT 'KINTEX',
msg_code varchar(60) NOT NULL, -- 메시지 코드(예: AUCTION_INVITE)
locale varchar(10) NOT NULL DEFAULT 'ko', -- ko/en/zh/ja
category varchar(40) NOT NULL, -- 분류(AUCTION/SETTLEMENT/ACCOUNT/VISITOR/MARKETING)
title varchar(300) NOT NULL, -- 제목/요약(플레이스홀더 포함 가능)
template text NOT NULL, -- 본문 템플릿({key} 플레이스홀더 포함)
use_yn char(1) NOT NULL DEFAULT 'Y',
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (tenant_id, msg_code, locale)
);
CREATE INDEX IF NOT EXISTS idx_sys_message_cat ON sys_message (tenant_id, category, msg_code);
-- ============================================================================
-- 2) 샘플 전량 시드 — 주요 메시지 리소스 전 카테고리 × (ko/en/zh/ja)
-- ON CONFLICT DO UPDATE 로 멱등. 플레이스홀더는 중괄호 {key}.
-- ============================================================================
INSERT INTO sys_message (tenant_id, msg_code, locale, category, title, template) VALUES
-- AUCTION: 옥션(입찰) 통지
('KINTEX','AUCTION_INVITE','ko','AUCTION','[{eventName}] 장치공사 입찰 안내',
'{companyName} 담당자님, {eventName} 부스({boothNo}) 장치공사 입찰에 초대합니다. 마감일: {dueDate}. 킨텍스 AI 전시시스템에서 견적을 제출해 주세요.'),
('KINTEX','AUCTION_INVITE','en','AUCTION','[{eventName}] Booth Construction Bid Invitation',
'Dear {companyName}, you are invited to bid for booth ({boothNo}) construction of {eventName}. Deadline: {dueDate}. Please submit your quotation on the KINTEX AI system.'),
('KINTEX','AUCTION_INVITE','zh','AUCTION','[{eventName}] 展位施工招标邀请',
'{companyName} 负责人您好,诚邀您参与 {eventName} 展位({boothNo})施工招标。截止日期:{dueDate}。请在 KINTEX AI 系统提交报价。'),
('KINTEX','AUCTION_INVITE','ja','AUCTION','[{eventName}] ブース施工入札のご案内',
'{companyName} ご担当者様、{eventName} ブース({boothNo})施工の入札にご招待します。締切: {dueDate}。KINTEX AI システムで見積をご提出ください。'),
-- AUCTION: 낙찰
('KINTEX','AUCTION_AWARD','ko','AUCTION','[{eventName}] 낙찰 안내',
'{companyName} 담당자님, {eventName} 장치공사 입찰에 낙찰되셨습니다. 낙찰 금액: {amount}원. 세부 일정은 시스템에서 확인해 주세요.'),
('KINTEX','AUCTION_AWARD','en','AUCTION','[{eventName}] Award Notice',
'Dear {companyName}, you have won the construction bid for {eventName}. Award amount: {amount} KRW. Please check the schedule on the system.'),
('KINTEX','AUCTION_AWARD','zh','AUCTION','[{eventName}] 中标通知',
'{companyName} 负责人您好,恭喜您中标 {eventName} 施工项目。中标金额:{amount} 韩元。请在系统查看详细日程。'),
('KINTEX','AUCTION_AWARD','ja','AUCTION','[{eventName}] 落札のご案内',
'{companyName} ご担当者様、{eventName} 施工入札に落札されました。落札金額: {amount}ウォン。詳細日程はシステムでご確認ください。'),
-- SETTLEMENT: 정산 청구
('KINTEX','SETTLE_INVOICE','ko','SETTLEMENT','[정산] 청구서 발행 안내',
'{companyName} 담당자님, 청구서({invoiceNo})가 발행되었습니다. 청구 금액: {amount}원, 납부 기한: {dueDate}. 감사합니다.'),
('KINTEX','SETTLE_INVOICE','en','SETTLEMENT','[Settlement] Invoice Issued',
'Dear {companyName}, invoice ({invoiceNo}) has been issued. Amount: {amount} KRW, due by {dueDate}. Thank you.'),
('KINTEX','SETTLE_INVOICE','zh','SETTLEMENT','[结算] 账单已开具',
'{companyName} 负责人您好,账单({invoiceNo})已开具。金额:{amount} 韩元,付款期限:{dueDate}。谢谢。'),
('KINTEX','SETTLE_INVOICE','ja','SETTLEMENT','[精算] 請求書発行のご案内',
'{companyName} ご担当者様、請求書({invoiceNo})が発行されました。金額: {amount}ウォン、支払期限: {dueDate}。ありがとうございます。'),
-- SETTLEMENT: 마감임박
('KINTEX','SETTLE_DUE','ko','SETTLEMENT','[정산] 납부 마감 임박 안내',
'{companyName} 담당자님, 청구서({invoiceNo})의 납부 기한이 {dueDate}로 임박했습니다. 기한 내 납부 부탁드립니다.'),
('KINTEX','SETTLE_DUE','en','SETTLEMENT','[Settlement] Payment Due Soon',
'Dear {companyName}, payment for invoice ({invoiceNo}) is due on {dueDate}. Please complete payment on time.'),
('KINTEX','SETTLE_DUE','zh','SETTLEMENT','[结算] 付款截止临近',
'{companyName} 负责人您好,账单({invoiceNo})付款期限 {dueDate} 临近,请按时付款。'),
('KINTEX','SETTLE_DUE','ja','SETTLEMENT','[精算] 支払期限が近づいています',
'{companyName} ご担当者様、請求書({invoiceNo})の支払期限 {dueDate} が近づいています。期限内のお支払をお願いします。'),
-- ACCOUNT: 환영
('KINTEX','WELCOME','ko','ACCOUNT','환영합니다, {name}님',
'{name}님, KINTEX AI 전시·행사시스템에 오신 것을 환영합니다. {eventName} 준비를 함께 시작해 보세요.'),
('KINTEX','WELCOME','en','ACCOUNT','Welcome, {name}',
'Hi {name}, welcome to the KINTEX AI Exhibition System. Let us start preparing {eventName} together.'),
('KINTEX','WELCOME','zh','ACCOUNT','欢迎您,{name}',
'{name} 您好,欢迎使用 KINTEX AI 展览系统。让我们一起开始筹备 {eventName} 吧。'),
('KINTEX','WELCOME','ja','ACCOUNT','ようこそ、{name}様',
'{name}様、KINTEX AI 展示会システムへようこそ。{eventName} の準備を一緒に始めましょう。'),
-- ACCOUNT: 비밀번호 재설정
('KINTEX','PASSWORD_RESET','ko','ACCOUNT','[보안] 비밀번호 재설정 안내',
'{name}님, 비밀번호 재설정을 요청하셨습니다. 아래 링크는 {expireMin}분간 유효합니다: {resetLink}. 본인이 아니라면 무시하세요.'),
('KINTEX','PASSWORD_RESET','en','ACCOUNT','[Security] Reset Your Password',
'Hi {name}, you requested a password reset. The link is valid for {expireMin} minutes: {resetLink}. Ignore if this was not you.'),
('KINTEX','PASSWORD_RESET','zh','ACCOUNT','[安全] 重置密码',
'{name} 您好,您申请了重置密码。链接 {expireMin} 分钟内有效:{resetLink}。若非本人操作请忽略。'),
('KINTEX','PASSWORD_RESET','ja','ACCOUNT','[セキュリティ] パスワード再設定のご案内',
'{name}様、パスワード再設定のリクエストを受け付けました。リンクは {expireMin} 分間有効です: {resetLink}。心当たりがない場合は無視してください。'),
-- VISITOR: 체크인
('KINTEX','CHECKIN','ko','VISITOR','[{eventName}] 체크인 완료',
'{name}님, {eventName} 체크인이 완료되었습니다. 관심 부스({boothNo})를 둘러보세요. 즐거운 관람 되세요.'),
('KINTEX','CHECKIN','en','VISITOR','[{eventName}] Check-in Complete',
'Hi {name}, your check-in for {eventName} is complete. Explore booth ({boothNo}). Enjoy the show.'),
('KINTEX','CHECKIN','zh','VISITOR','[{eventName}] 签到完成',
'{name} 您好,您在 {eventName} 的签到已完成。请参观展位({boothNo})。祝您观展愉快。'),
('KINTEX','CHECKIN','ja','VISITOR','[{eventName}] チェックイン完了',
'{name}様、{eventName} のチェックインが完了しました。ブース({boothNo})をご覧ください。ごゆっくりお楽しみください。'),
-- VISITOR: 배지 발급
('KINTEX','BADGE_ISSUE','ko','VISITOR','[{eventName}] 배지 발급 안내',
'{name}님, 관람 배지({badgeNo})가 발급되었습니다. 입장 시 QR 코드를 제시해 주세요.'),
('KINTEX','BADGE_ISSUE','en','VISITOR','[{eventName}] Badge Issued',
'Hi {name}, your badge ({badgeNo}) for {eventName} has been issued. Present the QR code at entry.'),
('KINTEX','BADGE_ISSUE','zh','VISITOR','[{eventName}] 参观证已发放',
'{name} 您好,您在 {eventName} 的参观证({badgeNo})已发放。入场时请出示二维码。'),
('KINTEX','BADGE_ISSUE','ja','VISITOR','[{eventName}] バッジ発行のご案内',
'{name}様、{eventName} の入場バッジ({badgeNo})が発行されました。入場時に QR コードをご提示ください。'),
-- MARKETING: EDM(홍보 메일)
('KINTEX','EDM','ko','MARKETING','[{eventName}] 사전등록 안내',
'{name}님, {eventName}이(가) {eventDate}에 열립니다. 지금 사전등록하고 다양한 혜택을 받으세요.'),
('KINTEX','EDM','en','MARKETING','[{eventName}] Pre-registration Open',
'Hi {name}, {eventName} opens on {eventDate}. Pre-register now to enjoy exclusive benefits.'),
('KINTEX','EDM','zh','MARKETING','[{eventName}] 预登记开放',
'{name} 您好,{eventName} 将于 {eventDate} 举行。立即预登记,享受专属优惠。'),
('KINTEX','EDM','ja','MARKETING','[{eventName}] 事前登録のご案内',
'{name}様、{eventName} が {eventDate} に開催されます。今すぐ事前登録して特典をお受け取りください。')
ON CONFLICT (tenant_id, msg_code, locale) DO UPDATE
SET category = EXCLUDED.category,
title = EXCLUDED.title,
template = EXCLUDED.template,
use_yn = 'Y',
updated_at = now();

View File

@ -0,0 +1,220 @@
-- ============================================================================
-- V49 — 잔여 빈 테이블 전수 보강 (화면이 쓰나 비어 있던 12개 도메인 테이블)
-- ============================================================================
-- 배경: V42 대량시드(40테이블)·V47(e-2026-live 폐루프) 이후에도, CREATE 는 되었으나
-- INSERT 가 전혀 없던(=화면에서 "빈 상태") 테이블이 다수 남아 있었다(매퍼 감사).
-- 대상(전부 매퍼/서비스가 조회):
-- render_job · cms_media · logistics_equipment_request · logistics_rental_order ·
-- logistics_inbound · mail_log · edm_followup_log(+draft campaign) ·
-- webhook_subscription · webhook_delivery · login_history · sys_role_menu · audit_log
-- 스코프: 이벤트 종속 도메인은 두 데모 행사(e-2026-live=오늘 · e-2026-smf=8월) 모두 채워
-- 행사 전환 시에도 물류/렌탈/입고/렌더/팔로업이 보이게 한다(고아 레코드 금지 — 기존 booth/lead/company/menu 참조).
-- 원칙: 멱등(ON CONFLICT DO NOTHING) · tenant_id='KINTEX' · PII 더미(홍길동·010-0000-*·*@kintex.test) ·
-- 기존 시드 불변. 서버 최신 V47, 진행 중 V48(메시지관리)과 충돌 회피 → V49 확정.
--
-- 의도적으로 비워 두는 테이블(보안·운영성):
-- error_log(운영 500 요약만 기록) · password_reset(1회용 재설정 토큰) ·
-- webhook_inbound_endpoint / webhook_inbound_event(secret_enc AES-256-GCM 봉인은
-- 앱 부팅 시더 전용 — SQL 평문 저장 금지, V35 주석 준수).
-- ============================================================================
-- ============================================================================
-- 1) render_job — 부스별 나노바나나 렌더 결과(상태 분포: DONE·RUNNING·QUEUED·FAILED)
-- ============================================================================
-- e-2026-live 부스(booth-live-01..06) + e-2026-smf 부스(booth-smf-a102/b101..b106).
INSERT INTO render_job (id, event_id, booth_id, shot_preset, status, image_url, schema_hash, model_version, error_message, created_at, updated_at) VALUES
-- e-2026-live
('rj-live-01','e-2026-live','booth-live-01','S1','DONE','/uploads/render/rj-live-01.png','sha1-live01a','gemini-3.1-flash-image-preview',NULL, now()-interval '5 hour', now()-interval '5 hour'),
('rj-live-02','e-2026-live','booth-live-01','S3','DONE','/uploads/render/rj-live-02.png','sha1-live01b','gemini-3.1-flash-image-preview',NULL, now()-interval '5 hour', now()-interval '4 hour'),
('rj-live-03','e-2026-live','booth-live-02','S1','DONE','/uploads/render/rj-live-03.png','sha1-live02a','gemini-3.1-flash-image-preview',NULL, now()-interval '4 hour', now()-interval '3 hour'),
('rj-live-04','e-2026-live','booth-live-03','S2','RUNNING',NULL,'sha1-live03a','gemini-3.1-flash-image-preview',NULL, now()-interval '20 minute', now()-interval '2 minute'),
('rj-live-05','e-2026-live','booth-live-04','S1','QUEUED',NULL,'sha1-live04a',NULL,NULL, now()-interval '8 minute', now()-interval '8 minute'),
('rj-live-06','e-2026-live','booth-live-05','S4','FAILED',NULL,'sha1-live05a','gemini-3.1-flash-image-preview','생성 시간 초과 — 재시도 대기(요약).', now()-interval '2 hour', now()-interval '2 hour'),
('rj-live-07','e-2026-live','booth-live-06','S1','DONE','/uploads/render/rj-live-07.png','sha1-live06a','gemini-3.1-flash-image-preview',NULL, now()-interval '6 hour', now()-interval '6 hour'),
-- e-2026-smf
('rj-smf-01','e-2026-smf','booth-smf-a102','S1','DONE','/uploads/render/rj-smf-01.png','sha1-smfa102','gemini-3.1-flash-image-preview',NULL, TIMESTAMPTZ '2026-07-20 10:00+09', TIMESTAMPTZ '2026-07-20 10:03+09'),
('rj-smf-02','e-2026-smf','booth-smf-b101','S2','DONE','/uploads/render/rj-smf-02.png','sha1-smfb101','gemini-3.1-flash-image-preview',NULL, TIMESTAMPTZ '2026-07-21 11:00+09', TIMESTAMPTZ '2026-07-21 11:04+09'),
('rj-smf-03','e-2026-smf','booth-smf-b102','S1','DONE','/uploads/render/rj-smf-03.png','sha1-smfb102','gemini-3.1-flash-image-preview',NULL, TIMESTAMPTZ '2026-07-22 09:30+09', TIMESTAMPTZ '2026-07-22 09:33+09'),
('rj-smf-04','e-2026-smf','booth-smf-b103','S3','QUEUED',NULL,'sha1-smfb103',NULL,NULL, TIMESTAMPTZ '2026-07-23 14:00+09', TIMESTAMPTZ '2026-07-23 14:00+09'),
('rj-smf-05','e-2026-smf','booth-smf-b104','S5','FAILED',NULL,'sha1-smfb104','gemini-3.1-flash-image-preview','입력 평면도 해상도 부족 — 재업로드 요망(요약).', TIMESTAMPTZ '2026-07-23 15:20+09', TIMESTAMPTZ '2026-07-23 15:21+09')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 2) cms_media — 미디어 라이브러리(전역 + 행사별)
-- ============================================================================
INSERT INTO cms_media (id, event_id, file_name, url, content_type, size_bytes, alt_text, uploader_name, created_at) VALUES
('med-global-01', NULL, 'kintex-logo.svg', '/uploads/cms/kintex-logo.svg', 'image/svg+xml', 18240, 'KINTEX AI 전시·행사시스템 로고', '관리자', now()-interval '30 day'),
('med-global-02', NULL, 'hero-hall.jpg', '/uploads/cms/hero-hall.jpg', 'image/jpeg', 482113, '킨텍스 전시홀 메인 히어로', '관리자', now()-interval '25 day'),
('med-global-03', NULL, 'brochure-cover.png', '/uploads/cms/brochure-cover.png', 'image/png', 231544, '공식 도록 표지', '관리자', now()-interval '20 day'),
('med-live-01','e-2026-live', 'smart-auto-poster.jpg', '/uploads/cms/smart-auto-poster.jpg','image/jpeg', 356820, '스마트 물류·자동화 코리아 포스터', '홍길동', now()-interval '10 day'),
('med-live-02','e-2026-live', 'floorplan-live.png', '/uploads/cms/floorplan-live.png', 'image/png', 512900, 'e-2026-live 홀 배치도', '홍길동', now()-interval '7 day'),
('med-live-03','e-2026-live', 'sponsor-banner.jpg', '/uploads/cms/sponsor-banner.jpg', 'image/jpeg', 198340, '스폰서 배너(다이아몬드)', '홍길동', now()-interval '3 day'),
('med-smf-01','e-2026-smf', 'smf-keyvisual.jpg', '/uploads/cms/smf-keyvisual.jpg', 'image/jpeg', 421077, '스마트 팩토리 엑스포 키비주얼', '관리자', TIMESTAMPTZ '2026-07-15 10:00+09'),
('med-smf-02','e-2026-smf', 'smf-map.png', '/uploads/cms/smf-map.png', 'image/png', 488213, 'e-2026-smf 전시장 안내도', '관리자', TIMESTAMPTZ '2026-07-16 10:00+09'),
('med-smf-03','e-2026-smf', 'smf-speaker.jpg', '/uploads/cms/smf-speaker.jpg', 'image/jpeg', 167902, '기조연설 연사 프로필(더미)', '관리자', TIMESTAMPTZ '2026-07-17 10:00+09')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 3) logistics_equipment_request — 부대장비 신청(마스터 스냅샷, 상태 분포)
-- ============================================================================
INSERT INTO logistics_equipment_request
(id, event_id, equipment_id, equipment_name, unit_price, booth_no, quantity, use_date, days, amount, status, note, reject_reason, requested_by, decided_by, decided_at) VALUES
('ler-live-01','e-2026-live','leq-fork-25','지게차 2.5t',180000,'L-101',1, CURRENT_DATE, 1, 180000,'approved','반입일 오전 상차','', 'demo-exhibitor','demo-organizer', now()-interval '1 day'),
('ler-live-02','e-2026-live','leq-manp-01','상하차 인력', 90000,'L-102',2, CURRENT_DATE, 1, 180000,'requested','대형 장비 상하차 인력 2인','', 'demo-exhibitor',NULL,NULL),
('ler-live-03','e-2026-live','leq-lift-01','고소작업 리프트',150000,'L-103',1, CURRENT_DATE+1, 1, 150000,'done','트러스 설치 완료','', 'demo-exhibitor','demo-organizer', now()-interval '2 day'),
('ler-smf-01','e-2026-smf','leq-fork-05','지게차 5t',260000,'A-102',1, DATE '2026-08-04', 1, 260000,'approved','반입일 대형 화물 상차','', 'demo-exhibitor','demo-organizer', TIMESTAMPTZ '2026-07-28 10:00+09'),
('ler-smf-02','e-2026-smf','leq-lift-01','고소작업 리프트',150000,'B-101',1, DATE '2026-08-04', 2, 300000,'requested','2일간 사이니지 설치','', 'demo-exhibitor',NULL,NULL),
('ler-smf-03','e-2026-smf','leq-crane-5','이동식 크레인 5t',420000,'B-104',1, DATE '2026-08-05', 1, 420000,'rejected','','중량물 크레인은 안전관리계획서 선제출 필요', 'demo-exhibitor','demo-organizer', TIMESTAMPTZ '2026-07-29 14:00+09')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 4) logistics_rental_order — 렌탈 품목 주문(마스터 스냅샷, 상태 분포)
-- ============================================================================
INSERT INTO logistics_rental_order
(id, event_id, item_id, item_name, daily_price, booth_no, quantity, start_date, end_date, days, amount, status, note, ordered_by, decided_by, decided_at) VALUES
('lro-live-01','e-2026-live','lri-tv-55','55인치 모니터',60000,'L-101',2, CURRENT_DATE, CURRENT_DATE+2, 3, 360000,'confirmed','부스 시연용 2대','demo-exhibitor','demo-organizer', now()-interval '1 day'),
('lro-live-02','e-2026-live','lri-chair-01','상담용 의자',3000,'L-101',10, CURRENT_DATE, CURRENT_DATE+2, 3, 90000,'ordered','상담 부스 의자','demo-exhibitor',NULL,NULL),
('lro-live-03','e-2026-live','lri-info-01','인포메이션 데스크',25000,'L-102',1, CURRENT_DATE, CURRENT_DATE+2, 3, 75000,'delivered','안내 데스크 설치 완료','demo-exhibitor','demo-organizer', now()-interval '6 hour'),
('lro-smf-01','e-2026-smf','lri-table-01','접이식 테이블',8000,'A-102',5, DATE '2026-08-04', DATE '2026-08-07', 4, 160000,'confirmed','상담존 테이블','demo-exhibitor','demo-organizer', TIMESTAMPTZ '2026-07-28 11:00+09'),
('lro-smf-02','e-2026-smf','lri-spot-01','LED 스포트 조명',5000,'B-101',12, DATE '2026-08-04', DATE '2026-08-07', 4, 240000,'ordered','제품 진열 조명','demo-exhibitor',NULL,NULL),
('lro-smf-03','e-2026-smf','lri-shelf-01','진열 선반',12000,'B-103',3, DATE '2026-08-04', DATE '2026-08-07', 4, 144000,'cancelled','발주 취소(부스 설계 변경)','demo-exhibitor','demo-organizer', TIMESTAMPTZ '2026-07-30 09:00+09')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 5) logistics_inbound — 반입 화물 추적(운송장, 상태 분포)
-- ============================================================================
INSERT INTO logistics_inbound
(id, event_id, tracking_no, carrier, booth_no, item_desc, quantity, status, eta_date, warehouse_at, delivered_at, note, registered_by, updated_by) VALUES
('lib-live-01','e-2026-live','CJ-1000-2201','CJ대한통운','L-101','AMR 시연 장비 3종',3,'delivered_booth', CURRENT_DATE, now()-interval '8 hour', now()-interval '3 hour','부스 전달 완료','demo-exhibitor','demo-organizer'),
('lib-live-02','e-2026-live','LO-2200-3310','로젠택배','L-102','비전 검사 모듈 팔레트',1,'arrived_warehouse', CURRENT_DATE, now()-interval '2 hour', NULL,'창고 대기 — 부스 전달 예정','demo-exhibitor','demo-organizer'),
('lib-live-03','e-2026-live','HJ-3300-4420','한진택배','L-103','협동로봇 본체',2,'inbound_scheduled', CURRENT_DATE+1, NULL, NULL,'익일 입고 예정','demo-exhibitor',NULL),
('lib-smf-01','e-2026-smf','CJ-5000-1102','CJ대한통운','A-102','스마트팩토리 데모 셀',1,'registered', DATE '2026-08-03', NULL, NULL,'반입 신청 등록','demo-exhibitor',NULL),
('lib-smf-02','e-2026-smf','LO-5100-2203','로젠택배','B-101','전자 사이니지 패널 6매',6,'inbound_scheduled', DATE '2026-08-03', NULL, NULL,'개막 전일 입고','demo-exhibitor','demo-organizer')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 6) mail_log — 발송 이력(제목/마스킹 수신자만, 본문·원문 미저장)
-- ============================================================================
INSERT INTO mail_log (tenant_id, id, category, ref_id, subject, recipient_masked, status, detail, created_at) VALUES
('KINTEX','ml-0001','AUCTION_OPEN','AUC-LIVE-01','[킨텍스] L-101 아일랜드 부스 장치공사 옥션 초대','han***@kintex.test', 'sent','초대 공사업체 3사 발송', now()-interval '3 day'),
('KINTEX','ml-0002','AUCTION_OPEN','AUC-LIVE-02','[킨텍스] 메인 통로 전기·조명 설치 옥션 초대','del***@kintex.test', 'sent','초대 공사업체 3사 발송', now()-interval '2 day'),
('KINTEX','ml-0003','EDM','cp-live-001','[스마트 물류·자동화 코리아] 개막 안내','vis***@kintex.test', 'sent','사전등록자 5,400명 대상 발송', now()-interval '1 day'),
('KINTEX','ml-0004','EDM','cp-live-002','[VIP 초대] 바이어 상담회 안내','buy***@kintex.test', 'sent','VIP 1,200명 대상 발송 예약', now()-interval '6 hour'),
('KINTEX','ml-0005','PASSWORD_RESET',NULL,'[킨텍스] 비밀번호 재설정 안내','dem***@kintex.test','sent','재설정 링크 발송(유효 30분)', now()-interval '5 hour'),
('KINTEX','ml-0006','EDM','cp-smf-001','[스마트 팩토리 엑스포] 사전등록 오픈 안내','pre***@kintex.test','sent','사전등록 안내 발송', TIMESTAMPTZ '2026-07-18 10:00+09'),
('KINTEX','ml-0007','AUCTION_OPEN','AUC-2026-0511','[킨텍스] D-1 옥외 사이니지 설치 옥션 초대','sec***@kintex.test','failed','수신 도메인 일시 오류 — 재발송 대상', TIMESTAMPTZ '2026-07-25 14:00+09'),
('KINTEX','ml-0008','EDM','cp-smf-003','[스마트 팩토리 엑스포] 세미나 프로그램 안내','edu***@kintex.test','skipped','수신거부 목록 필터링', TIMESTAMPTZ '2026-07-27 10:00+09')
ON CONFLICT (tenant_id, id) DO NOTHING;
-- ============================================================================
-- 7) edm_followup_log(+ draft campaign) — 핫리드(score>=80) 팔로업 자동 초안 1회
-- ============================================================================
-- 리드당 초안 캠페인(draft) 1건 생성 후 followup_log 로 링크(멱등 키: UNIQUE(event_id, lead_id)).
INSERT INTO edm_campaign (id, event_id, name, status, audience, meta, open_rate, click_rate) VALUES
('cp-fu-live-001','e-2026-live','[팔로업] 스마트 물류 AMR 커스텀 제안','draft', 1, 'AI 자동 초안 · ld-live-001', '-','-'),
('cp-fu-live-002','e-2026-live','[팔로업] AI 비전 검사 모듈 파일럿 안내','draft', 1, 'AI 자동 초안 · ld-live-002', '-','-'),
('cp-fu-smf-001','e-2026-smf','[팔로업] 협동로봇 HR-A1 도입 제안','draft', 1, 'AI 자동 초안 · ld-smf-006', '-','-'),
('cp-fu-smf-002','e-2026-smf','[팔로업] 협동로봇 HR-A1 견적 안내','draft', 1, 'AI 자동 초안 · ld-smf-001', '-','-'),
('cp-fu-smf-003','e-2026-smf','[팔로업] AI 비전 검사 모듈 사례집','draft', 1, 'AI 자동 초안 · ld-smf-003', '-','-')
ON CONFLICT (id) DO NOTHING;
INSERT INTO edm_followup_log (tenant_id, id, event_id, lead_id, campaign_id, hot_score, body_draft, created_at) VALUES
('KINTEX','fu-live-001','e-2026-live','ld-live-001','cp-fu-live-001',92,
'안녕하세요, 홍 팀장님. 스마트 물류 AMR 커스텀 제안서를 준비했습니다. 다음 주 미팅 가능하실까요?', now()-interval '3 hour'),
('KINTEX','fu-live-002','e-2026-live','ld-live-002','cp-fu-live-002',85,
'안녕하세요, 김 님. 요청하신 AI 비전 검사 모듈 견적서와 파일럿 프로그램 안내드립니다.', now()-interval '5 hour'),
('KINTEX','fu-smf-006','e-2026-smf','ld-smf-006','cp-fu-smf-001',91,
'안녕하세요, 문 팀장님. 협동로봇 HR-A1 도입 제안서를 보내드립니다.', TIMESTAMPTZ '2026-08-01 15:00+09'),
('KINTEX','fu-smf-001','e-2026-smf','ld-smf-001','cp-fu-smf-002',87,
'안녕하세요, 김 담당자님. 협동로봇 HR-A1 견적서를 준비했습니다.', TIMESTAMPTZ '2026-08-01 15:10+09'),
('KINTEX','fu-smf-003','e-2026-smf','ld-smf-003','cp-fu-smf-003',82,
'안녕하세요, 박 소장님. AI 비전 검사 모듈 도입 사례집을 보내드립니다.', TIMESTAMPTZ '2026-08-01 15:20+09')
-- 멱등 키는 업무 유니크 (event_id, lead_id) — V47/V42 가 동일 리드에 이미 초안을 시드했으면(다른 id) 건너뛴다.
-- (id PK 는 신규이므로 실충돌은 항상 (event_id, lead_id) 쪽에서 발생.)
ON CONFLICT (event_id, lead_id) DO NOTHING;
-- ============================================================================
-- 8) webhook_subscription — 아웃바운드 구독(발송 disabled 스텁)
-- ============================================================================
-- secret_hash = SHA-256(secret) hex 자리(원문 미저장·발송 disabled 이므로 검증 불요). 데모 고정 해시.
INSERT INTO webhook_subscription (tenant_id, id, event_type, callback_url, secret_hash, description, active, created_by) VALUES
('KINTEX','whs-001','lead.hot', 'https://hooks.example.test/kintex/lead-hot', '3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b','핫리드 발생 시 CRM 연동(스텁)', true, 'system-seed'),
('KINTEX','whs-002','invoice.paid', 'https://hooks.example.test/kintex/invoice-paid','5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8','청구 완납 시 회계 연동(스텁)', true, 'system-seed'),
('KINTEX','whs-003','auction.awarded','https://hooks.example.test/kintex/award', '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b','옥션 낙찰 시 계약 연동(스텁)', true, 'system-seed'),
('KINTEX','whs-004','visitor.checkin','https://hooks.example.test/kintex/checkin', 'd4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35','현장 체크인 실시간 연동(스텁)', false, 'system-seed')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 9) webhook_delivery — 아웃바운드 발송 큐(status=disabled 스텁, 실 HTTP 콜 없음)
-- ============================================================================
INSERT INTO webhook_delivery (tenant_id, id, subscription_id, event_type, payload_json, status, note, created_at) VALUES
('KINTEX','whd-001','whs-001','lead.hot',
'{"eventId":"e-2026-live","leadId":"ld-live-001","score":92}'::text,'disabled','스텁 — 실발송 없음', now()-interval '3 hour'),
('KINTEX','whd-002','whs-002','invoice.paid',
'{"eventId":"e-2026-live","invoiceId":"inv-live-01","total":7187400}'::text,'disabled','스텁 — 실발송 없음', now()-interval '5 day'),
('KINTEX','whd-003','whs-003','auction.awarded',
'{"eventId":"e-2026-live","auctionId":"AUC-LIVE-01","awardId":"aw-live-01"}'::text,'disabled','스텁 — 실발송 없음', now()-interval '1 day'),
('KINTEX','whd-004','whs-001','lead.hot',
'{"eventId":"e-2026-smf","leadId":"ld-smf-006","score":91}'::text,'disabled','스텁 — 실발송 없음', TIMESTAMPTZ '2026-08-01 15:00+09')
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 10) login_history — 로그인 이력(마스킹 이메일·IP, 결과 분포)
-- ============================================================================
-- id 는 bigserial → 명시하지 않고 자동 채번. 재실행 시 중복 방지 위해 NOT EXISTS 가드.
INSERT INTO login_history (tenant_id, user_id, email_masked, result, fail_reason, ip_masked, user_agent, created_at)
SELECT * FROM (VALUES
('KINTEX','demo-organizer', 'org***@kintex.test','SUCCESS',NULL, '203.0.113.*', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126', now()-interval '2 hour'),
('KINTEX','demo-exhibitor', 'exh***@kintex.test','SUCCESS',NULL, '203.0.113.*', 'Mozilla/5.0 (Macintosh; Intel Mac OS X) Safari/17', now()-interval '5 hour'),
('KINTEX','demo-contractor','con***@kintex.test','FAIL', 'BAD_CREDENTIAL','198.51.100.*','Mozilla/5.0 (Windows NT 10.0; Win64; x64) Edge/126', now()-interval '1 day'),
('KINTEX','demo-contractor','con***@kintex.test','SUCCESS',NULL, '198.51.100.*','Mozilla/5.0 (Windows NT 10.0; Win64; x64) Edge/126', now()-interval '23 hour'),
('KINTEX','demo-visitor', 'vis***@kintex.test','SUCCESS',NULL, '203.0.113.*', 'Mozilla/5.0 (Linux; Android 14) Chrome/126 Mobile', now()-interval '40 minute'),
('KINTEX', NULL, 'unk***@kintex.test','FAIL', 'BAD_CREDENTIAL','198.51.100.*','Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/125',now()-interval '3 day'),
('KINTEX','demo-exhibitor', 'exh***@kintex.test','LOCKED', 'LOCKED', '198.51.100.*','Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126',now()-interval '2 day')
) AS v(tenant_id, user_id, email_masked, result, fail_reason, ip_masked, user_agent, created_at)
WHERE NOT EXISTS (SELECT 1 FROM login_history WHERE user_agent LIKE 'Mozilla/5.0%' AND ip_masked IN ('203.0.113.*','198.51.100.*') LIMIT 1);
-- ============================================================================
-- 11) sys_role_menu — 역할-메뉴 권한 매핑(RBAC 조회 화면 /admin/role-menus)
-- ============================================================================
-- ADMIN: 전 메뉴 읽기/쓰기. MANAGER: 업무(m-work*) 읽기/쓰기. USER: 업무 읽기전용.
INSERT INTO sys_role_menu (role_code, menu_id, read_yn, write_yn)
SELECT 'ADMIN', id, 'Y', 'Y' FROM sys_menu
ON CONFLICT (role_code, menu_id) DO NOTHING;
INSERT INTO sys_role_menu (role_code, menu_id, read_yn, write_yn)
SELECT 'MANAGER', id, 'Y', 'Y' FROM sys_menu WHERE id LIKE 'm-work%'
ON CONFLICT (role_code, menu_id) DO NOTHING;
INSERT INTO sys_role_menu (role_code, menu_id, read_yn, write_yn)
SELECT 'USER', id, 'Y', 'N' FROM sys_menu WHERE id LIKE 'm-work%'
ON CONFLICT (role_code, menu_id) DO NOTHING;
-- ============================================================================
-- 12) audit_log — 감사 로그(관리 조회 화면 /admin/audit)
-- ============================================================================
-- id 는 bigserial 자동 채번. 재실행 중복 방지 위해 대표 summary 존재 여부로 가드.
INSERT INTO audit_log (actor_id, actor_name, action, target_type, target_id, event_id, summary, ruleset_version, result, ip_hint, created_at)
SELECT * FROM (VALUES
('demo-organizer','주최자(데모)','AWARD_SELECT','auction','AUC-LIVE-01','e-2026-live','옥션 낙찰 선정 — (주)한빛부스디자인(4,200,000원)', 'auction-v1.0','SUCCESS','203.0.113.*', now()-interval '1 day'),
('demo-organizer','주최자(데모)','INVOICE_ISSUE','invoice','inv-live-01','e-2026-live','청구서 발행 — (주)스마트오토메이션 7,187,400원', 'billing-v1.0','SUCCESS','203.0.113.*', now()-interval '18 day'),
('demo-organizer','주최자(데모)','PAYMENT_RECORD','invoice','inv-live-01','e-2026-live','수납 등록 — 잔금 4,312,440원', 'billing-v1.0','SUCCESS','203.0.113.*', now()-interval '5 day'),
('demo-organizer','주최자(데모)','REFUND_RECORD','invoice','inv-live-08','e-2026-live','환불 산정 — 1,320,000원(개막일 이후 50%)', 'refund-v1.0','SUCCESS','203.0.113.*', now()-interval '2 day'),
('demo-exhibitor','참가업체(데모)','LEAD_EXPORT','lead',NULL,'e-2026-live','리드 6건 내보내기(CSV)', NULL,'SUCCESS','203.0.113.*', now()-interval '4 hour'),
('demo-contractor','공사업체(데모)','BID_SUBMIT','auction','AUC-LIVE-02','e-2026-live','응찰 제출 — 3,650,000원', 'auction-v1.0','SUCCESS','198.51.100.*', now()-interval '12 hour'),
('admin','시스템관리자','ROLE_MENU_UPDATE','sys_role_menu','MANAGER','','역할-메뉴 권한 변경 — MANAGER 업무 메뉴 쓰기 허용', NULL,'SUCCESS','203.0.113.*', now()-interval '10 day'),
('demo-organizer','주최자(데모)','AWARD_SELECT','auction','AUC-2026-0210','e-2026-smf','옥션 낙찰 선정 — 세계전기시설(12,000,000원)', 'auction-v1.0','SUCCESS','203.0.113.*', TIMESTAMPTZ '2026-07-30 11:00+09')
) AS v(actor_id, actor_name, action, target_type, target_id, event_id, summary, ruleset_version, result, ip_hint, created_at)
WHERE NOT EXISTS (SELECT 1 FROM audit_log WHERE action='AWARD_SELECT' AND target_id='AUC-LIVE-01');

View File

@ -0,0 +1,49 @@
package com.zioinfo.kintex.system.message;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* 메시지 템플릿 렌더({key} 치환) 단위테스트 DB/스프링 없이 renderTemplate 순수 로직 검증(#35).
*/
class MessageRenderTest {
private final MessageService service = new MessageService(null);
@Test
void replacesKnownPlaceholders() {
Map<String, Object> p = Map.of("name", "홍길동", "eventName", "스마트팩토리");
String out = service.renderTemplate("{name}님, {eventName}에 오신 것을 환영합니다.", p);
assertEquals("홍길동님, 스마트팩토리에 오신 것을 환영합니다.", out);
}
@Test
void keepsMissingPlaceholderAsOriginal() {
String out = service.renderTemplate("{name}님, {eventName} 안내", Map.of("name", "김철수"));
assertEquals("김철수님, {eventName} 안내", out, "누락 파라미터는 원본 {key} 유지");
}
@Test
void nullValueBecomesEmpty() {
Map<String, Object> p = new HashMap<>();
p.put("name", null);
String out = service.renderTemplate("[{name}] 알림", p);
assertEquals("[] 알림", out, "null 값은 빈 문자열로 치환");
}
@Test
void nullTemplateAndParamsSafe() {
assertEquals("", service.renderTemplate(null, Map.of("a", "b")));
assertEquals("고정 문구", service.renderTemplate("고정 문구", null));
}
@Test
void ignoresNonPlaceholderBraces() {
String out = service.renderTemplate("금액 {amount} (단위: {}) 원", Map.of("amount", "1,000"));
assertEquals("금액 1,000 (단위: {}) 원", out, "빈 중괄호는 플레이스홀더로 오탐하지 않음");
}
}

View File

@ -53,8 +53,8 @@
overflow: hidden; overflow: hidden;
} }
.kx-shell__brand-sub { .kx-shell__brand-sub {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
line-height: 1.15; line-height: 1.15;
color: var(--color-neutral-500); color: var(--color-neutral-500);
white-space: nowrap; white-space: nowrap;
@ -63,7 +63,7 @@
} }
.kx-shell__brand-sub--en { .kx-shell__brand-sub--en {
font-size: 9px; font-size: 9px;
font-weight: 500; font-weight: var(--fw-medium);
letter-spacing: 0.02em; letter-spacing: 0.02em;
color: var(--color-neutral-400, var(--color-neutral-500)); color: var(--color-neutral-400, var(--color-neutral-500));
text-transform: uppercase; text-transform: uppercase;
@ -84,7 +84,7 @@
} }
.kx-shell__event-name { .kx-shell__event-name {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -121,8 +121,8 @@
.kx-shell__nav-section { .kx-shell__nav-section {
margin: var(--space-2) 0 2px; margin: var(--space-2) 0 2px;
padding: 4px var(--space-4) 3px; padding: 4px var(--space-4) 3px;
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -148,7 +148,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
text-align: left; text-align: left;
transition: background 0.15s ease, color 0.15s ease; transition: background 0.15s ease, color 0.15s ease;
} }
@ -182,7 +182,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 500; font-weight: var(--fw-medium);
text-decoration: none; text-decoration: none;
transition: background 0.15s ease, color 0.15s ease; transition: background 0.15s ease, color 0.15s ease;
} }
@ -193,10 +193,10 @@
.kx-shell__nav-link.is-active { .kx-shell__nav-link.is-active {
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-shell__nav-link--top { .kx-shell__nav-link--top {
font-weight: 600; font-weight: var(--fw-semibold);
} }
/* 스코프 진입(button) — nav-link 와 동일 표현. */ /* 스코프 진입(button) — nav-link 와 동일 표현. */
.kx-shell__nav-link--btn { .kx-shell__nav-link--btn {
@ -267,14 +267,14 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 600; font-weight: var(--fw-semibold);
font-size: 13px; font-size: 13px;
} }
.kx-shell__user-name { .kx-shell__user-name {
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 0;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -376,7 +376,7 @@
} }
.kx-shell__crumb-cur { .kx-shell__crumb-cur {
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 600; font-weight: var(--fw-semibold);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -511,12 +511,12 @@
padding: 0; padding: 0;
font-family: inherit; font-family: inherit;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 500; font-weight: var(--fw-medium);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-mdi__tab.is-active .kx-mdi__tab-btn { .kx-mdi__tab.is-active .kx-mdi__tab-btn {
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-mdi__tab-label { .kx-mdi__tab-label {
overflow: hidden; overflow: hidden;

View File

@ -71,7 +71,7 @@
transform: rotate(-24deg); transform: rotate(-24deg);
color: rgba(255, 255, 255, 0.55); color: rgba(255, 255, 255, 0.55);
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
pointer-events: none; pointer-events: none;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
@ -92,16 +92,16 @@
color: var(--color-on-accent); color: var(--color-on-accent);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
padding: 2px 8px; padding: 2px 8px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-aiimg__sample { .kx-aiimg__sample {
background: rgba(109, 74, 255, 0.9); background: rgba(109, 74, 255, 0.9);
color: var(--color-on-accent); color: var(--color-on-accent);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
padding: 2px 8px; padding: 2px 8px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-aiimg__gen { .kx-aiimg__gen {
position: absolute; position: absolute;
@ -116,7 +116,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 6px 12px; padding: 6px 12px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
box-shadow: var(--shadow-level2); box-shadow: var(--shadow-level2);
} }
@ -124,7 +124,7 @@
background: var(--color-ai-accent-strong); background: var(--color-ai-accent-strong);
} }
.kx-aiimg__notice { .kx-aiimg__notice {
font-size: 11px; font-size: var(--fs-micro);
line-height: 15px; line-height: 15px;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }

View File

@ -6,7 +6,7 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
line-height: var(--lh-caption); line-height: var(--lh-caption);
font-weight: 600; font-weight: var(--fw-semibold);
white-space: nowrap; white-space: nowrap;
} }
@ -66,5 +66,5 @@
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
} }
.kx-badge__spark { .kx-badge__spark {
font-size: 11px; font-size: var(--fs-micro);
} }

View File

@ -8,7 +8,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
border: 1px solid transparent; border: 1px solid transparent;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
line-height: 1; line-height: 1;
transition: background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease; transition: background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
white-space: nowrap; white-space: nowrap;

View File

@ -29,7 +29,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: rgba(16, 24, 40, 0.72); background: rgba(16, 24, 40, 0.72);
pointer-events: none; pointer-events: none;
@ -64,8 +64,8 @@
border-radius: 50%; border-radius: 50%;
background: var(--color-white); background: var(--color-white);
color: var(--color-primary-700); color: var(--color-primary-700);
font-size: 14px; font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: -1px; letter-spacing: -1px;
box-shadow: var(--shadow-level2); box-shadow: var(--shadow-level2);
} }

View File

@ -61,7 +61,7 @@
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 0;
font-size: var(--fs-h3, var(--fs-body)); font-size: var(--fs-h3, var(--fs-body));
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@ -25,7 +25,7 @@
} }
.kx-state__title { .kx-state__title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-state__desc { .kx-state__desc {

View File

@ -130,7 +130,7 @@
"contactAddress": "217-60 Kintex-ro, Ilsanseo-gu, Goyang-si, Gyeonggi-do", "contactAddress": "217-60 Kintex-ro, Ilsanseo-gu, Goyang-si, Gyeonggi-do",
"contactTel": "Main line 1899-1001", "contactTel": "Main line 1899-1001",
"contactEmail": "exhibition@kintex.com", "contactEmail": "exhibition@kintex.com",
"bottom": "© KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si" "bottom": "2026 © KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si"
}, },
"footerbar": { "footerbar": {
"copyright": "© 2026 KINTEX. All rights reserved.", "copyright": "© 2026 KINTEX. All rights reserved.",

View File

@ -130,7 +130,7 @@
"contactAddress": "京畿道高陽市一山西区KINTEXロ217-60", "contactAddress": "京畿道高陽市一山西区KINTEXロ217-60",
"contactTel": "代表番号 1899-1001", "contactTel": "代表番号 1899-1001",
"contactEmail": "exhibition@kintex.com", "contactEmail": "exhibition@kintex.com",
"bottom": "© KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si" "bottom": "2026 © KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si"
}, },
"footerbar": { "footerbar": {
"copyright": "© 2026 KINTEX. All rights reserved.", "copyright": "© 2026 KINTEX. All rights reserved.",

View File

@ -130,7 +130,7 @@
"contactAddress": "경기도 고양시 일산서구 킨텍스로 217-60", "contactAddress": "경기도 고양시 일산서구 킨텍스로 217-60",
"contactTel": "대표번호 1899-1001", "contactTel": "대표번호 1899-1001",
"contactEmail": "exhibition@kintex.com", "contactEmail": "exhibition@kintex.com",
"bottom": "© KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si" "bottom": "2026 © KINTEX. All Rights Reserved. (10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si"
}, },
"footerbar": { "footerbar": {
"copyright": "© 2026 KINTEX. All rights reserved.", "copyright": "© 2026 KINTEX. All rights reserved.",

View File

@ -130,7 +130,7 @@
"contactAddress": "京畿道高阳市一山西区KINTEX路217-60", "contactAddress": "京畿道高阳市一山西区KINTEX路217-60",
"contactTel": "总机 1899-1001", "contactTel": "总机 1899-1001",
"contactEmail": "exhibition@kintex.com", "contactEmail": "exhibition@kintex.com",
"bottom": "© KINTEX. 版权所有。(10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si" "bottom": "2026 © KINTEX. 版权所有。(10390) 217-60, Kintex-ro, Ilsanseo-gu, Goyang-si"
}, },
"footerbar": { "footerbar": {
"copyright": "© 2026 KINTEX. 版权所有。", "copyright": "© 2026 KINTEX. 版权所有。",

View File

@ -15,7 +15,7 @@
.kx-adm__title { .kx-adm__title {
font-size: var(--fs-h1); font-size: var(--fs-h1);
line-height: var(--lh-h1); line-height: var(--lh-h1);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-adm__subtitle { .kx-adm__subtitle {
@ -34,7 +34,7 @@
} }
.kx-adm-field__label { .kx-adm-field__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-adm-field__input { .kx-adm-field__input {
@ -60,7 +60,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
white-space: nowrap; white-space: nowrap;
} }
.kx-adm-pill--success { color: var(--color-success); background: #e6f4ee; } .kx-adm-pill--success { color: var(--color-success); background: #e6f4ee; }
@ -114,7 +114,7 @@
.kx-adm-audit thead th { .kx-adm-audit thead th {
background: var(--color-neutral-050); background: var(--color-neutral-050);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
letter-spacing: 0.02em; letter-spacing: 0.02em;
border-bottom: 2px solid var(--color-neutral-200); border-bottom: 2px solid var(--color-neutral-200);
@ -132,19 +132,19 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.kx-adm-actor__body { display: flex; flex-direction: column; } .kx-adm-actor__body { display: flex; flex-direction: column; }
.kx-adm-actor__name { font-weight: 600; color: var(--color-neutral-900); } .kx-adm-actor__name { font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-actor__id { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-actor__id { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-action { .kx-adm-action {
display: inline-block; display: inline-block;
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-adm-summary { .kx-adm-summary {
@ -158,7 +158,7 @@
white-space: nowrap; white-space: nowrap;
} }
.kx-adm-target { display: flex; flex-direction: column; } .kx-adm-target { display: flex; flex-direction: column; }
.kx-adm-target__type { font-weight: 600; color: var(--color-neutral-700); } .kx-adm-target__type { font-weight: var(--fw-semibold); color: var(--color-neutral-700); }
.kx-adm-target__id { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-target__id { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-pager { .kx-adm-pager {
@ -173,7 +173,7 @@
} }
.kx-adm-pager__info { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-pager__info { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-pager__nav { display: flex; align-items: center; gap: var(--space-3); } .kx-adm-pager__nav { display: flex; align-items: center; gap: var(--space-3); }
.kx-adm-pager__page { font-size: var(--fs-body); font-weight: 600; color: var(--color-neutral-700); } .kx-adm-pager__page { font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--color-neutral-700); }
.kx-adm-pager__size { .kx-adm-pager__size {
display: flex; display: flex;
align-items: center; align-items: center;
@ -193,7 +193,7 @@
.kx-adm-group__body { flex: 1; min-width: 0; padding: var(--space-4); } .kx-adm-group__body { flex: 1; min-width: 0; padding: var(--space-4); }
.kx-adm-group__title { .kx-adm-group__title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
text-transform: capitalize; text-transform: capitalize;
@ -211,25 +211,25 @@
gap: 6px; gap: 6px;
flex-wrap: wrap; flex-wrap: wrap;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-family: ui-monospace, 'JetBrains Mono', monospace; font-family: ui-monospace, 'JetBrains Mono', monospace;
} }
.kx-adm-setrow__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-setrow__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-setrow__control { display: flex; align-items: center; gap: var(--space-2); } .kx-adm-setrow__control { display: flex; align-items: center; gap: var(--space-2); }
.kx-adm-setrow__input { flex: 1; min-width: 0; } .kx-adm-setrow__input { flex: 1; min-width: 0; }
.kx-adm-setrow__hint { margin-top: 6px; font-size: 11px; color: var(--color-neutral-500); } .kx-adm-setrow__hint { margin-top: 6px; font-size: var(--fs-micro); color: var(--color-neutral-500); }
.kx-adm-secret { .kx-adm-secret {
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 1px 6px; padding: 1px 6px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
.kx-adm-typechip { .kx-adm-typechip {
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-100); background: var(--color-neutral-100);
padding: 1px 6px; padding: 1px 6px;
@ -265,7 +265,7 @@
.kx-adm-switch input:focus-visible + .kx-adm-switch__track { outline: 2px solid var(--color-primary-100); } .kx-adm-switch input:focus-visible + .kx-adm-switch__track { outline: 2px solid var(--color-primary-100); }
.kx-adm-switch__label { font-size: var(--fs-caption); color: var(--color-neutral-700); } .kx-adm-switch__label { font-size: var(--fs-caption); color: var(--color-neutral-700); }
.kx-adm-add__title { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); margin-bottom: var(--space-3); } .kx-adm-add__title { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); margin-bottom: var(--space-3); }
.kx-adm-add__grid { .kx-adm-add__grid {
display: grid; display: grid;
grid-template-columns: 1.4fr 1.4fr 0.8fr; grid-template-columns: 1.4fr 1.4fr 0.8fr;
@ -286,7 +286,7 @@
.kx-adm-ruleset { display: grid; grid-template-columns: 300px 1fr; gap: var(--space-4); align-items: start; } .kx-adm-ruleset { display: grid; grid-template-columns: 300px 1fr; gap: var(--space-4); align-items: start; }
.kx-adm-ruleset__versions { display: flex; flex-direction: column; gap: var(--space-3); } .kx-adm-ruleset__versions { display: flex; flex-direction: column; gap: var(--space-3); }
.kx-adm-ruleset__vhead { display: flex; align-items: center; justify-content: space-between; } .kx-adm-ruleset__vhead { display: flex; align-items: center; justify-content: space-between; }
.kx-adm-ruleset__vhead h2 { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-ruleset__vhead h2 { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-vlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); } .kx-adm-vlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.kx-adm-vitem { .kx-adm-vitem {
width: 100%; width: 100%;
@ -317,13 +317,13 @@
padding: var(--space-4); padding: var(--space-4);
border-bottom: var(--border-card); border-bottom: var(--border-card);
} }
.kx-adm-ruleset__ehead h2 { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-ruleset__ehead h2 { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-rules { width: 100%; border-collapse: collapse; } .kx-adm-rules { width: 100%; border-collapse: collapse; }
.kx-adm-rules th, .kx-adm-rules td { padding: 10px 12px; text-align: left; font-size: var(--fs-body); } .kx-adm-rules th, .kx-adm-rules td { padding: 10px 12px; text-align: left; font-size: var(--fs-body); }
.kx-adm-rules thead th { .kx-adm-rules thead th {
background: var(--color-neutral-050); background: var(--color-neutral-050);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
letter-spacing: 0.02em; letter-spacing: 0.02em;
border-bottom: 2px solid var(--color-neutral-200); border-bottom: 2px solid var(--color-neutral-200);
@ -331,8 +331,8 @@
vertical-align: bottom; vertical-align: bottom;
} }
.kx-adm-rules tbody tr { border-bottom: 1px solid var(--color-neutral-200); } .kx-adm-rules tbody tr { border-bottom: 1px solid var(--color-neutral-200); }
.kx-adm-rulename { display: block; font-weight: 600; color: var(--color-neutral-900); } .kx-adm-rulename { display: block; font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-rulecode { display: block; font-size: 11px; color: var(--color-neutral-500); font-family: ui-monospace, monospace; } .kx-adm-rulecode { display: block; font-size: var(--fs-micro); color: var(--color-neutral-500); font-family: ui-monospace, monospace; }
.kx-adm-disclaimer { padding: var(--space-3) var(--space-4); font-size: var(--fs-caption); color: var(--color-neutral-500); border-top: var(--border-card); } .kx-adm-disclaimer { padding: var(--space-3) var(--space-4); font-size: var(--fs-caption); color: var(--color-neutral-500); border-top: var(--border-card); }
/* ═══════════ SCR-A9 테넌트 ═══════════ */ /* ═══════════ SCR-A9 테넌트 ═══════════ */
@ -345,13 +345,13 @@
padding: var(--space-4); padding: var(--space-4);
border-bottom: var(--border-card); border-bottom: var(--border-card);
} }
.kx-adm-tenant__lhead h2 { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-tenant__lhead h2 { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-tenants { width: 100%; border-collapse: collapse; } .kx-adm-tenants { width: 100%; border-collapse: collapse; }
.kx-adm-tenants th, .kx-adm-tenants td { padding: 10px 12px; text-align: left; font-size: var(--fs-body); } .kx-adm-tenants th, .kx-adm-tenants td { padding: 10px 12px; text-align: left; font-size: var(--fs-body); }
.kx-adm-tenants thead th { .kx-adm-tenants thead th {
background: var(--color-neutral-050); background: var(--color-neutral-050);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
letter-spacing: 0.02em; letter-spacing: 0.02em;
border-bottom: 2px solid var(--color-neutral-200); border-bottom: 2px solid var(--color-neutral-200);
@ -361,10 +361,10 @@
.kx-adm-tenants tbody tr { border-bottom: 1px solid var(--color-neutral-200); } .kx-adm-tenants tbody tr { border-bottom: 1px solid var(--color-neutral-200); }
.kx-adm-tenants tbody tr:hover { background: var(--color-primary-050); } .kx-adm-tenants tbody tr:hover { background: var(--color-primary-050); }
.kx-adm-tenants tbody tr.is-selected { background: var(--color-primary-050); } .kx-adm-tenants tbody tr.is-selected { background: var(--color-primary-050); }
.kx-adm-tenants__name { font-weight: 600; color: var(--color-neutral-900); } .kx-adm-tenants__name { font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-tenant__wizard { display: flex; flex-direction: column; gap: 4px; } .kx-adm-tenant__wizard { display: flex; flex-direction: column; gap: 4px; }
.kx-adm-wiz__title { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-wiz__title { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-wiz { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; } .kx-adm-wiz { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; }
.kx-adm-wiz__step { display: flex; gap: var(--space-3); padding-bottom: var(--space-4); position: relative; } .kx-adm-wiz__step { display: flex; gap: var(--space-3); padding-bottom: var(--space-4); position: relative; }
.kx-adm-wiz__step:not(:last-child)::before { .kx-adm-wiz__step:not(:last-child)::before {
@ -385,7 +385,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
background: var(--color-neutral-100); background: var(--color-neutral-100);
color: var(--color-neutral-500); color: var(--color-neutral-500);
z-index: 1; z-index: 1;
@ -393,7 +393,7 @@
.kx-adm-wiz__step.is-done .kx-adm-wiz__dot { background: var(--color-success); color: var(--color-on-accent); } .kx-adm-wiz__step.is-done .kx-adm-wiz__dot { background: var(--color-success); color: var(--color-on-accent); }
.kx-adm-wiz__step.is-current .kx-adm-wiz__dot { background: var(--color-primary-600); color: var(--color-on-accent); } .kx-adm-wiz__step.is-current .kx-adm-wiz__dot { background: var(--color-primary-600); color: var(--color-on-accent); }
.kx-adm-wiz__body { display: flex; flex-direction: column; gap: 2px; padding-top: 3px; } .kx-adm-wiz__body { display: flex; flex-direction: column; gap: 2px; padding-top: 3px; }
.kx-adm-wiz__label { font-size: var(--fs-body); font-weight: 600; color: var(--color-neutral-900); } .kx-adm-wiz__label { font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-wiz__step.is-todo .kx-adm-wiz__label { color: var(--color-neutral-500); } .kx-adm-wiz__step.is-todo .kx-adm-wiz__label { color: var(--color-neutral-500); }
.kx-adm-wiz__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-wiz__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); }
@ -422,7 +422,7 @@
.kx-adm-tbl thead th { .kx-adm-tbl thead th {
background: var(--color-neutral-050); background: var(--color-neutral-050);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
letter-spacing: 0.02em; letter-spacing: 0.02em;
border-bottom: 2px solid var(--color-neutral-200); border-bottom: 2px solid var(--color-neutral-200);
@ -432,7 +432,7 @@
.kx-adm-tbl tbody tr { border-bottom: 1px solid var(--color-neutral-200); } .kx-adm-tbl tbody tr { border-bottom: 1px solid var(--color-neutral-200); }
.kx-adm-tbl tbody tr:hover { background: var(--color-primary-050); } .kx-adm-tbl tbody tr:hover { background: var(--color-primary-050); }
.kx-adm-tbl tbody tr.is-selected { background: var(--color-primary-050); } .kx-adm-tbl tbody tr.is-selected { background: var(--color-primary-050); }
.kx-adm-tbl__strong { font-weight: 600; color: var(--color-neutral-900); } .kx-adm-tbl__strong { font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-tbl__actions { display: flex; gap: var(--space-2); justify-content: flex-end; white-space: nowrap; } .kx-adm-tbl__actions { display: flex; gap: var(--space-2); justify-content: flex-end; white-space: nowrap; }
/* 역할 칩 */ /* 역할 칩 */
@ -442,7 +442,7 @@
padding: 2px 8px; padding: 2px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-adm-rolechip--admin { color: var(--color-error); background: #fef3f2; } .kx-adm-rolechip--admin { color: var(--color-error); background: #fef3f2; }
.kx-adm-rolechip--manager { color: var(--color-primary-700); background: var(--color-primary-100); } .kx-adm-rolechip--manager { color: var(--color-primary-700); background: var(--color-primary-100); }
@ -475,7 +475,7 @@
box-shadow: var(--shadow-modal, 0 20px 48px rgba(15, 23, 42, 0.24)); box-shadow: var(--shadow-modal, 0 20px 48px rgba(15, 23, 42, 0.24));
padding: var(--space-5, 24px); padding: var(--space-5, 24px);
} }
.kx-adm-modal__title { font-size: var(--fs-h2); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-modal__title { font-size: var(--fs-h2); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-modal__desc { margin-top: 4px; font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-modal__desc { margin-top: 4px; font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-modal__grid { .kx-adm-modal__grid {
margin-top: var(--space-4); margin-top: var(--space-4);
@ -510,7 +510,7 @@
} }
.kx-adm-secnote { .kx-adm-secnote {
margin-top: var(--space-3); margin-top: var(--space-3);
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -535,8 +535,8 @@
} }
.kx-adm-permcheck input { margin-top: 3px; } .kx-adm-permcheck input { margin-top: 3px; }
.kx-adm-permcheck__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; } .kx-adm-permcheck__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kx-adm-permcheck__name { font-weight: 600; color: var(--color-neutral-900); font-size: var(--fs-body); } .kx-adm-permcheck__name { font-weight: var(--fw-semibold); color: var(--color-neutral-900); font-size: var(--fs-body); }
.kx-adm-permcheck__code { font-size: 11px; color: var(--color-neutral-500); font-family: ui-monospace, monospace; } .kx-adm-permcheck__code { font-size: var(--fs-micro); color: var(--color-neutral-500); font-family: ui-monospace, monospace; }
.kx-adm-permcheck__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-permcheck__desc { font-size: var(--fs-caption); color: var(--color-neutral-500); }
/* ── 공통코드 2단(그룹 → 코드) ── */ /* ── 공통코드 2단(그룹 → 코드) ── */
@ -550,7 +550,7 @@
padding: var(--space-4); padding: var(--space-4);
border-bottom: var(--border-card); border-bottom: var(--border-card);
} }
.kx-adm-codes__head h2 { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-codes__head h2 { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-glist { list-style: none; margin: 0; padding: var(--space-2); display: flex; flex-direction: column; gap: 4px; } .kx-adm-glist { list-style: none; margin: 0; padding: var(--space-2); display: flex; flex-direction: column; gap: 4px; }
.kx-adm-gitem { .kx-adm-gitem {
width: 100%; width: 100%;
@ -567,8 +567,8 @@
} }
.kx-adm-gitem:hover { background: var(--color-neutral-050); } .kx-adm-gitem:hover { background: var(--color-neutral-050); }
.kx-adm-gitem.is-active { border-left-color: var(--color-primary-600); background: var(--color-primary-050); } .kx-adm-gitem.is-active { border-left-color: var(--color-primary-600); background: var(--color-primary-050); }
.kx-adm-gitem__name { font-weight: 600; color: var(--color-neutral-900); } .kx-adm-gitem__name { font-weight: var(--fw-semibold); color: var(--color-neutral-900); }
.kx-adm-gitem__code { font-size: 11px; color: var(--color-neutral-500); font-family: ui-monospace, monospace; } .kx-adm-gitem__code { font-size: var(--fs-micro); color: var(--color-neutral-500); font-family: ui-monospace, monospace; }
/* ── 메뉴 트리 ── */ /* ── 메뉴 트리 ── */
.kx-adm-menu__name { display: inline-flex; align-items: center; gap: 6px; } .kx-adm-menu__name { display: inline-flex; align-items: center; gap: 6px; }
@ -586,7 +586,7 @@
max-width: 480px; max-width: 480px;
} }
.kx-adm-denied__icon { color: var(--color-error); } .kx-adm-denied__icon { color: var(--color-error); }
.kx-adm-denied__title { font-size: var(--fs-h1); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-denied__title { font-size: var(--fs-h1); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-denied__desc { font-size: var(--fs-body); color: var(--color-neutral-500); } .kx-adm-denied__desc { font-size: var(--fs-body); color: var(--color-neutral-500); }
.kx-adm-denied__code { margin-top: var(--space-3); font-size: var(--fs-caption); font-family: ui-monospace, monospace; color: var(--color-neutral-400, #94a3b8); } .kx-adm-denied__code { margin-top: var(--space-3); font-size: var(--fs-caption); font-family: ui-monospace, monospace; color: var(--color-neutral-400, #94a3b8); }
@ -632,18 +632,18 @@
.kx-adm-health-card--error { border-left-color: var(--color-error); } .kx-adm-health-card--error { border-left-color: var(--color-error); }
.kx-adm-health-card--neutral { border-left-color: var(--color-warning); } .kx-adm-health-card--neutral { border-left-color: var(--color-warning); }
.kx-adm-health-card__label { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-health-card__label { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-health-card__value { font-size: var(--fs-h2); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-health-card__value { font-size: var(--fs-h2); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-health-card__sub { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-health-card__sub { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); } .kx-adm-metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
.kx-adm-metric { display: flex; flex-direction: column; gap: 4px; } .kx-adm-metric { display: flex; flex-direction: column; gap: 4px; }
.kx-adm-metric__label { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-metric__label { font-size: var(--fs-caption); color: var(--color-neutral-500); }
.kx-adm-metric__value { font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); } .kx-adm-metric__value { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-adm-metric__sub { font-size: var(--fs-caption); color: var(--color-neutral-500); } .kx-adm-metric__sub { font-size: var(--fs-caption); color: var(--color-neutral-500); }
/* 폼(인증정책·설정) */ /* 폼(인증정책·설정) */
.kx-adm-form { display: grid; gap: var(--space-3); } .kx-adm-form { display: grid; gap: var(--space-3); }
.kx-adm-form__section { .kx-adm-form__section {
font-size: var(--fs-h3); font-weight: 700; color: var(--color-neutral-900); font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900);
margin: var(--space-2) 0 0; padding-top: var(--space-2); border-top: 1px solid var(--color-neutral-100); margin: var(--space-2) 0 0; padding-top: var(--space-2); border-top: 1px solid var(--color-neutral-100);
} }
.kx-adm-form__section:first-child { border-top: none; padding-top: 0; } .kx-adm-form__section:first-child { border-top: none; padding-top: 0; }

View File

@ -37,7 +37,7 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-success); color: var(--color-success);
} }
.kx-admin__status-dot { .kx-admin__status-dot {
@ -133,14 +133,14 @@
50% { box-shadow: 0 0 0 5px rgba(217, 45, 32, 0); } 50% { box-shadow: 0 0 0 5px rgba(217, 45, 32, 0); }
} }
.kx-livecard__state { .kx-livecard__state {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.02em; letter-spacing: 0.02em;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-livecard__title { .kx-livecard__title {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-livecard__meta { .kx-livecard__meta {
@ -192,7 +192,7 @@
} }
.kx-slideadm__label { .kx-slideadm__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-slideadm__upload input[type='file'] { .kx-slideadm__upload input[type='file'] {
@ -233,7 +233,7 @@
} }
.kx-slideadm__count { .kx-slideadm__count {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 500; font-weight: var(--fw-medium);
color: var(--color-neutral-500); color: var(--color-neutral-500);
margin-left: var(--space-2); margin-left: var(--space-2);
} }
@ -292,8 +292,8 @@
flex-shrink: 0; flex-shrink: 0;
padding: 2px 8px; padding: 2px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-slideadm__live-badge { .kx-slideadm__live-badge {
background: rgba(0, 102, 179, 0.1); background: rgba(0, 102, 179, 0.1);
@ -313,7 +313,7 @@
white-space: nowrap; white-space: nowrap;
} }
.kx-slideadm__row-meta { .kx-slideadm__row-meta {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
margin-top: 2px; margin-top: 2px;
} }

View File

@ -134,10 +134,10 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 500; font-weight: var(--fw-medium);
} }
.kx-dept-tree__row.is-selected .kx-dept-tree__name { .kx-dept-tree__row.is-selected .kx-dept-tree__name {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-dept-tree__sort { .kx-dept-tree__sort {
@ -188,7 +188,7 @@
.kx-dept-detail__headMain { min-width: 0; } .kx-dept-detail__headMain { min-width: 0; }
.kx-dept-detail__title { .kx-dept-detail__title {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0; margin: 0;
} }

View File

@ -81,7 +81,7 @@
} }
.kx-aicon__ungrounded { .kx-aicon__ungrounded {
color: #B54708; color: #B54708;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-aicon__degraded { .kx-aicon__degraded {
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -118,7 +118,7 @@
} }
.kx-aicon__cites-h { .kx-aicon__cites-h {
display: block; display: block;
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700, #344054); color: var(--color-neutral-700, #344054);
margin-bottom: var(--space-1, 4px); margin-bottom: var(--space-1, 4px);
} }

View File

@ -91,7 +91,7 @@
} }
.kx-bi__global-title { .kx-bi__global-title {
font-size: var(--fs-h3, 1.125rem); font-size: var(--fs-h3, 1.125rem);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
/* 전역 섹션의 하위 split은 동일 폭 2컬럼 */ /* 전역 섹션의 하위 split은 동일 폭 2컬럼 */

View File

@ -76,12 +76,12 @@
} }
.kx-appqr__name { .kx-appqr__name {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-appqr__ver { .kx-appqr__ver {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-primary-600); background: var(--color-primary-600);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
@ -145,7 +145,7 @@
background: var(--color-primary-050); background: var(--color-primary-050);
color: var(--color-primary-700); color: var(--color-primary-700);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-appqr__step-text { .kx-appqr__step-text {
font-size: var(--fs-body); font-size: var(--fs-body);

View File

@ -185,7 +185,7 @@ export function ManagerApprovalQueuePage() {
) : rows.length === 0 ? ( ) : rows.length === 0 ? (
<EmptyState title={t(`approval.empty.${tab}`)} /> <EmptyState title={t(`approval.empty.${tab}`)} />
) : ( ) : (
<table className="kx-aq__table"> <table className="kx-table">
<thead> <thead>
<tr> <tr>
<th>{t('approval.colTitle')}</th> <th>{t('approval.colTitle')}</th>

View File

@ -16,7 +16,7 @@
} }
.kx-apf__head h1 { .kx-apf__head h1 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-apf__back { .kx-apf__back {
@ -64,7 +64,7 @@
.kx-apf__reject-head strong { .kx-apf__reject-head strong {
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: #b91c1c; color: #b91c1c;
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-apf__reject-body { .kx-apf__reject-body {
font-size: var(--fs-body); font-size: var(--fs-body);
@ -88,7 +88,7 @@
font-size: var(--fs-caption); font-size: var(--fs-caption);
} }
.kx-apf__prev-act { .kx-apf__prev-act {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-800); color: var(--color-neutral-800);
} }
.kx-apf__prev-meta { .kx-apf__prev-meta {
@ -125,7 +125,7 @@
} }
.kx-apf__label { .kx-apf__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-apf__req { .kx-apf__req {
@ -192,7 +192,7 @@
min-width: 52px; min-width: 52px;
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
background: var(--color-primary-050); background: var(--color-primary-050);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
@ -260,7 +260,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 6px 14px; padding: 6px 14px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
} }
.kx-line-editor__add:hover:not(:disabled) { .kx-line-editor__add:hover:not(:disabled) {
@ -300,7 +300,7 @@
} }
.kx-apm__head h2 { .kx-apm__head h2 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-apm__close { .kx-apm__close {
@ -318,7 +318,7 @@
} }
.kx-apm__label { .kx-apm__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-apm__textarea { .kx-apm__textarea {
@ -334,7 +334,7 @@
border-color: var(--color-primary-600); border-color: var(--color-primary-600);
} }
.kx-apm__hint { .kx-apm__hint {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-apm__foot { .kx-apm__foot {

View File

@ -25,7 +25,7 @@
} }
.kx-aq__title-row h1 { .kx-aq__title-row h1 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-aq__hall-tabs { .kx-aq__hall-tabs {
@ -45,7 +45,7 @@
border-color: var(--color-primary-600); border-color: var(--color-primary-600);
background: var(--color-primary-050); background: var(--color-primary-050);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-aq__search { .kx-aq__search {
display: flex; display: flex;
@ -123,43 +123,16 @@
overflow: auto; overflow: auto;
background: var(--color-white); background: var(--color-white);
} }
.kx-aq__table { /* 표는 정본 .kx-table 수렴(approval-queue.css 자체 테이블 정의 제거).
width: 100%; * 아래는 /컬럼 modifier 유지. */
border-collapse: collapse;
font-size: var(--fs-body);
}
.kx-aq__table thead {
position: sticky;
top: 0;
}
.kx-aq__table th {
text-align: left;
padding: 10px 12px;
font-size: var(--fs-caption);
color: var(--color-neutral-500);
font-weight: 600;
letter-spacing: 0.02em;
background: var(--color-neutral-050);
border-bottom: 2px solid var(--color-neutral-200);
white-space: nowrap;
vertical-align: bottom;
}
.kx-aq__col-ai { .kx-aq__col-ai {
color: var(--color-ai-accent) !important; color: var(--color-ai-accent) !important;
} }
.kx-aq__col-act { .kx-aq__col-act {
text-align: right; text-align: right;
} }
.kx-aq__table td {
padding: 10px 12px;
border-bottom: 1px solid var(--color-neutral-200);
vertical-align: middle;
}
.kx-aq__table tbody tr:hover {
background: var(--row-hover);
}
.kx-aq__event { .kx-aq__event {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-aq__booth { .kx-aq__booth {
@ -173,8 +146,8 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 3px 8px; padding: 3px 8px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
} }
@ -195,7 +168,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 5px 14px; padding: 5px 14px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
} }
.kx-aq__review:hover { .kx-aq__review:hover {
@ -262,7 +235,7 @@
.kx-aq__load-scale { .kx-aq__load-scale {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-aq__card--ai { .kx-aq__card--ai {
@ -318,7 +291,7 @@
} }
.kx-aq__tab.is-active { .kx-aq__tab.is-active {
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 700; font-weight: var(--fw-bold);
border-bottom-color: var(--color-primary-600); border-bottom-color: var(--color-primary-600);
} }
.kx-aq__notice { .kx-aq__notice {
@ -331,7 +304,7 @@
} }
.kx-aq__count { .kx-aq__count {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-600); color: var(--color-primary-600);
background: var(--color-primary-050); background: var(--color-primary-050);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
@ -350,8 +323,8 @@
align-items: center; align-items: center;
padding: 2px 10px; padding: 2px 10px;
border-radius: 999px; border-radius: 999px;
font-size: 12px; font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
background: rgba(148, 163, 184, 0.16); background: rgba(148, 163, 184, 0.16);
color: #475569; color: #475569;
} }

View File

@ -45,7 +45,7 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-rev__meta { .kx-rev__meta {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-100); background: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
@ -186,7 +186,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: var(--fw-bold);
font-size: var(--fs-caption); font-size: var(--fs-caption);
border: 2px solid rgba(255, 255, 255, 0.3); border: 2px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
@ -244,8 +244,8 @@
border-bottom: 1px solid var(--color-neutral-200); border-bottom: 1px solid var(--color-neutral-200);
} }
.kx-rev__ai-count { .kx-rev__ai-count {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-error); color: var(--color-error);
background: #fef3f2; background: #fef3f2;
padding: 2px 8px; padding: 2px 8px;
@ -277,13 +277,13 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: var(--fw-bold);
font-size: 12px; font-size: var(--fs-caption);
flex-shrink: 0; flex-shrink: 0;
} }
.kx-rev__vtitle { .kx-rev__vtitle {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: 2px; margin-bottom: 2px;
} }
@ -321,8 +321,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
flex-shrink: 0; flex-shrink: 0;
} }
.kx-rev__avatar--con { .kx-rev__avatar--con {
@ -340,7 +340,7 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-rev__comment-top span { .kx-rev__comment-top span {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-rev__comment p { .kx-rev__comment p {
@ -407,8 +407,8 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 12px; font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
background: rgba(148, 163, 184, 0.2); background: rgba(148, 163, 184, 0.2);
color: #475569; color: #475569;
} }

View File

@ -425,13 +425,13 @@ function BoqPanel({
) : ( ) : (
<> <>
<div className="kx-boq__scroll"> <div className="kx-boq__scroll">
<table className="kx-boq__table"> <table className="kx-table">
<thead> <thead>
<tr> <tr>
<th>{t('auction.boq.colSeq')}</th> <th>{t('auction.boq.colSeq')}</th>
<th>{t('auction.boq.colTrade')}</th> <th>{t('auction.boq.colTrade')}</th>
<th>{t('auction.boq.colItem')}</th> <th>{t('auction.boq.colItem')}</th>
<th className="kx-boq__num">{t('auction.boq.colQty')}</th> <th className="kx-num">{t('auction.boq.colQty')}</th>
<th>{t('auction.boq.colUnit')}</th> <th>{t('auction.boq.colUnit')}</th>
<th>{t('auction.boq.colBasis')}</th> <th>{t('auction.boq.colBasis')}</th>
</tr> </tr>
@ -444,7 +444,7 @@ function BoqPanel({
<span className="kx-boq__trade">{l.trade}</span> <span className="kx-boq__trade">{l.trade}</span>
</td> </td>
<td>{l.item}</td> <td>{l.item}</td>
<td className="kx-boq__num tnum">{l.qty.toLocaleString()}</td> <td className="kx-num tnum">{l.qty.toLocaleString()}</td>
<td>{l.unit}</td> <td>{l.unit}</td>
<td className="kx-boq__basis">{l.basis}</td> <td className="kx-boq__basis">{l.basis}</td>
</tr> </tr>

View File

@ -16,7 +16,7 @@
font-size: var(--fs-h1); font-size: var(--fs-h1);
line-height: var(--lh-h1); line-height: var(--lh-h1);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-auc__subtitle { .kx-auc__subtitle {
font-size: var(--fs-body); font-size: var(--fs-body);
@ -35,8 +35,8 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 3px 10px; padding: 3px 10px;
@ -119,7 +119,7 @@
.kx-auc-card__name { .kx-auc-card__name {
font-size: var(--fs-h3); font-size: var(--fs-h3);
line-height: var(--lh-h3); line-height: var(--lh-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-auc-card__status { .kx-auc-card__status {
@ -152,7 +152,7 @@
} }
.kx-auc-card__metric-value { .kx-auc-card__metric-value {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-auc-card__metric-value--price { .kx-auc-card__metric-value--price {
@ -176,7 +176,7 @@
gap: 4px; gap: 4px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-weight: 600; font-weight: var(--fw-semibold);
} }
/* 카테고리·유형 태그 */ /* 카테고리·유형 태그 */
@ -186,7 +186,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
@ -206,7 +206,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-auc-pill--live { .kx-auc-pill--live {
background: #e6f4ee; background: #e6f4ee;
@ -237,7 +237,7 @@
} }
.kx-auc-open__head h2 { .kx-auc-open__head h2 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
@ -265,15 +265,15 @@
.kx-auc .kx-field__label { .kx-auc .kx-field__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-weight: 600; font-weight: var(--fw-semibold);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.kx-auc .kx-field__label-note { .kx-auc .kx-field__label-note {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-error); color: var(--color-error);
font-weight: 500; font-weight: var(--fw-medium);
} }
.kx-auc .kx-input { .kx-auc .kx-input {
height: 36px; height: 36px;
@ -321,7 +321,7 @@
justify-content: space-between; justify-content: space-between;
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-weight: 600; font-weight: var(--fw-semibold);
margin-bottom: 4px; margin-bottom: 4px;
} }
.kx-weight input[type='range'] { .kx-weight input[type='range'] {
@ -343,12 +343,12 @@
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
color: var(--color-ai-accent); color: var(--color-ai-accent);
cursor: pointer; cursor: pointer;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-auc-attach small { .kx-auc-attach small {
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-weight: 400; font-weight: var(--fw-regular);
font-size: 11px; font-size: var(--fs-micro);
} }
/* 초대 업체 리스트 */ /* 초대 업체 리스트 */
@ -374,9 +374,9 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-invite__blocked-tag { .kx-invite__blocked-tag {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-error); color: var(--color-error);
font-weight: 600; font-weight: var(--fw-semibold);
} }
/* ── SCR-27 상세 레이아웃 ── */ /* ── SCR-27 상세 레이아웃 ── */
@ -404,7 +404,7 @@
background: #fef3f2; background: #fef3f2;
color: var(--color-error); color: var(--color-error);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
padding: 3px 10px; padding: 3px 10px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
} }
@ -435,14 +435,14 @@
align-items: flex-end; align-items: flex-end;
} }
.kx-aucd__metric-label { .kx-aucd__metric-label {
font-size: 11px; font-size: var(--fs-micro);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.04em; letter-spacing: 0.04em;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-aucd__metric-value { .kx-aucd__metric-value {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-aucd__metric-value--ok { .kx-aucd__metric-value--ok {
@ -481,7 +481,7 @@
border: none; border: none;
background: transparent; background: transparent;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
} }
@ -518,7 +518,7 @@
.kx-viewer__placeholder-title { .kx-viewer__placeholder-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-viewer__placeholder-desc { .kx-viewer__placeholder-desc {
font-size: var(--fs-caption); font-size: var(--fs-caption);
@ -529,7 +529,7 @@
position: absolute; position: absolute;
right: 16px; right: 16px;
bottom: 12px; bottom: 12px;
font-size: 11px; font-size: var(--fs-micro);
font-style: italic; font-style: italic;
color: rgba(255, 255, 255, 0.4); color: rgba(255, 255, 255, 0.4);
pointer-events: none; pointer-events: none;
@ -556,7 +556,7 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-toggle { .kx-toggle {
@ -624,7 +624,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: var(--fw-bold);
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
flex-shrink: 0; flex-shrink: 0;
@ -639,7 +639,7 @@
} }
.kx-rank__alias { .kx-rank__alias {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-rank__row--me .kx-rank__alias { .kx-rank__row--me .kx-rank__alias {
@ -647,21 +647,21 @@
} }
.kx-rank__price { .kx-rank__price {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-rank__price--masked { .kx-rank__price--masked {
font-size: var(--fs-body); font-size: var(--fs-body);
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-rank__tag { .kx-rank__tag {
font-size: 11px; font-size: var(--fs-micro);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
background: #e6f4ee; background: #e6f4ee;
color: var(--color-success); color: var(--color-success);
font-weight: 600; font-weight: var(--fw-semibold);
white-space: nowrap; white-space: nowrap;
} }
@ -683,21 +683,21 @@
background: var(--color-white); background: var(--color-white);
} }
.kx-countdown__label { .kx-countdown__label {
font-size: 11px; font-size: var(--fs-micro);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.16em; letter-spacing: 0.16em;
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-countdown__value { .kx-countdown__value {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.kx-rank__note { .kx-rank__note {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
text-align: center; text-align: center;
line-height: 1.5; line-height: 1.5;
@ -723,12 +723,12 @@
border-left-color: var(--color-success); border-left-color: var(--color-success);
} }
.kx-award__chip-label { .kx-award__chip-label {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-award__chip-value { .kx-award__chip-value {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
@ -764,14 +764,14 @@
} }
.kx-compare thead th { .kx-compare thead th {
background: var(--color-neutral-050); background: var(--color-neutral-050);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-compare thead th.kx-compare__metric-col, .kx-compare thead th.kx-compare__metric-col,
.kx-compare td.kx-compare__metric-col { .kx-compare td.kx-compare__metric-col {
text-align: left; text-align: left;
width: 22%; width: 22%;
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-compare__co { .kx-compare__co {
@ -779,7 +779,7 @@
} }
.kx-compare__co-name { .kx-compare__co-name {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-compare__co--rec .kx-compare__co-name { .kx-compare__co--rec .kx-compare__co-name {
@ -787,16 +787,16 @@
} }
.kx-compare__co-sub { .kx-compare__co-sub {
display: block; display: block;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 400; font-weight: var(--fw-regular);
color: var(--color-neutral-500); color: var(--color-neutral-500);
margin-top: 2px; margin-top: 2px;
} }
.kx-compare__rec-tag { .kx-compare__rec-tag {
display: inline-block; display: inline-block;
margin-bottom: 6px; margin-bottom: 6px;
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-primary-700); background: var(--color-primary-700);
padding: 2px 8px; padding: 2px 8px;
@ -805,15 +805,15 @@
.kx-compare__best { .kx-compare__best {
background: #f0fbf6; background: #f0fbf6;
color: var(--color-success); color: var(--color-success);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-compare__val-strong { .kx-compare__val-strong {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-compare__note { .kx-compare__note {
display: block; display: block;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
margin-top: 2px; margin-top: 2px;
} }
@ -825,7 +825,7 @@
} }
.kx-compare__score-val { .kx-compare__score-val {
font-size: var(--fs-display); font-size: var(--fs-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
} }
.kx-compare__star { .kx-compare__star {
@ -857,7 +857,7 @@
} }
.kx-risk__grade-value { .kx-risk__grade-value {
font-size: 40px; font-size: 40px;
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
line-height: 1.1; line-height: 1.1;
} }
@ -882,7 +882,7 @@
} }
.kx-award__panel h2 { .kx-award__panel h2 {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-award__rec { .kx-award__rec {
@ -907,11 +907,11 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-award__rec-name { .kx-award__rec-name {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-award__rec-ai { .kx-award__rec-ai {
display: flex; display: flex;
@ -931,12 +931,12 @@
display: block; display: block;
} }
.kx-award__rec-cell-label { .kx-award__rec-cell-label {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-award__rec-cell-value { .kx-award__rec-cell-value {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-award__textarea { .kx-award__textarea {
@ -955,7 +955,7 @@
padding: var(--space-3); padding: var(--space-3);
background: #fef3f2; background: #fef3f2;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 11px; font-size: var(--fs-micro);
line-height: 1.5; line-height: 1.5;
color: var(--color-error); color: var(--color-error);
} }
@ -965,7 +965,7 @@
} }
.kx-award__steps-title { .kx-award__steps-title {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
} }
@ -992,8 +992,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
flex-shrink: 0; flex-shrink: 0;
} }
@ -1036,7 +1036,7 @@
} }
.kx-boq__title { .kx-boq__title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-text-strong); color: var(--color-text-strong);
} }
.kx-boq__scope { .kx-boq__scope {
@ -1048,46 +1048,22 @@
border: var(--border-card); border: var(--border-card);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
.kx-boq__table { /* BOQ 표는 정본 .kx-table 로 수렴. 셀 상단정렬(다행 항목)만 스코프 유지. */
width: 100%; .kx-boq__scroll .kx-table td {
border-collapse: collapse;
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
.kx-boq__table th,
.kx-boq__table td {
padding: 10px 12px;
border-bottom: 1px solid var(--color-neutral-200);
text-align: left;
vertical-align: top; vertical-align: top;
} }
.kx-boq__table thead th {
background: var(--color-neutral-050);
font-size: var(--fs-caption);
font-weight: 600;
letter-spacing: 0.02em;
color: var(--color-neutral-500);
border-bottom: 2px solid var(--color-neutral-200);
white-space: nowrap;
vertical-align: bottom;
position: sticky;
top: 0;
}
.kx-boq__num {
text-align: right;
}
.kx-boq__trade { .kx-boq__trade {
display: inline-block; display: inline-block;
padding: 2px 8px; padding: 2px 8px;
border-radius: 999px; border-radius: 999px;
background: var(--color-primary-surface, #eef2ff); background: var(--color-primary-surface, #eef2ff);
color: var(--color-primary-600, #2f42fc); color: var(--color-primary-600, #2f42fc);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-boq__basis { .kx-boq__basis {
color: var(--color-text-muted); color: var(--color-text-muted);
font-size: 11px; font-size: var(--fs-micro);
} }
.kx-boq__note { .kx-boq__note {
font-size: var(--fs-caption); font-size: var(--fs-caption);

View File

@ -16,8 +16,9 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
.kx-bsales__title { .kx-bsales__title {
font-size: 20px; font-size: var(--fs-h1);
font-weight: 700; line-height: var(--lh-h1);
font-weight: var(--fw-bold);
margin: 0; margin: 0;
} }
.kx-bsales__subtitle { .kx-bsales__subtitle {
@ -38,18 +39,18 @@
gap: 6px; gap: 6px;
padding: 6px 12px; padding: 6px 12px;
border-radius: 999px; border-radius: 999px;
font-size: 12px; font-size: var(--fs-caption);
background: var(--kx-surface-2, #f1f5f9); background: var(--kx-surface-2, #f1f5f9);
border: 1px solid var(--kx-border, #e2e8f0); border: 1px solid var(--kx-border, #e2e8f0);
} }
.kx-bsales__stat strong { font-size: 15px; font-weight: 700; } .kx-bsales__stat strong { font-size: 15px; font-weight: var(--fw-bold); }
.kx-bsales__stat.is-available { color: #15803d; background: #f0fdf4; border-color: #bbf7d0; } .kx-bsales__stat.is-available { color: #15803d; background: #f0fdf4; border-color: #bbf7d0; }
.kx-bsales__stat.is-held { color: #b45309; background: #fffbeb; border-color: #fde68a; } .kx-bsales__stat.is-held { color: #b45309; background: #fffbeb; border-color: #fde68a; }
.kx-bsales__stat.is-sold { color: #475569; background: #f1f5f9; border-color: #cbd5e1; } .kx-bsales__stat.is-sold { color: #475569; background: #f1f5f9; border-color: #cbd5e1; }
.kx-bsales__stat.is-blocked { color: #b91c1c; background: #fef2f2; border-color: #fecaca; } .kx-bsales__stat.is-blocked { color: #b91c1c; background: #fef2f2; border-color: #fecaca; }
.kx-bsales__rate { .kx-bsales__rate {
margin-left: auto; margin-left: auto;
font-size: 12px; font-size: var(--fs-caption);
color: var(--kx-text-muted, #64748b); color: var(--kx-text-muted, #64748b);
} }
@ -94,14 +95,14 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
font-weight: 600; font-weight: var(--fw-semibold);
font-size: 13px; font-size: 13px;
} }
.kx-boothstatus { .kx-boothstatus {
font-size: 11px; font-size: var(--fs-micro);
padding: 2px 8px; padding: 2px 8px;
border-radius: 999px; border-radius: 999px;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-boothstatus.is-available { color: #15803d; background: #f0fdf4; } .kx-boothstatus.is-available { color: #15803d; background: #f0fdf4; }
.kx-boothstatus.is-held { color: #b45309; background: #fffbeb; } .kx-boothstatus.is-held { color: #b45309; background: #fffbeb; }
@ -110,10 +111,10 @@
.kx-boothcard__price { .kx-boothcard__price {
font-size: 22px; font-size: 22px;
font-weight: 700; font-weight: var(--fw-bold);
line-height: 1.1; line-height: 1.1;
} }
.kx-boothcard__won { font-size: 13px; font-weight: 500; color: var(--kx-text-muted, #64748b); margin-left: 2px; } .kx-boothcard__won { font-size: 13px; font-weight: var(--fw-medium); color: var(--kx-text-muted, #64748b); margin-left: 2px; }
.kx-boothcard__meta { .kx-boothcard__meta {
list-style: none; list-style: none;
@ -122,7 +123,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 3px; gap: 3px;
font-size: 12px; font-size: var(--fs-caption);
color: var(--kx-text-muted, #64748b); color: var(--kx-text-muted, #64748b);
} }
@ -134,5 +135,5 @@
} }
.kx-boothcard__actions .kx-btn { .kx-boothcard__actions .kx-btn {
padding: 5px 10px; padding: 5px 10px;
font-size: 12px; font-size: var(--fs-caption);
} }

View File

@ -222,7 +222,7 @@ export function MultilingualCmsPage() {
) : contents.length === 0 ? ( ) : contents.length === 0 ? (
<div className="kx-cms-empty"> .</div> <div className="kx-cms-empty"> .</div>
) : ( ) : (
<table className="kx-i18n-table"> <table className="kx-table kx-i18n-table">
<thead> <thead>
<tr> <tr>
<th style={{ width: '30%' }}> / (KO)</th> <th style={{ width: '30%' }}> / (KO)</th>

View File

@ -39,8 +39,8 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 2px 10px; padding: 2px 10px;
@ -55,8 +55,8 @@
gap: 4px; gap: 4px;
padding: 1px 10px; padding: 1px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
white-space: nowrap; white-space: nowrap;
} }
.kx-cms-pill--draft { .kx-cms-pill--draft {
@ -92,7 +92,7 @@
} }
.kx-cms-label { .kx-cms-label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-cms-input, .kx-cms-input,
@ -278,7 +278,7 @@
padding: 0 0 8px; padding: 0 0 8px;
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-family: inherit; font-family: inherit;
} }
@ -331,8 +331,8 @@
var(--color-neutral-050) 16px var(--color-neutral-050) 16px
); );
color: var(--color-primary-600); color: var(--color-primary-600);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
text-align: center; text-align: center;
padding: 6px; padding: 6px;
} }
@ -375,7 +375,7 @@
} }
.kx-cms-block__kind { .kx-cms-block__kind {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-cms-block__body { .kx-cms-block__body {
@ -413,7 +413,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: var(--color-neutral-050); background: var(--color-neutral-050);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
font-size: var(--fs-body); font-size: var(--fs-body);
flex-wrap: wrap; flex-wrap: wrap;
} }
@ -435,8 +435,8 @@
gap: var(--space-2); gap: var(--space-2);
} }
.kx-cms-flow__label { .kx-cms-flow__label {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -474,7 +474,7 @@
} }
.kx-cms-step.is-current { .kx-cms-step.is-current {
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-cms-step.is-current .kx-cms-step__dot { .kx-cms-step.is-current .kx-cms-step__dot {
border-color: var(--color-primary-600); border-color: var(--color-primary-600);
@ -502,11 +502,11 @@
} }
.kx-cms-ver__v { .kx-cms-ver__v {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-cms-ver__meta { .kx-cms-ver__meta {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-cms-schedcard { .kx-cms-schedcard {
@ -519,8 +519,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
margin-bottom: 4px; margin-bottom: 4px;
} }
@ -611,7 +611,7 @@
background: transparent; background: transparent;
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
font-family: inherit; font-family: inherit;
} }
@ -680,7 +680,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-mb-site__logo { .kx-mb-site__logo {
@ -692,7 +692,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 800; font-weight: var(--fw-bold);
font-style: italic; font-style: italic;
} }
.kx-mb-site__menu { .kx-mb-site__menu {
@ -718,14 +718,14 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
background: rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.14);
border: 1px solid rgba(255, 255, 255, 0.25); border: 1px solid rgba(255, 255, 255, 0.25);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
} }
.kx-mb-hero__title { .kx-mb-hero__title {
font-size: 30px; font-size: 30px;
line-height: 1.2; line-height: 1.2;
font-weight: 800; font-weight: var(--fw-bold);
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
max-width: 30ch; max-width: 30ch;
} }
@ -743,7 +743,7 @@
.kx-mb-btn { .kx-mb-btn {
padding: 10px 20px; padding: 10px 20px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-weight: 700; font-weight: var(--fw-bold);
font-size: var(--fs-body); font-size: var(--fs-body);
cursor: default; cursor: default;
border: 1px solid transparent; border: 1px solid transparent;
@ -777,7 +777,7 @@
} }
.kx-mb-section__title { .kx-mb-section__title {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-mb-cards { .kx-mb-cards {
@ -808,15 +808,15 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
background: var(--color-primary-600); background: var(--color-primary-600);
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-mb-card__body { .kx-mb-card__body {
padding: var(--space-3); padding: var(--space-3);
border-left: 4px solid var(--color-primary-600); border-left: 4px solid var(--color-primary-600);
} }
.kx-mb-card__title { .kx-mb-card__title {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: 4px; margin-bottom: 4px;
} }
@ -868,8 +868,8 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
background: #e6f4ee; background: #e6f4ee;
color: var(--color-success); color: var(--color-success);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-mb-status__dot { .kx-mb-status__dot {
width: 6px; width: 6px;
@ -890,7 +890,7 @@
flex: 1; flex: 1;
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -917,11 +917,11 @@
} }
.kx-mb-activity__t { .kx-mb-activity__t {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-mb-activity__m { .kx-mb-activity__m {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -951,7 +951,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-i18n-covcard__pct { .kx-i18n-covcard__pct {
@ -992,45 +992,22 @@
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
} }
/* 매트릭스 테이블 */ /* 매트릭스 테이블 — 기본 그리드는 정본 .kx-table 로 수렴. 아래는 확장만 유지. */
.kx-i18n-table {
width: 100%;
border-collapse: collapse;
font-size: var(--fs-body);
}
.kx-i18n-table thead th {
text-align: left;
padding: 10px 12px;
font-size: var(--fs-caption);
font-weight: 600;
letter-spacing: 0.02em;
color: var(--color-neutral-500);
background: var(--color-neutral-050);
border-bottom: 2px solid var(--color-neutral-200);
white-space: nowrap;
vertical-align: bottom;
}
.kx-i18n-table tbody td { .kx-i18n-table tbody td {
padding: 10px 12px;
border-bottom: 1px solid var(--color-neutral-200);
color: var(--color-neutral-700);
vertical-align: top; vertical-align: top;
} }
.kx-i18n-table tbody tr { .kx-i18n-table tbody tr {
cursor: pointer; cursor: pointer;
} }
.kx-i18n-table tbody tr:hover {
background: var(--color-neutral-050);
}
.kx-i18n-table tbody tr.is-active { .kx-i18n-table tbody tr.is-active {
background: var(--color-primary-050); background: var(--color-primary-050);
} }
.kx-i18n-row__key { .kx-i18n-row__key {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-i18n-row__id { .kx-i18n-row__id {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-family: var(--font-mono); font-family: var(--font-mono);
} }
@ -1100,13 +1077,13 @@
background: var(--color-ai-surface); background: var(--color-ai-surface);
} }
.kx-i18n-tone__t { .kx-i18n-tone__t {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
margin-bottom: 4px; margin-bottom: 4px;
} }
.kx-i18n-tone__p { .kx-i18n-tone__p {
font-size: 11px; font-size: var(--fs-micro);
line-height: 1.5; line-height: 1.5;
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-style: italic; font-style: italic;
@ -1119,8 +1096,8 @@
/* 섹션 라벨(공통 소제목) */ /* 섹션 라벨(공통 소제목) */
.kx-cms-sublabel { .kx-cms-sublabel {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -1162,11 +1139,11 @@
background: color-mix(in srgb, var(--color-success) 10%, transparent); background: color-mix(in srgb, var(--color-success) 10%, transparent);
color: var(--color-success); color: var(--color-success);
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-cms-disabled-hint { .kx-cms-disabled-hint {
margin-left: auto; margin-left: auto;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
border: 1px dashed var(--color-neutral-300); border: 1px dashed var(--color-neutral-300);
border-radius: 6px; border-radius: 6px;

View File

@ -17,7 +17,7 @@
padding: var(--space-3) var(--space-4); padding: var(--space-3) var(--space-4);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-comp__banner.is-block { .kx-comp__banner.is-block {
background: #fef3f2; background: #fef3f2;
@ -30,7 +30,7 @@
border: 1px solid #a6e0c8; border: 1px solid #a6e0c8;
} }
.kx-comp__banner-note { .kx-comp__banner-note {
font-weight: 400; font-weight: var(--fw-regular);
} }
.kx-comp__drop { .kx-comp__drop {
@ -81,8 +81,8 @@
gap: var(--space-2); gap: var(--space-2);
} }
.kx-comp__lg { .kx-comp__lg {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -130,8 +130,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 12px; font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
flex-shrink: 0; flex-shrink: 0;
background: var(--color-error); background: var(--color-error);
color: var(--color-on-accent); color: var(--color-on-accent);
@ -156,12 +156,12 @@
} }
.kx-comp__item-label { .kx-comp__item-label {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-comp__sev { .kx-comp__sev {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -182,8 +182,8 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-comp__doc { .kx-comp__doc {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
background: var(--color-ai-surface); background: var(--color-ai-surface);
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
@ -231,8 +231,8 @@
position: absolute; position: absolute;
top: 12%; top: 12%;
right: 6%; right: 6%;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-error); background: var(--color-error);
padding: 3px 8px; padding: 3px 8px;

View File

@ -14,7 +14,7 @@
font-size: var(--fs-h1); font-size: var(--fs-h1);
line-height: var(--lh-h1); line-height: var(--lh-h1);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-cbd__subtitle { .kx-cbd__subtitle {
font-size: var(--fs-body); font-size: var(--fs-body);
@ -78,7 +78,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
@ -88,7 +88,7 @@
} }
.kx-booth__name { .kx-booth__name {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-booth__meta { .kx-booth__meta {
@ -112,7 +112,7 @@
} }
.kx-booth__right-value { .kx-booth__right-value {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
} }
/* 시공 스테퍼 */ /* 시공 스테퍼 */
@ -148,7 +148,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
background: var(--color-neutral-100); background: var(--color-neutral-100);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -168,7 +168,7 @@
.kx-step--done .kx-step__label, .kx-step--done .kx-step__label,
.kx-step--current .kx-step__label { .kx-step--current .kx-step__label {
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-booth__actions { .kx-booth__actions {
@ -220,7 +220,7 @@
} }
.kx-cbd-bid__name { .kx-cbd-bid__name {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-cbd-bid__price { .kx-cbd-bid__price {
@ -236,8 +236,8 @@
gap: 4px; gap: 4px;
} }
.kx-rankpill { .kx-rankpill {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }

View File

@ -29,7 +29,7 @@
background: var(--color-primary-600); background: var(--color-primary-600);
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
} }
/* 히어로 타임라인 */ /* 히어로 타임라인 */
@ -78,7 +78,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-neutral-200); background: var(--color-neutral-200);
} }
@ -113,7 +113,7 @@
} }
.kx-timeline__title { .kx-timeline__title {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-timeline__card { .kx-timeline__card {
@ -122,7 +122,7 @@
} }
.kx-timeline__imminent { .kx-timeline__imminent {
color: var(--color-violation-warn-text); color: var(--color-violation-warn-text);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-timeline__muted { .kx-timeline__muted {
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -150,7 +150,7 @@
.kx-kpi__value { .kx-kpi__value {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-kpi__sub { .kx-kpi__sub {
@ -162,7 +162,7 @@
} }
.kx-kpi--warn .kx-kpi__sub { .kx-kpi--warn .kx-kpi__sub {
color: var(--color-violation-warn-text); color: var(--color-violation-warn-text);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-kpi--error .kx-kpi__value { .kx-kpi--error .kx-kpi__value {
color: var(--color-error); color: var(--color-error);
@ -193,7 +193,7 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-dash__degraded { .kx-dash__degraded {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 2px 8px; padding: 2px 8px;
@ -222,7 +222,7 @@
text-align: left; text-align: left;
padding: 10px 12px; padding: 10px 12px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
letter-spacing: 0.02em; letter-spacing: 0.02em;
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-050); background: var(--color-neutral-050);

View File

@ -22,7 +22,7 @@
} }
.kx-studio__ctx h2 { .kx-studio__ctx h2 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-studio__ctx-sub { .kx-studio__ctx-sub {
@ -36,7 +36,7 @@
background: var(--color-white); background: var(--color-white);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-size: 16px; font-size: var(--fs-h3);
cursor: pointer; cursor: pointer;
} }
.kx-studio__back:hover { .kx-studio__back:hover {
@ -64,7 +64,7 @@
justify-content: center; justify-content: center;
background: var(--color-ai-surface); background: var(--color-ai-surface);
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-size: 20px; font-size: var(--fs-h2);
} }
.kx-studio__body { .kx-studio__body {
@ -100,7 +100,7 @@
border: none; border: none;
background: transparent; background: transparent;
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-acc__chevron { .kx-acc__chevron {
@ -159,7 +159,7 @@
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-studio__toggle--req { .kx-studio__toggle--req {
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-studio__seg { .kx-studio__seg {
display: inline-flex; display: inline-flex;
@ -173,7 +173,7 @@
background: transparent; background: transparent;
padding: 6px 18px; padding: 6px 18px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-studio__seg button.is-active { .kx-studio__seg button.is-active {
@ -208,7 +208,7 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-studio__tile-label { .kx-studio__tile-label {
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-studio__tile-empty button { .kx-studio__tile-empty button {
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
@ -217,7 +217,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 6px 14px; padding: 6px 14px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
/* Before/After */ /* Before/After */
@ -244,7 +244,7 @@
transform: rotate(-24deg); transform: rotate(-24deg);
color: rgba(255, 255, 255, 0.55); color: rgba(255, 255, 255, 0.55);
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
pointer-events: none; pointer-events: none;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
@ -255,7 +255,7 @@
left: var(--space-2); left: var(--space-2);
} }
.kx-studio__compare-notice { .kx-studio__compare-notice {
font-size: 11px; font-size: var(--fs-micro);
line-height: 15px; line-height: 15px;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -287,7 +287,7 @@
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-chip--pass { .kx-chip--pass {
background: #e6f4ee; background: #e6f4ee;

View File

@ -42,7 +42,7 @@
} }
.kx-doc__label { .kx-doc__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-doc__input, .kx-doc__input,
@ -153,14 +153,14 @@
} }
.kx-doc__step-label { .kx-doc__step-label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-doc__step.is-active .kx-doc__step-label { .kx-doc__step.is-active .kx-doc__step-label {
color: var(--color-warning); color: var(--color-warning);
} }
.kx-doc__step-sub { .kx-doc__step-sub {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -207,7 +207,7 @@
} }
.kx-doc__progress-num { .kx-doc__progress-num {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-doc__progress-track { .kx-doc__progress-track {
@ -254,7 +254,7 @@
color: var(--color-ai-accent); color: var(--color-ai-accent);
} }
.kx-doc__doc-name { .kx-doc__doc-name {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-size: var(--fs-body); font-size: var(--fs-body);
} }
@ -269,7 +269,7 @@
padding: 1px 10px; padding: 1px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
background: var(--color-neutral-100); background: var(--color-neutral-100);
color: var(--color-neutral-500); color: var(--color-neutral-500);
white-space: nowrap; white-space: nowrap;
@ -321,7 +321,7 @@
} }
.kx-doc__issue-title { .kx-doc__issue-title {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-doc__issue-desc { .kx-doc__issue-desc {
@ -340,7 +340,7 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
margin-bottom: 4px; margin-bottom: 4px;
} }
@ -366,7 +366,7 @@
.kx-doc__stat-value { .kx-doc__stat-value {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: 1; line-height: 1;
font-weight: 700; font-weight: var(--fw-bold);
margin: var(--space-2) 0; margin: var(--space-2) 0;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@ -474,7 +474,7 @@
.kx-rep__a4-title h2 { .kx-rep__a4-title h2 {
display: inline-block; display: inline-block;
font-size: 22px; font-size: 22px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.3em; letter-spacing: 0.3em;
border-bottom: 3px solid #141b2c; border-bottom: 3px solid #141b2c;
padding: 0 16px 6px; padding: 0 16px 6px;
@ -489,19 +489,19 @@
.kx-rep__table td { .kx-rep__table td {
border: 1px solid #141b2c; border: 1px solid #141b2c;
padding: 7px 9px; padding: 7px 9px;
font-size: 12px; font-size: var(--fs-caption);
text-align: left; text-align: left;
vertical-align: top; vertical-align: top;
} }
.kx-rep__table th { .kx-rep__table th {
background: #f2f4f7; background: #f2f4f7;
font-weight: 700; font-weight: var(--fw-bold);
width: 22%; width: 22%;
white-space: nowrap; white-space: nowrap;
} }
.kx-rep__a4 h4 { .kx-rep__a4 h4 {
font-size: 14px; font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
margin: 0 0 10px; margin: 0 0 10px;
} }
.kx-rep__a4 section { .kx-rep__a4 section {
@ -511,7 +511,7 @@
border: 1px solid #141b2c; border: 1px solid #141b2c;
padding: 12px; padding: 12px;
min-height: 90px; min-height: 90px;
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-rep__a4 ul { .kx-rep__a4 ul {
margin: 0; margin: 0;
@ -534,8 +534,8 @@
gap: 14px; gap: 14px;
} }
.kx-rep__sign-name { .kx-rep__sign-name {
font-size: 14px; font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-rep__stamp { .kx-rep__stamp {
width: 54px; width: 54px;
@ -543,8 +543,8 @@
border: 2px solid var(--color-error); border: 2px solid var(--color-error);
border-radius: 50%; border-radius: 50%;
color: var(--color-error); color: var(--color-error);
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -20,7 +20,7 @@
.kx-exh__company { .kx-exh__company {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -101,7 +101,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: var(--fw-bold);
border: 2px solid var(--color-neutral-200); border: 2px solid var(--color-neutral-200);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-white); background: var(--color-white);
@ -125,7 +125,7 @@
} }
.kx-stepper__label { .kx-stepper__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-stepper__item.is-current .kx-stepper__label { .kx-stepper__item.is-current .kx-stepper__label {
@ -135,7 +135,7 @@
position: absolute; position: absolute;
top: 62px; top: 62px;
white-space: nowrap; white-space: nowrap;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-primary-700); color: var(--color-primary-700);
background: var(--color-primary-050); background: var(--color-primary-050);
padding: 2px 8px; padding: 2px 8px;
@ -206,7 +206,7 @@
background: none; background: none;
color: var(--color-primary-600); color: var(--color-primary-600);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
padding: 0; padding: 0;
} }
@ -277,7 +277,7 @@
left: 0; left: 0;
right: 0; right: 0;
padding: var(--space-3); padding: var(--space-3);
font-size: 11px; font-size: var(--fs-micro);
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent); background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
} }
@ -290,7 +290,7 @@
transform: rotate(-24deg); transform: rotate(-24deg);
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
font-size: 15px; font-size: 15px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
pointer-events: none; pointer-events: none;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);

View File

@ -25,7 +25,7 @@
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-auto__sub { .kx-auto__sub {
@ -36,7 +36,7 @@
.kx-auto__close { .kx-auto__close {
background: none; background: none;
border: none; border: none;
font-size: 16px; font-size: var(--fs-h3);
color: var(--color-neutral-500); color: var(--color-neutral-500);
padding: 4px; padding: 4px;
} }
@ -96,7 +96,7 @@
} }
.kx-auto__card-title { .kx-auto__card-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-auto__metrics { .kx-auto__metrics {
@ -111,9 +111,9 @@
} }
.kx-auto__viol-ok { .kx-auto__viol-ok {
color: var(--color-success); color: var(--color-success);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-auto__viol-bad { .kx-auto__viol-bad {
color: var(--color-error); color: var(--color-error);
font-weight: 600; font-weight: var(--fw-semibold);
} }

View File

@ -24,7 +24,7 @@
} }
.kx-editor__hall { .kx-editor__hall {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-editor__version { .kx-editor__version {
@ -40,8 +40,8 @@
font-size: 8px; font-size: 8px;
} }
.kx-editor__degraded { .kx-editor__degraded {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 2px 8px; padding: 2px 8px;
@ -81,8 +81,8 @@
border-bottom: var(--border-card); border-bottom: var(--border-card);
} }
.kx-tool-group h3 { .kx-tool-group h3 {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: var(--color-neutral-500); color: var(--color-neutral-500);
@ -123,7 +123,7 @@
height: 28px; height: 28px;
} }
.kx-palette__label { .kx-palette__label {
font-size: 11px; font-size: var(--fs-micro);
text-align: center; text-align: center;
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
@ -247,7 +247,7 @@
.kx-editor__zoom span { .kx-editor__zoom span {
width: 44px; width: 44px;
text-align: center; text-align: center;
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
@ -264,7 +264,7 @@
border: none; border: none;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-editor__tabs button.is-active { .kx-editor__tabs button.is-active {
@ -298,7 +298,7 @@
margin: 0; margin: 0;
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 500; font-weight: var(--fw-medium);
} }
/* 검증 */ /* 검증 */
@ -319,7 +319,7 @@
padding: 4px 12px; padding: 4px 12px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-valid__chip--block { .kx-valid__chip--block {
background: #fef3f2; background: #fef3f2;
@ -332,7 +332,7 @@
.kx-valid__ok { .kx-valid__ok {
background: #e6f4ee; background: #e6f4ee;
color: var(--color-success); color: var(--color-success);
font-weight: 600; font-weight: var(--fw-semibold);
padding: var(--space-3); padding: var(--space-3);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-body); font-size: var(--fs-body);
@ -364,8 +364,8 @@
height: 18px; height: 18px;
border-radius: 50%; border-radius: 50%;
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -389,15 +389,15 @@
} }
.kx-valid__label { .kx-valid__label {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-valid__locate { .kx-valid__locate {
background: none; background: none;
border: none; border: none;
color: var(--color-primary-600); color: var(--color-primary-600);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
flex-shrink: 0; flex-shrink: 0;
} }
.kx-valid__measured { .kx-valid__measured {
@ -408,8 +408,8 @@
.kx-valid__doc { .kx-valid__doc {
display: inline-block; display: inline-block;
margin-top: var(--space-2); margin-top: var(--space-2);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
background: var(--color-ai-surface); background: var(--color-ai-surface);
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
@ -417,7 +417,7 @@
padding: 2px 8px; padding: 2px 8px;
} }
.kx-valid__disclaimer { .kx-valid__disclaimer {
font-size: 11px; font-size: var(--fs-micro);
line-height: 15px; line-height: 15px;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }

View File

@ -75,8 +75,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 14px; font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-cmp__wm { .kx-cmp__wm {
position: absolute; position: absolute;
@ -87,7 +87,7 @@
transform: rotate(-24deg); transform: rotate(-24deg);
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
pointer-events: none; pointer-events: none;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
@ -107,8 +107,8 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-cmp__reco { .kx-cmp__reco {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
background: var(--color-primary-100); background: var(--color-primary-100);
padding: 2px 8px; padding: 2px 8px;
@ -130,7 +130,7 @@
} }
.kx-cmp__metrics dd { .kx-cmp__metrics dd {
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-cmp__ok { .kx-cmp__ok {
color: var(--color-success); color: var(--color-success);

View File

@ -16,7 +16,7 @@
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-gallery__head { .kx-gallery__head {
@ -58,7 +58,7 @@
padding: 5px 12px; padding: 5px 12px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-gallery__chip.is-active { .kx-gallery__chip.is-active {
background: var(--color-primary-600); background: var(--color-primary-600);
@ -90,7 +90,7 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-gallery__status { .kx-gallery__status {
font-weight: 600; font-weight: var(--fw-semibold);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -151,7 +151,7 @@
} }
.kx-lightbox__notice { .kx-lightbox__notice {
margin-top: var(--space-3); margin-top: var(--space-3);
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }

View File

@ -57,7 +57,7 @@
gap: 2px; gap: 2px;
} }
.kx-hall__hallname { .kx-hall__hallname {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-size: var(--fs-body); font-size: var(--fs-body);
} }
@ -114,7 +114,7 @@
} }
.kx-hall__chiptext em { .kx-hall__chiptext em {
font-style: normal; font-style: normal;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-hall__chip.is-conflict .kx-hall__chiptext em { .kx-hall__chip.is-conflict .kx-hall__chiptext em {
@ -124,7 +124,7 @@
border: none; border: none;
background: transparent; background: transparent;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: var(--fs-h3);
line-height: 1; line-height: 1;
color: var(--color-neutral-400); color: var(--color-neutral-400);
padding: 0 2px; padding: 0 2px;
@ -188,7 +188,7 @@
color: var(--color-success); color: var(--color-success);
} }
.kx-hall__total td { .kx-hall__total td {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
border-top: 2px solid var(--color-neutral-200); border-top: 2px solid var(--color-neutral-200);
} }
@ -316,7 +316,7 @@
} }
.kx-hall__detailsec h3 { .kx-hall__detailsec h3 {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0; margin: 0;
} }

View File

@ -24,7 +24,7 @@
} }
.kx-home__section-title { .kx-home__section-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0; margin: 0;
} }
@ -53,7 +53,7 @@
} }
.kx-home__hello { .kx-home__hello {
font-size: var(--fs-h1); font-size: var(--fs-h1);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0 0 4px; margin: 0 0 4px;
} }
@ -108,7 +108,7 @@
} }
.kx-kpitile__value { .kx-kpitile__value {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
/* Stitch 정합: 임박/경과 타일 값 강조색 */ /* Stitch 정합: 임박/경과 타일 값 강조색 */
@ -171,7 +171,7 @@
} }
.kx-home__banner-title { .kx-home__banner-title {
font-size: var(--fs-h1); font-size: var(--fs-h1);
font-weight: 700; font-weight: var(--fw-bold);
margin: 0 0 6px; margin: 0 0 6px;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
} }
@ -295,7 +295,7 @@
.kx-poster__fallback-text { .kx-poster__fallback-text {
color: #fff; color: #fff;
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
text-align: center; text-align: center;
line-height: 1.35; line-height: 1.35;
} }
@ -316,8 +316,8 @@
border-left-color: var(--color-neutral-500); border-left-color: var(--color-neutral-500);
} }
.kx-poster__hall { .kx-poster__hall {
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-600); color: var(--color-primary-600);
margin-bottom: 1px; margin-bottom: 1px;
} }
@ -329,7 +329,7 @@
} }
.kx-poster__name { .kx-poster__name {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
@ -354,7 +354,7 @@
padding: 2px 8px; padding: 2px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-home__chip.is-ongoing { .kx-home__chip.is-ongoing {
background: var(--color-success-bg); background: var(--color-success-bg);
@ -405,7 +405,7 @@
} }
.kx-home__cal-title { .kx-home__cal-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
min-width: 120px; min-width: 120px;
text-align: center; text-align: center;
} }
@ -470,7 +470,7 @@
padding: 6px 0; padding: 6px 0;
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-home__cal-wd.is-sun { .kx-home__cal-wd.is-sun {
@ -528,11 +528,11 @@
color: #fff; color: #fff;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
padding: 1px 7px; padding: 1px 7px;
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-home__cal-more { .kx-home__cal-more {
margin-top: auto; margin-top: auto;
font-size: 10px; font-size: var(--fs-nano);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
/* spanning bar 레인 — 셀 위에 절대배치(요일 헤더 아래 24px 오프셋) */ /* spanning bar 레인 — 셀 위에 절대배치(요일 헤더 아래 24px 오프셋) */
@ -548,7 +548,7 @@
height: 18px; height: 18px;
line-height: 18px; line-height: 18px;
border-radius: 3px; border-radius: 3px;
font-size: 11px; font-size: var(--fs-micro);
padding: 0 6px; padding: 0 6px;
color: #fff; color: #fff;
white-space: nowrap; white-space: nowrap;
@ -578,7 +578,7 @@
} }
.kx-home__cal-list-title { .kx-home__cal-list-title {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0 0 var(--space-2); margin: 0 0 var(--space-2);
} }
@ -609,7 +609,7 @@
.kx-home__cal-list-name { .kx-home__cal-list-name {
font-size: var(--fs-body); font-size: var(--fs-body);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 500; font-weight: var(--fw-medium);
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
@ -701,7 +701,7 @@
} }
.kx-home__widget-head h2 { .kx-home__widget-head h2 {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0; margin: 0;
} }
@ -713,7 +713,7 @@
height: 20px; height: 20px;
padding: 0 6px; padding: 0 6px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -767,7 +767,7 @@
.kx-home__todo-title { .kx-home__todo-title {
font-size: var(--fs-body); font-size: var(--fs-body);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 500; font-weight: var(--fw-medium);
} }
.kx-home__todo-target { .kx-home__todo-target {
font-size: var(--fs-caption); font-size: var(--fs-caption);
@ -778,7 +778,7 @@
} }
.kx-home__todo-due { .kx-home__todo-due {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
flex-shrink: 0; flex-shrink: 0;
} }
.kx-home__todo-due.is-normal { .kx-home__todo-due.is-normal {
@ -802,7 +802,7 @@
color: var(--color-success); color: var(--color-success);
} }
.kx-home__todo-done-title { .kx-home__todo-done-title {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0; margin: 0;
} }
@ -850,8 +850,8 @@
.kx-home__new { .kx-home__new {
background: var(--color-error); background: var(--color-error);
color: #fff; color: #fff;
font-size: 10px; font-size: var(--fs-nano);
font-weight: 700; font-weight: var(--fw-bold);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 1px 5px; padding: 1px 5px;
} }
@ -951,7 +951,7 @@
} }
.kx-home__ws-card-title { .kx-home__ws-card-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 4px 0 0; margin: 4px 0 0;
} }
@ -969,7 +969,7 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
.kx-home__ws-empty-title { .kx-home__ws-empty-title {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: 0 0 4px; margin: 0 0 4px;
} }
@ -1051,7 +1051,7 @@
} }
.kx-home__chart-cap { .kx-home__chart-cap {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -1084,7 +1084,7 @@
} }
.kx-home__chart-legend-item b { .kx-home__chart-legend-item b {
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 700; font-weight: var(--fw-bold);
} }
@media (max-width: 1100px) { @media (max-width: 1100px) {
@ -1145,7 +1145,7 @@
gap: var(--space-2); gap: var(--space-2);
margin: 0; margin: 0;
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 800; font-weight: var(--fw-bold);
color: var(--color-ai-accent-strong); color: var(--color-ai-accent-strong);
letter-spacing: -0.01em; letter-spacing: -0.01em;
} }
@ -1227,14 +1227,14 @@
align-items: center; align-items: center;
gap: var(--space-1); gap: var(--space-1);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
line-height: 1.25; line-height: 1.25;
} }
.kx-home__aihub-card-tag { .kx-home__aihub-card-tag {
flex: 0 0 auto; flex: 0 0 auto;
font-size: 10px; font-size: var(--fs-nano);
font-weight: 800; font-weight: var(--fw-bold);
letter-spacing: 0.04em; letter-spacing: 0.04em;
padding: 1px 5px; padding: 1px 5px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);

View File

@ -99,14 +99,14 @@
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
color: #d3c7ff; color: #d3c7ff;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
} }
.kx-login__headline { .kx-login__headline {
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: -0.01em; letter-spacing: -0.01em;
} }
@media (min-width: 1280px) { @media (min-width: 1280px) {
@ -153,12 +153,12 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: var(--fw-bold);
font-size: 18px; font-size: 18px;
} }
.kx-login__logo-text { .kx-login__logo-text {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-login__logo-img { .kx-login__logo-img {
@ -168,7 +168,7 @@
} }
.kx-login__logo-suffix { .kx-login__logo-suffix {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
padding-left: 2px; padding-left: 2px;
border-left: 1px solid var(--color-neutral-200); border-left: 1px solid var(--color-neutral-200);
@ -185,7 +185,7 @@
.kx-login__title { .kx-login__title {
font-size: var(--fs-h1); font-size: var(--fs-h1);
line-height: var(--lh-h1); line-height: var(--lh-h1);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
text-align: center; text-align: center;
} }
@ -224,7 +224,7 @@
border: none; border: none;
color: var(--color-primary-600); color: var(--color-primary-600);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 500; font-weight: var(--fw-medium);
text-align: center; text-align: center;
} }
.kx-login__invite-link:hover { .kx-login__invite-link:hover {
@ -259,7 +259,7 @@
padding: 0; padding: 0;
color: var(--color-primary-600); color: var(--color-primary-600);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
cursor: pointer; cursor: pointer;
} }
.kx-login__text-link:hover { .kx-login__text-link:hover {
@ -308,7 +308,7 @@
background: var(--color-success, #16a34a); background: var(--color-success, #16a34a);
} }
.kx-pw__label { .kx-pw__label {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-pw__label--error { .kx-pw__label--error {
@ -335,7 +335,7 @@
position: absolute; position: absolute;
bottom: var(--space-5); bottom: var(--space-5);
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-size: 11px; font-size: var(--fs-micro);
} }
/* ── 공통 필드 ── */ /* ── 공통 필드 ── */
@ -346,7 +346,7 @@
} }
.kx-field label { .kx-field label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-field input { .kx-field input {
@ -383,7 +383,7 @@
.kx-ws__header h1 { .kx-ws__header h1 {
font-size: var(--fs-h1); font-size: var(--fs-h1);
line-height: var(--lh-h1); line-height: var(--lh-h1);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-ws__header p { .kx-ws__header p {
@ -419,7 +419,7 @@
.kx-ws__card-title { .kx-ws__card-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
line-height: var(--lh-h3); line-height: var(--lh-h3);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-ws__card-meta { .kx-ws__card-meta {
@ -464,7 +464,7 @@
.kx-modal__title { .kx-modal__title {
font-size: var(--fs-h2); font-size: var(--fs-h2);
line-height: var(--lh-h2); line-height: var(--lh-h2);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -494,7 +494,7 @@
} }
.kx-modal__blocked h3 { .kx-modal__blocked h3 {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-error); color: var(--color-error);
} }
.kx-modal__blocked p { .kx-modal__blocked p {
@ -567,7 +567,7 @@
.kx-otp__code-input { .kx-otp__code-input {
letter-spacing: 6px; letter-spacing: 6px;
text-align: center; text-align: center;
font-size: 20px; font-size: var(--fs-h2);
} }
.kx-otp__ok { .kx-otp__ok {
font-size: var(--fs-body); font-size: var(--fs-body);

View File

@ -38,8 +38,8 @@
/* 샘플 데이터 배지 */ /* 샘플 데이터 배지 */
.kx-sample { .kx-sample {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
border: 1px solid #fcd9a8; border: 1px solid #fcd9a8;
@ -50,8 +50,8 @@
/* 오프라인 강등(폴백) 표시 — 집계 API 미연결 시 시연 데이터 안내 */ /* 오프라인 강등(폴백) 표시 — 집계 API 미연결 시 시연 데이터 안내 */
.kx-vis__degraded { .kx-vis__degraded {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
border: 1px solid #fcd9a8; border: 1px solid #fcd9a8;
@ -72,7 +72,7 @@
align-items: center; align-items: center;
gap: 4px; gap: 4px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
background: var(--color-neutral-100); background: var(--color-neutral-100);
border: 1px solid transparent; border: 1px solid transparent;
@ -93,7 +93,7 @@
} }
.kx-kpi__unit { .kx-kpi__unit {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 600; font-weight: var(--fw-semibold);
margin-left: 3px; margin-left: 3px;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -140,7 +140,7 @@
border: none; border: none;
background: none; background: none;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-primary-600); color: var(--color-primary-600);
cursor: pointer; cursor: pointer;
} }
@ -207,7 +207,7 @@
} }
.kx-mkt__campaign-top h3 { .kx-mkt__campaign-top h3 {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -248,8 +248,8 @@
/* 캠페인 상태 pill */ /* 캠페인 상태 pill */
.kx-mkt__cpill { .kx-mkt__cpill {
flex-shrink: 0; flex-shrink: 0;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 2px 8px; padding: 2px 8px;
border-radius: 4px; border-radius: 4px;
} }
@ -277,7 +277,7 @@
} }
.kx-mkt__field-label { .kx-mkt__field-label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin: var(--space-4) 0 var(--space-2); margin: var(--space-4) 0 var(--space-2);
} }
@ -323,7 +323,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -385,8 +385,8 @@
.kx-mkt__preview-head { .kx-mkt__preview-head {
padding: var(--space-2); padding: var(--space-2);
text-align: center; text-align: center;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-050); background: var(--color-neutral-050);
border-bottom: 1px solid var(--color-neutral-200); border-bottom: 1px solid var(--color-neutral-200);
@ -409,7 +409,7 @@
} }
.kx-mkt__preview-title { .kx-mkt__preview-title {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -422,7 +422,7 @@
margin-top: var(--space-4); margin-top: var(--space-4);
padding-top: var(--space-3); padding-top: var(--space-3);
border-top: 1px solid var(--color-neutral-100); border-top: 1px solid var(--color-neutral-100);
font-size: 10px; font-size: var(--fs-nano);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-mkt__preview-links { .kx-mkt__preview-links {
@ -463,8 +463,8 @@
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
} }
.kx-mkt__tier-badge { .kx-mkt__tier-badge {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.08em; letter-spacing: 0.08em;
color: var(--tier-accent, var(--color-primary-700)); color: var(--tier-accent, var(--color-primary-700));
background: color-mix(in srgb, var(--tier-accent, #0066b3) 12%, white); background: color-mix(in srgb, var(--tier-accent, #0066b3) 12%, white);
@ -478,7 +478,7 @@
} }
.kx-mkt__tier-price { .kx-mkt__tier-price {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 800; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
margin-bottom: var(--space-4); margin-bottom: var(--space-4);
} }
@ -511,7 +511,7 @@
} }
.kx-mkt__tier-remain { .kx-mkt__tier-remain {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-mkt__tier-remain strong { .kx-mkt__tier-remain strong {
@ -598,7 +598,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 700; font-weight: var(--fw-bold);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -619,8 +619,8 @@
} }
.kx-mkt__spill { .kx-mkt__spill {
flex-shrink: 0; flex-shrink: 0;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 2px 8px; padding: 2px 8px;
border-radius: 4px; border-radius: 4px;
} }
@ -678,7 +678,7 @@
background: var(--color-primary-050); background: var(--color-primary-050);
color: var(--color-primary-700); color: var(--color-primary-700);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
cursor: pointer; cursor: pointer;
} }
.kx-mkt__roi-link:hover { .kx-mkt__roi-link:hover {
@ -714,5 +714,5 @@
background: var(--kx-success-bg, #ecfdf5); background: var(--kx-success-bg, #ecfdf5);
color: var(--kx-success-fg, #065f46); color: var(--kx-success-fg, #065f46);
border: 1px solid var(--kx-success-border, #a7f3d0); border: 1px solid var(--kx-success-border, #a7f3d0);
font-size: 14px; font-size: var(--fs-body);
} }

View File

@ -66,10 +66,10 @@
} }
.kx-m__accent { .kx-m__accent {
color: var(--color-primary-600); color: var(--color-primary-600);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-m__offline { .kx-m__offline {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-100); background: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
@ -99,7 +99,7 @@
padding: 7px; padding: 7px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
} }
@ -125,7 +125,7 @@
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: var(--space-3); padding: var(--space-3);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
@ -149,7 +149,7 @@
} }
.kx-m__acc-head span:first-child { .kx-m__acc-head span:first-child {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-m__acc-count { .kx-m__acc-count {
@ -209,7 +209,7 @@
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
background: var(--color-neutral-100); background: var(--color-neutral-100);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
font-size: 20px; font-size: var(--fs-h2);
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
} }
@ -222,7 +222,7 @@
} }
.kx-m__warn h3 { .kx-m__warn h3 {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-error); color: var(--color-error);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -253,7 +253,7 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-m__rule { .kx-m__rule {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-100); background: var(--color-neutral-100);
padding: 2px 8px; padding: 2px 8px;
@ -269,7 +269,7 @@
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 600; font-weight: var(--fw-semibold);
background: var(--color-white); background: var(--color-white);
} }
.kx-m__unit { .kx-m__unit {
@ -283,7 +283,7 @@
.kx-m__field-err { .kx-m__field-err {
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-error); color: var(--color-error);
font-weight: 600; font-weight: var(--fw-semibold);
} }
/* 하단 */ /* 하단 */
@ -302,7 +302,7 @@
} }
.kx-m__foot-note { .kx-m__foot-note {
text-align: center; text-align: center;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -314,7 +314,7 @@
background: var(--color-primary-600); background: var(--color-primary-600);
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
cursor: pointer; cursor: pointer;
} }
.kx-m__cta:disabled { .kx-m__cta:disabled {
@ -353,12 +353,12 @@
} }
.kx-m__compare-head h2 { .kx-m__compare-head h2 {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-m__flag { .kx-m__flag {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
background: var(--color-ai-surface); background: var(--color-ai-surface);
border: 1px solid var(--color-ai-accent); border: 1px solid var(--color-ai-accent);
@ -388,7 +388,7 @@
top: 6px; top: 6px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 10px; font-size: var(--fs-nano);
color: var(--color-neutral-700); color: var(--color-neutral-700);
background: rgba(255, 255, 255, 0.85); background: rgba(255, 255, 255, 0.85);
padding: 2px 6px; padding: 2px 6px;
@ -399,7 +399,7 @@
.kx-m__compare-div { .kx-m__compare-div {
align-self: center; align-self: center;
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-size: 14px; font-size: var(--fs-body);
} }
.kx-m__dots { .kx-m__dots {
display: flex; display: flex;
@ -426,7 +426,7 @@
} }
.kx-m__ilist h3 { .kx-m__ilist h3 {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
padding: 0 var(--space-1); padding: 0 var(--space-1);
} }
@ -458,9 +458,9 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-m__icard-label small { .kx-m__icard-label small {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-error); color: var(--color-error);
font-weight: 700; font-weight: var(--fw-bold);
margin-top: 2px; margin-top: 2px;
} }
.kx-m__verdict { .kx-m__verdict {
@ -477,7 +477,7 @@
padding: 6px 12px; padding: 6px 12px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
} }

View File

@ -43,7 +43,7 @@
} }
.kx-dock__label { .kx-dock__label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-dock__filters-spacer { .kx-dock__filters-spacer {
@ -86,7 +86,7 @@
padding: 10px; padding: 10px;
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
border-right: 1px solid var(--color-neutral-100); border-right: 1px solid var(--color-neutral-100);
display: flex; display: flex;
@ -101,8 +101,8 @@
.kx-dock__time { .kx-dock__time {
padding: 10px 2px; padding: 10px 2px;
text-align: center; text-align: center;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
border-right: 1px solid var(--color-neutral-100); border-right: 1px solid var(--color-neutral-100);
} }
@ -122,7 +122,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
border-right: 1px solid var(--color-neutral-100); border-right: 1px solid var(--color-neutral-100);
background: var(--color-neutral-050); background: var(--color-neutral-050);
@ -150,7 +150,7 @@
margin: 4px 2px; margin: 4px 2px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 6px 8px; padding: 6px 8px;
font-size: 11px; font-size: var(--fs-micro);
line-height: 1.25; line-height: 1.25;
display: flex; display: flex;
align-items: center; align-items: center;
@ -164,7 +164,7 @@
.kx-dock__block--priority { .kx-dock__block--priority {
background: var(--color-violation-warn-bg); background: var(--color-violation-warn-bg);
color: var(--color-violation-warn-text); color: var(--color-violation-warn-text);
font-weight: 700; font-weight: var(--fw-bold);
border: 1px solid var(--color-violation-warn); border: 1px solid var(--color-violation-warn);
} }
.kx-dock__block-text { .kx-dock__block-text {
@ -280,11 +280,11 @@
} }
.kx-dock__qr-title { .kx-dock__qr-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
display: block; display: block;
} }
.kx-dock__qr-sub { .kx-dock__qr-sub {
font-size: 11px; font-size: var(--fs-micro);
opacity: 0.85; opacity: 0.85;
text-transform: uppercase; text-transform: uppercase;
} }
@ -338,7 +338,7 @@
.kx-dock__chart-axis { .kx-dock__chart-axis {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 10px; font-size: var(--fs-nano);
color: var(--color-neutral-500); color: var(--color-neutral-500);
padding: 0 2px; padding: 0 2px;
} }
@ -425,7 +425,7 @@
gap: 10px; gap: 10px;
} }
.kx-logi__item-name { .kx-logi__item-name {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-text, #101828); color: var(--color-text, #101828);
} }
.kx-logi__item-meta { .kx-logi__item-meta {
@ -464,8 +464,8 @@
color: #b42318; color: #b42318;
} }
.kx-logi__pill { .kx-logi__pill {
font-size: 12px; font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 2px 10px; padding: 2px 10px;
border-radius: 999px; border-radius: 999px;
white-space: nowrap; white-space: nowrap;

View File

@ -57,7 +57,7 @@
top: var(--space-4); top: var(--space-4);
left: var(--space-4); left: var(--space-4);
z-index: 2; z-index: 2;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 3px 10px; padding: 3px 10px;
@ -76,7 +76,7 @@
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
.kx-ops__degraded-badge { .kx-ops__degraded-badge {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
padding: 2px 8px; padding: 2px 8px;
@ -117,15 +117,15 @@
box-shadow: inset 0 0 0 1px rgba(217, 45, 32, 0.25); box-shadow: inset 0 0 0 1px rgba(217, 45, 32, 0.25);
} }
.kx-ops__hall-label { .kx-ops__hall-label {
font-weight: 700; font-weight: var(--fw-bold);
font-size: var(--fs-h3); font-size: var(--fs-h3);
} }
.kx-ops__hall-tag { .kx-ops__hall-tag {
position: absolute; position: absolute;
top: var(--space-3); top: var(--space-3);
right: var(--space-3); right: var(--space-3);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-error); background: var(--color-error);
padding: 2px 8px; padding: 2px 8px;
@ -133,7 +133,7 @@
} }
.kx-ops__hall-people { .kx-ops__hall-people {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
@ -233,7 +233,7 @@
} }
.kx-ops__pill { .kx-ops__pill {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -294,7 +294,7 @@
/* 주차 */ /* 주차 */
.kx-ops__note { .kx-ops__note {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-ops__zones { .kx-ops__zones {
@ -318,12 +318,12 @@
} }
.kx-ops__zone-label { .kx-ops__zone-label {
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-ops__zone-pct { .kx-ops__zone-pct {
font-size: var(--fs-h2); font-size: var(--fs-h2);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-ops__zone-pct.is-success { color: var(--color-success); } .kx-ops__zone-pct.is-success { color: var(--color-success); }
.kx-ops__zone-pct.is-warning { color: var(--color-warning); } .kx-ops__zone-pct.is-warning { color: var(--color-warning); }

View File

@ -743,7 +743,8 @@
} }
.kxp-ecard__media { .kxp-ecard__media {
position: relative; position: relative;
aspect-ratio: 16 / 11; /* 전시 포스터는 세로형(실측 0.67~0.8) — 세로 프레임으로 맞춰 왜곡/과다크롭 방지. */
aspect-ratio: 3 / 4;
background: var(--color-neutral-100); background: var(--color-neutral-100);
overflow: hidden; overflow: hidden;
} }
@ -4186,6 +4187,10 @@ a.kxp-ai__cite:hover {
.kxp-ecard__poster { .kxp-ecard__poster {
position: absolute; position: absolute;
inset: 0; inset: 0;
width: 100%;
height: 100%;
/* 늘어남(distortion) 방지 — 비율 유지하며 프레임을 채운다. */
object-fit: cover;
} }
.kxp-ecard__posterfallback { .kxp-ecard__posterfallback {
position: absolute; position: absolute;

View File

@ -38,7 +38,7 @@
} }
.kx-cv-title { .kx-cv-title {
font-size: var(--fs-h3); font-size: var(--fs-h3);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
min-width: 160px; min-width: 160px;
text-align: center; text-align: center;
@ -119,7 +119,7 @@
padding: var(--space-2); padding: var(--space-2);
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-050); background: var(--color-neutral-050);
border-bottom: 1px solid var(--color-neutral-200); border-bottom: 1px solid var(--color-neutral-200);
@ -170,14 +170,14 @@
/* 다른 달 날짜 숫자 — 흐린 회색(muted). */ /* 다른 달 날짜 숫자 — 흐린 회색(muted). */
.kx-cv-cell.is-out .kx-cv-daynum { .kx-cv-cell.is-out .kx-cv-daynum {
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-weight: 500; font-weight: var(--fw-medium);
} }
/* 오늘 — 진한 아웃라인/알약 제거 → Nifty식 옅은 노랑 셀 틴트 + 숫자 강조. */ /* 오늘 — 진한 아웃라인/알약 제거 → Nifty식 옅은 노랑 셀 틴트 + 숫자 강조. */
.kx-cv-cell.is-today { .kx-cv-cell.is-today {
background: var(--kx-cv-today-bg); background: var(--kx-cv-today-bg);
} }
.kx-cv-cell.is-today .kx-cv-daynum { .kx-cv-cell.is-today .kx-cv-daynum {
font-weight: 800; font-weight: var(--fw-bold);
} }
.kx-cv-cell.is-selected { .kx-cv-cell.is-selected {
background: var(--color-primary-050); background: var(--color-primary-050);
@ -188,7 +188,7 @@
left: var(--space-2); left: var(--space-2);
height: var(--kx-cv-daynum-h); height: var(--kx-cv-daynum-h);
line-height: var(--kx-cv-daynum-h); line-height: var(--kx-cv-daynum-h);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
z-index: 2; z-index: 2;
} }
@ -207,8 +207,8 @@
display: inline-block; display: inline-block;
margin-left: 4px; margin-left: 4px;
font-style: normal; font-style: normal;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-error); color: var(--color-error);
max-width: 72px; max-width: 72px;
overflow: hidden; overflow: hidden;
@ -257,7 +257,7 @@
background: color-mix(in srgb, var(--kx-bar-color) 14%, var(--color-white)); background: color-mix(in srgb, var(--kx-bar-color) 14%, var(--color-white));
color: color-mix(in srgb, var(--kx-bar-color) 72%, var(--color-neutral-900)); color: color-mix(in srgb, var(--kx-bar-color) 72%, var(--color-neutral-900));
border-left: 3px solid var(--kx-bar-color); border-left: 3px solid var(--kx-bar-color);
font-size: 11px; font-size: var(--fs-micro);
line-height: var(--kx-cv-bar-h); line-height: var(--kx-cv-bar-h);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -295,7 +295,7 @@
padding: 0 4px; padding: 0 4px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--kx-bar-color, var(--color-primary-600)) 22%, transparent); background: color-mix(in srgb, var(--kx-bar-color, var(--color-primary-600)) 22%, transparent);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-cv-bar:hover .kx-cv-bar-badge { .kx-cv-bar:hover .kx-cv-bar-badge {
background: rgba(255, 255, 255, 0.25); background: rgba(255, 255, 255, 0.25);
@ -312,8 +312,8 @@
padding: 0 4px; padding: 0 4px;
border: none; border: none;
background: transparent; background: transparent;
font-size: 10px; font-size: var(--fs-nano);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@ -382,7 +382,7 @@
} }
.kx-cv-day-head { .kx-cv-day-head {
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-cv-day-list { .kx-cv-day-list {
@ -405,7 +405,7 @@
background: color-mix(in srgb, var(--kx-bar-color) 14%, var(--color-white)); background: color-mix(in srgb, var(--kx-bar-color) 14%, var(--color-white));
color: color-mix(in srgb, var(--kx-bar-color) 72%, var(--color-neutral-900)); color: color-mix(in srgb, var(--kx-bar-color) 72%, var(--color-neutral-900));
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: var(--fs-micro);
line-height: 1.4; line-height: 1.4;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -461,8 +461,8 @@
flex: 0 0 auto; flex: 0 0 auto;
padding: 1px 6px; padding: 1px 6px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
margin-right: 4px; margin-right: 4px;
background: var(--color-neutral-100); background: var(--color-neutral-100);
color: var(--color-neutral-700); color: var(--color-neutral-700);
@ -541,11 +541,11 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-cv-more-item-range { .kx-cv-more-item-range {
flex: 0 0 auto; flex: 0 0 auto;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -562,7 +562,7 @@
min-height: 120px; min-height: 120px;
} }
.kx-cv-bar-title { .kx-cv-bar-title {
font-size: 10px; font-size: var(--fs-nano);
} }
/* 좁은 화면: 툴바 제목 고정폭 해제(가로 오버플로 방지). */ /* 좁은 화면: 툴바 제목 고정폭 해제(가로 오버플로 방지). */
.kx-cv-title { .kx-cv-title {
@ -572,6 +572,6 @@
} }
.kx-cv-dow { .kx-cv-dow {
padding: var(--space-1); padding: var(--space-1);
font-size: 11px; font-size: var(--fs-micro);
} }
} }

View File

@ -125,7 +125,7 @@
/* 행사 상태 pill */ /* 행사 상태 pill */
.kx-evstatus { .kx-evstatus {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
white-space: nowrap; white-space: nowrap;
@ -159,7 +159,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: var(--color-white); background: var(--color-white);
color: var(--color-neutral-700); color: var(--color-neutral-700);
font-size: 16px; font-size: var(--fs-h3);
cursor: pointer; cursor: pointer;
} }
.kx-cal__nav:hover { .kx-cal__nav:hover {
@ -172,7 +172,7 @@
} }
.kx-cal__wd { .kx-cal__wd {
text-align: center; text-align: center;
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
padding-bottom: 4px; padding-bottom: 4px;
} }
@ -193,7 +193,7 @@
.kx-cal__cell.has-event { .kx-cal__cell.has-event {
background: var(--color-primary-050); background: var(--color-primary-050);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-cal__cell.has-event::after { .kx-cal__cell.has-event::after {
content: ''; content: '';
@ -206,13 +206,13 @@
} }
.kx-cal__cell.is-holiday { .kx-cal__cell.is-holiday {
color: var(--color-error); color: var(--color-error);
font-weight: 700; font-weight: var(--fw-bold);
} }
/* today — 진남색 알약 제거 → Nifty식 옅은 노랑 틴트 + 진한 숫자(회색 테두리 유지). */ /* today — 진남색 알약 제거 → Nifty식 옅은 노랑 틴트 + 진한 숫자(회색 테두리 유지). */
.kx-cal__cell.is-today { .kx-cal__cell.is-today {
background: var(--color-warning-bg); background: var(--color-warning-bg);
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 700; font-weight: var(--fw-bold);
} }
.kx-cal__cell.is-today.has-event::after { .kx-cal__cell.is-today.has-event::after {
background: var(--color-primary-600); background: var(--color-primary-600);
@ -263,7 +263,7 @@
.kx-mcal__dow { .kx-mcal__dow {
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
padding: 6px 0; padding: 6px 0;
background: var(--color-neutral-050); background: var(--color-neutral-050);
@ -299,11 +299,11 @@
color: var(--color-primary-600); color: var(--color-primary-600);
} }
.kx-mcal__cell.is-today .kx-mcal__daynum { .kx-mcal__cell.is-today .kx-mcal__daynum {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-mcal__chip { .kx-mcal__chip {
font-size: 11px; font-size: var(--fs-micro);
line-height: 1.35; line-height: 1.35;
padding: 1px 6px; padding: 1px 6px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@ -329,7 +329,7 @@
color: #1f7a54; color: #1f7a54;
} }
.kx-mcal__more { .kx-mcal__more {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -363,15 +363,15 @@
grid-template-columns: 1fr auto auto; grid-template-columns: 1fr auto auto;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-availlist__label { color: var(--kx-text, #0f172a); } .kx-availlist__label { color: var(--kx-text, #0f172a); }
.kx-availlist__meta { color: var(--kx-text-muted, #64748b); } .kx-availlist__meta { color: var(--kx-text-muted, #64748b); }
.kx-availbadge { .kx-availbadge {
font-size: 11px; font-size: var(--fs-micro);
padding: 2px 8px; padding: 2px 8px;
border-radius: 999px; border-radius: 999px;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-availbadge.is-open { color: #15803d; background: #f0fdf4; } .kx-availbadge.is-open { color: #15803d; background: #f0fdf4; }
.kx-availbadge.is-full { color: #b45309; background: #fffbeb; } .kx-availbadge.is-full { color: #b45309; background: #fffbeb; }

View File

@ -18,8 +18,9 @@
} }
.kx-settle__title { .kx-settle__title {
margin: 0; margin: 0;
font-size: 22px; font-size: var(--fs-h1);
font-weight: 700; line-height: var(--lh-h1);
font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-settle__subtitle { .kx-settle__subtitle {
@ -63,7 +64,7 @@
background: var(--color-white); background: var(--color-white);
} }
.kx-settle__kpi-sub { .kx-settle__kpi-sub {
font-size: 12px; font-size: var(--fs-caption);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-settle__kpi--info { .kx-settle__kpi--info {
@ -115,7 +116,7 @@
} }
.kx-settle__paynote { .kx-settle__paynote {
margin: 12px 0 0; margin: 12px 0 0;
font-size: 12px; font-size: var(--fs-caption);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -161,8 +162,8 @@
display: inline-block; display: inline-block;
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: 12px; font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
line-height: 18px; line-height: 18px;
} }
.kx-settle__pill--muted { .kx-settle__pill--muted {
@ -210,12 +211,12 @@
} }
.kx-settle__modal-head h2 { .kx-settle__modal-head h2 {
margin: 0; margin: 0;
font-size: 16px; font-size: var(--fs-h3);
} }
.kx-settle__close { .kx-settle__close {
border: none; border: none;
background: none; background: none;
font-size: 16px; font-size: var(--fs-h3);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
} }
@ -235,13 +236,13 @@
gap: 4px; gap: 4px;
} }
.kx-settle__meta-k { .kx-settle__meta-k {
font-size: 12px; font-size: var(--fs-caption);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-settle__meta-v { .kx-settle__meta-v {
font-size: 14px; font-size: var(--fs-body);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-settle__lines { .kx-settle__lines {
@ -252,14 +253,14 @@
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-settle__total-row td { .kx-settle__total-row td {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
border-top: 1px solid var(--color-neutral-200); border-top: 1px solid var(--color-neutral-200);
} }
.kx-settle__cat { .kx-settle__cat {
display: inline-block; display: inline-block;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-primary-700); color: var(--color-primary-700);
background: var(--color-primary-050); background: var(--color-primary-050);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@ -269,7 +270,7 @@
.kx-settle__pay-hist h3 { .kx-settle__pay-hist h3 {
margin: 0 0 8px; margin: 0 0 8px;
font-size: 14px; font-size: var(--fs-body);
} }
.kx-settle__pay-hist ul { .kx-settle__pay-hist ul {
list-style: none; list-style: none;
@ -289,7 +290,7 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
} }
.kx-settle__pay-method { .kx-settle__pay-method {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@ -328,11 +329,11 @@
} }
.kx-settle__form-error { .kx-settle__form-error {
margin: 0; margin: 0;
font-size: 12px; font-size: var(--fs-caption);
color: var(--color-error); color: var(--color-error);
} }
.kx-settle__muted { .kx-settle__muted {
font-size: 12px; font-size: var(--fs-caption);
color: var(--color-neutral-500); color: var(--color-neutral-500);
text-align: left; text-align: left;
} }
@ -343,8 +344,8 @@
margin-left: 6px; margin-left: 6px;
padding: 1px 7px; padding: 1px 7px;
border-radius: 999px; border-radius: 999px;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
vertical-align: middle; vertical-align: middle;
white-space: nowrap; white-space: nowrap;
} }
@ -368,7 +369,7 @@
.kx-settle__tax h3 { .kx-settle__tax h3 {
margin: 0; margin: 0;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-settle__tax-doc { .kx-settle__tax-doc {
display: grid; display: grid;
@ -381,14 +382,14 @@
.kx-settle__tax-row { .kx-settle__tax-row {
display: flex; display: flex;
gap: 10px; gap: 10px;
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-settle__tax-amounts { .kx-settle__tax-amounts {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 14px; gap: 14px;
margin-top: 4px; margin-top: 4px;
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-settle__cancel { .kx-settle__cancel {
margin-top: 12px; margin-top: 12px;
@ -404,7 +405,7 @@
.kx-settle__cancel-box h4 { .kx-settle__cancel-box h4 {
margin: 0; margin: 0;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-settle__refund { .kx-settle__refund {
display: grid; display: grid;
@ -414,7 +415,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 12px; gap: 12px;
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-settle__refund-row--em { .kx-settle__refund-row--em {
padding-top: 4px; padding-top: 4px;
@ -455,5 +456,5 @@
.kx-settle__report-group h3 { .kx-settle__report-group h3 {
margin: 0 0 6px; margin: 0 0 6px;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: var(--fw-semibold);
} }

View File

@ -14,13 +14,23 @@
gap: var(--space-5); gap: var(--space-5);
} }
/* 카드 */ /*
* 카드 정본 Nifty 카드 계보
* 라운딩 8px · 1px 중립 테두리 · subtle 그림자 · hover elevation.
* 각진/미클립 카드 0 목표: 미디어 카드는 .kx-card--flush + __media 모서리 클립.
* */
.kx-card { .kx-card {
border: var(--border-card); border: var(--border-card);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
background: var(--color-white); background: var(--color-white);
padding: var(--space-4); padding: var(--space-4);
box-shadow: var(--shadow-card);
transition: box-shadow 0.15s ease, border-color 0.15s ease;
} }
.kx-card:hover {
box-shadow: var(--shadow-level2);
}
/* 헤더 행(현행) — 제목 + 우측 액션 */
.kx-card__head { .kx-card__head {
display: flex; display: flex;
align-items: center; align-items: center;
@ -38,6 +48,75 @@
margin: -4px 0 var(--space-3); margin: -4px 0 var(--space-3);
} }
/* Nifty 구조형 카드 (헤더/바디/푸터·미디어 클립)
* .kx-card--flush 카드 자체 패딩 제거 + overflow:hidden(미디어를 라운딩에 클립).
* 구획별 패딩·neutral-200 구분선은 파트가 담당. */
.kx-card--flush {
padding: 0;
overflow: hidden;
}
.kx-card__media {
display: block;
width: 100%;
overflow: hidden;
background: var(--color-neutral-100);
}
.kx-card__media > img,
.kx-card__media > video,
.kx-card__media > canvas,
.kx-card__media > svg {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.kx-card__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--color-neutral-200);
}
.kx-card__header h2,
.kx-card__header h3 {
font-size: var(--fs-h3);
line-height: var(--lh-h3);
color: var(--color-neutral-900);
}
.kx-card__body {
padding: var(--space-4);
}
.kx-card__footer {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border-top: 1px solid var(--color-neutral-200);
}
/*
* 공용 제목 클래스 페이지/섹션 제목 편차(20/22/26/30px) 근본 해소.
* 역할토큰 1:1 고정. 화면별 제목은 클래스로 수렴한다.
* */
.kx-page__title {
font-size: var(--fs-h1);
line-height: var(--lh-h1);
font-weight: var(--fw-bold);
color: var(--color-neutral-900);
}
.kx-page__subtitle {
font-size: var(--fs-body);
line-height: var(--lh-body);
color: var(--color-neutral-500);
}
.kx-section__title {
font-size: var(--fs-h2);
line-height: var(--lh-h2);
font-weight: var(--fw-semibold);
color: var(--color-neutral-900);
}
/* 세그먼트 토글 */ /* 세그먼트 토글 */
.kx-seg { .kx-seg {
display: inline-flex; display: inline-flex;
@ -52,7 +131,7 @@
background: transparent; background: transparent;
border-radius: 3px; border-radius: 3px;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
} }
@ -73,7 +152,7 @@
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
/* KPI 카드(공통) */ /* KPI 카드(공통) — 카드 정본과 동일 Nifty 엘리베이션 */
.kx-kpi { .kx-kpi {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -82,6 +161,11 @@
border: var(--border-card); border: var(--border-card);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
background: var(--color-white); background: var(--color-white);
box-shadow: var(--shadow-card);
transition: box-shadow 0.15s ease;
}
.kx-kpi:hover {
box-shadow: var(--shadow-level2);
} }
.kx-kpi__label { .kx-kpi__label {
display: flex; display: flex;
@ -92,13 +176,13 @@
} }
.kx-kpi__ai { .kx-kpi__ai {
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-weight: 700; font-weight: var(--fw-bold);
font-size: 11px; font-size: var(--fs-micro);
} }
.kx-kpi__value { .kx-kpi__value {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: var(--lh-display); line-height: var(--lh-display);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-kpi__sub { .kx-kpi__sub {
@ -116,7 +200,7 @@
.kx-delta { .kx-delta {
align-self: flex-start; align-self: flex-start;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
padding: 1px 8px; padding: 1px 8px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -131,7 +215,7 @@
/* degraded 배지 */ /* degraded 배지 */
.kx-bi__degraded { .kx-bi__degraded {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-warning); color: var(--color-warning);
background: var(--color-warning-bg); background: var(--color-warning-bg);
padding: 2px 8px; padding: 2px 8px;
@ -160,7 +244,7 @@
z-index: 1; z-index: 1;
padding: 10px 12px; padding: 10px 12px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
text-align: left; text-align: left;
letter-spacing: 0.02em; letter-spacing: 0.02em;
@ -190,6 +274,41 @@
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
/* 비교 매트릭스 표준 (.kx-matrix) 목록 표준과 분리된 의도적 패턴.
* 헤더 강조(neutral-900/700)·중앙 정렬·항목별 1px 보더. auction 낙찰 비교 . */
.kx-matrix {
width: 100%;
border-collapse: collapse;
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
.kx-matrix th,
.kx-matrix td {
padding: var(--space-3) var(--space-4);
border: 1px solid var(--color-neutral-200);
text-align: center;
vertical-align: middle;
}
.kx-matrix thead th {
font-size: var(--fs-caption);
font-weight: var(--fw-bold);
color: var(--color-neutral-900);
background: var(--color-neutral-050);
white-space: nowrap;
}
.kx-matrix tbody th {
font-weight: var(--fw-semibold);
color: var(--color-neutral-900);
text-align: left;
background: var(--color-neutral-050);
}
.kx-matrix .kx-num {
font-variant-numeric: tabular-nums;
}
/* 참고: docs 리포트 (.kx-rep__table) A4 종이 시뮬레이션(고정 배경·고정 잉크색)이라
* 테마 토큰(다크서 반전) 부적합 + 신규 인쇄색 토큰 금지 의도적 테마 독립 예외로 유지한다. */
/* 가로 막대 리스트 */ /* 가로 막대 리스트 */
.kx-barlist { .kx-barlist {
list-style: none; list-style: none;
@ -223,7 +342,7 @@
.kx-grade { .kx-grade {
display: inline-block; display: inline-block;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }

View File

@ -20,7 +20,7 @@
} }
.kx-sg__eyebrow { .kx-sg__eyebrow {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.06em; letter-spacing: 0.06em;
text-transform: uppercase; text-transform: uppercase;
color: var(--color-primary-600); color: var(--color-primary-600);
@ -92,9 +92,9 @@
border-bottom: var(--border-card); border-bottom: var(--border-card);
margin-bottom: var(--space-4); margin-bottom: var(--space-4);
} }
.kx-sg__t-display { font-size: var(--fs-display); line-height: var(--lh-display); font-weight: 700; color: var(--color-neutral-900); } .kx-sg__t-display { font-size: var(--fs-display); line-height: var(--lh-display); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-sg__t-headline { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 700; color: var(--color-neutral-900); } .kx-sg__t-headline { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: var(--fw-bold); color: var(--color-neutral-900); }
.kx-sg__t-title { font-size: var(--fs-h3); font-weight: 600; color: var(--color-neutral-700); } .kx-sg__t-title { font-size: var(--fs-h3); font-weight: var(--fw-semibold); color: var(--color-neutral-700); }
.kx-sg__t-body { font-size: var(--fs-body); color: var(--color-neutral-700); } .kx-sg__t-body { font-size: var(--fs-body); color: var(--color-neutral-700); }
.kx-sg__swatches { .kx-sg__swatches {
@ -120,7 +120,7 @@
flex: 1; flex: 1;
} }
.kx-sg__swatch-hex { .kx-sg__swatch-hex {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -144,7 +144,7 @@
} }
.kx-sg__viol { .kx-sg__viol {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-error); color: var(--color-error);
background: #fef3f2; background: #fef3f2;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@ -225,7 +225,7 @@
.kx-sg__err-msg { .kx-sg__err-msg {
color: var(--color-error); color: var(--color-error);
font-style: normal; font-style: normal;
font-size: 11px; font-size: var(--fs-micro);
} }
.kx-sg__check-row { .kx-sg__check-row {
grid-column: 1 / -1; grid-column: 1 / -1;
@ -254,7 +254,7 @@
background: #fef3f2; background: #fef3f2;
color: var(--color-error); color: var(--color-error);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
padding: 10px var(--space-4); padding: 10px var(--space-4);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
border: 1px solid rgba(217, 45, 32, 0.3); border: 1px solid rgba(217, 45, 32, 0.3);

View File

@ -29,7 +29,7 @@
background: var(--color-white); background: var(--color-white);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: var(--fs-h3);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-uord__wizard { .kx-uord__wizard {
@ -55,8 +55,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
background: var(--color-neutral-200); background: var(--color-neutral-200);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
@ -67,7 +67,7 @@
} }
.kx-uord__wizard li.is-current { .kx-uord__wizard li.is-current {
color: var(--color-primary-700); color: var(--color-primary-700);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-uord__wizard li.is-current::before { .kx-uord__wizard li.is-current::before {
background: var(--color-primary-600); background: var(--color-primary-600);
@ -119,8 +119,8 @@
position: absolute; position: absolute;
bottom: var(--space-3); bottom: var(--space-3);
right: var(--space-3); right: var(--space-3);
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
border: 1px solid var(--color-primary-100); border: 1px solid var(--color-primary-100);

View File

@ -26,7 +26,7 @@
padding: 4px 12px; padding: 4px 12px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-violation-warn-text); color: var(--color-violation-warn-text);
background: var(--color-violation-warn-bg); background: var(--color-violation-warn-bg);
} }
@ -43,7 +43,7 @@
padding: 6px 14px; padding: 6px 14px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-util__tab.is-active { .kx-util__tab.is-active {
@ -110,7 +110,7 @@
border: none; border: none;
background: transparent; background: transparent;
color: var(--color-neutral-500); color: var(--color-neutral-500);
font-size: 12px; font-size: var(--fs-caption);
} }
.kx-devtable__del:hover { .kx-devtable__del:hover {
color: var(--color-error); color: var(--color-error);
@ -123,7 +123,7 @@
padding: 6px 12px; padding: 6px 12px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-util__compute { .kx-util__compute {
@ -200,7 +200,7 @@
font-style: normal; font-style: normal;
} }
.kx-quote__amount { .kx-quote__amount {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-quote__total { .kx-quote__total {
@ -216,12 +216,12 @@
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-quote__disclaimer { .kx-quote__disclaimer {
font-size: 11px; font-size: var(--fs-micro);
line-height: 15px; line-height: 15px;
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-quote__ruleset { .kx-quote__ruleset {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-quote__empty { .kx-quote__empty {
@ -239,14 +239,14 @@
gap: 4px; gap: 4px;
} }
.kx-quote__submitted-title { .kx-quote__submitted-title {
font-weight: 700; font-weight: var(--fw-bold);
font-size: var(--fs-body); font-size: var(--fs-body);
} }
.kx-quote__relay { .kx-quote__relay {
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-quote__diagram { .kx-quote__diagram {
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-util__banner { .kx-util__banner {
font-size: var(--fs-caption); font-size: var(--fs-caption);

View File

@ -22,7 +22,7 @@
padding: 4px 10px; padding: 4px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: #fde68a; color: #fde68a;
background: rgba(180, 83, 9, 0.28); background: rgba(180, 83, 9, 0.28);
border: 1px solid rgba(253, 224, 71, 0.4); border: 1px solid rgba(253, 224, 71, 0.4);
@ -102,7 +102,7 @@
.kx-wire__path-label { .kx-wire__path-label {
fill: var(--color-white); fill: var(--color-white);
font-size: 1.4px; font-size: 1.4px;
font-weight: 600; font-weight: var(--fw-semibold);
text-anchor: middle; text-anchor: middle;
paint-order: stroke; paint-order: stroke;
stroke: rgba(16, 24, 40, 0.85); stroke: rgba(16, 24, 40, 0.85);

View File

@ -316,5 +316,5 @@
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-checkin__inside-sub b { color: var(--color-neutral-700); font-weight: 600; } .kx-checkin__inside-sub b { color: var(--color-neutral-700); font-weight: var(--fw-semibold); }
.kx-checkin__hours { min-width: 0; } .kx-checkin__hours { min-width: 0; }

View File

@ -38,8 +38,8 @@
/* 샘플 데이터 배지 */ /* 샘플 데이터 배지 */
.kx-sample { .kx-sample {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
border: 1px solid #fcd9a8; border: 1px solid #fcd9a8;
@ -50,8 +50,8 @@
/* 오프라인 강등(폴백) 표시 — 집계 API 미연결 시 시연 데이터 안내 */ /* 오프라인 강등(폴백) 표시 — 집계 API 미연결 시 시연 데이터 안내 */
.kx-vis__degraded { .kx-vis__degraded {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-warning); color: var(--color-warning);
background: #fff4e5; background: #fff4e5;
border: 1px solid #fcd9a8; border: 1px solid #fcd9a8;
@ -139,7 +139,7 @@
} }
.kx-vis__donut-title { .kx-vis__donut-title {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }
@ -202,8 +202,8 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-vis__form-state { .kx-vis__form-state {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
background: var(--color-neutral-050); background: var(--color-neutral-050);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
@ -250,7 +250,7 @@
background: var(--color-primary-600); background: var(--color-primary-600);
color: var(--color-on-accent); color: var(--color-on-accent);
font-size: 8px; font-size: 8px;
font-weight: 700; font-weight: var(--fw-bold);
letter-spacing: 0.14em; letter-spacing: 0.14em;
text-align: center; text-align: center;
padding: 5px 0; padding: 5px 0;
@ -261,7 +261,7 @@
} }
.kx-badgecard__name { .kx-badgecard__name {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-primary-700); color: var(--color-primary-700);
} }
.kx-badgecard__org { .kx-badgecard__org {
@ -279,7 +279,7 @@
} }
.kx-badgecard__tag { .kx-badgecard__tag {
font-size: 7px; font-size: 7px;
font-weight: 800; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-ai-accent); background: var(--color-ai-accent);
padding: 2px 5px; padding: 2px 5px;
@ -295,7 +295,7 @@
overflow: hidden; overflow: hidden;
} }
.kx-vis__pii-note { .kx-vis__pii-note {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-vis__person { .kx-vis__person {
@ -303,7 +303,7 @@
align-items: center; align-items: center;
gap: var(--space-3); gap: var(--space-3);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-weight: 500; font-weight: var(--fw-medium);
} }
.kx-vis__avatar { .kx-vis__avatar {
width: 30px; width: 30px;
@ -313,7 +313,7 @@
background: var(--color-primary-100); background: var(--color-primary-100);
color: var(--color-primary-700); color: var(--color-primary-700);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -328,8 +328,8 @@
/* 유형 pill */ /* 유형 pill */
.kx-vis__type { .kx-vis__type {
display: inline-block; display: inline-block;
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
padding: 2px 10px; padding: 2px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
} }
@ -352,7 +352,7 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-vis__checkin.is-done { .kx-vis__checkin.is-done {
color: var(--color-success); color: var(--color-success);
@ -379,7 +379,7 @@
gap: 5px; gap: 5px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
color: var(--color-primary-600); color: var(--color-primary-600);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-vis__badge-none { .kx-vis__badge-none {
font-size: var(--fs-caption); font-size: var(--fs-caption);
@ -425,7 +425,7 @@
align-items: center; align-items: center;
gap: 4px; gap: 4px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
background: var(--color-neutral-100); background: var(--color-neutral-100);
border: 1px solid transparent; border: 1px solid transparent;
@ -450,7 +450,7 @@
outline-offset: -2px; outline-offset: -2px;
} }
.kx-vis__lead-name { .kx-vis__lead-name {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
@ -488,8 +488,8 @@
background: var(--color-ai-accent); background: var(--color-ai-accent);
} }
.kx-gauge__num { .kx-gauge__num {
font-size: 11px; font-size: var(--fs-micro);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-on-accent); color: var(--color-on-accent);
background: var(--color-primary-600); background: var(--color-primary-600);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
@ -523,8 +523,8 @@
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-vis__hot { .kx-vis__hot {
font-size: 10px; font-size: var(--fs-nano);
font-weight: 800; font-weight: var(--fw-bold);
color: #4500d8; color: #4500d8;
background: #e9e3ff; background: #e9e3ff;
padding: 2px 7px; padding: 2px 7px;
@ -557,7 +557,7 @@
.kx-vis__ai-score { .kx-vis__ai-score {
font-size: var(--fs-display); font-size: var(--fs-display);
line-height: 1; line-height: 1;
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-ai-accent); color: var(--color-ai-accent);
} }
.kx-vis__ai-actions { .kx-vis__ai-actions {
@ -588,7 +588,7 @@
} }
.kx-vis__ai-check { .kx-vis__ai-check {
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-weight: 700; font-weight: var(--fw-bold);
flex-shrink: 0; flex-shrink: 0;
} }
@ -600,7 +600,7 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
margin-bottom: var(--space-2); margin-bottom: var(--space-2);
} }

View File

@ -61,7 +61,7 @@
} }
.kx-label { .kx-label {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-input, .kx-input,
@ -104,7 +104,7 @@
text-align: left; text-align: left;
padding: 10px 12px; padding: 10px 12px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
letter-spacing: 0.02em; letter-spacing: 0.02em;
color: var(--color-neutral-500); color: var(--color-neutral-500);
border-bottom: 2px solid var(--color-neutral-200); border-bottom: 2px solid var(--color-neutral-200);
@ -130,7 +130,7 @@
background: var(--color-primary-100); background: var(--color-primary-100);
} }
.kx-list-table__title { .kx-list-table__title {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-list-table__muted { .kx-list-table__muted {
@ -149,7 +149,7 @@
padding: 1px 10px; padding: 1px 10px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
white-space: nowrap; white-space: nowrap;
} }
.kx-pill--neutral { .kx-pill--neutral {
@ -246,7 +246,7 @@
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-thread__author { .kx-thread__author {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-thread__text { .kx-thread__text {
@ -297,7 +297,7 @@
background: transparent; background: transparent;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
@ -338,7 +338,7 @@
background: var(--color-primary-100); background: var(--color-primary-100);
} }
.kx-msg__item.is-unread .kx-msg__item-title { .kx-msg__item.is-unread .kx-msg__item-title {
font-weight: 700; font-weight: var(--fw-bold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
} }
.kx-msg__item-top { .kx-msg__item-top {
@ -396,7 +396,7 @@
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
padding: 2px 6px 2px 10px; padding: 2px 6px 2px 10px;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
} }
.kx-tag button { .kx-tag button {
border: none; border: none;
@ -445,7 +445,7 @@
padding: 8px; padding: 8px;
text-align: center; text-align: center;
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-cal__wd--sun { .kx-cal__wd--sun {
@ -474,7 +474,7 @@
} }
.kx-cal__daynum { .kx-cal__daynum {
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
} }
.kx-cal__cell.is-today .kx-cal__daynum { .kx-cal__cell.is-today .kx-cal__daynum {
@ -488,7 +488,7 @@
justify-content: center; justify-content: center;
} }
.kx-cal__event { .kx-cal__event {
font-size: 11px; font-size: var(--fs-micro);
line-height: 16px; line-height: 16px;
padding: 1px 6px; padding: 1px 6px;
border-radius: 4px; border-radius: 4px;
@ -498,7 +498,7 @@
white-space: nowrap; white-space: nowrap;
} }
.kx-cal__more { .kx-cal__more {
font-size: 11px; font-size: var(--fs-micro);
color: var(--color-neutral-500); color: var(--color-neutral-500);
} }
.kx-cal__legend { .kx-cal__legend {
@ -575,7 +575,7 @@
background: var(--color-ai-surface); background: var(--color-ai-surface);
color: var(--color-ai-accent); color: var(--color-ai-accent);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 700; font-weight: var(--fw-bold);
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
} }
@ -595,7 +595,7 @@
border: none; border: none;
background: transparent; background: transparent;
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-500); color: var(--color-neutral-500);
cursor: pointer; cursor: pointer;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
@ -696,7 +696,7 @@
border: var(--border-card); border: var(--border-card);
background: var(--color-white); background: var(--color-white);
font-size: var(--fs-caption); font-size: var(--fs-caption);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
cursor: pointer; cursor: pointer;
} }
@ -731,7 +731,7 @@
min-width: 0; min-width: 0;
} }
.kx-noti__title { .kx-noti__title {
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-900); color: var(--color-neutral-900);
font-size: var(--fs-body); font-size: var(--fs-body);
} }
@ -782,7 +782,7 @@
background: transparent; background: transparent;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: var(--fs-body); font-size: var(--fs-body);
font-weight: 600; font-weight: var(--fw-semibold);
color: var(--color-neutral-700); color: var(--color-neutral-700);
cursor: pointer; cursor: pointer;
} }
@ -797,7 +797,7 @@
background: var(--color-primary-600); background: var(--color-primary-600);
color: #fff; color: #fff;
font-size: 26px; font-size: 26px;
font-weight: 700; font-weight: var(--fw-bold);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -75,6 +75,7 @@
--ai-skeleton-a: var(--dk-ai-skeleton-a); --ai-skeleton-a: var(--dk-ai-skeleton-a);
--ai-skeleton-b: var(--dk-ai-skeleton-b); --ai-skeleton-b: var(--dk-ai-skeleton-b);
--focus-ring: var(--dk-primary-700); --focus-ring: var(--dk-primary-700);
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-level2: 0 4px 12px rgba(0, 0, 0, 0.5); --shadow-level2: 0 4px 12px rgba(0, 0, 0, 0.5);
} }
@ -113,6 +114,7 @@
--ai-skeleton-a: var(--dk-ai-skeleton-a); --ai-skeleton-a: var(--dk-ai-skeleton-a);
--ai-skeleton-b: var(--dk-ai-skeleton-b); --ai-skeleton-b: var(--dk-ai-skeleton-b);
--focus-ring: var(--dk-primary-700); --focus-ring: var(--dk-primary-700);
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-level2: 0 4px 12px rgba(0, 0, 0, 0.5); --shadow-level2: 0 4px 12px rgba(0, 0, 0, 0.5);
} }
} }

View File

@ -66,8 +66,19 @@
--lh-body: 22px; --lh-body: 22px;
--fs-caption: 12px; --fs-caption: 12px;
--lh-caption: 18px; --lh-caption: 18px;
/* 마이크로 라벨(비공식 계층 공식화) — audit: 11px 128건 흡수 */
--fs-micro: 11px;
--lh-micro: 16px;
/* 초소형(배지·QR·범례) — 10/9/8px 수렴 */
--fs-nano: 10px;
--fs-mono: 13px; --fs-mono: 13px;
/* ── 폰트 웨이트 토큰 (audit P0) — 페이지 간 700/600/500/800 혼용 수렴 ── */
--fw-regular: 400;
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: 700; /* 800 남발 → 700 수렴 (display/hero 예외만 인라인) */
/* ── 라운드 (§1-4) — 최대 8px, 칩은 pill ── */ /* ── 라운드 (§1-4) — 최대 8px, 칩은 pill ── */
--radius-sm: 4px; /* 버튼·인풋·작은 요소 */ --radius-sm: 4px; /* 버튼·인풋·작은 요소 */
--radius-lg: 8px; /* 카드·모달·섹션 */ --radius-lg: 8px; /* 카드·모달·섹션 */
@ -75,7 +86,8 @@
/* ── 엘리베이션 — 톤 레이어 + 1px 테두리 ── */ /* ── 엘리베이션 — 톤 레이어 + 1px 테두리 ── */
--border-card: 1px solid var(--color-neutral-200); --border-card: 1px solid var(--color-neutral-200);
--shadow-level2: 0 4px 12px rgba(16, 24, 40, 0.1); --shadow-card: 0 1px 2px rgba(16, 24, 40, 0.06); /* Nifty 카드 기본(subtle) */
--shadow-level2: 0 4px 12px rgba(16, 24, 40, 0.1); /* 카드 hover 상승 */
--accent-ai: 4px solid var(--color-ai-accent); /* AI 카드 좌측 액센트 */ --accent-ai: 4px solid var(--color-ai-accent); /* AI 카드 좌측 액센트 */
/* ── 그리드·간격 (§1-4) — 4px 베이스 ── */ /* ── 그리드·간격 (§1-4) — 4px 베이스 ── */