/* fanpages.css -- the shell every fan page shares.
   Each franchise's own rules now live in assets/css/fan/<page>.css and are
   loaded only by that page. Splitting them took the CSS a fan page has to
   download from 274 KB to well under 100 KB; before, every page shipped all
   thirty-three themes to render one.

   A rule was moved out only when every class it targets appears on exactly
   one fan page. Anything shared, or referenced from more than one page,
   stayed here -- so the split cannot change what any page renders. */

/* ═════════════ 1. Shared shell ═════════════ */
.fan-hero { padding: 5rem var(--gutter) 2.5rem; max-width: var(--maxw); }

@media (max-width: 640px) { .fan-hero { padding-top: 1.6rem; } }

.fan-kicker {
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--a, var(--gold)); margin-bottom: 1rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem;
}

.fan-title {
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(2.6rem, 8vw, 6rem); line-height: 0.92;
  letter-spacing: -0.04em; margin-bottom: 1rem;
}

.fan-lede-hero { max-width: 62ch; color: var(--ink-dim); font-size: 1.04rem; line-height: 1.8; }

.fan-hero .back-link { margin-bottom: 1.6rem; }

.fan-links { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.6rem; }

.fan-link {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.04em;
  padding: 0.6rem 1.1rem; border-radius: 100px;
  border: 1px solid var(--line); color: var(--ink);
  transition: color .25s, border-color .25s, background .25s, transform .25s var(--ease);
}

.fan-link:hover {
  color: var(--a, var(--gold)); transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 55%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 8%, transparent);
}

.fan-sec { padding: 0 var(--gutter); max-width: var(--maxw); margin-bottom: 3.4rem; }

.subsec--fan { color: var(--a, var(--gold)); }

.fan-lede { max-width: 70ch; margin: -0.6rem 0 1.8rem; color: var(--ink-dim); font-size: 0.96rem; line-height: 1.75; }

/* ── kind: cards ── */
.fan-cards {
  display: grid; gap: clamp(0.9rem, 2vw, 1.4rem);
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

.fan-card {
  position: relative; display: flex; flex-direction: column; gap: 0.45rem;
  padding: 1.3rem 1.25rem 1.2rem;
  border: 1px solid var(--line); border-radius: 16px;
  background: rgba(255,255,255,0.016);
  transition: transform .4s var(--ease), border-color .4s, box-shadow .4s;
}

.fan-card:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
  box-shadow: 0 22px 46px -30px #000, 0 0 34px -24px var(--a, #f5c63c);
}

/* Row alignment, same as the tiles further down: every card in a row shares
   its rows, so the tag, title, sub, description, link and meta line up across
   the row even when one card carries a part its neighbours don't (fanpage.js
   fills those in as an empty .fan-slot). */
@supports (grid-template-rows: subgrid) {
  .fan-cards { --rowgap: clamp(0.9rem, 2vw, 1.4rem); row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-cards > .fan-card {
    display: grid; grid-row: span 6; grid-template-rows: subgrid;
    row-gap: 0; align-content: start; margin-bottom: var(--rowgap);
  }
  .fan-card > * + * { margin-top: 0.45rem; }   /* was the flex column's gap */
  .fan-card > .fan-visit, .fan-card > .fan-visits { justify-self: start; }
  /* .fan-meta used margin-top:auto to drop to the card's foot. In a shared row
     that bottom-aligns a one-line meta against a two-line one, so it starts at
     the top of its row like every other part now. */
  .fan-cards > .fan-card > .fan-meta { margin-top: 0.45rem; align-self: start; }
  /* the "I went here" row is a seventh part, so a section that opts into it
     spans one row more. Every card in such a section emits the part, empty or
     not, which is what keeps a row of cards in step. */
  .fan-cards--been > .fan-card { grid-row: span 7; }
  /* the completion chips (finished stamp, hours, rating) are a row of their
     own in the same way: `chips: true` on the section, one more row, and one
     more again when the section carries both */
  .fan-cards--chips > .fan-card { grid-row: span 7; }
  .fan-cards--chips.fan-cards--been > .fan-card { grid-row: span 8; }
}

/* ── "I have actually been" ──
   The stamp, and my own photograph from the place, at the foot of a card. */
.fan-beenwrap { display: flex; flex-direction: column; gap: 0.55rem; align-items: flex-start; }

/* The pill wraps as a set of whole words, never mid-phrase: "I went here" and
   the date each stay on one line and the date drops below when the card is too
   narrow for both, which is what the twelve-across parks grid does. */
.fan-been {
  display: inline-flex; align-items: baseline; flex-wrap: wrap;
  gap: 0.1rem 0.4rem;
  padding: 0.28rem 0.6rem 0.3rem; max-width: 100%;
  border: 1px solid color-mix(in srgb, var(--a, #f5c63c) 42%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--a, #f5c63c) 11%, transparent);
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--a, var(--gold));
  white-space: nowrap;
}

.fan-been i { font-style: normal; font-size: 0.72rem; line-height: 1; }

.fan-been em {
  font-style: normal; color: var(--mute);
  white-space: normal;
}

.fan-beenpic {
  position: relative; display: block; width: 100%;
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  transition: border-color .4s, box-shadow .4s, transform .4s var(--ease);
}

.fan-beenpic img {
  width: 100%; height: auto; display: block;
  aspect-ratio: 4 / 3; object-fit: cover;
}

.fan-beenpic span {
  display: block; padding: 0.5rem 0.7rem 0.55rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--mute);
}

.fan-beenpic:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 48%, transparent);
  box-shadow: 0 18px 38px -28px #000, 0 0 28px -22px var(--a, #f5c63c);
}

@media (prefers-reduced-motion: reduce) {
  .fan-beenpic, .fan-beenpic:hover { transition: none; transform: none; }
}

.fan-card h4 {
  font-family: var(--serif); font-weight: 600; font-size: 1.1rem;
  letter-spacing: -0.02em; line-height: 1.2; color: var(--ink);
}

.fan-card:hover h4 { color: var(--a, var(--gold)); }

.fan-sub { font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.06em; color: var(--a, var(--gold)); opacity: .9; }

.fan-card p { font-size: 0.85rem; line-height: 1.6; color: var(--ink-dim); }

.fan-meta {
  margin-top: auto; padding-top: 0.5rem;
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--mute);
}

.fan-tag {
  align-self: flex-start; font-family: var(--mono); font-size: 0.55rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.2rem 0.5rem; border-radius: 100px;
  color: var(--a, var(--gold));
  border: 1px solid color-mix(in srgb, var(--a, #f5c63c) 40%, transparent);
}

/* an outbound link on a card or tile */
.fan-visit {
  align-self: flex-start; margin-top: 0.15rem;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--a, var(--gold));
  border-bottom: 1px dotted color-mix(in srgb, var(--a, #f5c63c) 55%, transparent);
  padding-bottom: 1px; transition: color .25s, border-color .25s;
}

.fan-visit:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* several links on one row (`links` on an item, see fanpage.js) */
.fan-visits { display: flex; flex-wrap: wrap; gap: 0.15rem 0.9rem; align-self: flex-start; }

.fan-visits > .fan-visit { margin-top: 0.15rem; }

/* ── kind: rank ── */
.fan-rank { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; }

.fan-rankrow {
  display: flex; align-items: flex-start; gap: 1.1rem;
  padding: 0.9rem 1.1rem; border: 1px solid var(--line); border-radius: 14px;
  background: rgba(255,255,255,0.012);
  transition: transform .35s var(--ease), border-color .35s, background .35s;
}

.fan-rankrow:hover {
  transform: translateX(5px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 5%, transparent);
}

.fan-num {
  font-family: var(--mono); font-size: 1.1rem; font-weight: 500;
  color: var(--a, var(--gold)); opacity: .75; min-width: 2.2rem; line-height: 1.4;
}

.fan-rankbody { display: flex; flex-direction: column; gap: 0.25rem; }

.fan-rankhead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.7rem; }

.fan-rankhead b { font-family: var(--serif); font-weight: 600; font-size: 1.02rem; letter-spacing: -0.01em; }

.fan-rankhead i { font-style: normal; font-family: var(--mono); font-size: 0.62rem; color: var(--mute); }

.fan-rankdesc { font-size: 0.85rem; line-height: 1.6; color: var(--ink-dim); }

/* ── kind: timeline ── */
.fan-time { position: relative; padding-left: 1.4rem; }

.fan-time::before {
  content: ""; position: absolute; left: 3px; top: 6px; bottom: 6px; width: 1px;
  background: linear-gradient(var(--a, #f5c63c), transparent);
  opacity: .4;
}

.fan-tick { position: relative; display: flex; flex-wrap: wrap; gap: 0.3rem 1.4rem; padding: 0.7rem 0 0.9rem; }

.fan-tick::before {
  content: ""; position: absolute; left: -1.4rem; top: 1.15rem;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--a, var(--gold)); box-shadow: 0 0 12px -1px var(--a, #f5c63c);
}

.fan-when {
  font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em;
  color: var(--a, var(--gold)); min-width: 9rem; padding-top: 0.15rem;
}

.fan-what { display: flex; flex-direction: column; gap: 0.2rem; max-width: 62ch; }

.fan-what b { font-family: var(--serif); font-weight: 600; font-size: 1rem; }

.fan-what em { font-style: normal; font-size: 0.85rem; line-height: 1.6; color: var(--ink-dim); }

/* ── kind: tiles ── */
.fan-tiles {
  display: grid; gap: 0.7rem;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
}

.fan-tile {
  display: flex; align-items: flex-start; gap: 0.8rem;
  padding: 0.8rem 0.9rem; border: 1px solid var(--line); border-radius: 12px;
  background: rgba(255,255,255,0.012);
  transition: transform .35s var(--ease), border-color .35s, background .35s;
}

/* hover only where it can end: on touch the last tapped tile stayed lifted */
@media (hover: hover) {
.fan-tile:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 6%, transparent);
}
}

.fan-swatch {
  flex: none; width: 12px; height: 12px; margin-top: 3px; border-radius: 3px;
  background: var(--a, var(--gold));
  box-shadow: 0 0 12px -1px var(--a, #f5c63c);
}

.fan-tiletext { display: flex; flex-direction: column; gap: 0.15rem; }

.fan-tiletext b { font-family: var(--serif); font-weight: 600; font-size: 0.94rem; }

.fan-tiletext i { font-style: normal; font-family: var(--mono); font-size: 0.6rem; color: var(--a, var(--gold)); opacity: .85; }

.fan-tiletext em { font-style: normal; font-size: 0.78rem; line-height: 1.5; color: var(--ink-dim); }

/* Row alignment, same idea as the app cards (see components.css): each tile is
   a SUBGRID of its row in .fan-tiles, so every tile in a row puts its title on
   one line, its sub on the next, then the description, the chips and the visit
   link, however much any one tile carries. The columns stay as they were:
   swatch · text · screenshots.

   fanpage.js emits an empty .fan-slot for a part an item does not have; drop
   that and the parts below it climb a row and the alignment goes. */
@supports (grid-template-rows: subgrid) {
  /* the row gap moves onto the tiles: a subgrid otherwise inherits the grid's
     gap between every row it spans, which would space a tile's own title, sub
     and description apart by the full grid gap (see components.css) */
  .fan-tiles { --rowgap: 0.7rem; row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-tiles--compact { --rowgap: 0.5rem; }
  .fan-tiles > .fan-tile {
    display: grid; align-content: start;
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 0.8rem; row-gap: 0;
    grid-row: span 5; grid-template-rows: subgrid;
    margin-bottom: var(--rowgap);
  }
  .fan-tiles--compact > .fan-tile { column-gap: 0.55rem; }
  .fan-tile > .fan-swatch { grid-column: 1; grid-row: 1; }
  .fan-tile > .fan-tiletext {
    grid-column: 2; grid-row: 1 / span 5;
    display: grid; grid-template-rows: subgrid; row-gap: 0;
  }
  /* .fan-tiletext was a flex column with a gap; grid rows need real margins */
  .fan-tile > .fan-tiletext > * + * { margin-top: 0.15rem; }
  /* grid items stretch by default, which would drag the visit link's underline
     across the whole tile; the text blocks still want the full width */
  .fan-tile > .fan-tiletext > .fan-visit, .fan-tile > .fan-tiletext > .fan-visits { justify-self: start; }
  .fan-tile > .fan-tileshots, .fan-tile > .fan-slot { grid-column: 3; grid-row: 1 / span 5; }
  .fan-slot { display: block; height: 0; }

  /* The switchable sets (the LEGO catalogue) carry a strip of screenshots,
     and an auto third column sized from a 1200px banner took the whole tile:
     the text column collapsed to nothing and the title sat under the banner,
     at every viewport width. The grid-view rule further down meant to stack
     the tile as a column, but it sets flex-direction on what is a grid here,
     so it did nothing. So the strip gets a track that does not size from the
     image: in grid view a row of its own under the text (the tile spans one
     more row, and the text keeps its five shared rows so titles, subs and
     descriptions still line up across the row); in list view a fixed share
     of the width, and on a phone a row of its own again. */
  .fan-tiles--switch.is-grid > .fan-tile { grid-template-columns: minmax(0, 1fr); grid-row: span 7; }
  .fan-tiles--switch.is-grid > .fan-tile > .fan-swatch { grid-column: 1; grid-row: 1; }
  .fan-tiles--switch.is-grid > .fan-tile > .fan-tiletext { grid-column: 1; grid-row: 2 / span 5; }
  .fan-tiles--switch.is-grid > .fan-tile > .fan-tileshots,
  .fan-tiles--switch.is-grid > .fan-tile > .fan-slot { grid-column: 1; grid-row: 7; }

  .fan-tiles--switch.is-list > .fan-tile { grid-template-columns: auto minmax(0, 1fr) min(44%, 480px); }
  .fan-tiles--switch.is-list > .fan-tile > .fan-tileshots { width: 100%; }
  @media (max-width: 760px) {
    .fan-tiles--switch.is-list > .fan-tile { grid-template-columns: auto minmax(0, 1fr); grid-row: span 6; }
    .fan-tiles--switch.is-list > .fan-tile > .fan-tileshots,
    .fan-tiles--switch.is-list > .fan-tile > .fan-slot { grid-column: 1 / -1; grid-row: 6; }
  }
}

/* compact: for the long sets, where thirty tiles at full size is a wall */
.fan-tiles--compact { gap: 0.5rem; grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); }

.fan-tiles--compact .fan-tile { padding: 0.6rem 0.7rem; gap: 0.55rem; border-radius: 10px; }

.fan-tiles--compact .fan-swatch { width: 9px; height: 9px; margin-top: 4px; }

.fan-tiles--compact .fan-tiletext b { font-size: 0.84rem; }

.fan-tiles--compact .fan-tiletext i { font-size: 0.54rem; }

.fan-tiles--compact .fan-tiletext em { font-size: 0.71rem; line-height: 1.45; }

/* ── the controls row: sort axes on the left, the tally pill on the right ── */
.fan-sort {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin: 0 0 1rem;
}

.fan-sortset { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }

.fan-sort-k {
  font-family: var(--mono); font-size: 0.54rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--mute); margin-right: 0.15rem;
}

.fan-sortbtn {
  font: inherit; cursor: pointer; padding: 0.34rem 0.62rem 0.3rem;
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255,255,255,0.02);
  transition: color .25s, border-color .25s, background .25s;
}

.fan-sortdir i { font-style: normal; font-size: 0.8rem; line-height: 1; }

/* the tally pill: how many of these I have actually finished */
.fan-tally {
  margin-left: auto; display: inline-flex; align-items: baseline; gap: 0.3rem;
  padding: 0.3rem 0.7rem 0.26rem; border-radius: 999px;
  border: 1px solid rgba(95,208,160,0.4); background: rgba(95,208,160,0.09);
}

.fan-tally b { font-family: var(--mono); font-size: 0.8rem; font-weight: 600; color: #7fe0b8; }

.fan-tally em { font-style: normal; color: var(--mute); font-size: 0.7rem; }

.fan-tally i {
  font-style: normal; font-family: var(--mono); font-size: 0.54rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--mute);
  margin-left: 0.15rem;
}

.fan-sortbtn:hover {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
}

.fan-sortbtn.is-on {
  color: var(--a, var(--gold));
  border-color: color-mix(in srgb, var(--a, #f5c63c) 55%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 10%, transparent);
}

.fan-sortbtn:focus-visible { outline: 2px solid var(--a, var(--gold)); outline-offset: 2px; }

/* ── group headers, dropped in by the Group control (fanpage.js) ──
   Spanning the full grid row is what turns a flat grid into sections; the same
   rule holds in list view, where every row is full width anyway. Not .reveal:
   these are created after reveal.js has scanned, so they must never start at
   opacity 0 or they would simply stay invisible. */
.fan-grouphead {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 0.55rem;
  margin: 1.05rem 0 0.05rem;
  font-family: var(--mono); font-size: 0.62rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--a, var(--gold));
}

.fan-grouphead:first-child { margin-top: 0.1rem; }

.fan-grouphead i {
  font-style: normal; font-weight: 400; letter-spacing: 0;
  color: var(--mute);
}

/* the series' rating average sits on the far side of the rule line: the
   ::after has flex: 1 and everything is order 0, so order: 1 is "after it" */
.fan-grouphead em {
  font-style: normal; font-weight: 400; letter-spacing: 0.08em;
  color: var(--ink-dim); order: 1;
}

.fan-grouphead::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--a, #f5c63c) 38%, transparent), transparent);
}

/* a tile with images opens them in the lightbox on click (gallery.js) */
.fan-tile[data-images] { cursor: pointer; }

/* ── a tile's own screenshot, and its completion stamp ──
   Green rather than the page accent on purpose: "I finished this" is the same
   claim on every page, so it should not repaint itself per franchise. */
/* The text column is a flex item sized from its content, and an <img> inside it
   would otherwise let a wide screenshot push the tile past its grid column.
   Keyed on the shot, not on .is-done: a tile can carry one without the other. */
.fan-tile:has(.fan-tileshot) .fan-tiletext { min-width: 0; }

/* every screenshot a title has, in one container. The banner takes the full
   width and the rest pair up under it, two to a row; flex rather than grid,
   because an odd shot left on the last row must sit CENTERED under its pair,
   which grid placement cannot do. */
.fan-tileshots {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.45rem; align-content: flex-start;
}

.fan-tileshots .fan-tileshot { flex: 0 0 calc((100% - 0.45rem) / 2); }

.fan-tileshots .fan-tileshot:first-child { flex-basis: 100%; }

.fan-tileshot {
  display: block; max-width: 100%; border-radius: 6px; overflow: hidden;
  border: 1px solid var(--line); line-height: 0;
  transition: border-color .3s;
}

.fan-tileshot:hover { border-color: color-mix(in srgb, var(--a, #f5c63c) 55%, transparent); }

/* No forced aspect-ratio and no cropping: these are Steam library headers, and
   the play time and achievement count sit in the strip along the bottom, which
   is the whole reason the shot is here. */
.fan-tileshot img { display: block; width: 100%; height: auto; }

/* `cols: N` on a tiles section. Below the breakpoints it still reflows on its
   own; the fixed count only kicks in once each column can actually hold a
   screenshot at a sensible size. */
.fan-tiles[data-cols] { grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); }

@media (min-width: 900px) {
  .fan-tiles[data-cols] { grid-template-columns: repeat(var(--cols, 3), 1fr); }
}

/* ── my own rating, out of ten ──
   The bar behind the figure IS the score, so a column of these can be read as
   a ranking without comparing the numbers one at a time. Painted in the page
   accent rather than a fixed colour, because unlike the green completion stamp
   this is an opinion about that page's own subject. */
.fan-rate {
  display: inline-flex; align-items: baseline; gap: 0.14rem;
  padding: 0.28rem 0.6rem 0.24rem; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--a, #f5c63c) 42%, transparent);
  /* the fill IS the background: a hard gradient stop at the score. Drawn on
     the pill itself rather than an absolutely positioned child, because at
     least one engine sized that child against the text instead of the pill
     and the bar came out at half its width. */
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--a, #f5c63c) 20%, transparent) var(--pct, 0%),
    rgba(255,255,255,0.02) var(--pct, 0%));
}

.fan-rate-v {
  font-family: var(--mono); font-size: 0.76rem;
  font-weight: 600; color: var(--a, var(--gold));
}

.fan-rate-o {
  font-family: var(--mono); font-size: 0.52rem;
  letter-spacing: 0.1em; color: var(--mute);
}

.fan-tally--rate { border-color: color-mix(in srgb, var(--a, #f5c63c) 42%, transparent); background: color-mix(in srgb, var(--a, #f5c63c) 8%, transparent); }

.fan-tally--rate b { color: var(--a, var(--gold)); }

/* ── the grid / list switch ──
   Same tiles, same order, two densities. Grid is the banner and the numbers;
   list opens each row out full width and brings the screenshots with it. */
.fan-viewbtn svg { width: 11px; height: 11px; opacity: .8; }

/* Grid keeps just the banner; the full set is list view's job */
.fan-tiles--switch.is-grid .fan-tileshot:not(:first-child) { display: none; }

/* Grid: the accent stops being a dot in the corner and becomes the colour
   band across the top of the tile, with the Steam banner underneath the text
   as the tile's one image. */
.fan-tiles--switch.is-grid .fan-tile {
  flex-direction: column; gap: 0.5rem; padding-top: 0.85rem;
}

.fan-tiles--switch.is-grid .fan-swatch {
  width: 100%; height: 4px; margin: 0; border-radius: 2px;
}

.fan-tiles--switch.is-grid .fan-tiletext { width: 100%; min-width: 0; }

.fan-tiles--switch.is-list { grid-template-columns: 1fr; gap: 0.6rem; }

@media (min-width: 900px) {
  .fan-tiles--switch.is-list[data-cols] { grid-template-columns: 1fr; }
}

.fan-tiles--switch.is-list .fan-tile { padding: 0.9rem 1.1rem; gap: 1rem; align-items: stretch; }

.fan-tiles--switch.is-list .fan-swatch { width: 4px; height: auto; align-self: stretch; margin: 0; border-radius: 2px; }

.fan-tiles--switch.is-list .fan-tiletext { flex: 1; min-width: 0; gap: 0.3rem; }

.fan-tiles--switch.is-list .fan-tiletext b { font-size: 1rem; }

.fan-tiles--switch.is-list .fan-tiletext em { font-size: 0.82rem; max-width: 78ch; }

/* The screenshots are the point of list view: the game's whole set sits in
   its own grid to the right of the text: banner across the top, the rest
   two-up beneath it. On narrow screens the set drops below the text instead
   of squeezing next to it. */
.fan-tiles--switch.is-list .fan-tileshots { flex: none; width: min(44%, 480px); }

@media (max-width: 760px) {
  .fan-tiles--switch.is-list .fan-tile { flex-wrap: wrap; }
  .fan-tiles--switch.is-list .fan-tileshots { width: 100%; }
}

.fan-tiles--switch.is-list .fan-chips { margin-top: 0.55rem; }

.fan-done {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.3rem 0.68rem 0.26rem;
  border: 1px solid rgba(95,208,160,0.42); border-radius: 999px;
  background: rgba(95,208,160,0.09);
}

/* ── when I finished it ──
   Reads as a quieter sibling of the green 100% stamp it sits next to: same
   pill, no fill, so the completion stamp stays the loud one and the date is
   the footnote to it. */
.fan-fin {
  display: inline-flex; align-items: baseline; gap: 0.36rem;
  padding: 0.3rem 0.62rem 0.26rem;
  border: 1px solid rgba(95,208,160,0.24); border-radius: 999px;
}
.fan-fin i {
  font-style: normal; font-size: 0.6rem; line-height: 1;
  color: rgba(95,208,160,0.75);
}
.fan-fin-l {
  font-family: var(--mono); font-size: 0.54rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--mute);
}
.fan-fin-v {
  font-family: var(--mono); font-size: 0.68rem; font-weight: 500;
  letter-spacing: 0.05em; color: var(--ink-dim);
}

.fan-done span {
  font-family: var(--mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.08em;
  color: #7fe0b8; font-style: normal; line-height: 1.2;
}

.fan-done .fan-done-h { color: var(--ink-dim); letter-spacing: 0.04em; }

/* play time without a completion claim: same chip, no green */
.fan-done--part { border-color: var(--line); background: rgba(255,255,255,0.025); }

.fan-done--part .fan-done-h { color: var(--ink-dim); }

/* the chips row: what it took me, then what it was reckoned to take. The row
   owns the top margin so the two chips cannot double it up between them. */
.fan-chips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem;
  align-self: flex-start; margin-top: 0.55rem;
}

.fan-chips:empty { display: none; margin-top: 0; }

.fan-proj {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.3rem 0.68rem 0.26rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255,255,255,0.025);
}

.fan-proj span {
  font-family: var(--mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.04em;
  font-style: normal; line-height: 1.2; color: var(--ink-dim);
}

.fan-proj .fan-proj-l {
  font-size: 0.5rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--mute);
}

/* under the projection is the good direction, so it gets the green */
.fan-proj .fan-proj-d.is-under { color: #7fe0b8; }

.fan-proj .fan-proj-d.is-over { color: #e8a24c; }

.fan-tally--time { border-color: var(--line); background: rgba(255,255,255,0.025); }

.fan-tally--time b { color: var(--ink); }

.fan-tile.is-done .fan-swatch { box-shadow: 0 0 12px -1px var(--a, #f5c63c), 0 0 0 3px rgba(95,208,160,0.18); }

/* ── kind: quotes ── */
.fan-quotes {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

/* Each quote carries its own --a, so the whole card is graded to whoever said
   it: their blade colour, or their faction's if they never held one. */
.fan-quote {
  padding: 1.3rem 1.4rem; border-left: 3px solid var(--a, var(--gold));
  background: linear-gradient(90deg, color-mix(in srgb, var(--a, #f5c63c) 9%, transparent), transparent 74%);
  border-radius: 0 12px 12px 0;
  box-shadow: -10px 0 30px -22px var(--a, #f5c63c);
  transition: background .4s, box-shadow .4s, transform .4s var(--ease);
}

.fan-quote:hover {
  transform: translateX(4px);
  background: linear-gradient(90deg, color-mix(in srgb, var(--a, #f5c63c) 16%, transparent), transparent 78%);
  box-shadow: -12px 0 40px -20px var(--a, #f5c63c);
}

/* row alignment: the attribution lines up across a row of quotes however many
   lines the quote itself runs to (same subgrid idea as the cards and tiles) */
@supports (grid-template-rows: subgrid) {
  .fan-quotes { --rowgap: 1rem; row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-quotes > .fan-quote {
    display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: 0;
    align-content: start; margin-bottom: var(--rowgap);
  }
  .fan-quote > figcaption { margin-top: 0.75rem; }
}

.fan-quote blockquote {
  font-family: var(--serif); font-size: 1.06rem; line-height: 1.5;
  letter-spacing: -0.01em; color: var(--ink);
}

.fan-quote figcaption {
  margin-top: 0.6rem; font-family: var(--mono); font-size: 0.62rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--a, #f5c63c) 72%, var(--mute));
}

/* ── kind: stats ── a strip of big figures */
.fan-stats {
  display: grid; gap: 0.7rem;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
}

.fan-stat {
  display: flex; flex-direction: column; gap: 0.32rem;
  padding: 1.1rem 1.1rem 1.2rem;
  border: 1px solid var(--line); border-radius: 14px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--a, #f5c63c) 7%, transparent), transparent 72%);
  transition: transform .35s var(--ease), border-color .35s;
}

.fan-stat:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
}

/* row alignment: figure, label and note each on their own shared line */
@supports (grid-template-rows: subgrid) {
  .fan-stats { --rowgap: 0.7rem; row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-stats > .fan-stat {
    display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0;
    align-content: start; margin-bottom: var(--rowgap);
  }
  .fan-stat > * + * { margin-top: 0.32rem; }
}

.fan-stat b {
  font-family: var(--serif); font-weight: 700; line-height: 1;
  font-size: clamp(1.7rem, 3.4vw, 2.35rem); letter-spacing: -0.035em;
  color: var(--a, var(--gold));
}

.fan-stat i {
  font-style: normal; font-family: var(--mono); font-size: 0.6rem;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink);
}

.fan-stat em { font-style: normal; font-size: 0.78rem; line-height: 1.55; color: var(--ink-dim); }

/* ── kind: era ── a rail that bleeds off both edges, hung under a time axis */
.fan-rail {
  position: relative; overflow-x: auto; overflow-y: hidden;
  margin: 0 calc(var(--gutter) * -1); padding: 0 var(--gutter);
  scroll-snap-type: x proximity; scrollbar-width: none;
  overscroll-behavior-x: contain;   /* the end of the rail must not become a back swipe */
}

.fan-rail::-webkit-scrollbar { display: none; }

.fan-rail-track {
  position: relative; display: flex; gap: 0.9rem;
  width: max-content; padding: 2.2rem 0 0.6rem;
}

.fan-rail-track::before {                 /* the axis the eras hang from */
  content: ""; position: absolute; left: 0; right: 0; top: 1.55rem; height: 1px;
  background: linear-gradient(90deg,
    transparent, color-mix(in srgb, var(--a, #f5c63c) 70%, transparent) 6%,
    color-mix(in srgb, var(--a, #f5c63c) 70%, transparent) 94%, transparent);
  opacity: .45;
}

.fan-era {
  position: relative; flex: none; width: 258px;
  display: flex; flex-direction: column; gap: 0.38rem;
  padding: 1rem 1.05rem 1.1rem; scroll-snap-align: start;
  border: 1px solid var(--line); border-radius: 14px;
  background: rgba(255,255,255,0.014);
  transition: transform .4s var(--ease), border-color .4s, background .4s;
}

.fan-era:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 5%, transparent);
}

.fan-era-when {
  font-family: var(--mono); font-size: 0.63rem; letter-spacing: 0.1em;
  color: var(--a, var(--gold));
}

.fan-era-dot {
  position: absolute; top: -0.66rem; left: 1.15rem;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--a, var(--gold)); box-shadow: 0 0 14px -1px var(--a, #f5c63c);
}

.fan-era h4 { font-family: var(--serif); font-weight: 600; font-size: 1.02rem; letter-spacing: -0.02em; line-height: 1.2; }

.fan-era p { font-size: 0.82rem; line-height: 1.6; color: var(--ink-dim); }

.fan-era .fan-meta { margin-top: auto; }

.fan-rail.is-dragging { cursor: grabbing; user-select: none; }

.fan-rail.is-dragging .fan-era { transition: none; }

.fan-rail-hint {
  margin-top: 0.5rem; font-family: var(--mono); font-size: 0.58rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--mute);
}

/* ── kind: films ── the numeral set enormous behind the type: as close to a
   poster as this site gets, and still no artwork */
.fan-films {
  display: grid; gap: clamp(0.9rem, 2vw, 1.3rem);
  grid-template-columns: repeat(auto-fit, minmax(282px, 1fr));
}

.fan-film {
  position: relative; overflow: hidden; display: flex; min-height: 252px;
  padding: 1.4rem 1.35rem 1.3rem;
  border: 1px solid var(--line); border-radius: 18px;
  background: linear-gradient(163deg, color-mix(in srgb, var(--a, #f5c63c) 11%, transparent), rgba(6,7,10,.55) 60%);
  transition: transform .45s var(--ease), border-color .45s, box-shadow .45s;
}

.fan-film:hover {
  transform: translateY(-6px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
  box-shadow: 0 26px 54px -32px #000, 0 0 46px -28px var(--a, #f5c63c);
}

.fan-film-num {
  position: absolute; right: 0.02em; bottom: -0.26em; pointer-events: none;
  font-family: var(--serif); font-weight: 700; line-height: 0.78;
  font-size: clamp(6.5rem, 15vw, 10.5rem); letter-spacing: -0.05em;
  color: var(--a, var(--gold)); opacity: .1;
  transition: opacity .45s, transform .5s var(--ease);
}

.fan-film:hover .fan-film-num { opacity: .2; transform: translateY(-5px); }

.fan-film-body { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 0.45rem; width: 100%; }

.fan-film h4 { font-family: var(--serif); font-weight: 700; font-size: 1.32rem; letter-spacing: -0.03em; line-height: 1.1; }

.fan-film p { font-size: 0.86rem; line-height: 1.65; color: var(--ink-dim); max-width: 40ch; }

.fan-film .fan-meta { margin-top: auto; }

/* ── kind: sabers ── a rack of hilts; the blade extends on hover, and a click
   leaves it lit. The whole thing is one <button> so touch and keys work. */
.fan-rack {
  display: grid; gap: 0.7rem;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
}

.fan-saber {
  position: relative; display: block; text-align: left; width: 100%;
  padding: 2.3rem 0.95rem 1rem; font: inherit; color: var(--ink);
  border: 1px solid var(--line); border-radius: 14px;
  background: rgba(255,255,255,0.012); cursor: pointer;
  transition: transform .35s var(--ease), border-color .35s, background .35s, box-shadow .35s;
}

/* Row alignment: name, owner and note each on a shared line across the rack,
   so a saber with no owner line doesn't lift its note above its neighbours'.
   The blade and hilt are absolutely positioned, so they aren't rows.
   fanpage.js fills a missing part in with an empty .fan-slot. */
@supports (grid-template-rows: subgrid) {
  .fan-rack { --rowgap: 0.7rem; row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-rack > .fan-saber {
    display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0;
    align-content: start; margin-bottom: var(--rowgap);
  }
  .fan-saber > .saber-text {
    grid-row: 1 / span 3; display: grid; grid-template-rows: subgrid; row-gap: 0;
  }
  .saber-text > * + * { margin-top: 0.18rem; }   /* was the flex column's gap */
}

/* hover only where it can end: on touch a tapped saber kept its :hover state and could not be un-lit */
@media (hover: hover) {
  .fan-saber:hover { transform: translateY(-3px); }
  .fan-saber:hover .saber-blade { width: calc(100% - 1.9rem - 29px); }
  .fan-saber:hover {
    border-color: color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
    background: color-mix(in srgb, var(--a, #f5c63c) 6%, transparent);
  }
}

.fan-saber.is-lit {
  border-color: color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 6%, transparent);
}

.fan-saber.is-lit { box-shadow: 0 0 40px -26px var(--a, #f5c63c); }

.fan-saber:focus-visible { outline: 2px solid var(--a, var(--gold)); outline-offset: 3px; }

.saber-hilt {
  position: absolute; left: 0.95rem; top: 1rem; z-index: 1;
  display: flex; align-items: center; gap: 2px;
  width: 30px; height: 9px; padding-left: 4px; border-radius: 2px;
  background: linear-gradient(180deg, #d2d8df, #737c88 52%, #2a2f36);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.6);
}

.saber-hilt i { width: 2px; height: 5px; border-radius: 1px; background: rgba(0,0,0,.45); }

.saber-blade {
  position: absolute; left: calc(0.95rem + 29px); top: calc(1rem + 1.5px);
  height: 6px; width: 0; border-radius: 0 3px 3px 0;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--a, #f5c63c) 60%, #fff), var(--a, var(--gold)) 30%);
  box-shadow: 0 0 10px 1px var(--a, #f5c63c),
              0 0 30px 4px color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
  transition: width .42s cubic-bezier(.2, .9, .3, 1);
}

.saber-blade i {                              /* the white core inside the glow */
  position: absolute; inset: 1.6px 3px 1.6px 0; border-radius: 2px;
  background: rgba(255,255,255,.92);
}

/* the Darksaber: a black blade, read only by the white edge burning off it */
.saber--dark .saber-blade {
  background: linear-gradient(90deg, #16161d, #08080c 60%);
  box-shadow:
    0 0 0 1.4px rgba(232, 240, 252, .9),
    0 0 12px 1px rgba(200, 216, 240, .55),
    0 0 34px 6px rgba(140, 168, 210, .28);
}

.saber--dark .saber-blade i { background: transparent; }

.fan-saber:focus-visible .saber-blade,
.fan-saber.is-lit .saber-blade { width: calc(100% - 1.9rem - 29px); }

.saber-text { display: flex; flex-direction: column; gap: 0.18rem; }

.saber-text b { font-family: var(--serif); font-weight: 600; font-size: 0.96rem; }

.saber-text i { font-style: normal; font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.06em; color: var(--a, var(--gold)); opacity: .9; }

.saber-text em { font-style: normal; font-size: 0.78rem; line-height: 1.5; color: var(--ink-dim); }

/* ── kind: gallery ── my own screenshots, the one place these pages use photos */
.fan-shots {
  display: grid; gap: clamp(0.9rem, 2vw, 1.4rem);
  grid-template-columns: 1fr;
}

@media (min-width: 760px) { .fan-shots--two { grid-template-columns: 1fr 1fr; } }

/* `grid: true`: my own photographs, several at a time. A full-width column is
   right for a wide app screenshot and wrong for a camera roll. */
.fan-shots--grid { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.fan-shots--grid .fan-shot img { aspect-ratio: 4 / 3; object-fit: cover; }

.fan-shot {
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  background: rgba(255,255,255,0.014);
  transition: transform .4s var(--ease), border-color .4s, box-shadow .4s;
}

.fan-shot:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 45%, transparent);
  box-shadow: 0 22px 46px -30px #000, 0 0 34px -26px var(--a, #f5c63c);
}

.fan-shot img { width: 100%; height: auto; display: block; }

.fan-shot figcaption {
  display: flex; flex-direction: column; gap: 0.3rem;
  padding: 0.9rem 1.05rem 1rem;
  border-top: 1px solid var(--line);
}

.fan-shot figcaption b {
  font-family: var(--serif); font-weight: 600; font-size: 0.98rem;
  letter-spacing: -0.02em; color: var(--a, var(--gold));
}

/* direct children only: a caption can also carry the completion chips
   (.fan-chips, see fanpage.js), whose own spans and tick must keep their own
   type rather than the caption's */
.fan-shot figcaption > span { font-size: 0.83rem; line-height: 1.6; color: var(--ink-dim); }

.fan-shot figcaption > i {
  font-style: normal; font-family: var(--mono); font-size: 0.58rem;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute);
}

/* ── kind: links ── the "elsewhere" block that closes every fan page */
.fan-linkgrid {
  display: grid; gap: 0.7rem;
  grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
}

.fan-linkcard {
  display: flex; flex-direction: column; gap: 0.38rem;
  padding: 1.05rem 1.1rem 1rem;
  border: 1px solid var(--line); border-radius: 14px;
  background: rgba(255,255,255,0.012);
  transition: transform .35s var(--ease), border-color .35s, background .35s, box-shadow .35s;
}

/* row alignment: title, blurb and host line up across a row of link cards */
@supports (grid-template-rows: subgrid) {
  .fan-linkgrid { --rowgap: 0.7rem; row-gap: 0; margin-bottom: calc(-1 * var(--rowgap)); }
  .fan-linkgrid > .fan-linkcard {
    display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0;
    align-content: start; margin-bottom: var(--rowgap);
  }
  .fan-linkcard > * + * { margin-top: 0.38rem; }
}

.fan-linkcard:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--a, #f5c63c) 50%, transparent);
  background: color-mix(in srgb, var(--a, #f5c63c) 6%, transparent);
  box-shadow: 0 20px 42px -30px #000, 0 0 34px -26px var(--a, #f5c63c);
}

.fan-linkcard:focus-visible { outline: 2px solid var(--a, var(--gold)); outline-offset: 3px; }

.fan-linkhead { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; }

.fan-linkhead b { font-family: var(--serif); font-weight: 600; font-size: 1rem; letter-spacing: -0.02em; }

.fan-linkhead i {
  font-style: normal; flex: none; color: var(--a, var(--gold)); opacity: .55;
  transition: transform .35s var(--ease), opacity .35s;
}

.fan-linkcard:hover .fan-linkhead b { color: var(--a, var(--gold)); }

.fan-linkcard:hover .fan-linkhead i { opacity: 1; transform: translate(2px, -2px); }

.fan-linkcard p { font-size: 0.8rem; line-height: 1.55; color: var(--ink-dim); }

.fan-linkhost {
  margin-top: auto; padding-top: 0.5rem; overflow-wrap: anywhere;
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em;
  color: var(--mute); transition: color .35s;
}

.fan-linkcard:hover .fan-linkhost { color: color-mix(in srgb, var(--a, #f5c63c) 70%, var(--mute)); }

@media (max-width: 600px) {
  .fan-stats { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); }
  .fan-stat { padding: 0.9rem 0.9rem 1rem; }
  .fan-era { width: 76vw; max-width: 300px; }
  .fan-film { min-height: 196px; padding: 1.15rem 1.1rem 1.1rem; }
  .fan-film-num { font-size: 5.4rem; }
  .fan-film h4 { font-size: 1.15rem; }
}

/* ── backdrop brightness guard ──
   Every *-sky is a fixed layer behind the whole page. The failure mode I keep
   hitting is designing one in isolation, where a value looks right on its own,
   and then it drowns the text once real content sits on top. Worse, a
   drop-shadow glow does NOT come out of the element's opacity budget -- it adds
   light on top of it -- so ".55 opacity" can read far hotter than .55 suggests.

   Third pass at this. The first two set the cap too politely (.78), which is
   still bright when the thing underneath is a glowing sun or a gold sky -- 78%
   of too-bright is too bright. And `.bat-sky` set its own opacity *after* this
   rule, so at equal specificity it silently won and was never capped at all.

   Hence !important: this is a ceiling, not a suggestion, and a page-level rule
   must not be able to quietly opt out of it. Tune the pieces inside a backdrop
   however you like -- but the layer as a whole never exceeds this. */
[class$="-sky"] { opacity: .36 !important; }

@media (prefers-reduced-transparency: reduce) { [class$="-sky"] { opacity: .24 !important; } }

/* ── "When I got into it" ──
   One per fan page, above every section. Uses the page's own accent (--a) so it
   picks up each franchise's colour without 33 separate rules. */
.fan-intro-when {
  max-width: var(--maxw); margin: 0 0 2.6rem; padding: 1rem 1.2rem;
  display: grid; gap: 0.35rem;
  border-left: 3px solid var(--a, var(--ink-dim));
  background: linear-gradient(90deg, color-mix(in srgb, var(--a, #888) 8%, transparent), transparent 70%);
  border-radius: 0 5px 5px 0;
}

@media (min-width: 720px) { .fan-intro-when { margin-left: var(--gutter); margin-right: var(--gutter); } }

.fan-when-k {
  font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--mute);
}

.fan-intro-when b {
  font-family: var(--sans); font-size: 1.02rem; font-weight: 650;
  color: var(--a, var(--ink)); line-height: 1.4;
}

.fan-when-n { font-size: 0.9rem; line-height: 1.75; color: var(--ink-dim); max-width: 68ch; }

/* ── the shared interactive block (fan-play.js) ──
   A hotbar, modelled on the Minecraft one further down this file: numbered
   square slots in a row, every option visible at once, selected slot lit. The
   earlier two-column version broke badly at narrow widths and left a tall empty
   column next to a short one -- a single full-width strip has neither problem
   and scales to any number of items.

   Each page supplies its own accent per slot through --c, so one set of rules
   covers all twenty-four without a per-franchise stylesheet. */
.fan-play {
  padding: 0 var(--gutter); max-width: var(--maxw); margin: 0 0 3.4rem;
}

.fp-head h3 {
  font-family: var(--sans); font-weight: 650; font-size: 1.2rem; margin-bottom: 0.5rem;
}

.fp-head p { font-size: 0.94rem; line-height: 1.75; color: var(--ink-dim); max-width: 74ch; }

/* Wraps rather than scrolls: a Pokemon page with twenty-odd balls should show
   them all stacked up, not hide them behind a horizontal scrollbar. */
.fp-bar {
  display: flex; flex-wrap: wrap; gap: 5px;
  margin: 1.2rem 0 0.9rem; padding: 7px;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line); border-radius: 5px;
}

.fp-slot {
  position: relative; width: 52px; height: 52px; flex: none; cursor: pointer; padding: 0;
  display: grid; place-items: center;
  background: rgba(255,255,255,.05);
  border: 2px solid rgba(255,255,255,.1);
  box-shadow: inset -3px -3px 0 rgba(0,0,0,.4);
  transition: border-color .12s ease, background .12s ease, transform .12s ease;
}

.fp-slot svg { width: 26px; height: 26px; fill: none; stroke: var(--c); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round; }

.fp-slot i { width: 22px; height: 22px; background: var(--c); border-radius: 3px; opacity: .8; }

.fp-slot u {
  position: absolute; top: 1px; left: 4px; text-decoration: none;
  font-family: var(--mono); font-size: 0.5rem; color: rgba(255,255,255,.35);
}

/* Names are for screen readers and for the readout below, not for the slot. */
.fp-slot-n { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

.fp-slot:hover { border-color: rgba(255,255,255,.5); transform: translateY(-2px); }

.fp-slot.is-sel {
  border-color: var(--c);
  background: color-mix(in srgb, var(--c) 20%, transparent);
  box-shadow: inset -3px -3px 0 rgba(0,0,0,.3), 0 0 0 2px color-mix(in srgb, var(--c) 45%, transparent),
              0 0 18px -2px color-mix(in srgb, var(--c) 65%, transparent);
}

.fp-slot.is-sel svg { stroke-width: 1.9; }

.fp-slot:focus-visible { outline: 2px solid var(--c); outline-offset: 2px; }

.fp-hint {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.06em;
  color: var(--ink-dim); line-height: 1.7; min-height: 1.7em;
}

.fp-hint span { color: var(--mute); }

.fp-go {
  margin-top: 0.9rem; font: inherit; cursor: pointer;
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.55rem 1.15rem; border-radius: 3px; color: var(--ink);
  background: color-mix(in srgb, var(--a, #8fa8c0) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--a, #8fa8c0) 45%, transparent);
  transition: background .15s ease, border-color .15s ease;
}

.fp-go:hover:not(:disabled) { background: color-mix(in srgb, var(--a, #8fa8c0) 26%, transparent); }

.fp-go:disabled { opacity: .45; cursor: default; }

.fp-go:focus-visible { outline: 2px solid var(--a, #8fa8c0); outline-offset: 2px; }

.fp-out {
  margin-top: 1.2rem; padding: 1rem 1.15rem;
  border-left: 3px solid var(--line); border-radius: 0 5px 5px 0;
  background: rgba(255,255,255,.025);
}

.fp-out-head { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; }

.fp-out b { font-family: var(--sans); font-size: 1.1rem; font-weight: 650; }

.fp-out span {
  font-family: var(--mono); font-size: 0.55rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--mute);
}

.fp-out p { margin-top: 0.6rem; font-size: 0.93rem; line-height: 1.75; color: var(--ink-dim); max-width: 72ch; }

.fp-tally {
  margin-top: 0.8rem; font-family: var(--mono); font-size: 0.55rem;
  letter-spacing: 0.08em; color: var(--mute); line-height: 1.7;
}

@media (max-width: 560px) {
  .fp-slot { width: 42px; height: 42px; }
  .fp-slot svg { width: 21px; height: 21px; }
}

@media (prefers-reduced-motion: reduce) { .fp-slot:hover { transform: none; } }

/* ───────── Star Wars: the crawl, on a starfield ───────── */
body[data-fan="sw"] {
  --a: #ffe81f;
  --hud-grid: rgba(245,198,60,0.045); --hud-sweep: rgba(255,232,31,0.035);
  --hud-lit: rgba(255,232,31,0.2); --hud-halo: rgba(245,198,60,0.06);
}

body[data-fan="sw"] #field { opacity: .3; }

body[data-fan="sw"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.02em;
  color: #ffe81f; text-shadow: 0 0 40px rgba(255,232,31,.35);
}

/* the era rail reads as a HoloNet scrub: the axis glows, the dots are beacons */
body[data-fan="sw"] .fan-rail-track::before {
  height: 2px; opacity: .55;
  box-shadow: 0 0 16px rgba(255,232,31,.35);
}

body[data-fan="sw"] .fan-era {
  background: linear-gradient(168deg, rgba(255,232,31,.05), rgba(6,7,10,.6) 70%);
}

body[data-fan="sw"] .fan-era-when { text-transform: uppercase; letter-spacing: 0.14em; }

/* the prequel one-sheets: numerals in crawl yellow, tinted per film */
body[data-fan="sw"] .fan-film h4 { text-transform: uppercase; letter-spacing: -0.01em; }

body[data-fan="sw"] .fan-film-num { font-weight: 700; opacity: .13; }

body[data-fan="sw"] .fan-film:hover .fan-film-num { opacity: .24; }

/* saga rows: the numeral is the episode number, so give it room and weight */
body[data-fan="sw"] .fan-num { font-family: var(--serif); font-weight: 700; opacity: .85; }

/* stats read like a datapad readout */
body[data-fan="sw"] .fan-stat b { text-shadow: 0 0 30px rgba(255,232,31,.3); }

/* quotes get the yellow bar and a faint holo wash */
body[data-fan="sw"] .fan-quote blockquote { text-wrap: balance; }

/* ───────── Harry Potter, the castle at night under the enchanted ceiling ─────────
   Layers, back to front: the sky gradient (with the castle's own glow washing up
   from the ridge), a star tile, the moon, the Hogwarts silhouette, then the Great
   Hall candles floating over all of it. */
body[data-fan="hp"] { --a: #d3a625; }

@keyframes hp-twinkle { from { opacity: .34; } to { opacity: .62; } }

@keyframes hp-windows { from { opacity: .45; } to { opacity: .72; } }

@keyframes hp-float { from { transform: translateY(0); } to { transform: translateY(-22px); } }

@keyframes hp-flicker {
  0%, 100% { opacity: 1; }
  42%      { opacity: .72; }
  57%      { opacity: .95; }
  78%      { opacity: .8; }
}

body[data-fan="hp"] .fan-title { font-family: Georgia, "Times New Roman", serif; letter-spacing: -0.02em; }

body[data-fan="hp"] .fan-card h4,
body[data-fan="hp"] .fan-rankhead b,
body[data-fan="hp"] .fan-what b { font-family: Georgia, "Times New Roman", serif; }

@keyframes hat-ponder {
  0%, 100% { transform: rotate(-2.5deg); }
  50%      { transform: rotate(2.5deg); }
}

@keyframes hat-mutter { from { transform: scaleY(.7); } to { transform: scaleY(1.7); } }

@keyframes hat-shout {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.09) rotate(-3deg); }
  100% { transform: scale(1); }
}

@keyframes hat-said {
  from { opacity: 0; transform: translateY(10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

.crest--mine {
  border-color: color-mix(in srgb, var(--hl) 60%, transparent);
  box-shadow: 0 0 46px -22px var(--hl);
}

.crest-mine {
  position: absolute; top: 0; right: 0; z-index: 2;
  padding: 0.24rem 0.7rem; border-radius: 0 16px 0 12px;
  font-family: var(--mono); font-size: 0.5rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: #0a0d16;
  background: var(--hl);
}

/* the crest the Hat just shouted */
.crest.is-sorted {
  border-color: var(--hl, var(--h1));
  box-shadow: 0 0 0 1px var(--hl, var(--h1)), 0 26px 54px -26px #000,
              0 0 60px -14px var(--hl, var(--h1));
  animation: crest-chosen 1.1s var(--ease);
}

@keyframes crest-chosen {
  0%   { transform: none; }
  35%  { transform: translateY(-10px) scale(1.03); }
  100% { transform: none; }
}

/* ── castable spells ── */
#spells .fan-tile { cursor: pointer; }

#spells .fan-tile.is-cast {
  animation: spell-cast .7s var(--ease);
  border-color: var(--a);
  background: color-mix(in srgb, var(--a) 18%, transparent);
}

@keyframes spell-cast {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--a) 65%, transparent); }
  100% { box-shadow: 0 0 0 22px transparent; }
}

@keyframes spell-spark {
  from { transform: translate(0, 0) scale(1); opacity: 1; }
  to   { transform: translate(var(--dx), var(--dy)) scale(.2); opacity: 0; }
}

.fan-tile { position: relative; }

/* crest cards: Hogwarts houses, Westerosi houses, bending arts
   --h1  the house colour, used for structure: borders, glows, washes
   --h2  its secondary
   --hl  optional legible version of --h1, for anything set as TYPE. Some house
         colours (Ravenclaw navy, Gryffindor maroon, Hufflepuff's black) are far
         too dark to read at 0.6rem on this background, so type takes --hl where
         a page provides one and falls back to --h1/--h2 where it does not.
   tilt.js binds .crest, so these lift and catch a glare like the app cards. */
.crest-grid {
  display: grid; gap: clamp(0.9rem, 2vw, 1.4rem);
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  padding: 0 var(--gutter); max-width: var(--maxw); margin-bottom: 3.4rem;
}

.crest {
  position: relative; overflow: hidden; text-align: center; cursor: pointer;
  padding: 1.6rem 1.2rem 1.3rem; border-radius: 16px;
  border: 1px solid color-mix(in srgb, var(--h1) 42%, transparent);
  background:
    radial-gradient(125% 78% at 50% 0%, color-mix(in srgb, var(--h1) 20%, transparent), transparent 64%),
    linear-gradient(165deg, color-mix(in srgb, var(--h1) 12%, transparent), rgba(9,7,9,.72) 72%);
  transition: transform .45s var(--ease), box-shadow .45s, border-color .45s, background .45s;
}

/* Row alignment: sigil, name, the person under it, the blurb and the tag row
   each get a shared line across the row, so a longer blurb in one crest can't
   drop that crest's tags below its neighbours'. Every crest carries the same
   five parts (the "my house" badge is absolutely positioned, so it is not one
   of them and doesn't shift the rest). */
@supports (grid-template-rows: subgrid) {
  .crest-grid {
    --rowgap: clamp(0.9rem, 2vw, 1.4rem); row-gap: 0;
    margin-bottom: calc(3.4rem - var(--rowgap));   /* keeps its own 3.4rem tail */
  }
  .crest-grid > .crest {
    display: grid; grid-row: span 5; grid-template-rows: subgrid; row-gap: 0;
    align-content: start; margin-bottom: var(--rowgap);
  }
}

.crest:hover,
.crest:focus-visible {
  transform: translateY(-6px);
  border-color: color-mix(in srgb, var(--hl, var(--h1)) 80%, transparent);
  background:
    radial-gradient(125% 78% at 50% 0%, color-mix(in srgb, var(--h1) 34%, transparent), transparent 68%),
    linear-gradient(165deg, color-mix(in srgb, var(--h1) 18%, transparent), rgba(9,7,9,.68) 72%);
  box-shadow: 0 26px 50px -28px #000, 0 0 48px -20px var(--hl, var(--h1));
}

.crest:focus-visible { outline: 2px solid var(--hl, var(--h1)); outline-offset: 3px; }

.crest-sigil {
  width: 74px; height: 74px; margin: 0 auto 0.9rem; color: var(--hl, var(--h1));
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--h1) 40%, transparent));
  transition: transform .5s var(--ease), filter .5s;
}

.crest:hover .crest-sigil {
  transform: translateY(-2px) scale(1.07);
  filter: drop-shadow(0 0 20px color-mix(in srgb, var(--hl, var(--h1)) 65%, transparent));
}

.crest h4 {
  font-family: Georgia, serif; font-size: 1.25rem; letter-spacing: 0.01em;
  color: var(--hl, var(--h1)); margin-bottom: 0.15rem;
  text-shadow: 0 0 26px color-mix(in srgb, var(--h1) 45%, transparent);
}

.crest .crest-sub {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--hl, var(--h2)) 40%, var(--ink-dim));
}

.crest p {
  margin-top: 0.7rem; font-size: 0.84rem; line-height: 1.6;
  color: color-mix(in srgb, var(--ink) 76%, transparent);
  transition: color .4s;
}

.crest:hover p { color: var(--ink); }

.crest-tags {
  margin-top: 0.9rem; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.35rem;
}

.crest-tags span {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.2rem 0.55rem; border-radius: 100px;
  color: color-mix(in srgb, var(--hl, var(--h2)) 78%, var(--ink));
  border: 1px solid color-mix(in srgb, var(--hl, var(--h2)) 42%, transparent);
  background: color-mix(in srgb, var(--hl, var(--h2)) 9%, transparent);
  transition: border-color .4s, background .4s;
}

.crest:hover .crest-tags span {
  border-color: color-mix(in srgb, var(--hl, var(--h2)) 65%, transparent);
  background: color-mix(in srgb, var(--hl, var(--h2)) 16%, transparent);
}

/* ───────── Avengers, Stark red and gold ───────── */
body[data-fan="mcu"] { --a: #e0483a; }

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

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

@keyframes mcu-portal-breathe { from { opacity: .28; } to { opacity: .6; } }

@keyframes mcu-orbit-spin {
  from { transform: translate(-50%, -50%) rotateX(var(--tilt, 70deg)) rotate(0turn); }
  to   { transform: translate(-50%, -50%) rotateX(var(--tilt, 70deg)) rotate(1turn); }
}

@keyframes mcu-ember-drift { from { background-position: 0 0; } to { background-position: 60px -300px; } }

/* shared drawing primitives, used by every .fan-art */
.fan-art .art-line {
  fill: none; stroke: var(--a, var(--gold)); stroke-width: 2.4;
  stroke-linejoin: round; stroke-linecap: round;
  opacity: .85;
}

.fan-art .art-fill { fill: color-mix(in srgb, var(--a, #f5c63c) 12%, transparent); }

.fan-art .art-soft { fill: none; stroke: var(--a, var(--gold)); stroke-width: 1.4; opacity: .4; }

.fan-art .art-lit  { fill: var(--a, var(--gold)); opacity: .9; }

/* ── hero drawing block ──
   The house pattern, same shape as .jp-herohead on Jurassic Park: artwork on
   the left, the writing on the right. Every fan page gets one. Add a .fan-art
   block to the hero, drop the drawing in .fan-art-fig, put a quote and a
   paragraph in .fan-art-body, and style the drawing under .fan-art--<slug>. */
/* THE GEOMETRY IS SET HERE, not inherited. This block is a direct child of
   <main> on every page, but it is written as a <section> on half of them and a
   <div> on the other half, and only the <section> ones were picking up
   `padding: 0 var(--gutter); max-width: var(--maxw)` from layout.css. So the
   <div> pages had a hero drawing with no gutter and no cap: it started a full
   gutter left of the words above it and ran to the window edge, with the quote
   column drifting further right the wider the monitor got.

   It used to say `max-width: none`, which unset the cap on the <section> half
   as well. Stating both here makes the tag irrelevant: on a <section> these are
   the values it already had, on a <div> they are the ones it was missing. */
.fan-art {
  display: grid; gap: clamp(1.2rem, 4vw, 2.8rem); align-items: center;
  grid-template-columns: 1fr; margin: 2.4rem 0 0;
  max-width: var(--maxw); padding: 0 var(--gutter);
}

@media (min-width: 820px) {
  .fan-art { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
  /* the gauntlet is tall and narrow, so it gets a slimmer column than the
     saber or the castle, which are both wide */
  .fan-art--mcu { grid-template-columns: minmax(0, 0.62fr) minmax(0, 1.38fr); }
}

.fan-art-fig { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }

.fan-art-fig svg { width: 100%; height: auto; display: block; overflow: visible; }

.fan-art-body { display: flex; flex-direction: column; gap: 0.7rem; min-width: 0; max-width: 62ch; }

.fan-art-body q {
  quotes: "\201C" "\201D";
  font-family: var(--serif); font-style: italic; font-weight: 500;
  font-size: clamp(1.05rem, 2.3vw, 1.45rem); line-height: 1.4;
  letter-spacing: -0.015em; color: var(--ink); text-wrap: balance;
}

.fan-art-by {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: color-mix(in srgb, var(--a, #f5c63c) 62%, var(--mute));
}

.fan-art-body p {
  margin-top: 0.2rem; max-width: 54ch;
  font-size: 0.92rem; line-height: 1.75; color: var(--ink-dim);
}

.fan-art-body p + p { margin-top: 0.1rem; }

@keyframes sw-ignite { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@keyframes sw-hum { from { opacity: .88; } to { opacity: 1; } }

@keyframes hp-lumos { from { opacity: .55; } to { opacity: 1; } }

/* Avengers: the gauntlet plating, and six stones that actually light */
.fan-art--mcu .fan-art-fig { max-width: 300px; margin-inline: auto; }

@keyframes mcu-gemlight { from { opacity: .72; } to { opacity: 1; } }

.crest--stone .crest-sigil { filter: drop-shadow(0 0 14px color-mix(in srgb, var(--h1) 60%, transparent)); }

body[data-fan="mcu"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.06em;
  background: linear-gradient(180deg, #f6d365, #e0483a 70%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* the six stones */
.mcu-stones {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(0.8rem, 2vw, 1.4rem);
  padding: 0 var(--gutter); max-width: var(--maxw); margin-bottom: 3rem;
}

@media (max-width: 980px) { .mcu-stones { grid-template-columns: repeat(3, 1fr); } }

@media (max-width: 480px) { .mcu-stones { grid-template-columns: repeat(2, 1fr); } }

.mcu-stone { text-align: center; }

.mcu-gem {
  width: 62px; height: 62px; margin: 0 auto 0.7rem; border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fff, var(--s) 42%, color-mix(in srgb, var(--s) 40%, #000) 100%);
  box-shadow: 0 0 34px -6px var(--s), inset 0 0 14px -4px rgba(255,255,255,.6);
  animation: mcu-pulse 5s ease-in-out infinite;
}

@keyframes mcu-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.06); } }

.mcu-stone b { display: block; font-family: var(--serif); font-size: 0.98rem; color: var(--s); }

.mcu-stone i { display: block; font-style: normal; font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); margin-top: 0.2rem; }

.mcu-stone em { display: block; font-style: normal; font-size: 0.78rem; line-height: 1.5; color: var(--ink-dim); margin-top: 0.4rem; }

/* ───────── Minecraft, everything is a block ───────── */
body[data-fan="mc"] { --a: #7fbf4f; }

@keyframes mc-ore-glint { 0%, 100% { opacity: .38; } 50% { opacity: .72; } }

@keyframes mc-lava-glow { from { opacity: .55; } to { opacity: .95; } }

@keyframes mc-rise {
  from { transform: translateY(0) rotate(0deg); }
  to   { transform: translateY(-130vh) rotate(90deg); }
}

body[data-fan="mc"] .fan-title,
body[data-fan="mc"] .fan-card h4,
body[data-fan="mc"] .fan-rankhead b,
body[data-fan="mc"] .fan-tiletext b,
body[data-fan="mc"] .fan-what b {
  font-family: var(--mono); font-weight: 700; letter-spacing: 0;
  text-shadow: 3px 3px 0 rgba(0,0,0,.7);
}

body[data-fan="mc"] .fan-card,
body[data-fan="mc"] .fan-tile,
body[data-fan="mc"] .fan-rankrow {
  border-radius: 0; border-width: 2px;
  border-color: rgba(255,255,255,.08);
  box-shadow: inset -3px -3px 0 rgba(0,0,0,.35), inset 3px 3px 0 rgba(255,255,255,.05);
}

body[data-fan="mc"] .fan-card:hover,
body[data-fan="mc"] .fan-tile:hover { transform: translateY(-4px); }

body[data-fan="mc"] .fan-swatch { border-radius: 0; box-shadow: inset -2px -2px 0 rgba(0,0,0,.4); }

/* Minecraft: an isometric grass block and a diamond pickaxe */
.fan-art--mc .fan-art-fig { max-width: 330px; }

@keyframes mc-swing {
  0%   { transform: rotate(-38deg); }
  45%  { transform: rotate(26deg); }
  100% { transform: rotate(0deg); }
}

@keyframes mc-swish {
  0%   { opacity: 0; }
  35%  { opacity: .85; }
  100% { opacity: 0; }
}

@keyframes mc-motes { from { opacity: .25; } to { opacity: .8; } }

@keyframes mc-ender-bob { from { transform: translateY(0); } to { transform: translateY(-6px); } }

.mc-slot.is-sel { border-color: #fff; box-shadow: 0 0 0 2px rgba(255,255,255,.25); }

/* ───────── Pirates: charts, rope and lantern light ───────── */
body[data-fan="potc"] { --a: #d8b06a; }

@keyframes potc-turn { to { transform: rotate(360deg); } }

@keyframes potc-roll { from { transform: translateX(0); } to { transform: translateX(-130px); } }

@keyframes potc-glint { from { opacity: .4; } to { opacity: .85; } }

@keyframes potc-sail {
  from { transform: translate(0, 0) rotate(-.8deg); }
  to   { transform: translate(-26px, 5px) rotate(.8deg); }
}

@keyframes potc-fogdrift { from { transform: translateX(-3%); } to { transform: translateX(3%); } }

@keyframes potc-needle {
  0%   { transform: rotate(0deg); }
  14%  { transform: rotate(128deg); }
  26%  { transform: rotate(96deg); }
  44%  { transform: rotate(258deg); }
  58%  { transform: rotate(212deg); }
  76%  { transform: rotate(340deg); }
  88%  { transform: rotate(300deg); }
  100% { transform: rotate(360deg); }
}

/* the Black Pearl in the hero */
.fan-art--potc .fan-art-fig { max-width: 520px; }

@keyframes potc-lantern { from { opacity: .55; } to { opacity: 1; } }

@keyframes potc-heave {
  from { transform: translateY(0) rotate(-.7deg); }
  to   { transform: translateY(-7px) rotate(.7deg); }
}

body[data-fan="potc"] .fan-title { font-family: Georgia, serif; font-variant: small-caps; letter-spacing: 0.02em; }

body[data-fan="potc"] .fan-card h4,
body[data-fan="potc"] .fan-rankhead b { font-family: Georgia, serif; font-variant: small-caps; letter-spacing: 0.04em; }

body[data-fan="potc"] .fan-card { background: linear-gradient(165deg, rgba(216,176,106,.05), rgba(255,255,255,.01) 60%); }

/* ───────── Jurassic Park: amber, and a warning sign ───────── */
body[data-fan="jp"] { --a: #e0642a; }

@keyframes jp-storm-drift { from { transform: translateX(-4%); } to { transform: translateX(4%); } }

@keyframes jp-lightning {
  0%, 86%  { opacity: 0; }
  87%      { opacity: .5; }
  88%      { opacity: 0; }
  90%      { opacity: .32; }
  91%, 100%{ opacity: 0; }
}

@keyframes jp-fence-buzz {
  0%, 68%, 100% { opacity: 1; }
  70%           { opacity: .45; }
  72%           { opacity: 1; }
  86%           { opacity: .6; }
  88%           { opacity: 1; }
}

@keyframes jp-rainfall { from { background-position: 0 0, 0 0; } to { background-position: 90px 380px, 130px 460px; } }

@keyframes jp-sway { from { transform: rotate(-1.6deg); } to { transform: rotate(1.6deg); } }

/* the gates in the hero */
.fan-art--jp .fan-art-fig { max-width: 540px; }

@keyframes jp-open-l { from { transform: scaleX(1); } to { transform: scaleX(.72); } }

@keyframes jp-open-r { from { transform: scaleX(1); } to { transform: scaleX(.72); } }

@keyframes jp-flicker {
  from { transform: scaleY(.9) scaleX(1.05); opacity: .8; }
  to   { transform: scaleY(1.12) scaleX(.95); opacity: 1; }
}

body[data-fan="jp"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.01em;
  -webkit-text-stroke: 1px rgba(0,0,0,.6);
}

body[data-fan="jp"] .fan-card { background: linear-gradient(170deg, rgba(224,100,42,.06), rgba(255,255,255,.01) 62%); }

.jp-amber {                         /* the mosquito in amber */
  width: 120px; height: 150px; flex: none;
}

.jp-herohead { display: flex; align-items: center; gap: clamp(1rem, 4vw, 3rem); flex-wrap: wrap; }

/* ───────── shared reduced-motion ───────── */
@media (prefers-reduced-motion: reduce) {
  .hp-sky span, .hp-stars, .hp-castle .hp-lit,
  .hat, .hat .hat-mouth, .crest.is-sorted,
  .mc-ores i, .mc-lava, .mc-voxels i, .mc-ender, .mce-motes, .mc-sword-arm, .mc-swish,
  .mcu-gem, .mcu-sky i, .mcu-portal, .mcu-portal::before, .mcu-embers, .mcu-orbit,
  .wl-halo, .wl-core, .wl-spark circle,
  .fan-art--mcu .gem, .fan-art--saber .saber-blade-art, .potc-rose, .potc-swell, .potc-glint,
  .potc-far, .potc-fog, .pearl-lantern circle, .fan-art--potc svg,
  .jp-storm, .jp-bolt, .jp-fence, .jp-rain, .jp-fern, .jp-gate, .jp-flame,
  .off-panels i:nth-child(7), .ofs-screen, .boys-scan, .boys-beams i, .bys-v, .byh-v, .jl-aurora, .jls-hubring, .jls-node circle, .jlh-cell, .jlh-flash, .jlb-star, .fn-bus, .fn-storm, .fnh-omega, .ijr-path, .ijr-plane, .ij-torch i, .ij-dust, .fnr-eye circle, .fnaf-hall, .fnf-pupil, .kn-sky::after, .knm-glow, .pk-motes, .lb-fire path, .fan-art--lotr .lotr-doors svg > *, .pjw-1, .pjw-2, .pjw-3, .pj-bolts i, .pjh-drop, .dz-fw i, .unl-lamp circle, .uni-globefig .ung-grid, .uni-beam, .spdr-swing .sps-rig, .wsh-web, .spdr-tendrils, .bat-signal, .bat-rain, .btr, .nj-bolt, .nj-motes, .njs-flame, .njs-spark circle, .njg-aura,
  .rdr-sun, .rdr-dust, .simt-glow circle, .sim-clouds, .dnt, .pkg-blades, .pkm-sparks, .pkb, .pkb-burst,
  .pc-needle, .got-snow, .atc-ring, .atc-yy, .atla-air, .atla-water, .atla-embers,
  .atk-orbit, .atla-rocks i, .atla-comet i { animation: none !important; }
}

/* ───────── Stranger Things, neon red over the Upside Down ───────── */
body[data-fan="st"] { --a: #e8261d; }

@keyframes st-torch { from { transform: translateX(-32%); } to { transform: translateX(32%); } }

@keyframes st-rot { from { background-position: 0 0; } to { background-position: 260px 0; } }

@keyframes st-fair { 0%, 100% { opacity: .5; } 33% { opacity: 1; } 66% { opacity: .7; } }

@keyframes st-neon { from { filter: saturate(1); } to { filter: saturate(1.35) brightness(1.12); } }

@keyframes st-vines { from { background-position: 0 0, 0 0; } to { background-position: 0 300px, 0 -300px; } }

@keyframes st-rift { from { opacity: .5; transform: scaleX(.94); } to { opacity: 1; transform: scaleX(1.06); } }

@keyframes st-flayer { from { opacity: .55; transform: translateY(8px); } to { opacity: 1; transform: translateY(-8px); } }

@keyframes st-firework {
  0%   { transform: scale(.2); opacity: 0; }
  12%  { opacity: 1; }
  60%  { transform: scale(2.4); opacity: 0; }
  100% { transform: scale(2.4); opacity: 0; }
}

@keyframes st-clock { 0%, 49% { opacity: .5; } 50%, 100% { opacity: 1; } }

@keyframes st-riftline { from { opacity: .35; } to { opacity: .9; } }

@keyframes st-buzz {
  0%, 88%, 100% { opacity: 1; }
  90%           { opacity: .35; }
  92%           { opacity: 1; }
  95%           { opacity: .55; }
  96%           { opacity: 1; }
}

.st-hud.is-on { opacity: 1; transform: none; }

/* the wall: bulbs are lit by JS now, not by a fixed three-letter loop */
.st-letter.is-on .st-bulb {
  background: var(--b, #ff3a2a);
  box-shadow: 0 0 18px 6px color-mix(in srgb, var(--b, #ff3a2a) 75%, transparent),
              0 0 46px 14px color-mix(in srgb, var(--b, #ff3a2a) 35%, transparent);
}

.st-letter.is-on b {
  color: #fff2ee;
  text-shadow: 0 0 14px color-mix(in srgb, var(--b, #ff3a2a) 90%, transparent);
}

/* the season heading rule picks up the season's own colour */
.fan-sec[data-season] .subsec--fan { color: var(--sc, var(--a)); }

.fan-sec[data-season="1"] { --sc: #e8261d; }

.fan-sec[data-season="2"] { --sc: #e0702a; }

.fan-sec[data-season="3"] { --sc: #ff5fb0; }

.fan-sec[data-season="4"] { --sc: #ff3b30; }

.fan-sec[data-season="5"] { --sc: #f0a020; }

.fan-sec[data-season] .fan-card:hover,
.fan-sec[data-season] .fan-tile:hover { border-color: color-mix(in srgb, var(--sc) 55%, transparent); }

.fan-sec[data-season] .fan-swatch { background: var(--sc); box-shadow: 0 0 12px -1px var(--sc); }

.fan-sec[data-season] .fan-sub,
.fan-sec[data-season] .fan-tag,
.fan-sec[data-season] .fan-num { color: var(--sc); }

@keyframes st-drift { to { transform: translate3d(0, -260px, 0); } }

body[data-fan="st"] .fan-title {
  font-family: Georgia, serif; text-transform: uppercase; letter-spacing: 0.04em;
  color: #e8261d;
  text-shadow: 0 0 8px rgba(232,38,29,.9), 0 0 30px rgba(232,38,29,.6), 0 0 70px rgba(232,38,29,.35);
}

@keyframes st-blink { 0%,100% { opacity: 1; } 45% { opacity: .28; } }

/* ───────── Game of Thrones, cold steel ───────── */
body[data-fan="got"] { --a: #b9c2cc; }

@keyframes got-fall {
  from { background-position: 0 -420px; }
  to   { background-position: 90px 420px; }
}

body[data-fan="got"] .fan-title {
  font-family: Copperplate, "Copperplate Gothic Light", Georgia, serif;
  text-transform: uppercase; letter-spacing: 0.1em; font-size: clamp(2rem, 6vw, 4.4rem);
  background: linear-gradient(180deg, #f2f6fa, #7f8894 60%, #dfe7ee);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

body[data-fan="got"] .crest-sigil { width: 66px; height: 66px; }

/* ───────── Avatar: TLA, four elements ───────── */
body[data-fan="atla"] { --a: #e8a13f; }

@keyframes atla-turn { to { transform: rotate(360deg); } }

@keyframes atla-koi { to { transform: rotate(360deg); } }

@keyframes atla-float {
  from { transform: translateY(0) rotate(-6deg); }
  to   { transform: translateY(-26px) rotate(6deg); }
}

@keyframes atla-sozin {
  0%, 84%   { transform: translate(0, 0) rotate(18deg); opacity: 0; }
  86%       { opacity: .9; }
  97%       { opacity: .9; }
  100%      { transform: translate(190%, 62%) rotate(18deg); opacity: 0; }
}

@keyframes atla-drift { from { background-position: 0 0, 0 0; } to { background-position: 300px 0, -300px 0; } }

@keyframes atla-swell { from { transform: translateY(-14px) scaleX(.96); } to { transform: translateY(14px) scaleX(1.04); } }

@keyframes atla-rise { from { background-position: 0 0; } to { background-position: 60px -300px; } }

body[data-fan="atla"] .fan-title {
  font-family: Papyrus, "Bradley Hand", Georgia, fantasy; letter-spacing: 0.02em;
  font-size: clamp(2rem, 6vw, 4.6rem);
}

body[data-fan="atla"] .crest { border-radius: 50% 50% 16px 16px; }

/* ───────── Red Dead, sepia and dust ───────── */
body[data-fan="rdr"] { --a: #c9402f; }

@keyframes rdr-bake { from { opacity: .42; transform: scale(.97); } to { opacity: .68; transform: scale(1.03); } }

@keyframes rdr-blow { from { transform: translateX(-4%); } to { transform: translateX(4%); } }

/* the revolver and hat in the hero */
.fan-art--rdr .fan-art-fig { max-width: 560px; }

body[data-fan="rdr"] .fan-title {
  font-family: "American Typewriter", "Courier New", var(--mono); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; font-size: clamp(2rem, 6vw, 4.6rem);
}

/* ───────── Ninjago, gold and elemental ───────── */
body[data-fan="ninjago"] { --a: #e0b040; }

body[data-fan="ninjago"] .fan-title {
  font-style: italic; text-transform: uppercase; letter-spacing: 0.04em; transform: skewX(-7deg);
  background: linear-gradient(180deg, #ffe9a8, #e0b040 58%, #a87a20);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ───────── The Simpsons, Springfield sky ───────── */
body[data-fan="simpsons"] { --a: #ffd21f; }

@keyframes sim-hum { from { opacity: .25; } to { opacity: .6; } }

@keyframes sim-drift { from { transform: translateX(-3%); } to { transform: translateX(3%); } }

/* the donut in the hero */
.fan-art--sim .fan-art-fig { max-width: 330px; margin-inline: auto; }

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

body[data-fan="simpsons"] .fan-title {
  font-family: "Arial Rounded MT Bold", "Trebuchet MS", var(--sans); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.01em; text-shadow: 5px 5px 0 #1a3a80;
}

/* ───────── The Office: beige carpet, blue trim ───────── */
body[data-fan="office"] { --a: #6f9fd0; }

@keyframes off-flick {
  0%, 84%, 100% { opacity: 1; }
  86% { opacity: .25; } 87% { opacity: 1; }
  90% { opacity: .3; }  91% { opacity: 1; } 93% { opacity: .5; } 94% { opacity: 1; }
}

@keyframes off-screen { from { opacity: .35; } to { opacity: .62; } }

body[data-fan="office"] .fan-title { font-weight: 600; letter-spacing: 0.01em; }

body[data-fan="office"] .fan-title::after { content: "."; color: var(--a); }

/* ───────── The Boys, Vought red ───────── */
body[data-fan="boys"] { --a: #e02a2a; }

@keyframes bys-pulse { 0%, 100% { opacity: .72; } 50% { opacity: 1; } }

@keyframes bys-scan {
  0%, 8%    { transform: translateX(-100%) scaleX(.4); opacity: 0; }
  14%       { opacity: 1; }
  46%       { transform: translateX(100%) scaleX(.4); opacity: 1; }
  54%, 100% { transform: translateX(100%) scaleX(.4); opacity: 0; }
}

@keyframes bys-sweep { from { transform: rotate(-9deg); } to { transform: rotate(9deg); } }

@keyframes byh-pulse { 0%, 100% { opacity: .82; } 50% { opacity: 1; } }

body[data-fan="boys"] .fan-title {
  font-family: Impact, Haettenschweiler, "Arial Narrow", sans-serif;
  text-transform: uppercase; letter-spacing: 0.02em; transform: skewX(-5deg); color: #e02a2a;
}

/* ───────── Batman, a signal in the cloud ───────── */
body[data-fan="batman"] { --a: #c9cdd2; }

@keyframes bat-flicker { from { opacity: .07; } to { opacity: .16; } }

@keyframes bat-fall { from { background-position: 0 0, 0 0; } to { background-position: 80px 360px, 120px 440px; } }

/* the batarang */
.fan-art--bat .fan-art-fig { max-width: 420px; }

@keyframes bat-spin {
  0%, 55%  { transform: rotate(0deg); }
  85%      { transform: rotate(1080deg); }
  100%     { transform: rotate(1080deg); }
}

body[data-fan="batman"] .fan-title {
  font-family: Impact, Haettenschweiler, "Arial Narrow", sans-serif;
  text-transform: uppercase; letter-spacing: 0.16em;
  background: linear-gradient(180deg, #eef2f6, #6f7884);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ───────── Spider-Man, a web across the corner ───────── */
body[data-fan="spidey"] { --a: #e02a3a; }

@keyframes spd-creep { from { transform: scale(1); } to { transform: scale(1.08); } }

body.is-symbiote .fan-sec[data-symbiote] .fan-card {
  background: linear-gradient(168deg, rgba(232,232,240,.07), rgba(4,4,6,.85) 70%);
  border-color: rgba(232,232,240,.28);
}

body.is-symbiote .fan-sec[data-symbiote] .fan-card:hover {
  border-color: rgba(232,232,240,.75);
  box-shadow: 0 24px 50px -28px #000, 0 0 40px -18px rgba(232,232,240,.55);
}

body.is-symbiote .fan-sec[data-symbiote] .fan-card h4,
body.is-symbiote .fan-sec[data-symbiote] .subsec--fan { color: #f2f2f8; }

body.is-symbiote .fan-sec[data-symbiote] .fan-tag {
  color: #0a0a0d; background: #e8e8f0; border-color: #e8e8f0;
}

.fan-sec[data-symbiote] .fan-card,
.fan-sec[data-symbiote] .subsec--fan,
.fan-sec[data-symbiote] .fan-card h4 { transition: color .8s ease, background .8s ease, border-color .8s ease; }

/* the section title gets teeth while the suit is on */
body.is-symbiote .fan-sec[data-symbiote] .subsec--fan {
  letter-spacing: 0.06em;
  text-shadow: 0 0 22px rgba(232,232,240,.45);
}

@keyframes spd-swing {
  0%   { transform: rotate(-34deg); opacity: 0; }
  8%   { opacity: 1; }
  50%  { transform: rotate(30deg); opacity: 1; }
  70%  { transform: rotate(44deg); opacity: 0; }
  71%  { transform: rotate(-34deg); opacity: 0; }
  100% { transform: rotate(-34deg); opacity: 0; }
}

/* the web-shooter */
.fan-art--spd .fan-art-fig { max-width: 440px; }

@keyframes spd-fire {
  0%, 12%  { stroke-dashoffset: 340; opacity: 0; }
  22%      { opacity: .95; }
  46%      { stroke-dashoffset: 0; opacity: .95; }
  76%      { stroke-dashoffset: 0; opacity: 0; }
  100%     { stroke-dashoffset: 340; opacity: 0; }
}

body[data-fan="spidey"] .fan-title {
  font-style: italic; text-transform: uppercase; letter-spacing: 0.02em;
  color: #e02a3a; -webkit-text-stroke: 1.5px #1a3a8a; paint-order: stroke fill;
}

/* ───────── Pokémon, a poké ball in the corner ───────── */
body[data-fan="pokemon"] { --a: #ffd43f; }

@keyframes pkm-sway { from { transform: skewX(-1.4deg); } to { transform: skewX(1.4deg); } }

@keyframes pkm-rise { from { background-position: 0 0; } to { background-position: 40px -300px; } }

/* the Poké Ball in the hero */
.fan-art--pkm .fan-art-fig { max-width: 340px; margin-inline: auto; }

@keyframes pkm-wobble {
  0%, 62%, 100% { transform: rotate(0deg); }
  68% { transform: rotate(-7deg); }
  74% { transform: rotate(6deg); }
  80% { transform: rotate(-4deg); }
  86% { transform: rotate(0deg); }
}

@keyframes pkm-open {
  0%, 60% { opacity: 0; transform: scale(.2); }
  66%     { opacity: .9; transform: scale(.6); }
  78%     { opacity: 0;  transform: scale(1.15); }
  100%    { opacity: 0;  transform: scale(1.15); }
}

body[data-fan="pokemon"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.03em; transform: skewX(-6deg);
  color: #ffd43f; -webkit-text-stroke: 2px #2b4b9b; paint-order: stroke fill;
}

/* ───────── Five Nights at Freddy’s, a camera feed at 3 AM ───────── */
body[data-fan="fnaf"] { --a: #c98f4f; }

@keyframes fnaf-jitter { 0%,92%,100% { opacity: .5; } 93% { opacity: .95; } 96% { opacity: .3; } }

@keyframes fnaf-stare {
  0%, 62%, 100% { opacity: .9; }
  64%, 67%      { opacity: .06; }
  69%           { opacity: .9; }
  86%, 88%      { opacity: .06; }
}

@keyframes fnaf-spill { 0%, 100% { opacity: .75; } 47% { opacity: 1; } 49% { opacity: .3; } 51% { opacity: 1; } }

@keyframes fnf-look { 0%, 72%, 100% { transform: scale(1); } 74%, 80% { transform: scale(.45); } }

body[data-fan="fnaf"] .fan-title {
  font-family: var(--mono); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  font-size: clamp(1.8rem, 5.4vw, 4rem);
  animation: fr-flicker 6s steps(1, end) infinite;
}

/* ───────── Fortnite, storm front ───────── */
body[data-fan="fortnite"] { --a: #6fb0ff; }

@keyframes fn-close { to { transform: scale(.72); } }

@keyframes fn-roll { from { transform: translate3d(-2%, 0, 0); } to { transform: translate3d(2%, 0, 0); } }

@keyframes fn-fly {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(100vw + 16vw), 5vh, 0); }
}

@keyframes fnh-glow { 0%, 100% { opacity: .84; } 50% { opacity: 1; } }

body[data-fan="fortnite"] .fan-title {
  font-family: Futura, "Century Gothic", "Trebuchet MS", var(--sans); font-weight: 700;
  font-style: italic; text-transform: uppercase; letter-spacing: 0.06em;
  background: linear-gradient(120deg, #8fd0ff, #6f7fff 55%, #b06fff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ───────── Indiana Jones, a dotted line across the map ───────── */
body[data-fan="indiana"] { --a: #d8b06a; }

@keyframes ij-haze { from { transform: translate3d(-1.5%, 0, 0); } to { transform: translate3d(1.5%, 0, 0); } }

@keyframes ij-travel { to { stroke-dashoffset: -104; } }

@keyframes ij-fly { from { offset-distance: 0%; } to { offset-distance: 100%; } }

@keyframes ij-flicker { 0%, 100% { opacity: .55; transform: scale(1); } 45% { opacity: 1; transform: scale(1.18); } }

body[data-fan="indiana"] .fan-title { font-family: Georgia, serif; font-variant: small-caps; letter-spacing: 0.06em; }

/* ───────── Parks and Rec, Pawnee parks-sign green ───────── */
body[data-fan="parks"] { --a: #5fbf7f; }

@keyframes pk-float { to { background-position: -280px -180px; } }

body[data-fan="parks"] .fan-title { font-family: "Trebuchet MS", var(--sans); font-weight: 700; letter-spacing: -0.01em; }

body[data-fan="parks"] .fan-title::after {
  content: ""; display: block; width: 120px; height: 5px; margin-top: 0.7rem;
  background: var(--a); border-radius: 3px;
}

/* ───────── The Lord of the Rings, ring inscription ───────── */
body[data-fan="lotr"] { --a: #d9b45f; }

@keyframes lotr-glow { from { opacity: .55; } to { opacity: 1; } }

@keyframes lotr-beacon {
  0%            { opacity: 0; transform: scaleY(.5); }
  6%            { opacity: 1; transform: scaleY(1.12); }
  10%           { transform: scaleY(.96); }
  14%           { transform: scaleY(1.06); }
  56%           { opacity: 1; transform: scaleY(1); }
  70%, 100%     { opacity: 0; transform: scaleY(.5); }
}

@keyframes ld-shine { 0%, 100% { opacity: .72; } 50% { opacity: 1; } }

body[data-fan="lotr"] .fan-title {
  font-family: Copperplate, "Copperplate Gothic Light", Georgia, serif;
  text-transform: uppercase; letter-spacing: 0.14em; font-size: clamp(1.8rem, 5vw, 3.8rem);
}

/* ───────── Breaking Bad, desert green ───────── */
body[data-fan="bb"] { --a: #4fbf7f; }

body[data-fan="bb"] .fan-title {
  font-weight: 500; text-transform: lowercase; letter-spacing: 0;
}

/* ───────── Kung Fu Panda, ink and cinnabar ───────── */
body[data-fan="kfp"] { --a: #e0703a; }

@keyframes kfp-coil { from { transform: translate3d(-1.5%, 1%, 0); } to { transform: translate3d(1.5%, -1%, 0); } }

@keyframes kfp-sway { from { transform: skewX(-.5deg); } to { transform: skewX(.5deg); } }

@keyframes kfp-fall {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: .85; }
  50%  { transform: translate3d(-40px, 52vh, 0) rotate(220deg); opacity: .85; }
  92%  { opacity: .5; }
  100% { transform: translate3d(34px, 108vh, 0) rotate(460deg); opacity: 0; }
}

@keyframes kfs-bloom { from { opacity: .7; } to { opacity: 1; } }

@keyframes kfs-shine { 0%, 100% { opacity: 0; } 50% { opacity: .5; } }

body[data-fan="kfp"] .fan-title {
  font-family: "Bradley Hand", "Brush Script MT", Georgia, cursive; font-weight: 700;
  letter-spacing: 0.01em;
}

/* ───────── Avatar (Pandora), bioluminescence ───────── */
body[data-fan="avatar"] { --a: #5fd6e0; }

@keyframes av-rise { to { transform: translate3d(0, -300px, 0); } }

body[data-fan="avatar"] .fan-title {
  font-family: Papyrus, "Bradley Hand", Georgia, fantasy;
  text-transform: uppercase; letter-spacing: 0.2em;
  text-shadow: 0 0 30px rgba(95,214,224,.5);
}

/* ───────── Invincible: comic yellow, and one red splash ───────── */
body[data-fan="invincible"] { --a: #f0d040; }

@keyframes inv-roll { from { transform: translate3d(-2%, 0, 0); } to { transform: translate3d(2%, 0, 0); } }

@keyframes inv-streak {
  0%        { stroke-dashoffset: 1500; opacity: 0; }
  10%       { opacity: .34; }
  58%       { stroke-dashoffset: 0; opacity: .34; }
  76%, 100% { stroke-dashoffset: 0; opacity: 0; }
}

@keyframes inv-fly {
  0%        { offset-distance: 0%; opacity: 0; }
  10%       { opacity: .6; }
  58%       { offset-distance: 100%; opacity: .6; }
  60%, 100% { offset-distance: 100%; opacity: 0; }
}

body[data-fan="invincible"] .fan-title {
  font-family: Impact, Haettenschweiler, "Arial Narrow", sans-serif;
  text-transform: uppercase; letter-spacing: 0.02em;
  color: #f0d040; -webkit-text-stroke: 2px #2a3a7a; paint-order: stroke fill;
}

/* ───────── Percy Jackson, sea and bronze ───────── */
body[data-fan="percy"] { --a: #5fa8e0; }

@keyframes pj-swell { from { transform: translateX(-2.5%); } to { transform: translateX(2.5%); } }

@keyframes pj-strike {
  0%, 88%, 100% { opacity: 0; }
  89%           { opacity: .9; }
  90%           { opacity: 0; }
  92%           { opacity: .7; }
  93%           { opacity: 0; }
}

@keyframes pj-flick { 0%, 100% { transform: scaleY(1) skewX(-3deg); } 50% { transform: scaleY(1.22) skewX(4deg); } }

.pj-sigil.is-on {
  animation: pj-burn 1.1s cubic-bezier(.2,.8,.3,1) forwards;
  filter: drop-shadow(0 0 14px var(--g, #f0d88a));
}

@keyframes pj-burn {
  0%   { opacity: 0; scale: .4; }
  55%  { opacity: 1; scale: 1.18; }
  100% { opacity: 1; scale: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .pj-fire i { animation: none; }
  .pj-sigil.is-on { animation: none; opacity: 1; scale: 1; }
}

@keyframes pj-bob { from { transform: translateY(-4px); } to { transform: translateY(4px); } }

body[data-fan="percy"] .fan-title { font-family: Georgia, serif; font-style: italic; letter-spacing: -0.01em; }

/* ───────── Dune: sand, and two suns ───────── */
body[data-fan="dune"] { --a: #e0a050; }

@keyframes dn-sign {
  0%        { transform: translateX(-14%); opacity: 0; }
  12%, 74%  { opacity: .5; }
  100%      { transform: translateX(70%); opacity: 0; }
}

@keyframes dn-blow { to { background-position: 620px 0, 360px 0; } }

@keyframes dn-glint { from { opacity: .45; } to { opacity: 1; } }

@keyframes dn-flap { from { transform: scaleY(.82); } to { transform: scaleY(1.12); } }

@keyframes dn-cross {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(100vw + 14vw), 7vh, 0); }
}

body[data-fan="dune"] .fan-title {
  font-weight: 300; text-transform: uppercase; letter-spacing: 0.34em;
  font-size: clamp(1.6rem, 5vw, 3.6rem); text-indent: 0.34em;
}

/* ───────── LEGO, a baseplate of studs ───────── */
body[data-fan="lego"] { --a: #ffd21f; }

@keyframes lg-shift { to { background-position: 46px 46px; } }

.lg-studbtn.is-on { border-color: var(--c); background: color-mix(in srgb, var(--c) 16%, transparent); }

/* Three silhouettes, shared by the buttons and the falling pieces: a stud is a
   squashed dome, a brick is a rounded slab, a minikit is a glowing block. */
.lg-shape--stud  { border-radius: 50% / 34%; }

.lg-shape--brick { border-radius: 3px; }

.lg-shape--kit {
  border-radius: 3px;
  box-shadow: inset 0 -5px 0 -2px rgba(0,0,0,.2), 0 0 12px var(--c), 0 2px 6px rgba(0,0,0,.4);
  opacity: .92;
}

.lg-studbtn.is-on .lg-studbtn-v { color: var(--c); }

.lg-bit.lg-shape--kit { box-shadow: inset 0 -4px 0 -2px rgba(0,0,0,.2), 0 0 10px var(--c); }

@keyframes lg-throw {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(.6); opacity: 0; }
  12%  { opacity: 1; }
  55%  { transform: translate3d(calc(var(--dx) * .6), calc(var(--lift) * -1), 0)
                     rotate(calc(var(--spin) * .6)) scale(1); opacity: 1; }
  100% { transform: translate3d(var(--dx), 40px, 0) rotate(var(--spin)) scale(.8); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .lg-bit.lg-shape--kit { box-shadow: inset 0 -4px 0 -2px rgba(0,0,0,.2), 0 0 10px var(--c); }
.lg-bit.is-up { animation: none; opacity: .8; }
  .lg-studfield { animation: none; }
  .lg-studbtn:hover { transform: none; }
}

body[data-fan="lego"] .fan-title {
  font-family: "Arial Rounded MT Bold", "Trebuchet MS", var(--sans); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em;
  text-shadow: 4px 4px 0 #d01012, 8px 8px 0 rgba(0,0,0,.45);
}

body[data-fan="lego"] .fan-card,
body[data-fan="lego"] .fan-tile { border-radius: 6px; }

/* ───────── Disney, night sky over a castle ───────── */
body[data-fan="disney"] { --a: #9fd0ff; }

@keyframes dz-bloom { 0%,100% { opacity: .35; transform: translateX(-50%) scale(.94); } 50% { opacity: .9; transform: translateX(-50%) scale(1.04); } }

body[data-fan="disney"] .fan-title {
  font-family: "Snell Roundhand", "Brush Script MT", "Bradley Hand", cursive;
  font-style: italic; letter-spacing: 0.01em;
  text-shadow: 0 0 44px rgba(159,208,255,.45);
}

/* ───────── Universal: the globe, turning ───────── */
body[data-fan="universal"] { --a: #8fb8e8; }

.dz-sky > *, .uni-sky > span { position: absolute; inset: 0; display: block; }

@keyframes dz-burst {
  0%   { transform: scale(.2); opacity: 0; }
  10%  { opacity: .85; }
  55%  { transform: scale(2.3); opacity: 0; }
  100% { transform: scale(2.3); opacity: 0; }
}

@keyframes uni-arc { from { opacity: .45; } to { opacity: 1; } }

@keyframes uni-sweep { from { transform: rotate(-2deg); } to { transform: rotate(2deg); } }

@keyframes uni-turn { to { transform: rotate(360deg); } }

body[data-fan="universal"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.16em; font-weight: 600;
  font-size: clamp(1.6rem, 4.6vw, 3.4rem);
  background: linear-gradient(180deg, #eaf2ff, #7f9ec4 62%, #dce8f8);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

@media (prefers-reduced-motion: reduce) { .uni-sky i, .dz-sky::before { animation: none; } }

/* ═════════════ Game of Thrones: Westeros, and the Wall ═════════════ */
.got-map {
  padding: 0 var(--gutter); max-width: var(--maxw); margin: 0 0 3.4rem;
  display: grid; gap: clamp(1.2rem, 3vw, 2.4rem); align-items: center;
  grid-template-columns: 1fr;
}

@media (min-width: 860px) { .got-map { grid-template-columns: 0.85fr 1.15fr; } }

.got-legend { display: flex; flex-direction: column; gap: 0.8rem; }

.got-legend h4 {
  font-family: Copperplate, Georgia, serif; text-transform: uppercase;
  letter-spacing: 0.14em; font-size: 1.1rem; color: #dfe7ee;
}

.got-legend p { font-size: 0.92rem; line-height: 1.7; color: var(--ink-dim); max-width: 56ch; }

/* the oath, carved into the Wall */
.got-oath {
  position: relative; margin-top: 1rem; padding: 1.4rem 1.5rem;
  border-left: 3px solid rgba(160,205,235,.6); border-radius: 0 8px 8px 0;
  background: linear-gradient(90deg, rgba(140,190,230,.10), transparent 76%);
  overflow: hidden;
}

.got-oath::before {                       /* frost */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(102deg, rgba(200,230,255,.05) 0 2px, transparent 2px 14px);
}

.got-oath p { font-family: Georgia, serif; font-size: 1.02rem; line-height: 1.75; color: #dceaf6; }

.got-oath span {
  display: block; margin-top: 0.7rem; font-family: var(--mono);
  font-size: 0.6rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--mute);
}

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

@keyframes rdr-honour { from { left: 26%; } to { left: 82%; } }

@keyframes nj-strike {
  0%, 88%  { opacity: 0; }
  89%      { opacity: .3; }
  90%      { opacity: 0; }
  92%      { opacity: .16; }
  93%, 100%{ opacity: 0; }
}

@keyframes nj-drift { from { background-position: 0 0; } to { background-position: 50px -320px; } }

/* the Sword of Fire in the hero */
.fan-art--nj .fan-art-fig { max-width: 250px; margin-inline: auto; }

@keyframes nj-burn {
  from { transform: scaleY(.94) scaleX(1.05); opacity: .75; }
  to   { transform: scaleY(1.08) scaleX(.95); opacity: 1; }
}

@keyframes nj-flick { from { opacity: .25; } to { opacity: .9; } }

@keyframes nj-pulse { from { opacity: .08; } to { opacity: .2; } }

@keyframes nj-whirl { to { transform: rotate(360deg); } }

@keyframes sim-write { 0% { width: 4%; } 70%, 100% { width: 62%; } }

@keyframes jl-pow {
  0%, 70%, 100% { transform: scale(.9) rotate(0deg); opacity: .16; }
  78%           { transform: scale(1.12) rotate(6deg); opacity: .5; }
  86%           { transform: scale(1) rotate(2deg); opacity: .3; }
}

/* Cards as comic panels: hard black rule, hard offset shadow, no blur --
   print has no soft shadows. */
body[data-fan="jl"] .fan-card,
body[data-fan="jl"] .fan-tile {
  border: 2.5px solid #05070d;
  border-radius: 3px;
  box-shadow: 5px 5px 0 #05070d, 0 0 0 1px rgba(120,160,220,.22);
  transition: transform .16s ease, box-shadow .16s ease;
}

body[data-fan="jl"] .fan-card:hover,
body[data-fan="jl"] .fan-tile:hover {
  transform: translate(-2px, -2px);
  box-shadow: 9px 9px 0 #05070d, 0 0 0 1px rgba(120,160,220,.4);
}

/* Section headings get the inked outline comic logos use. The heading is an
   h3.subsec, not an h2 -- and text-shadow inherits, so the small note span
   nested inside has to opt back out or it gets outlined too. */
body[data-fan="jl"] .subsec {
  text-shadow: 2px 2px 0 #05070d, -1px -1px 0 #05070d, 1px -1px 0 #05070d, -1px 1px 0 #05070d;
  letter-spacing: .01em;
}

body[data-fan="jl"] .subsec-yr { text-shadow: none; }

body[data-fan="jl"] .fan-title {
  text-shadow: 4px 4px 0 #05070d, -2px -2px 0 #05070d, 2px -2px 0 #05070d, -2px 2px 0 #05070d;
}

/* The tag chip reads as a cover flash. */
body[data-fan="jl"] .fan-tag {
  border: 2px solid #05070d; border-radius: 2px;
  box-shadow: 2px 2px 0 #05070d; font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
  body[data-fan="jl"] .fan-card, body[data-fan="jl"] .fan-tile { transition: none; }
}

@keyframes jl-drift { from { transform: translate3d(-2%, -1%, 0); } to { transform: translate3d(2%, 1%, 0); } }

@keyframes jl-ping {
  0%   { r: 42; opacity: .45; }
  70%  { r: 120; opacity: 0; }
  100% { r: 120; opacity: 0; }
}

@keyframes jl-blip { 0%, 100% { opacity: .35; } 50% { opacity: .85; } }

@keyframes jl-strike { 0%, 88%, 100% { opacity: 1; } 90% { opacity: .35; } 92% { opacity: 1; } 95% { opacity: .5; } 97% { opacity: 1; } }

@keyframes jl-emblem { 0%, 100% { opacity: .82; } 50% { opacity: 1; } }

body[data-fan="jl"] { --a: #5f9fe0; }

body[data-fan="jl"] .fan-title {
  text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700;
  background: linear-gradient(180deg, #eaf2ff, #5f9fe0 56%, #d8b45f);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

@keyframes jl-orbit { to { transform: rotate(360deg); } }

@keyframes boys-slosh { from { height: 60%; } to { height: 65%; } }

/* ───────── Knott's Berry Farm: a boardwalk at dusk ───────── */
body[data-fan="knotts"] { --a: #b06fd8; }

@keyframes kn-drift { to { background-position: 320px -120px; } }

@keyframes knm-lamp { 0%, 100% { opacity: .36; } 50% { opacity: .62; } }

body[data-fan="knotts"] .fan-title {
  font-family: "American Typewriter", Georgia, serif; font-weight: 700;
  font-variant: small-caps; letter-spacing: 0.04em;
}

@keyframes dex-blink { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

@keyframes fnaf-snow { 0% { opacity: .5; } 33% { opacity: .8; } 66% { opacity: .4; } 100% { opacity: .65; } }

.fs-bot--bonnie { fill: #45406a; }

.fs-bot--chica  { fill: #6a5f2a; }

.fs-bot--foxy   { fill: #6a3524; }

.fnaf-burst.is-on { animation: fnaf-flash .26s steps(3, end) 1; }

@keyframes fnaf-flash { 0% { opacity: .9; } 60% { opacity: .35; } 100% { opacity: 0; } }

.fnaf-cam.is-on {
  background: rgba(200,240,200,.16); color: #dff0d8;
  border-color: rgba(200,240,200,.3);
}

@media (prefers-reduced-motion: reduce) { .fnaf-burst.is-on { animation: none; } }

/* Named off-rec because it started life on The Office's talking-head frame.
   That block is gone; the blink is still the right one for a rec dot. */
@keyframes rec-blink { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

.fnaf-usage i.is-on { background: #5fd07f; box-shadow: 0 0 8px rgba(95,208,127,.6); }

.fnaf-usage i.is-on:nth-child(3) { background: #f0c840; box-shadow: 0 0 8px rgba(240,200,64,.6); }

.fnaf-usage i.is-on:nth-child(4) { background: #e04a3a; box-shadow: 0 0 8px rgba(224,74,58,.6); }

@keyframes lotr-turn { to { transform: rotate(360deg); } }

@keyframes lotr-breathe { from { opacity: .5; } to { opacity: 1; } }

@keyframes bb-rise { 0% { transform: translateY(0); opacity: 0; } 15% { opacity: .8; } 100% { transform: translateY(-58px); opacity: 0; } }

@keyframes kfp-sheen { 0%, 100% { transform: translateX(-30%); } 50% { transform: translateX(30%); } }

@keyframes av-drift { to { background-position: 130px -260px; } }

@keyframes av-glide {
  from { transform: translate3d(0, 0, 0) rotate(-3deg); }
  50%  { transform: translate3d(58vw, 6vh, 0) rotate(3deg); }
  to   { transform: translate3d(122vw, -2vh, 0) rotate(-3deg); }
}

@keyframes avs-flow { to { stroke-dashoffset: -24; } }

@keyframes avs-bob { from { transform: translateY(-5px); } to { transform: translateY(5px); } }

.av-plant--pod svg { fill: hsl(var(--h, 180) 70% 62% / .12); }

/* Lit plants hold for a beat and fade behind the pointer -- an instant drop
   reads as a hover state, the lag reads as something reacting. */
.av-plant.is-lit {
  opacity: 1;
  filter: drop-shadow(0 0 9px hsl(var(--h, 180) 90% 62% / .95));
  transition: opacity .12s ease, filter .12s ease;
}

@keyframes av-float { from { transform: translate(-50%, -6px); } to { transform: translate(-50%, 8px); } }

@keyframes av-bub { 0% { transform: translateY(0); opacity: 0; } 12% { opacity: .8; } 100% { transform: translateY(-190px); opacity: 0; } }

@keyframes dz-pop {
  0% { opacity: 0; transform: scale(.2); }
  8% { opacity: 1; transform: scale(1); }
  30% { opacity: 0; transform: scale(1.25); }
  100% { opacity: 0; transform: scale(1.25); }
}

/* The caption sits inside the figure now, so it must clear the padded stage. */
.fan-art--dz .dz-stage figcaption, .fan-art--uni .uni-globefig figcaption {
  margin: 0; padding: 0.9rem 1rem 1rem; text-align: center;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--mute); line-height: 1.7;
}

/* Width caps go on the figure, never on .fan-art -- that grid holds the prose
   column too, and capping it squeezes the writing down to nothing. */
@media (min-width: 820px) {
  .fan-art--dz  { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
  .fan-art--uni { grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); }
}

@keyframes ung-turn { from { transform: scaleX(1); } 50% { transform: scaleX(.08); } to { transform: scaleX(1); } }
