/* ==========================================================================
   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);
}

/* 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 stat-fit.js (it operates on
   the VALUE's --fit class — 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. Scoped to the dashboard stat context so other stat strips keep
   the global .stat-cell__value behavior; this changes wrap/shrink only — no
   font-size change — so the value stays legible and same-size everywhere.
   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. Mirrors the dashboard-scoped .dashboard-stat-strip
   .stat-strip__list precedent above. */
.dashboard-stat-cell .stat-cell__value {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* JS-applied nowrap (2026-06-19-sales-statcard-amount-autoresize). Added at
   runtime by ~/js/kit/stat-fit.js (NEVER from markup) so the no-JS / failed-JS
   client keeps the wrap fallback above and never shows a half-applied nowrap
   spill. stat-fit.js shrinks an overflowing value's font-size to fit on one
   line while this class is applied. IMPORTANT: while this class is present its
   white-space:nowrap renders the .dashboard-stat-cell .stat-cell__value
   overflow-wrap:anywhere rule above INERT (nowrap wins) — so a value that still
   overflows at the legibility floor would SPILL into the next grid track. To
   prevent that, stat-fit.js REMOVES this class at the floor; dropping the nowrap
   un-inerts overflow-wrap:anywhere so the floored value WRAPS instead of
   spilling (WCAG 1.4.4). Token-clean: nowrap only, no px/hex/ms. */
.stat-cell__value--fit {
  white-space: nowrap;
}

.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);
}
