/* Up to 10 KPI cards in a row on desktop */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 14px;
}

@media (min-width: 1400px) { .kpi-grid { grid-template-columns: repeat(10, 1fr); } }
@media (min-width: 1100px) and (max-width: 1399px) { .kpi-grid { grid-template-columns: repeat(8, 1fr); } }
@media (max-width: 720px)  { .kpi-grid { grid-template-columns: repeat(2, 1fr); } }

.kpi-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  box-shadow: var(--shadow-card);
}

.kpi-icon {
  font-size: 20px;
  margin-bottom: 2px;
}

.kpi-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  font-family: var(--font-body);
}

.kpi-value {
  font-size: 24px;
  font-weight: 700;
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.1;
}

.kpi-note {
  font-size: 9px;
  color: var(--text-dim);
  font-family: var(--font-body);
  margin-top: 2px;
  /* Never let a note run past two lines — keeps card heights even. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Used only by the classic KPI-tile Tracker (pages/tracker-classic.js).
   The Arsenal dashboard has its own .tracker-cal-* calendar card. */
/* â”€â”€ Calendar widget card â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Double-width pseudo-card: a compact list of appointments in the selected
   timeframe. Each row (and the "+N more" footer) jumps to the Calendar page. */
.kpi-card-wide { grid-column: span 2; }
@media (max-width: 720px) { .kpi-card-wide { grid-column: 1 / -1; } }

.kpi-calendar-card { padding: 14px 16px; gap: 8px; }

.kpi-cal-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.kpi-calendar-card .kpi-icon { font-size: 16px; margin-bottom: 0; }

.kpi-cal-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.kpi-cal-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 5px 6px;
  border: none;
  background: transparent;
  border-radius: 6px;
  text-align: left;
  cursor: pointer;
  font-family: 'DM Sans', sans-serif;
  color: var(--text);
}
.kpi-cal-item:hover { background: var(--hover, rgba(46,40,32,0.06)); }

.kpi-cal-when {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  min-width: 58px;
  line-height: 1.15;
}
.kpi-cal-date { font-size: 11px; font-weight: 700; }
.kpi-cal-time { font-size: 10px; color: var(--text-dim); }

.kpi-cal-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.kpi-cal-more {
  align-self: flex-start;
  margin-top: 2px;
  padding: 3px 6px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: 'DM Sans', sans-serif;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent, var(--text-dim));
}
.kpi-cal-more:hover { text-decoration: underline; }

.kpi-cal-empty {
  font-size: 12px;
  color: var(--text-dim);
  font-family: 'DM Sans', sans-serif;
  padding: 6px 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Generic card + stat anatomy
   ───────────────────────────────────────────────────────────────────────────
   The pieces a dashboard card is built from. Separate from .kpi-card, which
   is the older single-figure tile still used by Team, Admin and the member
   detail modal — these compose instead: head + body, and the body holds
   either figures or a chart.

   Separation is carried by the hairline and the gap between cards, not by
   elevation. --shadow-card is deliberately almost invisible; it exists to
   stop the card looking pasted on, not to lift it.
   ═════════════════════════════════════════════════════════════════════════ */

.card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.card-head {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 26px;
}

/* Small tinted square behind the card's glyph. Keeps an emoji or an SVG the
   same size and alignment, so a row of cards has one optical rhythm. */
.card-icon {
  flex: none;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 1;
  border-radius: 8px;
  background: var(--bg-warm);
  color: var(--text-muted);
}

/* The Arsenal dashboard passes inline SVG here (arsenalIcon()) rather than a
   text glyph. An <svg> with no width/height would lay out at its 300×150
   default and blow the tile apart, so it is sized explicitly. currentColor on
   the stroke means it still inherits .card-icon's ink. */
.card-icon > svg {
  width: 15px;
  height: 15px;
  display: block;
}

.card-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  flex: none;
}

/* The ⓘ at the far right of a stat card. A button, not a tooltip: it opens the
   card's explainer (components/card-info-modal.js), because a figure built out
   of a status set, a date fallback and a cost basis needs more than one line of
   title text to be trusted. */
.card-info {
  flex: none;
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
  border-radius: var(--radius-pill);
  transition: color 0.12s ease, background 0.12s ease;
}

.card-info:hover { color: var(--text-muted); background: var(--hover); }
.card-info:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Held lit while its panel is open, so a row of three cards never leaves you
   guessing which ⓘ the thing on screen belongs to. */
.card-info.is-open {
  color: var(--accent);
  background: var(--hover);
}

.card-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  min-width: 0;
  flex: 1;
}

/* Cards never scroll internally. A scrollbar inside a dashboard card hides
   data behind a gesture nobody thinks to make — if content doesn't fit, the
   card grows or the content is summarised. */
.card, .card-body { overflow: visible; }

.card-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  min-height: 80px;
  padding: 20px 12px;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
}

.card-foot {
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.4;
}

/* ── Figures ──────────────────────────────────────────────────────────────
   Two measures side by side in one card. They share a row so the pair reads
   as one subject ("premium") split two ways, rather than two unrelated
   numbers that happen to be adjacent. */

.stat-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 18px;
  align-items: start;
}

.stat { display: flex; flex-direction: column; gap: 5px; min-width: 0; }

.stat-label {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stat-value {
  font-size: 27px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.025em;
  color: var(--text);
  /* Aligned figures in a column, so equal-width digits stop the two values
     jittering against each other as the timeframe changes. */
  font-variant-numeric: tabular-nums;
}

.stat-value.is-empty { color: var(--text-dim); font-weight: 500; }

@media (max-width: 1280px) { .stat-value { font-size: 23px; } }

/* ── Change pill ──────────────────────────────────────────────────────────
   Movement against the previous equivalent period.

   Classed by MEANING, not by direction: for CAC and every cost figure a rise
   is bad, so is-good / is-bad is decided by the caller and the arrow glyph
   carries the direction independently. Colour alone never says whether a
   number moved the right way — the arrow does that too. */

.delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  align-self: flex-start;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.delta.is-good { background: var(--green-soft); color: var(--green); }
.delta.is-bad  { background: var(--red-soft);   color: var(--red); }
.delta.is-flat { background: var(--hover);      color: var(--text-dim); }

.delta-arrow { font-size: 9px; line-height: 1; }

/* No prior period to compare against (All Time, or the first period on
   record). Holds the row's height so cards in a row stay aligned. */
.delta.is-none {
  background: transparent;
  color: var(--text-dim);
  padding-left: 0;
  font-weight: 500;
}

/* ── Card explainer ────────────────────────────────────────────────────────
   The panel behind a card's ⓘ (components/card-info-modal.js). Anchored to the
   button it opened from and positioned in JS; everything here is chrome.

   Both pieces are in document.body rather than beside the card. Pages that
   declare `container: … / inline-size` (the Tracker does) become the containing
   block for any fixed-position descendant, so a panel mounted inside one would
   be laid out against the page box and every viewport clamp would be off by the
   scroll offset. */
.card-info-backdrop {
  position: fixed;
  inset: 0;
  z-index: 940;
  background: rgba(var(--shadow-rgb), 0.28);
  display: none;
}
.card-info-backdrop.open { display: block; }

.card-info-pop {
  position: fixed;
  z-index: 941;
  width: 360px;
  max-width: calc(100vw - 24px);
  display: none;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-pop);
  /* NOT overflow:hidden — the arrow hangs outside the panel box and would be
     clipped away. The head and the scroll area round their own outer corners
     instead, which is the only content that reaches the panel's edge. */
}
.card-info-pop.open {
  display: flex;
  animation: card-info-in 0.14s ease-out;
  transform-origin: var(--arrow-x, 50%) top;
}
.card-info-pop:focus { outline: none; }

/* Grows out of the ⓘ rather than fading in place — the panel opens from a
   specific button, and the motion is what says which one. --arrow-x is the
   button's centre, so the origin tracks it after a viewport clamp. */
@keyframes card-info-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
.card-info-pop.is-above {
  animation-name: card-info-in-above;
  transform-origin: var(--arrow-x, 50%) bottom;
}
@keyframes card-info-in-above {
  from { opacity: 0; transform: translateY(4px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .card-info-pop.open { animation: none; }
}

/* The pointer back at the ⓘ. A rotated square with two of its borders drawn,
   so it inherits the panel's own border and background in every theme. */
.card-info-arrow {
  position: absolute;
  left: var(--arrow-x, 50%);
  top: -6px;
  width: 11px;
  height: 11px;
  margin-left: -5.5px;
  background: var(--bg-warm);
  border-left: 1px solid var(--border);
  border-top: 1px solid var(--border);
  transform: rotate(45deg);
  border-radius: 2px 0 0 0;
}
.card-info-pop.is-above .card-info-arrow {
  top: auto;
  bottom: -6px;
  background: var(--surface);
  border: 0;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  border-radius: 0 0 2px 0;
}

.card-info-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 14px 14px 12px 16px;
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-warm);
  border-radius: calc(var(--radius-card) - 1px) calc(var(--radius-card) - 1px) 0 0;
  flex: none;
}

.card-info-heading { min-width: 0; flex: 1; }

.card-info-eyebrow {
  display: block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.card-info-title {
  margin: 3px 0 0;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--text);
}

.card-info-close {
  flex: none;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--text-dim);
  font-size: 11px;
  cursor: pointer;
  border-radius: var(--radius-pill);
}
.card-info-close:hover { color: var(--text); background: var(--hover); }
.card-info-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The only scroller in the panel. max-height is set in JS from the room the
   anchor actually left, so a card near the bottom of a short window gets a
   shorter panel instead of one running off the screen. */
.card-info-scroll {
  /* Explicit flex so the shrinking lands here and not on the head, which is
     flex:none. Without a stated basis this only works by default-value luck. */
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 12px 16px 16px;
  min-height: 0;
  border-radius: 0 0 calc(var(--radius-card) - 1px) calc(var(--radius-card) - 1px);
}

.card-info-lead {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-muted);
}

.card-info-section { margin-top: 14px; }

.card-info-sub {
  margin: 0 0 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.card-info-defs { margin: 0; }

.card-info-def + .card-info-def {
  margin-top: 9px;
  padding-top: 9px;
  border-top: 1px solid var(--border-light);
}

.card-info-def dt {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}

.card-info-def dd {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* Formulas read as code because they ARE the code — tabular figures and a mono
   face keep "closes ÷ billables × 100" from being mistaken for prose. */
.card-info-formula {
  padding: 9px 10px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-control);
  background: var(--hover);
}
.card-info-formula + .card-info-formula { margin-top: 7px; }

.card-info-formula-name {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-bottom: 4px;
}

.card-info-formula code {
  display: block;
  font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}

.card-info-formula-note {
  margin: 6px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-muted);
}

.card-info-notes {
  margin: 0;
  padding-left: 16px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}
.card-info-notes li + li { margin-top: 7px; }

/* On a phone the anchoring stops earning its keep — 360px of panel next to a
   card that is already the full width is just a sheet with an arrow on it. */
@media (max-width: 560px) {
  .card-info-pop { width: calc(100vw - 24px); }
  .card-info-arrow { display: none; }
}
