/*
 * Hand-written styles that sit on top of the compiled Tailwind output.
 *
 * This file ships as-is and is NOT processed by Tailwind, so anything defined
 * here renders without regenerating static/css/output.css (which is only built
 * inside the Docker image). Colours below are literal values from the Tailwind
 * `stone` palette so the two stay in sync visually.
 */

:root {
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --stone-100: #f5f5f4;
  --stone-200: #e7e5e4;
}

/* ── Shared chrome ───────────────────────────────────────────────────────── */

[x-cloak] { display: none !important; }

dialog::backdrop { background: rgba(0, 0, 0, 0.92); }

dialog {
  background: transparent;
  border: none;
  padding: 0;
  max-width: 100vw;
  max-height: 100vh;
}

/* ── Spinner ─────────────────────────────────────────────────────────────── */

/*
 * Ring spinner drawn with borders and tinted via `currentColor`, so the same
 * class works on a white button and on the dark lightbox backdrop.
 * A fast rotation (0.7s) makes waits feel shorter than a slow one does.
 */
/*
 * NOTE: this file loads *after* output.css, so `.spinner { display }` would
 * otherwise beat Tailwind's `.hidden { display: none }` — equal specificity,
 * later rule wins — and a spinner marked `hidden` would show forever. The
 * `.spinner.hidden` rule below (0,2,0) settles it. Anything here that sets a
 * property Tailwind also sets via a utility needs the same treatment.
 */
.spinner {
  display: inline-block;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 9999px;
  animation: spin 0.7s linear infinite;
}

.spinner.hidden { display: none; }

.spinner-sm {
  width: 0.875rem;
  height: 0.875rem;
  border-width: 1.5px;
}

.spinner-lg {
  width: 2.25rem;
  height: 2.25rem;
  border-width: 2.5px;
}

/*
 * Holds at opacity 0 for the first 200ms. Most loads finish inside that window,
 * so the spinner never flashes on fast navigations — but a genuine wait still
 * gets an indicator. `x-show` toggles `display`, which restarts CSS animations,
 * so every navigation gets a fresh delay without any JS timer.
 */
.spinner-delayed {
  animation:
    spin 0.7s linear infinite,
    spinner-in 150ms ease-out 200ms both;
}

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

@keyframes spinner-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── Grid tiles ──────────────────────────────────────────────────────────── */

/*
 * Tiles fade in once decoded instead of popping in. The `is-loaded` class is
 * added by the `@load` handler — and by an `x-init` guard, because for a cached
 * image `load` can fire before Alpine binds the handler.
 */
.tile-img {
  opacity: 0;
  transition: opacity 300ms var(--ease-out);
}

.tile-img.is-loaded { opacity: 1; }

/* Gated so a tap on a touch device doesn't leave the tile stuck dimmed. */
@media (hover: hover) and (pointer: fine) {
  .tile-img.is-loaded:hover { opacity: 0.9; }
}

/* ── Lightbox ────────────────────────────────────────────────────────────── */

/*
 * The blurred stand-in is the grid thumbnail, which the browser already has in
 * cache — it costs no extra request and paints immediately. The slight scale-up
 * hides the transparent edge bleed that `blur()` leaves behind.
 */
.lb-placeholder {
  filter: blur(12px);
  transform: scale(1.03);
  transition: opacity 200ms var(--ease-out);
}

.lb-placeholder.is-hidden { opacity: 0; }

.lb-full {
  opacity: 0;
  transition:
    opacity 200ms var(--ease-out),
    transform 200ms var(--ease-out);
}

.lb-full.is-loaded { opacity: 1; }

/* ── Skeletons ───────────────────────────────────────────────────────────── */

.skeleton {
  background-color: var(--stone-200);
  animation: skeleton-pulse 1.6s ease-in-out infinite;
}

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* Stand-in text bars inside card- and row-shaped skeletons. */
.skeleton-line {
  height: 0.5rem;
  width: 70%;
  border-radius: 9999px;
}

.skeleton-line--short { width: 40%; }

/* ── Reduced motion ──────────────────────────────────────────────────────── */

/*
 * Drop the decorative pulses and fades. The spinner keeps turning: it is the
 * only signal that work is in flight, so removing it would remove information
 * rather than just motion.
 */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }

  .tile-img,
  .lb-full,
  .lb-placeholder {
    transition: none;
  }
}
