디자인 핸드오프(design_handoff_console_redesign)를 실제 앱에 반영.
CSS 드롭인 교체 + 최소 마크업 추가, HTMX 훅·id·data 속성은 모두 보존.
- app.css: 재디자인 토큰/컴포넌트로 교체(요약카드 호버 리프트, 표
제브라, 중앙 경고형 배포 모달, secondary/ghost/danger 버튼 체계,
진행률·상태칩·스켈레톤 등).
- base.html: 브랜드 로고 큐브(.brand-mark) 추가 + 데모 배너.
- repo_list: 요약 카드 .card-top/.card-ico + 검색 돋보기 아이콘.
- table_wrap: 회전형 정렬 인디케이터, 동기화 버튼 아이콘.
- progress: .sync-head/.sync-pct/.is-active/.indeterminate 구조.
- deploy_modal: 중앙 경고형 + 현재→대상 프리뷰 카드 + 타입-투-컨펌
체크/X 시각 상태.
- deploy_success: 그려지는 체크 원 배너.
- version_list: 표 → .version-item 카드 리스트.
Pico v2 호환(미리보기는 pico-shim 기준이라 안 드러났던 것):
- Pico 의 `:is(...).secondary`(0,2,0)·hover(0,4,0) 특정성을 이기도록
오버라이드를 :is() 형태로 상향 → 동기화 버튼 흰배경+파란글씨 유지,
hover 시 어두워지던 문제 해결.
- `input[type=search]` 의 Pico 기본 돋보기 배경 제거(아이콘 중복).
- `td` 배경을 투명 처리해 tr 제브라/hover 가 보이도록(Pico 의
`td{background:var(--pico-background-color)}` 가 덮던 문제), 줄무늬
색을 --bg-alt 로 또렷하게.
- .row-action 을 40px 고정 → 버튼↔진행률↔배지 교체 시 높이 흔들림·
위아래 테두리 잘림 해소.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1252 lines
32 KiB
CSS
1252 lines
32 KiB
CSS
/* 커스텀 스타일 — design-ref.md (Toss 스타일) 기반. Pico 뒤에 로드해 변수 오버라이드.
|
|
폰트는 폐쇄망 대비 로컬 동봉(static/fonts), CDN 의존 없음.
|
|
|
|
[재디자인 노트]
|
|
- 토큰(그림자/모션/포커스)을 추가해 라이트·다크가 함께 정돈되도록 함.
|
|
- hover/press/정렬/진행/모달 진입에 절제된 마이크로 인터랙션 추가.
|
|
- 확정 스타일: 요약카드=화이트+호버리프트, 표=제브라, 배포모달=중앙 경고형.
|
|
- 모든 모션은 prefers-reduced-motion 에서 비활성화(파일 맨 끝). */
|
|
|
|
@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: oklch(0.63 0.147 255); /* Toss Blue 계열이되 채도를 낮춰 눈부심(쨍) 완화 */
|
|
--accent-hover: oklch(0.55 0.16 255);
|
|
--accent-deep: #1e40af;
|
|
--accent-soft: #e8f3ff;
|
|
--success-text: #00a37a;
|
|
--success-soft: rgba(0, 200, 150, 0.12);
|
|
--warning-text: #c5700a;
|
|
--warning-soft: rgba(197, 112, 10, 0.12);
|
|
--danger: #f04452;
|
|
--danger-hover: #e0313f;
|
|
--danger-soft: rgba(240, 68, 82, 0.1);
|
|
|
|
--radius-card: 12px;
|
|
|
|
/* 그림자 — 단계형. design-ref §6: 얇은 카드 그림자 + 무거운 모달 그림자 */
|
|
--shadow-sm: 0 1px 2px rgba(25, 31, 40, 0.04);
|
|
--shadow-card: 0 1px 3px rgba(25, 31, 40, 0.05);
|
|
--shadow-card-hover: 0 4px 16px rgba(25, 31, 40, 0.09);
|
|
--shadow-modal: 0 12px 40px rgba(25, 31, 40, 0.18);
|
|
|
|
/* 모션 토큰 — 도구답게 짧고 자연스럽게 */
|
|
--ease: cubic-bezier(0.32, 0.72, 0, 1);
|
|
--dur-fast: 0.12s;
|
|
--dur: 0.2s;
|
|
--dur-slow: 0.32s;
|
|
--focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
|
|
|
|
/* 영문/숫자(저장소명·버전·패키지수·시각)는 모노 — 도구다운 가독성 (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: 8px;
|
|
--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: var(--shadow-card);
|
|
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;
|
|
--success-soft: rgba(46, 224, 168, 0.14);
|
|
--warning-text: #ffb454;
|
|
--warning-soft: rgba(255, 180, 84, 0.14);
|
|
--danger: #ff6b78;
|
|
--danger-hover: #ff8591;
|
|
--danger-soft: rgba(255, 107, 120, 0.14);
|
|
|
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
--shadow-card-hover: 0 4px 18px rgba(0, 0, 0, 0.45);
|
|
--shadow-modal: 0 12px 44px rgba(0, 0, 0, 0.6);
|
|
--focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
|
|
|
|
--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: var(--shadow-card);
|
|
--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 {
|
|
background: var(--bg-alt);
|
|
color: var(--text-secondary);
|
|
-webkit-font-smoothing: antialiased;
|
|
transition:
|
|
background-color var(--dur) ease,
|
|
color var(--dur) ease;
|
|
}
|
|
|
|
main.container {
|
|
max-width: 1040px;
|
|
padding-block: 40px;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
color: var(--text);
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
text-wrap: balance;
|
|
}
|
|
h2 {
|
|
font-size: 22px;
|
|
margin: 0;
|
|
}
|
|
|
|
section {
|
|
margin-bottom: 44px;
|
|
}
|
|
|
|
.mono {
|
|
font-family: var(--font-mono);
|
|
font-feature-settings: "tnum" 1;
|
|
}
|
|
|
|
/* 공통 포커스 링 — 키보드 접근성. 마우스 클릭엔 안 뜨게 focus-visible. */
|
|
:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring);
|
|
}
|
|
|
|
/* 상단 내비 — 스크롤해도 상단 고정, 미세한 그림자로 레이어 분리 */
|
|
.app-nav {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 20;
|
|
background: color-mix(in srgb, var(--bg) 88%, transparent);
|
|
backdrop-filter: saturate(1.4) blur(10px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.app-nav .container {
|
|
max-width: 1040px;
|
|
padding-block: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
.app-nav .brand {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
letter-spacing: -0.02em;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
}
|
|
.app-nav .brand .accent {
|
|
color: var(--accent);
|
|
}
|
|
/* 브랜드 앞 로고 마크 — 차분한 단색(그라디언트 제거로 눈부심 완화) */
|
|
.app-nav .brand .brand-mark {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 7px;
|
|
background: var(--accent);
|
|
display: inline-grid;
|
|
place-items: center;
|
|
color: #fff;
|
|
font-size: 13px;
|
|
font-weight: 800;
|
|
box-shadow: var(--shadow-sm);
|
|
flex: none;
|
|
}
|
|
|
|
/* 테마 토글 (아이콘 버튼) */
|
|
.theme-toggle {
|
|
--pico-background-color: transparent;
|
|
--pico-border-color: var(--border-strong);
|
|
--pico-color: var(--text-secondary);
|
|
width: auto;
|
|
margin: 0;
|
|
padding: 8px 10px;
|
|
line-height: 0;
|
|
border-radius: 9px;
|
|
transition:
|
|
background-color var(--dur-fast) ease,
|
|
border-color var(--dur-fast) ease,
|
|
transform var(--dur-fast) var(--ease);
|
|
}
|
|
.theme-toggle:hover {
|
|
--pico-background-color: var(--surface);
|
|
--pico-border-color: var(--text-dim);
|
|
--pico-color: var(--text);
|
|
}
|
|
.theme-toggle:active {
|
|
transform: scale(0.94);
|
|
}
|
|
.theme-toggle .icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
transition: transform var(--dur) var(--ease);
|
|
}
|
|
.theme-toggle:hover .icon {
|
|
transform: rotate(20deg);
|
|
}
|
|
.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;
|
|
}
|
|
|
|
.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~8, weight 600, 누름 피드백 (design-ref §4·§6) ── */
|
|
button,
|
|
[role="button"] {
|
|
font-weight: 600;
|
|
letter-spacing: -0.01em;
|
|
border-radius: 8px;
|
|
white-space: nowrap;
|
|
transition:
|
|
background-color var(--dur-fast) ease,
|
|
border-color var(--dur-fast) ease,
|
|
box-shadow var(--dur-fast) ease,
|
|
opacity var(--dur) ease,
|
|
transform var(--dur-fast) var(--ease);
|
|
}
|
|
button:not(:disabled):hover,
|
|
[role="button"]:not(:disabled):hover {
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
button:not(:disabled):active,
|
|
[role="button"]:not(:disabled):active {
|
|
transform: scale(0.97);
|
|
box-shadow: none;
|
|
}
|
|
/* primary 눌림 시 더 짙은 파랑 (design-ref: pressed=accent-deep) */
|
|
button[type="submit"]:not(.secondary):not(.ghost):not(.danger):active,
|
|
button:not(.secondary):not(.ghost):not(.danger):not([role]):active {
|
|
--pico-primary-background: var(--accent-deep);
|
|
--pico-primary-border: var(--accent-deep);
|
|
}
|
|
/* 동기화 등 반복 노출 액션: 흰 배경 + 파란 글씨 + 옅은 파란 테두리.
|
|
표에 여러 개 떠도 번잡하지 않고, ghost(새로고침)와 확실히 구분됨.
|
|
Pico v2 는 `:is(button,[type=submit],[type=button],[role=button]).secondary`
|
|
(특정성 0,2,0)로 secondary 를 칠하므로, 같은 :is() 형태로 특정성을 맞춰
|
|
뒤에 로드되는 app.css 가 이기게 한다(단순 `button.secondary`=0,1,1 은 짐). */
|
|
:is(button, [role="button"], a).secondary {
|
|
--pico-background-color: var(--bg);
|
|
--pico-border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
|
|
--pico-color: var(--accent);
|
|
font-weight: 600;
|
|
}
|
|
/* Pico 의 secondary hover 는 `:is(button,…).secondary:is([aria-current]…,:hover,…)`
|
|
= 특정성 (0,4,0) 이라 어두운 배경 + 흰 글씨로 덮어쓴다. 같은 (0,4,0) :is() 형태로
|
|
맞춰(뒤에 로드되어 우선) 밝은 파랑 hover 를 유지한다. 글씨색(--pico-color)도
|
|
함께 지정하지 않으면 Pico 가 흰색으로 만들어 옅은 배경 위에서 글씨가 사라진다. */
|
|
:is(button, [role="button"], a).secondary:hover:not(:disabled) {
|
|
--pico-background-color: var(--accent-soft);
|
|
--pico-border-color: var(--accent);
|
|
--pico-color: var(--accent);
|
|
}
|
|
button.danger,
|
|
[role="button"].danger {
|
|
--pico-background-color: var(--danger);
|
|
--pico-border-color: var(--danger);
|
|
--pico-color: #ffffff;
|
|
}
|
|
button.danger:not(:disabled):hover,
|
|
[role="button"].danger:not(:disabled):hover {
|
|
--pico-background-color: var(--danger-hover);
|
|
--pico-border-color: var(--danger-hover);
|
|
}
|
|
/* 유틸리티(새로고침 등): 중립 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;
|
|
cursor: not-allowed;
|
|
}
|
|
/* 표 안 액션 버튼: 컴팩트하되 글자는 또렷하게 */
|
|
.btn-sm {
|
|
padding: 7px 14px;
|
|
font-size: 14px;
|
|
margin: 0;
|
|
width: auto;
|
|
}
|
|
|
|
/* 버튼 로딩 상태 — 텍스트를 스피너로 교체. .is-loading 토글로 사용.
|
|
(HTMX hx-disabled-elt 로 비활성화될 때 함께 붙이면 자연스러움) */
|
|
button.is-loading,
|
|
[role="button"].is-loading {
|
|
color: transparent !important;
|
|
position: relative;
|
|
pointer-events: none;
|
|
}
|
|
button.is-loading::after,
|
|
[role="button"].is-loading::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
margin: auto;
|
|
width: 15px;
|
|
height: 15px;
|
|
border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
|
|
border-top-color: var(--pico-color, #fff);
|
|
border-radius: 50%;
|
|
animation: spin 0.6s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* ── 요약 카드 ── */
|
|
.summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.summary .card {
|
|
position: relative;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-card);
|
|
padding: 20px 22px;
|
|
box-shadow: var(--shadow-card);
|
|
transition:
|
|
border-color var(--dur) ease,
|
|
box-shadow var(--dur) ease,
|
|
transform var(--dur) var(--ease);
|
|
}
|
|
.summary .card:hover {
|
|
border-color: var(--border-strong);
|
|
box-shadow: var(--shadow-card-hover);
|
|
transform: translateY(-2px);
|
|
}
|
|
/* 카드 상단 라벨 + 아이콘 칩 한 줄 */
|
|
.summary .card-top {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 12px;
|
|
}
|
|
.summary .card-ico {
|
|
width: 34px;
|
|
height: 34px;
|
|
border-radius: 9px;
|
|
display: grid;
|
|
place-items: center;
|
|
background: var(--surface);
|
|
color: var(--text-muted);
|
|
flex: none;
|
|
}
|
|
.summary .card-ico .icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
.summary .card.is-accent .card-ico {
|
|
background: var(--accent-soft);
|
|
color: var(--accent);
|
|
}
|
|
.summary .card.is-success .card-ico {
|
|
background: var(--success-soft);
|
|
color: var(--success-text);
|
|
}
|
|
.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: var(--shadow-card);
|
|
}
|
|
.table-card table {
|
|
margin: 0;
|
|
}
|
|
.table-card thead th {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--text-dim);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
background: var(--bg-alt);
|
|
border-bottom: 1px solid var(--border);
|
|
padding-block: 13px;
|
|
position: sticky;
|
|
top: 0;
|
|
}
|
|
.table-card tbody td {
|
|
height: 56px;
|
|
font-size: 14px;
|
|
border-bottom: 1px solid var(--border);
|
|
padding-block: 12px;
|
|
vertical-align: middle;
|
|
/* Pico 의 `td,th{background-color:var(--pico-background-color)}`(흰색)이 td 자신의
|
|
배경으로 칠해져 tr 의 제브라/hover 배경을 덮어버린다(특정성이 아니라 페인트 순서 문제).
|
|
td 배경을 투명으로 돌려 tr 배경이 보이게 한다. */
|
|
background: transparent;
|
|
}
|
|
.table-card tbody tr {
|
|
transition: background-color var(--dur-fast) ease;
|
|
}
|
|
.table-card tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
/* 2B안(확정): 제브라 스트라이프 + 틴티드 블루 hover.
|
|
짝수 행에 연한 회색(--bg-alt)을 깔아 넓은 표에서도 행이 눈으로 잘 따라감.
|
|
(기존 color-mix 값은 거의 흰색이라 실제로 안 보였음 → bg-alt 로 또렷하되 은은하게) */
|
|
.table-card tbody tr:nth-child(even) {
|
|
background: var(--bg-alt);
|
|
}
|
|
.table-card tbody tr:hover {
|
|
background: var(--surface-2);
|
|
}
|
|
.repo-name a {
|
|
font-family: var(--font-mono);
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
transition: color var(--dur-fast) ease;
|
|
}
|
|
.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;
|
|
/* 버튼(테두리 포함 ~37.7px)↔진행률↔배지로 내용이 바뀌어도 높이가 안 흔들리도록
|
|
40px 로 고정하고 가운데 정렬(td 는 56px 고정이라 여유). 이전 36px 는 버튼보다
|
|
낮아 위아래 테두리가 잘리거나(overflow:hidden) 높이가 튀었다(min-height). */
|
|
height: 40px;
|
|
}
|
|
|
|
/* ── 검증 배지 칩 (dot + 라벨, 이모지 없음) ── */
|
|
.badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 11px;
|
|
border-radius: 999px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
border: 1px solid transparent;
|
|
}
|
|
.badge::before {
|
|
content: "";
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
flex: none;
|
|
}
|
|
.badge-pass {
|
|
color: var(--success-text);
|
|
background: var(--success-soft);
|
|
}
|
|
/* PASS 배지의 dot 은 살짝 은은한 펄스 (통과했음을 안심시키는 신호) */
|
|
.badge-pass::before {
|
|
box-shadow: 0 0 0 0 color-mix(in srgb, var(--success-text) 55%, transparent);
|
|
animation: dot-pulse 2.4s var(--ease) infinite;
|
|
}
|
|
@keyframes dot-pulse {
|
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success-text) 45%, transparent); }
|
|
60% { box-shadow: 0 0 0 5px transparent; }
|
|
100% { box-shadow: 0 0 0 0 transparent; }
|
|
}
|
|
.badge-warn {
|
|
color: var(--warning-text);
|
|
background: var(--warning-soft);
|
|
}
|
|
.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);
|
|
}
|
|
|
|
/* 버전 스냅샷 리스트 */
|
|
.version-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 16px 18px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-card);
|
|
background: var(--bg);
|
|
box-shadow: var(--shadow-card);
|
|
margin-bottom: 12px;
|
|
transition:
|
|
border-color var(--dur) ease,
|
|
box-shadow var(--dur) ease;
|
|
}
|
|
.version-item:hover {
|
|
border-color: var(--border-strong);
|
|
box-shadow: var(--shadow-card-hover);
|
|
}
|
|
.version-item .ver-no {
|
|
font-family: var(--font-mono);
|
|
font-weight: 700;
|
|
font-size: 15px;
|
|
color: var(--text);
|
|
}
|
|
.version-item .ver-meta {
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
font-size: 13px;
|
|
}
|
|
.version-item .spacer {
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* 패키지 증감 */
|
|
.delta-add {
|
|
color: var(--success-text);
|
|
font-weight: 600;
|
|
}
|
|
.delta-del {
|
|
color: var(--danger);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* ── 데모(가짜 데이터) 모드 경고 배너 — 전 페이지 최상단. 실연동 오인 방지. ── */
|
|
.demo-banner {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 20px;
|
|
background: var(--warning-soft);
|
|
color: var(--warning-text);
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
border-bottom: 1px solid color-mix(in srgb, var(--warning-text) 28%, transparent);
|
|
}
|
|
.demo-banner strong {
|
|
font-weight: 700;
|
|
}
|
|
.demo-banner .demo-dot {
|
|
flex: none;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
animation: dot-pulse-warn 2s var(--ease) infinite;
|
|
}
|
|
@keyframes dot-pulse-warn {
|
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warning-text) 45%, transparent); }
|
|
60% { box-shadow: 0 0 0 6px transparent; }
|
|
100% { box-shadow: 0 0 0 0 transparent; }
|
|
}
|
|
/* 연결 배지의 데모 변형 */
|
|
.status-demo {
|
|
color: var(--warning-text);
|
|
background: var(--warning-soft);
|
|
}
|
|
|
|
/* ── 배포 완료 배너 (#flash) ── */
|
|
.flash-banner {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin: 4px 0 22px;
|
|
padding: 13px 16px;
|
|
border-radius: 10px;
|
|
background: var(--success-soft);
|
|
color: var(--success-text);
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
border: 1px solid color-mix(in srgb, var(--success-text) 24%, transparent);
|
|
animation: flash-in var(--dur-slow) var(--ease);
|
|
}
|
|
@keyframes flash-in {
|
|
from { opacity: 0; transform: translateY(-8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
/* 성공 체크 원 — dot 대신 그려지는 체크 애니메이션 */
|
|
.flash-banner .flash-check {
|
|
flex: none;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
background: var(--success-text);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.flash-banner .flash-check svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
stroke: #fff;
|
|
stroke-width: 3;
|
|
fill: none;
|
|
stroke-linecap: round;
|
|
stroke-linejoin: round;
|
|
stroke-dasharray: 20;
|
|
stroke-dashoffset: 20;
|
|
animation: check-draw 0.4s var(--ease) 0.12s forwards;
|
|
}
|
|
@keyframes check-draw {
|
|
to { stroke-dashoffset: 0; }
|
|
}
|
|
.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;
|
|
}
|
|
|
|
/* ── 배포 확인 모달 ── */
|
|
#modal[open],
|
|
dialog.deploy-dialog[open] {
|
|
animation: backdrop-in var(--dur) ease;
|
|
}
|
|
@keyframes backdrop-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
/* 모달이 열릴 때 배경 스크롤 잠금 + 다이얼로그 overflow 허용 → 순간 스크롤바 깜뱡임 제거 */
|
|
html:has(dialog[open]) {
|
|
overflow: hidden;
|
|
}
|
|
dialog.deploy-dialog,
|
|
dialog#modal {
|
|
overflow: visible;
|
|
}
|
|
.deploy-modal {
|
|
width: min(520px, 92vw);
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 16px;
|
|
box-shadow: var(--shadow-modal);
|
|
padding: 0;
|
|
overflow: hidden;
|
|
animation: modal-in var(--dur-slow) var(--ease);
|
|
}
|
|
@keyframes modal-in {
|
|
from { opacity: 0; transform: scale(0.96); }
|
|
to { opacity: 1; transform: scale(1); }
|
|
}
|
|
/* 위험 확인 아이콘 — 중앙 정렬 원형(의식적·경고적). 모달 헤더. */
|
|
.deploy-modal .modal-head {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
gap: 10px;
|
|
padding: 26px 24px 6px;
|
|
}
|
|
.deploy-modal .modal-head .warn-ico {
|
|
flex: none;
|
|
width: 52px;
|
|
height: 52px;
|
|
border-radius: 50%;
|
|
display: grid;
|
|
place-items: center;
|
|
background: var(--danger-soft);
|
|
color: var(--danger);
|
|
}
|
|
.deploy-modal .modal-head .warn-ico svg {
|
|
width: 26px;
|
|
height: 26px;
|
|
}
|
|
.deploy-modal .modal-head h3 {
|
|
font-size: 21px;
|
|
margin: 0;
|
|
color: var(--text);
|
|
}
|
|
.deploy-modal .modal-head .head-sub {
|
|
display: block;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--danger);
|
|
margin-top: 2px;
|
|
}
|
|
.deploy-modal .modal-body {
|
|
padding: 20px 24px 24px;
|
|
}
|
|
.deploy-note {
|
|
font-size: 15px;
|
|
color: var(--text-secondary);
|
|
margin-bottom: 16px;
|
|
}
|
|
.deploy-note .repo-hl {
|
|
font-family: var(--font-mono);
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
}
|
|
/* 현재 → 대상 프리뷰: 카드형 before/after */
|
|
.deploy-preview {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto 1fr;
|
|
align-items: stretch;
|
|
gap: 10px;
|
|
margin: 0 0 20px;
|
|
}
|
|
.deploy-preview .pv-box {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
padding: 12px 14px;
|
|
}
|
|
.deploy-preview .pv-box.pv-target {
|
|
background: var(--accent-soft);
|
|
border-color: color-mix(in srgb, var(--accent) 32%, transparent);
|
|
}
|
|
.deploy-preview .pv-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: var(--text-dim);
|
|
margin-bottom: 6px;
|
|
}
|
|
.deploy-preview .pv-target .pv-label {
|
|
color: var(--accent);
|
|
}
|
|
.deploy-preview .pv-ver {
|
|
font-family: var(--font-mono);
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
line-height: 1.15;
|
|
}
|
|
.deploy-preview .pv-pkg {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin-top: 4px;
|
|
}
|
|
.deploy-preview .pv-arrow {
|
|
align-self: center;
|
|
color: var(--text-dim);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.deploy-preview .pv-arrow svg {
|
|
width: 22px;
|
|
height: 22px;
|
|
}
|
|
|
|
/* 타입-투-컨펌 — 저장소 이름을 정확히 입력해야 확정 버튼이 열림 */
|
|
.confirm-label {
|
|
display: block;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-secondary);
|
|
}
|
|
.confirm-label .repo-hl {
|
|
font-family: var(--font-mono);
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
background: var(--surface);
|
|
padding: 1px 7px;
|
|
border-radius: 5px;
|
|
}
|
|
.confirm-field {
|
|
position: relative;
|
|
margin-top: 8px;
|
|
}
|
|
.confirm-label input {
|
|
font-family: var(--font-mono);
|
|
font-size: 14px;
|
|
--pico-form-element-spacing-vertical: 0.6rem;
|
|
height: auto;
|
|
padding-right: 40px;
|
|
}
|
|
/* 입력 상태 시각화: 비어있음(중립) → 불일치(빨강) → 일치(초록 체크) */
|
|
.confirm-field .confirm-state {
|
|
position: absolute;
|
|
right: 12px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 18px;
|
|
height: 18px;
|
|
opacity: 0;
|
|
transition: opacity var(--dur) ease;
|
|
}
|
|
.confirm-field.is-valid input {
|
|
border-color: var(--success-text);
|
|
box-shadow: 0 0 0 3px var(--success-soft);
|
|
}
|
|
.confirm-field.is-valid .confirm-state.ok {
|
|
opacity: 1;
|
|
color: var(--success-text);
|
|
}
|
|
.confirm-field.is-invalid input {
|
|
border-color: var(--danger);
|
|
}
|
|
.confirm-field.is-invalid .confirm-state.bad {
|
|
opacity: 1;
|
|
color: var(--danger);
|
|
}
|
|
.confirm-hint {
|
|
font-size: 12px;
|
|
margin-top: 6px;
|
|
min-height: 1em;
|
|
color: var(--text-dim);
|
|
font-family: var(--font-mono);
|
|
}
|
|
.confirm-field.is-invalid ~ .confirm-hint {
|
|
color: var(--danger);
|
|
}
|
|
.confirm-field.is-valid ~ .confirm-hint {
|
|
color: var(--success-text);
|
|
}
|
|
/* 불일치로 확정 버튼 누르려 하면 흔들림 (JS 로 .shake 토글) */
|
|
.confirm-field.shake {
|
|
animation: shake 0.32s var(--ease);
|
|
}
|
|
@keyframes shake {
|
|
0%, 100% { transform: translateX(0); }
|
|
20% { transform: translateX(-6px); }
|
|
40% { transform: translateX(6px); }
|
|
60% { transform: translateX(-4px); }
|
|
80% { transform: translateX(4px); }
|
|
}
|
|
.modal-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-top: 22px;
|
|
}
|
|
.modal-actions button {
|
|
width: auto;
|
|
margin: 0;
|
|
padding: 11px 20px;
|
|
font-size: 15px;
|
|
}
|
|
|
|
/* ── 동기화 진행률 바 ── */
|
|
.sync-progress {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 5px;
|
|
width: 150px;
|
|
}
|
|
.sync-progress progress {
|
|
margin: 0;
|
|
width: 100%;
|
|
height: 6px;
|
|
border-radius: 999px;
|
|
}
|
|
.sync-progress small {
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
font-feature-settings: "tnum" 1;
|
|
}
|
|
/* 진행 중인 결정형 바 위에 흐르는 shimmer — 값(width)은 progress 가 담당,
|
|
shimmer 는 "지금 살아있음"을 보여주는 오버레이 */
|
|
.sync-progress {
|
|
position: relative;
|
|
}
|
|
.sync-progress.is-active progress::after {
|
|
content: "";
|
|
}
|
|
.sync-progress .sync-head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
}
|
|
.sync-progress .sync-pct {
|
|
color: var(--accent);
|
|
font-weight: 700;
|
|
}
|
|
/* 값을 모르는 초기 상태용 인디터미닛 바 */
|
|
.sync-progress.indeterminate progress {
|
|
background:
|
|
linear-gradient(90deg, transparent, var(--accent), transparent) no-repeat,
|
|
var(--pico-progress-background-color, var(--surface));
|
|
background-size: 40% 100%;
|
|
animation: indeterminate 1.2s var(--ease) infinite;
|
|
}
|
|
@keyframes indeterminate {
|
|
0% { background-position: -40% 0; }
|
|
100% { background-position: 140% 0; }
|
|
}
|
|
|
|
/* ── 연결 상태 칩 (dot + 텍스트) ── */
|
|
.status-line {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 12px;
|
|
border-radius: 999px;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
background: var(--surface);
|
|
}
|
|
.status-line::before {
|
|
content: "";
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
flex: none;
|
|
}
|
|
.status-ok {
|
|
color: var(--success-text);
|
|
background: var(--success-soft);
|
|
}
|
|
.status-ok::before {
|
|
animation: dot-pulse 2.4s var(--ease) infinite;
|
|
}
|
|
.status-bad {
|
|
color: var(--danger);
|
|
background: var(--danger-soft);
|
|
}
|
|
.status-loading {
|
|
color: var(--text-muted);
|
|
}
|
|
.status-loading::before {
|
|
animation: blink 1s steps(2, start) infinite;
|
|
}
|
|
@keyframes blink {
|
|
50% { opacity: 0.3; }
|
|
}
|
|
|
|
/* ── 검색 툴바 / 정렬 헤더 / 페이저 ── */
|
|
.list-toolbar {
|
|
margin-bottom: 14px;
|
|
position: relative;
|
|
}
|
|
/* 선택자에 input[type=search] 를 포함해 특정성 (0,3,1) 확보.
|
|
Pico v2 의 검색 아이콘/패딩 규칙(input:not(...)[type=search], 0,2,1)을 이겨야
|
|
기본 돋보기 배경이 사라지고 인라인 .search-ico 만 남는다(아이콘 중복 제거). */
|
|
.list-toolbar input[type="search"].repo-search {
|
|
width: 100%;
|
|
margin: 0;
|
|
font-size: 14px;
|
|
height: auto;
|
|
/* 좌측 돋보기 아이콘 자리 확보 (아이콘 left:13 + 16px → 텍스트 40px 부터) */
|
|
padding: 0.62rem 14px 0.62rem 40px;
|
|
/* Pico 기본 [type=search] 돋보기 배경 아이콘 제거 (인라인 .search-ico 사용) */
|
|
background-image: none;
|
|
}
|
|
.list-toolbar .search-ico {
|
|
position: absolute;
|
|
left: 13px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 16px;
|
|
height: 16px;
|
|
color: var(--text-dim);
|
|
pointer-events: none;
|
|
}
|
|
.table-card thead th a.th-sort {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
color: inherit;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
transition: color var(--dur-fast) ease;
|
|
}
|
|
.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;
|
|
transition: transform var(--dur) var(--ease);
|
|
display: inline-block;
|
|
}
|
|
.th-sort.is-active.desc .sort-ind {
|
|
transform: rotate(180deg);
|
|
}
|
|
.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;
|
|
}
|
|
.pager button {
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
/* ── 빈 상태 / 에러 상태 ── */
|
|
.empty-note {
|
|
padding: 48px 16px;
|
|
color: var(--text-muted);
|
|
text-align: center;
|
|
}
|
|
.empty-note .empty-ico {
|
|
width: 40px;
|
|
height: 40px;
|
|
margin: 0 auto 12px;
|
|
color: var(--text-dim);
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
.empty-note .empty-title {
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
font-size: 15px;
|
|
}
|
|
.empty-note .empty-sub {
|
|
font-size: 13px;
|
|
margin-top: 4px;
|
|
}
|
|
.error-note {
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
background: var(--danger-soft);
|
|
color: var(--danger);
|
|
border: 1px solid color-mix(in srgb, var(--danger) 22%, transparent);
|
|
font-weight: 500;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
/* ── 스켈레톤 로딩 (표 초기/재조회) ── */
|
|
.skeleton {
|
|
background: linear-gradient(
|
|
90deg,
|
|
var(--surface) 25%,
|
|
color-mix(in srgb, var(--surface) 60%, var(--bg)) 37%,
|
|
var(--surface) 63%
|
|
);
|
|
background-size: 400% 100%;
|
|
border-radius: 6px;
|
|
animation: skeleton 1.4s ease infinite;
|
|
}
|
|
@keyframes skeleton {
|
|
0% { background-position: 100% 0; }
|
|
100% { background-position: 0 0; }
|
|
}
|
|
.skeleton-row {
|
|
height: 16px;
|
|
}
|
|
|
|
/* HTMX 요청 중 페이드 (hx-swap 대상에 자동 적용) */
|
|
.htmx-swapping {
|
|
opacity: 0.4;
|
|
transition: opacity var(--dur) ease;
|
|
}
|
|
tr.htmx-request .row-action button {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.summary {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
.deploy-preview {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.deploy-preview .pv-arrow {
|
|
transform: rotate(90deg);
|
|
}
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
모션 최소화 선호 존중 (design-ref §6 취지)
|
|
───────────────────────────────────────────────────────────── */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.001ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.001ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
.summary .card:hover {
|
|
transform: none;
|
|
}
|
|
}
|