chore: import upstream snapshot with attribution
CI / lint (push) Has been cancelled
CI / js-syntax (push) Successful in 10m24s
CI / deps-audit (push) Has been cancelled
CI / test (push) Failing after 24m55s
CI / sast-bandit (push) Failing after 11m13s
CI / trivy (push) Failing after 9m32s
Docker Publish / build-and-push (push) Failing after 34m3s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:32:09 +08:00
commit 8f10353f0c
135 changed files with 37786 additions and 0 deletions
+443
View File
@@ -0,0 +1,443 @@
/* ───────────── Top bar ───────────── */
.app:not(.is-import) .appbar {
padding-top: 12px;
padding-bottom: 14px;
}
/* ─── Collapsed icon strip (double-click to toggle) ─── */
/* Expandable body wrapper — same grid-row trick as .widget-body */
.appbar-body {
display: grid;
grid-template-rows: 1fr;
overflow: hidden;
opacity: 1;
width: 100%;
min-height: 0;
transition: grid-template-rows 220ms ease, opacity 180ms ease;
}
.appbar-body-inner {
min-height: 0;
width: 100%;
display: grid;
grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
align-items: stretch;
gap: 22px;
}
/* Strip wrapper — collapsed by default */
.appbar-icon-strip {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
opacity: 0;
pointer-events: none;
min-height: 0; /* prevent flex parent from reserving space when row is 0fr */
transition: grid-template-rows 220ms ease, opacity 200ms ease 20ms;
}
.appbar-strip-inner {
min-height: 0;
display: flex;
align-items: center;
gap: 5px;
margin: 0 auto;
}
/* Collapsed state */
.app.appbar-collapsed .appbar-body {
grid-template-rows: 0fr;
opacity: 0;
pointer-events: none;
height: 0; /* force flex child to zero — grid-template-rows alone isn't enough in a flex container */
}
.app.appbar-collapsed .appbar-icon-strip {
grid-template-rows: 1fr;
opacity: 1;
pointer-events: auto;
width: 100%;
height: 40px;
align-items: center;
}
.app.appbar-collapsed .appbar {
min-height: 52px;
height: 52px;
padding: 6px 14px;
justify-content: center;
align-items: center;
}
.app.appbar-collapsed .appbar-strip-inner {
height: 40px;
align-items: center;
}
.strip-sq {
width: 36px;
height: 36px;
border-radius: 8px;
border: 1px solid var(--glass-line);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
cursor: pointer;
transition: background 120ms, border-color 120ms, opacity 120ms;
}
.strip-sq:hover { background: rgba(255,255,255,0.08) !important; }
.strip-sq-process:hover { filter: brightness(1.1); }
.strip-sq-brand {
font-family: var(--font-mono);
font-weight: 700;
font-size: 12px;
color: var(--ink);
background: rgba(255,255,255,0.05);
letter-spacing: -0.03em;
}
.strip-sq-url {
background: rgba(255,255,255,0.03);
color: var(--ink-faint);
margin-right: 3px;
}
.strip-sq-stems {
display: flex;
align-items: center;
gap: 5px;
}
.strip-sq-stem {
width: 36px;
height: 36px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.1);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
cursor: pointer;
transition: opacity 120ms, background 120ms;
}
.strip-sq-stem:hover { opacity: 0.85 !important; }
.strip-sq-stem.inactive { opacity: 0.2; }
.strip-sq-process {
background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 100%);
color: #17130c;
border-color: transparent;
margin-left: 4px;
}
.strip-sq-process.loading {
cursor: progress;
filter: saturate(0.92);
}
.strip-sq-process.loading svg {
display: none;
}
.strip-sq-process.loading::before {
content: "";
width: 16px;
height: 16px;
flex: 0 0 16px;
border-radius: 50%;
background: conic-gradient(currentColor 0deg 270deg, transparent 270deg 360deg);
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
animation: spin 0.85s linear infinite;
}
.brand-version {
font-size: 11px;
color: rgba(216,222,230,0.35);
margin-top: 3px;
display: flex;
align-items: center;
gap: 6px;
}
.brand-update-chip {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 1px 5px;
border-radius: 3px;
background: rgba(100, 200, 120, 0.10);
border: 1px solid rgba(100, 200, 120, 0.22);
color: #6dcc82;
font-size: 9px;
text-decoration: none;
}
.brand-update-chip:hover {
background: rgba(100, 200, 120, 0.2);
color: #88dfa0;
}
.appbar {
display: flex;
flex-direction: column;
padding: 20px 24px 22px;
border: 1px solid var(--glass-line);
border-radius: 16px;
background:
radial-gradient(circle at 82% -20%, rgba(216, 168, 74, 0.11), transparent 20rem),
linear-gradient(180deg, rgba(27, 39, 48, 0.76), rgba(13, 22, 30, 0.84));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.045),
var(--shadow-panel);
transition: padding 220ms ease;
}
.brand-group {
display: flex;
flex-direction: column;
justify-content: center;
gap: 5px;
min-width: 0;
padding-right: 8px;
}
.brand-row {
display: flex;
align-items: center;
gap: 8px;
}
.brand {
margin: 0;
font-family: var(--font-mono);
font-weight: 700;
font-size: 24px;
line-height: 1;
color: var(--ink);
letter-spacing: -0.01em;
}
.brand-logo {
display: block;
width: 190px;
height: auto;
}
.beta-badge {
display: inline-flex;
align-items: center;
height: 20px;
padding: 0 5px;
border: 1px solid rgba(216, 168, 74, 0.65);
border-radius: 6px;
background: var(--gold-soft);
color: var(--gold-bright);
font-size: 11px;
font-weight: 700;
line-height: 1;
}
.brand-sub {
font-size: 13px;
color: var(--ink-dim);
white-space: nowrap;
}
.post-urlbar,
.post-process-btn {
display: none;
}
.appbar-form {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
min-width: 0;
}
.appbar-import-panel {
min-width: 0;
}
.appbar-import-panel .import-input-row {
grid-template-columns: minmax(0, 1fr) clamp(140px, 14vw, 220px);
gap: clamp(12px, 2vw, 28px);
padding-bottom: 22px;
}
.appbar-import-panel .url-wrap {
min-height: 58px;
padding: 0 20px;
border: 1px solid rgba(148, 163, 184, 0.24);
border-radius: 11px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.025), transparent),
rgba(3, 9, 14, 0.72);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.035),
inset 0 -1px 0 rgba(0, 0, 0, 0.32),
0 0 0 0 rgba(216, 168, 74, 0);
transition:
border-color var(--t-base) var(--easing),
box-shadow var(--t-base) var(--easing),
background-color var(--t-base) var(--easing);
}
.appbar-import-panel .url-wrap:focus-within {
border-color: rgba(216, 168, 74, 0.56);
background-color: rgba(6, 13, 19, 0.86);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.045),
inset 0 -1px 0 rgba(0, 0, 0, 0.34),
0 0 0 3px rgba(216, 168, 74, 0.08),
0 14px 34px rgba(0, 0, 0, 0.22);
}
.appbar-import-panel .url-prefix {
color: #cbd2dc;
opacity: 0.92;
}
.appbar-import-panel input[type="url"] {
font-size: clamp(15px, 0.95vw, 18px);
height: 100%;
color: #edf0f4;
caret-color: var(--gold-bright);
letter-spacing: 0;
}
.appbar-import-panel .btn-primary {
height: 58px;
min-width: 0;
width: 100%;
border-radius: 11px;
font-size: clamp(14px, 0.9vw, 16px);
}
.appbar-import-panel .stem-choice-row {
grid-template-columns: clamp(110px, 11vw, 156px) repeat(6, minmax(0, 1fr));
gap: clamp(8px, 1vw, 14px);
padding-top: 20px;
}
.appbar-import-panel .stem-choice-row > span {
font-size: clamp(12px, 0.8vw, 14px);
}
.appbar-import-panel .stem-choice {
min-height: 48px;
padding: 0 14px;
border-radius: 10px;
font-size: clamp(12px, 0.8vw, 14px);
background:
linear-gradient(180deg, color-mix(in srgb, currentColor 9%, transparent), transparent),
rgba(8, 15, 22, 0.42);
}
.url-wrap {
display: flex;
align-items: center;
gap: 16px;
flex: 1;
min-width: 0;
cursor: text;
}
.url-prefix {
flex-shrink: 0;
color: #d7dbe2;
}
.appbar-form input[type="url"],
.appbar-import-panel input[type="url"],
.import-card input[type="url"] {
flex: 1;
background: transparent;
border: 0;
outline: 0;
color: var(--ink);
font-family: var(--font-mono);
font-size: 19px;
padding: 0;
min-width: 0;
appearance: none;
box-shadow: none;
}
.appbar-form input[type="url"]::placeholder,
.appbar-import-panel input[type="url"]::placeholder,
.import-card input[type="url"]::placeholder {
color: #9da3ad;
opacity: 0.78;
}
.appbar-import-panel input[type="url"]::selection {
background: rgba(216, 168, 74, 0.28);
color: #fff6de;
}
.appbar-import-panel input[type="url"] {
font-size: clamp(15px, 0.95vw, 18px);
color: #edf0f4;
}
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
height: 56px;
min-width: 150px;
background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 100%);
color: #17130c;
border: 0;
border-radius: var(--radius);
padding: 0 24px;
font-family: var(--font-mono);
font-size: 16px;
font-weight: 700;
cursor: pointer;
position: relative;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.38),
0 16px 36px rgba(216, 168, 74, 0.18);
transition:
filter var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.btn-primary:hover:not(:disabled) {
filter: brightness(1.08);
}
.btn-primary:active:not(:disabled) {
transform: scale(0.98);
}
.btn-primary:disabled {
opacity: 0.82;
cursor: progress;
}
.btn-primary.loading {
filter: saturate(0.92);
}
.btn-primary.loading .icon {
display: none;
}
.btn-primary.loading::before {
content: "";
width: 18px;
height: 18px;
flex: 0 0 18px;
border-radius: 50%;
background: conic-gradient(currentColor 0deg 270deg, transparent 270deg 360deg);
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 0);
animation: spin 0.85s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.btn-primary.loading::before {
animation-duration: 1.8s;
}
}
+110
View File
@@ -0,0 +1,110 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
background:
radial-gradient(circle at 50% 0%, rgba(216, 168, 74, 0.1), transparent 34rem),
radial-gradient(circle at 0% 45%, rgba(216, 168, 74, 0.055), transparent 28rem),
radial-gradient(circle at 100% 45%, rgba(216, 168, 74, 0.055), transparent 28rem),
linear-gradient(180deg, #081016 0%, #050a0f 100%);
color: var(--ink);
font-family: var(--font-mono);
font-size: 14px;
line-height: 1.5;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.hidden {
display: none !important;
}
:focus {
outline: 0;
}
:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
border-radius: var(--radius-xs);
}
.surface-panel {
border: 1px solid var(--glass-line);
background:
linear-gradient(180deg, rgba(28, 39, 48, 0.82), rgba(15, 23, 31, 0.86));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.045),
var(--shadow-panel);
}
.app {
max-width: 1640px;
margin: 0 auto;
min-height: 100vh;
padding: 24px 28px 0;
display: flex;
flex-direction: column;
gap: 16px;
}
.app:not(.is-import) {
max-width: none;
width: 100%;
height: 100vh;
min-height: 0;
padding: 16px 29px 0;
gap: 8px;
overflow: hidden;
}
.app.is-import .now-playing,
.app.is-import #lanes {
display: none;
}
.app:not(.is-import) .import-page,
.app:not(.is-import) .site-footer {
display: none;
}
.site-footer {
display: flex;
align-items: center;
gap: 24px;
margin-top: auto;
padding: 18px 14px 22px;
color: #8d929a;
font-size: 14px;
}
.site-footer > span {
margin-right: auto;
}
.site-footer nav {
display: flex;
align-items: center;
gap: 26px;
}
.site-footer a {
color: inherit;
text-decoration: none;
}
.site-footer a:hover {
color: var(--ink);
}
.help-btn {
height: 42px;
padding: 0 16px;
border: 1px solid rgba(148, 163, 184, 0.16);
border-radius: var(--radius);
background: rgba(18, 26, 34, 0.75);
color: var(--ink);
font: inherit;
}
+493
View File
@@ -0,0 +1,493 @@
/* ──────────────────────────────────────────────
catalog.css — Left catalog / history panel
────────────────────────────────────────────── */
/* ─── Panel shell ─── */
.catalog {
display: flex;
flex-direction: column;
background: linear-gradient(180deg, rgba(28,39,48,0.82), rgba(15,23,31,0.86));
border: 1px solid var(--glass-line);
border-radius: 12px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.045), var(--shadow-panel);
padding: 12px 10px;
gap: 10px;
height: 100%;
min-height: 0;
position: relative;
top: 0;
overflow: hidden;
transition: padding 220ms ease;
}
.app.cat-collapsed .catalog { padding: 12px 8px; }
/* ─── Header (click to collapse) ─── */
.catalog-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 6px 10px;
border-bottom: 1px solid rgba(157,170,182,0.1);
cursor: pointer;
user-select: none;
border-radius: 6px;
transition: background 120ms;
flex-shrink: 0;
}
.catalog-head:hover { background: rgba(31,43,52,0.55); }
.catalog-head h3 {
margin: 0;
font-family: var(--font-mono);
font-size: 17px;
font-weight: 600;
color: var(--ink);
white-space: nowrap;
}
.catalog-head .cat-count {
color: var(--ink-faint);
font-size: 11px;
flex: 1;
padding-left: 8px;
white-space: nowrap;
}
.cat-chevron {
width: 15px;
height: 15px;
color: var(--ink-faint);
transition: transform 220ms ease;
flex-shrink: 0;
}
/* Hide text content when collapsed */
.app.cat-collapsed .catalog-head h3,
.app.cat-collapsed .catalog-head .cat-count { display: none; }
.app.cat-collapsed .catalog-head { justify-content: center; padding: 10px 0; }
.app.cat-collapsed .cat-chevron { transform: rotate(180deg); }
/* ─── Search ─── */
.catalog-search {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(21,31,39,0.6);
color: var(--ink-faint);
font-size: 12px;
flex-shrink: 0;
}
.catalog-search svg { flex-shrink: 0; }
.catalog-search input {
min-width: 0;
width: 100%;
border: 0;
background: transparent;
color: var(--ink);
font: inherit;
font-size: 12px;
outline: none;
padding: 0;
}
.catalog-search input::placeholder {
color: var(--ink-faint);
}
/* ─── New folder button ─── */
.new-folder-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
border: 1px dashed var(--line);
border-radius: 8px;
background: transparent;
color: var(--ink-dim);
font-family: var(--font-mono);
font-size: 12px;
cursor: pointer;
transition: all 120ms;
width: 100%;
flex-shrink: 0;
}
.new-folder-btn:hover {
border-color: var(--gold-line);
color: var(--gold);
}
.new-folder-btn svg { width: 13px; height: 13px; flex-shrink: 0; }
/* ─── Catalog list ─── */
.catalog-list {
display: flex;
flex-direction: column;
gap: 4px;
overflow-y: auto;
flex: 1;
padding-right: 2px;
}
/* ─── Folder ─── */
.folder { display: flex; flex-direction: column; }
.folder-head {
--folder-color: var(--gold);
display: flex;
align-items: center;
gap: 7px;
padding: 6px 8px;
border-radius: 7px;
cursor: pointer;
color: var(--ink-dim);
transition: background 120ms, color 120ms;
user-select: none;
}
.folder-head:hover { background: rgba(31,43,52,0.55); color: var(--ink); }
.folder.drop-target .folder-head {
background: rgba(216,168,74,0.10);
box-shadow: inset 0 0 0 1px var(--gold-line);
color: var(--ink);
}
.folder-head .f-chevron {
width: 11px; height: 11px;
transition: transform 180ms ease;
flex-shrink: 0;
color: var(--ink-faint);
}
.folder.collapsed .folder-head .f-chevron { transform: rotate(-90deg); }
.folder-head .f-icon {
width: 13px;
height: 13px;
flex-shrink: 0;
color: var(--folder-color);
}
.folder-head .f-name {
font-size: 12px;
font-weight: 500;
color: inherit;
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background: transparent;
border: none;
padding: 0;
font-family: inherit;
outline: none;
cursor: inherit;
}
.folder-head .f-name[contenteditable="true"] {
background: rgba(28,34,43,0.8);
padding: 1px 4px;
border-radius: 3px;
cursor: text;
}
.folder-head .f-count { font-size: 10.5px; color: var(--ink-faint); }
.folder-color-dot {
width: 13px;
height: 13px;
border: 1px solid rgba(255,255,255,0.22);
border-radius: 999px;
background: var(--folder-color);
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.22);
cursor: pointer;
padding: 0;
}
.folder-color-dot:hover,
.folder-color-dot:focus-visible {
border-color: rgba(255,255,255,0.82);
outline: none;
}
.folder-color-dot.active {
box-shadow: 0 0 0 2px rgba(255,255,255,0.18), inset 0 0 0 1px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.86);
}
.folder-head .f-del {
width: 18px; height: 18px;
border: none; background: transparent;
color: var(--ink-faint);
cursor: pointer;
opacity: 0;
display: flex; align-items: center; justify-content: center;
border-radius: 4px;
padding: 0;
}
.folder-head:hover .f-del { opacity: 1; }
.folder-head .f-del:hover { background: rgba(21,31,39,0.8); color: var(--danger); }
.folder-editor-backdrop {
position: fixed;
inset: 0;
z-index: 80;
display: grid;
place-items: center;
background: rgba(3, 8, 13, 0.42);
backdrop-filter: blur(2px);
}
.folder-editor {
width: min(300px, calc(100vw - 32px));
border: 1px solid var(--glass-line);
border-radius: 10px;
background: linear-gradient(180deg, rgba(28,39,48,0.98), rgba(13,21,29,0.98));
box-shadow: var(--shadow-panel);
padding: 12px;
color: var(--ink);
font-family: var(--font-mono);
}
.folder-editor-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
font-size: 13px;
font-weight: 600;
}
.folder-editor-close {
width: 24px;
height: 24px;
border: 1px solid transparent;
border-radius: 6px;
background: transparent;
color: var(--ink-faint);
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
}
.folder-editor-close:hover {
background: rgba(21,31,39,0.8);
color: var(--ink);
}
.folder-editor-field {
display: grid;
gap: 7px;
margin-bottom: 12px;
color: var(--ink-faint);
font-size: 10.5px;
}
.folder-editor-name {
width: 100%;
min-height: 34px;
border: 1px solid var(--line);
border-radius: 7px;
background: rgba(10, 17, 24, 0.78);
color: var(--ink);
font: inherit;
font-size: 12px;
padding: 0 10px;
outline: none;
}
.folder-editor-name:focus {
border-color: var(--gold-line);
box-shadow: 0 0 0 2px rgba(216,168,74,0.12);
}
.folder-editor-colors {
display: flex;
align-items: center;
gap: 9px;
}
.folder-editor-colors .folder-color-dot {
width: 22px;
height: 22px;
}
.folder-editor-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 14px;
}
.folder-editor-actions button {
min-height: 30px;
border-radius: 7px;
font-family: var(--font-mono);
font-size: 11px;
cursor: pointer;
padding: 0 11px;
}
.folder-editor-cancel {
border: 1px solid var(--line);
background: rgba(21,31,39,0.52);
color: var(--ink-dim);
}
.folder-editor-save {
border: 1px solid var(--gold-line);
background: rgba(216,168,74,0.16);
color: var(--gold);
}
.folder-body {
display: flex;
flex-direction: column;
gap: 3px;
padding: 3px 0 3px 14px;
margin-left: 7px;
border-left: 1px solid rgba(157,170,182,0.1);
}
.folder.collapsed .folder-body { display: none; }
.folder-empty {
color: var(--ink-faint);
font-size: 11px;
padding: 5px 8px;
font-style: italic;
}
/* ─── Track item ─── */
.cat-item {
display: flex;
gap: 9px;
align-items: center;
padding: 7px 8px;
border: 1px solid transparent;
border-radius: 9px;
cursor: pointer;
transition: background 120ms, border-color 120ms;
}
.cat-item:hover { background: rgba(31,43,52,0.55); border-color: var(--line); }
.cat-item.active { background: rgba(31,43,52,0.55); border-color: var(--gold-line); }
.cat-item.dragging { opacity: 0.4; }
.cat-item[draggable="true"] { cursor: grab; }
.cat-thumb {
width: 44px; height: 44px;
border-radius: 7px;
flex-shrink: 0;
overflow: hidden;
border: 1px solid var(--line);
background: rgba(21,31,39,0.8);
}
.cat-thumb svg,
.cat-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cat-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cat-title {
color: var(--ink);
font-size: 12px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cat-sub {
color: var(--ink-faint);
font-size: 10.5px;
display: flex;
gap: 5px;
white-space: nowrap;
overflow: hidden;
}
.cat-sub .dot { opacity: 0.4; }
.cat-status {
width: 7px; height: 7px;
border-radius: 50%;
background: #5fbc56;
flex-shrink: 0;
}
.cat-status.processing {
background: var(--gold);
animation: cat-pulse 1.4s infinite;
}
@keyframes cat-pulse { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
.cat-del {
width: 20px;
height: 20px;
border: 0;
border-radius: 5px;
background: transparent;
color: var(--ink-faint);
cursor: pointer;
opacity: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
flex-shrink: 0;
transition: opacity 120ms, background 120ms, color 120ms;
}
.cat-item:hover .cat-del,
.cat-del:focus-visible {
opacity: 1;
}
.cat-del:hover {
background: rgba(21,31,39,0.8);
color: var(--danger);
}
/* ─── Catalog footer ─── */
.catalog-foot {
display: flex;
align-items: center;
gap: 7px;
padding: 8px 6px 0;
border-top: 1px solid rgba(157,170,182,0.08);
color: var(--ink-faint);
font-size: 11px;
flex-shrink: 0;
}
.catalog-foot svg { flex-shrink: 0; }
/* ─── Collapsed strip: just album thumbs ─── */
.cat-collapsed-strip {
display: none;
flex-direction: column;
gap: 8px;
align-items: center;
padding-top: 4px;
overflow-y: auto;
flex: 1;
}
.app.cat-collapsed .cat-collapsed-strip { display: flex; }
.app.cat-collapsed .catalog-search,
.app.cat-collapsed .new-folder-btn,
.app.cat-collapsed .catalog-list,
.app.cat-collapsed .catalog-foot { display: none; }
.strip-thumb {
width: 40px; height: 40px;
border-radius: 7px;
overflow: hidden;
border: 1px solid var(--line);
cursor: pointer;
transition: border-color 120ms, transform 120ms;
flex-shrink: 0;
background: rgba(21,31,39,0.8);
color: var(--ink-dim);
}
.strip-thumb:hover { transform: scale(1.06); }
.strip-thumb.active {
border-color: var(--gold-line);
box-shadow: 0 0 0 2px rgba(216,168,74,0.18);
}
.strip-thumb.folder-thumb {
color: var(--folder-color, var(--gold));
}
.strip-thumb.trash-thumb {
color: var(--ink-faint);
}
.strip-thumb svg,
.strip-thumb img { width: 100%; height: 100%; display: block; object-fit: cover; }
.strip-thumb.folder-thumb svg,
.strip-thumb.trash-thumb svg {
width: 22px;
height: 22px;
margin: 8px auto;
}
+2424
View File
File diff suppressed because it is too large Load Diff
+124
View File
@@ -0,0 +1,124 @@
/* ───────────── Job progress ───────────── */
.job {
display: none !important;
background: var(--panel);
border: var(--border-w) solid var(--line);
border-radius: var(--radius);
padding: 14px 16px;
}
.job-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
margin-bottom: 10px;
}
#job-title {
font-size: 14px;
flex: 1;
word-break: break-word;
}
#job-stage {
font-size: 13px;
color: var(--ink-dim);
white-space: nowrap;
flex-shrink: 0;
font-variant-numeric: tabular-nums;
}
progress {
width: 100%;
height: 4px;
appearance: none;
background: var(--line);
border: 0;
border-radius: 2px;
overflow: hidden;
}
progress::-webkit-progress-bar {
background: var(--line);
}
progress::-webkit-progress-value {
background: var(--ink);
transition: width var(--t-base) var(--easing);
}
progress::-moz-progress-bar {
background: var(--ink);
}
/* Compact technical detail line under the progress bar -- shows the
truthful backend `stage` value while #job-stage rotates phrases. */
.job-detail {
margin-top: 6px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--ink-dim);
opacity: 0.65;
letter-spacing: 0.02em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cancel-btn {
flex-shrink: 0;
background: transparent;
color: var(--ink-dim);
border: var(--border-w) solid var(--line);
border-radius: var(--radius-xs);
padding: 4px 10px;
font: inherit;
font-family: var(--font-mono);
font-size: 11px;
cursor: pointer;
transition:
background-color var(--t-base) var(--easing),
color var(--t-base) var(--easing),
border-color var(--t-base) var(--easing);
}
.cancel-btn:hover:not(:disabled) {
background: var(--danger);
color: var(--bg);
border-color: var(--danger);
}
.cancel-btn:disabled {
opacity: 0.5;
cursor: default;
}
/* ───────────── Error ───────────── */
.error {
background: rgba(214, 90, 74, 0.1);
border: var(--border-w) solid rgba(214, 90, 74, 0.5);
color: var(--danger);
border-radius: var(--radius);
padding: 12px 16px;
display: flex;
align-items: flex-start;
gap: 12px;
}
.error-msg {
flex: 1;
word-break: break-word;
font-size: 13px;
}
.retry-btn {
flex-shrink: 0;
background: transparent;
color: var(--danger);
border: var(--border-w) solid var(--danger);
border-radius: var(--radius-xs);
padding: 5px 12px;
font: inherit;
font-family: var(--font-mono);
font-size: 12px;
cursor: pointer;
transition:
background-color var(--t-base) var(--easing),
color var(--t-base) var(--easing);
}
.retry-btn:hover {
background: var(--danger);
color: var(--bg);
}
+106
View File
@@ -0,0 +1,106 @@
/* ───────────── Master fader + VU ─────────────
* Console-style: tick-marked vertical fader on the left, twin segmented
* LED VU meters on the right, both inside a recessed dark panel.
* Custom rendering throughout — `appearance: none` so the browser
* doesn't fall back to its blue-track default vertical slider, which
* ignores ::-webkit-slider-thumb overrides and offsets the thumb. */
.np-master {
display: flex;
align-items: stretch;
gap: 10px;
height: 140px;
padding: 8px 10px;
background: linear-gradient(180deg, #141414, #0d0d0d);
border: 1px solid var(--line);
border-radius: 6px;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.4);
}
/* Fader column with tick marks down both sides. */
.master-fader-track {
position: relative;
height: 100%;
width: 44px;
display: flex;
align-items: stretch;
justify-content: center;
}
.master-fader-track::before,
.master-fader-track::after {
content: "";
position: absolute;
top: 4px;
bottom: 4px;
width: 5px;
background-image: repeating-linear-gradient(
to bottom,
var(--line-strong) 0,
var(--line-strong) 1px,
transparent 1px,
transparent 8px
);
pointer-events: none;
}
.master-fader-track::before { left: 4px; }
.master-fader-track::after { right: 4px; }
.master-fader {
/* No `appearance: slider-vertical` — that's a legacy webkit shortcut
that forces a blue-track default vertical slider AND ignores
::-webkit-slider-thumb overrides (which is why the thumb floats
left of center). `writing-mode: vertical-lr` rotates the
horizontal range input, `direction: rtl` puts max at the top. */
-webkit-appearance: none;
appearance: none;
-moz-appearance: none;
writing-mode: vertical-lr;
direction: rtl;
width: 100%;
height: 100%;
background: transparent;
cursor: pointer;
outline: none;
margin: 0;
padding: 0;
}
.master-fader::-webkit-slider-runnable-track {
width: 4px;
height: 100%;
background: #050505;
border: 1px solid var(--line);
border-radius: 2px;
}
.master-fader::-moz-range-track {
width: 4px;
height: 100%;
background: #050505;
border: 1px solid var(--line);
border-radius: 2px;
}
.master-fader::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
/* In writing-mode: vertical-lr, `width` is the long (horizontal) axis
and `height` is the thin (vertical) axis. Pill-shaped horizontal
handle. margin-left: -12px centers it on the 4px track. */
width: 28px;
height: 14px;
margin-left: -12px;
background: linear-gradient(180deg, #6a6a6a 0%, #2a2a2a 55%, #161616 100%);
border: 1px solid #050505;
border-radius: 3px;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.18),
inset 0 -1px 0 rgba(0, 0, 0, 0.7),
0 1px 2px rgba(0, 0, 0, 0.7);
cursor: pointer;
}
.master-fader::-moz-range-thumb {
width: 28px;
height: 14px;
background: linear-gradient(180deg, #6a6a6a 0%, #2a2a2a 55%, #161616 100%);
border: 1px solid #050505;
border-radius: 3px;
cursor: pointer;
}
+418
View File
@@ -0,0 +1,418 @@
/* ───────────── Preview mixer ───────────── */
.mixer-column {
min-width: 0;
display: grid;
grid-template-columns: repeat(6, minmax(68px, 1fr));
gap: 10px;
}
.lane-header {
min-height: 190px;
display: grid;
grid-template-rows: auto 1fr;
justify-items: center;
position: relative;
transition: opacity var(--t-base) var(--easing);
color: var(--stem-color, #d8dde4);
}
.lane-header[data-stem="vocals"] { --stem-color: #e54249; }
.lane-header[data-stem="drums"] { --stem-color: #f06a14; }
.lane-header[data-stem="bass"] { --stem-color: #eab414; }
.lane-header[data-stem="guitar"] { --stem-color: #5bbf50; }
.lane-header[data-stem="piano"] { --stem-color: #7f45d8; }
.lane-header[data-stem="other"] { --stem-color: #3e8dcf; }
.lane-header[data-stem="original"] { --stem-color: #a8b0bd; }
.lane-header + .lane-header {
border-left: 1px solid var(--glass-line);
}
.lane-header.muted {
opacity: 0.45;
}
.lane-stripe {
display: none;
}
.lane-content {
display: contents;
}
.lane-name-row {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
min-width: 0;
}
.lane-icon {
width: 29px;
height: 29px;
flex: 0 0 auto;
color: var(--stem-color);
filter: drop-shadow(0 0 10px color-mix(in srgb, currentColor 34%, transparent));
}
.lane-icon-toggle {
width: 42px;
height: 42px;
display: grid;
place-items: center;
padding: 0;
border: 1px solid transparent;
border-radius: 8px;
background: transparent;
color: var(--stem-color);
cursor: pointer;
opacity: 0.38;
transition:
opacity var(--t-base) var(--easing),
border-color var(--t-base) var(--easing),
background-color var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.lane-icon-toggle.active {
opacity: 1;
border-color: color-mix(in srgb, currentColor 24%, transparent);
background: color-mix(in srgb, currentColor 8%, transparent);
box-shadow: inset 0 0 18px color-mix(in srgb, currentColor 8%, transparent);
}
.lane-icon-toggle:hover:not(:disabled) {
opacity: 1;
transform: translateY(-1px);
}
.lane-icon-toggle:focus-visible {
outline: 2px solid var(--gold);
outline-offset: 2px;
}
.lane-icon-toggle:disabled {
cursor: default;
opacity: 0.25;
}
.lane-name {
color: #d7dce3 !important;
font-size: 14px;
}
.lane-controls {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 106px;
align-items: end;
justify-items: center;
gap: 10px;
}
.lane-mini-wave {
display: none;
}
.lane-dl {
display: none;
color: #d8dde4;
text-decoration: none;
}
.lane-knob {
grid-column: 1 / -1;
grid-row: 1;
width: 48px;
height: 104px;
border-radius: 0;
border: 0;
background:
repeating-linear-gradient(to bottom, rgba(148, 163, 184, 0.22) 0 1px, transparent 1px 12px),
linear-gradient(90deg, transparent 0 44%, rgba(148, 163, 184, 0.2) 44% 56%, transparent 56% 100%);
position: relative;
cursor: ns-resize;
/* --lane-pos: 0..1 = bottom..top. Default 0.5 = unity gain (vol=1
of LANE_VOLUME_MAX=2). JS rewrites this when volume changes. */
--lane-pos: 0.5;
}
/* Fader thumb. Travels vertically along the track based on --lane-pos.
The travel envelope is 100% - 18px (the thumb's own height) so the
thumb never overshoots the track edges. The transition is only for
programmatic moves (Reset, dblclick, wheel) -- during an active
drag we kill the transition (.lane-knob.dragging rule below) so the
thumb tracks the cursor 1:1 instead of chasing it through an
easing curve, which reads as a rigid/laggy fader. */
.lane-knob::before {
content: "";
position: absolute;
left: 50%;
top: calc((1 - var(--lane-pos)) * (100% - 14px));
width: 30px;
height: 14px;
transform: translateX(-50%);
border-radius: 3px;
border: 1px solid #050505;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.2), transparent 42%),
linear-gradient(180deg, #6a6a6a 0%, #2a2a2a 55%, #161616 100%);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.18),
inset 0 -1px 0 rgba(0, 0, 0, 0.7),
0 0 0 1px color-mix(in srgb, currentColor 34%, transparent),
0 1px 2px rgba(0, 0, 0, 0.72);
transition: top 120ms var(--easing);
will-change: top;
pointer-events: none;
}
.lane-knob.dragging::before {
transition: none;
}
/* Slight grip cue while actively dragging -- thumb gets a tighter
shadow ring so it feels "picked up". */
.lane-knob.dragging {
cursor: grabbing;
}
.lane-knob-indicator {
display: none;
}
.lane-knob.disabled {
opacity: 0.45;
pointer-events: none;
}
.ms-btn {
width: 30px;
height: 30px;
border: 0;
border-radius: 6px;
background: rgba(255, 255, 255, 0.055);
color: #d8dde4;
font-family: var(--font-mono);
font-size: 14px;
font-weight: 700;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition:
background-color var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.ms-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.ms-btn:active {
transform: scale(0.92);
}
.ms-btn.mute.active {
background: var(--danger);
color: white;
}
.ms-btn.solo.active {
background: var(--gold);
color: #17130c;
}
.ms-btn:disabled {
opacity: 0.38;
pointer-events: none;
}
.lane-controls .ms-btn.mute {
grid-column: 1;
grid-row: 2;
}
.lane-controls .ms-btn.solo {
grid-column: 2;
grid-row: 2;
}
.lane-vu {
display: none;
}
.app:not(.is-import) .preview-mixer-body {
height: 100%;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0;
padding-top: 0;
}
.app:not(.is-import) .mixer-column {
height: 100%;
grid-template-columns: repeat(var(--visible-track-count, 6), minmax(52px, 1fr));
gap: 0;
}
.app:not(.is-import) .lane-header {
min-height: 0;
height: 100%;
grid-template-rows: 64px 1fr;
padding: 7px 0 6px;
overflow: hidden;
}
.app:not(.is-import) .lane-name-row {
gap: 6px;
}
.app:not(.is-import) .lane-icon {
width: 26px;
height: 26px;
}
.app:not(.is-import) .lane-icon-toggle {
width: 38px;
height: 38px;
}
.app:not(.is-import) .lane-name {
font-size: 12px;
}
.app:not(.is-import) .lane-controls {
width: 100%;
height: auto;
min-height: 0;
align-self: stretch;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
gap: 0;
padding: 0 5px 38px;
}
.app:not(.is-import) .lane-knob {
width: 42px;
height: 100%;
min-height: 0;
justify-self: center;
}
.app:not(.is-import) .lane-vu {
display: block;
position: absolute;
left: 50%;
top: 72px;
bottom: 48px;
width: 12px;
height: auto;
transform: translateX(18px);
border-radius: 2px;
overflow: hidden;
background:
linear-gradient(to top,
rgba(48, 197, 87, 0.14) 0 55%,
rgba(225, 178, 46, 0.14) 55% 80%,
rgba(214, 64, 64, 0.14) 80% 100%),
rgba(4, 10, 15, 0.82);
box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.12);
}
.app:not(.is-import) .lane-dl {
display: grid;
place-items: center;
position: absolute;
left: 8px;
right: 8px;
bottom: 6px;
height: 34px;
transform: none;
border: 1px solid color-mix(in srgb, var(--stem-color) 28%, rgba(148, 163, 184, 0.18));
border-radius: 8px;
background:
linear-gradient(180deg, color-mix(in srgb, var(--stem-color) 13%, transparent), transparent),
rgba(255, 255, 255, 0.055);
color: var(--stem-color);
opacity: 0.9;
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.035),
0 6px 14px rgba(0, 0, 0, 0.16);
transition:
opacity var(--t-base) var(--easing),
background-color var(--t-base) var(--easing),
border-color var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.app:not(.is-import) .lane-dl svg {
width: 19px;
height: 19px;
stroke-width: 2.15;
}
.app:not(.is-import) .lane-dl:hover:not(.disabled) {
opacity: 1;
border-color: color-mix(in srgb, var(--stem-color) 58%, transparent);
background:
linear-gradient(180deg, color-mix(in srgb, var(--stem-color) 18%, transparent), transparent),
rgba(255, 255, 255, 0.075);
transform: translateY(-1px);
}
.app:not(.is-import) .lane-dl:focus-visible {
outline: 2px solid var(--gold);
outline-offset: 2px;
}
.app:not(.is-import) .lane-dl.disabled {
opacity: 0.28;
pointer-events: none;
}
.app:not(.is-import) .lane-vu::before {
content: "";
position: absolute;
left: 1px;
right: 1px;
top: 1px;
bottom: 1px;
background:
repeating-linear-gradient(to top, transparent 0 3px, rgba(0, 0, 0, 0.4) 3px 5px),
linear-gradient(to top, #24bf55 0 58%, #e1b22e 58% 80%, #d64040 80% 100%);
clip-path: inset(calc(100% - var(--vu-level, 0%)) 0 0 0);
transition: clip-path 30ms linear;
}
.app:not(.is-import) .lane-vu::after {
content: "";
position: absolute;
left: 1px;
right: 1px;
bottom: var(--vu-peak, 0%);
height: 2px;
background: rgba(255, 255, 255, 0.78);
transition: bottom 80ms linear;
}
.app:not(.is-import) .master-strip {
height: 100%;
min-height: 0;
grid-template-columns: 1fr;
grid-template-rows: 72px 1fr;
gap: 8px;
overflow: hidden;
}
.app:not(.is-import) .master-strip .master-fader-track {
height: calc(100% - 4px);
min-height: 0;
}
.app:not(.is-import) .master-strip .master-fader-track::before,
.app:not(.is-import) .master-strip .master-fader-track::after {
display: none;
}
+311
View File
@@ -0,0 +1,311 @@
/* ───────────── Responsive ─────────────
* Breakpoints:
* ≤1100px — laptop / portrait tablet: now-playing card reflows to 3-col
* with master spanning full width below transport.
* ≤900px — landscape phone / small tablet: appbar wraps, lanes scroll
* horizontally with a sticky mixer column, master compresses.
* ≤640px — phone: mixer column narrower, brand smaller, transport
* buttons full-width touch-friendly, master inline-compact.
* ≤480px — small phone: tightest layout, art shrinks, paddings minimal.
* pointer:coarse — touch input regardless of screen size: bump
* interactive targets to a comfortable hit area.
*/
@media (max-width: 1100px) {
.import-card {
width: min(100%, 900px);
}
.stem-choice-row {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.stem-choice-row > span {
grid-column: 1 / -1;
}
.appbar {
grid-template-columns: 1fr;
}
.appbar-import-panel .import-input-row {
grid-template-columns: minmax(0, 1fr) minmax(144px, 180px);
}
.appbar-import-panel .stem-choice-row {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.appbar-import-panel .stem-choice-row > span {
grid-column: 1 / -1;
}
.feature-strip {
grid-template-columns: 1fr;
gap: 22px;
}
.feature-item + .feature-item {
border-left: 0;
border-top: 1px solid var(--gold-line);
padding-left: 0;
padding-top: 22px;
}
.now-playing {
grid-template-columns: auto 1fr auto;
grid-template-areas:
"art info transport"
"art master master";
gap: 14px 16px;
}
.np-art { grid-area: art; }
.np-info { grid-area: info; }
.np-transport { grid-area: transport; }
.np-master { grid-area: master; }
}
@media (max-width: 900px) {
.app {
padding: 16px;
gap: 12px;
}
.appbar {
gap: 12px;
}
.import-page {
padding: 54px 18px 20px;
}
.import-copy h2 {
font-size: 31px;
}
.import-card {
margin-top: 36px;
padding: 18px;
}
.import-input-row {
grid-template-columns: 1fr;
}
.appbar-import-panel {
padding: 16px;
}
.appbar-import-panel .import-input-row {
grid-template-columns: 1fr;
gap: 12px;
}
.btn-primary {
width: 100%;
}
.brand-group {
flex: 1;
}
.appbar-form {
order: 3;
width: 100%;
flex: none;
}
/* Compress the master section to recover vertical space — the full
140px panel makes the now-playing card too tall when stacked. */
.np-master {
height: 90px;
padding: 6px 8px;
}
/* Lane scrolling: mixer column stays put, waves scroll under it. */
.lanes-body,
.lanes-ruler {
overflow-x: auto;
}
.mixer-column,
.lanes-ruler-gutter {
position: sticky;
left: 0;
z-index: 4;
background: var(--panel);
}
/* Keep the waves area readable when scrolling — without a min-width
the waveform canvases collapse below ~250px and become unusable. */
.waves-column,
.lanes-ruler-time {
min-width: 480px;
}
}
@media (max-width: 640px) {
:root {
--header-w: 200px;
}
.brand {
font-size: 22px;
}
.brand-logo {
width: 146px;
}
.brand-sub {
font-size: 12px;
}
.import-copy h2 {
font-size: 26px;
}
.import-copy p {
font-size: 15px;
}
.stem-choice-row {
grid-template-columns: 1fr 1fr;
}
.feature-strip {
padding: 22px;
}
.feature-item {
grid-template-columns: 52px 1fr;
}
.now-playing {
grid-template-columns: auto 1fr;
grid-template-areas:
"art info"
"transport master";
gap: 12px;
}
/* Inline transport + master side-by-side rather than stacked, so the
card stays compact. Master is already compressed by the 900px rule. */
.np-master {
height: 80px;
}
.np-transport {
align-self: center;
}
.lane-mini-wave {
display: none; /* save horizontal space in the mixer column */
}
.np-title {
font-size: 14px;
-webkit-line-clamp: 3;
}
}
@media (max-width: 480px) {
:root {
--header-w: 180px;
--lane-h: 84px;
}
.app {
padding: 12px;
gap: 10px;
}
/* Single-column stem buttons; the 2-col grid still squeezes labels at
~360px because each button needs the icon + text + 8px gap. */
.stem-choice-row {
grid-template-columns: 1fr;
}
.stem-choice {
min-height: 44px;
}
.feature-strip {
margin-top: 24px;
}
.privacy-note {
margin-top: 22px;
font-size: 13px;
text-align: center;
}
.now-playing {
padding: 10px 12px;
grid-template-columns: auto 1fr;
grid-template-areas:
"art info"
"transport transport"
"master master";
gap: 10px;
}
.np-art {
width: 56px;
height: 56px;
}
.np-art-play {
width: 28px;
height: 28px;
}
.np-master {
height: 72px;
}
.np-transport {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.chip {
padding: 3px 8px;
font-size: 12px;
}
/* Drop the second chip column to one row of just the essentials */
.np-chips {
gap: 6px;
}
/* Mixer rows: tighten internal padding so M/S/knob/cloud fit. */
.lane-content {
padding: 8px 36px 8px 8px;
}
.lane-name {
font-size: 13px;
}
/* Tighten lane VU on phone so the row stays readable. */
.lane-vu {
width: 18px;
right: 4px;
}
/* Keep waves usable — lower the min-width slightly on tiny screens. */
.waves-column,
.lanes-ruler-time {
min-width: 360px;
}
}
/* Touch-input devices: enlarge interactive targets to ≥40px hit area
regardless of screen size. Desktop stays compact. */
@media (pointer: coarse) {
.ms-btn {
width: 40px;
height: 40px;
font-size: 13px;
}
.lane-knob {
width: 40px;
height: 40px;
}
.lane-knob-indicator {
transform-origin: 50% 16px;
height: 11px;
top: 5px;
}
.lane-dl {
width: 36px;
height: 36px;
}
.btn-transport {
width: 44px;
height: 44px;
}
.avatar-btn {
width: 44px;
height: 44px;
}
/* Bigger master fader thumb so it's easier to grab. */
.master-fader::-webkit-slider-thumb {
width: 32px;
height: 18px;
margin-left: -14px;
}
.master-fader::-moz-range-thumb {
width: 32px;
height: 18px;
}
}
/* Landscape orientation on short screens (phones held sideways): the
now-playing card eats too much vertical space if it tries to stack.
Prefer a single horizontal row. */
@media (max-height: 500px) and (orientation: landscape) {
.now-playing {
grid-template-columns: auto 1fr auto auto;
grid-template-areas: "art info transport master";
gap: 10px;
padding: 8px 12px;
}
.np-master {
height: 70px;
}
.np-art {
width: 56px;
height: 56px;
}
}
+632
View File
@@ -0,0 +1,632 @@
/* ───────────── Import page ───────────── */
.import-page {
min-height: clamp(420px, 60vh, 704px);
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
padding: clamp(28px, 6vw, 78px) clamp(14px, 3vw, 24px) clamp(18px, 3vw, 24px);
border: 1px solid var(--glass-line);
border-radius: 12px;
background:
radial-gradient(circle at 50% 22%, rgba(216, 168, 74, 0.1), transparent 23rem),
radial-gradient(circle at 8% 45%, rgba(216, 168, 74, 0.06), transparent 22rem),
radial-gradient(circle at 92% 45%, rgba(216, 168, 74, 0.06), transparent 22rem),
linear-gradient(180deg, rgba(16, 26, 34, 0.78), rgba(8, 15, 22, 0.88));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035);
}
.import-copy {
text-align: center;
position: relative;
z-index: 2;
}
.import-copy h2 {
margin: 0;
color: #f1f3f5;
font-size: clamp(24px, 3.4vw + 8px, 39px);
line-height: 1.12;
font-weight: 700;
}
.import-copy h2 span {
color: var(--gold-bright);
white-space: nowrap;
}
.import-copy p {
margin: 16px 0 0;
color: #a9b0bb;
font-size: clamp(14px, 1vw + 8px, 18px);
}
.import-card {
width: min(100%, 1088px);
margin-top: clamp(20px, 4vw, 58px);
padding: clamp(14px, 2vw, 19px) clamp(16px, 3vw, 36px) clamp(18px, 2.5vw, 28px);
border-radius: 13px;
position: relative;
z-index: 2;
}
.import-input-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 24px;
align-items: center;
padding-bottom: 18px;
border-bottom: 1px solid var(--glass-line);
}
.stem-choice-row {
display: grid;
grid-template-columns: 138px repeat(6, minmax(86px, 1fr));
gap: 14px;
align-items: center;
padding-top: 22px;
}
.stem-choice-row > span {
color: #d9dde4;
font-weight: 600;
white-space: nowrap;
}
.stem-choice {
min-height: 54px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: var(--radius);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.025), transparent),
rgba(8, 15, 22, 0.34);
color: currentColor;
font: inherit;
font-weight: 700;
transition:
border-color var(--t-base) var(--easing),
background-color var(--t-base) var(--easing),
box-shadow var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.stem-choice:hover {
transform: translateY(-1px);
background-color: rgba(255, 255, 255, 0.045);
box-shadow: inset 0 0 22px color-mix(in srgb, currentColor 10%, transparent);
}
/* Deselected state -- the user has unchecked this stem from the
"stems to extract" set, so it's greyed out and won't appear in the
studio dashboard after processing. Maintains layout (still occupies
its grid slot) so the row doesn't reflow on toggle. */
.stem-choice[aria-pressed="false"] {
opacity: 0.32;
border-color: rgba(148, 163, 184, 0.2) !important;
background: rgba(8, 15, 22, 0.2);
}
.stem-choice[aria-pressed="false"]:hover {
opacity: 0.55;
}
.stem-choice svg {
flex: 0 0 auto;
}
.stem-choice.vocals { color: #ff3f46; border-color: rgba(255, 63, 70, 0.28); }
.stem-choice.drums { color: #ff7a1a; border-color: rgba(255, 122, 26, 0.24); }
.stem-choice.bass { color: #ffd04d; border-color: rgba(255, 208, 77, 0.24); }
.stem-choice.guitar { color: #3fc952; border-color: rgba(63, 201, 82, 0.24); }
.stem-choice.piano { color: #974cff; border-color: rgba(151, 76, 255, 0.24); }
.stem-choice.other { color: #3695ff; border-color: rgba(54, 149, 255, 0.24); }
.privacy-note {
display: inline-flex;
align-items: center;
gap: 9px;
margin-top: 34px;
color: #a6adb7;
font-size: 15px;
position: relative;
z-index: 2;
}
.feature-strip {
width: min(100%, 1160px);
min-height: 136px;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
align-items: center;
gap: clamp(18px, 2.4vw, 36px);
margin-top: clamp(22px, 4vw, 54px);
padding: clamp(16px, 2.4vw, 24px) clamp(18px, 3vw, 46px);
border-radius: 13px;
position: relative;
z-index: 2;
}
.feature-item {
display: grid;
grid-template-columns: clamp(40px, 4vw, 68px) 1fr;
align-items: center;
gap: clamp(12px, 1.5vw, 24px);
color: var(--gold);
}
.feature-item svg {
width: 100%;
height: auto;
max-width: 48px;
}
.feature-item + .feature-item {
border-left: 1px solid var(--gold-line);
padding-left: clamp(18px, 2.4vw, 42px);
}
.feature-item strong {
display: block;
color: var(--ink);
font-size: clamp(14px, 1vw + 6px, 16px);
margin-bottom: 6px;
}
.feature-item span {
color: #a6adb7;
font-size: clamp(13px, 0.8vw + 8px, 15px);
}
/* The HTML hard-codes <br> in feature descriptions for desktop layout;
on narrow viewports those forced breaks produce ragged 2-word lines.
Collapse them so the text wraps naturally. */
@media (max-width: 720px) {
.feature-item br { display: none; }
}
.wave-lines {
position: absolute;
top: 152px;
width: 32%;
height: 120px;
opacity: 0.46;
background:
repeating-linear-gradient(108deg, transparent 0 24px, rgba(216, 168, 74, 0.52) 25px 27px, transparent 28px 50px);
mask-image: radial-gradient(ellipse, black 0 48%, transparent 72%);
}
.wave-left {
left: 0;
transform: skewY(-7deg);
}
.wave-right {
right: 0;
transform: skewY(7deg);
}
/* Decorative diagonal lines crowd the headline on narrow viewports
(they're absolutely positioned at top: 152px; width: 32%) -- hide
them below the tablet breakpoint to keep the headline area clean. */
@media (max-width: 720px) {
.wave-lines { display: none; }
}
/* ───────────── Now-playing card ───────────── */
.now-playing {
display: grid;
grid-template-columns:
clamp(110px, 11vw, 160px)
minmax(0, 1.2fr)
minmax(0, 0.9fr)
minmax(0, 0.65fr)
minmax(0, 0.65fr)
minmax(0, 0.8fr);
align-items: center;
gap: 0;
min-height: 224px;
padding: 23px 24px;
border-radius: 12px;
}
.app:not(.is-import) .now-playing {
flex: 0 0 auto;
min-height: clamp(120px, 14vh, 160px);
padding-top: clamp(7px, 1vh, 12px);
padding-bottom: clamp(7px, 1vh, 12px);
}
.app:not(.is-import) .np-art {
width: clamp(92px, 13vh, 132px);
height: clamp(92px, 14vh, 144px);
}
.np-art {
position: relative;
width: 154px;
height: 168px;
border-radius: 9px;
overflow: hidden;
background:
linear-gradient(135deg, rgba(14, 22, 30, 0.95), rgba(5, 9, 13, 0.96)),
var(--panel-2);
flex-shrink: 0;
}
.np-art-placeholder {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: var(--ink-faint);
background: linear-gradient(135deg, #1a1a1a, #0e0e0e);
}
.np-art-img {
position: absolute;
inset: 6px;
width: 100%;
height: 100%;
width: calc(100% - 12px);
height: calc(100% - 12px);
object-fit: contain;
display: none;
border-radius: 6px;
}
.np-art-img.loaded {
display: block;
}
.np-art-play {
width: 50px;
height: 50px;
border-radius: 50%;
border: 0;
background: linear-gradient(180deg, var(--gold-bright), var(--gold));
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 12px 24px rgba(216, 168, 74, 0.2);
transition: transform var(--t-fast) var(--easing), background-color var(--t-base) var(--easing);
}
.np-art-play:hover {
background: linear-gradient(180deg, #ecc76e, #c78a30);
}
.np-art-play svg {
margin-left: 2px;
}
.np-info {
display: flex;
flex-direction: column;
gap: 14px;
min-width: 0;
padding: 0 clamp(14px, 2vw, 28px) 0 clamp(16px, 2.2vw, 30px);
border-right: 1px solid var(--glass-line);
}
.np-title {
font-size: clamp(19px, 1.45vw, 24px);
font-weight: 500;
color: var(--ink);
line-height: 1.2;
white-space: normal;
overflow: hidden;
text-overflow: clip;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow-wrap: anywhere;
max-width: 100%;
}
.np-chips {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.chip {
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 5px 12px;
background: rgba(255, 255, 255, 0.055);
color: var(--ink);
border: var(--border-w) solid var(--line);
border-radius: var(--radius-xs);
font-size: 14px;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.np-play-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 16px;
margin-top: 6px;
color: #c6cbd2;
font-size: 14px;
}
.np-scrub {
height: 4px;
border-radius: 999px;
background: rgba(148, 163, 184, 0.16);
position: relative;
}
.np-scrub span {
position: absolute;
left: 0;
top: 50%;
width: 14px;
height: 14px;
transform: translateY(-50%);
border-radius: 50%;
background: var(--gold-bright);
box-shadow: 0 0 0 4px rgba(216, 168, 74, 0.18);
}
.stem-energy,
.analysis-card {
min-height: clamp(100px, 14vh, 156px);
padding: 0 clamp(10px, 1.4vw, 24px);
border-left: 1px solid var(--glass-line);
}
.stem-energy {
grid-column: 3;
}
.key-card {
grid-column: 4;
}
.loudness-card {
grid-column: 5;
}
.beat-card {
grid-column: 6;
}
.loudness-card.hidden + .beat-card {
grid-column: 5 / 7;
}
.stem-energy h3,
.analysis-card h3 {
margin: 0 0 13px;
color: #c9ced6;
font-size: 14px;
text-transform: uppercase;
}
.stem-energy h3 span,
.analysis-card h3 span,
.panel-title span {
display: inline-grid;
place-items: center;
width: 16px;
height: 16px;
margin-left: 4px;
border: 1px solid currentColor;
border-radius: 50%;
font-size: 10px;
opacity: 0.72;
}
.energy-row {
display: grid;
grid-template-columns: 78px 1fr 40px;
align-items: center;
gap: 9px;
margin: 5px 0;
color: var(--stem-color);
font-size: 13px;
}
.energy-row span {
font-weight: 700;
}
.energy-row b {
height: 12px;
border-radius: 999px;
background:
linear-gradient(90deg, currentColor 0 var(--v, 0%), rgba(148, 163, 184, 0.13) var(--v, 0%) 100%);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
transition: background 80ms linear;
}
.energy-row em {
color: #c8cdd4;
font-style: normal;
font-weight: 700;
}
.vocals { --stem-color: #e54249; color: var(--stem-color); }
.drums { --stem-color: #f06a14; color: var(--stem-color); }
.bass { --stem-color: #eab414; color: var(--stem-color); }
.guitar { --stem-color: #5bbf50; color: var(--stem-color); }
.piano { --stem-color: #7f45d8; color: var(--stem-color); }
.other { --stem-color: #3e8dcf; color: var(--stem-color); }
.original { --stem-color: #a8b0bd; color: var(--stem-color); }
.analysis-card {
display: flex;
flex-direction: column;
justify-content: center;
gap: 8px;
}
.key-card {
align-items: center;
text-align: center;
}
.beat-card {
align-items: center;
text-align: center;
}
.analysis-card strong {
color: var(--gold-bright);
font-size: clamp(17px, 1.7vw, 26px);
line-height: 1.1;
font-weight: 500;
}
.analysis-card small {
color: #b9bfc7;
font-size: 12px;
}
.confidence-ring {
width: 66px;
height: 66px;
position: relative;
display: grid;
place-items: center;
border-radius: 50%;
background:
conic-gradient(
var(--gold) calc(var(--confidence-pct, 0) * 1%),
rgba(216, 168, 74, 0.16) 0
);
color: #e9edf2;
isolation: isolate;
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.confidence-ring::before {
content: "";
position: absolute;
inset: 4px;
border-radius: 50%;
background: var(--panel);
}
.confidence-ring::after {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
box-shadow:
inset 0 0 0 1px rgba(216, 168, 74, 0.12),
0 0 14px rgba(216, 168, 74, 0.1);
pointer-events: none;
}
.confidence-ring span {
position: relative;
z-index: 1;
}
.loudness-card strong {
display: grid;
grid-template-columns: auto 1fr;
gap: 12px;
align-items: baseline;
color: #f0f2f5;
}
.beat-values {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 16px;
min-width: 0;
}
.beat-values strong {
display: grid;
grid-template-columns: auto 1fr;
gap: 12px;
align-items: baseline;
color: var(--gold-bright);
text-align: left;
}
.beat-values strong small {
display: block;
margin-top: 0;
color: #b9bfc7;
}
.np-transport {
display: flex;
align-items: center;
gap: 26px;
color: #cfd4db;
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.np-transport-row {
display: flex;
align-items: center;
gap: 22px;
}
.btn-transport {
width: 58px;
height: 58px;
background: rgba(255, 255, 255, 0.055);
color: var(--ink);
border: 1px solid var(--glass-line);
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition:
background-color var(--t-base) var(--easing),
color var(--t-base) var(--easing),
border-color var(--t-base) var(--easing),
transform var(--t-fast) var(--easing);
}
.btn-transport:hover {
background: rgba(255, 255, 255, 0.09);
border-color: var(--line-strong);
}
.btn-transport:active {
transform: scale(0.94);
}
.btn-transport .pause-icon {
display: none;
}
.btn-transport.playing .pause-icon {
display: block;
}
.btn-transport.playing svg:not(.pause-icon) {
display: none;
}
.btn-transport.playing {
background: linear-gradient(180deg, #4ade80, #16a34a);
color: white;
border-color: #15803d;
box-shadow: 0 16px 34px rgba(34, 197, 94, 0.22);
}
.btn-transport.stopped {
background: linear-gradient(180deg, #f87171, #dc2626);
color: white;
border-color: #b91c1c;
box-shadow: 0 16px 34px rgba(239, 68, 68, 0.22);
}
.btn-transport.loop.active {
background: linear-gradient(180deg, var(--gold-bright), var(--gold));
color: white;
border-color: var(--gold-line);
box-shadow: 0 16px 34px rgba(216, 168, 74, 0.18);
}
.np-time {
font-size: 13px;
color: var(--ink-dim);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
+71
View File
@@ -0,0 +1,71 @@
/* ── Design tokens ── */
:root {
color-scheme: dark;
/* Surfaces */
--bg: #0b0f12;
--bg-2: #0f1418;
--panel: #131a1f;
--panel-2: #182026;
--panel-3: #1d262d;
--border: #232c34;
--border-strong: #2e3942;
/* Text */
--fg: #e8ecf0;
--fg-2: #c2c9d1;
--muted: #8a939c;
--muted-2: #5d666e;
/* Stem colours */
--vocals: #ef4444;
--drums: #f97316;
--bass: #eab308;
--guitar: #22c55e;
--piano: #a855f7;
--other: #9ca3af;
/* Accent */
--accent: #f4b740;
--accent-2: #d99a2b;
/* Legacy compat aliases used by waves.css / player.js */
--gold: var(--accent);
--gold-bright: #f8c054;
--gold-soft: rgba(244,183,64,0.16);
--gold-line: rgba(244,183,64,0.28);
--ink: var(--fg);
--ink-dim: var(--fg-2);
--ink-faint: var(--muted);
--line: var(--border);
--line-strong: var(--border-strong);
--glass-line: var(--border);
--danger: #d65a4a;
--focus-ring: rgba(236,236,236,0.55);
/* Typography */
--font-sans: 'Inter', -apple-system, system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
/* Layout */
--header-w: 300px;
--lane-h: 86px;
--radius: 10px;
--radius-sm: 8px;
--radius-xs: 6px;
/* Motion */
--t-fast: 80ms;
--t-base: 120ms;
--easing: cubic-bezier(0.2,0.8,0.2,1);
--shadow-panel: 0 4px 24px rgba(0,0,0,0.4);
}
@media (prefers-reduced-motion: reduce) {
:root { --t-fast: 0ms; --t-base: 0ms; }
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
+1011
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff