/* ============================================================ Framework — A minimal, indie CSS framework Typography: Pretendard | Palette: Neutral ============================================================ */ @import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-dynamic-subset.min.css"); /* ── Tokens ─────────────────────────────────────────────── */ :root { /* Palette */ --neutral-50: oklch(0.985 0 0); --neutral-100: oklch(0.97 0 0); --neutral-200: oklch(0.922 0 0); --neutral-300: oklch(0.87 0 0); --neutral-400: oklch(0.715 0 0); --neutral-500: oklch(0.556 0 0); --neutral-600: oklch(0.439 0 0); --neutral-700: oklch(0.371 0 0); --neutral-800: oklch(0.269 0 0); --neutral-900: oklch(0.205 0 0); --neutral-950: oklch(0.145 0 0); /* Semantic */ --color-bg: var(--neutral-50); --color-surface: #ffffff; --color-surface-2: var(--neutral-100); --color-border: var(--neutral-200); --color-border-strong: var(--neutral-300); --color-text: var(--neutral-900); --color-text-2: var(--neutral-600); --color-text-3: var(--neutral-400); --color-accent: var(--neutral-900); --color-accent-text: var(--neutral-50); --color-focus: var(--neutral-900); /* Typography */ --font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, sans-serif; --font-mono: "SF Mono", "Fira Code", "Consolas", monospace; --text-xs: 0.75rem; /* 12px */ --text-sm: 0.875rem; /* 14px */ --text-base: 1rem; /* 16px */ --text-md: 1.0625rem; /* 17px */ --text-lg: 1.1875rem; /* 19px */ --text-xl: 1.4375rem; /* 23px */ --text-2xl: 1.8125rem; /* 29px */ --text-3xl: 2.375rem; /* 38px */ --text-4xl: 3.125rem; /* 50px */ --leading-tight: 1.2; --leading-snug: 1.4; --leading-normal: 1.6; --leading-loose: 1.8; --tracking-tight: -0.03em; --tracking-snug: -0.02em; --tracking-normal: -0.01em; --tracking-wide: 0.04em; --tracking-wider: 0.08em; /* Spacing */ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem; --space-16: 4rem; --space-20: 5rem; --space-24: 6rem; /* Radius */ --radius-sm: 4px; --radius-md: 7px; --radius-lg: 11px; --radius-xl: 16px; --radius-full: 9999px; /* Shadows */ --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05); --shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.08), 0 1px 2px -1px oklch(0 0 0 / 0.06); --shadow-md: 0 4px 12px -2px oklch(0 0 0 / 0.08), 0 2px 6px -2px oklch(0 0 0 / 0.05); --shadow-lg: 0 12px 32px -4px oklch(0 0 0 / 0.1), 0 4px 12px -4px oklch(0 0 0 / 0.06); --shadow-xl: 0 24px 48px -8px oklch(0 0 0 / 0.14), 0 8px 24px -8px oklch(0 0 0 / 0.08); /* Transitions */ --ease: cubic-bezier(0.16, 1, 0.3, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --transition: 150ms var(--ease); --transition-slow: 280ms var(--ease); } /* ── Dark mode ──────────────────────────────────────────── */ [data-theme="light"] { color-scheme: light; --color-bg: var(--neutral-50); --color-surface: #ffffff; --color-surface-2: var(--neutral-100); --color-border: var(--neutral-200); --color-border-strong: var(--neutral-300); --color-text: var(--neutral-900); --color-text-2: var(--neutral-600); --color-text-3: var(--neutral-400); --color-accent: var(--neutral-900); --color-accent-text: var(--neutral-50); --color-focus: var(--neutral-900); --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05); --shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.08), 0 1px 2px -1px oklch(0 0 0 / 0.06); --shadow-md: 0 4px 12px -2px oklch(0 0 0 / 0.08), 0 2px 6px -2px oklch(0 0 0 / 0.05); --shadow-lg: 0 12px 32px -4px oklch(0 0 0 / 0.1), 0 4px 12px -4px oklch(0 0 0 / 0.06); --shadow-xl: 0 24px 48px -8px oklch(0 0 0 / 0.14), 0 8px 24px -8px oklch(0 0 0 / 0.08); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } :root:not([data-theme="light"]) { --color-bg: var(--neutral-950); --color-surface: var(--neutral-900); --color-surface-2: var(--neutral-800); --color-border: var(--neutral-800); --color-border-strong: var(--neutral-700); --color-text: var(--neutral-50); --color-text-2: var(--neutral-400); --color-text-3: var(--neutral-600); --color-accent: var(--neutral-50); --color-accent-text: var(--neutral-950); --color-focus: var(--neutral-200); --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.3); --shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.4), 0 1px 2px -1px oklch(0 0 0 / 0.3); --shadow-md: 0 4px 12px -2px oklch(0 0 0 / 0.5), 0 2px 6px -2px oklch(0 0 0 / 0.4); --shadow-lg: 0 12px 32px -4px oklch(0 0 0 / 0.6), 0 4px 12px -4px oklch(0 0 0 / 0.4); --shadow-xl: 0 24px 48px -8px oklch(0 0 0 / 0.7), 0 8px 24px -8px oklch(0 0 0 / 0.5); } } [data-theme="dark"] { color-scheme: dark; --color-bg: var(--neutral-950); --color-surface: var(--neutral-900); --color-surface-2: var(--neutral-800); --color-border: var(--neutral-800); --color-border-strong: var(--neutral-700); --color-text: var(--neutral-50); --color-text-2: var(--neutral-400); --color-text-3: var(--neutral-600); --color-accent: var(--neutral-50); --color-accent-text: var(--neutral-950); --color-focus: var(--neutral-200); --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.3); --shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.4), 0 1px 2px -1px oklch(0 0 0 / 0.3); --shadow-md: 0 4px 12px -2px oklch(0 0 0 / 0.5), 0 2px 6px -2px oklch(0 0 0 / 0.4); --shadow-lg: 0 12px 32px -4px oklch(0 0 0 / 0.6), 0 4px 12px -4px oklch(0 0 0 / 0.4); --shadow-xl: 0 24px 48px -8px oklch(0 0 0 / 0.7), 0 8px 24px -8px oklch(0 0 0 / 0.5); } /* ── Reset & Base ───────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; -webkit-text-size-adjust: 100%; tab-size: 4; scroll-behavior: smooth; } body { font-family: var(--font-sans); font-size: var(--text-base); font-weight: 400; line-height: var(--leading-normal); letter-spacing: var(--tracking-normal); color: var(--color-text); background-color: var(--color-bg); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } /* ── Typography ─────────────────────────────────────────── */ h1, h2, h3, h4, h5, h6 { font-family: var(--font-sans); font-weight: 600; line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--color-text); } h1 { font-size: var(--text-4xl); letter-spacing: -0.04em; font-weight: 700; } h2 { font-size: var(--text-3xl); letter-spacing: -0.03em; } h3 { font-size: var(--text-2xl); letter-spacing: -0.025em; } h4 { font-size: var(--text-xl); letter-spacing: -0.02em; } h5 { font-size: var(--text-lg); letter-spacing: -0.015em; } h6 { font-size: var(--text-base); letter-spacing: var(--tracking-normal); } p { line-height: var(--leading-normal); color: var(--color-text-2); max-width: 68ch; } p + p { margin-top: var(--space-4); } small, .text-sm { font-size: var(--text-sm); } .text-xs { font-size: var(--text-xs); } .text-lg { font-size: var(--text-lg); } .text-xl { font-size: var(--text-xl); } .text-muted { color: var(--color-text-2); } .text-faint { color: var(--color-text-3); } .text-strong { color: var(--color-text); } strong, b { font-weight: 600; color: var(--color-text); } em, i { font-style: italic; } s, del { text-decoration: line-through; color: var(--color-text-3); } a { color: var(--color-text); text-decoration: underline; text-decoration-color: var(--color-border-strong); text-underline-offset: 3px; transition: text-decoration-color var(--transition); } a:hover { text-decoration-color: var(--color-text); } code, kbd, samp, pre { font-family: var(--font-mono); font-size: 0.875em; } code { background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 1px 5px; letter-spacing: 0; } pre { background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); overflow-x: auto; font-size: var(--text-sm); line-height: 1.7; } pre code { background: none; border: none; padding: 0; font-size: inherit; } kbd { background: var(--color-surface-2); border: 1px solid var(--color-border); border-bottom-width: 2px; border-radius: var(--radius-sm); padding: 1px 6px; font-size: var(--text-xs); color: var(--color-text-2); } blockquote { border-left: 2px solid var(--color-border-strong); padding: var(--space-1) 0 var(--space-1) var(--space-5); color: var(--color-text-2); font-style: italic; } hr { border: none; border-top: 1px solid var(--color-border); margin: var(--space-8) 0; } ul, ol { padding-left: var(--space-5); color: var(--color-text-2); } li { line-height: var(--leading-normal); margin-top: var(--space-1); } li:first-child { margin-top: 0; } label { font-size: var(--text-sm); font-weight: 500; color: var(--color-text); letter-spacing: var(--tracking-normal); display: block; margin-bottom: var(--space-2); } .label-hint { font-size: var(--text-xs); color: var(--color-text-3); font-weight: 400; margin-left: var(--space-2); } /* ── Layout — Container ─────────────────────────────────── */ .container { width: 100%; max-width: 1120px; margin-inline: auto; padding-inline: var(--space-6); } .container-sm { max-width: 640px; } .container-md { max-width: 800px; } .container-lg { max-width: 1120px; } .container-xl { max-width: 1400px; } @media (max-width: 640px) { .container { padding-inline: var(--space-4); } } /* ── Layout — Grid ──────────────────────────────────────── */ .grid { display: grid; gap: var(--space-6); } .grid-2 { grid-template-columns: repeat(2, 1fr); } .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-4 { grid-template-columns: repeat(4, 1fr); } .grid-auto { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } @media (max-width: 1024px) { .grid-3 { grid-template-columns: repeat(2, 1fr); } .grid-4 { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 640px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } .grid { gap: var(--space-4); } } /* ── Layout — Stack & Cluster ───────────────────────────── */ .stack { display: flex; flex-direction: column; } .stack-1 { gap: var(--space-1); } .stack-2 { gap: var(--space-2); } .stack-3 { gap: var(--space-3); } .stack-4 { gap: var(--space-4); } .stack-6 { gap: var(--space-6); } .stack-8 { gap: var(--space-8); } .stack-12 { gap: var(--space-12); } .cluster { display: flex; flex-wrap: wrap; align-items: center; } .cluster-2 { gap: var(--space-2); } .cluster-3 { gap: var(--space-3); } .cluster-4 { gap: var(--space-4); } /* ── Landmarks & Sections ───────────────────────────────── */ .section { padding-block: var(--space-16); } .section-sm { padding-block: var(--space-10); } .section-lg { padding-block: var(--space-24); } .section-header { margin-bottom: var(--space-10); } .section-header h2, .section-header h3 { margin-bottom: var(--space-2); } .section-label { display: inline-block; font-size: var(--text-xs); font-weight: 500; letter-spacing: var(--tracking-wider); text-transform: uppercase; color: var(--color-text-3); margin-bottom: var(--space-3); } /* ── Nav ────────────────────────────────────────────────── */ .nav { position: sticky; top: 0; z-index: 100; background-color: color-mix(in oklch, var(--color-bg) 85%, transparent); backdrop-filter: blur(12px) saturate(1.4); -webkit-backdrop-filter: blur(12px) saturate(1.4); } .nav-inner { display: flex; align-items: center; gap: var(--space-6); height: 54px; padding-inline: var(--space-6); max-width: 1120px; margin-inline: auto; } .nav-brand { font-size: var(--text-base); font-weight: 600; letter-spacing: var(--tracking-tight); color: var(--color-text); text-decoration: none; flex-shrink: 0; } .nav-brand:hover { text-decoration: none; color: var(--color-text); } .nav-links { display: flex; align-items: center; gap: var(--space-1); list-style: none; padding: 0; margin: 0; } .nav-links li { margin-top: 0; } .nav-links a { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-3); font-size: var(--text-sm); font-weight: 450; color: var(--color-text-2); text-decoration: none; border-radius: var(--radius-md); letter-spacing: var(--tracking-normal); transition: color var(--transition); } .nav-links a:hover { color: var(--color-text); } .nav-links a.active { color: var(--color-text); font-weight: 500; } .nav-spacer { flex: 1; } .nav-actions { display: flex; align-items: center; gap: var(--space-2); } /* ── Buttons ────────────────────────────────────────────── */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 9px var(--space-5); font-family: var(--font-sans); font-size: var(--text-base); font-weight: 500; line-height: 1; letter-spacing: var(--tracking-normal); border: 1px solid transparent; border-radius: var(--radius-md); cursor: pointer; transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), opacity var(--transition); user-select: none; white-space: nowrap; text-decoration: none; outline: none; } .btn:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; } .btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; } /* Primary */ .btn-primary { background: var(--color-accent); color: var(--color-accent-text); border-color: var(--color-accent); } .btn-primary:hover { background: color-mix(in oklch, var(--color-accent), var(--color-bg) 18%); border-color: color-mix(in oklch, var(--color-accent), var(--color-bg) 18%); } .btn-primary:active { background: color-mix(in oklch, var(--color-accent), var(--color-bg) 36%); border-color: color-mix(in oklch, var(--color-accent), var(--color-bg) 36%); } /* Secondary */ .btn-secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border); box-shadow: var(--shadow-xs); } .btn-secondary:hover { background: var(--color-surface-2); border-color: var(--color-border-strong); } .btn-secondary:active { background: var(--color-border); border-color: var(--color-border-strong); } /* Ghost */ .btn-ghost { background: transparent; color: var(--color-text-2); border-color: transparent; } .btn-ghost:hover { background: var(--color-surface-2); color: var(--color-text); } .btn-ghost:active { background: var(--color-border); color: var(--color-text); } /* Sizes */ .btn-xs { padding: 5px var(--space-3); font-size: var(--text-xs); border-radius: var(--radius-sm); } .btn-sm { padding: 7px var(--space-4); font-size: var(--text-sm); } .btn-lg { padding: 13px var(--space-6); font-size: var(--text-lg); } /* ── Form Controls — Shared ─────────────────────────────── */ .input, .textarea, .select { display: block; width: 100%; font-family: var(--font-sans); font-size: var(--text-sm); font-weight: 400; line-height: var(--leading-snug); color: var(--color-text); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 8px var(--space-3); transition: border-color var(--transition), box-shadow var(--transition); outline: none; appearance: none; -webkit-appearance: none; letter-spacing: var(--tracking-normal); } .input::placeholder, .textarea::placeholder { color: var(--color-text-3); } .input:hover, .textarea:hover, .select:hover { border-color: var(--color-border-strong); } .input:focus, .textarea:focus, .select:focus { border-color: var(--color-text-3); box-shadow: 0 0 0 3px oklch(from var(--color-focus) l c h / 0.12); } .input:disabled, .textarea:disabled, .select:disabled { opacity: 0.5; cursor: not-allowed; background: var(--color-surface-2); } .input.is-error, .textarea.is-error, .select.is-error { border-color: oklch(0.55 0.18 25); box-shadow: 0 0 0 3px oklch(0.55 0.18 25 / 0.1); } /* Input sizes */ .input-sm { padding: 5px 10px; font-size: var(--text-xs); } .input-lg { padding: 11px var(--space-4); font-size: var(--text-base); } /* ── Textarea ────────────────────────────────────────────── */ .textarea { resize: vertical; min-height: 100px; line-height: var(--leading-normal); } /* ── Select ──────────────────────────────────────────────── */ .select { cursor: pointer; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%23888' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M1 1l4 4 4-4'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right var(--space-3) center; padding-right: var(--space-8); } /* ── Field wrapper ───────────────────────────────────────── */ .field { display: flex; flex-direction: column; gap: var(--space-2); } .field-hint { font-size: var(--text-xs); color: var(--color-text-3); line-height: var(--leading-snug); } .field-error { font-size: var(--text-xs); color: oklch(0.55 0.18 25); } /* Input with icon/addon */ .input-group { position: relative; } .input-group .input { padding-left: var(--space-8); } .input-group .input-icon { position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%); color: var(--color-text-3); pointer-events: none; display: flex; } /* ── Checkbox ────────────────────────────────────────────── */ .checkbox-label, .radio-label { display: inline-flex; align-items: center; gap: var(--space-3); cursor: pointer; user-select: none; font-size: var(--text-sm); font-weight: 400; color: var(--color-text); line-height: var(--leading-snug); } .checkbox-label input[type="checkbox"], .radio-label input[type="radio"] { appearance: none; -webkit-appearance: none; flex-shrink: 0; width: 16px; height: 16px; border: 1.5px solid var(--color-border-strong); background: var(--color-surface); cursor: pointer; transition: background var(--transition), border-color var(--transition), box-shadow var(--transition); position: relative; display: grid; place-items: center; } .checkbox-label input[type="checkbox"] { border-radius: var(--radius-sm); } .radio-label input[type="radio"] { border-radius: 50%; } .checkbox-label input[type="checkbox"]:checked, .radio-label input[type="radio"]:checked { background: var(--color-accent); border-color: var(--color-accent); } .checkbox-label input[type="checkbox"]:checked::after { content: ''; width: 9px; height: 5.5px; border-left: 1.5px solid var(--color-accent-text); border-bottom: 1.5px solid var(--color-accent-text); transform: rotate(-45deg) translateY(-1px); } .radio-label input[type="radio"]:checked::after { content: ''; width: 6px; height: 6px; background: var(--color-accent-text); border-radius: 50%; } .checkbox-label input[type="checkbox"]:focus-visible, .radio-label input[type="radio"]:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; } .checkbox-label input[type="checkbox"]:hover:not(:checked), .radio-label input[type="radio"]:hover:not(:checked) { border-color: var(--color-text-3); } /* ── Switch ──────────────────────────────────────────────── */ .switch-label { display: inline-flex; align-items: center; gap: var(--space-3); cursor: pointer; user-select: none; font-size: var(--text-sm); color: var(--color-text); } .switch-label input[type="checkbox"] { appearance: none; -webkit-appearance: none; position: relative; width: 34px; height: 20px; background: var(--color-border-strong); border-radius: var(--radius-full); cursor: pointer; flex-shrink: 0; transition: background var(--transition); } .switch-label input[type="checkbox"]::after { content: ''; position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; background: white; border-radius: 50%; box-shadow: var(--shadow-xs); transition: transform var(--transition), box-shadow var(--transition); } .switch-label input[type="checkbox"]:checked { background: var(--color-accent); } .switch-label input[type="checkbox"]:checked::after { transform: translateX(14px); box-shadow: var(--shadow-sm); } .switch-label input[type="checkbox"]:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; } /* ── Range ───────────────────────────────────────────────── */ .range { appearance: none; -webkit-appearance: none; width: 100%; height: 4px; background: var(--color-border); border-radius: var(--radius-full); cursor: pointer; outline: none; accent-color: var(--color-accent); } .range::-webkit-slider-thumb { appearance: none; -webkit-appearance: none; width: 16px; height: 16px; background: var(--color-accent); border-radius: 50%; cursor: pointer; box-shadow: 0 0 0 3px var(--color-bg), var(--shadow-sm); transition: transform var(--transition), box-shadow var(--transition); } .range::-moz-range-thumb { width: 16px; height: 16px; background: var(--color-accent); border-radius: 50%; cursor: pointer; border: none; box-shadow: 0 0 0 3px var(--color-bg), var(--shadow-sm); } .range:hover::-webkit-slider-thumb { transform: scale(1.1); } .range:focus-visible { outline: none; } .range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--color-bg), 0 0 0 5px var(--color-focus); } /* ── Card ────────────────────────────────────────────────── */ .card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); overflow: hidden; transition: box-shadow var(--transition-slow), border-color var(--transition-slow); } .card-hover:hover { box-shadow: var(--shadow-md); border-color: var(--color-border-strong); } .card-body { padding: var(--space-5) var(--space-6); } .card-header { padding: var(--space-4) var(--space-6); border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; } .card-footer { padding: var(--space-4) var(--space-6); border-top: 1px solid var(--color-border); background: var(--color-surface-2); } .card-title { font-size: var(--text-base); font-weight: 600; letter-spacing: var(--tracking-tight); color: var(--color-text); line-height: var(--leading-tight); } .card-description { font-size: var(--text-sm); color: var(--color-text-2); margin-top: var(--space-1); line-height: var(--leading-snug); } /* ── Badge / Tag ─────────────────────────────────────────── */ .badge { display: inline-flex; align-items: center; gap: var(--space-1); padding: 2px var(--space-2); font-size: var(--text-xs); font-weight: 500; letter-spacing: 0; border-radius: var(--radius-sm); background: var(--color-surface-2); color: var(--color-text-2); border: 1px solid var(--color-border); line-height: 1.5; } .badge-solid { background: var(--color-accent); color: var(--color-accent-text); border-color: var(--color-accent); } /* ── Group ───────────────────────────────────────────────── */ .group { display: flex; align-items: center; } .group > .btn:not(:first-child), .group > .input:not(:first-child) { border-left-width: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; } .group > .btn:not(:last-child), .group > .input:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } /* Vertical group */ .group-vertical { display: flex; flex-direction: column; } .group-vertical > * + * { border-top-width: 0; } .group-vertical > *:not(:first-child) { border-top-left-radius: 0; border-top-right-radius: 0; } .group-vertical > *:not(:last-child) { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } /* ── Dropdown (details / summary) ───────────────────────── */ details.dropdown { position: relative; display: inline-flex; } details.dropdown > summary { list-style: none; cursor: pointer; user-select: none; } details.dropdown > summary::-webkit-details-marker { display: none; } details.dropdown[open] .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0) scale(1); } .dropdown-menu { position: absolute; top: calc(100% + 6px); left: 0; min-width: 200px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: var(--space-1); z-index: 200; opacity: 0; visibility: hidden; transform: translateY(-4px) scale(0.98); transform-origin: top left; transition: opacity var(--transition), visibility var(--transition), transform var(--transition); } .dropdown-item { display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: 7px var(--space-3); font-size: var(--text-sm); font-weight: 400; color: var(--color-text-2); background: transparent; border: none; border-radius: var(--radius-md); cursor: pointer; text-align: left; text-decoration: none; transition: background var(--transition), color var(--transition); letter-spacing: var(--tracking-normal); font-family: var(--font-sans); } .dropdown-item:hover { background: var(--color-surface-2); color: var(--color-text); } .dropdown-item.active { color: var(--color-text); font-weight: 500; } .dropdown-item.danger { color: oklch(0.55 0.18 25); } .dropdown-item.danger:hover { background: oklch(0.55 0.18 25 / 0.08); } .dropdown-divider { height: 1px; background: var(--color-border); margin: var(--space-1); } .dropdown-label { padding: var(--space-1) var(--space-3) 4px; font-size: var(--text-xs); font-weight: 500; letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--color-text-3); } /* ── Accordion (details / summary) ──────────────────────── */ .accordion { border: 1px solid var(--color-border); border-radius: var(--radius-lg); overflow: hidden; } .accordion details + details { border-top: 1px solid var(--color-border); } .accordion details > summary { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: var(--space-4) var(--space-5); font-family: var(--font-sans); font-size: var(--text-sm); font-weight: 500; color: var(--color-text); background: transparent; cursor: pointer; text-align: left; transition: background var(--transition); letter-spacing: var(--tracking-normal); list-style: none; user-select: none; } .accordion details > summary::-webkit-details-marker { display: none; } .accordion details > summary:hover { background: var(--color-surface-2); } .accordion-icon { flex-shrink: 0; width: 16px; height: 16px; color: var(--color-text-3); transition: transform var(--transition); } .accordion details[open] > summary .accordion-icon { transform: rotate(180deg); } @keyframes accordionIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } } .accordion details[open] > .accordion-body { animation: accordionIn var(--transition) var(--ease); } .accordion-body { padding: 0 var(--space-5) var(--space-5); font-size: var(--text-sm); color: var(--color-text-2); line-height: var(--leading-normal); } /* ── Modal (dialog) ──────────────────────────────────────── */ dialog.modal { position: fixed; top: 50%; left: 50%; translate: -50% -50%; margin: 0; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl); width: calc(100% - var(--space-8)); max-width: 500px; max-height: 90vh; display: flex; flex-direction: column; padding: 0; overflow: hidden; opacity: 0; pointer-events: none; transform: scale(0.96) translateY(8px); transition: opacity var(--transition-slow), transform var(--transition-slow), display var(--transition-slow) allow-discrete, overlay var(--transition-slow) allow-discrete; } dialog.modal[open] { opacity: 1; pointer-events: auto; transform: scale(1) translateY(0); } @starting-style { dialog.modal[open] { opacity: 0; transform: scale(0.96) translateY(8px); } } dialog.modal::backdrop { background: oklch(0 0 0 / 0.35); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); transition: background var(--transition-slow), display var(--transition-slow) allow-discrete, overlay var(--transition-slow) allow-discrete; } @starting-style { dialog.modal[open]::backdrop { background: oklch(0 0 0 / 0); } } dialog.modal-sm { max-width: 360px; } dialog.modal-lg { max-width: 720px; } .modal-header { display: flex; align-items: flex-start; justify-content: space-between; padding: var(--space-5) var(--space-6); flex-shrink: 0; } .modal-title { font-size: var(--text-lg); font-weight: 600; letter-spacing: var(--tracking-tight); color: var(--color-text); line-height: var(--leading-tight); } .modal-subtitle { font-size: var(--text-sm); color: var(--color-text-2); margin-top: var(--space-1); } .modal-close { flex-shrink: 0; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; background: var(--color-surface-2); border: none; border-radius: var(--radius-md); cursor: pointer; color: var(--color-text-3); transition: background var(--transition), color var(--transition); margin-left: var(--space-4); flex-shrink: 0; } .modal-close:hover { background: var(--color-border); color: var(--color-text); } .modal-body { padding: 0 var(--space-6) var(--space-5); overflow-y: auto; flex: 1; font-size: var(--text-sm); color: var(--color-text-2); line-height: var(--leading-normal); } .modal-footer { padding: var(--space-4) var(--space-6); border-top: 1px solid var(--color-border); display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); flex-shrink: 0; background: var(--color-surface-2); } /* ── Loading ─────────────────────────────────────────────── */ @keyframes spin { to { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } } @keyframes shimmer { from { background-position: -400px 0; } to { background-position: 400px 0; } } .spinner { display: inline-block; width: 18px; height: 18px; border: 2px solid var(--color-border); border-top-color: var(--color-text); border-radius: 50%; animation: spin 0.65s linear infinite; flex-shrink: 0; } .spinner-sm { width: 14px; height: 14px; border-width: 1.5px; } .spinner-lg { width: 28px; height: 28px; border-width: 2.5px; } .skeleton { background: linear-gradient( 90deg, var(--color-surface-2) 25%, var(--color-border) 50%, var(--color-surface-2) 75% ); background-size: 400px 100%; animation: shimmer 1.4s ease infinite; border-radius: var(--radius-sm); display: block; } .skeleton-text { height: 14px; } .skeleton-text + .skeleton-text { margin-top: var(--space-2); } .skeleton-text.w-3\/4 { width: 75%; } .skeleton-text.w-1\/2 { width: 50%; } .skeleton-text.w-full { width: 100%; } .skeleton-circle { border-radius: 50%; width: 36px; height: 36px; flex-shrink: 0; } .skeleton-card { height: 120px; border-radius: var(--radius-lg); } .loading-dots { display: inline-flex; align-items: center; gap: 4px; } .loading-dots span { display: inline-block; width: 5px; height: 5px; background: currentColor; border-radius: 50%; animation: pulse 1.2s ease-in-out infinite; } .loading-dots span:nth-child(2) { animation-delay: 0.2s; } .loading-dots span:nth-child(3) { animation-delay: 0.4s; } /* ── Table ───────────────────────────────────────────────── */ .table-wrap { width: 100%; overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius-lg); } table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); } thead th { text-align: left; font-size: var(--text-xs); font-weight: 500; letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--color-text-3); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border); background: var(--color-surface-2); white-space: nowrap; } thead th:first-child { border-top-left-radius: var(--radius-lg); } thead th:last-child { border-top-right-radius: var(--radius-lg); } tbody td { padding: 11px var(--space-4); border-bottom: 1px solid var(--color-border); color: var(--color-text-2); vertical-align: middle; } tbody tr:last-child td { border-bottom: none; } tbody tr:hover td { background: var(--color-surface-2); } /* ── Avatar ──────────────────────────────────────────────── */ .avatar { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: var(--color-surface-2); border: 1px solid var(--color-border); font-size: var(--text-xs); font-weight: 600; color: var(--color-text-2); overflow: hidden; flex-shrink: 0; letter-spacing: 0; } .avatar img { width: 100%; height: 100%; object-fit: cover; } .avatar-sm { width: 24px; height: 24px; font-size: 10px; } .avatar-lg { width: 44px; height: 44px; font-size: var(--text-base); } .avatar-xl { width: 60px; height: 60px; font-size: var(--text-lg); } /* ── Alert / Banner ──────────────────────────────────────── */ .alert { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface-2); font-size: var(--text-sm); color: var(--color-text-2); } .alert-icon { flex-shrink: 0; margin-top: 1px; } /* ── Divider with label ──────────────────────────────────── */ .divider { display: flex; align-items: center; gap: var(--space-4); color: var(--color-text-3); font-size: var(--text-xs); } .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--color-border); } /* ── Utilities ───────────────────────────────────────────── */ .w-full { width: 100%; } .flex { display: flex; } .inline-flex { display: inline-flex; } .items-center { align-items: center; } .items-start { align-items: flex-start; } .justify-center { justify-content: center; } .justify-between { justify-content: space-between; } .flex-1 { flex: 1; } .flex-wrap { flex-wrap: wrap; } .gap-1 { gap: var(--space-1); } .gap-2 { gap: var(--space-2); } .gap-3 { gap: var(--space-3); } .gap-4 { gap: var(--space-4); } .gap-6 { gap: var(--space-6); } .gap-8 { gap: var(--space-8); } .mt-1 { margin-top: var(--space-1); } .mt-2 { margin-top: var(--space-2); } .mt-3 { margin-top: var(--space-3); } .mt-4 { margin-top: var(--space-4); } .mt-6 { margin-top: var(--space-6); } .mt-8 { margin-top: var(--space-8); } .mt-12 { margin-top: var(--space-12); } .mb-1 { margin-bottom: var(--space-1); } .mb-2 { margin-bottom: var(--space-2); } .mb-3 { margin-bottom: var(--space-3); } .mb-4 { margin-bottom: var(--space-4); } .mb-6 { margin-bottom: var(--space-6); } .mb-8 { margin-bottom: var(--space-8); } .rounded-sm { border-radius: var(--radius-sm); } .rounded-md { border-radius: var(--radius-md); } .rounded-lg { border-radius: var(--radius-lg); } .rounded-full { border-radius: var(--radius-full); } .shadow-sm { box-shadow: var(--shadow-sm); } .shadow-md { box-shadow: var(--shadow-md); } .shadow-lg { box-shadow: var(--shadow-lg); } .bg-surface { background: var(--color-surface); } .bg-surface-2 { background: var(--color-surface-2); } .border { border: 1px solid var(--color-border); } .opacity-50 { opacity: 0.5; } .cursor-not-allowed { cursor: not-allowed; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } /* ── Focus styles ────────────────────────────────────────── */ :focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; } /* ── Scrollbar ───────────────────────────────────────────── */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: var(--radius-full); } ::-webkit-scrollbar-thumb:hover { background: var(--color-text-3); } /* ── Selection ───────────────────────────────────────────── */ ::selection { background: oklch(from var(--color-accent) l c h / 0.15); } /* ── Responsive ──────────────────────────────────────────── */ /* Tablet (≤768px) */ @media (max-width: 768px) { /* Typography */ h1 { font-size: var(--text-3xl); } h2 { font-size: var(--text-2xl); } h3 { font-size: var(--text-xl); } /* Sections */ .section { padding-block: var(--space-12); } .section-lg { padding-block: var(--space-16); } .section-sm { padding-block: var(--space-8); } /* Nav — hide links, show hamburger toggle */ .nav-links { display: none; } .nav-inner { gap: var(--space-3); padding-inline: var(--space-4); } .nav-hamburger { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: transparent; border: none; border-radius: var(--radius-md); color: var(--color-text-2); cursor: pointer; transition: background var(--transition), color var(--transition); } .nav-hamburger:hover { background: var(--color-surface-2); color: var(--color-text); } /* Mobile nav drawer */ .nav-drawer { display: block; position: fixed; inset: 54px 0 0 0; background: color-mix(in oklch, var(--color-bg) 96%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-top: 1px solid var(--color-border); padding: var(--space-4); z-index: 99; opacity: 0; visibility: hidden; transform: translateY(-8px); transition: opacity var(--transition), visibility var(--transition), transform var(--transition); } .nav-drawer.open { opacity: 1; visibility: visible; transform: translateY(0); } .nav-drawer-links { list-style: none; padding: 0; margin: 0 0 var(--space-4); } .nav-drawer-links li { margin-top: 0; } .nav-drawer-links a { display: block; padding: var(--space-3) var(--space-3); font-size: var(--text-base); font-weight: 450; color: var(--color-text-2); text-decoration: none; border-radius: var(--radius-md); transition: background var(--transition), color var(--transition); } .nav-drawer-links a:hover, .nav-drawer-links a.active { background: var(--color-surface-2); color: var(--color-text); } .nav-drawer-actions { display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-4); border-top: 1px solid var(--color-border); } .nav-drawer-actions .btn { width: 100%; justify-content: center; } } /* Mobile (≤640px) */ @media (max-width: 640px) { /* Typography */ h1 { font-size: var(--text-2xl); letter-spacing: -0.02em; } h2 { font-size: var(--text-xl); } h3 { font-size: var(--text-lg); } /* Sections */ .section { padding-block: var(--space-10); } .section-lg { padding-block: var(--space-12); } .section-sm { padding-block: var(--space-6); } /* Section header */ .section-header { margin-bottom: var(--space-8); } /* Nav actions — hide on small screens (only hamburger + brand) */ .nav-actions { display: none; } /* Stack on mobile */ .sm\:stack { flex-direction: column; align-items: stretch; } .sm\:stack > * { width: 100%; justify-content: center; } /* Full width button on mobile */ .sm\:w-full { width: 100%; } /* Modal */ dialog.modal { width: 100%; max-width: 100%; max-height: 85vh; top: auto; bottom: 0; left: 0; translate: 0 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; transform: translateY(16px); } dialog.modal[open] { transform: translateY(0); } @starting-style { dialog.modal[open] { transform: translateY(16px); opacity: 0; } } /* Table — allow horizontal scroll on small screens */ .table-wrap { border-radius: var(--radius-md); } /* Pre — tighten padding */ pre { padding: var(--space-3) var(--space-4); font-size: var(--text-xs); } /* Card footer — stack buttons */ .card-footer { flex-wrap: wrap; } } /* Desktop-only: hide hamburger */ @media (min-width: 769px) { .nav-hamburger { display: none; } .nav-drawer { display: none !important; } }