/* ═══════════════════════════════════════════════════════════════════════
   MIUUM skeleton placeholders.  Source: _chrome/skeleton.css, copied to
   /skeleton.css by tools/apply-chrome.mjs.  Loaded from the head block on
   every stamped page and hand-linked by the three portals (they are in the
   SKIP set, so their ?v=N is hand-edited, check it with chrome.css).

   ONE pattern for the whole project: a height-stable placeholder that keeps
   the box, so nothing moves when the real content lands.  The rules are in
   /skeleton.js (MiuumSkeleton) and mirrored in the partner portal's
   src/components/Skeleton.jsx; this file is only the paint.

   ── CLASSES ────────────────────────────────────────────────────────────
   .mmsk             the container the placeholder lives in
   .mmsk-row         one repeat
   .mmsk-line        bars sharing one visual line (a title and a code, say)
   .mmsk-b           one bar.  Width, height and margins come from inline
                     --w / --h / --mt / --mb custom properties, never from
                     generated classes, so a shape stays data, not CSS.
   .mmsk-b--circle   round (avatars, map pins)
   .mmsk-b--box      fills its parent (maps, images)
   .mmsk-b--right    pushed to the end of its line
   .mmsk-live        the single polite live region, visually hidden
   .mmsk-fail        failure block, .mmsk-fail-retry its button
   .mmsk--still      no shimmer (what prefers-reduced-motion applies too)
   .mmsk--css        the CSS-only variant, see the bottom of this file

   ── COLOUR ─────────────────────────────────────────────────────────────
   Four token vocabularies are in play across this project plus one surface
   with none at all, so a fill computed from the page :root is invisible or
   wrong on three of them.  Everything below paints from exactly two
   variables and NOTHING ELSE IN THIS FILE NAMES A COLOUR:

     --mmsk-fill    the block, the surface's ink at 10%
     --mmsk-sheen   the sweep over it

   The default rule reads --ink (the 27 pages that link style.css) and falls
   back to --text (the journal, the blog post and the three portals, which
   name their ink that way).  Every other surface redefines the pair below.
   ═══════════════════════════════════════════════════════════════════════ */

.mmsk {
  --mmsk-fill:  color-mix(in srgb, var(--ink, var(--text, #1C1917)) 10%, transparent);
  --mmsk-sheen: color-mix(in srgb, var(--ink, var(--text, #1C1917)) 8%, transparent);
}
/* Light theme: the sweep has to be LIGHTER than the block to read, so it
   comes off the surface token instead of the ink.  It is carried at a higher
   percentage than the fill because a 10% white over a 10% grey is invisible;
   the derivation (surface, not ink) is what matters, not the number. */
html[data-theme="light"] .mmsk {
  --mmsk-sheen: color-mix(in srgb, var(--card, var(--panel, #FFFFFF)) 65%, transparent);
}

/* Dark bands inside a light page (style.css:59-63 keeps this exact list near
   black in both themes).  A skeleton in one of them takes the dark values
   whatever html[data-theme] says.  This is where the last contrast bug came
   from, so the list is copied rather than guessed. */
html[data-theme="light"] :is(.on-dark, .page-hero, footer, .mobile-menu,
  .booking-modal-box, .mm-nav, .mm-menu, .mm-topbar, .cta-band, .lg-hero) .mmsk {
  --mmsk-fill:  rgba(244, 239, 230, 0.10);
  --mmsk-sheen: rgba(244, 239, 230, 0.08);
}

/* The reserve form and the booking flow (.mm, booking.css:11) are DELIBERATELY
   dark in both themes, so both values come off --bk-text and neither follows
   html[data-theme].  --bk-bg-2 is darker than the panel and would read as a
   hole, not a block, so it is not used here. */
.mm .mmsk,
html[data-theme="light"] .mm .mmsk {
  --mmsk-fill:  color-mix(in srgb, var(--bk-text) 10%, transparent);
  --mmsk-sheen: color-mix(in srgb, var(--bk-text) 8%, transparent);
}
/* The account overlay (.mm-acct) DOES follow the theme: booking.css:43-61
   flips --bk-text to #23201B and --bk-panel-s to #FFFFFF there, so the fill
   above is already correct and only the sweep needs the light treatment. */
html[data-theme="light"] .mm-acct .mmsk {
  --mmsk-sheen: color-mix(in srgb, var(--bk-panel-s) 65%, transparent);
}

/* The chat widget has NO custom properties anywhere in chat.css and is dark
   on top of a light page as well as a dark one.  These two values are
   hardcoded on purpose.  Do not "fix" them to tokens: there are none to
   read, and a fill off the page :root is invisible in here. */
#pl-chat-window .mmsk {
  --mmsk-fill:  rgba(255, 255, 255, 0.08);
  --mmsk-sheen: rgba(255, 255, 255, 0.05);
}

/* ── Shape ──────────────────────────────────────────────────────────── */
.mmsk { display: block; }
.mmsk--box, .mmsk--box .mmsk-row { height: 100%; }
/* A flex column, not a block, on purpose: adjacent block margins collapse to
   the larger of the two, which silently ate 56px out of a card built from
   ten bars and made every shape shorter than the thing it stands in. In a
   flex column --mt and --mb both count, so a measured shape adds up. */
.mmsk-row { display: flex; flex-direction: column; }
.mmsk-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap, 10px); margin-top: var(--mt, 0); margin-bottom: var(--mb, 0); }
.mmsk-line > .mmsk-b { margin-top: 0; margin-bottom: 0; }

.mmsk-b {
  display: block;
  flex: none;
  width: var(--w, 100%);
  height: var(--h, 12px);
  margin-top: var(--mt, 0);
  /* --mb lets a slim bar occupy the taller line box of the text it stands in
     without being drawn that thick, which is how a card of 13px bars comes to
     exactly the height of a card of 13px/1.65 paragraphs. */
  margin-bottom: var(--mb, 0);
  border-radius: var(--r, 6px);
  background: var(--mmsk-fill);
  position: relative;
  overflow: hidden;
}
.mmsk-b--circle { border-radius: 50%; }
.mmsk-b--box    { width: 100%; height: 100%; border-radius: inherit; }
.mmsk-b--right  { margin-left: auto; }

/* ── Motion ─────────────────────────────────────────────────────────── */
/* A flat 90deg sweep.  chrome.css's mmShimmer is skewed for the nav CTA,
   which reads as a diagonal on a flat bar, so this is its own keyframe. */
@keyframes mmskSheen {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
.mmsk-b::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, var(--mmsk-sheen) 50%, transparent 100%);
  transform: translateX(-100%);
  animation: mmskSheen 1.6s ease-in-out infinite;
}
/* This block has to live here.  style.css:697-707 carries a global
   *{animation-duration:0.01ms!important} kill switch, but only the 27 pages
   that link style.css get it; agency-portal, agency-wholesale, agent-portal,
   blog-post, blog, corporate-portal, corporate, journal, payment-return and
   travel-agents do not.  Under reduced motion the blocks are simply static
   tinted shapes, which is the point of keeping the fill separate. */
@media (prefers-reduced-motion: reduce) {
  .mmsk-b::after { display: none; animation: none; }
}
.mmsk--still .mmsk-b::after { display: none; animation: none; }

/* A reserved skeleton: in the box from the first frame so nothing grows into
   place at the 150ms mark, invisible until it is actually needed. */
.mmsk--wait { visibility: hidden; }

/* ── The single polite announcement ─────────────────────────────────── */
/* One per region, never one per block, and every placeholder node carries
   aria-hidden so a screen reader never reads the fake bars. */
.mmsk-live {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Failure ────────────────────────────────────────────────────────── */
/* Never a skeleton that runs forever: every region ends in content, in this
   block, or in this block via the timeout.  Colour is currentColor so it
   inherits whatever surface it lands on, on purpose. */
.mmsk-fail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 14px 0;
  font: inherit;
  font-size: 13px;
  line-height: 1.55;
}
.mmsk-fail > span { flex: 1 1 200px; min-width: 0; opacity: .85; }
.mmsk-fail-retry {
  flex: none;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  border-radius: 999px;
  padding: 7px 15px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  opacity: .8;
  transition: opacity .15s ease;
}
.mmsk-fail-retry:hover { opacity: 1; }

/* ── CSS-only variant ───────────────────────────────────────────────── */
/* For a region whose replacement is guaranteed WITHOUT a network call: the
   markup carries the skeleton inline and the 150 ms delay costs no JS, so a
   warm cache never flashes it.  It covers the shape, the aria, the motion
   rule and the theme, but NOT the 300 ms minimum, the failure state or the
   timeout, so it is allowed only on journal.html #posts and blog-post.html
   #root, where blog-data.js is a synchronous same-origin script that always
   replaces the region.  FORBIDDEN behind a network call.
   The delay survives style.css's reduced-motion kill switch: that zeroes
   animation-duration, not animation-delay. */
@keyframes mmskReveal { to { visibility: visible; } }
.mmsk--css {
  visibility: hidden;
  animation: mmskReveal 0s linear 150ms forwards;
}
