/* ============================================================================
   Shared skeleton loading system — the canonical shimmer + a small set of
   shapes. Loaded by the app SPA (index.html), the landing page, and the
   super-admin console so every surface shares ONE look.

   CSS-driven only (no JS animation, CSP-safe): a highlight sweeps across a
   muted block. Dark mode (token-aware glow), reduced-motion (static block),
   and accessibility (.sr-only status) all live here so callers don't repeat
   themselves. The markup is produced by /js/skeleton.js (window.Skel).
   ========================================================================== */

:root {
  --skel-base: var(--fill); /* iOS's grey fill, as approved (s14, s16) */
  --skel-glow: rgba(255, 255, 255, 0.62);
}

/* On dark surfaces a 62%-white sweep is harsh — use a faint highlight instead. */
@media (prefers-color-scheme: dark) {
  :root {
    --skel-glow: rgba(255, 255, 255, 0.08);
  }
}

/* Screen-reader-only status (no global .sr-only existed before). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- base shimmer block ---------- */
.skel {
  display: block; /* so atoms size by width/height even outside a flex parent (e.g. in a <td>) */
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--skel-base);
}

.skel::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--skel-glow), transparent);
  animation: skel-shimmer 1.2s infinite;
}

@keyframes skel-shimmer {
  to {
    transform: translateX(100%);
  }
}

/* Respect reduced-motion: drop the sweep, keep a static muted block. */
@media (prefers-reduced-motion: reduce) {
  .skel::after {
    content: none;
    animation: none;
  }
}

/* ---------- grace period ----------
   A placeholder stays invisible for its first 300ms and then fades in. Most pages answer sooner, so
   they go straight from the title to their content; a skeleton shown for a split second and swapped
   out read as a flicker on every tab change (owner, iPad, 19/10/2026). The space is kept, so nothing
   jumps. A panel or stat row that only holds placeholders waits with them, so no empty white card
   flashes either. Under reduced motion the fade is cut to nothing but the wait stays. */
.skel,
.skel-table,
.skel-rows,
.panel:has(> .skel-table, > .skel-rows),
.stats-row:has(> .skel) {
  animation: skel-grace 0.2s ease-out 0.3s both;
}

@keyframes skel-grace {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

/* ---------- atoms ---------- */
.skel-line {
  height: 13px;
  border-radius: 999px;
}

.skel-line.is-sm {
  height: 10px;
}

.skel-avatar {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: 50%;
}

/* ---------- table skeleton (header strip + rows) ----------
   Borderless + padded so it can sit inside an existing .panel without a
   double frame — matches where the real <table> renders. */
.skel-table {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
}

.skel-trow {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 30px;
}

.skel-trow .skel-line {
  flex: 0 0 auto;
}

.skel-trow--head .skel-line {
  height: 10px;
  opacity: 0.7;
}

/* ---------- list rows with an avatar + two text lines ----------
   For real lists that show an avatar/icon (notifications, mate lists, the
   landing school list). ~56px desktop, tightened toward ~48px on phone. */
.skel-rows {
  display: flex;
  flex-direction: column;
}

.skel-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 13px 16px;
  min-height: 56px;
  border-bottom: 1px solid var(--line);
}

.skel-row:last-child {
  border-bottom: none;
}

.skel-row-lines {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.skel-row-lines .skel-line:first-child {
  width: 52%;
}

.skel-row-lines .skel-line:last-child {
  width: 30%;
}

.skel-row-trail {
  flex: 0 0 auto;
  width: 54px;
  height: 24px;
  border-radius: 999px;
}

/* ---------- content-card skeletons (grid children) ---------- */
.skel-card {
  min-height: 168px;
  border-radius: var(--radius);
}

/* ---------- stat-card skeletons (match .stat ≈ 108px) ---------- */
.skel-stat {
  min-height: 108px;
  border-radius: var(--r);
}

/* The row shapes a phone draws inside a figure's placeholder (below); a card shows none. */
.skel-stat > i {
  display: none;
}

/* On a phone the figures are rows of one grouped list (components.css): so are their skeletons
   (approved s14, s16). Each row is the card's own surface with the row's shapes in grey: the name,
   its note and the number, split by a hairline. The shapes pulse gently instead of the sweep; with
   Reduce Motion they hold still. */
@media (max-width: 760px) {
  .skel-stat {
    height: 64px;
    min-height: 64px;
    border-radius: 0;
    background: none;
  }

  .skel-stat::after {
    content: none;
  }

  .skel-stat + .skel-stat {
    border-top: 0.5px solid var(--sep);
  }

  .skel-stat > i {
    position: absolute;
    display: block;
    border-radius: 6px;
    background: var(--skel-base);
    animation: skel-pulse 1.2s ease-in-out infinite alternate;
  }

  .skel-stat > .skel-k1 {
    top: 16px;
    left: 16px;
    width: 42%;
    height: 13px;
  }

  .skel-stat > .skel-k2 {
    top: 37px;
    left: 16px;
    width: 26%;
    height: 10px;
  }

  .skel-stat > .skel-v {
    top: 23px;
    right: 16px;
    width: 28px;
    height: 18px;
  }
}

@keyframes skel-pulse {
  to {
    opacity: 0.5;
  }
}

@media (prefers-reduced-motion: reduce) {
  .skel-stat > i {
    animation: none;
  }
}

/* ---------- detail skeleton (modal): header + stat strip + rows ---------- */
.skel-detail-head {
  height: 104px;
  border-radius: 0;
}

.skel-detail-stats {
  display: flex;
  border-bottom: 1px solid var(--line);
}

.skel-detail-stats > div {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  padding: 17px 8px;
  border-right: 1px solid var(--line);
}

.skel-detail-stats > div:last-child {
  border-right: none;
}

.skel-detail-body {
  padding: 16px 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.skel-detail-body .skel-line {
  width: 100%;
}

.skel-detail-body > .skel-line:nth-child(odd) {
  width: 70%;
}

/* A row of label/value field groups (mirrors the modal's .row blocks). */
.skel-detail-fields {
  display: flex;
  gap: 18px;
}

.skel-detail-fields > div {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* Footer action-button placeholder. */
.skel-detail-foot {
  padding: 6px 22px 20px;
}

/* ---------- landing school-card skeleton (matches .lp-card) ----------
   Rendered as <li> straight into the existing .lp-grid, so it inherits that
   grid's columns/gap exactly. */
.skel-school {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 78px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  padding: 15px 16px;
}

.skel-school .skel-avatar {
  width: 46px;
  height: 46px;
}

.skel-school .skel-row-lines .skel-line:first-child {
  width: 70%;
}

.skel-school .skel-row-lines .skel-line:last-child {
  width: 40%;
}

/* ---------- phone tuning: full-width single column, ~48px rows ---------- */
@media (max-width: 640px) {
  .skel-row {
    min-height: 48px;
    padding: 11px 14px;
  }

  .skel-table {
    padding: 12px 14px;
  }

  .skel-card {
    min-height: 150px;
  }
}
