/* ==========================================================================
   ADMIN DASHBOARD (W3.v2.5 — Phase 10b)
   Page-level composition on top of StatStrip/Sparkline/StatusPill kit
   primitives. No new kit components. All color/motion via tokens.

   Loaded from HeadMeta.cshtml under the admin branch (prod = all pages;
   route-branching is a later refactor — splitting was required by the
   elfrique.components.admin.css 1000-line cap).

   Hard rules engaged:
     #2  no raw hex — all colour via tokens (--n-*, --brand-ink, --state-*)
     #3  no raw ms — motion via --d-* tokens
     #4  this file does not modify any kit partial
     #5  this file is the only place dashboard CSS lives
     #8  touch targets ≥ 44×44 on every link (padding + min-height)
     #10 prefers-reduced-motion: reduce fallback at the foot of this file
   ========================================================================== */

.admin-dashboard {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

/* ── StatStrip — dashboard wrapper adds reveal animation + sparkline slot. ── */

.dashboard-stat-strip {
  border: 1px solid var(--n-200);
  border-radius: var(--r-3);
  background-color: var(--n-0);
  box-shadow: var(--shadow-1);
  padding: var(--sp-4);
}

.dashboard-stat-strip .stat-strip__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  grid-auto-rows: 1fr;
  gap: var(--sp-4);
  margin: 0;
}

.dashboard-stat-cell {
  padding: var(--sp-3);
  border-radius: var(--r-2);
  border: 1px solid var(--n-100);
  background-color: var(--n-50);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  /* Container-query context (2026-06-24-statfit-css-only-drop-js): makes the cell
     a size container so the value's clamp(...,8cqi,...) down-scale resolves against
     THIS cell's inline size. Safe — no abs-positioned descendant/popover is anchored
     to a stat cell (the value, delta, sparkline and hint are all normal-flow), so
     establishing containment shifts no descendant's containing block. */
  container-type: inline-size;
}

/* Fixed 2-line header reservation (2026-06-20-dashboard-statcard-2line-bottom-align).
   Higher-specificity descendant override of the global single-class
   .stat-cell__label (elfrique.components.layout.css ~:195) — scoped to the
   dashboard stat context so global stat strips + the vendor-profile statstrip
   are untouched. Clamps the header to AT MOST 2 lines and RESERVES exactly 2
   lines of height so a 1-line header and a 2-3-line header occupy the SAME
   vertical block; combined with grid-auto-rows:1fr + the .stat-cell__body
   margin-top:auto below, every value lands on a shared bottom baseline across
   the equal-height row. The base label sets no own line-height and no --lh-*
   token exists, so a unitless ratio is set here (codebase idiom — cf. layout.css
   .contextual-sidebar__title line-height:1.3) and the 2-line min-height is
   derived from it: calc(1.3 * 2 * 1em) = two lines at the label's own font-size
   (--fs-1). Token-clean: a unitless line-height ratio + a calc on 1em — no raw
   px/hex/ms. NOTE: this clamps the LABEL only; the .stat-cell__value is left to
   WRAP (see the value rule below) so a long listing name is never truncated. */
.dashboard-stat-cell .stat-cell__label {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.3;
  min-height: calc(1.3 * 2 * 1em);
}

/* Bottom-align the value across the equal-height row. The cell is a flex column
   (above) inside grid-auto-rows:1fr (~:38), so margin-top:auto on the body pushes
   it to the card bottom — every .stat-cell__value sits at the same bottom position
   regardless of header length. Higher-specificity descendant override of the
   global .stat-cell__body (elfrique.components.layout.css ~:205); scoped so other
   stat strips keep their default flow. Independent of the value down-scale/wrap
   rule below — it targets .stat-cell__body, not .stat-cell__value (no selector
   overlap).

   :not(:has(.dashboard-stat-cell__spark)) EXCLUDES sparkline-bearing cells
   (AdminDashboard/Index.cshtml ~:130-135, when Model.SparklineByCellIndex is
   populated). In a spark cell the pre-existing sibling .dashboard-stat-cell__spark
   already carries margin-top:auto (~:117). If the body ALSO carried margin-top:auto
   there, the flex column would have TWO auto-margin children and per Flexbox the
   positive free space splits EQUALLY across both — the value/body would drift to
   mid-cell and the sparkline would un-pin from the bottom. Excluding the body in
   spark cells leaves exactly ONE auto-margin child (the spark), so the spark stays
   pinned to the bottom exactly as before this change; non-spark cells bottom-align
   the body as intended. :has()/:not() follows the codebase precedent in
   elfrique.components.layout.css (e.g. .content-card:has(.content-card__link:focus-visible),
   .media:has(...), .contestant-card:has(.contestant-photo)). Graceful degradation:
   on a (rare, legacy) engine without :has() the rule is dropped entirely, so the
   body simply does not bottom-align — the pre-change spark pinning is preserved and
   no double-margin drift occurs; no JS fallback is warranted for a layout nicety. */
.dashboard-stat-cell:not(:has(.dashboard-stat-cell__spark)) .stat-cell__body {
  margin-top: auto;
}

/* Long formatted currency totals (e.g. a high-magnitude naira amount on the
   Sales Analysis Detail strip) must fit their card instead of spilling the
   grid track. CSS-ONLY no-spill (2026-06-24-statfit-css-only-drop-js, supersedes
   the deleted ~/js/kit/stat-fit.js JS-measurement mechanism). Scoped to the
   dashboard stat context so other stat strips keep the global .stat-cell__value
   behavior.

   Two layers, container-query driven (the cell carries container-type:inline-size
   below so cqi resolves against the cell's own inline size):
   1. DOWN-SCALE so long values land on one line — font-size shrinks from the
      base --fs-5 ceiling toward an --fs-3 floor as the value/track narrows
      (clamp(var(--fs-3), 8cqi, var(--fs-5))). Short values stay at the --fs-5
      ceiling (same size as before). The 8cqi coefficient is a tuned estimate;
      a miss degrades to the wrap floor below, never a spill.
   2. WRAP FLOOR — min-width:0 lets the flex/grid child shrink below its
      intrinsic min so the break point is honored; overflow-wrap:anywhere permits
      a break inside an unbreakable token, so a value too long even at the --fs-3
      floor wraps cleanly onto a second legible line rather than spilling. This
      is the PRIMARY no-spill mechanism: the worst case is a clean wrap.
   Mirrors the dashboard-scoped .dashboard-stat-strip .stat-strip__list precedent
   above. Token-clean: --fs-3/--fs-5 scale tokens, cqi unit, clamp() function —
   no raw px/hex/ms (hard rules 2/3). */
.dashboard-stat-cell .stat-cell__value {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: clamp(var(--fs-3), 8cqi, var(--fs-5));
}

.dashboard-stat-cell__spark {
  margin-top: auto;
  min-height: 28px;
  /* Sparkline reveal — SVG fade + subtle rise. Respects reduced-motion below. */
  opacity: 0;
  animation: dashboard-spark-in var(--d-4) var(--ease-out-swift) forwards;
  animation-delay: var(--d-2);
}

@keyframes dashboard-spark-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Dashboard card reveal (used for the stat strip wrapper and all top-5
   cards + timeline). The fade-and-rise keyframe is intentionally subtle. */

[data-reveal-group] [data-reveal-item] {
  opacity: 0;
  animation: dashboard-reveal var(--d-3) var(--ease-out-swift) forwards;
}

[data-reveal-group] [data-reveal-item]:nth-child(1) { animation-delay: var(--d-1); }
[data-reveal-group] [data-reveal-item]:nth-child(2) { animation-delay: var(--d-2); }
[data-reveal-group] [data-reveal-item]:nth-child(3) { animation-delay: var(--d-3); }
[data-reveal-group] [data-reveal-item]:nth-child(4) { animation-delay: var(--d-4); }
[data-reveal-group] [data-reveal-item]:nth-child(n+5) { animation-delay: var(--d-5); }

@keyframes dashboard-reveal {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── 3-column top-5 grid ─────────────────────────────────────────────── */

.admin-dashboard__lists {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--sp-4);
}

/* ── Dashboard card — generic wrapper for top-5 lists and activity. ──── */

.admin-dashboard-card {
  border: 1px solid var(--n-200);
  border-radius: var(--r-3);
  background-color: var(--n-0);
  box-shadow: var(--shadow-1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-left: 7px;
  padding-right: 7px;
}

/* Vertical rhythm for stacked card forms — opt-in via .admin-config-stack
   wrapper. Used by AdminConfiguration/Fees, FxSettings, AppSettings, and
   any future per-group card-stack form. Scoped so pages whose cards live
   in a grid (which already supplies `gap`) are not double-spaced.
   Applies to every direct child after the first — covers card→card and
   card→submit-row uniformly, so the submit button sits clear of the last
   card above. The first child remains flush to the wrapper top. */
.admin-config-stack > * + * {
  margin-top: var(--sp-4);
}

/* Admin top-of-page alignment — collapse the default content/page-header
   top-padding stack so the breadcrumb's first row sits at the same vertical
   level as the user-name strip in the sidebar profile (which uses
   .dash-sidebar__profile padding-top: var(--sp-4) = 16px). Without these
   overrides the stack is .dash-layout__content padding-top var(--sp-5)
   (24px) plus .page-header padding-top var(--sp-5) (24px) = ~48px, which
   visually offsets the two columns. Scoped to body[data-shell="admin"]
   so the public shell's _PageHeader (Hero pages, About, etc.) keeps its
   default breathing room. */
body[data-shell="admin"] .dash-layout__content {
  padding-top: 0;
}
body[data-shell="admin"] .dash-layout__content .page-header {
  padding-top: var(--sp-4);
}

.admin-dashboard-card--wide {
  grid-column: 1 / -1;
}

.admin-dashboard-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-bottom: 1px solid var(--n-100);
}

.admin-dashboard-card__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--fs-3);
  font-weight: 600;
  color: var(--n-900);
}

.admin-dashboard-card__see-all {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--brand-ink);
  text-decoration: none;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-2);
  /* Touch target — padding + min-height keeps this ≥44px tall at base scale. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: background-color var(--d-1) var(--ease-out-swift);
}

.admin-dashboard-card__see-all:hover,
.admin-dashboard-card__see-all:focus-visible {
  background-color: var(--n-100);
  color: var(--brand-ink);
  text-decoration: none;
}

.admin-dashboard-card__see-all:focus-visible {
  outline: 3px solid var(--state-info);
  outline-offset: 2px;
}

.admin-dashboard-card__body {
  padding: var(--sp-2) 0;
  flex: 1;
}

.admin-dashboard-card__empty {
  margin: 0;
  padding: var(--sp-5) var(--sp-4);
  color: var(--n-500);
  font-size: var(--fs-2);
  text-align: center;
}

/* ── Click form — server-rendered title-button (M16 Phase 3 · W15.4). ─
   The card title doubles as a submit button so click-through implicitly
   re-ranks the card via AdminDashboardController.RecordCardClick. Browser
   default chrome (background, border, padding, font reset) is stripped so
   the button reads as the existing __title typography (serif + fs-3).
   Hard rule #8 compliance: min-height/min-width 44px keeps the touch
   target ≥44×44 even though the rendered text fits in a tighter box.
   Hard rule #4 satisfied: this CSS lives in dashboard.css, not as inline
   style on the kit partial / view. */

.admin-dashboard-card__click-form {
  margin: 0;
  display: inline-flex;
}

.admin-dashboard-card__click-btn {
  /* Reset browser-default <button> chrome so the button inherits
     .admin-dashboard-card__title typography (applied via the
     compounded class on the element). UA stylesheets override <button>
     font-family / font-size by default, so re-anchor explicitly to the
     same tokens used by .admin-dashboard-card__title. */
  background: transparent;
  border: 0;
  padding: var(--sp-1) var(--sp-2);
  margin: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-serif);
  font-size: var(--fs-3);
  font-weight: 600;
  color: var(--n-900);
  /* Touch target ≥44×44px per design-system Hard Rule #8. */
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-2);
  /* Subtle hover/focus affordance — matches the see-all link's pattern
     so the two interactive elements in the card head feel related. */
  transition: background-color var(--d-1) var(--ease-out-swift);
}

.admin-dashboard-card__click-btn:hover {
  background-color: var(--n-100);
}

.admin-dashboard-card__click-btn:focus-visible {
  outline: 3px solid var(--state-info);
  outline-offset: 2px;
  background-color: var(--n-100);
}

@media (prefers-reduced-motion: reduce) {
  /* Hard Rule #10 — disable the hover transition under reduced-motion.
     Static state still highlights via background-color; only the easing
     curve is suppressed. */
  .admin-dashboard-card__click-btn {
    transition: none;
  }
}

/* ── List rows — ordered top-5. ──────────────────────────────────────── */

.admin-dashboard-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.admin-dashboard-list__row + .admin-dashboard-list__row {
  border-top: 1px solid var(--n-100);
}

.admin-dashboard-list__link {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  color: inherit;
  text-decoration: none;
  min-height: 56px; /* 44px+ touch target floor */
  transition: background-color var(--d-1) var(--ease-out-swift);
}

.admin-dashboard-list__link:hover,
.admin-dashboard-list__link:focus-visible {
  background-color: var(--n-50);
  text-decoration: none;
  color: inherit;
}

.admin-dashboard-list__link:focus-visible {
  outline: 3px solid var(--state-info);
  outline-offset: -3px;
}

.admin-dashboard-list__primary {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}

.admin-dashboard-list__ref,
.admin-dashboard-list__name {
  font-weight: 600;
  color: var(--n-900);
  font-size: var(--fs-2);
}

/* __name allows wrapping: when a date/time, full name, or country-code +
   adjacent pill needs more than one line inside the stat-cell, it wraps
   instead of clipping with ellipsis. overflow-wrap handles the rare
   single-long-token case so it breaks rather than overflows. */
.admin-dashboard-list__name {
  overflow-wrap: anywhere;
}

/* __ref is reserved for monospace IDs / payment references / IPs — stays
   single-line + ellipsis so copy-paste targets remain stable and don't
   reflow. */
.admin-dashboard-list__ref {
  font-family: var(--font-mono);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-dashboard-list__subtext {
  font-size: var(--fs-1);
  color: var(--n-500);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-dashboard-list__secondary {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

.admin-dashboard-list__amount {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--fs-2);
  color: var(--n-900);
}

.admin-dashboard-list__time {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--n-500);
  white-space: nowrap;
}

.admin-dashboard-list__time--prominent {
  color: var(--brand-ink);
  font-weight: 500;
}

/* ── Timeline (recent activity) ──────────────────────────────────────── */

.admin-dashboard-timeline {
  list-style: none;
  margin: 0;
  padding: var(--sp-2) var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.admin-dashboard-timeline__row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-3);
  align-items: start;
}

.admin-dashboard-timeline__marker {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background-color: var(--brand-ink);
  margin-top: 6px;
  flex-shrink: 0;
}

.admin-dashboard-timeline__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.admin-dashboard-timeline__line {
  margin: 0;
  font-size: var(--fs-2);
  color: var(--n-900);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.admin-dashboard-timeline__actor {
  font-weight: 600;
}

.admin-dashboard-timeline__action {
  color: var(--n-700);
}

.admin-dashboard-timeline__target {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--n-500);
  padding: 2px var(--sp-2);
  background-color: var(--n-100);
  border-radius: var(--r-1);
}

.admin-dashboard-timeline__time {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--n-500);
}

/* ── Empty shell (facade failure). ───────────────────────────────────── */

.admin-dashboard__empty {
  padding: var(--sp-7) var(--sp-5);
  text-align: center;
  border: 1px dashed var(--n-300);
  border-radius: var(--r-3);
  background-color: var(--n-50);
}

.admin-dashboard__empty-title {
  margin: 0 0 var(--sp-2) 0;
  font-family: var(--font-serif);
  font-size: var(--fs-4);
  color: var(--n-900);
}

.admin-dashboard__empty-copy {
  margin: 0;
  color: var(--n-600);
  font-size: var(--fs-2);
}

.admin-dashboard__empty-link {
  color: var(--brand-ink);
  text-decoration: underline;
}

/* Hard rule #10 — reduced-motion fallback.
   Any card/sparkline reveal instantly renders under this branch. */
@media (prefers-reduced-motion: reduce) {
  .dashboard-stat-cell__spark,
  [data-reveal-group] [data-reveal-item] {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .admin-dashboard-card__see-all,
  .admin-dashboard-list__link {
    transition: none;
  }
}

/* ── Detail-card BEM hooks (M22 · Phase 5 · D4) ──────────────────────────
   Inline-style residuals on admin-dashboard-card detail consumers
   (UserDetail, OrderDetail) extracted to scoped class hooks so the
   canonical card pattern owns its variant styling. Pattern follows the
   M20 5c precedent: per-feature BEM hook on the canonical class. Avatar
   sizing in raw px follows codebase precedent (see
   .event-detail__organizer-avatar, .vendor-profile-header__avatar,
   .contextual-sidebar__avatar) — borders, image dimensions, and fixed
   visual sizes are not tokenized in this codebase; the hard-rule
   prohibition is on raw hex, raw ms, and banned greens. */

/* Profile avatar slot — square 80px, image variant + initials variant. */
.admin-dashboard-card__avatar {
  width:         80px;
  height:        80px;
}

.admin-dashboard-card__avatar--image {
  object-fit:    cover;
}

.admin-dashboard-card__avatar--initials {
  background-color: var(--n-200);
}

.admin-dashboard-card__avatar-initials-text {
  font-size:     var(--fs-5);
  color:         var(--n-700);
}

/* Empty-state glyph inside an admin-dashboard-card empty slot. */
.admin-dashboard-card__empty-icon {
  font-size:     var(--fs-6);
}

/* Stat-cell highlight rule — visually anchors a totals/result row in a
   stat-strip nested inside an admin-dashboard-card body. Used by
   OrderDetail.cshtml for the "Organiser Earnings" capstone row. */
.admin-dashboard-card__stat-cell--highlight {
  border-top:    2px solid var(--state-success);
}
