/* ══════════════════════════════════════════════════════════════
   tandoco · vita skeletons
   ──────────────────────────────────────────────────────────────
   Placeholder shapes that hold a content area open while its data
   is still travelling, so a slow phone shows "something's coming"
   instead of an empty hole that snaps full a few seconds later.

   Vita rules this follows:
     · warm paper base with a GOLD-tinted sweep (never grey)
     · the same radii as real vita cards (22px card, 50% disc, 999px pill)
     · one slow, soft sweep — no strobing, no pulsing opacity
     · prefers-reduced-motion kills the sweep, keeps the shape

   Naming: every class here is prefixed `tsk-` and every attribute
   `data-sk-`. The bare `.sk` class was NOT used on purpose — the
   homepage blog rail already owns it (styles/home.css `.blog-skel .sk`)
   and a collision would double up two different shimmers.

   Markup contract (the script does the rest — see scripts/skeleton.js):
     <div class="grid" data-sk="card:4"></div>
     <div class="disc" data-sk-photo>…<img>…</div>
   ══════════════════════════════════════════════════════════════ */

:root{
  --tsk-base:#ECE5D6;                 /* warm paper, a touch under --cream */
  --tsk-sweep:rgba(255,255,255,.85);
  --tsk-tint:rgba(246,196,69,.20);    /* the gold that makes it feel tandoco */
  --tsk-dur:1.65s;
}

/* ── the shimmer itself ─────────────────────────────────────── */
.tsk{
  position:relative;
  overflow:hidden;
  background:var(--tsk-base);
  background-image:linear-gradient(var(--tsk-tint),var(--tsk-tint));
  /* never let a placeholder be mistaken for a real, clickable thing */
  pointer-events:none;
  -webkit-user-select:none;user-select:none;
}
.tsk::after{
  content:"";
  position:absolute;inset:0;
  background:linear-gradient(100deg,
    transparent 25%,
    var(--tsk-sweep) 48%,
    var(--tsk-tint) 56%,
    transparent 76%);
  transform:translateX(-100%);
  animation:tsk-sweep var(--tsk-dur) cubic-bezier(.4,0,.25,1) infinite;
  will-change:transform;
}
@keyframes tsk-sweep{
  0%{transform:translateX(-100%);}
  60%,100%{transform:translateX(100%);}
}

/* tones ─ same sweep, surface-appropriate base */
[data-sk-tone="mist"]{
  --tsk-base:#DEE6F4;
  --tsk-tint:rgba(36,56,166,.09);
  --tsk-sweep:rgba(255,255,255,.9);
}
[data-sk-tone="navy"]{
  --tsk-base:rgba(255,255,255,.09);
  --tsk-tint:rgba(246,196,69,.10);
  --tsk-sweep:rgba(255,255,255,.20);
}
[data-sk-tone="gold"]{
  --tsk-base:rgba(255,255,255,.42);
  --tsk-tint:rgba(255,255,255,.20);
  --tsk-sweep:rgba(255,255,255,.78);
}

/* ── shapes ─────────────────────────────────────────────────── */
/* min-width:0 so a placeholder never forces a grid track wider than its
   share; flex:1 1 auto because a placeholder has no text inside it, and
   in a flex row (every one of our mobile scroll-rails) a box with no
   content collapses to nothing unless it is told to take a share of the
   space. Rails whose real cards are a fixed width can override with
   data-sk-w. */
.tsk-item{min-width:0;flex:1 1 auto;}
.tsk-block{border-radius:22px;}
.tsk-photo{border-radius:22px;aspect-ratio:4/3;width:100%;}
.tsk-disc{border-radius:50%;aspect-ratio:1/1;width:min(150px,52%);margin-inline:auto;}
.tsk-line{height:13px;border-radius:999px;}
.tsk-line + .tsk-line{margin-top:9px;}
.tsk-line.is-short{width:56%;}
.tsk-line.is-mid{width:78%;}
.tsk-line.is-title{height:18px;border-radius:8px;width:70%;}
.tsk-pill{height:38px;border-radius:999px;width:118px;}

.tsk-item .tsk-body{padding:16px 4px 4px;}
.tsk-item.is-centered .tsk-body{text-align:center;}
.tsk-item.is-centered .tsk-line{margin-inline:auto;}

/* list rows — thumbnail left, text right, stacked with the same rhythm
   as a real feed row (18px padding + a dashed rule between) */
.tsk-item.is-row{
  display:grid;grid-template-columns:132px minmax(0,1fr);
  gap:16px;align-items:center;padding:18px 2px;
  border-bottom:2px dashed rgba(36,56,166,.14);
}
.tsk-item.is-row .tsk-body{padding:0;}
.tsk-thumb{aspect-ratio:1/1;width:100%;border-radius:12px;}
@media(max-width:560px){
  .tsk-item.is-row{grid-template-columns:96px minmax(0,1fr);gap:12px;}
}

/* data-sk-frame ─ when the real cards sit on their own coloured tile, give
   the placeholder the same soft tile so the swap doesn't jump in weight */
[data-sk-frame] .tsk-item{
  background:rgba(255,255,255,.6);
  border-radius:26px;
  padding:26px 18px 22px;
}

/* ── the host while it waits ────────────────────────────────── */
[data-sk-item]{pointer-events:none;}

/* plain block hosts have no `gap`, so stacked placeholders would touch
   and read as one tall slab (rows bring their own rhythm already) */
[data-sk-stack] > [data-sk-item]:not(.is-row) + [data-sk-item]{margin-top:14px;}

/* fade out once the real thing arrives — the swap shouldn't blink */
[data-sk-item].tsk-done{
  opacity:0;
  transition:opacity .32s ease;
}

/* if the data never comes (dropped connection), stop the sweep rather
   than shimmer forever — the shape stays so the page doesn't jump */
[data-sk-stalled] .tsk::after{animation:none;opacity:.3;}

/* QA only (?sk=hold): keep the loading state on screen and hide the real
   content that arrived, so a screenshot shows the wait, not both at once */
[data-sk-held] > :not([data-sk-item]){display:none !important;}

/* ── photos: shimmer in the photo's own box until it lands ───
   Two cases, because an <img> is a replaced element and can't carry a
   ::before pseudo-element:
     A · a WRAPPER around the image  → sweep on a ::before, photo fades up
     B · the <img> ITSELF            → sweep painted as the image's own
         background, which the picture covers the instant it decodes.
   Case B adds nothing but a background, so it can't move layout — that's
   what makes the automatic <body data-sk-photos> mode safe.          */

/* A · wrapper */
[data-sk-photo]:not(img){position:relative;background:var(--tsk-base);}
[data-sk-photo]:not(img).tsk-photo-wait::before{
  content:"";
  position:absolute;inset:0;z-index:0;
  background:linear-gradient(100deg,
    transparent 25%,
    var(--tsk-sweep) 48%,
    var(--tsk-tint) 56%,
    transparent 76%);
  transform:translateX(-100%);
  animation:tsk-sweep var(--tsk-dur) cubic-bezier(.4,0,.25,1) infinite;
  pointer-events:none;
}
/* the photo fades up instead of popping in */
[data-sk-photo]:not(img) > img{transition:opacity .45s ease;}
[data-sk-photo]:not(img).tsk-photo-wait > img{opacity:0;}

/* B · the image itself */
img.tsk-photo-wait{
  /* An <img> with no pixels yet renders its alt text inside the box — so a
     shimmering placeholder ends up with "swole roll" or "cinnacloud-web"
     printed across it, which reads as a raw file name rather than as
     loading. Hide the text while we're waiting; it comes straight back
     when the class is dropped, so screen readers and broken-image
     fallbacks are untouched. */
  font-size:0;
  color:transparent;
  text-indent:-9999px;
  background-color:var(--tsk-base);
  background-image:linear-gradient(100deg,
    var(--tsk-base) 24%,
    var(--tsk-sweep) 47%,
    var(--tsk-tint) 55%,
    var(--tsk-base) 76%);
  background-size:230% 100%;
  background-repeat:no-repeat;
  animation:tsk-bg var(--tsk-dur) cubic-bezier(.4,0,.25,1) infinite;
}
@keyframes tsk-bg{
  0%{background-position:160% 0;}
  60%,100%{background-position:-60% 0;}
}

/* a photo that never arrived — keep the calm cream box, and swap the
   browser's torn-image glyph for a transparent pixel. `content` on a
   replaced element is what does the swap; where it isn't supported we
   simply fall back to today's behaviour. The alt attribute is untouched,
   so screen readers still get the description. */
img.tsk-photo-failed{
  background-color:var(--tsk-base);
  font-size:0;color:transparent;
  content:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
  object-fit:cover;
}

/* ── respect the setting ────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  .tsk::after,[data-sk-photo]:not(img).tsk-photo-wait::before{animation:none;opacity:.5;}
  img.tsk-photo-wait{animation:none;background-image:none;}
  [data-sk-item].tsk-done{transition:none;}
  [data-sk-photo]:not(img) > img{transition:none;}
}
