/* Level Up — the ONE skeleton primitive, for all four shells.
 *
 * A skeleton is the PAGE'S OWN SHAPE, held for the person while the data is
 * on its way — quests.js said it first, of the quest detail: "so a slow load
 * reads as the page arriving, not the list". A spinner says only "wait"; a
 * shape says "this is what is coming, and it is coming here". The pin
 * (test/skeleton.test.js) refuses a skeleton whose only child is one
 * rotating element, so the shortcut is unavailable rather than discouraged.
 *
 * WHY THIS FILE EXISTS. `.skel` — a linear-gradient sweep over a transparent
 * shape — was in app.css, which the person app, the portal and the platform
 * suite all link, and which the DESK does not link at all. The desk had no
 * primitive, chat had a row layout built on this one, feed had a third
 * spelling. One file, linked render-blocking by every shell's document, is
 * what makes "the same shimmer everywhere" true rather than aspirational.
 * app.css's copy is REMOVED, not left behind: two definitions of one shimmer
 * is how the 23 `:active` rules happened.
 *
 * TOKENS. Every colour is read with a fallback chain, because the desk's
 * stylesheet spells its raised surface `--surface-raised` and has no radius
 * token at all. A skeleton must render correctly in a document that has not
 * loaded its own stylesheet yet — that is the exact moment it is for.
 */

/* ------------------------------------------------------------ the shimmer */

.skel {
  background: linear-gradient(
    100deg,
    var(--surface, #1C1C1F) 40%,
    var(--raised, var(--surface-raised, #232326)) 50%,
    var(--surface, #1C1C1F) 60%);
  background-size: 200% 100%;
  animation: skel 1.2s linear infinite;
  border-radius: var(--r-s, 10px);
  color: transparent;
}
@keyframes skel { to { background-position: -200% 0; } }

/* ------------------------------------------------------------- the root */

/* Every template's root. It is furniture, not content: nothing in here is
   readable or selectable, and a screen reader is told so on the root rather
   than on each of the forty shapes underneath it.
   NO `pointer-events: none`. It was here for one run, and it is the reason
   the first slow-3G walk reported bare #view on every screen whose skeleton
   was plainly on the screen in the screenshot beside it: elementFromPoint
   honours pointer-events, so a skeleton that refuses the pointer is a
   skeleton the blank-frame proof — and every other hit test — cannot see.
   Nothing in a skeleton is pressable anyway; there is not a control in one. */
[data-skeleton] {
  -webkit-user-select: none;
  user-select: none;
  /* A skeleton stands in for the page's whole REGION, not only for the part
     its shapes reach. Without this the first walk under slow 3G sampled the
     viewport centre and hit bare #view on five screens: the shapes were
     there, correctly, in the top 250px of a 900px window, and everything
     below them was canvas. Reserving the region is also what stops the swap
     from being a jump — the content that arrives is at least this tall. */
  min-height: 70dvh;
}

/* ------------------------------------------------------- shape helpers */

/* The vocabulary the templates in skeleton.js are written in. Deliberately
   small: a skeleton mirrors the real layout by REUSING the real layout's
   container classes (.col, .qs, .portal-col, .wrap), and these only describe
   the placeholder shapes that stand in for text and controls. */
.skel-rows { display: flex; flex-direction: column; gap: 10px; }
.skel-row { display: flex; align-items: center; gap: 12px; }
.skel-cols { display: flex; align-items: center; gap: 12px; }
.skel-grow { flex: 1 1 auto; min-width: 0; }
.skel-circle { border-radius: 50%; flex: none; }
.skel-pill { border-radius: 999px; flex: none; }

/* A table's shape: the header rule and evenly spaced cells, so the swap to
   the real .portal-table moves no column edge. */
.skel-table { display: flex; flex-direction: column; gap: 0; width: 100%; }
.skel-table .skel-row {
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line, #2E2E33);
}
.skel-table .skel-row > .skel { flex: 1 1 0; }

/* ------------------------------------------------------- reduced motion */

/* A STATIC SHAPE, not a fast one. app.css's global block collapses every
   animation to .01ms with an iteration count of 1 — which for an infinite
   shimmer means one instant pass and then a frozen, half-swept gradient.
   Naming the animation away is the only way to stop it: `animation: none`
   sets animation-name, which nothing overrides with !important, so the
   duration rule up there becomes moot. The shape stays, in one flat colour,
   and the page still says what is coming.
   Placed after the .skel rule so the flat background wins the tie. */
/* A ghost line only a phone draws: the quest card's eyebrow wraps to two
   lines there once it carries the attribution (gate attribution). */
.skel--phone { display: none; }
@media (max-width: 560px) { .skel--phone { display: block; } }

@media (prefers-reduced-motion: reduce) {
  .skel {
    animation: none;
    background: var(--raised, var(--surface-raised, #232326));
    background-position: 0 0;
  }
}
