/* =========================================================================
   live-box — a single card that refreshes itself
   Behaviour and markup contract live in live-box.js (same directory pair;
   both files are shared verbatim by the main app and the tenant template
   apps via a linked Content item in each .csproj).

   Visually this is deliberately understated: a box that updates every
   couple of seconds must not announce itself every time, or a dashboard
   with a provisioning site becomes a strobe light. So the "busy" state is
   a thin progress hairline plus a barely-there dim of the content — enough
   to read as "working", quiet enough to ignore.

   THEMING. This file is used by three apps with three different palettes
   (the dark main app, and the light ecommerce/corporate owner panels), so
   it reads its colours from its own --live-box-* variables rather than from
   any one app's design tokens. The fallbacks below are neutral enough to
   look deliberate anywhere; an app that wants it on-brand overrides the
   four variables in its own stylesheet and changes nothing else:

     :root {
       --live-box-accent:  var(--my-accent);
       --live-box-track:   rgba(0, 0, 0, 0.08);
       --live-box-surface: var(--my-surface);
       --live-box-border:  var(--my-border);
       --live-box-text:    var(--my-text-muted);
       --live-box-radius:  var(--my-radius);
       --live-box-ease:    cubic-bezier(0.22, 1, 0.36, 1);
     }
   ========================================================================= */
[data-live-box] {
  --lb-accent: var(--live-box-accent, currentColor);
  --lb-track: var(--live-box-track, rgba(127, 127, 127, 0.18));
  --lb-surface: var(--live-box-surface, rgba(127, 127, 127, 0.08));
  --lb-border: var(--live-box-border, rgba(127, 127, 127, 0.28));
  --lb-text: var(--live-box-text, currentColor);
  --lb-radius: var(--live-box-radius, 999px);
  --lb-ease: var(--live-box-ease, cubic-bezier(0.22, 1, 0.36, 1));

  position: relative;
  /* The hairline is drawn inside the box's own rounded corner. */
  border-radius: inherit;
}

[data-live-box] > [data-live-content] {
  transition: opacity 0.35s var(--lb-ease), filter 0.35s var(--lb-ease);
}

/* Only dim once the refresh is slow enough to be worth acknowledging.
   A 60ms response that flashed the content would look like a glitch, so
   the dim is delayed and the fast case stays visually silent. */
[data-live-box].live-box-busy > [data-live-content] {
  opacity: 0.55;
  filter: saturate(0.7);
  transition-delay: 0.25s;
  pointer-events: none;
}

/* Indeterminate hairline along the top edge. An indeterminate bar is the
   honest choice here: the server can't tell us how far along a refresh is,
   and a fake percentage is worse than none.

   The sweep animates BACKGROUND-POSITION, not transform. A translated
   element would travel a full box-width outside its own bounds, and an
   absolutely positioned child sticking out that far inflates the whole
   document's scrollWidth — invisible on a page that sets overflow-x:
   hidden, a stray horizontal scrollbar on any page that doesn't. A
   background can't leave the box it paints, so the animation is contained
   by construction rather than by whatever the host page happens to clip. */
[data-live-box]::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  border-radius: var(--lb-radius);
  background-image: linear-gradient(90deg, transparent, var(--lb-accent), transparent);
  background-size: 50% 100%;
  background-repeat: no-repeat;
  background-position: -60% 0;
  opacity: 0;
  transition: opacity 0.18s var(--lb-ease);
  pointer-events: none;
}
[data-live-box].live-box-busy::after {
  opacity: 0.9;
  animation: live-box-sweep 1.1s var(--lb-ease) infinite;
}
@keyframes live-box-sweep {
  from { background-position: -60% 0; }
  to   { background-position: 160% 0; }
}

/* A box whose last refresh failed. Content stays on screen (see the catch
   in live-box.js) and this is the only hint that it's stale — the retry is
   automatic, so there's nothing for the user to do about it. */
[data-live-box][data-live-error] > [data-live-content] {
  opacity: 0.8;
}
[data-live-box][data-live-error] .live-box-stale {
  display: inline-flex;
}
.live-box-stale {
  display: none;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  opacity: 0.7;
  color: var(--lb-text);
}

/* Manual refresh affordance, for boxes with no poll interval. */
.live-box-refresh {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px solid var(--lb-border);
  background: var(--lb-surface);
  color: var(--lb-text);
  border-radius: var(--lb-radius);
  padding: 6px 14px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s var(--lb-ease), color 0.18s var(--lb-ease), border-color 0.18s var(--lb-ease);
}
.live-box-refresh:hover {
  border-color: var(--lb-accent);
  color: var(--lb-accent);
}
.live-box-refresh .live-box-refresh-icon {
  display: inline-block;
  transition: transform 0.35s var(--lb-ease);
}
[data-live-box].live-box-busy .live-box-refresh {
  /* Not `disabled`: live-box.js already drops a concurrent refresh, and
     disabling would steal focus from a keyboard user mid-interaction. */
  cursor: progress;
}
[data-live-box].live-box-busy .live-box-refresh-icon {
  animation: live-box-spin 0.9s linear infinite;
}
@keyframes live-box-spin {
  /* Negative, because these panels are RTL and the icon should turn the
     way the reading direction runs. */
  to { transform: rotate(-360deg); }
}

@media (prefers-reduced-motion: reduce) {
  [data-live-box].live-box-busy::after { animation: none; background-position: 50% 0; }
  [data-live-box].live-box-busy .live-box-refresh-icon { animation: none; }
  [data-live-box] > [data-live-content] { transition: none; }
}
