/* ═══════════════════════════════════════════════════════════════════════════
   MStar — the dashboard's one loading indicator.  STAR-BUILD-001, Batch 0.
   Pairs with /assets/mstar.js.  No dependencies.

   WHY THIS EXISTS
   STAR-AUDIT-001 (2026-08-02) found 124 user-triggered fetch actions that
   showed nothing at all between press and response, 63 more that only grayed
   a button, and 7 independent one-off spinner implementations. Same failure
   VTUX-113-BRAINDUMP-DEADAIR hit individually ("read as did-nothing"), but
   systemic. This file + mstar.js are the single replacement.

   THEMING
   Every colour flows from three custom properties, so a host page themes the
   star by setting them on any ancestor (or :root). Defaults are the Owner Hub
   gold/jade pair. Two ready-made modifiers ship below: .mstar-violet (matches
   index.html's #bc8cff accent) and .mstar-jade (kiosk surfaces).

   ACCESSIBILITY
   prefers-reduced-motion swaps every rotation for a slow opacity/scale breath
   — the indicator still reads as "working", it just stops spinning.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --mstar-primary: #e8b84b;   /* outer star + core   */
  --mstar-secondary: #82c8aa; /* counter-rotating mid */
  --mstar-accent: #d78850;    /* second orbiting dot  */
}

.mstar-violet { --mstar-primary: #bc8cff; --mstar-secondary: #7ad0e0; --mstar-accent: #ff9ec7; }
.mstar-jade   { --mstar-primary: #82c8aa; --mstar-secondary: #e8b84b; --mstar-accent: #d78850; }

/* ── the element itself ─────────────────────────────────────────────────── */
.mstar { display: inline-block; vertical-align: middle; line-height: 0; flex: none; }
.mstar svg { display: block; overflow: visible; }

.mstar-sm { width: 16px; height: 16px; }
.mstar-md { width: 32px; height: 32px; }
.mstar-lg { width: 72px; height: 72px; }

/* Rotation. transform-box:view-box makes 50%/50% resolve against the viewBox,
   which is what lets a <g> spin about the star's centre in Safari/iOS as well
   as Chrome — without it, iOS rotates about the page origin and the star flies
   off-screen. Do not remove. */
.mstar [data-spin] { transform-box: view-box; transform-origin: 50% 50%; }

.mstar .ms-cw-slow   { animation: mstar-cw  6s   linear infinite; }
.mstar .ms-cw-med    { animation: mstar-cw  2.8s linear infinite; }
.mstar .ms-cw-fast   { animation: mstar-cw  1.3s linear infinite; }
.mstar .ms-cw-spark  { animation: mstar-cw  1.1s linear infinite; }
.mstar .ms-ccw-mid   { animation: mstar-ccw 2.6s linear infinite; }
.mstar .ms-ccw-fast  { animation: mstar-ccw 1.6s linear infinite; }
.mstar .ms-ccw-spark { animation: mstar-ccw 1.9s linear infinite; }
.mstar .ms-orbit-cw  { animation: mstar-cw  4s   linear infinite; }
.mstar .ms-orbit-ccw { animation: mstar-ccw 7s   linear infinite; }
.mstar .ms-pulse     { animation: mstar-pulse 2s ease-in-out infinite; }

@keyframes mstar-cw  { to { transform: rotate(360deg); } }
@keyframes mstar-ccw { to { transform: rotate(-360deg); } }
@keyframes mstar-pulse {
  0%, 100% { transform: scale(1);    opacity: .92; }
  50%      { transform: scale(1.16); opacity: 1; }
}

/* ── mount: inside a button ─────────────────────────────────────────────── */
.mstar-btn-wrap { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

/* ── mount: filling a panel ─────────────────────────────────────────────── */
.mstar-panel-mount {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; padding: 34px 20px; min-height: 96px;
  font-size: 13px; letter-spacing: .4px; opacity: .85;
}
.mstar-panel-label { text-align: center; }

/* ── mount: full-screen overlay ─────────────────────────────────────────── */
.mstar-overlay {
  position: fixed; inset: 0; z-index: 99999;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
  background: rgba(6, 9, 8, .82);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .22s ease;
}
.mstar-overlay.mstar-on { opacity: 1; }
.mstar-overlay .mstar-ov-label {
  font-size: 15px; letter-spacing: .8px; color: var(--mstar-primary);
  text-align: center; max-width: 80vw;
}

/* ── reduced motion ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .mstar [data-spin],
  .mstar .ms-pulse { animation: mstar-pulse 2.4s ease-in-out infinite !important; }
  .mstar-overlay { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LEGACY SPINNER RETIREMENT — STAR-BUILD-001 Batch 3
   ═══════════════════════════════════════════════════════════════════════════
   STAR-AUDIT-001 counted seven independent spinner implementations. The most
   widespread by far is `.spinner`: a 2px border ring, defined separately in
   seven files (owner/hub.html, owner/members.html, owner/duplicates.html,
   owner/shelfwatch-upload.html, owner/settings/settings.css, kiosk.html,
   index.html) and used inline as <span class="spinner"></span> inside built
   HTML strings all over those pages.

   Rewriting those markup strings would mean touching dozens of innerHTML
   templates — exactly the nested-escaping territory that has already produced
   one silently-killed script block during this rollout. So instead the class
   itself is redefined here, as a pure-CSS star. Zero markup changes; every
   existing `.spinner` becomes the star wherever it already appears, including
   ones added later by code this rollout never touches.

   Specificity note: mstar.css loads BEFORE each page's inline <style>, so a
   bare `.spinner` here would lose on source order. The element-qualified
   selectors below score 0,1,1 and beat the pages' bare `.spinner` (0,1,0).
   They deliberately still lose to `.terminal-overlay .spinner` (0,2,0) in
   index.html, which is a large bespoke overlay spinner with its own sizing
   and is fine as it is.

   Two pseudo-elements rather than parent+child: siblings don't inherit each
   other's rotation or get clipped by each other's clip-path, so the two stars
   genuinely counter-rotate.

   GEOMETRY IS 5-POINT FOR A MEASURED REASON (Batch 7)
   This shipped as an 8-point star with a shallow inner radius (34% of 50%).
   Rasterising it at the sizes actually in use showed that at 12px on a 1x
   display — .sw-dr-spinner in the shelfwatch drawer — all eight points smear
   into the body and it reads as a gold cushion with a teal core, not a star.
   A 5-point star with a deep inner radius (19%) survives 12px intact and is
   still clean at 48px. Do not raise the point count or the inner radius
   without re-running scripts/render-mstar.py and looking at the 12px cell.
   ═══════════════════════════════════════════════════════════════════════════ */

span.spinner, div.spinner, i.spinner, b.spinner {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  border: none !important;      /* kill the old ring */
  border-radius: 0 !important;
  background: none !important;
  animation: none !important;   /* the pseudo-elements carry the motion */
}

span.spinner::before, div.spinner::before, i.spinner::before, b.spinner::before,
span.spinner::after,  div.spinner::after,  i.spinner::after,  b.spinner::after {
  content: '';
  position: absolute;
  display: block;
}

/* outer 8-point star, clockwise */
span.spinner::before, div.spinner::before, i.spinner::before, b.spinner::before {
  inset: 0;
  background: var(--mstar-primary, #e8b84b);
  clip-path: polygon(50.0% 0.0%, 61.2% 34.6%, 97.6% 34.5%, 68.1% 55.9%, 79.4% 90.5%, 50.0% 69.0%, 20.6% 90.5%, 31.9% 55.9%, 2.4% 34.5%, 38.8% 34.6%);
  animation: mstar-cw 1.9s linear infinite;
}

/* inner 4-point star, counter-clockwise */
span.spinner::after, div.spinner::after, i.spinner::after, b.spinner::after {
  inset: 32%;
  background: var(--mstar-secondary, #82c8aa);
  clip-path: polygon(79.4% 9.5%, 68.1% 44.1%, 97.6% 65.5%, 61.2% 65.4%, 50.0% 100.0%, 38.8% 65.4%, 2.4% 65.5%, 31.9% 44.1%, 20.6% 9.5%, 50.0% 31.0%);
  animation: mstar-ccw 1.25s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  span.spinner::before, div.spinner::before, i.spinner::before, b.spinner::before,
  span.spinner::after,  div.spinner::after,  i.spinner::after,  b.spinner::after {
    animation: mstar-pulse 2.4s ease-in-out infinite !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LEGACY SPINNER RETIREMENT, PART 2 — STAR-BUILD-001 Batch 4
   ═══════════════════════════════════════════════════════════════════════════
   Batch 3 retired `.spinner`. These are the remaining border-ring loaders the
   audit found, each invented separately in its own file:

     .spin              worker.html, staff-onboarding.html, verified-tasks.html
                        (26 usages, always followed by "Loading…"/"Sending…")
     .dlg-working-spin  verified-tasks.html — the 46px brain-dump spinner added
                        by VTUX-113-BRAINDUMP-DEADAIR, whose own comment reads
                        "the old tiny corner label read as nothing is
                        happening". That bug is the direct ancestor of this
                        whole rollout, so it is fitting that it becomes the star
     .sw-spinner        staff/shelfwatch.html
     .sw-dr-spinner     owner/shelfwatch.html
     .sw-manage-spinner owner/shelfwatch.html

   SPECIFICITY: doubled class, not element-qualified
   Batch 3 used `span.spinner` (0,1,1) because every `.spinner` usage was on a
   known element. These are not so tidy — `.spin` appears on both <span> and
   <div>. `.spin.spin` also scores (0,2,0), beats the pages' bare `.spin`
   (0,1,0) despite mstar.css loading first, and does it without caring what
   element it lands on. Repeating the class is intentional; it is not a typo.

   DELIBERATELY NOT RETIRED
   `.ptr-spinner` (owner/hub.html pull-to-refresh). Its ring is not a
   request-pending indicator — it is a pull-progress affordance that changes
   colour at the release threshold and only animates once refreshing has
   actually started (`#ptrIndicator.ptr-spinning`). Converting it would mean
   reproducing that conditional through an ID-specificity chain to say nothing
   new. It is a different thing that merely looked like a spinner in a grep.
   `.terminal-overlay .spinner` in index.html is likewise untouched — that file
   belongs to the parked, money-gated PR #1935.
   ═══════════════════════════════════════════════════════════════════════════ */

.spin.spin, .dlg-working-spin.dlg-working-spin,
.sw-spinner.sw-spinner, .sw-dr-spinner.sw-dr-spinner,
.sw-manage-spinner.sw-manage-spinner {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  border: none !important;
  border-radius: 0 !important;
  background: none !important;
  animation: none !important;
}

.spin.spin::before, .dlg-working-spin.dlg-working-spin::before,
.sw-spinner.sw-spinner::before, .sw-dr-spinner.sw-dr-spinner::before,
.sw-manage-spinner.sw-manage-spinner::before,
.spin.spin::after, .dlg-working-spin.dlg-working-spin::after,
.sw-spinner.sw-spinner::after, .sw-dr-spinner.sw-dr-spinner::after,
.sw-manage-spinner.sw-manage-spinner::after {
  content: '';
  position: absolute;
  display: block;
}

/* outer 8-point star, clockwise */
.spin.spin::before, .dlg-working-spin.dlg-working-spin::before,
.sw-spinner.sw-spinner::before, .sw-dr-spinner.sw-dr-spinner::before,
.sw-manage-spinner.sw-manage-spinner::before {
  inset: 0;
  background: var(--mstar-primary, #e8b84b);
  clip-path: polygon(50.0% 0.0%, 61.2% 34.6%, 97.6% 34.5%, 68.1% 55.9%, 79.4% 90.5%, 50.0% 69.0%, 20.6% 90.5%, 31.9% 55.9%, 2.4% 34.5%, 38.8% 34.6%);
  animation: mstar-cw 1.9s linear infinite;
}

/* inner 4-point star, counter-clockwise */
.spin.spin::after, .dlg-working-spin.dlg-working-spin::after,
.sw-spinner.sw-spinner::after, .sw-dr-spinner.sw-dr-spinner::after,
.sw-manage-spinner.sw-manage-spinner::after {
  inset: 32%;
  background: var(--mstar-secondary, #82c8aa);
  clip-path: polygon(79.4% 9.5%, 68.1% 44.1%, 97.6% 65.5%, 61.2% 65.4%, 50.0% 100.0%, 38.8% 65.4%, 2.4% 65.5%, 31.9% 44.1%, 20.6% 9.5%, 50.0% 31.0%);
  animation: mstar-ccw 1.25s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .spin.spin::before, .dlg-working-spin.dlg-working-spin::before,
  .sw-spinner.sw-spinner::before, .sw-dr-spinner.sw-dr-spinner::before,
  .sw-manage-spinner.sw-manage-spinner::before,
  .spin.spin::after, .dlg-working-spin.dlg-working-spin::after,
  .sw-spinner.sw-spinner::after, .sw-dr-spinner.sw-dr-spinner::after,
  .sw-manage-spinner.sw-manage-spinner::after {
    animation: mstar-pulse 2.4s ease-in-out infinite !important;
  }
}
