mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The account page lists the account's reports as a table and the Client System Info page lists every report (or each account's newest), sortable and searchable by account name or video card; a row opens every field. Values that say little about the real system are marked, and every caveat is a tooltip on the value and column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
270 lines
13 KiB
CSS
270 lines
13 KiB
CSS
.sidebar {
|
|
width: 250px;
|
|
background-color: var(--bs-tertiary-bg);
|
|
}
|
|
|
|
@media (min-width: 992px) {
|
|
nav.sidebar {
|
|
position: sticky;
|
|
top: 0;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
}
|
|
}
|
|
|
|
/* Narrow screens: the sidebar slides out (offcanvas-lg), and wide tables scroll sideways */
|
|
@media (max-width: 991.98px) {
|
|
nav.sidebar.offcanvas-lg { width: min(280px, 85vw); overflow-y: auto; }
|
|
.dt-container .dt-layout-table, .dt-container .dt-layout-row:has(table) { overflow-x: auto; }
|
|
}
|
|
|
|
/* .table-stack: on phones every row becomes a small card, each cell labelled with its column */
|
|
@media (max-width: 575.98px) {
|
|
.table-stack thead { display: none; }
|
|
.table-stack, .table-stack tbody, .table-stack tr, .table-stack td { display: block; width: 100% !important; }
|
|
.table-stack tr { border: 1px solid var(--bs-border-color); border-radius: .5rem; margin-bottom: .5rem; padding: .25rem 0; }
|
|
.table-stack td { border: 0; padding: .15rem .6rem; text-align: left !important; }
|
|
.table-stack td[data-label]:not([data-label=""])::before { content: attr(data-label) ": "; color: var(--bs-secondary-color); font-size: .8rem; }
|
|
.table-stack td:last-child { padding-top: .4rem; }
|
|
h2 { font-size: 1.4rem; }
|
|
.card-body { padding: .75rem; }
|
|
.table { font-size: .875rem; }
|
|
.table .btn-sm { padding: .15rem .4rem; }
|
|
}
|
|
|
|
.list-group-item {
|
|
border: none;
|
|
background-color: transparent;
|
|
}
|
|
|
|
.dataTables_wrapper .dataTables_paginate .paginate_button.current,
|
|
.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
|
|
background: var(--bs-primary) !important;
|
|
border-color: var(--bs-primary) !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
.item-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
object-fit: contain;
|
|
}
|
|
|
|
pre.log-output {
|
|
max-height: 70vh;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.item-tooltip {
|
|
--bs-tooltip-bg: #11141a;
|
|
--bs-tooltip-color: #e9ecef;
|
|
--bs-tooltip-opacity: 0.97;
|
|
}
|
|
|
|
.item-tooltip .tooltip-inner {
|
|
max-width: 320px;
|
|
border: 1px solid #3a3f4b;
|
|
text-align: left;
|
|
}
|
|
|
|
.rarity-2 { color: #4fc948; }
|
|
.rarity-3 { color: #3e8ee8; }
|
|
.rarity-4 { color: #b44be1; }
|
|
.item-equipped { color: #d16f05; }
|
|
|
|
/* ---- Reports charts (palette validated against the dark card surface #212529) ---- */
|
|
:root {
|
|
--viz-surface: #212529;
|
|
--viz-up: #3987e5;
|
|
--viz-down: #d95926;
|
|
--viz-grid: #2c2c2a;
|
|
--viz-baseline: #6c757d;
|
|
--viz-tick: #898781;
|
|
--viz-hover: rgba(255, 255, 255, 0.06);
|
|
}
|
|
|
|
.viz { width: 100%; min-height: 60px; }
|
|
.viz svg { display: block; }
|
|
.viz-grid { stroke: var(--viz-grid); stroke-width: 1; }
|
|
.viz-baseline { stroke: var(--viz-baseline); stroke-width: 1; }
|
|
.viz-tick { fill: var(--viz-tick); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
.viz-up { fill: var(--viz-up); background: var(--viz-up); }
|
|
.viz-down { fill: var(--viz-down); background: var(--viz-down); }
|
|
.viz-hit { fill: transparent; cursor: crosshair; }
|
|
.viz-hit.viz-hover { fill: var(--viz-hover); }
|
|
.viz-num { font-variant-numeric: tabular-nums; }
|
|
.viz-hero { font-size: 1.75rem; font-weight: 600; line-height: 1.2; }
|
|
|
|
.viz-legend { display: flex; gap: 1rem; font-size: 0.85rem; color: var(--bs-secondary-color); }
|
|
.viz-key { display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
.viz-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; flex: none; }
|
|
|
|
.viz-tooltip {
|
|
position: fixed;
|
|
z-index: 1080;
|
|
pointer-events: none;
|
|
min-width: 160px;
|
|
padding: 0.5rem 0.65rem;
|
|
font-size: 0.85rem;
|
|
color: var(--bs-body-color);
|
|
background: #11141a;
|
|
border: 1px solid #3a3f4b;
|
|
border-radius: 6px;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
}
|
|
.viz-tip-row { display: flex; align-items: center; gap: 0.4rem; }
|
|
|
|
.viz-map { position: relative; width: 100%; }
|
|
.viz-map canvas { display: block; max-width: 100%; border-radius: 4px; cursor: crosshair; }
|
|
.viz-scale { display: inline-flex; align-items: center; gap: 0.5rem; }
|
|
.viz-ramp {
|
|
display: inline-block;
|
|
width: 120px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
background: linear-gradient(to right, #184f95, #2a78d6, #6da7ec, #cde2fb);
|
|
}
|
|
.viz-scroll { max-height: 22rem; overflow-y: auto; }
|
|
/* In a card that fills the window (.panel-fill) the map takes the room left and reports.js fits the canvas, centred, into it */
|
|
@media (min-width: 992px) {
|
|
.panel-fill .viz-map { flex: 1 1 auto; min-height: 0; }
|
|
.panel-fill .viz-map canvas { position: absolute; inset: 0; margin: auto; }
|
|
}
|
|
@media (min-width: 1200px) { .panel-stack-xl .viz-scroll { max-height: none; } }
|
|
|
|
#itemLot { min-width: 18rem; }
|
|
#traceId { min-width: 16rem; }
|
|
|
|
.qr-box { width: 180px; height: 180px; background: #fff; border-radius: 6px; padding: 0; }
|
|
.qr-box svg { width: 100%; height: 100%; display: block; }
|
|
|
|
/* Sidebar groups that fold away; the dot shows while a closed group holds a waiting badge */
|
|
.nav-group { margin-top: .25rem; }
|
|
.nav-group-toggle {
|
|
display: flex; align-items: center; gap: .4rem; width: 100%;
|
|
padding: .45rem 1rem; border: 0; background: none;
|
|
color: var(--bs-secondary-color); font-size: .75rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; text-align: left;
|
|
}
|
|
.nav-group-toggle:hover { color: var(--bs-body-color); }
|
|
.nav-group-toggle::after {
|
|
content: ""; margin-left: auto; width: .45rem; height: .45rem;
|
|
border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
|
|
transform: rotate(45deg); transition: transform .15s;
|
|
}
|
|
.nav-group-toggle[aria-expanded="false"]::after { transform: rotate(-45deg); }
|
|
.nav-group-dot { display: none; width: .45rem; height: .45rem; border-radius: 50%; background: var(--bs-danger); }
|
|
.nav-group:has(.collapse:not(.show) [data-badge]:not(.d-none)) .nav-group-dot { display: inline-block; }
|
|
.sidebar .list-group-item { padding: .4rem 1rem; border-radius: .375rem; }
|
|
.sidebar .nav-group .list-group-item { padding-left: 1.5rem; }
|
|
|
|
/* Top bar: the signed-in user's menu sits at the right */
|
|
.topbar { z-index: 1020; min-height: 3rem; }
|
|
.user-avatar {
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
width: 1.9rem; height: 1.9rem; border-radius: 50%;
|
|
background: var(--bs-primary); color: #fff; font-weight: 600; font-size: .85rem;
|
|
}
|
|
|
|
/* The page column: clip wide content sideways without becoming a scroll container (that would stop position: sticky) */
|
|
.main-column { min-width: 0; overflow-x: clip; }
|
|
|
|
/* ---- Layout helpers that save page scrolling (dashboard.js keeps --topbar-h and --fill-top current) ---- */
|
|
:root { --topbar-h: 3.5rem; }
|
|
|
|
/* .fill-viewport[-lg|-xl]: reaches from where it starts to the bottom of the window, so the page needn't scroll while you
|
|
work in it (a map, a list with its details). The -lg/-xl forms only do so from that width up; below it they size to their content. */
|
|
.fill-viewport { height: calc(100dvh - var(--fill-top, 12rem)); min-height: var(--fill-min, 20rem); }
|
|
@media (min-width: 992px) { .fill-viewport-lg { height: calc(100dvh - var(--fill-top, 12rem)); min-height: var(--fill-min, 20rem); } }
|
|
@media (min-width: 1200px) { .fill-viewport-xl { height: calc(100dvh - var(--fill-top, 12rem)); min-height: var(--fill-min, 20rem); } }
|
|
/* A filling .row passes its height on to its columns */
|
|
.row.fill-viewport > * { height: 100%; }
|
|
@media (min-width: 992px) { .row.fill-viewport-lg > * { height: 100%; } }
|
|
@media (min-width: 1200px) { .row.fill-viewport-xl > * { height: 100%; } }
|
|
|
|
/* .panel-fill: a card whose body takes the height left under its header and scrolls inside (.panel-scroll does the same for any box) */
|
|
.panel-fill { display: flex; flex-direction: column; min-height: 0; }
|
|
.panel-fill > .card-body, .panel-fill > .list-group, .panel-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
|
.panel-fill > .card-header, .panel-fill > .card-footer { flex: none; }
|
|
/* .panel-cap: a .panel-fill card that grows with its content up to most of a window, then scrolls inside (lists that can get long) */
|
|
.panel-cap { max-height: min(36rem, 70dvh); }
|
|
/* Table headers stay at the top of a scrolling panel (a .table-responsive inside would otherwise become the scroll box) */
|
|
.panel-fill > .card-body .table-responsive, .panel-scroll .table-responsive { overflow: visible; }
|
|
.panel-fill thead th, .panel-scroll thead th, .sticky-head thead th {
|
|
position: sticky; top: 0; z-index: 2;
|
|
background: var(--bs-card-bg, var(--bs-body-bg)); box-shadow: inset 0 -1px 0 var(--bs-border-color);
|
|
}
|
|
|
|
/* .panel-stack-xl: a side column whose cards share the height of the row's main content (its tallest neighbour) from xl up,
|
|
each .panel-fill card scrolling inside; below xl the cards just stack. Wrap the cards in .panel-stack-xl > .panel-stack-inner. */
|
|
@media (min-width: 1200px) {
|
|
.panel-stack-xl { position: relative; height: 100%; min-height: 20rem; }
|
|
.panel-stack-xl > .panel-stack-inner { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 1rem; }
|
|
.panel-stack-xl > .panel-stack-inner > * { margin: 0 !important; }
|
|
.panel-stack-xl > .panel-stack-inner > .panel-fill { flex: 1 1 0; }
|
|
.panel-stack-xl > .panel-stack-inner > .panel-fit { flex: 0 1 auto; max-height: 50%; }
|
|
}
|
|
|
|
/* .sticky-toolbar: filters and buttons that stay under the top bar while the content below scrolls */
|
|
.sticky-toolbar { position: sticky; top: var(--topbar-h); z-index: 5; background: var(--bs-body-bg); padding-top: .5rem; padding-bottom: .5rem; }
|
|
|
|
/* Long lists in DataTables keep their column headers in view under the top bar (on narrow screens the table scrolls sideways instead) */
|
|
@media (min-width: 992px) {
|
|
table.dataTable > thead > tr > th {
|
|
position: sticky; top: var(--topbar-h); z-index: 2;
|
|
background: var(--bs-card-bg, var(--bs-body-bg)); box-shadow: inset 0 -1px 0 var(--bs-border-color);
|
|
}
|
|
.panel-fill table.dataTable > thead > tr > th, .panel-scroll table.dataTable > thead > tr > th { top: 0; }
|
|
.modal table.dataTable > thead > tr > th { position: static; }
|
|
}
|
|
|
|
/* details.about-text: a page's longer explanation, folded to one line until asked for (Ctrl+F still finds and opens it) */
|
|
details.about-text > summary {
|
|
display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; list-style: none;
|
|
color: var(--bs-secondary-color); font-size: .875rem;
|
|
}
|
|
details.about-text > summary::-webkit-details-marker { display: none; }
|
|
details.about-text > summary::before {
|
|
content: "?"; display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; height: 1.1rem; border-radius: 50%;
|
|
border: 1px solid currentColor; font-size: .7rem; font-weight: 600;
|
|
}
|
|
details.about-text > summary:hover { color: var(--bs-body-color); }
|
|
details.about-text[open] > summary { margin-bottom: .35rem; }
|
|
details.about-text > :not(summary) { color: var(--bs-secondary-color); font-size: .875rem; }
|
|
|
|
/* Wide screens: the menu can be hidden (the top bar's menu button; header.jinja2 keeps the choice) */
|
|
@media (min-width: 992px) {
|
|
html.sidebar-hidden nav.sidebar { display: none !important; }
|
|
html.sidebar-hidden .topbar-brand { display: inline !important; }
|
|
}
|
|
|
|
/* Changing pages without a reload (nav.js): a thin bar at the top while the next page loads */
|
|
main:focus { outline: none; }
|
|
.nav-progress {
|
|
position: fixed; top: 0; left: 0; z-index: 1100; width: 100%; height: 3px;
|
|
background: var(--bs-primary); transform-origin: left; transform: scaleX(0); opacity: 0; pointer-events: none;
|
|
}
|
|
html.nav-loading .nav-progress { opacity: 1; animation: nav-progress 10s cubic-bezier(.08, .7, .2, 1) forwards; }
|
|
html.nav-loading main { opacity: .65; transition: opacity .2s; }
|
|
@keyframes nav-progress { from { transform: scaleX(.08); } to { transform: scaleX(.92); } }
|
|
@media (prefers-reduced-motion: reduce) { html.nav-loading .nav-progress { animation: none; transform: scaleX(.5); } }
|
|
|
|
/* A playback slider with world change markers above it (World 3D capture replay, Packet Captures): one tick per change,
|
|
labelled with the zone; clicking one seeks to it */
|
|
.timeline-track { position: relative; min-width: 10rem; padding-top: 1.1rem; }
|
|
.timeline-track > input[type=range] { width: 100%; display: block; }
|
|
.timeline-markers { position: absolute; left: .5rem; right: .5rem; top: 0; height: 100%; pointer-events: none; }
|
|
.timeline-marker { position: absolute; top: 0; bottom: .35rem; width: 0; padding: 0; border: 0; border-left: 2px solid var(--bs-warning);
|
|
background: none; pointer-events: auto; cursor: pointer; }
|
|
.timeline-marker > span { position: absolute; left: 2px; top: 0; font-size: .68rem; line-height: 1; white-space: nowrap; padding: .05rem .25rem;
|
|
border-radius: .2rem; background: var(--bs-warning-bg-subtle); color: var(--bs-warning-text-emphasis); max-width: 9rem; overflow: hidden;
|
|
text-overflow: ellipsis; }
|
|
.timeline-marker:hover > span, .timeline-marker:focus-visible > span { max-width: none; z-index: 1; }
|
|
|
|
/* Client system info: values whose caveat is a tooltip; unreliable ones stand out more */
|
|
.sysinfo-tip { text-decoration: underline dotted; text-underline-offset: .2em; cursor: help; }
|
|
.sysinfo-unreliable { text-decoration-color: var(--bs-warning); }
|
|
#sysinfoTable tbody tr { cursor: pointer; }
|