/* ==========================================================================
   SKELETON LOADING SYSTEM
   Shared, theme-aware placeholder blocks used while real data loads from
   Supabase. Built to be dropped into ANY page — not specific to the home
   page. Card skeletons mirror the exact geometry (aspect-ratio, radius,
   spacing) of the real cards they stand in for, so there's no layout shift
   when real content swaps in.

   Usage: add `.skel` + a shape modifier to any block, or use one of the
   pre-composed card skeletons (.skel-post-card, .skel-post-card--feature,
   .skel-product-card) built with js/skeleton.js helpers.
   ========================================================================== */

/* ---- Base block ------------------------------------------------------- */
.skel{
  position: relative;
  display: block;
  background: var(--bg-raised);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
/* --bg-raised sits very close in value to --bg in dark mode (fine for
   raised surfaces like cards, which have their own border/shadow to read
   as "raised" — but a skeleton bar has neither, so on plain --bg sections
   like the article body it was nearly invisible). A dedicated, slightly
   lighter dark-mode fill for skeletons only, scoped here, without
   touching --bg-raised itself and rippling into cards/inputs elsewhere. */
[data-theme="dark"] .skel{
  background: color-mix(in srgb, var(--bg-raised) 100%, #ffffff 6%);
}
.skel::after{
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--bg) 55%, transparent) 50%,
    transparent 100%
  );
  animation: skel-shimmer 1.6s ease-in-out infinite;
}
[data-theme="dark"] .skel::after{
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, #ffffff 18%, transparent) 50%,
    transparent 100%
  );
}

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

/* Reduced-motion fallback: gentle pulse instead of a sweeping shimmer */
@media (prefers-reduced-motion: reduce){
  .skel::after{ animation: none; display: none; }
  .skel{ animation: skel-pulse 1.6s ease-in-out infinite; }
  @keyframes skel-pulse{
    0%, 100%{ opacity: 1; }
    50%{ opacity: 0.55; }
  }
}

/* ---- Shape modifiers ---------------------------------------------------- */
.skel--text{
  height: 0.95em;
  border-radius: 5px;
}
.skel--title{
  height: 1.3em;
  border-radius: 6px;
}
.skel--circle{
  border-radius: 50%;
  flex-shrink: 0;
}
.skel--rect{
  width: 100%;
  height: 100%;
}
.skel--pill{
  border-radius: var(--radius-pill);
}

/* ---- Post card skeleton (mirrors .post-card) ---------------------------- */
.skel-post-card{
  display: flex;
  flex-direction: column;
  height: 100%;
  /* Grid items default to min-width: auto, which lets the __media block's
     max-width: 388px (and other intrinsic content) refuse to shrink below
     that width even inside a narrower 1fr column — the same overflow bug
     already fixed on .product-card and .skel-category-tile. On the
     articles grid this pushed the skeleton past the column on mobile
     (single-column, well under 388px) and let it spill down over the
     "Load more" button sitting right below the grid. min-width: 0 lets
     the card actually shrink to fit its grid column like 1fr expects. */
  min-width: 0;
}
.skel-post-card__media{
  width: 100%;
  max-width: 388px;
  aspect-ratio: 388/300;
  border-radius: var(--radius-lg);
  margin-bottom: 1.1rem;
}
.skel-post-card__title{
  width: 90%;
  margin-bottom: 0.5rem;
}
.skel-post-card__title + .skel--title{
  width: 60%;
  margin-bottom: 0.65rem;
}
.skel-post-card__footer{
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-top: 0.5rem;
}
.skel-post-card__footer .skel--text{ width: 90px; }

/* ---- Feature post card skeleton (mirrors .post-card--feature) ----------- */
.skel-post-card--feature{
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: clamp(1.75rem, 4vw, 3rem);
}
.skel-post-card--feature .skel-post-card__media{
  flex-shrink: 0;
  width: 44%;
  max-width: 460px;
  margin-bottom: 0;
  aspect-ratio: 16/11;
}
.skel-post-card--feature__body{
  flex: 1;
  min-width: 0;
}
.skel-post-card--feature__body .skel--title{
  height: 1.6em;
  margin-bottom: 0.7rem;
}
.skel-post-card--feature__body .skel--title:nth-child(1){ width: 95%; }
.skel-post-card--feature__body .skel--title:nth-child(2){ width: 65%; }
.skel-post-card--feature .skel-post-card__footer{ margin-top: 1.5rem; }
.skel-post-card--feature .skel--pill{
  margin-top: 1.4rem;
  width: 130px;
  height: 40px;
}

@media (max-width: 980px){
  /* Must match .post-card--feature's own stacking breakpoint (home.css,
     980px) exactly — otherwise the skeleton renders as a side-by-side row
     right up until the real card loads and suddenly stacks, causing a
     visible layout jump anywhere between the two breakpoints. */
  .skel-post-card--feature{ flex-direction: column; align-items: stretch; }
  .skel-post-card--feature .skel-post-card__media{ width: 100%; max-width: none; }
}

/* ---- Product card skeleton (mirrors .product-card "Preview Reveal") ------ */
.skel-product-card{
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.skel-product-card__cover{
  width: 100%;
  aspect-ratio: 4/3;
  border-radius: var(--radius-lg);
}
.skel-product-card__body{
  display: flex;
  flex-direction: column;
  padding: 1rem 0.2rem 0;
  gap: 0.6rem;
  min-width: 0;
}
.skel-product-card__body .skel--pill{
  width: 70px;
  height: 18px;
  margin-bottom: 0.2rem;
}
.skel-product-card__body .skel--title{ width: 85%; }
.skel-product-card__body .skel--title + .skel--title{ width: 55%; }
.skel-product-card__body .skel--text{ width: 100%; }
.skel-product-card__footer-row{
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding-top: 0.5rem;
  min-width: 0;
}
.skel-product-card__footer-row .skel--text{ width: 60px; height: 1.1rem; flex-shrink: 1; }
.skel-product-card__footer-row .skel--pill{ width: 90px; height: 36px; margin-bottom: 0; flex-shrink: 0; }

/* ---- Category tile skeleton (mirrors .category-tile) --------------------- */
.skel-category-tile{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  /* Without this, a flex item's default min-width:auto lets its
     intrinsic content width win over the grid track's 1fr width,
     pushing the tile past its column — the "overflow to the right"
     seen on mobile once .category-rail drops to 2 columns. */
  min-width: 0;
}
.skel-category-tile__media{
  width: 100%;
  aspect-ratio: 1/1;
  border-radius: var(--radius-md);
}
.skel-category-tile__label{
  width: 70%;
  height: 1.05rem;
}

/* ---- Article header skeleton (mirrors .article-header) ------------------- */
.skel-article-header{
  display: grid;
  grid-template-columns: minmax(0, 460px) 1fr;
  align-items: center;
  gap: clamp(2rem, 5vw, 3.5rem);
}
.skel-article-header[hidden]{
  display: none;
}
.skel-article-header__media{
  border-radius: var(--radius-lg);
  aspect-ratio: 4/3;
}
.skel-article-header__content .skel--pill{
  width: 110px;
  height: 28px;
  margin-bottom: 1rem;
}
.skel-article-header__content .skel--title{
  height: 1.5em;
  margin-bottom: 0.6rem;
}
.skel-article-header__content .skel--title:nth-of-type(1){ width: 95%; }
.skel-article-header__content .skel--title:nth-of-type(2){ width: 70%; }
.skel-article-header__content .skel-article-header__author{
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.5rem;
}
.skel-article-header__content .skel--circle{
  width: 40px;
  height: 40px;
}
.skel-article-header__content .skel-article-header__author .skel--text{
  width: 110px;
}
.skel-article-header__content .skel-article-header__meta{
  display: flex;
  gap: 1.5rem;
  margin-top: 1.1rem;
}
.skel-article-header__content .skel-article-header__meta .skel--text{ width: 100px; }

@media (max-width: 780px){
  .skel-article-header{ grid-template-columns: 1fr; }
  .skel-article-header__media{ max-width: 420px; }
}

/* ---- Article body text skeleton (mirrors .article-content paragraphs) --- */
.skel-article-body{
  width: 100%;
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}
.skel-article-body .skel--text{ width: 100%; height: 1em; }
.skel-article-body .skel--text:nth-child(4n){ width: 60%; }
.skel-article-body__gap{ height: 0.6rem; }

/* ---- Popular-posts item skeleton (mirrors .article-popular__item) ------- */
.skel-article-popular-item{
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: 0.75rem;
  align-items: start;
}
.skel-article-popular-item__num{
  width: 24px;
  height: 1.4em;
}
.skel-article-popular-item__body .skel--title{
  height: 0.95em;
  margin-bottom: 0.5rem;
}
.skel-article-popular-item__body .skel--text{ width: 70px; }

/* ---- Category preview block skeleton (mirrors .category-block on
   categories.html: heading + "View All" + 3-up .post-card row) --------- */
.skel-category-block .section-head{
  align-items: center;
}
.skel-category-block__title{
  width: 180px;
  height: 1.6em;
}
.skel-category-block__link{
  width: 70px;
}

/* ---- Category page hero title skeleton (mirrors .orbit-hero--title-only
   h1, category.html only) ---------------------------------------------- */
.skel-category-hero-title{
  display: inline-block;
  width: 260px;
  max-width: 60vw;
  height: 0.85em;
  margin: 0 auto;
}

/* ---- Product page hero skeletons (product.html only) -------------------
   Same "skeleton lives inside the real element" approach as the category
   page's hero title: title/description/price already get replaced via
   textContent, and the buy buttons via innerHTML, once loadProduct()
   resolves — so no separate hidden-toggle overlay is needed here, and this
   is structurally immune to the .article-header[hidden] specificity bug
   fixed earlier. */
.product-gallery__skel{
  position: absolute;
  inset: 0;
  border-radius: 0;
}
.skel-product-hero__badge{
  display: inline-block;
  width: 90px;
  height: 28px;
  border-radius: var(--radius-pill);
  margin-bottom: 1rem;
}
.skel-product-hero__title{
  display: inline-block;
  width: 85%;
  height: 0.9em;
}
.skel-product-hero__desc{
  display: inline-block;
  width: 90%;
  max-width: 46ch;
  height: 0.9em;
}
.skel-product-hero__price{
  display: inline-block;
  width: 64px;
  height: 0.85em;
}
.skel-product-hero__btn{
  display: inline-block;
  width: 60px;
  height: 1.1em;
  background: color-mix(in srgb, currentColor 25%, transparent);
  opacity: 0.55;
}
/* The buy buttons (.btn-solid) have their own solid accent background and
   white-ish text — the shared .skel fill (var(--bg-raised)) would look like
   a mismatched patch sitting on top of it, so this uses a currentColor-based
   translucent fill instead (adapts automatically to the button's own text
   color in both themes), scoped only to the loading state. */
.btn.is-loading-skel{
  justify-content: center;
  pointer-events: none;
}

/* ---- Profile card skeletons (profile.html only) -------------------------
   Same "skeleton lives inside/beside the real element" approach as the
   product page: name/email/meta are cleared by their own textContent
   assignments once loadProfile() resolves, so no separate hidden-toggle
   overlay is needed for them. The avatar letter has no text to clear
   against while it's shimmering (an empty circle would just look blank),
   so it gets a real sibling overlay that JS removes explicitly instead. */
.profile-card__avatar-skel{
  position: absolute;
  inset: 0;
  border-radius: 50%;
}
.skel-profile__name{
  display: inline-block;
  width: 180px;
  max-width: 40vw;
  height: 0.85em;
}
.skel-profile__email{
  display: inline-block;
  width: 150px;
  height: 0.9em;
}
.skel-profile__meta{
  display: inline-block;
  width: 100px;
  height: 0.9em;
}

/* ---- Purchase-item skeleton (mirrors .purchase-item on purchases.html) -- */
.skel-purchase-item{
  display: grid;
  grid-template-columns: 96px 1fr auto;
  align-items: center;
  gap: 1.25rem;
  padding: 0.9rem 1.1rem;
  background: var(--card-bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--card-shadow);
}
.skel-purchase-item__media{
  width: 96px;
  height: 72px;
  border-radius: var(--radius-sm);
}
.skel-purchase-item__body{
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}
.skel-purchase-item__body .skel--title{ width: 65%; }
.skel-purchase-item__body .skel--text{ width: 120px; }
.skel-purchase-item__action{
  width: 110px;
  height: 38px;
}

@media (max-width: 640px){
  .skel-purchase-item{
    grid-template-columns: 72px 1fr;
    row-gap: 0.6rem;
    padding: 0.8rem;
  }
  .skel-purchase-item__media{ width: 72px; height: 54px; }
  .skel-purchase-item__action{
    grid-column: 1 / -1;
    justify-self: start;
  }
}

/* ---- Settings page skeleton (mirrors .settings-grid on settings.html) ---
   A whole-block skeleton rather than per-field placeholders: unlike
   profile/product, the real content here is an all-or-nothing gated
   section (initSettings() shows either the signed-out prompt or the
   whole .settings-grid, never a partial state), so the skeleton mirrors
   that same three-card shape instead of living inside individual fields. */
.skel-settings-grid .settings-card{ display: flex; flex-direction: column; }
.skel-settings__card-title{
  width: 130px;
  height: 1.4em;
  margin-bottom: 0.6rem;
}
.skel-settings__card-sub{
  width: 80%;
  max-width: 320px;
}
.skel-settings__field-row{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.5rem;
}
.skel-settings__field-row--static{
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hairline);
}
.skel-settings__field-label{
  width: 140px;
  height: 0.9em;
}
.skel-settings__field-btn{
  width: 64px;
  height: 36px;
}
.skel-settings__input{
  width: 100%;
  height: 46px;
  border-radius: var(--radius-sm);
  margin-top: 1.5rem;
}
.skel-settings__submit-btn{
  width: 150px;
  height: 44px;
  margin-top: 1.5rem;
}
