/* ==========================================================================
   MKH - Dashboard
   Each person's own tabs of widgets. Requires tokens.css, base.css,
   components.css and layout.css.

   The grid itself is GridStack's (lib/gridstack), fetched with the page that
   needs it; what is here is how a widget looks inside it, how the widgets
   stand one under another where there is no room for a grid, and the
   palette the charts are drawn in.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Chart palette

   Six colours in a fixed order, the brand's indigo first, checked for the
   ways colour vision differs and against both themes' cards (light
   #FFFFFF, dark #14162E): every neighbouring pair stays apart under
   protanopia and deuteranopia, every colour clears 3:1 on the dark card, and
   on the light card the three that do not are always drawn with their
   figure written beside them. The dark column is the same hues stepped for
   the dark card, not a second palette.

   "quiet" is what a comparison is drawn in - last year beside this year -
   and "other" is a seventh slice and anything after it.
   -------------------------------------------------------------------------- */

:root {
  --chart-1: #3C40BE;
  --chart-2: #EB6834;
  --chart-3: #1BAF7A;
  --chart-4: #EDA100;
  --chart-5: #E87BA4;
  --chart-6: #008300;
  --chart-other: #9FA8BC;
  --chart-quiet: #666F8A;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --chart-1: #686BCF;
    --chart-2: #D95926;
    --chart-3: #199E70;
    --chart-4: #C98500;
    --chart-5: #D55181;
    --chart-6: #008300;
    --chart-other: #717B94;
    --chart-quiet: #7F86AE;
  }
}

:root[data-theme="dark"] {
  --chart-1: #686BCF;
  --chart-2: #D95926;
  --chart-3: #199E70;
  --chart-4: #C98500;
  --chart-5: #D55181;
  --chart-6: #008300;
  --chart-other: #717B94;
  --chart-quiet: #7F86AE;
}

/* --------------------------------------------------------------------------
   Opening
   -------------------------------------------------------------------------- */

.dash-wait {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 48px 0;
  color: var(--txt-2);
  font-size: var(--fs-base);
}

.dash-wait__spin {
  width: 18px;
  height: 18px;
  border: 2px solid var(--line);
  border-top-color: var(--primary);
  border-radius: var(--r-full);
  animation: mkh-spin 0.7s linear infinite;
}

/* --------------------------------------------------------------------------
   Tabs

   The app's own tab strip, which scrolls sideways on a phone, with a count
   on each tab and a "New tab" at the end of the row. In edit mode the tools
   for the open tab sit beside it, and drop under it where there is no room.
   -------------------------------------------------------------------------- */

.dash-tabs {
  display: flex;
  align-items: flex-end;
  gap: 8px 14px;
  flex-wrap: wrap;
}

.dash-tabs__strip { flex: 1 1 320px; min-width: 0; margin-bottom: 0; }

.dash-tab { display: inline-flex; align-items: center; gap: 8px; }

.dash-tab__count {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  color: var(--txt-3);
  font-size: 10.5px;
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}

.dash-tab.is-active .dash-tab__count { background: var(--primary-soft); color: var(--primary-soft-fg); }

.dash-tab--new { color: var(--txt-3); }
.dash-tab--new svg { width: 14px; height: 14px; }
.dash-tab--new:disabled { opacity: 0.5; cursor: not-allowed; }

.dash-tabs__tools {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding-bottom: 6px;
}

.dash-tabs__remove { color: var(--bad); }
.dash-tabs__remove:disabled { color: var(--txt-3); }

/* --------------------------------------------------------------------------
   Notes above the grid
   -------------------------------------------------------------------------- */

/* The page spaces what is on it, so the notes bring no margin of their own. */
.dash-alert { margin: 0; }

.dash-hint,
.dash-hidden {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: 1.5;
}

.dash-hint svg { width: 16px; height: 16px; flex: none; margin-top: 1px; color: var(--info); }
.dash-hint__phone { display: none; }

/* The hint says what the layout in front of the reader does. The script lays
   the widgets one under another at the same width this query asks about. */
@container content (max-width: 900px) {
  .dash-hint__grid { display: none; }
  .dash-hint__phone { display: inline; }
}

/* --------------------------------------------------------------------------
   The grid

   GridStack lays the widgets out wherever the tab is wider than 900 pixels:
   twelve columns, rows of 76 pixels, 8 pixels of air around each widget.
   Narrower than that a quarter of the grid, which is a tile, is too narrow
   for a figure and what it is set against.
   The widgets wait out of sight until it has, then rise in one after
   another, so a tab never shows a column of boxes jumping into a grid.
   -------------------------------------------------------------------------- */

.dash-grid { position: relative; min-height: 160px; }

/* GridStack keeps 8 pixels of air around every widget, the outer edges too.
   Reaching past the page by that much lines the widgets up with the tabs
   above them. The script measures the page, not this, to choose the layout. */
.dash-grid.grid-stack { margin: -8px; }

.dash-grid:not(.is-ready) > .dash-item { opacity: 0; }

.dash-grid.is-ready > .dash-item {
  animation: mkh-rise 420ms var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 45ms);
}

/* What is being dragged or resized lifts off the page a little, and the
   space it will land in is drawn where it will land. */
.dash-grid .grid-stack-placeholder > .placeholder-content {
  border: 2px dashed var(--primary);
  border-radius: var(--r-lg);
  background: var(--primary-soft);
}

.dash-grid .ui-draggable-dragging > .dash-widget,
.dash-grid .ui-resizable-resizing > .dash-widget { box-shadow: var(--sh-lg); }

/* The place of a widget the role can no longer see, held empty. In edit
   mode it is outlined, which is why nothing can be dropped there. Standing
   one under another there is no place to hold. */
.dash-item--hidden { pointer-events: none; }
.dash-grid--stacked > .dash-item--hidden { display: none; }

.dash-held {
  display: grid;
  place-items: center;
  border: 1px dashed transparent;
  border-radius: var(--r-lg);
  color: transparent;
  font-size: var(--fs-xs);
}

.dash-board.is-editing .dash-held { border-color: var(--line-strong); color: var(--txt-3); }

/* GridStack's resize corner, quieter than its own and only in edit mode. */
.dash-grid > .grid-stack-item > .ui-resizable-se {
  right: 10px;
  bottom: 10px;
  width: 16px;
  height: 16px;
  opacity: 0.6;
}

/* --------------------------------------------------------------------------
   One under another

   Where the tab is 900 pixels wide or less there is no grid: the widgets
   stand in a single column in the order the phone keeps, each as tall as
   what it shows. Between 560 and 900, a large phone on its side or a
   tablet, the ones narrower than half the grid pair up two to a row, and
   the ones half its width or wider keep a row to themselves.
   -------------------------------------------------------------------------- */

.dash-grid--stacked {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
}

.dash-grid--stacked > .dash-item { position: relative; min-width: 0; }

/* Two tiles side by side are the height of the taller, so the row reads
   as a row. */
.dash-grid--stacked > .dash-item > .dash-widget { position: relative; height: 100%; min-height: 132px; }

.dash-grid--stacked .dash-widget--chart .dash-chart { flex: none; height: 220px; }

/* A widget the width of the page between two tiles would leave the first
   alone on its row, so a later tile moves up beside it. */
@container content (width > 560px) and (width <= 900px) {
  .dash-grid--stacked { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-flow: row dense; }
  .dash-grid--stacked > .dash-item[data-span="wide"] { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   A widget
   -------------------------------------------------------------------------- */

.dash-widget {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-xs);
  container-type: inline-size;
  container-name: widget;
  transition: box-shadow var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease);
}

.dash-widget__head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 6px 8px 2px 14px;
}

.dash-widget__title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--txt);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-widget__tools { display: flex; align-items: center; gap: 2px; flex: none; }

.dash-widget__tools .is-spinning { animation: mkh-spin 0.7s linear infinite; }

.dash-widget__grip { display: inline-grid; place-items: center; width: 14px; flex: none; color: var(--txt-3); }
.dash-widget__grip svg { width: 14px; height: 14px; }

/* The arrows a phone moves a widget with. On a grid it is dragged. */
.dash-widget__phone { display: none; }
.dash-board.is-editing .dash-grid--stacked .dash-widget__phone { display: inline-grid; }

/* On the grid a widget is the height it was given, so it can ask how tall
   it is as well as how wide. Standing one under another it is as tall as
   what it shows, and can ask only its width. */
.dash-grid.grid-stack .dash-widget { container-type: size; }

.dash-widget__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  padding: 2px 14px 4px;
  overflow: auto;
}

/* A list longer than its widget scrolls rather than squeezing its lines:
   a caption that clips its overflow would otherwise be squeezed to nothing.
   A tile's figure is the exception, and gives way at its foot. */
.dash-widget__body > * { flex-shrink: 0; }
.dash-widget__body > .dash-figure { flex-shrink: 1; }

.dash-widget__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 30px;
  padding: 4px 14px 8px;
  font-size: var(--fs-xs);
  color: var(--txt-3);
}

/* The report's name is what the footer is for, so the note of when it was
   updated has whatever the link leaves, and says as much as fits in that.
   The room is measured on the note itself: how much a widget of a given
   width leaves depends on how long its report's name is, and "Slow and
   Non-moving Stock" leaves a good deal less than "Purchases". */
.dash-widget__fresh {
  flex: 1 1 0;
  min-width: 0;
  container: fresh / inline-size;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dash-widget__fresh-short { display: none; }

/* "Updated at 12:45 PM" is the longest the note says, at 114 pixels, and
   its short form, "12:45 PM", "Just now" or "59m ago", is 51. Less room than
   that says nothing rather than half of it. The long form without its first
   word is not offered: "just now" and "at 9:30 AM" do not stand alone. */
@container fresh (max-width: 118px) {
  .dash-widget__fresh-word,
  .dash-widget__fresh-long { display: none; }
  .dash-widget__fresh-short { display: inline; }
}

@container fresh (max-width: 54px) {
  .dash-widget__fresh-short { display: none; }
}

.dash-widget__link {
  display: inline-flex;
  align-items: center;
  flex: 0 1 auto;
  gap: 2px;
  min-width: 0;
  margin-left: auto;
  color: var(--txt-3);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  text-decoration: none;
}
.dash-widget__link:hover { color: var(--primary); text-decoration: none; }

/* A finger needs more than the line of small type the link is. The target
   reaches past it rather than the footer growing to fit one. */
@media (pointer: coarse) {
  .dash-widget__link { position: relative; }
  .dash-widget__link::after { content: ""; position: absolute; inset: -12px -8px; }
}
.dash-widget__link svg { width: 13px; height: 13px; flex: none; }
.dash-widget__link-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Edit mode: every widget says it can be picked up. */
.dash-board.is-editing .dash-widget { border-style: dashed; border-color: var(--line-strong); }
.dash-board.is-editing .dash-grid:not(.dash-grid--stacked) .dash-widget__head { cursor: grab; }
.dash-board.is-editing .dash-grid:not(.dash-grid--stacked) .dash-widget:hover { border-color: var(--primary); }

/* --------------------------------------------------------------------------
   A figure: what a tile shows
   -------------------------------------------------------------------------- */

/* Allowed to be shorter than what it holds, so on the grid the line at the
   foot of a tile gives way before anything else does. */
.dash-figure { display: flex; flex-direction: column; gap: 3px; min-width: 0; min-height: 0; flex: 1 1 auto; }

.dash-figure__caption {
  overflow: hidden;
  font-size: var(--fs-xs);
  color: var(--txt-3);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The figure is the one thing in the tile read from across the room. In
   the sans everything else is set in, proportional digits, sized by the
   width of the tile it is in: 26 pixels from a tile about 290 wide, down to
   17 in the narrowest, where a crore still fits. */
.dash-figure__value {
  font-family: var(--font-sans);
  font-size: clamp(17px, 6px + 7cqi, 26px);
  font-weight: var(--fw-semibold);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--txt);
  white-space: nowrap;
}

.dash-figure__value--bad { color: var(--bad-fg); }
.dash-figure__value--warn { color: var(--warn-fg); }

/* What a figure counts, when it is not rupees: "12 runs", "340 packs". Set
   smaller and lighter than the figure, on its line. */
.dash-figure__unit {
  margin-left: 4px;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--txt-3);
}

/* A tile made two rows tall keeps its figure, the bar under it and which
   way it moved, and drops the detail rather than scrolling it. */
@container widget (height <= 180px) {
  .dash-widget--tile .dash-more,
  .dash-widget--tile .dash-spark { display: none; }

  .dash-widget--tile .dash-figure__delta > span:last-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.dash-figure__delta {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--txt-2);
}

.dash-figure__note { font-size: var(--fs-xs); color: var(--txt-3); line-height: 1.45; }
.dash-figure__note--warn { color: var(--warn-fg); }

/* Which way it moved is said by the arrow and the words; the colour on the
   arrow says whether that is good, for a figure where up is good. */
.dash-delta__arrow { width: 13px; height: 13px; flex: none; color: var(--txt-3); }
.dash-delta--up.dash-delta--up-is-good .dash-delta__arrow { color: var(--ok); }
.dash-delta--down.dash-delta--up-is-good .dash-delta__arrow { color: var(--bad); }

/* A cost, where more is the one that warns. */
.dash-delta--up.dash-delta--up-is-bad .dash-delta__arrow { color: var(--bad); }
.dash-delta--down.dash-delta--up-is-bad .dash-delta__arrow { color: var(--ok); }

/* The year of months under a figure: quiet, with the month being read
   picked out in the accent at its end. As tall as the tile has room for,
   between a hint of a line and a clear one: the figures above it are what
   the tile is for. */
.dash-spark {
  display: block;
  flex: 1 1 30px;
  width: 100%;
  min-height: 18px;
  max-height: 48px;
  margin-top: auto;
  overflow: visible;
}
.dash-spark__line { fill: none; stroke: var(--chart-quiet); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.dash-spark__ring { fill: none; stroke: var(--surface); stroke-width: 10; stroke-linecap: round; }
.dash-spark__end { fill: none; stroke: var(--chart-1); stroke-width: 7; stroke-linecap: round; }

/* --------------------------------------------------------------------------
   A meter: how much of a whole is in a worrying state

   The fill carries how worrying, and the track behind it is the lighter step
   of the same colour, so the bar reads as one state end to end. The words
   under it say the same, so the colour is never all there is.
   -------------------------------------------------------------------------- */

.dash-meter {
  height: 8px;
  margin-top: 6px;
  overflow: hidden;
  border-radius: var(--r-full);
  background: var(--surface-3);
}

.dash-meter__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: currentColor;
  transform-origin: left center;
  animation: dash-grow 700ms var(--ease-out) backwards 150ms;
}

.dash-meter--ok { color: var(--ok); background: var(--ok-bg); }
.dash-meter--warn { color: var(--warn); background: var(--warn-bg); }
.dash-meter--bad { color: var(--bad); background: var(--bad-bg); }

@keyframes dash-grow { from { transform: scaleX(0); } }

/* --------------------------------------------------------------------------
   Rows: a few named figures under a total
   -------------------------------------------------------------------------- */

.dash-rows { display: flex; flex-direction: column; margin: 6px 0 0; padding: 0; list-style: none; }

.dash-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0;
  border-top: 1px solid var(--line-soft);
  font-size: var(--fs-sm);
}

.dash-row__name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  color: var(--txt-2);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-row__value {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--txt);
  white-space: nowrap;
}

.dash-row--bad .dash-row__value { color: var(--bad-fg); }

.dash-row__flag {
  flex: none;
  margin-left: 4px;
  padding: 1px 6px;
  border-radius: var(--r-full);
  background: var(--bad-bg);
  color: var(--bad-fg);
  font-size: 10px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
}

.dash-swatch { display: inline-block; width: 9px; height: 9px; flex: none; border-radius: 3px; background: var(--swatch); }

/* Pills side by side under a figure, or beside a bill's amount. */
.dash-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 0; }
.dash-bill__badges { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; }

/* "Short" beside "Due Wed 21 Oct" is 175 pixels, which a widget under 360
   wide can only find by taking it from the customer's name: at 320 the name
   was left 75. Standing one under the other they need the wider of the two. */
@container widget (max-width: 360px) {
  .dash-bill__badges { flex-direction: column; align-items: flex-end; }
}

/* A whole split into its parts, side by side, a 2px gap of the card's
   colour between each. */
.dash-split {
  display: flex;
  gap: 2px;
  height: 8px;
  margin-top: 6px;
  overflow: hidden;
  border-radius: var(--r-full);
}

.dash-split__part {
  flex-basis: 0;
  min-width: 4px;
  background: var(--swatch);
  transform-origin: left center;
  animation: dash-grow 700ms var(--ease-out) backwards 150ms;
}

/* --------------------------------------------------------------------------
   A chart
   -------------------------------------------------------------------------- */

.dash-trend { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; }

.dash-trend__keys { display: flex; flex-wrap: wrap; gap: 4px 20px; margin: 0; }
.dash-trend__key { display: flex; flex-direction: column; gap: 1px; }
.dash-trend__key dt { font-size: var(--fs-xs); color: var(--txt-3); }
.dash-trend__key dd {
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  color: var(--txt);
  white-space: nowrap;
}
.dash-trend__key--quiet dd { font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--txt-2); }
.dash-trend__key dd.dash-trend__bad { color: var(--bad-fg); }

.dash-chart { position: relative; flex: 1 1 auto; min-height: 120px; }
.dash-chart > canvas { display: block; }

/* --------------------------------------------------------------------------
   A ranking: what sold most, line by line

   Each line is its name and figure, and under them a bar as long as the
   line is against the first, with its share and its change beside it. The
   ageing's bands are drawn the same way, coloured by how late the money is.
   -------------------------------------------------------------------------- */

.dash-empty-note { margin: 10px 0 0; font-size: var(--fs-sm); color: var(--txt-3); line-height: 1.5; }

.dash-rank {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.dash-rank__row { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

.dash-rank__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
  font-size: var(--fs-sm);
}

.dash-rank__name { min-width: 0; overflow: hidden; color: var(--txt); text-overflow: ellipsis; white-space: nowrap; }

.dash-rank__value {
  flex: none;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium);
  color: var(--txt);
}

.dash-rank__under { display: flex; align-items: center; gap: 8px; min-width: 0; }

.dash-rank__track {
  flex: 1 1 auto;
  height: 6px;
  min-width: 24px;
  overflow: hidden;
  border-radius: var(--r-full);
  background: var(--surface-3);
}

.dash-rank__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--chart-1);
  transform-origin: left center;
  animation: dash-grow 700ms var(--ease-out) backwards 150ms;
}

.dash-rank__meta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  margin-left: auto;
  font-size: var(--fs-xs);
  color: var(--txt-3);
  font-variant-numeric: tabular-nums;
}

.dash-rank__change { display: inline-flex; align-items: center; gap: 2px; color: var(--txt-2); }
.dash-rank__change .dash-delta__arrow { width: 12px; height: 12px; }
.dash-rank__change.dash-delta--up.dash-delta--up-is-good .dash-delta__arrow { color: var(--ok); }
.dash-rank__change.dash-delta--down.dash-delta--up-is-good .dash-delta__arrow { color: var(--bad); }

.dash-rank__new {
  padding: 1px 6px;
  border-radius: var(--r-full);
  background: var(--primary-soft);
  color: var(--primary-soft-fg);
  font-weight: var(--fw-semibold);
}

.dash-rank__row--rest .dash-rank__name,
.dash-rank__row--rest .dash-rank__value { color: var(--txt-3); }

/* A line that lost money. */
.dash-rank__value--bad { color: var(--bad-fg); }
.dash-rank__row--rest { padding-top: 6px; border-top: 1px solid var(--line-soft); }

/* The ageing's bands, by how late the money in them is. */
.dash-rank__row--ok .dash-rank__fill { background: var(--ok); }
.dash-rank__row--warn .dash-rank__fill { background: var(--warn); }
.dash-rank__row--bad .dash-rank__fill { background: var(--bad); }

/* --------------------------------------------------------------------------
   A ring: parts of one whole

   The ring and its key side by side where the widget is wide enough, the
   key under the ring where it is not. The total is in the middle.
   -------------------------------------------------------------------------- */

.dash-donut {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-height: 0;
  padding-top: 6px;
}

/* On the grid a widget is the height it was given, and a ring above four
   or more lines of key is taller than five rows hold. The ring gives way,
   down to a size its middle can still be read at, and the key keeps every
   line. Standing one under another nothing is short, and it is 168. */
.dash-donut__ring {
  position: relative;
  flex: 0 1 168px;
  min-width: 88px;
  min-height: 88px;
  max-width: 100%;
  aspect-ratio: 1;
}

/* Over the box a chart has elsewhere, which a ring does not fill. */
.dash-donut .dash-donut__ring .dash-chart { position: absolute; inset: 0; height: auto; min-height: 0; }

.dash-donut__centre {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 26%;
  text-align: center;
  pointer-events: none;
}

.dash-donut__total {
  max-width: 100%;
  overflow: hidden;
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--txt);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-donut__what { font-size: var(--fs-xs); color: var(--txt-3); }

.dash-donut__key {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  gap: 6px;
  align-self: stretch;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.dash-donut__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto 44px;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-sm);
}

.dash-donut__name { min-width: 0; overflow: hidden; color: var(--txt); text-overflow: ellipsis; white-space: nowrap; }
.dash-donut__value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--txt); }
.dash-donut__share { font-size: var(--fs-xs); color: var(--txt-3); text-align: right; font-variant-numeric: tabular-nums; }

@container widget (width > 560px) {
  .dash-donut { flex-direction: row; align-items: center; }
  .dash-donut__key { align-self: center; flex: 1 1 auto; max-width: 440px; }
}

/* --------------------------------------------------------------------------
   Bills: one per line, who and which on the left, how much and when on the
   right
   -------------------------------------------------------------------------- */

.dash-bills__total { margin: 2px 0 6px; }

.dash-bills { display: flex; flex-direction: column; margin: 4px 0 0; padding: 0; list-style: none; }

.dash-bill {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
  line-height: 1.35;
}

.dash-bill:first-child { border-top: 0; padding-top: 2px; }

.dash-bill__main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

a.dash-bill__main:hover .dash-bill__name { color: var(--primary); }
a.dash-bill__main:focus-visible { outline: none; border-radius: var(--r-sm); box-shadow: var(--ring-focus); }

.dash-bill__name {
  overflow: hidden;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--txt);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-bill__meta {
  overflow: hidden;
  font-size: var(--fs-xs);
  color: var(--txt-3);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-bill__side { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: none; }

.dash-bill__value {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--txt);
}

.dash-bill__when { font-size: var(--fs-xs); color: var(--txt-2); white-space: nowrap; }

/* Who saved a document, under when. A long name gives way rather than the
   document's number beside it: two fifths of the widget leaves the number
   the 120 it needs at 320, and "made by Super Administrator" the 170 it
   needs from a widget 425 wide. */
.dash-bill__who {
  max-width: 40cqw;
  overflow: hidden;
  font-size: var(--fs-xs);
  color: var(--txt-3);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A quantity somebody has to act on: short of what was ordered, or a shelf
   counted below nothing. */
.dash-bill__value--warn { color: var(--warn-fg); }
.dash-bill--bad .dash-bill__value { color: var(--bad-fg); }

.dash-bill .badge { height: 20px; padding: 0 8px; font-size: 11px; }

.dash-bills__more { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--txt-3); }

/* A finger needs more than the line of type an invoice is. */
@media (pointer: coarse) {
  a.dash-bill__main { min-height: 38px; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Quick actions
   -------------------------------------------------------------------------- */

/* Each button an icon over its name, as many to a row as the widget is
   wide enough for. On the grid they share out the height the widget was
   given; standing one under another they are as tall as they need. */
.dash-actions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  grid-auto-rows: minmax(76px, auto);
  gap: 8px;
  padding-top: 2px;
}

.dash-grid.grid-stack .dash-actions { flex: 1 0 auto; grid-auto-rows: minmax(76px, 1fr); }

/* Nine buttons sit three by three, or five and four, rather than four,
   four and one alone, or seven and two. From 848 pixels all nine fit in
   one row, and as many to a row as fit is what a widget that wide is for. */
@container widget (max-width: 560px) {
  .dash-actions { grid-template-columns: repeat(auto-fill, minmax(max(84px, calc((100% - 16px) / 3)), 1fr)); }
}

@container widget (width > 560px) and (width <= 848px) {
  .dash-actions { grid-template-columns: repeat(auto-fill, minmax(max(84px, calc((100% - 32px) / 5)), 1fr)); }
}

.dash-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  padding: 8px 6px;
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--txt);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}

.dash-action:hover { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-soft-fg); text-decoration: none; }
.dash-action:focus-visible { outline: none; box-shadow: var(--ring-focus); }

.dash-action__icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--primary-soft);
  color: var(--primary);
}
.dash-action__icon svg { width: 16px; height: 16px; }

.dash-action__label { max-width: 100%; line-height: 1.25; overflow-wrap: anywhere; text-wrap: balance; }

/* --------------------------------------------------------------------------
   Waiting, failing, not allowed
   -------------------------------------------------------------------------- */

.dash-skel { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; padding-top: 4px; }

.dash-skel__bar,
.dash-skel__col {
  display: block;
  border-radius: 6px;
  background: linear-gradient(90deg, var(--surface-3) 0%, var(--surface-2) 45%, var(--surface-3) 90%);
  background-size: 220% 100%;
  animation: dash-shimmer 1.3s ease-in-out infinite;
}

.dash-skel__bar { height: 10px; }
.dash-skel__bar--figure { width: 62%; height: 22px; }
.dash-skel__bar--short { width: 40%; }
.dash-skel__bar--line { width: 100%; height: 26px; margin-top: auto; }

.dash-skel__plot { display: flex; align-items: flex-end; gap: 8px; flex: 1 1 auto; min-height: 110px; }
.dash-skel__col { flex: 1 1 0; height: var(--h); border-radius: 4px 4px 0 0; }

.dash-skel__row { display: flex; justify-content: space-between; gap: 12px; }
.dash-skel__bar--name { width: 52%; }
.dash-skel__bar--value { width: 22%; }

@keyframes dash-shimmer { from { background-position: 100% 0; } to { background-position: -120% 0; } }

.dash-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 0;
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: 1.5;
}
.dash-note svg { width: 16px; height: 16px; flex: none; margin-top: 2px; }
.dash-note--bad { color: var(--bad-fg); }

/* --------------------------------------------------------------------------
   The picker
   -------------------------------------------------------------------------- */

.dash-pick__find { width: 100%; margin-bottom: var(--sp-4); }

.dash-pick__none { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); color: var(--txt-2); }

.dash-pick__group + .dash-pick__group { margin-top: var(--sp-5); }

.dash-pick__area {
  margin: 0 0 8px;
  font-size: 10.5px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--txt-3);
}

.dash-pick__list { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; }

.dash-pick__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.dash-pick__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--primary-soft);
  color: var(--primary);
}
.dash-pick__icon svg { width: 17px; height: 17px; }

.dash-pick__text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }

.dash-pick__name {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--txt);
}

.dash-pick__desc { font-size: var(--fs-xs); color: var(--txt-3); line-height: 1.45; }

.dash-pick__item > .btn { flex: none; }

/* --------------------------------------------------------------------------
   Naming a tab
   -------------------------------------------------------------------------- */

.dash-name .field { margin: var(--sp-3) 0 0; text-align: left; }

/* --------------------------------------------------------------------------
   Placeholder slot

   No longer on the dashboard, which now has real widgets; still the card the
   Reports index lists each report on.
   -------------------------------------------------------------------------- */

.slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 132px;
  padding: 20px;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  text-align: center;
  transition: border-color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease);
}
.slot:hover { border-color: var(--primary); background: var(--surface); }

.slot__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--txt-3);
}
.slot__icon svg { width: 18px; height: 18px; }

.slot__name {
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  color: var(--txt-2);
}
.slot__hint { font-size: var(--fs-sm); color: var(--txt-3); max-width: 34ch; line-height: 1.5; }

/* A slot that goes somewhere. The report cards are links, so they say so on
   hover and carry the focus ring a keyboard needs. */
.slot--link { text-decoration: none; color: inherit; cursor: pointer; }
.slot--link:hover .slot__name { color: var(--primary); }
.slot--link:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
