|
|
|
|
@@ -63,40 +63,84 @@
|
|
|
|
|
.kpi-strip { grid-auto-columns: minmax(0, 1fr); overflow-x: visible; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Icon beside the figure, nothing beneath it. The card used to carry a trend
|
|
|
|
|
row and a sparkline; with those gone it is a label and a number, and the
|
|
|
|
|
layout should not still be shaped around what it no longer holds. */
|
|
|
|
|
.kpi {
|
|
|
|
|
border: 1px solid var(--color-line); border-radius: 16px;
|
|
|
|
|
background: var(--color-surface); padding: 16px;
|
|
|
|
|
display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.kpi-icon {
|
|
|
|
|
width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center;
|
|
|
|
|
background: var(--color-brand-tint); color: var(--color-brand);
|
|
|
|
|
}
|
|
|
|
|
.kpi-head { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
|
|
|
.kpi-note { font: 400 11.5px/1.35 var(--font-sans); color: var(--color-ink-4); }
|
|
|
|
|
/* Two lines rather than an ellipsis. With Nearle Buddy open each card is about
|
|
|
|
|
195px wide, and "Cancelled Orders" truncated to "Cance..." is a label that
|
|
|
|
|
has stopped doing its job. The min-height keeps all five value rows level
|
|
|
|
|
whether the label took one line or two. */
|
|
|
|
|
.kpi-label { font: 500 12.5px/1.25 var(--font-sans); color: var(--color-ink-3); }
|
|
|
|
|
.kpi-value { font: 600 22px/1.15 var(--font-sans); color: var(--color-ink-1); font-variant-numeric: tabular-nums; }
|
|
|
|
|
/* The Console's own KPI tile used to live here — `.kpi`, `.kpi-icon`,
|
|
|
|
|
`.kpi-label`, `.kpi-value`, `.kpi-note` — a second implementation of the
|
|
|
|
|
same object that `components/KpiCard.tsx` renders, with a different icon
|
|
|
|
|
radius, a different value size and a different gap. Both appeared on the
|
|
|
|
|
same screen. The component won, this strip now renders it, and the classes
|
|
|
|
|
are gone rather than left as a near-copy for someone to edit by mistake. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Panels ─────────────────────────────────────────────────────────────── */
|
|
|
|
|
/* ── Panels ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
The Console's panels are the SAME object as `components/Panel.tsx` — a
|
|
|
|
|
framed card with a header band, a body and a footer band. They stay as CSS
|
|
|
|
|
classes rather than becoming that component because these six sections each
|
|
|
|
|
lay their own body out (a chart, a donut, a map, a list of alerts) and have
|
|
|
|
|
no table or pager to hand it; the component's job is the table case.
|
|
|
|
|
|
|
|
|
|
What matters is that they are the same DESIGN. They were not: this file had
|
|
|
|
|
a white header with a 15.5px title and a sub-line, the component has a
|
|
|
|
|
tinted band with an 11px eyebrow, and the two sat on the same screen. The
|
|
|
|
|
band and the type below are now the component's, restated here.
|
|
|
|
|
|
|
|
|
|
The panel no longer pads itself — the bands and the body pad separately, or
|
|
|
|
|
a header band cannot reach the panel's edges. */
|
|
|
|
|
.panel {
|
|
|
|
|
border: 1px solid var(--color-line); border-radius: 16px;
|
|
|
|
|
background: var(--color-surface); padding: 18px;
|
|
|
|
|
display: flex; flex-direction: column; gap: 14px; min-width: 0;
|
|
|
|
|
border: var(--card-border); border-radius: var(--card-radius);
|
|
|
|
|
background: var(--card-bg); box-shadow: var(--card-shadow);
|
|
|
|
|
display: flex; flex-direction: column; min-width: 0;
|
|
|
|
|
/* The clip belongs on the element that owns the radius, or the header
|
|
|
|
|
band's tint squares off the top two corners. */
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The body. Every direct child of `.panel` that is not a band is content, so
|
|
|
|
|
the padding and the stack live on a wrapper the sections already render. */
|
|
|
|
|
.panel > :not(.panel-head):not(.panel-foot) {
|
|
|
|
|
padding: 16px 18px;
|
|
|
|
|
}
|
|
|
|
|
/* Consecutive content blocks should not double their gutter. */
|
|
|
|
|
.panel > :not(.panel-head):not(.panel-foot) ~ :not(.panel-head):not(.panel-foot) {
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.panel-head {
|
|
|
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
|
|
|
gap: 14px; flex-wrap: wrap;
|
|
|
|
|
min-height: 48px; padding: 8px 18px;
|
|
|
|
|
background: var(--color-surface-subtle);
|
|
|
|
|
border-bottom: var(--card-border);
|
|
|
|
|
}
|
|
|
|
|
/* An eyebrow, matching the table panels: 11px caps. A 15.5px heading inside a
|
|
|
|
|
card competes with the page's own title above it. */
|
|
|
|
|
.panel-title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font: 600 11px/1.2 var(--font-sans);
|
|
|
|
|
letter-spacing: 0.08em; text-transform: uppercase;
|
|
|
|
|
color: var(--color-ink-3);
|
|
|
|
|
}
|
|
|
|
|
/* Title and sub-line sit on ONE row, the way the table panel's title and count
|
|
|
|
|
do. Every section wraps the pair in a plain `div`, which stacks them by
|
|
|
|
|
default — and a stacked pair is the two-line header this band replaced. */
|
|
|
|
|
.panel-head > div:first-child {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.panel-sub {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font: 400 12px/1.4 var(--font-sans);
|
|
|
|
|
color: var(--color-ink-4);
|
|
|
|
|
}
|
|
|
|
|
.panel-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
|
|
|
|
|
.panel-title { margin: 0; font: 600 15.5px/1.25 var(--font-sans); color: var(--color-ink-1); }
|
|
|
|
|
.panel-sub { margin: 3px 0 0; font: 400 12.5px/1.4 var(--font-sans); color: var(--color-ink-3); }
|
|
|
|
|
.panel-foot {
|
|
|
|
|
display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;
|
|
|
|
|
font: 400 12.5px/1 var(--font-sans); color: var(--color-ink-3);
|
|
|
|
|
padding: 8px 18px;
|
|
|
|
|
background: var(--color-surface-subtle);
|
|
|
|
|
border-top: var(--card-border);
|
|
|
|
|
font: 400 12px/1.5 var(--font-sans); color: var(--color-ink-3);
|
|
|
|
|
}
|
|
|
|
|
.panel-empty {
|
|
|
|
|
display: flex; flex-direction: column; gap: 5px; padding: 28px 20px; text-align: center;
|
|
|
|
|
@@ -198,8 +242,8 @@
|
|
|
|
|
.share {
|
|
|
|
|
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
|
|
|
|
|
padding: 10px 14px;
|
|
|
|
|
border: 1px solid var(--color-line); border-radius: 12px;
|
|
|
|
|
background: var(--color-surface-subtle);
|
|
|
|
|
border: var(--card-border); border-radius: var(--card-radius-sm);
|
|
|
|
|
background: var(--card-bg-subtle);
|
|
|
|
|
}
|
|
|
|
|
.share .donut-key { align-self: center; }
|
|
|
|
|
.share-label { font: 400 12.5px/1 var(--font-sans); color: var(--color-ink-3); }
|
|
|
|
|
@@ -236,8 +280,8 @@
|
|
|
|
|
.quick-list { display: flex; flex-direction: column; gap: 8px; }
|
|
|
|
|
.quick {
|
|
|
|
|
display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 12px; align-items: center;
|
|
|
|
|
padding: 11px 12px; border: 1px solid var(--color-line); border-radius: 12px;
|
|
|
|
|
text-decoration: none; background: var(--color-surface);
|
|
|
|
|
padding: 11px 12px; border: var(--card-border); border-radius: var(--card-radius-sm);
|
|
|
|
|
text-decoration: none; background: var(--card-bg);
|
|
|
|
|
}
|
|
|
|
|
.quick-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
|
|
|
.quick-note { font: 400 11.5px/1.3 var(--font-sans); color: var(--color-ink-3); }
|
|
|
|
|
@@ -277,13 +321,8 @@
|
|
|
|
|
.chip[data-tone="healthy"][data-active="true"] { background: #1c6b47; border-color: #1c6b47; }
|
|
|
|
|
.chip:focus-visible { outline: 2px solid var(--color-brand); outline-offset: 2px; }
|
|
|
|
|
|
|
|
|
|
.search {
|
|
|
|
|
display: inline-flex; align-items: center; gap: 8px; padding: 0 11px; height: 32px;
|
|
|
|
|
min-width: 220px; border: 1px solid var(--color-line); border-radius: 10px;
|
|
|
|
|
background: var(--color-surface); color: var(--color-ink-4);
|
|
|
|
|
}
|
|
|
|
|
.search input { border: 0; outline: 0; background: none; flex: 1; min-width: 0; font: 400 13px/1 var(--font-sans); color: var(--color-ink-1); }
|
|
|
|
|
.search:focus-within { border-color: var(--color-brand); }
|
|
|
|
|
/* `.search` was here — the Console and Counters each drew a label-wrapping-an-
|
|
|
|
|
input version of the search box. Both use `components/SearchInput` now. */
|
|
|
|
|
|
|
|
|
|
/* ── Segmented control ──────────────────────────────────────────────────── */
|
|
|
|
|
.seg { display: inline-flex; padding: 3px; gap: 2px; border: 1px solid var(--color-line); border-radius: 10px; background: var(--color-surface-subtle); }
|
|
|
|
|
@@ -303,7 +342,7 @@
|
|
|
|
|
|
|
|
|
|
.attention-card {
|
|
|
|
|
display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: 12px;
|
|
|
|
|
align-items: center; padding: 13px 14px; border: 1px solid var(--color-line); border-radius: 13px;
|
|
|
|
|
align-items: center; padding: 13px 14px; border: var(--card-border); border-radius: var(--card-radius);
|
|
|
|
|
}
|
|
|
|
|
.attention-card[data-tone="critical"] { background: #fdefee; border-color: #f2cbc7; }
|
|
|
|
|
.attention-card[data-tone="attention"] { background: #fdf6e8; border-color: #eeddb4; }
|
|
|
|
|
@@ -324,8 +363,8 @@
|
|
|
|
|
/* ── Loading ────────────────────────────────────────────────────────────── */
|
|
|
|
|
@keyframes sk-pulse { 0%, 100% { opacity: .55; } 50% { opacity: .9; } }
|
|
|
|
|
.sk-card, .sk-chart {
|
|
|
|
|
border: 1px solid var(--color-line); border-radius: 16px;
|
|
|
|
|
background: var(--color-surface-subtle); animation: sk-pulse 1.3s ease-in-out infinite;
|
|
|
|
|
border: var(--card-border); border-radius: var(--card-radius);
|
|
|
|
|
background: var(--card-bg-subtle); animation: sk-pulse 1.3s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
.sk-card { height: 104px; }
|
|
|
|
|
.sk-chart { height: 300px; }
|
|
|
|
|
|