/* ==========================================================================
   Lichievements — minimal, responsive design system
   ========================================================================== */

:root {
  --bg:        #000000; /* true black */
  --bg-tile:   #221c16;
  --bg-raised: #2a221a;
  --line:      #35291f;
  --text:      #f2ece3;
  --text-dim:  #a99d8c;
  --text-mute: #6f6558;
  --accent:       #e7c590; /* warm light board square */
  --accent-2:     #a18964; /* warm dark board square  */
  --accent-hover: #f0d3a3;
  --on-accent:    #201810; /* text/icon on top of the accent colour */

  --err-bg:   #2a1713;
  --err-line: #5a2b24;
  --err-text: #f0c2b8;

  --tile-locked-filter: grayscale(0.4) brightness(0.55);

  /* Caption overlay (sits on top of the tile art) */
  --cap-grad:  linear-gradient(to top, rgba(10, 8, 6, 0.94), rgba(10, 8, 6, 0.72) 65%, transparent);
  --cap-title: #f7f2ea;
  --cap-sub:   #c4b8a6;
  --ext-bg:    rgba(10, 8, 6, 0.6);

  --check-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");

  --radius: 12px;
  --gap: clamp(10px, 1.6vw, 18px);
  --cat-gap: clamp(32px, 5vw, 52px); /* space below each category; also the sticky hand-off point */
  --maxw: 1180px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: dark;
}

/* Light theme ------------------------------------------------------------- */
[data-theme='light'] {
  --bg:        #ffffff;
  --bg-tile:   #f2f4f6;
  --bg-raised: #e9edf1;
  --line:      #e0e5ea;
  --text:      #101a1f;
  --text-dim:  #4b5a63;
  --text-mute: #8a97a0;
  --accent:       #0891b2;
  --accent-2:     #0e7490;
  --accent-hover: #0aa2c7;
  --on-accent:    #ffffff;

  --err-bg:   #fdecea;
  --err-line: #f3b7ae;
  --err-text: #8a271a;

  --tile-locked-filter: grayscale(0.15) brightness(0.92);

  --cap-grad:  linear-gradient(to top, rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.78) 65%, transparent);
  --cap-title: #101a1f;
  --cap-sub:   #4b5a63;
  --ext-bg:    rgba(255, 255, 255, 0.8);

  color-scheme: light;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-weight: 400;
  line-height: 1.5;
  font-feature-settings: 'cv05', 'ss03';
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
}

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(24px, 5vw, 64px) clamp(16px, 4vw, 40px) 96px;
}

a { color: inherit; }

/* --- Header ---------------------------------------------------------------- */

.site-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 22px;
  margin-bottom: clamp(32px, 6vw, 64px);
}

.logo {
  font-size: clamp(2.6rem, 9vw, 5.2rem);
  line-height: 1;
  letter-spacing: -0.03em;
  margin: 0;
  user-select: none;
}
.logo strong { font-weight: 500; transition: color 0.2s var(--ease); }
.logo .thin  { font-weight: 200; color: var(--text-dim); }
/* Hovering anywhere on the wordmark tints the bold "li" with the accent. */
@media (hover: hover) {
  .logo:hover strong { color: var(--accent); }
}

.tagline {
  margin: 0;
  color: var(--text-dim);
  font-size: clamp(0.95rem, 2vw, 1.1rem);
  max-width: 44ch;
}

/* --- Buttons --------------------------------------------------------------- */

.btn {
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0.7em 1.5em;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s var(--ease), background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Don't keep an outline after a tap/click (only for keyboard focus). */
.btn:focus:not(:focus-visible) { outline: none; }

.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover { background: var(--accent-hover); }

.btn-ghost {
  background: transparent;
  color: var(--text-dim);
  border-color: var(--line);
}
@media (hover: hover) {
  .btn-ghost:hover { color: var(--text); border-color: var(--accent-2); }
}

.logo-mark { width: 1.2em; height: 1.2em; display: inline-block; vertical-align: -0.2em; }
.btn-icon { width: 1.15em; height: 1.15em; flex-shrink: 0; }

/* --- Account / status bar -------------------------------------------------- */

.statusbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 14px 20px;
  margin-bottom: 28px;
  color: var(--text-dim);
  font-size: 0.95rem;
}
.statusbar .user { font-weight: 600; color: var(--text); }
.statusbar[hidden] { display: none; }

.count-wrap { display: inline-flex; align-items: baseline; gap: 0.4em; }
.count {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.count-label { color: var(--text-dim); }

/* Reload / Log out sit on their own row below the status line. */
.statusbar-actions {
  flex-basis: 100%;
  display: flex;
  justify-content: center;
  gap: 12px;
}

/* Progress */
.progress {
  width: 100%;
  max-width: 420px;
  height: 6px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
  transition: opacity 0.5s var(--ease);
}
/* Fade the bar out on completion (see js/main.js) rather than snapping away. */
.progress.fading { opacity: 0; }
.progress > span {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  transition: width 0.3s var(--ease);
}
.progress.indeterminate > span {
  width: 35%;
  animation: slide 1.1s var(--ease) infinite;
}
@keyframes slide {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(340%); }
}

/* --- Categories + grid ----------------------------------------------------- */

/* Spacing is padding (inside the box) so the sticky header stays pinned until the
   category's bottom edge — i.e. until the next heading arrives — instead of
   releasing early at the last tile row (a collapsing margin would not extend the
   containing block). Tune --cat-gap to change the hand-off point. */
.category { margin-top: 0; padding-bottom: var(--cat-gap); }

.category-head {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 12px 0 10px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  cursor: pointer; /* tap toggles the table-of-contents collapse (see main.js) */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.category-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (hover: hover) {
  .category-head:hover h2 { color: var(--accent); }
}

/* Collapsed "table of contents" mode: hide everything but the section titles so
   they stack into a compact list. The toggle class sits on <body>, so the page
   chrome (header, footer, theme/GitHub row) is hidden alongside the grids. */
.toc-mode .site-header,
.toc-mode .theme-switch-row,
.toc-mode .site-footer { display: none; }
.toc-mode .grid { display: none; }
/* List view re-declares .grid as display:flex at equal specificity, so hide it
   with a more specific selector when both classes are on <body>. */
.toc-mode.list-view .grid { display: none; }
.toc-mode .category { padding-bottom: 0; }
/* Same collapse on the hints page: keep only the section headings. */
.toc-mode .hints-intro,
.toc-mode .hints-note,
.toc-mode .hint-desc,
.toc-mode .hint-note-inline,
.toc-mode .hint-openings,
.toc-mode .move-chips { display: none; }
.toc-mode .hint-group { margin-bottom: 0; }
.category-head h2 {
  margin: 0;
  font-size: clamp(1.05rem, 2.4vw, 1.4rem);
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Checkmark shown next to the heading once a category is fully unlocked. */
.done-check {
  align-self: center;
  display: inline-flex;
  width: 1.05em;
  height: 1.05em;
  margin-left: -4px;
  color: var(--accent);
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.done-check svg { width: 100%; height: 100%; }
.category-head.complete .done-check { opacity: 1; transform: none; }
.category-head .tally {
  margin-left: auto; /* push the count to the right edge of the header */
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.8rem;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}
.category-head.complete .tally { color: var(--accent); }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: var(--gap);
  margin-top: 20px;
  padding-bottom: 10px;
}
/* Phones: a 128px floor only fits two tracks on a ~390px screen, which blows the
   tiles up to ~175px. Ask for three columns outright instead. The switch is
   seamless: at 480px the auto-fill rule above already lands on three columns of
   the same width, so nothing jumps as the breakpoint is crossed. */
@media (max-width: 480px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* --- Tile ------------------------------------------------------------------ */

.tile {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-tile);
  border: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
  isolation: isolate;
}

.tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity 0.5s var(--ease), filter 0.5s var(--ease), transform 0.5s var(--ease);
}

/* SVG placeholder tiles: a coloured panel with a centred line icon. */
.tile .art-svg {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--tile-color, #555);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.tile .art-svg svg {
  width: 44%;
  height: 44%;
  fill: none;
  stroke: rgba(255, 255, 255, 0.95);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Locked state: dim, desaturated */
.tile .art {
  opacity: 0;
  transform: scale(1.04);
}
.tile .locked {
  opacity: 1;
  filter: var(--tile-locked-filter);
}

.tile.unlocked .art    { opacity: 1; transform: none; }
.tile.unlocked .locked { opacity: 0; }

/* Reveal animation */
.tile.revealing .art { animation: pop 0.6s var(--ease); }
@keyframes pop {
  0%   { opacity: 0; transform: scale(0.7) rotate(-4deg); }
  60%  { opacity: 1; transform: scale(1.06); }
  100% { transform: scale(1); }
}

/* Caption overlay */
.tile .caption {
  position: absolute;
  /* Bleed a couple pixels past the bottom edge (the tile is overflow:hidden, so
     this is clipped) to avoid a sub-pixel hairline of uncovered art below the
     overlay on some tiles. */
  inset: auto 0 -2px 0;
  padding: 10px 12px 12px;
  background: var(--cap-grad);
  /* Hide with opacity + a small in-bounds slide. Do NOT translate the caption
     fully out of the tile (e.g. translateY(101%)): moving it past this
     overflow:hidden clip box makes WebKit/iOS drop the exit transition, so the
     caption vanishes abruptly instead of fading down. */
  transform: translateY(14px);
  opacity: 0;
  pointer-events: none; /* invisible caption must not intercept taps on the tile */
  transition: transform 0.28s var(--ease), opacity 0.28s var(--ease);
  z-index: 2;
  padding-top: 40px;
}
.tile .caption h3 {
  margin: 0 0 2px;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--cap-title);
}
/* Tiered tiles: the achievement name reads as the bold heading (the tier name +
   description sits in the sub-line below). */
.tile .caption .tier-title { font-weight: 700; }
.tile .caption p {
  margin: 0;
  font-size: 0.72rem;
  color: var(--cap-sub);
  line-height: 1.3;
}

/* Show the caption: on hover (pointer devices only), on keyboard focus, or when
   a tap has "revealed" it (touch devices — see main.js). */
@media (hover: hover) {
  .tile:hover .caption { transform: translateY(0); opacity: 1; pointer-events: auto; }
}
.tile:focus-visible .caption,
.tile.revealed .caption { transform: translateY(0); opacity: 1; pointer-events: auto; }

.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Locked tiles show a subtle title too on hover, but muted */
.tile:not(.unlocked) .caption h3 { color: var(--text-dim); }

/* Link cue for unlocked, clickable tiles */
.tile .ext {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ext-bg);
  color: var(--accent);
  font-size: 0.7rem;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  z-index: 3;
}
@media (hover: hover) {
  .tile.unlocked[href]:hover .ext { opacity: 1; transform: none; }
}
.tile.unlocked[href]:focus-visible .ext,
.tile.unlocked[href].revealed .ext { opacity: 1; transform: none; }

.tile.unlocked[href] { cursor: pointer; }
.tile:not([href]) { cursor: default; }

/* Tiered (multi-step) tile: a segmented progress bar pinned to the bottom edge,
   shown once at least one tier is reached. The caption (higher z-index) covers it
   on hover to reveal the current tier + next target. */
.tier-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: none;
  align-items: center;
  gap: 8px;
  padding: 16px 10px 8px;
  background: var(--cap-grad);
  pointer-events: none;
}
.tile.unlocked[data-tiered] .tier-progress { display: flex; }
.tier-bar {
  flex: 1;
  height: 5px;
  border-radius: 999px;
  background: rgba(128, 128, 128, 0.4);
  overflow: hidden;
}
.tier-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.4s var(--ease);
}
.tier-count {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.62rem;
  color: var(--cap-sub);
  font-variant-numeric: tabular-nums;
}
/* List-view ladder: one line per cleared step + the next target. Hidden in grid
   (which uses the art + bottom bar instead). */
.tier-steps { display: none; }
.tier-steps-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.tier-steps-title { font-weight: 500; color: var(--text); }
.tier-steps-count {
  margin-left: auto;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}
.tier-steps-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.tier-steps-list li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-dim);
}
.tier-steps-list li.next { color: var(--text); }
/* Title + description wrap together; the description falls to its own line only
   when it doesn't fit inline. */
.tier-step-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.35em;
  row-gap: 2px;
}
.tier-step-desc { color: var(--text-mute); }
.tier-check {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 2px; /* nudge to align with the first text line */
  border-radius: 50%;
  border: 1.5px solid var(--line);
}
.tier-steps-list li.done .tier-check {
  border: none;
  background-color: var(--accent);
  -webkit-mask: var(--check-mask) center / 15px 15px no-repeat;
  mask: var(--check-mask) center / 15px 15px no-repeat;
}
.tier-target {
  flex: 0 0 auto;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tier-steps-list li.next .tier-target { color: var(--text-mute); } /* n/N — only the in-progress step shows a tally */

/* A cleared tier row can deep-link to the game that first reached it. To keep the
   dense rows uncluttered, the ↗ is hidden at rest and only fades in on hover
   (pointer devices); on touch it shows faintly so it stays discoverable. The
   whole row is the click target (see main.js). */
.tier-steps-list li.has-game { cursor: pointer; }
.tier-step-link {
  flex: 0 0 auto;
  margin-left: auto;      /* sit at the right edge, past the flexible step text */
  padding-left: 8px;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--text-mute);
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}
@media (hover: hover) {
  .tier-steps-list li.has-game:hover .tier-step-title { color: var(--accent); }
  .tier-steps-list li.has-game:hover .tier-step-link { opacity: 1; color: var(--accent); }
}
@media (hover: none) {
  .tier-step-link { opacity: 0.4; }
}
/* Touch devices: the whole row is a tap target, so give each step some vertical
   padding to make individual rows comfortably (and unambiguously) tappable. Keep
   the check top-aligned with the title on rows whose description wraps. */
@media (pointer: coarse) {
  .tier-steps-list li { padding: 6px 0; }
}

/* Grid view: an unlocked tiered tile (has-tiers, set in main.js) opens the tier
   modal on tap, so cue it as clickable. */
body:not(.list-view) .tile[data-tiered].has-tiers { cursor: pointer; }

/* --- List view ------------------------------------------------------------- */
/* The grid re-flows into a stacked list of rows matching the hints page. The
   same tiles are reused: the artwork hides and the caption becomes the always-
   visible two-line row (title over details), with the ↗ cue kept for links. */
/* Let the list-view container widen smoothly (1180px → 1480px) as the viewport
   grows instead of snapping at a breakpoint, so the three columns keep resizing
   fluidly all the way up. The clamp lands softly at both ends (no discontinuity).
   Tuned so max-width = 1180px at ~1300px viewport and 1480px at ~1900px. */
body.list-view .wrap { max-width: clamp(1180px, 530px + 50vw, 1480px); }

.list-view .grid {
  --list-col-gap: 28px;
  display: grid;
  /* 1–3 fluid columns, no fixed breakpoints. Each track is at least 300px, but
     also never narrower than a third of the row, so the column count tops out at
     three; below that the min(300px) floor lets it fall to two then one. Because
     the tracks are 1fr, every column keeps resizing smoothly with the viewport. */
  grid-template-columns: repeat(auto-fill, minmax(max(300px, (100% - 2 * var(--list-col-gap)) / 3), 1fr));
  align-items: start;
  gap: 10px var(--list-col-gap);
  margin-top: 16px;
}
.list-view .tile {
  aspect-ratio: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  overflow: visible;
}
.list-view .tile:not(.unlocked) { opacity: 0.72; }

/* Leading status marker, same as the hints page: an empty circle when locked,
   an accent check (colour follows the theme) once unlocked. */
.list-view .tile::before {
  content: '';
  order: 0;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
}
.list-view .tile.unlocked::before {
  border: none;
  background-color: var(--accent);
  -webkit-mask: var(--check-mask) center / 18px 18px no-repeat;
  mask: var(--check-mask) center / 18px 18px no-repeat;
}

/* No artwork in list view */
.list-view .tile .locked,
.list-view .tile .art,
.list-view .tile .art-svg { display: none; }
/* No progress bar in list view (needs to out-specify the .unlocked show rule). */
.list-view .tile.unlocked[data-tiered] .tier-progress { display: none; }

/* The caption stops being a hover overlay and becomes the static row body. */
.list-view .tile .caption {
  order: 1;
  flex: 1 1 auto;
  position: static;
  inset: auto;
  padding: 0;
  background: none;
  transform: none;
  opacity: 1;
  pointer-events: auto;
  z-index: auto;
}
/* Theme-aware text (the overlay's cap-title/cap-sub are light-on-dark only). */
.list-view .tile .caption h3 {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text);
}
.list-view .tile:not(.unlocked) .caption h3 { color: var(--text-dim); }
.list-view .tile .caption p {
  font-size: 0.8rem;
  color: var(--text-dim);
}

/* Link cue: hidden by default, shown static on the right for unlocked links. */
.list-view .tile .ext { display: none; }
.list-view .tile.unlocked[href] .ext {
  display: grid;
  order: 2;
  position: static;
  flex: 0 0 auto;
  opacity: 1;
  transform: none;
}

/* Tiered tiles in list view use the multi-line ladder instead of the single
   caption line and the row-level check (each step carries its own check). */
.list-view .tile[data-tiered] .tier-steps { display: block; order: 1; flex: 1 1 auto; }
.list-view .tile[data-tiered] .caption { display: none; }
.list-view .tile[data-tiered]::before { display: none; }
.list-view .tile[data-tiered] { align-items: flex-start; }

/* A tiered tile isn't clickable as a whole here (only its cleared rows are, each
   with its own pointer + ↗ cue), so drop the whole-row link affordance. */
.list-view .tile[data-tiered] { cursor: default; }
.list-view .tile[data-tiered].unlocked .ext { display: none; }

/* --- Footer ---------------------------------------------------------------- */

.site-footer {
  margin-top: 64px;
  text-align: center;
  color: var(--text-mute);
  font-size: 0.85rem;
}
.site-footer a { color: var(--text-dim); text-decoration: none; }
.site-footer a:hover { color: var(--accent); }

/* --- Hints page ------------------------------------------------------------ */

.hints-intro {
  color: var(--text-dim);
  font-size: clamp(0.95rem, 2vw, 1.05rem);
  line-height: 1.55;
  margin: 0 0 clamp(28px, 5vw, 44px);
}
.hint-group {
  margin-bottom: clamp(32px, 5vw, 48px);
}
/* Match the index category headers (.category-head + its h2). */
.hint-group h2 {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0;
  padding: 12px 0 10px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: clamp(1.05rem, 2.4vw, 1.4rem);
  font-weight: 600;
  letter-spacing: -0.01em;
}
/* The heading doubles as the table-of-contents toggle (see hints.html), so it
   sticks and is clickable exactly like the index category headers. */
.hint-group h2 {
  position: sticky;
  top: 0;
  z-index: 5;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.hint-group h2:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) {
  .hint-group h2:hover { color: var(--accent); }
}
.hint-desc {
  color: var(--text-dim);
  margin: 16px 0 18px;
  line-height: 1.5;
}
/* Small footnote below a collection's list. */
.hint-note-inline {
  margin: 12px 0 0;
  font-size: 0.82rem;
  font-style: italic;
  color: var(--text-mute);
  line-height: 1.45;
}
.hint-openings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.hint-openings li {
  display: grid;
  grid-template-columns: 1fr; /* title on the first line, moves on the second */
  row-gap: 3px;
  padding: 10px 14px;
  background: var(--bg-tile);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.hint-name {
  font-weight: 500;
}
.hint-openings .moves {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.82rem;
  color: var(--accent);
  word-spacing: 0.15em;
}

/* Encyclopedia: the twenty first moves as individual chips. */
.move-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.move-chip {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.85rem;
  padding: 6px 12px;
  min-width: 3.2em;
  text-align: center;
  background: var(--bg-tile);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
}

/* --- Hints progress (only painted when the user has analysis data) --------- */

.hints-note {
  margin: -12px 0 clamp(28px, 5vw, 44px);
  font-size: 0.85rem;
  color: var(--text-mute);
}
.hint-progress {
  margin-left: auto; /* push the count to the right edge, like the grid headers */
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.8rem;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hint-progress.complete { color: var(--accent); }

/* Completed members: a leading check + accent tint. The marker only appears on
   the has-progress page, so the logged-out reference list stays unmarked. It sits
   in its own column, spanning both the title and moves rows. */
.hints.has-progress .hint-openings li {
  position: relative; /* anchor for the stretched link below */
  grid-template-columns: auto 1fr auto;
  column-gap: 12px;
  align-items: center;
}
.hints.has-progress .hint-name,
.hints.has-progress .hint-openings .moves { grid-column: 2; }
/* A completed member with a game link: the whole row is clickable, like an
   unlocked tile on the index page. */
.hint-openings li.has-link { cursor: pointer; }
@media (hover: hover) {
  .hint-openings li.has-link:hover { border-color: var(--accent-2); }
}

/* Deep link to the game where a member first appeared (added by hints.html). */
.hint-link {
  grid-column: 3;
  grid-row: 1 / -1;
  align-self: center;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  color: var(--accent);
  text-decoration: none;
  font-size: 0.8rem;
}
/* Stretch the link's hit area over the whole row (Bootstrap-style stretched link). */
.hint-link::after {
  content: '';
  position: absolute;
  inset: 0;
}
@media (hover: hover) {
  .hint-link:hover { border-color: transparent; }
}
.hint-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hints.has-progress .hint-openings li::before {
  content: '';
  grid-column: 1;
  grid-row: 1 / -1;
  align-self: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
}
.hints.has-progress .hint-openings li.is-done::before {
  border: none;
  background-color: var(--accent); /* the check itself; colour follows the theme */
  -webkit-mask: var(--check-mask) center / 18px 18px no-repeat;
  mask: var(--check-mask) center / 18px 18px no-repeat;
}
.hints.has-progress .hint-openings li.is-done .hint-name { color: var(--text); }
.hints.has-progress .hint-openings li:not(.is-done) { opacity: 0.78; }

.hints.has-progress .move-chip.is-done {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
}
.hints.has-progress .move-chip:not(.is-done) { opacity: 0.7; }

/* --- Utility --------------------------------------------------------------- */

[hidden] { display: none !important; }

.error {
  max-width: 520px;
  margin: 0 auto 24px;
  padding: 12px 16px;
  border: 1px solid var(--err-line);
  background: var(--err-bg);
  color: var(--err-text);
  border-radius: var(--radius);
  text-align: center;
}

/* Round icon buttons (theme toggle + GitHub), centered below the last grid --- */
.theme-switch-row {
  /* Stick to the bottom of the viewport while scrolling, then settle into place
     above the footer once the page is scrolled all the way down. The solid
     background hides scrolled content behind the buttons and is seamless at
     rest (it matches the page background). */
  position: sticky;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-top: clamp(8px, 3vw, 24px);
  padding: 14px 0 14px;
  background: var(--bg);
  border-top: 1px solid var(--line);
}
/* Narrow phones: the home-bar allowance comes from the safe-area inset rather
   than a hard-coded number. NB: iOS only reports a real inset when the viewport
   opts in with viewport-fit=cover, which this page deliberately does NOT do —
   covering also pushes content up behind the translucent status bar. So this
   reads 0 here and the 14px base is the whole gap. */
@media (max-width: 439.98px) {
  .theme-switch-row { padding-bottom: calc(14px + env(safe-area-inset-bottom)); }
}
.icon-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  color: var(--text-dim);
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease), background 0.15s var(--ease);
}
@media (hover: hover) {
  .icon-btn:hover { color: var(--accent); border-color: var(--accent-2); }
}
.icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.icon-btn:focus:not(:focus-visible) { outline: none; }
.icon-btn svg { width: 20px; height: 20px; }
/* Show the correct glyph per theme */
.theme-toggle .icon-sun { display: none; }
[data-theme='light'] .theme-toggle .icon-sun { display: block; }
[data-theme='light'] .theme-toggle .icon-moon { display: none; }

/* View toggle shows the current view's glyph (grid by default, list when active) */
.view-toggle .icon-list { display: none; }
.list-view .view-toggle .icon-list { display: block; }
.list-view .view-toggle .icon-grid { display: none; }

/* --- Tier modal (grid view) ------------------------------------------------ */
/* A lightbox for browsing a tiered achievement's earned tiers: the tier image
   with prev/next arrows either side, its text below; the image links to the game
   that unlocked that tier (account ladders link to the achievement page instead).
   Opened by tapping any unlocked tiered (has-tiers) tile; Esc closes. */
/* While a tier modal is open the page underneath is frozen so scroll/swipe
   gestures drive the modal, not the grid behind it. */
body.modal-open { overflow: hidden; }
.tier-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.62);
  animation: tier-modal-in 0.18s var(--ease);
  /* We interpret touch swipes as tier paging, so suppress native scroll gestures. */
  touch-action: none;
}
.tier-modal[hidden] { display: none; }
@keyframes tier-modal-in { from { opacity: 0; } to { opacity: 1; } }
.tier-modal-body {
  position: relative;
  width: 100%;
  max-width: 380px;
  padding: 20px;
  text-align: center;
  background: var(--bg-tile);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
}
.tier-modal-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: none;
  background: none;
  border-radius: 8px;
  color: var(--text-mute);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}
.tier-modal-close:hover { color: var(--text); background: var(--line); }
.tier-modal-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}
.tier-modal-nav {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 50%;
  color: var(--text);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.tier-modal-nav:hover:not([disabled]) { border-color: var(--accent); color: var(--accent); }
.tier-modal-nav[disabled] { opacity: 0.28; cursor: default; }
.tier-modal-art {
  position: relative;
  flex: 0 0 auto;
  width: min(60vw, 220px);
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--tile-color, #555);
  text-decoration: none;
}
.tier-modal-art img { width: 100%; height: 100%; object-fit: cover; }
.tier-modal-art svg {
  width: 44%;
  height: 44%;
  fill: none;
  stroke: rgba(255, 255, 255, 0.95);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tier-modal-art .ext {
  position: absolute;
  bottom: 8px;
  right: 8px;
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.9rem;
}
.tier-modal-label {
  margin: 16px 0 2px;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.tier-modal-title { margin: 0 0 4px; font-size: 1.05rem; font-weight: 600; color: var(--text); }
.tier-modal-desc { margin: 0; font-size: 0.85rem; color: var(--text-dim); line-height: 1.4; }
/* Tiers with no destination (account/extra with no link): plain image, no ↗. */
.tier-modal-art.no-link { cursor: default; }
.tier-modal-art.no-link .ext { display: none; }

/* Narrow phones: the fixed nav buttons + art overflow the modal, so tighten the
   padding, shrink the arrows, and let the art scale down with the viewport. */
@media (max-width: 439.98px) {
  .tier-modal { padding: 12px; }
  .tier-modal-body { padding: 16px; }
  .tier-modal-stage { gap: 6px; }
  .tier-modal-nav { width: 34px; height: 34px; font-size: 1.05rem; }
  .tier-modal-art { width: min(56vw, 200px); }
}

/* Directional fade/slide when paging between tiers (added per step in main.js). */
.tier-modal-body.slide-next .tier-modal-art,
.tier-modal-body.slide-next .tier-modal-label,
.tier-modal-body.slide-next .tier-modal-title,
.tier-modal-body.slide-next .tier-modal-desc { animation: tm-page-next 0.22s var(--ease); }
.tier-modal-body.slide-prev .tier-modal-art,
.tier-modal-body.slide-prev .tier-modal-label,
.tier-modal-body.slide-prev .tier-modal-title,
.tier-modal-body.slide-prev .tier-modal-desc { animation: tm-page-prev 0.22s var(--ease); }
@keyframes tm-page-next { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
@keyframes tm-page-prev { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --- Hints page: two columns on very large screens -------------------------- */
/* (The index list view scales its own container width and column count fluidly —
   see `body.list-view .wrap` and `.list-view .grid` above — so it needs nothing
   here.) The hints page splits each collection's member list into two columns. We
   use a 2-track CSS grid rather than CSS multi-column: multicol intermittently
   fragments the flexbox rows (a tile's last step could end up pushed onto the next
   column fragment, overlapping the border), whereas grid items are never
   fragmented. `align-items: start` keeps a short item from stretching to its
   neighbour's height. */
@media (min-width: 1600px) {
  body.hints-page .wrap { max-width: 1480px; }

  /* Hints page: each collection's member list splits across two columns. */
  .hint-openings {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
    gap: 10px 28px;
  }

  /* Keep prose at a readable measure even though the container is now wider. */
  .hints-intro,
  .hint-desc,
  .hint-note-inline,
  .hints-note { max-width: 80ch; }
}
