/* The game board's shared chrome: the card the mode sits in, the status line
   under it, the row of level/view buttons, and the filter toggles.

   These lived in styles/modes/pixel.css, which only pixel.html links — so every
   other mode page got the unstyled version. That was most visible on Puzzle,
   whose post-solve "Puzzle View" / "Full Image" buttons carry .level-btn and so
   fell all the way back to the page's default red accent button, looking
   nothing like the identical control on the Pixel modes. Champion and Item were
   losing .splash-card and #status the same way.

   Anything genuinely specific to one mode stays in that mode's own file. */

/* The one element on the page that should read as sitting above the
   background — everything else is flat by design. */
.splash-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px;
  text-align: center;
  margin-bottom: 24px;
  box-shadow: var(--shadow-md);
}

#status {
  margin-top: 14px;
  font-size: var(--text-md);
  color: var(--text-muted);
  font-weight: 500;
}

/* Level selector. The Pixel modes fill this with five difficulty levels plus a
   post-solve "Full Image"; Puzzle fills it with its two post-solve views. Same
   control, so the same look. */
.level-row {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 14px;
  flex-wrap: wrap;
}

.level-btn {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-caps-sm);
  padding: 7px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text-muted);
  cursor: pointer;
}
.level-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--text);
}
.level-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.level-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* Filter toggles. The Pixel modes carry blur + grayscale + auto-reveal; Puzzle
   carries auto-reveal alone. The range-input and blur-value rules only ever
   match on the Pixel modes, but they are scoped to .filter-toggle and cost nothing
   elsewhere — keeping the set together beats splitting it across two files. */
.filter-row {
  display: flex;
  gap: 18px;
  justify-content: center;
  margin-top: 16px;
  flex-wrap: wrap;
}

.filter-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-caps-sm);
  text-transform: uppercase;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.filter-toggle input {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}
.filter-toggle:hover { color: var(--text); }
.filter-toggle input[type="range"] {
  width: 110px;
  height: 4px;
  accent-color: var(--accent);
  cursor: pointer;
}
.filter-toggle .blur-value {
  min-width: 34px;
  text-align: start;
  color: var(--text-faint);
  font-weight: 600;
}

/* Puzzle picks these up for the first time here. Its own copy of .filter-row,
   in styles/modes/puzzle.css, carried no media query, so its auto-reveal row
   held an 18px gap at every width while the Pixel modes' tightened to 12px. Both
   rows now narrow together — the intended consequence of sharing one
   definition, noted because it is a real change to a page the extraction
   otherwise left alone. */
@media (max-width: 640px) {
    .splash-card {
      padding: 12px;
    }

    .filter-row {
      gap: 12px;
    }

    .filter-toggle input[type="range"] {
      width: 80px;
    }
}

/* ── Loading spinner ─────────────────────────────────────────────────────
   SpinKit's "fading circle", by Tobias Ahlin — MIT licensed, vendored rather
   than linked because nothing else in this project loads from a third-party
   host in a render path (see the note on the stat icons in modes/table.js).
   https://github.com/tobiasahlin/SpinKit

   Twelve absolutely-positioned bars around a circle, each one rotated a further
   30 degrees and delayed a further twelfth of the cycle, so what animates is
   only opacity — no layout, no repaint of anything underneath.

   Sits INSIDE the image frame, absolutely positioned, and is a SIBLING of the
   image rather than its parent: #splashImg carries a user-driven blur/grayscale
   filter, and a wrapper would put the spinner behind that filter too. */
.img-loading {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  /* Above the checkerboard the puzzle draws as its own background, below
     nothing — the frame has no other children while this is visible. */
  z-index: 1;
}
.img-loading.visible {
  display: grid;
}

.sk-fading-circle {
  position: relative;
  width: 40px;
  height: 40px;
}
.sk-fading-circle > div {
  position: absolute;
  inset: 0;
}
.sk-fading-circle > div::before {
  content: "";
  display: block;
  margin: 0 auto;
  width: 15%;
  height: 15%;
  border-radius: 100%;
  /* --text-muted, not --text-faint. Faint is #555 on a #0A0A0A board — about
     2.7:1, which reads as smudges on the dark theme rather than as a spinner.
     This is a status indicator on an otherwise empty frame, so it has to be
     seen; muted still keeps it quieter than the art it stands in for. */
  background-color: var(--text-muted);
  animation: sk-circle-fade 1.2s infinite ease-in-out both;
}

@keyframes sk-circle-fade {
  0%, 39%, 100% { opacity: 0; }
  40% { opacity: 1; }
}

/* Twelve positions, twelve delays. Written out because the alternative is a
   preprocessor loop and there is no preprocessor here. */
.sk-fading-circle > div:nth-child(2)  { transform: rotate(30deg); }
.sk-fading-circle > div:nth-child(3)  { transform: rotate(60deg); }
.sk-fading-circle > div:nth-child(4)  { transform: rotate(90deg); }
.sk-fading-circle > div:nth-child(5)  { transform: rotate(120deg); }
.sk-fading-circle > div:nth-child(6)  { transform: rotate(150deg); }
.sk-fading-circle > div:nth-child(7)  { transform: rotate(180deg); }
.sk-fading-circle > div:nth-child(8)  { transform: rotate(210deg); }
.sk-fading-circle > div:nth-child(9)  { transform: rotate(240deg); }
.sk-fading-circle > div:nth-child(10) { transform: rotate(270deg); }
.sk-fading-circle > div:nth-child(11) { transform: rotate(300deg); }
.sk-fading-circle > div:nth-child(12) { transform: rotate(330deg); }

.sk-fading-circle > div:nth-child(2)::before  { animation-delay: -1.1s; }
.sk-fading-circle > div:nth-child(3)::before  { animation-delay: -1.0s; }
.sk-fading-circle > div:nth-child(4)::before  { animation-delay: -0.9s; }
.sk-fading-circle > div:nth-child(5)::before  { animation-delay: -0.8s; }
.sk-fading-circle > div:nth-child(6)::before  { animation-delay: -0.7s; }
.sk-fading-circle > div:nth-child(7)::before  { animation-delay: -0.6s; }
.sk-fading-circle > div:nth-child(8)::before  { animation-delay: -0.5s; }
.sk-fading-circle > div:nth-child(9)::before  { animation-delay: -0.4s; }
.sk-fading-circle > div:nth-child(10)::before { animation-delay: -0.3s; }
.sk-fading-circle > div:nth-child(11)::before { animation-delay: -0.2s; }
.sk-fading-circle > div:nth-child(12)::before { animation-delay: -0.1s; }

/* base.css caps every animation at 0.01ms with !important under reduced
   motion, which would leave eleven of the twelve dots frozen at opacity 0 —
   a single dot, reading as a speck of dust rather than as "loading". Restoring
   opacity here gives a static ring instead: still unmistakably a spinner, just
   not a spinning one. The #status line says "Loading..." in words alongside it
   either way. */
@media (prefers-reduced-motion: reduce) {
  .sk-fading-circle > div::before {
    opacity: 0.55;
  }
}
