MuSProt / frontend /src /styles /ui.css
WinslowFan's picture
Claude Opus 5.5
Serve SQLite from local disk instead of the bucket mount
3d86d3b
Raw History Blame Contribute Delete
22 kB
/* ───────────────────────── layout shell */
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.page {
max-width: var(--page-max);
margin: 0 auto;
padding: 28px var(--gutter) 64px;
display: flex;
flex-direction: column;
gap: 24px;
}
.page--narrow {
max-width: 960px;
}
.muted {
color: var(--muted);
}
.small {
font-size: 13px;
}
.strong {
font-weight: 600;
}
.eyebrow {
font-size: 12px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent-strong);
margin: 0 0 6px;
}
/* ───────────────────────── header */
.site-header {
position: sticky;
top: env(safe-area-inset-top, 0px);
z-index: 40;
background: color-mix(in srgb, var(--surface) 86%, transparent);
backdrop-filter: saturate(1.6) blur(12px);
-webkit-backdrop-filter: saturate(1.6) blur(12px);
border-bottom: 1px solid var(--line);
}
.site-header--home {
background: color-mix(in srgb, var(--hero-ink) 88%, transparent);
border-bottom-color: rgba(255, 255, 255, 0.06);
color: var(--hero-text);
}
.site-header__inner {
max-width: var(--page-max);
margin: 0 auto;
height: var(--header-h);
padding: 0 var(--gutter);
display: flex;
align-items: center;
gap: 20px;
}
.site-header__search {
flex: 1;
max-width: 520px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
color: inherit;
text-decoration: none !important;
flex-shrink: 0;
}
.brand__name {
font-weight: 700;
font-size: 18px;
letter-spacing: -0.02em;
color: var(--ink);
}
.site-header--home .brand__name {
color: #fff;
}
.logo-mark__bg {
fill: var(--hero-ink);
}
.site-header--home .logo-mark__bg {
fill: rgba(255, 255, 255, 0.08);
}
.logo-mark__edge {
fill: none;
stroke: #86b6ef;
stroke-width: 1.6;
stroke-linejoin: round;
opacity: 0.9;
}
.logo-mark__edge--soft {
stroke-dasharray: 2 2.4;
opacity: 0.6;
}
.logo-mark__n1 {
fill: #86b6ef;
}
.logo-mark__n2 {
fill: #3987e5;
}
.logo-mark__n3 {
fill: #eb6834;
}
.site-nav {
margin-left: auto;
display: flex;
align-items: center;
gap: 4px;
}
.site-nav__link {
padding: 8px 12px;
border-radius: 8px;
color: var(--ink-2);
font-weight: 500;
font-size: 14px;
text-decoration: none !important;
}
.site-nav__link:hover {
background: var(--surface-2);
color: var(--ink);
}
.site-nav__link.is-active {
color: var(--ink);
background: var(--surface-2);
}
.site-header--home .site-nav__link {
color: var(--hero-muted);
}
.site-header--home .site-nav__link:hover,
.site-header--home .site-nav__link.is-active {
color: #fff;
background: rgba(255, 255, 255, 0.08);
}
.site-nav .btn {
margin-left: 8px;
}
.menu-toggle {
display: none;
margin-left: auto;
border: 0;
background: transparent;
padding: 8px;
border-radius: 8px;
color: inherit;
}
.menu-toggle svg {
width: 22px;
height: 22px;
}
.mobile-nav {
display: none;
flex-direction: column;
gap: 6px;
padding: 12px var(--gutter) 16px;
border-top: 1px solid var(--line);
background: var(--surface);
color: var(--ink);
}
.mobile-nav__link {
padding: 10px 8px;
color: var(--ink);
font-weight: 500;
border-radius: 8px;
}
@media (max-width: 860px) {
.site-nav,
.site-header__search {
display: none;
}
.menu-toggle {
display: inline-flex;
}
.mobile-nav {
display: flex;
}
}
/* ───────────────────────── footer */
.site-footer {
margin-top: 48px;
background: var(--hero-ink);
color: var(--hero-muted);
font-size: 14px;
}
.site-footer__inner {
max-width: var(--page-max);
margin: 0 auto;
padding: 48px var(--gutter) 32px;
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(0, 2fr);
gap: 40px;
}
.site-footer .brand__name {
color: #fff;
}
.site-footer .logo-mark__bg {
fill: rgba(255, 255, 255, 0.08);
}
.site-footer__brand p {
margin-top: 14px;
max-width: 40ch;
line-height: 1.6;
}
.site-footer__cols {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
}
.site-footer h3 {
color: #fff;
font-size: 13px;
font-weight: 600;
margin-bottom: 12px;
letter-spacing: 0;
}
.site-footer__cols a {
display: block;
color: var(--hero-muted);
padding: 3px 0;
}
.site-footer__cols a:hover {
color: #fff;
}
.site-footer__base {
max-width: var(--page-max);
margin: 0 auto;
padding: 16px var(--gutter) calc(20px + env(safe-area-inset-bottom, 0px));
border-top: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
font-size: 13px;
}
.site-footer__base a {
color: var(--hero-muted);
}
@media (max-width: 760px) {
.site-footer__inner {
grid-template-columns: 1fr;
}
}
/* ───────────────────────── buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 38px;
padding: 0 14px;
border-radius: 9px;
border: 1px solid var(--line-strong);
background: var(--surface);
color: var(--ink);
font-weight: 550;
font-size: 14px;
white-space: nowrap;
text-decoration: none !important;
transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.btn:hover {
background: var(--surface-2);
}
.btn:disabled {
opacity: 0.45;
cursor: default;
}
/* fixed blue in both themes so white text keeps ≥ 4.5:1 contrast */
.btn--primary {
background: #2a78d6;
border-color: transparent;
color: #fff;
}
.btn--primary:hover {
background: #1c5cab;
}
.btn--ghost {
background: transparent;
border-color: var(--line);
}
.btn--sm {
height: 32px;
padding: 0 10px;
font-size: 13px;
border-radius: 8px;
}
.btn--sm svg {
width: 14px;
height: 14px;
}
.btn--lg {
height: 46px;
padding: 0 20px;
font-size: 15px;
border-radius: 11px;
}
.btn--block {
width: 100%;
margin-top: 12px;
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px solid var(--line);
background: transparent;
color: var(--muted);
}
.icon-btn:hover {
color: var(--ink);
background: var(--surface-2);
}
.icon-btn svg {
width: 16px;
height: 16px;
}
.chip {
display: inline-flex;
align-items: center;
height: 28px;
padding: 0 10px;
border-radius: 999px;
font-size: 13px;
border: 1px solid var(--line);
color: var(--ink-2);
background: var(--surface);
}
.chip:hover {
text-decoration: none;
border-color: var(--accent);
color: var(--accent-strong);
}
/* ───────────────────────── cards */
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
padding: 20px 22px;
min-width: 0;
}
.card--flush {
padding: 0;
overflow: hidden;
}
.card--flush > .card__head {
padding: 18px 22px 12px;
margin: 0;
}
.card__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.card__titles {
min-width: 0;
flex: 1 1 280px;
}
.card__title {
font-size: 16px;
font-weight: 650;
letter-spacing: -0.01em;
}
.card__kicker {
margin-top: 4px;
color: var(--muted);
font-size: 13.5px;
line-height: 1.5;
}
.card__actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.mini-title {
font-size: 12px;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
margin: 18px 0 8px;
}
.mini-title:first-of-type {
margin-top: 4px;
}
/* ───────────────────────── badges */
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
height: 26px;
padding: 0 10px;
border-radius: 999px;
font-size: 12.5px;
font-weight: 550;
background: var(--surface-2);
color: var(--ink-2);
border: 1px solid var(--line);
white-space: nowrap;
}
.badge--accent {
background: var(--accent-soft);
color: var(--accent-ink);
border-color: transparent;
}
.badge--outline {
background: transparent;
}
.badges {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 14px;
}
.swatch {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 3px;
flex-shrink: 0;
}
.swatch--dot {
border-radius: 50%;
}
.swatch--ring {
background: var(--surface);
box-shadow: inset 0 0 0 1.5px var(--faint);
}
.fid {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.chain-link {
font-family: var(--font-mono);
font-size: 0.92em;
white-space: nowrap;
}
.chain-link--strong {
font-weight: 600;
}
.ext {
display: inline-flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.ext__icon {
width: 12px;
height: 12px;
opacity: 0.7;
}
/* ───────────────────────── states: loading / empty / error */
.loading {
display: flex;
align-items: center;
gap: 10px;
color: var(--muted);
padding: 28px 4px;
justify-content: center;
}
.spinner {
display: inline-block;
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid var(--line-strong);
border-top-color: var(--accent);
animation: spin 0.8s linear infinite;
flex-shrink: 0;
}
.spinner--sm {
width: 14px;
height: 14px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.skeleton {
display: block;
border-radius: 8px;
background: linear-gradient(90deg, var(--surface-2), var(--surface-3), var(--surface-2));
background-size: 200% 100%;
animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer {
to {
background-position: -200% 0;
}
}
@media (prefers-reduced-motion: reduce) {
.skeleton,
.spinner {
animation-duration: 3s;
}
}
.state {
display: flex;
gap: 12px;
align-items: flex-start;
padding: 20px 22px;
color: var(--muted);
}
.state svg {
width: 20px;
height: 20px;
flex-shrink: 0;
margin-top: 2px;
}
.state strong {
color: var(--ink);
font-weight: 600;
}
.state p {
margin-top: 4px;
}
.state--error {
border: 1px solid color-mix(in srgb, var(--pos) 35%, transparent);
background: color-mix(in srgb, var(--pos) 7%, var(--surface));
border-radius: var(--radius);
}
.state--error svg {
color: var(--pos);
}
/* ───────────────────────── form controls */
.field {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 12px;
font-weight: 600;
color: var(--muted);
min-width: 140px;
}
.field--grow {
flex: 1 1 280px;
}
.field--sm {
min-width: 100px;
max-width: 130px;
}
.field--inline {
flex-direction: row;
align-items: center;
gap: 8px;
min-width: 0;
}
.field input,
.field select {
height: 38px;
padding: 0 12px;
border-radius: 9px;
border: 1px solid var(--line-strong);
background: var(--surface);
color: var(--ink);
font-size: 14px;
font-weight: 400;
outline: none;
}
.field select {
padding-right: 28px;
}
.field--inline select {
height: 32px;
}
.field input:focus,
.field select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.segmented {
display: inline-flex;
flex-wrap: wrap;
gap: 2px;
padding: 3px;
border-radius: 10px;
background: var(--surface-2);
border: 1px solid var(--line);
}
.segmented__opt {
display: inline-flex;
align-items: center;
gap: 6px;
height: 28px;
padding: 0 10px;
border: 0;
border-radius: 7px;
background: transparent;
color: var(--muted);
font-size: 13px;
font-weight: 550;
}
.segmented__opt:hover {
color: var(--ink);
}
.segmented__opt.is-on {
background: var(--surface);
color: var(--ink);
box-shadow: var(--shadow-sm), 0 0 0 1px var(--line);
}
.segmented__count {
font-size: 11.5px;
color: var(--faint);
font-weight: 500;
}
/* ───────────────────────── search */
.search {
position: relative;
width: 100%;
}
.search form {
display: flex;
align-items: center;
height: 40px;
border-radius: 10px;
border: 1px solid var(--line-strong);
background: var(--surface);
padding: 0 4px 0 12px;
transition: border-color 0.15s, box-shadow 0.15s;
}
.search form:focus-within {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.search__icon {
width: 17px;
height: 17px;
color: var(--faint);
flex-shrink: 0;
}
.search input {
flex: 1;
min-width: 0;
height: 100%;
border: 0;
outline: none;
background: transparent;
padding: 0 10px;
font-size: 14px;
color: var(--ink);
}
.search input::-webkit-search-cancel-button {
display: none;
}
.search input::placeholder {
color: var(--faint);
}
.search__clear {
display: inline-flex;
border: 0;
background: transparent;
color: var(--faint);
padding: 6px;
border-radius: 6px;
}
.search__clear svg {
width: 15px;
height: 15px;
}
.search__submit {
display: none;
border: 0;
background: #2a78d6;
color: #fff;
font-weight: 600;
border-radius: 9px;
padding: 0 18px;
height: calc(100% - 8px);
}
.search__submit:hover {
background: #1c5cab;
}
.search--lg form {
height: 58px;
border-radius: 14px;
padding: 0 6px 0 18px;
box-shadow: var(--shadow-lg);
border-color: transparent;
}
.search--lg .search__icon {
width: 21px;
height: 21px;
}
.search--lg input {
font-size: 16px;
padding: 0 14px;
}
.search--lg .search__submit {
display: inline-block;
}
.search__pop {
position: absolute;
z-index: 60;
top: calc(100% + 6px);
left: 0;
right: 0;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 12px;
box-shadow: var(--shadow-lg);
padding: 6px;
max-height: min(70vh, 460px);
overflow-y: auto;
color: var(--ink);
text-align: left;
}
.search__hint {
padding: 10px 12px;
color: var(--muted);
font-size: 13.5px;
}
.search__hint kbd {
font-size: 12px;
border: 1px solid var(--line-strong);
border-radius: 4px;
padding: 1px 5px;
background: var(--surface-2);
}
.search__item {
width: 100%;
display: flex;
align-items: center;
gap: 12px;
text-align: left;
border: 0;
background: transparent;
padding: 9px 10px;
border-radius: 8px;
color: var(--ink);
}
.search__item.is-active {
background: var(--surface-2);
}
.search__kind {
width: 30px;
height: 30px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: var(--accent-soft);
color: var(--accent-strong);
}
.search__kind--sequence {
background: color-mix(in srgb, var(--holo) 14%, var(--surface));
color: var(--holo);
}
.search__kind svg {
width: 16px;
height: 16px;
}
.search__text {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
}
.search__primary {
font-weight: 600;
font-size: 13.5px;
}
.search__secondary {
font-size: 12.5px;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search__enter {
width: 15px;
height: 15px;
color: var(--faint);
}
/* ───────────────────────── tables */
.table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13.5px;
}
.table th {
position: sticky;
top: 0;
text-align: left;
font-size: 12px;
font-weight: 600;
color: var(--muted);
background: var(--surface-2);
padding: 10px 14px;
border-bottom: 1px solid var(--line);
white-space: nowrap;
}
.table td {
padding: 11px 14px;
border-bottom: 1px solid var(--line);
vertical-align: middle;
color: var(--ink-2);
}
.table tbody tr:last-child td {
border-bottom: 0;
}
.table .num {
text-align: right;
}
.table--dense td {
padding: 8px 12px;
}
.table--dense th {
padding: 9px 12px;
}
.table--rows tbody tr.is-clickable {
cursor: pointer;
}
.table--rows tbody tr:hover td {
background: var(--surface-2);
}
.table tbody tr.is-selected td {
background: var(--accent-soft);
}
.table .sub {
font-size: 12px;
color: var(--faint);
margin-top: 2px;
}
.fn-cell {
max-width: 420px;
min-width: 220px;
}
.fn-cell span {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.45;
}
.methods-cell {
white-space: nowrap;
}
.binders-cell {
font-size: 12px;
white-space: nowrap;
}
.th-sort {
display: inline-flex;
align-items: center;
gap: 4px;
border: 0;
background: transparent;
padding: 0;
font: inherit;
color: inherit;
white-space: nowrap;
}
.th-sort:hover,
.th-sort.is-active {
color: var(--ink);
}
.th-sort__arrow {
font-size: 11px;
opacity: 0.55;
}
.th-sort.is-active .th-sort__arrow {
opacity: 1;
color: var(--accent-strong);
}
.table-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
flex-wrap: wrap;
}
.table-toolbar--wrap {
justify-content: flex-start;
}
.table-toolbar__count {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--muted);
font-size: 13.5px;
}
.table-toolbar__count svg {
width: 16px;
height: 16px;
}
.table-toolbar__count b {
color: var(--ink);
}
.is-stale {
opacity: 0.55;
transition: opacity 0.2s;
}
.delta--up {
color: var(--pos);
}
.delta--down {
color: var(--neg);
}
.pager {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-top: 1px solid var(--line);
flex-wrap: wrap;
}
.pager__info {
color: var(--muted);
font-size: 13px;
}
.pager__btns {
display: flex;
align-items: center;
gap: 8px;
}
.pager__page {
font-size: 13px;
color: var(--muted);
min-width: 60px;
text-align: center;
}
.meter {
display: inline-block;
position: relative;
width: 48px;
height: 6px;
border-radius: 3px;
background: var(--surface-3);
overflow: hidden;
vertical-align: middle;
}
.meter__fill {
position: absolute;
inset: 0 auto 0 0;
border-radius: 3px;
}
.meter-cell {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 12.5px;
}
.meter-inline {
display: inline-flex;
vertical-align: middle;
}
/* ───────────────────────── facts */
.facts {
margin: 0;
display: flex;
flex-direction: column;
}
.fact {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 9px 0;
border-bottom: 1px solid var(--line);
font-size: 14px;
}
.fact:last-child {
border-bottom: 0;
}
.fact dt {
color: var(--muted);
flex-shrink: 0;
}
.fact dd {
margin: 0;
text-align: right;
color: var(--ink);
font-weight: 500;
min-width: 0;
overflow-wrap: anywhere;
display: flex;
flex-wrap: wrap;
gap: 4px 10px;
justify-content: flex-end;
}
/* ───────────────────────── charts */
.chart {
position: relative;
width: 100%;
min-width: 0;
}
.chart svg {
display: block;
overflow: visible;
}
.tick {
font-size: 11px;
fill: var(--muted);
font-variant-numeric: tabular-nums;
}
.axis-title {
font-size: 12px;
fill: var(--muted);
font-weight: 500;
}
.grid-line {
stroke: var(--grid);
stroke-width: 1;
}
.axis-line {
stroke: var(--axis);
stroke-width: 1;
}
.threshold {
stroke: var(--line-strong);
stroke-width: 1;
}
.chart-tip {
position: absolute;
z-index: 20;
pointer-events: none;
transform: translate(-50%, calc(-100% - 14px));
min-width: 150px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 10px;
box-shadow: var(--shadow-lg);
padding: 9px 11px;
font-size: 12.5px;
color: var(--ink);
white-space: nowrap;
}
.chart-tip__title {
font-weight: 650;
margin-bottom: 4px;
}
.chart-tip__row {
display: flex;
align-items: center;
gap: 8px;
color: var(--ink-2);
}
.chart-tip__row b {
margin-left: auto;
color: var(--ink);
font-weight: 600;
padding-left: 12px;
}
.chart-tip__foot {
margin-top: 4px;
color: var(--muted);
font-size: 11.5px;
}
.legend {
list-style: none;
padding: 0;
margin: 12px 0 0;
display: flex;
flex-wrap: wrap;
gap: 6px 16px;
font-size: 12.5px;
color: var(--ink-2);
}
.legend li {
display: inline-flex;
align-items: center;
gap: 6px;
}
.legend b {
color: var(--muted);
font-weight: 500;
}
.barlist {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.barlist__row {
display: grid;
grid-template-columns: minmax(80px, 130px) minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
font-size: 13px;
}
.barlist__label {
color: var(--ink-2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.barlist__track svg {
display: block;
}
.barlist__value {
color: var(--muted);
font-size: 12.5px;
text-align: right;
}
.stacked__bar {
position: relative;
display: flex;
gap: 2px;
border-radius: 5px;
}
.stacked__seg {
display: block;
min-width: 3px;
height: 100%;
}
.stacked__seg:first-child {
border-radius: 5px 0 0 5px;
}
.stacked__seg:last-child {
border-radius: 0 5px 5px 0;
}
.stacked__seg:only-child {
border-radius: 5px;
}
.stacked__seg:hover {
filter: brightness(1.08);
}
.minisplit {
display: inline-flex;
gap: 2px;
width: 64px;
height: 8px;
vertical-align: middle;
}
.minisplit span {
display: block;
height: 100%;
border-radius: 2px;
min-width: 0;
}
.minisplit span:empty[style*='width: 0%'] {
display: none;
}
.state__retry {
margin-top: 10px;
}