/* ==========================================================================
   GUESTLY SKELETON SYSTEM — web
   --------------------------------------------------------------------------
   A skeleton is a PROMISE ABOUT LAYOUT: whatever fills this space in a moment
   will be this shape, in this place, at this size. That is why every composite
   below is measured against the real markup it stands in for — a placeholder of
   the wrong height causes the very layout shift it exists to prevent, and on
   the web that shift is a measurable CLS penalty, not just an annoyance.

   WHERE A SPINNER IS STILL RIGHT: skeletons are for CONTENT ARRIVING. An action
   in flight (submitting a booking, taking a payment) keeps its button spinner —
   there the layout is already known and the user is waiting on a verb.

   Everything here is built on the --g-* design tokens, so dark mode
   ([data-g-theme="dark"] / .g-dark) needs no separate rules, and Admin >
   Appearance governs skeletons exactly as it governs everything else.
   ========================================================================== */

/* Derived from the ink rather than hard-coded, so a placeholder reads at the
   same strength in both themes and follows Admin > Appearance. An earlier pass
   used --g-bg-soft directly, which on a near-white page was so faint the
   skeleton barely registered as content-shaped at all.

   DECLARED IN BOTH SCOPES ON PURPOSE. A custom property whose value contains
   var() is resolved where it is DECLARED, not where it is used — so a single
   :root declaration would bake in the light --g-text and hand near-white bars
   to the dark canvas. Each scope has to compute its own. */
:root {
    --g-skel-base: color-mix(in srgb, var(--g-text) 10%, var(--g-bg));
    --g-skel-glow: color-mix(in srgb, var(--g-text) 4%, var(--g-bg));
}
[data-g-theme="dark"], .g-dark {
    --g-skel-base: color-mix(in srgb, var(--g-text) 10%, var(--g-bg));
    --g-skel-glow: color-mix(in srgb, var(--g-text) 4%, var(--g-bg));
}

.g-skel {
    /* The sweep is a background-position animation: compositor work only, no
       layout, no paint of anything but this element. */
    background: linear-gradient(
        110deg,
        var(--g-skel-base) 30%,
        var(--g-skel-glow) 50%,
        var(--g-skel-base) 70%
    );
    background-size: 200% 100%;
    animation: g-shimmer 1.4s linear infinite;
    border-radius: var(--g-radius-sm);
    display: block;
    flex: none;
}

/* Blocks below the fold cost nothing until they scroll into view. */
.g-skel-block {
    content-visibility: auto;
    contain-intrinsic-size: auto 320px;
}

/* ------ shapes ------------------------------------------------------------ */

.g-skel-line   { height: 12px; border-radius: 6px; }
.g-skel-line.sm { height: 10px; }
.g-skel-line.lg { height: 14px; }
.g-skel-title  { height: 18px; width: 60%; border-radius: 9px; }
.g-skel-title.lg { height: 22px; }
.g-skel-circle { border-radius: 50%; }
.g-skel-img    { width: 100%; border-radius: var(--g-radius); }
.g-skel-chip   { height: 34px; width: 84px; border-radius: 17px; }
.g-skel-btn    { height: 46px; width: 132px; border-radius: 23px; }
.g-skel-btn.block { width: 100%; }

/* The ragged right edge. A stack of equal-length bars reads as a barcode,
   not as text, so paragraphs vary and always end short. */
.g-skel-p > .g-skel-line { margin-bottom: 8px; }
.g-skel-p > .g-skel-line:nth-child(3n+2) { width: 92%; }
.g-skel-p > .g-skel-line:last-child { width: 55%; margin-bottom: 0; }

/* ------ containers -------------------------------------------------------- */

.g-skel-stack { display: flex; flex-direction: column; gap: var(--g-s4); }
.g-skel-rail  { display: flex; gap: var(--g-s3); overflow: hidden; }
.g-skel-rail > * { flex: 0 0 220px; }

/* Mirrors the loaded results grid at every breakpoint. If .g-grid changes,
   this changes with it or the hand-off jumps. */
.g-skel-grid {
    display: grid;
    gap: var(--g-s5) var(--g-s4);
    grid-template-columns: 1fr;
}
@media (min-width: 600px) { .g-skel-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 992px) { .g-skel-grid { grid-template-columns: repeat(3, 1fr); } }

.g-skel-surface {
    /* NOT #fff. There is no --g-card token in the design system, so the old
       `var(--g-card, #fff)` always took the fallback and painted white cards
       onto the dark canvas. --g-bg-soft is the one surface token that is a
       shade of the page in both themes. */
    background: var(--g-bg-soft);
    border: 1px solid var(--g-border);
    border-radius: var(--g-radius);
    padding: var(--g-s4);
    display: flex;
    flex-direction: column;
    gap: var(--g-s3);
}

/* ------ composites -------------------------------------------------------- */

/* Property card: 16:10 photo, then title at 70% and meta at 40% — the exact
   proportions of the loaded .categories-single card. */
.g-skel-property .media { aspect-ratio: 16 / 10; border-radius: var(--g-radius-lg); }
.g-skel-property .t1 { height: 18px; width: 70%; margin-top: 10px; border-radius: 9px; }
.g-skel-property .t2 { height: 14px; width: 40%; margin-top: 6px; border-radius: 7px; }

/* Avatar + lines: settings rows, members, hosts. */
.g-skel-row { display: flex; align-items: center; gap: var(--g-s3); }
.g-skel-row .av { width: 44px; height: 44px; border-radius: 50%; }
.g-skel-row .body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; }
.g-skel-row .body .g-skel-line:first-child { width: 55%; }
.g-skel-row .body .g-skel-line:last-child  { width: 80%; height: 11px; }

/* Thumbnail + lines: bookings, wishlist, search suggestions. */
.g-skel-thumb-row { display: flex; gap: var(--g-s3); }
.g-skel-thumb-row .thumb { width: 72px; height: 72px; border-radius: 14px; flex: none; }
.g-skel-thumb-row .body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }

/* Trip / reservation card. */
.g-skel-booking .head { display: flex; gap: var(--g-s3); }
.g-skel-booking .head .thumb { width: 84px; height: 84px; border-radius: 14px; flex: none; }
.g-skel-booking .head .body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; }
.g-skel-booking .foot { display: flex; justify-content: space-between; }
.g-skel-booking .foot .g-skel-line:first-child { width: 96px; }
.g-skel-booking .foot .g-skel-line:last-child  { width: 64px; }

/* Money card. Never show an empty total — show the shape of one. */
.g-skel-revenue .amount { height: 30px; width: 150px; border-radius: 8px; }
.g-skel-revenue .chart  { height: 96px; border-radius: 12px; }

/* Notification row. */
.g-skel-notif { display: flex; gap: var(--g-s3); }
.g-skel-notif .av { width: 38px; height: 38px; border-radius: 50%; flex: none; }
.g-skel-notif .body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 7px; }

/* Chat: bubbles alternate sides and vary in width, as speech does. */
.g-skel-chat { display: flex; flex-direction: column; gap: 14px; }
.g-skel-chat .bubble { border-radius: 18px; height: 38px; max-width: 78%; }
.g-skel-chat .bubble.tall { height: 56px; }
.g-skel-chat .in  { align-self: flex-start; }
.g-skel-chat .out { align-self: flex-end; }

/* Calendar. The skeleton most worth getting right — a month grid that reflows
   by a row height on load is unmistakable. */
.g-skel-cal .head { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--g-s3); }
.g-skel-cal .head .m { width: 140px; height: 20px; border-radius: 10px; }
.g-skel-cal .head .nav { display: flex; gap: 8px; }
.g-skel-cal .head .nav span { width: 32px; height: 32px; border-radius: 50%; }
.g-skel-cal .week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 6px; }
.g-skel-cal .week.dow span { height: 10px; border-radius: 5px; }
.g-skel-cal .week span { aspect-ratio: 1 / 1; border-radius: 10px; }

/* Invoice / statement. */
.g-skel-invoice .lines { display: flex; flex-direction: column; gap: var(--g-s3); }
.g-skel-invoice .lines .r { display: flex; justify-content: space-between; }
.g-skel-invoice .lines .r .g-skel-line:first-child { width: 140px; }
.g-skel-invoice .lines .r .g-skel-line:last-child  { width: 60px; }

/* A form waiting on server-supplied values. */
.g-skel-form .field { display: flex; flex-direction: column; gap: 8px; margin-bottom: var(--g-s5); }
.g-skel-form .field .lbl { width: 30%; height: 11px; border-radius: 6px; }
.g-skel-form .field .inp { height: 52px; border-radius: 14px; }

/* Map surface with a floating result card. */
.g-skel-map { position: relative; min-height: 320px; }
.g-skel-map .canvas { position: absolute; inset: 0; border-radius: 0; }
.g-skel-map .card { position: absolute; left: var(--g-s4); right: var(--g-s4); bottom: var(--g-s4); }

/* Dashboard: a strip of stats, then cards. */
.g-skel-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--g-s3); }
@media (min-width: 768px) { .g-skel-stats { grid-template-columns: repeat(4, 1fr); } }
.g-skel-stat .n { width: 72px; height: 22px; border-radius: 6px; }
.g-skel-stat .c { width: 70%; height: 10px; border-radius: 5px; margin-top: 8px; }

/* Property detail: hero, title block, host, amenities, description. */
.g-skel-detail .hero { aspect-ratio: 4 / 3; border-radius: 0; }
.g-skel-detail .pad { padding: var(--g-s4); display: flex; flex-direction: column; gap: var(--g-s3); }
.g-skel-detail .amen { display: flex; gap: 10px; flex-wrap: wrap; }
/* The sticky booking widget must reserve its space from the first paint. */
.g-skel-detail .dock { height: 64px; border-radius: var(--g-radius-lg); }

/* ------ transitions ------------------------------------------------------- */
/* 250-350ms per the brief; 280 sits in the middle. Both halves run at once, so
   the swap is a crossfade rather than a flash of empty container. */

.g-skel-out { animation: g-skel-out 280ms ease forwards; }
.g-skel-in  { animation: g-skel-in 280ms ease forwards; }

@keyframes g-shimmer { to { background-position: -200% 0; } }
@keyframes g-skel-out { to { opacity: 0; } }
@keyframes g-skel-in { from { opacity: 0; } to { opacity: 1; } }

/* ------ failure ----------------------------------------------------------- */
/* Nothing may shimmer forever. When a load fails or stalls, the skeleton is
   replaced by plain language and a way out — never a dead end, never a code. */

.g-skel-fail {
    text-align: center;
    padding: var(--g-s7) var(--g-s5);
    color: var(--g-text-soft);
}
.g-skel-fail h4 { color: var(--g-text); font-size: var(--g-fs-body, 15px); margin: 0 0 6px; }
.g-skel-fail p { margin: 0 0 var(--g-s4); font-size: 14px; }

/* ------ accessibility ----------------------------------------------------- */
/* Reduce Motion costs the user the sweep, never the shape: the placeholder
   still communicates the layout, it simply holds still. */

@media (prefers-reduced-motion: reduce) {
    .g-skel { animation: none; background: var(--g-skel-base); }
    .g-skel-out, .g-skel-in { animation: none; }
}
