/* 커스텀 스타일 — design-ref.md (Toss 스타일) 기반. Pico 뒤에 로드해 변수 오버라이드. 폰트는 폐쇄망 대비 로컬 동봉(static/fonts), CDN 의존 없음. */ @font-face { font-family: "Pretendard Variable"; font-weight: 45 920; font-style: normal; font-display: swap; src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations"); } @font-face { font-family: "JetBrains Mono"; font-weight: 100 800; font-style: normal; font-display: swap; src: url("/static/fonts/JetBrainsMono.woff2") format("woff2-variations"); } :root { /* palette (design-ref §2) */ --bg: #ffffff; --bg-alt: #f9fafb; --surface: #f2f4f6; --surface-2: #e8f3ff; --text: #191f28; --text-secondary: #333d4b; --text-muted: #6b7684; --text-dim: #8b95a1; --border: #e5e8eb; --border-strong: #d1d6db; --accent: #3182f6; --accent-hover: #1b64da; --accent-deep: #1e40af; --accent-soft: #e8f3ff; --success-text: #00a37a; --warning-text: #c5700a; --danger: #f04452; --radius-card: 12px; /* 영문/숫자(저장소명·버전·패키지수·시각)는 모노 — 도구다운 가독성 (design-ref §3). 혹시 섞이는 한글은 모노에 글리프가 없으니 한글 sans 로 폴백. */ --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Apple SD Gothic Neo", "Noto Sans KR", monospace; /* Pico 매핑 */ --pico-font-family-sans-serif: "Pretendard Variable", -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Segoe UI", "Noto Sans KR", system-ui, sans-serif; --pico-font-family: var(--pico-font-family-sans-serif); --pico-line-height: 1.55; --pico-border-radius: 7px; --pico-background-color: var(--bg-alt); --pico-color: var(--text-secondary); --pico-h1-color: var(--text); --pico-h2-color: var(--text); --pico-h3-color: var(--text); --pico-muted-color: var(--text-muted); --pico-primary: #ffffff; --pico-primary-background: var(--accent); --pico-primary-hover-background: var(--accent-hover); --pico-primary-border: var(--accent); --pico-primary-hover-border: var(--accent-hover); --pico-card-background-color: var(--bg); --pico-card-border-color: var(--border); --pico-card-box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); color-scheme: light; } /* 다크 테마 — 토큰만 교체하면 컴포넌트는 자동으로 따라온다. design-ref 는 다크를 기본으로 쓰지 말라 하므로 토글 옵션으로만 제공. */ [data-theme="dark"] { color-scheme: dark; --bg: #1b1d23; --bg-alt: #131419; --surface: #262a33; --surface-2: #1e2b44; --text: #eceef2; --text-secondary: #c3c9d2; --text-muted: #8b95a1; --text-dim: #6b7480; --border: #2b2f39; --border-strong: #3a404c; --accent: #4d94ff; --accent-hover: #6aa6ff; --accent-deep: #2f6fd0; --accent-soft: #1e2b44; --success-text: #2ee0a8; --warning-text: #ffb454; --danger: #ff6b78; --pico-background-color: var(--bg-alt); --pico-color: var(--text-secondary); --pico-h1-color: var(--text); --pico-h2-color: var(--text); --pico-h3-color: var(--text); --pico-muted-color: var(--text-muted); --pico-primary: #ffffff; --pico-primary-background: var(--accent); --pico-primary-hover-background: var(--accent-hover); --pico-primary-border: var(--accent); --pico-primary-hover-border: var(--accent-hover); --pico-card-background-color: var(--bg); --pico-card-border-color: var(--border); --pico-card-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); --pico-form-element-background-color: var(--surface); --pico-form-element-border-color: var(--border); --pico-form-element-color: var(--text); --pico-progress-background-color: var(--surface); } body { transition: background-color 0.15s ease, color 0.15s ease; } body { background: var(--bg-alt); color: var(--text-secondary); -webkit-font-smoothing: antialiased; } main.container { max-width: 1040px; padding-block: 40px; } h1, h2, h3 { color: var(--text); font-weight: 700; letter-spacing: -0.02em; } h2 { font-size: 22px; margin: 0; } section { margin-bottom: 44px; } .mono { font-family: var(--font-mono); font-feature-settings: "tnum" 1; } /* 상단 내비 */ .app-nav { background: var(--bg); border-bottom: 1px solid var(--border); } .app-nav .container { max-width: 1040px; padding-block: 18px; display: flex; align-items: center; justify-content: space-between; } /* 테마 토글 (아이콘 버튼) */ .theme-toggle { --pico-background-color: transparent; --pico-border-color: var(--border-strong); --pico-color: var(--text-secondary); width: auto; margin: 0; padding: 7px 9px; line-height: 0; } .theme-toggle:hover { --pico-background-color: var(--surface); --pico-border-color: var(--text-dim); --pico-color: var(--text); } .theme-toggle .icon { width: 18px; height: 18px; } .theme-toggle .sun { display: none; } .theme-toggle .moon { display: inline-block; } [data-theme="dark"] .theme-toggle .moon { display: none; } [data-theme="dark"] .theme-toggle .sun { display: inline-block; } .app-nav .brand { margin: 0; font-size: 18px; font-weight: 700; color: var(--text); letter-spacing: -0.02em; } .app-nav .brand .accent { color: var(--accent); } .section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; } /* 상태: 라벨 + 배지 한 줄 */ .status-row { display: flex; align-items: center; gap: 12px; } .status-label { font-size: 22px; /* 저장소 h2 와 동일 */ font-weight: 700; letter-spacing: -0.02em; color: var(--text); } .status-row .status-line { font-size: 14px; } /* 버튼 — Toss: radius 7, weight 600, 누름 피드백 scale(0.98) (design-ref §4·§6) */ button, [role="button"] { font-weight: 600; transition: background-color 0.12s ease, border-color 0.12s ease, opacity 0.15s ease, transform 0.096s ease; } button:active, [role="button"]:active { transform: scale(0.98); } button.secondary, [role="button"].secondary, a.secondary { --pico-background-color: var(--surface-2); --pico-border-color: var(--surface-2); --pico-color: var(--accent-hover); font-weight: 600; } button.danger, [role="button"].danger { --pico-background-color: var(--danger); --pico-border-color: var(--danger); --pico-color: #ffffff; } /* 유틸리티(새로고침 등): 중립 ghost — 동기화(틴티드 블루)와 구분 */ button.ghost, [role="button"].ghost { --pico-background-color: transparent; --pico-border-color: var(--border-strong); --pico-color: var(--text-secondary); } button.ghost:hover, [role="button"].ghost:hover { --pico-background-color: var(--surface); --pico-border-color: var(--text-dim); --pico-color: var(--text); /* 호버 시 글자 하얘짐 방지 */ } /* 아이콘 + 글자 버튼 */ .btn-icon { display: inline-flex; align-items: center; gap: 6px; } .btn-icon .icon { flex: none; } button:disabled, [role="button"]:disabled { opacity: 0.45; } /* 표 안 액션 버튼: 컴팩트하되 글자는 또렷하게 */ .btn-sm { padding: 7px 14px; font-size: 14px; margin: 0; width: auto; } /* 요약 카드 */ .summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; } .summary .card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-card); padding: 20px 22px; box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); transition: border-color 0.12s ease; } .summary .card:hover { border-color: var(--border-strong); } .summary .value { font-family: var(--font-mono); font-size: 34px; font-weight: 600; color: var(--text); line-height: 1.1; letter-spacing: -0.02em; font-feature-settings: "tnum" 1; } .summary .value.is-accent { color: var(--accent); } .summary .value.is-success { color: var(--success-text); } .summary .label { margin-top: 8px; font-size: 13px; font-weight: 500; color: var(--text-muted); } /* 표 (카드로 감싸기, hairline 행, hover) */ .table-card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-card); overflow: hidden; box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); } .table-card table { margin: 0; } .table-card thead th { font-size: 13px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; background: var(--bg-alt); border-bottom: 1px solid var(--border); padding-block: 14px; } .table-card tbody td { height: 56px; font-size: 14px; border-bottom: 1px solid var(--border); padding-block: 12px; vertical-align: middle; } .table-card tbody tr:last-child td { border-bottom: none; } .table-card tbody tr:hover { background: var(--bg-alt); } .repo-name a { font-family: var(--font-mono); font-weight: 600; color: var(--text); text-decoration: none; } .repo-name a:hover { color: var(--accent); } /* 숫자/버전/시각: 모노 + tabular — 본문 셀(td)과 .mono 에만 적용. 헤더(th)는 한글 라벨이라 모노 제외(Pretendard 유지). */ .mono, td.num { font-family: var(--font-mono); font-feature-settings: "tnum" 1; } td.num, th.num { text-align: right; } /* 액션 셀: 버튼↔진행률↔완료배지로 내용이 바뀌어도 행 높이·열 너비가 안 변하게 너비·높이를 고정한 박스로 만든다. 넘치는 내용은 클립. */ .row-action { display: flex; align-items: center; width: 172px; height: 36px; overflow: hidden; } /* 검증 배지 칩 (dot + 라벨, 이모지 없음) */ .badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border-radius: 999px; font-size: 14px; font-weight: 500; white-space: nowrap; } .badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; } .badge-pass { color: var(--success-text); background: rgba(0, 200, 150, 0.12); } .badge-warn { color: var(--warning-text); background: rgba(255, 149, 0, 0.13); } .badge-unset { color: var(--text-muted); background: var(--surface); } .badge-deployed { color: var(--accent-hover); background: var(--accent-soft); } /* 뒤로가기(대시보드) 버튼 — ghost, 제목과 간격 확보 */ .back-btn { margin-bottom: 20px; text-decoration: none; } /* 버전 검증 상세 */ .verify-detail { display: flex; align-items: center; gap: 12px; margin-top: 20px; margin-bottom: 20px; } .verify-detail small { color: var(--text-muted); font-family: var(--font-mono); } /* 패키지 증감 */ .delta-add { color: var(--success-text); } .delta-del { color: var(--danger); } /* 배포 완료 배너 (#flash) */ .flash-banner { display: flex; align-items: center; gap: 10px; margin: 4px 0 22px; padding: 12px 16px; border-radius: 10px; background: rgba(0, 200, 150, 0.1); color: var(--success-text); font-size: 15px; font-weight: 600; } .flash-banner .flash-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: currentColor; } .flash-banner .flash-sub { color: var(--text-muted); font-weight: 500; font-size: 13px; } .flash-banner .flash-warn { margin-left: auto; color: var(--warning-text); font-weight: 500; font-size: 13px; } /* 배포 확인 모달 — 본문/표/라벨 15px 로 통일 */ .deploy-modal h3 { font-size: 20px; } .deploy-note { font-size: 17px; color: var(--text-secondary); margin-bottom: 14px; } .deploy-preview { margin: 0 0 18px; font-size: 17px; } .deploy-preview, .deploy-preview tr, .deploy-preview th, .deploy-preview td { border: none; background: none; } .deploy-preview th { width: 30%; padding: 8px 0; text-align: left; font-weight: 500; color: var(--text-muted); } .deploy-preview td { padding: 8px 0; text-align: right; } .confirm-label { display: block; font-size: 17px; } /* 입력란만 작게 (본문보다 4px 작은 14px + 컴팩트 높이) */ .confirm-label input { margin-top: 8px; font-size: 14px; --pico-form-element-spacing-vertical: 0.5rem; height: auto; } .modal-actions { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 20px; } /* 모달 버튼: 편안한 표준 크기, 한 줄, 너비 auto */ .modal-actions button { width: auto; margin: 0; padding: 11px 20px; font-size: 16px; } /* 동기화 진행률 바 */ .sync-progress { display: flex; flex-direction: column; gap: 5px; width: 150px; } .sync-progress progress { margin: 0; width: 100%; height: 6px; } .sync-progress small { color: var(--text-muted); font-family: var(--font-mono); font-size: 12px; } /* 연결 상태 칩 (dot + 텍스트) */ .status-line { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 999px; font-weight: 500; background: var(--surface); } .status-line::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; } .status-ok { color: var(--success-text); background: rgba(0, 200, 150, 0.1); } .status-bad { color: var(--danger); background: rgba(240, 68, 82, 0.1); } .status-loading { color: var(--text-muted); } /* 검색 툴바 / 정렬 헤더 / 페이저 */ .list-toolbar { margin-bottom: 14px; } .repo-search { width: 100%; margin: 0; font-size: 14px; /* 세로로 얇게 */ --pico-form-element-spacing-vertical: 0.5rem; height: auto; } .table-card thead th a.th-sort { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; cursor: pointer; } .table-card thead th a.th-sort:hover { color: var(--text-secondary); } .table-card thead th a.th-sort.is-active { color: var(--accent); } .sort-ind { font-size: 11px; } .pager { display: flex; align-items: center; justify-content: flex-end; gap: 14px; padding: 12px 16px; border-top: 1px solid var(--border); color: var(--text-muted); font-size: 13px; } .empty-note { padding: 16px; color: var(--text-muted); } @media (max-width: 640px) { .summary { grid-template-columns: repeat(2, 1fr); } }