/* ─────────────────────────────────────────────────────────────
   PT LOADING — Universal progress + region-loader standard
   Companion to pt-editorial.css. Two mechanisms:
     1. A global top progress bar (#pt-progress) — the app-wide
        "something is loading" signal. Driven by pt-loading.js.
     2. Region-level loaders — a CSS spinner (.pt-spinner) and a
        skeleton shimmer (.pt-skeleton) for async data regions,
        wired to HTMX's built-in .htmx-indicator / .htmx-request.
   No new colors: every value resolves to a pt-editorial token.
   Falls back gracefully under prefers-reduced-motion (no trickle,
   no shimmer — a static / opacity-pulse indicator instead).
   ──────────────────────────────────────────────────────────── */

/* ====================================================================
   1. GLOBAL TOP PROGRESS BAR
   A 2.5px burnt-sienna sliver pinned to the very top of the viewport.
   Fixed + transform-only animation → zero layout shift (no CLS).
   The element lives in base.html and starts hidden; pt-loading.js
   toggles [data-state] and writes the --pt-progress custom property.
   ==================================================================== */
#pt-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2.5px;
  z-index: 9999;            /* above masthead + citation popover (z 50) */
  pointer-events: none;     /* never intercept clicks */
  /* Hidden until JS arms it. opacity (not display) so the fade-out at
     100% is smooth and the bar reserves no space when idle. */
  opacity: 0;
  transition: opacity var(--duration-base, 240ms) var(--ease-out, ease);
}
#pt-progress[data-state="loading"],
#pt-progress[data-state="done"] {
  opacity: 1;
}

/* The advancing fill. We animate scaleX from a transform-origin at the
   left edge — GPU-cheap, no reflow. --pt-progress is 0..1, written by JS. */
#pt-progress .pt-progress-bar {
  height: 100%;
  width: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--pt-progress, 0));
  background: var(--accent-primary, #b04a32);
  /* A faint leading glow so the head of the bar reads as "in motion"
     without a separate animated peg. Tokens only. */
  box-shadow: 0 0 8px rgba(176, 74, 50, 0.5);
  /* Trickle easing: each JS step is eased so growth feels organic, and
     the final 0→1 snap on completion is quick but not instant. */
  transition: transform var(--duration-slow, 480ms) var(--ease-out, ease);
}
/* On completion JS flips to data-state="done" and sets --pt-progress:1;
   we want that last jump fast, then the whole bar fades (opacity above). */
#pt-progress[data-state="done"] .pt-progress-bar {
  transition: transform var(--duration-fast, 120ms) var(--ease-out, ease);
}

/* ====================================================================
   2. REGION SPINNER  (.pt-spinner)
   For small tiles, inline "fetching…" states, and known-slow panels
   where reserving skeleton geometry isn't worth it. Editorial, thin,
   ink-toned ring with a burnt-sienna head. Sizes: default / -sm / -lg.
   ==================================================================== */
.pt-spinner {
  display: inline-block;
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid var(--hairline-strong, rgba(26, 31, 44, 0.18));
  border-top-color: var(--accent-primary, #b04a32);
  border-radius: 999px;
  animation: pt-spin 720ms linear infinite;
  /* Keep the baseline tidy when sat next to text. */
  vertical-align: -0.2em;
}
.pt-spinner-sm { width: 0.9rem;  height: 0.9rem;  border-width: 2px; }
.pt-spinner-lg { width: 1.9rem;  height: 1.9rem;  border-width: 2.5px; }

@keyframes pt-spin { to { transform: rotate(360deg); } }

/* A centered, labelled loading state for an empty region. Drop a
   <div class="pt-loading-region"> with a spinner + caption inside any
   panel that's about to be filled. Reserves a comfortable min-height so
   the surrounding layout doesn't jump when content arrives. */
.pt-loading-region {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 7rem;
  padding: 1.5rem 1rem;
  color: var(--ink-tertiary, #6c7280);
  text-align: center;
}
.pt-loading-region .pt-loading-label {
  font-size: 0.86rem;
  letter-spacing: 0.01em;
}

/* ====================================================================
   3. SKELETON SHIMMER  (.pt-skeleton)
   Higher-craft placeholder for data tables / cards / text blocks. Reads
   as the page's bones loading in, not a generic spinner. A skeleton box
   paints --bg-data with a slow diagonal sheen sweeping across it.
   Author composes a few of these to mirror the eventual layout; helper
   modifiers cover the common shapes.
   ==================================================================== */
.pt-skeleton {
  display: block;
  position: relative;
  overflow: hidden;
  background: var(--bg-data, #f5efe2);
  border-radius: 3px;
  /* The bar itself carries no content; reserve its own height. */
  min-height: 1rem;
}
.pt-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--bg-data-hover, #ede5d3) 50%,
    transparent 100%
  );
  animation: pt-shimmer 1.4s var(--ease-out, ease) infinite;
}
@keyframes pt-shimmer { to { transform: translateX(100%); } }

/* Shape helpers — compose to sketch the incoming layout. */
.pt-skeleton-text   { height: 0.85rem; margin: 0.35rem 0; border-radius: 2px; }
.pt-skeleton-text.is-short { width: 45%; }
.pt-skeleton-text.is-med   { width: 70%; }
.pt-skeleton-title  { height: 1.4rem;  width: 55%; margin: 0.2rem 0 0.7rem; }
.pt-skeleton-line   { height: 1px; }                 /* hairline placeholder */
.pt-skeleton-row    { height: 2.4rem; margin: 0.4rem 0; }  /* table row */
.pt-skeleton-card   { height: 8rem; border-radius: 4px; }
.pt-skeleton-pill   { height: 1.4rem; width: 4.5rem; border-radius: 999px;
                      display: inline-block; }

/* A skeleton stack that mimics a results table: a few rows of varied
   width. Authors can just drop <div class="pt-skeleton-table"></div>
   styled rows, or hand-compose. Provided for the most common case. */
.pt-skeleton-table > .pt-skeleton-row:nth-child(odd)  { opacity: 1; }
.pt-skeleton-table > .pt-skeleton-row:nth-child(even) { opacity: 0.7; }

/* ====================================================================
   4. HTMX INTEGRATION  (.htmx-indicator / .htmx-request)
   HTMX auto-toggles these classes during a request:
     • .htmx-indicator         → opacity 0 by default, 1 while requesting
     • .htmx-request <elt>      → applied to the request-issuing element
   We make any .pt-spinner / .pt-loading-region / .pt-skeleton placed
   inside (or pointed at via hx-indicator) fade in only while in flight.
   ==================================================================== */
/* HTMX's own convention: an .htmx-indicator is invisible until the
   triggering element (or the indicator's own ancestor) gets
   .htmx-request. We refine the transition to match editorial motion. */
.htmx-indicator {
  opacity: 0;
  transition: opacity var(--duration-base, 240ms) var(--ease-out, ease);
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* Convenience: a target region marked [aria-busy] during an in-flight
   swap (pt-loading.js sets/clears this) dims its stale content so the
   incoming skeleton/spinner reads as the active layer. */
[aria-busy="true"] { cursor: progress; }
.pt-loading-region[aria-busy],
.htmx-indicator.pt-loading-region { /* keep centered loaders full-opacity logic above */ }

/* ====================================================================
   5. ACCESSIBILITY — visually-hidden live region
   pt-loading.js writes "Loading…" / "" into #pt-progress-status so
   screen readers hear state changes without a visual flash. Mirror of
   pt-editorial's .pt-sr-only (duplicated so this file is standalone).
   ==================================================================== */
.pt-progress-status {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ====================================================================
   6. REDUCED MOTION
   No trickle scaleX transition, no shimmer sweep, no spinner rotation.
   Fall back to a calm static / opacity-pulse so the signal survives.
   ==================================================================== */
@media (prefers-reduced-motion: reduce) {
  /* Top bar: don't animate width. While loading, show a steady partial
     bar that pulses opacity gently; snap to full on done. */
  #pt-progress .pt-progress-bar {
    transition: none;
    transform: scaleX(1);              /* full-width, indeterminate */
    animation: pt-progress-pulse 1.6s ease-in-out infinite;
  }
  #pt-progress[data-state="done"] .pt-progress-bar {
    animation: none;
  }
  @keyframes pt-progress-pulse {
    0%, 100% { opacity: 0.45; }
    50%      { opacity: 1; }
  }

  /* Spinner: stop rotation, render a static ring (still reads as "busy"
     thanks to the accent head). */
  .pt-spinner { animation: none; }

  /* Skeleton: kill the sweep; a flat tinted block with a slow opacity
     pulse still communicates "loading" without motion sickness. */
  .pt-skeleton::after { animation: none; opacity: 0; }
  .pt-skeleton { animation: pt-skeleton-pulse 1.8s ease-in-out infinite; }
  @keyframes pt-skeleton-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.6; }
  }
}
