Files
lanspread/design/design_reference/styles.css
T
ddidderr e308009a08 docs(design): specify download progress treatment
Document and mock the redesigned downloading state for the launcher. The
reference now replaces the action button slot with a dedicated progress
primitive, covers both card and detail-modal layouts, and records the sizing,
number formatting, container-query fallback, and sample-data expectations that
implementation work should follow.

This commit keeps the design package separate from application code so the
next UI/backend changes can be reviewed against a stable reference.

Test Plan:
- git diff --cached --check

Refs: local design reference update
2026-05-20 23:09:46 +02:00

1127 lines
30 KiB
CSS

/* SoftLAN Launcher — styles
Steam-like dark UI, blue accent (configurable). System sans for UI,
Bebas Neue for cover-art display type.
*/
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap');
:root {
--accent: #3b82f6;
--bg-0: #0a0e13;
--bg-1: #0f151c;
--bg-2: #131b25;
--bg-3: #1a2330;
--bg-4: #232f3e;
--bd-1: rgba(255,255,255,0.06);
--bd-2: rgba(255,255,255,0.10);
--bd-3: rgba(255,255,255,0.16);
--t-1: #e6edf3;
--t-2: #9aa6b4;
--t-3: #6b7785;
--t-4: #4a5663;
--ok: #22c55e;
--warn: #f59e0b;
--danger: #ef4444;
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", Inter, system-ui, sans-serif;
--font-display: "Bebas Neue", "Oswald", Impact, "Arial Narrow Bold", sans-serif;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
}
* { box-sizing: border-box; }
/* ─── Launcher root ─── */
.launcher {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: var(--bg-0);
color: var(--t-1);
font-family: var(--font-ui);
font-size: 13px;
line-height: 1.4;
overflow: hidden;
position: relative;
isolation: isolate;
}
/* Background variants */
.bg-flat { background: var(--bg-0); }
.bg-gradient {
background:
radial-gradient(ellipse 80% 50% at 50% -10%, color-mix(in srgb, var(--accent) 22%, transparent) 0%, transparent 60%),
linear-gradient(180deg, #0c1218 0%, var(--bg-0) 100%);
}
.bg-animated {
background:
radial-gradient(ellipse 60% 40% at 20% 0%, color-mix(in srgb, var(--accent) 24%, transparent) 0%, transparent 55%),
radial-gradient(ellipse 55% 40% at 85% 8%, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 55%),
linear-gradient(180deg, #0c1218 0%, var(--bg-0) 100%);
background-size: 100% 100%;
animation: bgshift 18s ease-in-out infinite alternate;
}
@keyframes bgshift {
0% { background-position: 0% 0%, 0% 0%, 0% 0%; }
100% { background-position: 10% 4%, -6% 2%, 0% 0%; }
}
/* ─── Top bar — shared ─── */
.topbar {
position: relative;
z-index: 10;
background: rgba(10,14,19,0.65);
-webkit-backdrop-filter: blur(20px) saturate(140%);
backdrop-filter: blur(20px) saturate(140%);
border-bottom: 1px solid var(--bd-1);
}
/* Variant 1: single row */
.topbar-single {
display: flex;
align-items: center;
gap: 18px;
padding: 14px 24px;
flex-wrap: nowrap;
}
/* Variant 2: two row */
.topbar-two .topbar-row {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 24px;
}
.topbar-two .topbar-row1 {
border-bottom: 1px solid var(--bd-1);
}
.topbar-two .topbar-row1-right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
.topbar-two .topbar-row2 { padding-top: 4px; padding-bottom: 4px; }
.topbar-two .topbar-row2-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
/* ─── Brand ─── */
.brand {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.brand-mark {
width: 28px; height: 28px;
border-radius: 7px;
display: grid; place-items: center;
font-family: var(--font-display);
font-size: 20px;
letter-spacing: 0.02em;
color: white;
box-shadow: 0 6px 20px -6px color-mix(in srgb, var(--accent) 60%, black), inset 0 1px 0 rgba(255,255,255,0.22);
}
.brand-name {
font-weight: 700;
font-size: 15px;
letter-spacing: -0.005em;
color: var(--t-1);
}
.brand-name-soft { color: var(--t-3); font-weight: 500; margin-left: 4px; }
/* ─── Segmented filters (variant 1) ─── */
.seg {
position: relative;
display: inline-flex;
background: var(--bg-2);
border: 1px solid var(--bd-1);
border-radius: 999px;
padding: 4px;
flex-shrink: 0;
}
.seg-thumb {
position: absolute;
top: 4px; bottom: 4px;
border-radius: 999px;
transition: left .22s cubic-bezier(.4,1.2,.5,1), width .22s cubic-bezier(.4,1.2,.5,1), background .15s;
box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--accent) 60%, black);
}
.seg-btn {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: transparent;
color: var(--t-2);
border: 0;
border-radius: 999px;
font: inherit;
font-weight: 600;
font-size: 12.5px;
cursor: pointer;
white-space: nowrap;
transition: color .15s;
}
.seg-btn:hover { color: var(--t-1); }
.seg-btn.is-active { color: white; }
.seg-count {
font-size: 11px;
font-weight: 700;
padding: 1px 6px;
border-radius: 999px;
background: rgba(255,255,255,0.08);
color: inherit;
font-variant-numeric: tabular-nums;
}
.seg-btn.is-active .seg-count { background: rgba(0,0,0,0.25); color: white; }
/* ─── Underline filters (variant 2) ─── */
.utabs {
display: flex;
align-items: stretch;
gap: 4px;
}
.utab {
position: relative;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 14px 12px;
background: transparent;
border: 0;
color: var(--t-2);
font: inherit;
font-weight: 600;
font-size: 13.5px;
cursor: pointer;
transition: color .15s;
}
.utab:hover { color: var(--t-1); }
.utab.is-active { color: var(--t-1); }
.utab-count {
font-size: 11.5px;
font-weight: 600;
padding: 1px 7px;
border-radius: 999px;
background: rgba(255,255,255,0.06);
color: var(--t-3);
font-variant-numeric: tabular-nums;
}
.utab.is-active .utab-count { color: var(--t-1); background: rgba(255,255,255,0.10); }
.utab-underline {
position: absolute;
left: 12px; right: 12px;
bottom: 0;
height: 2px;
border-radius: 2px 2px 0 0;
opacity: 0;
transform: scaleX(0.4);
transform-origin: center;
transition: opacity .2s, transform .25s cubic-bezier(.4,1.2,.5,1);
}
.utab.is-active .utab-underline { opacity: 1; transform: scaleX(1); }
/* ─── Search ─── */
.search {
position: relative;
display: flex;
align-items: center;
gap: 8px;
padding: 0 12px;
background: var(--bg-2);
border: 1px solid var(--bd-1);
border-radius: 8px;
height: 36px;
min-width: 220px;
color: var(--t-3);
transition: border-color .15s, background .15s, box-shadow .15s;
}
.search-wide { min-width: 320px; flex: 0 1 380px; }
.search:focus-within {
border-color: color-mix(in srgb, var(--accent) 60%, var(--bd-2));
background: var(--bg-1);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
.search:focus-within { color: var(--t-1); }
.search input {
flex: 1; min-width: 0;
background: transparent; border: 0; outline: 0;
color: var(--t-1);
font: inherit;
font-size: 13px;
}
.search input::placeholder { color: var(--t-3); }
.search-kbd {
display: inline-grid; place-items: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 4px;
background: rgba(255,255,255,0.06);
border: 1px solid var(--bd-1);
font-size: 11px;
color: var(--t-3);
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.search:focus-within .search-kbd { opacity: 0.4; }
/* ─── Sort menu ─── */
.sort { position: relative; flex-shrink: 0; }
.sort-btn {
display: inline-flex; align-items: center; gap: 8px;
height: 36px; padding: 0 12px;
background: var(--bg-2);
border: 1px solid var(--bd-1);
border-radius: 8px;
color: var(--t-2);
font: inherit; font-size: 12.5px;
cursor: pointer;
transition: border-color .15s, color .15s;
}
.sort-btn:hover { color: var(--t-1); border-color: var(--bd-2); }
.sort-btn strong { color: var(--t-1); font-weight: 600; }
.sort-menu {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 50;
min-width: 200px;
padding: 4px;
background: var(--bg-3);
border: 1px solid var(--bd-2);
border-radius: 10px;
box-shadow: 0 16px 40px -8px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.04);
}
.sort-menu button {
display: flex; align-items: center; gap: 8px;
width: 100%;
padding: 8px 10px;
background: transparent;
border: 0;
border-radius: 6px;
color: var(--t-1);
font: inherit; font-size: 12.5px;
text-align: left;
cursor: pointer;
}
.sort-menu button:hover { background: rgba(255,255,255,0.06); }
.sort-check { width: 14px; display: inline-grid; place-items: center; color: var(--accent); }
/* ─── Directory button ─── */
.dirbtn {
display: inline-flex; align-items: center; gap: 8px;
height: 36px; padding: 0 12px;
background: var(--bg-2);
border: 1px solid var(--bd-1);
border-radius: 8px;
color: var(--t-2);
font: inherit; font-size: 12.5px;
cursor: pointer;
max-width: 360px;
transition: border-color .15s, color .15s;
flex-shrink: 1;
min-width: 0;
}
.dirbtn:hover { border-color: var(--bd-2); color: var(--t-1); }
.dirbtn-label { color: var(--t-1); font-weight: 600; flex-shrink: 0; }
.dirbtn-path {
color: var(--t-3);
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 11.5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
/* ─── Kebab menu ─── */
.kebab { position: relative; }
.kebab-btn {
width: 36px; height: 36px;
display: grid; place-items: center;
background: var(--bg-2);
border: 1px solid var(--bd-1);
border-radius: 8px;
color: var(--t-2);
cursor: pointer;
transition: color .15s, border-color .15s;
}
.kebab-btn:hover { color: var(--t-1); border-color: var(--bd-2); }
.kebab-menu {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 50;
min-width: 180px;
padding: 4px;
background: var(--bg-3);
border: 1px solid var(--bd-2);
border-radius: 10px;
box-shadow: 0 16px 40px -8px rgba(0,0,0,0.5);
}
.kebab-menu button {
display: block;
width: 100%;
padding: 8px 10px;
background: transparent;
border: 0;
border-radius: 6px;
color: var(--t-1);
font: inherit; font-size: 12.5px;
text-align: left;
cursor: pointer;
}
.kebab-menu button:hover { background: rgba(255,255,255,0.06); }
.kebab-sep { height: 1px; background: var(--bd-1); margin: 4px 0; }
/* ─── Storage meter ─── */
.storage {
display: flex; flex-direction: column;
gap: 6px;
min-width: 240px;
}
.storage-compact { min-width: 200px; }
.storage-bar {
position: relative;
height: 6px;
background: rgba(255,255,255,0.06);
border-radius: 3px;
overflow: hidden;
}
.storage-i { position: absolute; top: 0; left: 0; bottom: 0; }
.storage-l {
position: absolute; top: 0; bottom: 0;
left: calc((var(--installed-pct, 15.3)) * 1%);
}
.storage-compact .storage-bar { height: 4px; }
.storage-text {
display: flex; align-items: center;
gap: 10px;
font-size: 11px;
color: var(--t-3);
font-variant-numeric: tabular-nums;
}
.storage-text > span { display: inline-flex; align-items: center; gap: 5px; }
.storage-sq {
width: 8px; height: 8px;
border-radius: 2px;
}
.storage-free { margin-left: auto; color: var(--t-2); }
/* ─── Grid wrapper / results bar ─── */
.grid-wrap {
flex: 1;
overflow: auto;
padding: 18px 24px 32px;
scrollbar-width: thin;
scrollbar-color: var(--bd-3) transparent;
}
.grid-wrap::-webkit-scrollbar { width: 10px; }
.grid-wrap::-webkit-scrollbar-thumb { background: var(--bd-3); border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }
.results-bar {
display: flex; align-items: center; justify-content: space-between;
padding: 4px 4px 16px;
gap: 16px;
}
.results-count { color: var(--t-2); font-size: 12.5px; }
.results-count strong { color: var(--t-1); font-weight: 700; }
/* ─── Grid ─── */
.grid {
display: grid;
gap: var(--card-gap, 16px);
grid-template-columns: repeat(auto-fill, minmax(var(--card-min, 188px), 1fr));
}
.density-compact { --card-min: 148px; --card-gap: 12px; }
.density-normal { --card-min: 188px; --card-gap: 16px; }
.density-large { --card-min: 244px; --card-gap: 20px; }
/* ─── Card ─── */
.card {
position: relative;
display: flex;
flex-direction: column;
background: linear-gradient(180deg, var(--bg-2) 0%, var(--bg-1) 100%);
border: 1px solid var(--bd-1);
border-radius: var(--radius-md);
cursor: pointer;
overflow: hidden;
transition: transform .18s cubic-bezier(.4,1.2,.5,1), border-color .18s, box-shadow .18s;
outline: 0;
}
.card:hover, .card:focus-visible {
transform: translateY(-2px);
border-color: color-mix(in srgb, var(--accent) 45%, var(--bd-2));
box-shadow:
0 14px 30px -16px color-mix(in srgb, var(--accent) 50%, black),
0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}
.card:focus-visible {
box-shadow:
0 14px 30px -16px color-mix(in srgb, var(--accent) 50%, black),
0 0 0 2px var(--accent);
}
/* ─── Cover ─── */
.card-cover-wrap {
position: relative;
width: 100%;
overflow: hidden;
background: var(--bg-3);
}
.card-cover-wrap[data-aspect="box"] { aspect-ratio: 2 / 3; }
.card-cover-wrap[data-aspect="square"] { aspect-ratio: 1 / 1; }
.card-cover-wrap[data-aspect="banner"] { aspect-ratio: 16 / 9; }
.cover {
position: absolute; inset: 0;
overflow: hidden;
transition: transform .35s cubic-bezier(.4,1.2,.5,1);
}
.card:hover .cover { transform: scale(1.03); }
.cover-base, .cover-blob, .cover-grain, .cover-vignette, .cover-mark { position: absolute; inset: 0; pointer-events: none; }
.cover-grain {
background-image:
repeating-linear-gradient(0deg, rgba(255,255,255,0.018) 0 1px, transparent 1px 3px),
repeating-linear-gradient(90deg, rgba(0,0,0,0.04) 0 1px, transparent 1px 3px);
mix-blend-mode: overlay;
opacity: 0.7;
}
.cover-vignette {
background: linear-gradient(180deg, transparent 30%, rgba(0,0,0,0.62) 100%);
}
.cover-mark { width: 100%; height: 100%; }
.cover-titlewrap {
position: absolute;
left: 0; right: 0; bottom: 0;
z-index: 2;
padding: 14px 14px 14px;
}
.cover-title {
font-family: var(--font-display);
font-weight: 400;
letter-spacing: 0.018em;
line-height: 1.02;
text-transform: uppercase;
color: white;
overflow-wrap: normal;
word-break: normal;
}
.cover-sub {
font-family: var(--font-ui);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
opacity: 0.85;
}
/* banner mode: title centered + smaller padding */
.card-cover-wrap[data-aspect="banner"] .cover-titlewrap {
padding: 14px 18px;
}
/* ─── State chip ─── */
.state-chip {
position: absolute;
top: 10px; right: 10px;
z-index: 3;
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 9px;
border-radius: 999px;
background: rgba(8,12,16,0.78);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border: 1px solid rgba(255,255,255,0.08);
font-size: 10.5px;
font-weight: 600;
color: var(--t-1);
letter-spacing: 0.01em;
}
.state-dot { width: 6px; height: 6px; border-radius: 999px; box-shadow: 0 0 8px currentColor; }
.state-chip[data-state="installed"] .state-dot { box-shadow: 0 0 8px var(--ok); }
.state-chip[data-state="local"] .state-dot { box-shadow: 0 0 8px var(--warn); }
/* Multiplayer badge */
.card-mp {
position: absolute;
top: 10px; left: 10px;
z-index: 3;
display: inline-flex; align-items: center; gap: 4px;
padding: 4px 8px;
border-radius: 999px;
background: rgba(8,12,16,0.65);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border: 1px solid rgba(255,255,255,0.06);
font-size: 10.5px;
font-weight: 600;
color: var(--t-1);
font-variant-numeric: tabular-nums;
}
/* ─── Card body ─── */
.card-body {
padding: 11px 12px 12px;
display: flex; flex-direction: column;
gap: 8px;
}
.card-title {
font-weight: 600;
font-size: 13.5px;
color: var(--t-1);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.005em;
}
.card-meta {
display: flex; align-items: center; gap: 6px;
font-size: 11.5px;
color: var(--t-3);
font-variant-numeric: tabular-nums;
}
.card-meta .card-dot { opacity: 0.5; }
.density-compact .card-body { padding: 9px 10px 10px; gap: 6px; }
.density-compact .card-title { font-size: 12.5px; }
.density-compact .card-meta { font-size: 11px; }
.density-large .card-body { padding: 14px 14px 14px; gap: 10px; }
.density-large .card-title { font-size: 15px; }
/* ─── Action buttons ─── */
.act-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 32px;
padding: 0 14px;
border-radius: 7px;
border: 0;
font: inherit;
font-weight: 600;
font-size: 12.5px;
letter-spacing: 0.005em;
cursor: pointer;
transition: transform .12s, filter .12s, background .15s;
white-space: nowrap;
}
.act-btn:hover { filter: brightness(1.12); }
.act-btn:active { transform: scale(0.98); }
.act-full { width: 100%; }
.act-lg { height: 44px; padding: 0 22px; font-size: 14px; gap: 8px; }
.act-lg svg { width: 14px; height: 14px; }
.act-play {
color: white;
background: linear-gradient(180deg, #2bd07f 0%, #1aa460 100%);
box-shadow: 0 6px 16px -8px #1aa460, inset 0 1px 0 rgba(255,255,255,0.25);
}
.act-install {
color: white;
background: var(--accent);
box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--accent) 80%, black), inset 0 1px 0 rgba(255,255,255,0.22);
}
.act-download {
color: var(--t-1);
background: rgba(255,255,255,0.08);
border: 1px solid var(--bd-2);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
}
.act-download:hover { background: rgba(255,255,255,0.12); border-color: var(--bd-3); }
/* ─── Download progress (in place of action button when state === 'downloading') ─── */
.dl {
position: relative;
overflow: hidden;
border-radius: 7px;
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--bd-2));
background: rgba(255,255,255,0.04);
color: var(--t-1);
font: inherit;
font-variant-numeric: tabular-nums;
container-type: inline-size;
isolation: isolate;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.04),
0 0 0 1px color-mix(in srgb, var(--accent) 16%, transparent);
}
.dl-full { width: 100%; }
.dl-fill {
position: absolute; inset: 0 auto 0 0;
width: 0%;
background:
linear-gradient(180deg,
color-mix(in srgb, var(--accent) 38%, transparent) 0%,
color-mix(in srgb, var(--accent) 26%, transparent) 100%);
border-right: 1px solid color-mix(in srgb, var(--accent) 75%, transparent);
box-shadow: 2px 0 8px color-mix(in srgb, var(--accent) 35%, transparent);
transition: width 480ms cubic-bezier(.4,0,.2,1);
z-index: 0;
}
/* shimmering scanline on top of the fill so it reads as 'live' */
.dl-fill::after {
content: '';
position: absolute; inset: 0;
background:
repeating-linear-gradient(115deg,
transparent 0 14px,
rgba(255,255,255,0.05) 14px 22px);
background-size: 200% 100%;
animation: dl-stripe 1.4s linear infinite;
mix-blend-mode: screen;
opacity: 0.85;
}
@keyframes dl-stripe {
from { background-position: 0% 0%; }
to { background-position: -36px 0%; }
}
/* live pulse dot */
.dl-pulse {
width: 7px; height: 7px; border-radius: 99px;
background: var(--accent);
box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 70%, transparent);
animation: dl-pulse 1.4s ease-out infinite;
flex: 0 0 auto;
}
@keyframes dl-pulse {
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 70%, transparent); }
70% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 0%, transparent); }
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}
/* ── md (card tile) ── */
.dl-md {
height: 32px;
padding: 0 10px;
}
.dl-md-row {
position: relative; z-index: 1;
display: flex; align-items: center; justify-content: space-between;
width: 100%; height: 100%;
gap: 8px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.005em;
}
.dl-md .dl-pct {
display: inline-flex; align-items: center;
color: var(--t-1);
}
.dl-md .dl-pulse { margin-right: 6px; }
.dl-md .dl-pct-sym { opacity: 0.55; font-weight: 600; margin-left: 1px; }
.dl-md .dl-speed {
color: var(--t-2);
font-size: 11px;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
/* Container-query graceful degradation: when the tile is narrow,
drop the speed and centre the percentage so nothing truncates. */
@container (max-width: 132px) {
.dl-md .dl-speed { display: none; }
.dl-md-row { justify-content: center; gap: 6px; }
}
@container (max-width: 96px) {
.dl-md .dl-pulse { display: none; }
}
/* Density tuning — match the existing button heights */
.density-compact .dl-md { height: 30px; padding: 0 9px; }
.density-compact .dl-md-row { font-size: 11.5px; }
.density-compact .dl-md .dl-speed { font-size: 10.5px; }
.density-large .dl-md { height: 34px; padding: 0 12px; }
.density-large .dl-md-row { font-size: 13px; }
.density-large .dl-md .dl-speed { font-size: 11.5px; }
/* ── lg (detail overlay) ── */
.dl-lg {
height: 56px;
padding: 0;
border-radius: 9px;
flex: 1 1 auto;
min-width: 260px;
}
.dl-lg-grid {
position: relative; z-index: 1;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
grid-template-rows: auto auto;
grid-template-areas:
"primary pct cancel"
"secondary pct cancel";
align-items: center;
height: 100%;
padding: 0 14px 0 16px;
column-gap: 14px;
row-gap: 2px;
}
.dl-lg-primary {
grid-area: primary;
display: flex; align-items: center; gap: 8px;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
color: color-mix(in srgb, var(--accent) 80%, white);
min-width: 0;
}
.dl-lg-primary .dl-label { white-space: nowrap; }
.dl-lg-secondary {
grid-area: secondary;
display: flex; align-items: center; gap: 7px;
font-size: 12px;
font-weight: 500;
color: var(--t-2);
min-width: 0;
overflow: hidden;
}
.dl-lg-secondary .dl-bytes {
color: var(--t-1);
font-weight: 600;
white-space: nowrap;
}
.dl-lg-secondary .dl-bytes .dl-of { color: var(--t-2); font-weight: 500; }
.dl-lg-secondary .dl-speed { color: var(--t-1); font-weight: 600; white-space: nowrap; }
.dl-lg-secondary .dl-eta { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.dl-sep { opacity: 0.45; }
/* Gracefully drop the ETA when the modal is narrow */
@container (max-width: 380px) {
.dl-lg-secondary .dl-eta,
.dl-lg-secondary .dl-sep-eta { display: none; }
}
.dl-lg-pct {
grid-area: pct;
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--t-1);
font-variant-numeric: tabular-nums;
line-height: 1;
}
.dl-lg-pct .dl-pct-sym {
font-size: 12px;
font-weight: 600;
opacity: 0.55;
margin-left: 1px;
}
.dl-cancel {
grid-area: cancel;
display: inline-flex; align-items: center; justify-content: center;
width: 28px; height: 28px;
border-radius: 6px;
border: 1px solid var(--bd-2);
background: rgba(255,255,255,0.04);
color: var(--t-2);
cursor: pointer;
transition: background .15s, border-color .15s, color .15s;
}
.dl-cancel:hover {
background: rgba(239,68,68,0.12);
border-color: rgba(239,68,68,0.40);
color: #fca5a5;
}
/* Ghost / secondary */
.ghost-btn {
display: inline-flex; align-items: center; gap: 7px;
height: 44px; padding: 0 18px;
background: rgba(255,255,255,0.04);
border: 1px solid var(--bd-2);
border-radius: 8px;
color: var(--t-1);
font: inherit; font-size: 13.5px; font-weight: 600;
cursor: pointer;
transition: background .15s, border-color .15s, color .15s;
}
.ghost-btn:hover { background: rgba(255,255,255,0.08); border-color: var(--bd-3); }
.ghost-danger { color: #f87171; }
.ghost-danger:hover { background: rgba(239,68,68,0.10); border-color: rgba(239,68,68,0.40); color: #fca5a5; }
/* ─── Modal ─── */
.modal-scrim {
position: absolute;
inset: 0;
z-index: 100;
background: rgba(4,7,11,0.7);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
display: grid;
place-items: center;
padding: 32px;
animation: fadein .18s ease;
}
@keyframes fadein { from { opacity: 0 } to { opacity: 1 } }
.modal {
width: min(880px, 100%);
max-height: 100%;
background: linear-gradient(180deg, var(--bg-2) 0%, var(--bg-1) 100%);
border: 1px solid var(--bd-2);
border-radius: 14px;
overflow: hidden;
position: relative;
box-shadow: 0 30px 80px -10px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.04);
display: flex; flex-direction: column;
animation: modalin .25s cubic-bezier(.3,1.3,.4,1);
}
@keyframes modalin { from { transform: scale(.96) translateY(8px); opacity: 0 } to { transform: scale(1) translateY(0); opacity: 1 } }
.modal-close {
position: absolute;
top: 14px; right: 14px;
z-index: 5;
width: 32px; height: 32px;
display: grid; place-items: center;
background: rgba(8,12,16,0.7);
border: 1px solid var(--bd-2);
border-radius: 8px;
color: var(--t-1);
cursor: pointer;
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
transition: background .15s, border-color .15s;
}
.modal-close:hover { background: rgba(255,255,255,0.10); border-color: var(--bd-3); }
.modal-hero {
position: relative;
aspect-ratio: 16 / 7;
overflow: hidden;
}
.modal-hero .cover { transform: none !important; }
.modal-hero-fade {
position: absolute; inset: 0;
background: linear-gradient(180deg, transparent 40%, var(--bg-2) 100%);
pointer-events: none;
}
.modal-hero-text {
position: absolute;
left: 28px; right: 28px; bottom: 22px;
z-index: 2;
}
.modal-hero-text .modal-title {
font-family: var(--font-ui);
font-size: 32px;
font-weight: 700;
letter-spacing: -0.015em;
color: white;
margin: 6px 0 0;
text-shadow: 0 4px 24px rgba(0,0,0,0.6);
}
.modal-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.modal-tag {
display: inline-block;
padding: 3px 8px;
background: rgba(8,12,16,0.6);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border: 1px solid var(--bd-2);
border-radius: 4px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--t-1);
}
.modal-state {
position: absolute;
top: 18px;
left: 24px;
z-index: 3;
}
.modal-state .state-chip {
position: static;
font-size: 11.5px;
padding: 5px 11px;
}
/* Banner cover treatment inside modal: hide the cover's own title (we show our own h2) */
.modal-hero .cover-titlewrap { opacity: 0.14; }
.modal-body {
padding: 22px 28px 26px;
display: flex; flex-direction: column; gap: 18px;
}
.modal-meta {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.meta-cell {
padding: 10px 12px;
background: rgba(255,255,255,0.025);
border: 1px solid var(--bd-1);
border-radius: 8px;
}
.meta-label {
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--t-3);
}
.meta-value {
margin-top: 4px;
font-size: 14px;
font-weight: 600;
color: var(--t-1);
display: flex; align-items: center; gap: 6px;
}
.meta-mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 13px; }
.modal-desc {
margin: 0;
font-size: 14px;
line-height: 1.55;
color: var(--t-2);
text-wrap: pretty;
max-width: 64ch;
}
.modal-actions {
display: flex; align-items: center; gap: 10px;
padding-top: 4px;
}
.modal-actions-spacer { flex: 1; }
/* ─── Settings dialog ─── */
.settings-modal {
width: min(640px, 100%);
background: var(--bg-2);
}
.settings-head {
position: relative;
padding: 22px 28px 18px;
border-bottom: 1px solid var(--bd-1);
}
.settings-head h2 {
margin: 0;
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--t-1);
}
.settings-close {
position: absolute;
top: 18px;
right: 18px;
background: transparent;
}
.settings-close:hover { background: rgba(255,255,255,0.06); }
.settings-body {
padding: 22px 28px 26px;
display: flex; flex-direction: column;
gap: 26px;
max-height: 70vh;
overflow: auto;
}
.settings-section {
display: flex; flex-direction: column;
gap: 14px;
}
.settings-section-title {
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--t-3);
}
.settings-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
.settings-row-info { min-width: 0; flex: 1; }
.settings-row-label {
font-size: 14px;
font-weight: 600;
color: var(--t-1);
}
.settings-row-hint {
margin-top: 3px;
font-size: 12px;
color: var(--t-3);
}
.settings-row-control { flex-shrink: 0; }
.settings-foot {
display: flex;
justify-content: flex-end;
padding: 14px 22px 18px;
border-top: 1px solid var(--bd-1);
gap: 10px;
}
.settings-done {
height: 36px;
padding: 0 22px;
font-size: 13.5px;
}
.settings-done:hover {
filter: brightness(1.1);
border-color: transparent !important;
}
/* ─── Settings: color swatches ─── */
.swatch-row {
display: inline-flex;
gap: 8px;
}
.swatch {
position: relative;
width: 32px; height: 32px;
padding: 0;
background: transparent;
border: 0;
border-radius: 9px;
cursor: pointer;
}
.swatch-dot {
display: block;
width: 100%; height: 100%;
border-radius: 8px;
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
transition: transform .15s, box-shadow .15s;
}
.swatch:hover .swatch-dot { transform: scale(1.06); }
.swatch.is-active .swatch-dot {
box-shadow: 0 0 0 2px var(--bg-2), 0 0 0 4px currentColor;
}
.swatch-check {
position: absolute;
inset: 0;
display: grid;
place-items: center;
color: white;
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));
pointer-events: none;
}
/* ─── Settings: segmented radio ─── */
.srad {
display: inline-flex;
background: var(--bg-3);
border: 1px solid var(--bd-1);
border-radius: 8px;
padding: 3px;
}
.srad-btn {
display: inline-flex; align-items: center;
height: 30px;
padding: 0 14px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: var(--t-2);
font: inherit;
font-weight: 600;
font-size: 12.5px;
cursor: pointer;
transition: color .15s, background .15s;
white-space: nowrap;
}
.srad-btn:hover { color: var(--t-1); }
.srad-btn.is-active {
color: white;
box-shadow: 0 2px 8px -2px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.18);
}