/* Film Collection Explorer
   Dark ground, two data inks, one rule: where colour carries data it means seen
   or missing, and nothing else.

   The badge families are the single exception, and they are allowed it because
   they stay outside that job. A family hue tints its heading, its glyph and the
   hairline over its block — the chrome that says which section you are in. It
   never touches a badge's state: earned still wears the filled seen mark and a
   locked bar is still drawn in the gap ink, so a card in the violet section is
   read the same way as a card in the teal one. Identity above, state below. */

@import url('./fonts.css');

/* The completion bar is drawn as a gradient stop, so the stop position has to
   be a registered custom property for it to be animatable at all. */
@property --pct {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 0%;
}

:root {
  --ground: #12171a;
  --panel: #1a2126;
  --panel-hi: #212a30;
  --ink: #e4e2da;
  --muted: #a3aeb3;
  --faint: #8d989d;
  --seen: #7fa88b;
  --gap: #d2a24c;

  /* The muted seen ink recedes on a poster. Where seen has to be read at a
     glance across a wall of them it gets this solid fill instead. Held at the
     brightest green that still carries white label text at 4.9:1. */
  --seen-solid: #2b8049;

  /* The unfilled half of a bar is the gap colour held well back, so a nearly
     empty career still reads as a warm absence rather than as nothing. */
  --gap-track: #3a3327;
  --seen-track: #232c27;
  --rule: #232c31;

  --display: 'Fraunces', Georgia, serif;
  --body: 'IBM Plex Sans', system-ui, sans-serif;
  --data: 'IBM Plex Mono', ui-monospace, monospace;

  /* A genuine scale, 1.25 at the small end opening to 1.4 at display sizes.
     Every step sits about an eighth above where it started: the ratios are
     unchanged, the whole thing just reads at a comfortable distance now. */
  /* The bottom two steps were set for density and read too small on a dark
     ground. They are each lifted a notch; the ratios above --step-0 are
     untouched, so only the supporting type moves. */
  --step--2: 0.8125rem;
  --step--1: 0.875rem;
  --step-0: 1rem;
  --step-1: 1.125rem;
  --step-2: 1.375rem;
  --step-3: 1.8125rem;
  --step-4: clamp(2rem, 1.35rem + 2.5vw, 2.8rem);
  --step-5: clamp(2.5rem, 1.35rem + 4.9vw, 4.5rem);
  --step-6: clamp(3.05rem, 1.1rem + 7.8vw, 6.7rem);

  --gutter: clamp(1rem, 0.4rem + 2.4vw, 2.75rem);
  --measure: 68ch;
  --max: 78rem;

  color-scheme: dark;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  /* Jump to an in-page anchor and land below the pinned masthead, not behind it. */
  scroll-padding-top: calc(var(--nav-h, 4.5rem) + 1rem);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}

a {
  color: inherit;
  text-decoration-color: var(--faint);
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-color: var(--gap);
}

:focus-visible {
  outline: 2px solid var(--gap);
  outline-offset: 2px;
  border-radius: 1px;
}

.shell {
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--gutter);
  top: 0.5rem;
  /* Above the sticky masthead, or the focused skip link hides behind it. */
  z-index: 30;
  background: var(--panel-hi);
  padding: 0.5rem 0.75rem;
}

/* ---------- Top-tier accent ---------- */

/* The Completionist signature: a 6px hairline of the whole family aurora pinned
   to the very top of the viewport, full browser width, drifting like the hero
   numbers. Fixed and above the masthead (20) and skip link (30), but below the
   search modal and toasts (40+), so it never covers an overlay. The body reserves
   its 6px so the line sits clear of the header rather than over it. */
.toptier-accent {
  position: fixed;
  inset: 0 0 auto 0;
  height: 6px;
  z-index: 35;
  pointer-events: none;
  background: linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd);
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}

body:has(.toptier-accent) {
  padding-top: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .toptier-accent {
    animation: none;
    --aurora: 30%;
  }
}

/* ---------- Masthead ---------- */

.masthead {
  /* Pinned so the sections can be reached from anywhere without scrolling back
     up. It keeps its own height — brand beside nav — and the page runs under it.
     A script mirrors that height into --nav-h so the sticky sidebar and in-page
     anchors below can sit clear of it. */
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  padding-block: clamp(0.5rem, 1.4vw, 1.05rem) 0.35rem;
  /* Nearly opaque, so what scrolls under is only just sensed. Browsers that can
     blur trade a little more of that opacity for frosted glass, below. */
  background: color-mix(in srgb, var(--ground) 95%, transparent);
  border-bottom: 1px solid var(--rule);
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .masthead {
    background: color-mix(in srgb, var(--ground) 80%, transparent);
    -webkit-backdrop-filter: blur(12px) saturate(1.2);
    backdrop-filter: blur(12px) saturate(1.2);
  }
}

/* The air under the masthead belongs to two rules, not one: its own bottom
   padding and the top padding of whatever leads the page. Trimming only the
   masthead's share would leave the header sitting visibly off-centre in its own
   band, so whatever comes first gives up the same proportion. A person page
   opens with the back link rather than a section, and needs saying separately
   or it keeps the old gap. */
.masthead + main > .section:first-child {
  padding-top: clamp(0.55rem, 1.05vw, 0.9rem);
}

.masthead + main > .back:first-child {
  padding-top: 0.35rem;
}

/* The first section opens right under the masthead's own bottom rule, so its
   head's top border draws a second line a few pixels below it. Drop it on that
   one section: the masthead line already divides the page here. */
.masthead + main > .section:first-child .section__head {
  border-top: none;
  padding-top: 0;
}

.masthead__title {
  font-family: var(--display);
  font-optical-sizing: auto;
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-size: var(--step-4);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.015em;
}

.masthead__title a {
  text-decoration: none;
}

.masthead__meta {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.01em;
}

.masthead__meta b {
  color: var(--ink);
  font-weight: 500;
}

/* ---------- Section headings ---------- */

.section {
  padding-block: clamp(1.5rem, 3vw, 2.5rem) 0;
}

.section__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  border-top: 1px solid var(--rule);
  padding-top: 0.75rem;
  margin-bottom: 1rem;
}

.section__title {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* A title that leads with a glyph: flex so the icon sits centred against the
   label with a little air between them, rather than jammed to its edge. */
.section__title--icon {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* This is the sentence that explains what a section is for, so it is read
   before anything under it. It sits a tier up from the other supporting text. */
.section__note {
  font-size: var(--step-0);
  color: var(--muted);
  max-width: var(--measure);
  line-height: 1.5;
}

/* A heading that also carries page-level actions: the text stays left, the
   buttons sit on the right and drop below it on narrow screens. */
.section__head--split {
  align-items: center;
}

.section__headline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
}

.page-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-left: auto;
}

.page-actions:empty {
  display: none;
}

.page-actions__btn {
  font: inherit;
  /* The page's own voice rather than the data font: these are things to do,
     not values to read. */
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--rule);
  padding: 0.75rem 1.25rem;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, filter 120ms ease;
}

.page-actions__btn:hover {
  color: var(--ink);
  border-color: var(--faint);
}

.page-actions__btn--primary {
  background: var(--gap);
  border-color: var(--gap);
  color: var(--ground);
  font-weight: 600;
}

.page-actions__btn--primary:hover {
  color: var(--ground);
  filter: brightness(1.08);
}

.page-actions__plus {
  font-size: 1.1em;
  line-height: 0;
}

/* Square, so it reads as a sibling of the labelled buttons rather than a
   shrunken one. The padding is set to match their height exactly. */
.page-actions__btn--icon {
  padding: 0.625rem;
  gap: 0;
}

.page-actions__btn .icon {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
}

/* ---------- Nearest misses ---------- */
/* A list, not cards. The missing titles are the actionable content, so they
   are on the surface rather than behind a click. */

/* Closest to done: a five-up band of tiles that fills the content width and
   wraps down on narrow screens. Each carries one of five aurora hues by
   position, so a page reads as a spectrum rather than a wall of text. */
.misses {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10.5rem), 1fr));
  gap: 0.75rem;
}

/* display: grid outweighs the browser's own [hidden] rule, so the pager's
   hidden tiles have to be re-hidden by hand. */
.miss[hidden] {
  display: none;
}

/* The five hues repeat every five tiles, which lines up with the pager's
   five-per-page: whichever page is showing, its tiles run the same spectrum. */
.miss:nth-child(5n + 1) { --hue: #5dadbd; }
.miss:nth-child(5n + 2) { --hue: #90a0d2; }
.miss:nth-child(5n + 3) { --hue: #ab97d4; }
.miss:nth-child(5n + 4) { --hue: #cf89cf; }
.miss:nth-child(5n + 5) { --hue: #cc9481; }

.miss {
  --hue: #5dadbd;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 1rem 1rem 1.1rem;
  border-radius: 12px;
  background: color-mix(in srgb, var(--hue) 8%, var(--ground));
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  transition: background 140ms ease, border-color 140ms ease;
}

.miss:hover,
.miss:focus-within {
  background: color-mix(in srgb, var(--hue) 14%, var(--ground));
  border-color: color-mix(in srgb, var(--hue) 45%, var(--rule));
}

.miss__link {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  text-decoration: none;
  color: inherit;
}

/* The whole tile is the hit area, but only the link owns it. */
.miss__link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.miss__count {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--display);
  line-height: 1;
  color: color-mix(in srgb, var(--hue) 80%, var(--ink));
  margin-bottom: 0.15rem;
}

.miss__count b {
  font-size: var(--step-4);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.miss__togo {
  font-family: var(--data);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
}

.miss__name {
  font-family: var(--display);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
}

.miss__role {
  font-size: var(--step--2);
  color: var(--muted);
  font-family: var(--data);
  letter-spacing: 0.02em;
}

/* Pinned to the foot so tiles of a row keep one baseline however many films
   each is short of. A career is at most three films from done, so the list is
   always reserved for three lines: every tile is the same height whatever its
   page holds, and the pager below never jumps. */
.miss__films {
  list-style: none;
  margin: auto 0 0;
  /* Reset the ul's default padding-inline-start (~40px), or the titles sit
     indented toward the middle instead of flush under the name. */
  padding: 0.65rem 0 0;
  border-top: 1px solid color-mix(in srgb, var(--hue) 18%, var(--rule));
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: var(--step--2);
  color: var(--muted);
  /* Three lines (line-height 1.5em each) + two gaps + the padding and rule
     above, in the border-box these sit in. Matches a full three-film tile
     exactly, so shorter ones reserve the same space rather than collapse. */
  min-height: calc(3 * 1.5em + 2 * 0.2rem + 0.65rem + 1px);
}

.miss__films li {
  line-height: 1.5;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.miss__year {
  color: var(--faint);
}

/* ---------- Pager ---------- */
/* Shared by the misses list and the board. Empty until scout.js fills it, and
   an empty div would still eat the section's rhythm. */
.pager:empty {
  display: none;
}

/* display: flex below would beat the hidden attribute, the same trap as
   .miss[hidden] above. scout.js hides the pager when one page holds it all. */
.pager[hidden] {
  display: none;
}

.pager {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.pager__status {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* ---------- Filters ---------- */

/* Role buttons left, name search right, and on a narrow screen the search
   drops under the buttons at full width rather than squeezing them. */
.board-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  margin-bottom: 0.75rem;
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

/* Empty until scout.js fills it, and an empty flex item would still eat the
   row's gap. */
.board-find:empty {
  display: none;
}

/* The width lives on the flex item, not the label inside it: the label is
   shrink-to-fit, so sizing it would only ever fill whatever this box already
   was. */
.board-find {
  width: 14rem;
  max-width: 100%;
}

.board-find__label {
  display: block;
}

/* Once the buttons have taken their own row there is nothing to sit beside,
   so the field takes the width rather than leaving a short stub. */
@media (max-width: 34rem) {
  .board-find {
    width: 100%;
  }
}

.filter {
  font: inherit;
  font-size: var(--step--1);
  font-family: var(--data);
  letter-spacing: 0.02em;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--rule);
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  transition:
    color 120ms ease,
    border-color 120ms ease;
}

.filter:hover {
  color: var(--ink);
  border-color: var(--faint);
}

/* The ends of the list are stated, not hidden: a greyed button says there is
   no further to go, where a vanished one would leave the row jumping. */
.filter:disabled {
  color: var(--faint);
  border-color: var(--rule);
  cursor: default;
}

.filter[aria-pressed='true'] {
  color: var(--ground);
  background: var(--ink);
  border-color: var(--ink);
}

/* ---------- The board ---------- */
/* The signature: each row's completion bar is that row's bottom rule. Read the
   table down for the ranking, defocus and it is a bar chart. */

.board-wrap {
  overflow-x: auto;
}

/* Past the Free cap the board is gated (site/render.js boardLocked): a blurred
   run of row-shaped shimmer under an unlock prompt — the same bargain the locked
   badge families and stats bands make. --hue tints the shared .stats-lock chip;
   the Scout gate borrows the board's leading teal. */
.board-locked {
  position: relative;
  --hue: #5dadbd;
  margin-top: 0.75rem;
}

.board--blurred {
  min-width: 0;
  filter: blur(6px);
  opacity: 0.5;
  pointer-events: none;
  user-select: none;
}

/* No head sits above this wrapper, so the prompt covers the whole blurred run. */
.board-locked .stats-lock {
  inset-block: 0;
}

.board {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step-0);
  min-width: 34rem;
}

.board caption {
  text-align: left;
  font-size: var(--step--1);
  color: var(--faint);
  padding-bottom: 0.5rem;
}

.board thead th {
  text-align: left;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 0.5rem 0.5rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}

/* The last three columns are figures and are set flush right, so their headings
   go with them. Left-aligned, each one sat over the gap before its column and
   read as a label for the wrong thing. */
.board thead th[data-key='seen'],
.board thead th[data-key='pct'],
.board thead th[data-key='remaining'] {
  text-align: right;
}

/* And the sort arrow hangs on the inside, so the word itself finishes flush
   with the figures. Trailing, it took the label off the column's edge — by the
   width of the arrow on the sorted column, and by the gap kept for one
   everywhere else, which is worse: the three headings would not even line up
   with each other. */
.board thead th[data-key='seen'] .sort,
.board thead th[data-key='pct'] .sort,
.board thead th[data-key='remaining'] .sort {
  flex-direction: row-reverse;
}

.board th[aria-sort] .sort {
  cursor: pointer;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  padding: 0.25rem 0;
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
}

.board th[aria-sort] .sort::after {
  content: '';
  color: var(--faint);
  font-family: var(--data);
}

.board th[aria-sort='descending'] .sort::after {
  content: '\2193';
  color: var(--gap);
}

.board th[aria-sort='ascending'] .sort::after {
  content: '\2191';
  color: var(--gap);
}

/* The bar is drawn at its true width by default, so the table is honest with
   scripting off. Only when JS is present does it start empty and fill. */
.board tbody tr {
  --pct: var(--target);
  background-image: linear-gradient(
    to right,
    var(--seen) 0 var(--pct),
    var(--gap-track) var(--pct) 100%
  );
  background-size: 100% 2px;
  background-position: 0 100%;
  background-repeat: no-repeat;
}

.js .board tbody tr {
  --pct: 0%;
}

.js .board tbody tr.in-view {
  --pct: var(--target);
}

@media (prefers-reduced-motion: no-preference) {
  .js .board tbody tr {
    transition: --pct 700ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }
}

.board tbody tr:hover {
  background-color: var(--panel);
}

.board td {
  padding: 0.6rem 0.5rem 0.65rem;
  vertical-align: baseline;
}

.board .c-name {
  font-size: var(--step-1);
  font-weight: 500;
  width: 40%;
}

.board .c-name a {
  text-decoration: none;
}

.board .c-name a:hover {
  text-decoration: underline;
  text-decoration-color: var(--gap);
}

/* Favorite column: a narrow marker. Its header wears an outline heart for a
   label; a favorited career wears a filled one, in the seen green every other
   favorite on the site takes. */
.board .th-fav,
.board .c-fav {
  width: 1%;
  text-align: center;
  white-space: nowrap;
}

.board .th-fav .sort {
  padding-inline: 0;
}

.board .c-fav__mark {
  display: inline-flex;
}

/* Favorited: the seen green every favorite on the site takes. */
.board .c-fav__mark--on {
  color: var(--seen);
}

/* Not favorited: a dimmed outline, on every row, marking the place to click. */
.board .c-fav__mark--off {
  color: var(--faint);
}

/* scout.js swaps the marker for this once a backend admits writes: a quiet
   toggle sized to the heart, its hit area padded out past the glyph without
   shifting the column. Filled and green when on, a faint outline when off that
   warms to the same green on hover, previewing the fill a click would give it. */
.board .c-fav__btn {
  display: inline-flex;
  padding: 0.3rem;
  margin: -0.3rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--faint);
  transition: color 120ms ease;
}

.board .c-fav__btn.is-on {
  color: var(--seen);
}

.board .c-fav__btn:hover,
.board .c-fav__btn:focus-visible {
  color: var(--seen);
}

.board .c-fav__btn:focus-visible {
  outline: 2px solid var(--seen);
  outline-offset: 2px;
}

.board .th-fav svg,
.board .c-fav__mark svg,
.board .c-fav__btn svg {
  width: 1em;
  height: 1em;
  vertical-align: middle;
}

.board .c-role {
  color: var(--muted);
  font-family: var(--data);
  font-size: var(--step--1);
  white-space: nowrap;
}

.board .c-count,
.board .c-pct,
.board .c-left {
  font-family: var(--data);
  text-align: right;
  white-space: nowrap;
}

.board .c-count {
  color: var(--muted);
}

.board .c-count b {
  color: var(--ink);
  font-weight: 500;
}

.board .c-pct {
  font-variant-numeric: tabular-nums;
}

.board .c-left {
  color: var(--gap);
  font-size: var(--step--1);
}

.board tr[data-remaining='0'] .c-left {
  color: var(--seen);
}

.board .empty td {
  color: var(--muted);
  padding-block: 2rem;
  text-align: center;
}

/* ---------- Person page ---------- */

.person {
  padding-block: clamp(1.5rem, 4vw, 3rem);
  border-bottom: 1px solid var(--rule);
}

.person__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 1rem 2rem;
  align-items: end;
}

.person__name {
  font-family: var(--display);
  font-optical-sizing: auto;
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-size: var(--step-6);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

.person__role {
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 0.75rem;
}

.person__stats {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.9;
}

.person__stats b {
  color: var(--ink);
  font-weight: 500;
}

.person__bar {
  grid-column: 1 / -1;
  height: 3px;
  margin-top: 0.5rem;
  background-image: linear-gradient(
    to right,
    var(--seen) 0 var(--pct),
    var(--gap-track) var(--pct) 100%
  );
  --pct: var(--target);
}

/* Under the bar, aligned with the name: this is a fact about the career, not
   about any film in the list below it. Shown only on the pages that have one,
   so it never leaves a gap where a control was declined. */
.person__actions {
  grid-column: 1 / -1;
  justify-self: start;
  margin-top: 0.75rem;
}

.person__actions:empty {
  display: none;
}

/* The favorite heart carries at the button's own size, and the filled state is
   coloured by page-actions__btn--on, so it needs no colour of its own. */
.person__fav svg {
  width: 1.15rem;
  height: 1.15rem;
}

.back {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  text-decoration: none;
  display: inline-block;
  padding-block: 1rem;
}

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

/* The filmography. One set of elements, two layouts.
   In both, unseen films are the bright ones and seen films recede, which is the
   same inversion the collection grid uses. */

.viewswitch {
  display: inline-flex;
  border: 1px solid var(--rule);
  margin-bottom: 1rem;
}

.viewswitch .seg__btn + .seg__btn {
  border-left: 1px solid var(--rule);
}

.films {
  list-style: none;
  margin: 0;
  padding: 0;
}

.film__art {
  position: relative;
  display: block;
  background: var(--panel);
  overflow: hidden;
}

.film__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.film__noart {
  display: grid;
  place-items: center;
  height: 100%;
  padding: 0.5rem;
  text-align: center;
  font-size: var(--step--2);
  color: var(--muted);
  border: 1px solid var(--rule);
}

.film--seen .film__poster {
  opacity: 0.4;
  filter: saturate(0.5);
  transition: opacity 160ms ease, filter 160ms ease;
}

.film--seen:hover .film__poster,
.film--seen:focus-within .film__poster {
  opacity: 1;
  filter: none;
}

/* ---- Poster grid, the default ---- */

.films[data-view='grid'] {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 1.25rem 1rem;
}

.films[data-view='grid'] .film {
  display: grid;
  grid-template-rows: auto auto auto;
  gap: 0.35rem;
  align-content: start;
  padding: 0;
  border: 0;
}

.films[data-view='grid'] .film__art {
  aspect-ratio: 2 / 3;
}

.films[data-view='grid'] .film__title {
  font-size: var(--step--1);
  line-height: 1.3;
}

.films[data-view='grid'] .film__year {
  order: 3;
  font-size: var(--step--2);
}

.films[data-view='grid'] .stars {
  order: 4;
}

/* ---- List ---- */

.films[data-view='list'] .film {
  display: grid;
  grid-template-columns: 2.25rem 3.5rem minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--rule);
}

/* A thumbnail, not a poster: enough to recognise a film, not enough to dominate. */
.films[data-view='list'] .film__art {
  width: 2.25rem;
  aspect-ratio: 2 / 3;
}

.films[data-view='list'] .film__noart {
  font-size: 0;
  border: 1px solid var(--rule);
}

.films[data-view='list'] .film__title {
  font-size: var(--step-1);
}

.films[data-view='list'] .mark {
  position: static;
  background: none;
  padding: 0;
}

/* ---- Shared emphasis ---- */

.film__year {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
  margin: 0;
}

.film__title {
  margin: 0;
}

.film--unseen .film__title {
  color: var(--ink);
  font-weight: 500;
}

.film--seen .film__title {
  color: var(--muted);
  font-weight: 400;
}

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

.foot {
  border-top: 1px solid var(--rule);
  margin-top: clamp(2rem, 6vw, 4rem);
  /* Extra bottom padding clears the fixed .editbar (Add Film + account cog),
     which floats in the same bottom-right corner as .foot__links. Without it,
     scrolling to the page foot drops the bar on top of the About link. */
  padding-block: 1.5rem 5rem;
  font-size: var(--step--1);
  color: var(--faint);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: center;
}

.foot a {
  color: var(--muted);
}

/* The site's own links, pushed to the far side of the footer so they sit
   opposite the TMDB attribution rather than trailing it. On a wrap (narrow
   screens) the auto margin collapses and they fall neatly onto the next line. */
.foot__links {
  margin-left: auto;
  display: flex;
  gap: 1.5rem;
}

.tmdb-logo {
  height: 0.85rem;
  width: auto;
  opacity: 0.75;
}

/* ---------- Small screens ---------- */

@media (max-width: 46rem) {
  .person__grid {
    grid-template-columns: 1fr;
    align-items: start;
  }

  .board .c-role {
    display: none;
  }

  /* The list drops the thumbnail and the year on a narrow screen: the title
     and the watched state are what matter at that width. */
  .films[data-view='list'] .film {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.5rem;
  }

  .films[data-view='list'] .film__art,
  .films[data-view='list'] .film__year {
    display: none;
  }

  .films[data-view='grid'] {
    grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ================================================================
   Collection grid, search, and the editing layer
   ================================================================ */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Masthead nav ---------- */

.masthead__brand {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
}

.nav {
  display: flex;
  align-items: center;
  gap: 0.25rem 1rem;
  flex-wrap: wrap;
}

.nav__link {
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  padding-block: 0.35rem;
  border-bottom: 1px solid transparent;
}

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

.nav__link[aria-current='page'] {
  color: var(--ink);
  border-bottom-color: var(--gap);
}

.nav__search {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--rule);
  padding: 0.3rem 0.6rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}

.nav__search:hover {
  color: var(--ink);
  border-color: var(--faint);
}

kbd {
  font-family: var(--data);
  font-size: 0.85em;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  padding: 0.05em 0.35em;
  color: var(--muted);
}

/* ---------- Collection layout ---------- */

.collection {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}

.facets {
  position: sticky;
  /* Below the pinned masthead, not under it. --nav-h is that header's measured
     height; the fallback covers the moment before the script runs. */
  top: calc(var(--nav-h, 4.5rem) + 1rem);
  display: grid;
  gap: 0.75rem;
  max-height: calc(100vh - var(--nav-h, 4.5rem) - 2rem);
  overflow-y: auto;
  /* Same as a facet's own list: scrolling the sidebar to its end should not
     carry on into the page behind it. */
  overscroll-behavior: contain;
  padding-right: 0.5rem;
}

.facets__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  justify-content: space-between;
}

.facets__clear {
  font: inherit;
  font-size: var(--step--1);
  background: none;
  border: 0;
  color: var(--gap);
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.facets__find input,
.board-find__label input,
.grid-sort select,
.dialog__input {
  font: inherit;
  font-size: var(--step-0);
  width: 100%;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--rule);
  padding: 0.4rem 0.6rem;
}

.seg {
  display: inline-flex;
  /* Four filters do not fit across the sidebar at every width. Wrapping to a
     second line inside the box beats squeezing a label until it is cut off. */
  flex-wrap: wrap;
  border: 1px solid var(--rule);
}

.seg__btn {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  background: transparent;
  color: var(--muted);
  border: 0;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  /* Never shrink a label into an ellipsis or a clipped word. */
  flex: 0 0 auto;
  white-space: nowrap;
}

.seg__btn + .seg__btn {
  border-left: 1px solid var(--rule);
}

/* Square, so three icons and the word All read as one row of equal weights
   rather than a label followed by three afterthoughts. */
.seg__btn--icon {
  display: inline-grid;
  place-items: center;
  padding: 0.3rem 0.55rem;
}

.seg__btn--icon svg {
  display: block;
}

.seg__btn[aria-pressed='true'] {
  background: var(--ink);
  color: var(--ground);
}

.facet {
  border-top: 1px solid var(--rule);
  padding-top: 0.5rem;
}

.facet__label {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  list-style: none;
}

.facet__label::-webkit-details-marker {
  display: none;
}

.facet__label::before {
  content: '+ ';
  color: var(--faint);
}

.facet[open] .facet__label::before {
  content: '− ';
}

.facet__options {
  display: grid;
  gap: 0.1rem;
  margin-top: 0.4rem;
  max-height: 14rem;
  overflow-y: auto;
  /* Keep the wheel inside the list: at the top or bottom of one facet, a scroll
     should stop, not run on into the sidebar and then the page behind it. */
  overscroll-behavior: contain;
  /* The counts sit flush right against the scrollbar without this. The padding
     is what moves them clear of an overlay scrollbar, which is drawn over the
     content and reserves nothing. The gutter is for the platforms whose
     scrollbars do take width: it keeps a facet that scrolls lining its numbers
     up with one that does not. */
  padding-right: 0.7rem;
  scrollbar-gutter: stable;
}

.facet__option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.45rem;
  font-size: var(--step--1);
  color: var(--muted);
  cursor: pointer;
  padding: 0.12rem 0;
}

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

.facet__option b {
  color: var(--faint);
  font-family: var(--data);
  font-weight: 400;
  font-size: var(--step--2);
}

.facet__option input {
  accent-color: var(--gap);
}

/* ---------- The grid ---------- */

.grid-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 1rem;
}

.grid-count {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0;
}

.grid-sort select {
  width: auto;
}

.grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 1.25rem 1rem;
}

.card {
  display: grid;
  gap: 0.45rem;
  align-content: start;
}

.card__art {
  position: relative;
  aspect-ratio: 2 / 3;
  background: var(--panel);
  overflow: hidden;
  /* A quiet edge on hover, so it is clear the poster is a way in. */
  outline: 1px solid transparent;
  outline-offset: -1px;
  transition: outline-color 160ms ease;
}

/* Every poster is shown as it is. Seen and unseen are carried by the mark and
   the title, not by dimming the artwork — a wall of half-faded posters is
   harder to read than the state it was encoding. */
.card__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: filter 160ms ease;
}

.card:hover .card__poster,
.card:focus-within .card__poster {
  filter: brightness(1.12) saturate(1.05);
}

.card:hover .card__art,
.card:focus-within .card__art {
  outline-color: color-mix(in srgb, var(--gap) 55%, transparent);
}

.card__noart {
  display: grid;
  place-items: center;
  height: 100%;
  padding: 0.75rem;
  text-align: center;
  font-size: var(--step--1);
  color: var(--muted);
  border: 1px solid var(--rule);
}

.card__meta {
  display: grid;
  gap: 0.1rem;
}

.card__title {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.3;
  color: var(--ink);
}

.card--seen .card__title {
  color: var(--muted);
}

.card__year {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}

.grid-empty {
  color: var(--muted);
  padding-block: 3rem;
  text-align: center;
}

.grid-sentinel {
  height: 1px;
}

/* ---------- Watch mark and stars ---------- */

/* The mark is the one thing you scan a wall of posters for, so it is set a
   step up from the other card data and its ink is lifted off the base hue. */
.mark {
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.03em;
  color: color-mix(in srgb, var(--gap) 80%, #fff);
  white-space: nowrap;
}

/* Bottom right of the artwork, wherever the artwork is. The grid on a person's
   page is the same poster wearing the same mark, and having it sit in the other
   corner there made two views of one film look like two different things. */
.card .mark,
.films[data-view='grid'] .mark {
  position: absolute;
  right: 0;
  bottom: 0;
  padding: 0.3rem 0.55rem;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(3px);
}

/* The mark on a film the collection does not contain: a career on the board
   reaches it, which is the only reason there is a page for it at all. It is
   worn in the heading of that page, the one place such a film is ever met. */
.detail__scouted {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  /* The same gold-and-dark pairing the Add Film button uses, so the two reads
     of "this came from somewhere" match. */
  color: var(--ground);
  background: var(--gap);
  /* Deliberately hoverable: the tooltip is what names the career it came
     from, which is the whole answer to "why is this here". */
  cursor: help;
}

/* Inline-grid so it sits on the heading's line rather than starting its own,
   and centred on the text rather than the baseline the icon has no business
   sharing. */
.detail__scouted {
  display: inline-grid;
  vertical-align: middle;
  margin-inline-start: 0.5rem;
  /* The heading is set in the display face at a large size; the mark stays the
     size it is in the grid so it reads as the same mark. */
  translate: 0 -0.1em;
}

.detail__scouted:hover {
  filter: brightness(1.08);
}

/* Seen is the one state worth spotting from across a grid of posters, so it is
   a filled chip rather than tinted text. Every place a film is met wears the
   same one — both grids, either view, and the film's own page — so the mark
   never has to be read twice to mean the same thing.

   The [data-view] selector is carrying specificity, not choosing a view: it
   has to outrank the two rules above that give the mark a poster's backdrop in
   grid and take it away again in list. */
.card--seen .mark,
.detail--seen .mark,
.films[data-view] .film--seen .mark {
  color: #fff;
  background: var(--seen-solid);
  backdrop-filter: none;
  padding: 0.3rem 0.55rem;
}

.card--seen .mark--button:hover,
.card--seen .mark--button:focus-visible,
.detail--seen .mark--button:hover,
.detail--seen .mark--button:focus-visible,
.films[data-view] .film--seen .mark--button:hover,
.films[data-view] .film--seen .mark--button:focus-visible {
  color: #fff;
  background: color-mix(in srgb, var(--seen-solid) 82%, #fff);
}

.mark--button {
  border: 0;
  cursor: pointer;
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.03em;
  color: inherit;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
}

.mark--button:hover {
  color: var(--ink);
}

/* As a grid or flex item the inline-flex box would be blockified and stretched
   to the full card width, which would put the five-star end of the hit area
   somewhere out over the poster. It has to hug its glyphs. */
.stars {
  display: inline-flex;
  width: max-content;
  max-width: 100%;
  justify-self: start;
  gap: 0.05em;
  font-size: var(--step-0);
  line-height: 1;
  color: var(--faint);
}

.star {
  position: relative;
  color: var(--seen-track);
}

/* Each star is filled by a clipped overlay, which is what makes half steps
   possible without half-star glyphs. */
.star::before {
  content: '★';
  position: absolute;
  inset: 0;
  width: var(--fill, 0%);
  overflow: hidden;
  color: var(--seen);
}

.stars--interactive {
  cursor: pointer;
  padding: 0.15rem 0;
}

.stars--interactive:hover .star::before {
  color: var(--gap);
}

/* ---------- Rating filter ---------- */

.facet__rating {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 0.5rem;
}

/* The card control read the other way: a picker, not a display. Larger than the
   card stars so it invites a click, and the same gold-on-hover the film rating
   shows, so the two read as one gesture. */
.stars--filter {
  cursor: pointer;
  font-size: var(--step-1);
  padding: 0.1rem 0;
}

.stars--filter:hover .star::before,
.stars--filter:focus-visible .star::before {
  color: var(--gap);
}

.stars--filter:focus-visible {
  outline: 2px solid var(--gap);
  outline-offset: 3px;
  border-radius: 2px;
}

.facet__rating-clear {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--faint);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.facet__rating-clear:hover {
  color: var(--ink);
}

/* ---------- Search palette ---------- */

.palette[hidden] {
  display: none;
}

.palette {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: start center;
  padding: clamp(2rem, 10vh, 8rem) 1rem 1rem;
}

.palette__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ground) 78%, transparent);
  backdrop-filter: blur(4px);
}

.palette__panel {
  position: relative;
  width: min(38rem, 100%);
  background: var(--panel);
  border: 1px solid var(--rule);
  box-shadow: 0 24px 60px rgb(0 0 0 / 0.45);
  display: grid;
}

.palette__input {
  font: inherit;
  font-size: var(--step-2);
  background: transparent;
  color: var(--ink);
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 0.9rem 1rem;
}

.palette__input:focus {
  outline: none;
}

.palette__results {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 24rem;
  overflow-y: auto;
}

.palette__link {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.5rem 1rem;
  text-decoration: none;
}

.palette__result.is-active .palette__link,
.palette__link:hover {
  background: var(--panel-hi);
}

.palette__result.is-active .palette__link {
  box-shadow: inset 2px 0 0 var(--gap);
}

.palette__name {
  color: var(--ink);
}

.palette__meta,
.palette__empty {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.palette__empty {
  padding: 1rem;
}

.palette__hint {
  margin: 0;
  padding: 0.5rem 1rem;
  border-top: 1px solid var(--rule);
  font-size: var(--step--2);
  color: var(--faint);
  display: flex;
  gap: 0.3rem;
  align-items: center;
  flex-wrap: wrap;
}

body.is-searching {
  overflow: hidden;
}

/* ---------- Edit bar and dialogs ---------- */

.editbar {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--panel);
  border: 1px solid var(--rule);
  padding: 0.4rem 0.5rem;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.4);
}

.editbar__btn {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--rule);
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

.editbar__btn:hover {
  color: var(--ink);
  border-color: var(--faint);
}

.editbar__btn--primary {
  background: var(--gap);
  border-color: var(--gap);
  color: var(--ground);
}

.editbar__btn--primary:hover {
  color: var(--ground);
  filter: brightness(1.08);
}

/* The account control: a square button that is nothing but its cog, sized so it
   sits as a sibling of Add Film rather than a shrunken one. */
.editbar__btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.3rem;
  border-color: transparent;
}

.editbar__btn--icon svg {
  display: block;
  width: 1.2rem;
  height: 1.2rem;
}

/* The cog and its menu are one unit, so the popover can anchor to the button. */
.editbar__account {
  position: relative;
  display: inline-flex;
}

/* A small popover that opens upward — the bar sits at the foot of the page — and
   aligns to the bar's right edge. Its own panel, one step brighter than the bar
   behind it so the two read as stacked rather than merged. */
.editbar__menu {
  position: absolute;
  right: 0;
  bottom: calc(100% + 0.5rem);
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-width: 11rem;
  padding: 0.3rem;
  background: var(--panel);
  border: 1px solid var(--rule);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.4);
}

.editbar__menu[hidden] {
  display: none;
}

.editbar__menuitem {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  text-align: left;
  background: transparent;
  color: var(--muted);
  border: 0;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
}

.editbar__menuitem:hover,
.editbar__menuitem:focus-visible {
  color: var(--ink);
  background: var(--rule);
  outline: none;
}

.editbar__note {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--gap);
  max-width: 12rem;
}

/* ---------- Dev tier switcher ---------- */

/* A local-only affordance (site/scripts/tier-toggle.js) for previewing any page
   as any plan. The script only injects it on the loopback dev server, so this
   never shows in production. Pinned to the top-right so it sits clear of the
   fixed .editbar in the opposite corner. */
.tierdev {
  position: fixed;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.15rem;
  padding: 0.2rem 0.3rem;
  background: rgb(0 0 0 / 0.72);
  border: 1px solid var(--rule);
  font-family: var(--data);
  font-size: 0.7rem;
  backdrop-filter: blur(4px);
}

.tierdev__tag {
  color: var(--faint);
  letter-spacing: 0.08em;
  padding: 0 0.15rem;
}

.tierdev__btn {
  font: inherit;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  padding: 0.12rem 0.4rem;
  cursor: pointer;
  line-height: 1.4;
  white-space: nowrap;
}

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

.tierdev__btn[aria-pressed='true'] {
  background: var(--gap);
  color: var(--ground);
}

/* ---------- Profile ---------- */

/* The account page: a short definition list, each value on its own ruled row so
   the page has room to grow downward as billing and export arrive. */
.profile {
  margin: 1.5rem 0 0;
  max-width: 34rem;
}

.profile__row {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.9rem 0;
  border-top: 1px solid var(--rule);
}

.profile__row:last-child {
  border-bottom: 1px solid var(--rule);
}

.profile__key {
  flex: 0 0 7rem;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.profile__val {
  margin: 0;
  font-family: var(--data);
  color: var(--ink);
  word-break: break-word;
}

.profile__loading {
  color: var(--faint);
}

.profile__actions {
  margin-top: 1.75rem;
}

/* ---------- Prose ---------- */

/* Running text on the flatter pages — About, the Pricing intro. Held to a
   readable measure and spaced like paragraphs rather than interface. */
.prose {
  max-width: var(--measure);
  margin: 1.5rem 0 0;
  color: var(--ink);
  line-height: 1.6;
}

.prose p + p {
  margin-top: 1rem;
}

.prose b {
  font-weight: 600;
}

.prose--lead {
  font-size: var(--step-0);
  color: var(--muted);
}

/* ---------- Pricing ---------- */

.pricing-note {
  max-width: var(--measure);
  margin: 1rem 0 0;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--gap);
  line-height: 1.5;
}

.tiers {
  margin-top: 2rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  /* Stretch so all three boxes stand at the height of the tallest, and the
     Subscribe buttons line up along one baseline. */
  align-items: stretch;
}

.tier {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.5rem 1.25rem;
  border: 1px solid var(--rule);
}

/* The middle tier wears the accent the rest of the site keeps for the thing you
   are meant to look at. */
.tier--featured {
  border-color: var(--gap);
}

/* The top tier wears the aurora — the same moving rainbow the stats hero numbers
   and the login walkthrough button carry (stats-aurora over --aurora). A solid
   ground fills the padding-box so only the border burns, exactly as on the button. */
.tier--aurora {
  border: 3px solid transparent;
  background:
    linear-gradient(var(--ground), var(--ground)) padding-box,
    linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd) border-box;
  background-size: auto, 240% 100%;
  background-position: 0 0, var(--aurora) 50%;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}

@media (prefers-reduced-motion: reduce) {
  .tier--aurora { animation: none; --aurora: 30%; }
}

/* The subscribe control on the paid tiers. margin-top:auto drops it to the foot of
   the card so, with the boxes stretched to equal height, the buttons align along
   one baseline. Disabled for now — billing is not wired up during the beta — so it
   reads as the placeholder it is. */
.tier__cta {
  margin-top: auto;
  border: 0;
  border-radius: 8px;
  background: var(--gap);
  color: var(--ground);
  font: inherit;
  font-weight: 600;
  font-size: var(--step--1);
  padding: 0.7rem 1rem;
}
.tier__cta[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.tier__name {
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--ink);
}

.tier__price {
  font-family: var(--display);
  font-size: var(--step-3);
  line-height: 1;
  color: var(--ink);
}

.tier__per {
  margin-left: 0.35rem;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.tier__for {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.tier__list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.tier__list li {
  position: relative;
  padding-left: 1.1rem;
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink);
}

.tier__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 0.4rem;
  height: 0.4rem;
  border: 1px solid var(--faint);
}

@media (max-width: 46rem) {
  .tiers {
    grid-template-columns: 1fr;
  }
}

.dialog {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 1rem;
}

.dialog__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ground) 80%, transparent);
  backdrop-filter: blur(4px);
}

.dialog__panel {
  position: relative;
  width: min(32rem, 100%);
  max-height: 80vh;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--rule);
  padding: 1.25rem;
  box-shadow: 0 24px 60px rgb(0 0 0 / 0.45);
}

.dialog__title {
  font-family: var(--display);
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-size: var(--step-3);
  margin-bottom: 0.75rem;
}

.dialog__close {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  font-size: 1.4rem;
  line-height: 1;
  background: none;
  border: 0;
  color: var(--muted);
  cursor: pointer;
}

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

.dialog__form {
  display: grid;
  gap: 0.75rem;
  justify-items: start;
}

.dialog__label {
  display: grid;
  gap: 0.3rem;
  width: 100%;
  font-size: var(--step--1);
  color: var(--muted);
}

.dialog__error {
  margin: 0;
  color: var(--gap);
  font-size: var(--step--1);
}

.dialog__hint {
  margin: 0.6rem 0 0;
  font-size: var(--step--1);
  color: var(--muted);
}

/* The column guide in the import dialog. Names in the data face against their
   plain-language note, so the shape of a good file is readable at a glance. */
.cols {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 1rem;
  margin: 0.75rem 0 0;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--rule);
  background: var(--panel);
  font-size: var(--step--1);
}

.cols__name {
  font-family: var(--data);
  font-weight: 500;
  color: var(--ink);
}

.cols__note {
  margin: 0;
  color: var(--muted);
}

.cols__aside {
  margin-top: 0.5rem;
}

.dialog__results {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.25rem;
}

/* A result row: poster, the title/year, and the two ways to add it pushed to
   the right. In the Add dialog the row is an <li> and the buttons are the
   controls, so it highlights on hover only to group what you are reading. The
   remap picker on a film page reuses this class on a <button> — the whole row is
   the control there — so the reset below keeps that button looking like a row. */
.dialog__result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.7rem;
  width: 100%;
  text-align: left;
  font: inherit;
  background: transparent;
  color: var(--ink);
  border: 1px solid transparent;
  padding: 0.35rem;
}

button.dialog__result {
  cursor: pointer;
}

.dialog__result:hover {
  background: var(--panel-hi);
  border-color: var(--rule);
}

.dialog__result img {
  flex: none;
  background: var(--ground);
}

.dialog__result-text {
  flex: 1;
  min-width: 8rem;
}

.dialog__year {
  color: var(--muted);
  font-family: var(--data);
  font-size: var(--step--1);
}

/* The pair of add buttons, kept together and pushed to the trailing edge. */
.dialog__result-actions {
  flex: none;
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
}

.dialog__add {
  font: inherit;
  font-family: var(--body);
  font-size: var(--step--1);
  line-height: 1;
  white-space: nowrap;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--rule);
  padding: 0.45rem 0.7rem;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, filter 120ms ease;
}

.dialog__add:hover {
  color: var(--ink);
  border-color: var(--faint);
}

.dialog__add:disabled {
  opacity: 0.5;
  cursor: default;
}

.dialog__add--primary {
  background: var(--gap);
  border-color: var(--gap);
  color: var(--ground);
  font-weight: 600;
}

.dialog__add--primary:hover {
  color: var(--ground);
  filter: brightness(1.08);
}

/* Already yours: shown, so the absence of it from the results is not a puzzle,
   but not offered, because adding it again would do nothing. */
.dialog__result.is-present {
  opacity: 0.55;
}

.dialog__result.is-present:hover {
  background: transparent;
  border-color: transparent;
}

.dialog__present {
  display: block;
  color: var(--muted);
  font-family: var(--data);
  font-size: var(--step--1);
}

.dialog__empty {
  color: var(--muted);
  font-size: var(--step--1);
  padding: 0.5rem 0;
}

/* ---------- Small screens ---------- */

@media (max-width: 60rem) {
  .collection {
    grid-template-columns: 1fr;
  }

  .facets {
    position: static;
    max-height: none;
    overflow: visible;
  }

  .facet__options {
    max-height: 11rem;
  }
}

@media (max-width: 46rem) {
  .grid {
    grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  }

  .editbar {
    left: 1rem;
    right: 1rem;
    justify-content: center;
  }

  .editbar__note {
    max-width: none;
  }
}

/* ---------- Import ---------- */

/* ---------- One film ---------- */

.card__link {
  display: block;
  height: 100%;
  text-decoration: none;
}

.card__titlelink {
  text-decoration: none;
}

.card__titlelink:hover {
  text-decoration: underline;
  text-decoration-color: var(--gap);
}

.detail {
  display: grid;
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
  gap: clamp(1.25rem, 4vw, 2.5rem);
  align-items: start;
  padding-block: clamp(1rem, 3vw, 2rem);
}

.detail__art {
  position: relative;
  aspect-ratio: 2 / 3;
  background: var(--panel);
  overflow: hidden;
}

.detail__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.detail__noart {
  display: grid;
  place-items: center;
  height: 100%;
  padding: 1rem;
  text-align: center;
  color: var(--muted);
  border: 1px solid var(--rule);
}

.detail__title {
  font-family: var(--display);
  font-size: var(--step-4);
  line-height: 1.1;
  margin: 0;
}

.detail__year {
  font-family: var(--data);
  color: var(--muted);
  margin: 0.35rem 0 1rem;
}

.detail__desc {
  max-width: 62ch;
  margin: 0 0 1.25rem;
}

.detail__state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding-block: 0.85rem;
  border-block: 1px solid var(--rule);
}

.detail__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 1.25rem;
  margin: 1.25rem 0;
  font-size: var(--step--1);
}

.detail__facts dt {
  font-family: var(--data);
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.detail__facts dd {
  margin: 0;
  color: var(--ink);
}

/* The Watched row carries its own undo, so the count can be walked back down
   without leaving the page. Quiet until wanted: it sits after the count and
   only colours up on hover. */
.detail__facts .views {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

/* The flex display above outranks the plain [hidden] rule, so an unseen film —
   or one whose last viewing was just removed — would keep an empty row. Say it
   outright at matching specificity so hidden means hidden. */
.detail__facts .views[hidden] {
  display: none;
}

.views__undo {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.03em;
  color: var(--faint);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

.views__undo:hover,
.views__undo:focus-visible {
  color: var(--ink);
}

.detail__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

.detail__note,
.detail__source {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
  margin: 1.25rem 0 0;
}

.credits {
  border-top: 1px solid var(--rule);
  padding-block: 1.5rem 0;
}

.credits__title {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.75rem;
}

.credits__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--rule);
  border-block: 1px solid var(--rule);
}

.credit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem 1rem;
  padding: 0.6rem 0.75rem;
  background: var(--ground);
}

.credit__name {
  font-weight: 500;
  min-width: 0;
}

/* A career page behind the name: an ordinary link, kept quiet so the row still
   reads as a list of people rather than a wall of underlines. */
a.credit__name {
  color: inherit;
  text-decoration: none;
}

a.credit__name:hover,
a.credit__name:focus-visible {
  color: var(--seen);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.credit__role {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

.credit__controls {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
}

/* The Auto badge: a quiet state, not a control. In the seen green so it carries
   a little further than the faint ink around it, but still no border or fill —
   the row's one action stays the heart beside it. */
.credit__auto {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--seen);
  white-space: nowrap;
}

.credit__auto svg {
  width: 0.95em;
  height: 0.95em;
}

/* The favorite heart: one control for everyone, earned or not. An outline in
   the muted ink until chosen, then filled in the seen green — the same green
   every other "on Scout" state on the site already wears. */
.credit__fav {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  white-space: nowrap;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--rule);
  padding: 0.35rem 0.7rem;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}

.credit__fav svg {
  width: 1em;
  height: 1em;
}

.credit__fav:hover,
.credit__fav--on {
  color: var(--seen);
  border-color: color-mix(in srgb, var(--seen) 45%, var(--rule));
}

.credit__fav:disabled {
  cursor: default;
  opacity: 0.6;
}

/* On a read-only copy the heart is a plain state, so it must not carry the
   button's frame and look like something still waiting to be pressed. */
.credit__fav--static {
  border-color: transparent;
  padding-inline: 0;
  cursor: default;
}

.detail__missing {
  padding-block: 3rem;
  color: var(--muted);
}

/* Destructive, so it is outlined in the gap colour rather than filled: it
   should be findable without competing with the primary action anywhere. */
.page-actions__btn--danger {
  border-color: color-mix(in srgb, var(--gap) 45%, var(--rule));
  color: var(--gap);
}

.page-actions__btn--danger:hover {
  border-color: var(--gap);
  color: var(--gap);
  background: color-mix(in srgb, var(--gap) 12%, transparent);
}

.dialog__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

@media (max-width: 46rem) {
  .detail {
    grid-template-columns: minmax(0, 1fr);
  }

  .detail__art {
    max-width: 14rem;
  }
}

/* ---------- Getting started ---------- */

/* With no films there is nothing to filter, sort or count, so the controls get
   out of the way rather than sitting there greyed out over an empty grid. */
.collection-bare .facets,
.collection-bare .grid-bar {
  display: none;
}

.collection-bare .collection {
  display: block;
}

.onboard {
  max-width: 34rem;
  margin: clamp(1.5rem, 5vw, 3.5rem) auto;
  padding: clamp(1.25rem, 3vw, 2rem);
  border: 1px solid var(--rule);
  background: var(--panel);
  text-align: center;
}

.onboard__title {
  font-family: var(--display);
  font-size: var(--step-3);
  margin: 0 0 0.5rem;
}

.onboard__note {
  color: var(--muted);
  margin: 0 auto 1.25rem;
}

.onboard__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
}

.onboard__actions:empty {
  display: none;
}

.onboard__aside {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
  margin: 0;
}

.onboard__aside code {
  color: var(--muted);
}

.dropzone {
  display: grid;
  place-items: center;
  gap: 0.35rem;
  margin-top: 0.9rem;
  padding: 2rem 1rem;
  border: 1px dashed var(--faint);
  background: var(--ground);
  color: var(--muted);
  text-align: center;
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}

.dropzone:hover,
.dropzone:focus-visible {
  border-color: var(--gap);
  color: var(--ink);
}

.dropzone.is-over {
  border-color: var(--gap);
  border-style: solid;
  background: var(--panel-hi);
  color: var(--ink);
}

.dropzone.is-busy {
  cursor: progress;
  border-style: solid;
  color: var(--gap);
}

.dropzone__text {
  font-size: var(--step-1);
}

/* ---------- Import progress ---------- */

.progress {
  margin-top: 0.9rem;
}

.progress__label {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0 0 0.4rem;
}

.progress__track {
  height: 4px;
  background: var(--gap-track);
  overflow: hidden;
}

.progress__fill {
  height: 100%;
  width: 0;
  background: var(--gap);
  transition: width 300ms ease;
}

/* No total to divide by — a phase that cannot be counted, or a backend that
   does not report at all. A sweep says "working" without claiming to know how
   much is left, which a half-filled bar would. */
.progress--sweeping .progress__fill {
  width: 35%;
  transition: none;
  animation: progress-sweep 1.1s ease-in-out infinite;
}

@keyframes progress-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(340%); }
}

/* Someone who has asked not to see motion still gets the bar, just still. */
@media (prefers-reduced-motion: reduce) {
  .progress--sweeping .progress__fill {
    animation: none;
    width: 100%;
    opacity: 0.4;
  }
}

.dropzone__formats {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ---------- Watchlist ---------- */

/* The queue reuses the collection's card wholesale: a poster is a poster, and
   learning a second one would be work for the reader with nothing behind it.
   What changes is the ink. Everything here is unseen by definition, so the
   titles carry the gap colour rather than the collection's neutral, and the
   grid runs a size larger because a shortlist is meant to be looked at rather
   than scanned. */

.card--queued .card__title {
  color: var(--ink);
}

.card--queued .card__art {
  outline-color: color-mix(in srgb, var(--gap) 22%, transparent);
}

.grid--queue {
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
}

/* Off the list is a quiet action, not a destructive one: nothing is deleted
   and nothing about the film changes, so it sits back until reached for. */
.queue__remove {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  justify-self: start;
  margin-top: 0.15rem;
  background: transparent;
  color: var(--faint);
  border: 1px solid transparent;
  padding: 0.2rem 0.4rem;
  margin-inline-start: -0.4rem;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}

.queue__remove:hover {
  color: var(--gap);
  border-color: color-mix(in srgb, var(--gap) 35%, transparent);
}

.queue__remove:disabled {
  cursor: progress;
  color: var(--faint);
}

.queue-empty {
  color: var(--muted);
  padding-block: 3rem;
  max-width: var(--measure);
}

/* Queued is a state, not an action, so it reads as the seen colour rather than
   as a filled button still waiting to be pressed. Matches .credit__fav--on. */
.page-actions__btn--on {
  color: var(--seen);
  border-color: color-mix(in srgb, var(--seen) 45%, var(--rule));
}

.page-actions__btn--on:hover {
  color: var(--seen);
  border-color: var(--seen);
  background: color-mix(in srgb, var(--seen) 12%, transparent);
}

/* ---------- Badges ---------- */

/* The wall keeps the file's one rule: colour means seen or missing, never
   rank. An earned badge wears the same filled mark a watched film wears, and a
   locked one is drawn in the gap ink like a career still short of done. There
   is no gold, silver or bronze here, because a tier is not a state and the
   palette has no way to say it that would not also say "watched". */

.tally {
  display: grid;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

.tally__count {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step-1);
  color: var(--muted);
}

/* The two counts carry the aurora the hero numbers wear — big and lit, with the
   connective words left small and muted around them so the numbers read as the
   headline of the page. */
.tally__count b {
  font-family: var(--display);
  font-size: var(--step-4);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  background: linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd);
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}

/* The page's own bar runs the whole family spectrum rather than one ink,
   because it is the one measure that belongs to every section at once. The
   gradient is scaled into the filled width instead of being clipped out of a
   full-width one, so all five hues are visible however little is earned. */
.tally__bar {
  height: 8px;
  border-radius: 999px;
  background:
    linear-gradient(to right, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481) left / var(--pct) 100%
      no-repeat,
    var(--gap-track);
  --pct: var(--target);
}

/* What is nearest to falling. The same job Scout's "Closest to done" does, so
   it is set the same way: quiet, and reading as a list of leads rather than a
   scoreboard. */
.tally__next {
  margin: 0;
  font-size: var(--step--1);
  color: var(--faint);
}

.tally__next b {
  color: var(--gap);
  font-weight: 500;
}

/* All a family heading carries. Set in the data face at the supporting size so
   it reads as a count against the label above it, not as a sentence. */
.section__tally {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
  white-space: nowrap;
}

/* One hue per family, each tuned to about 7:1 on the ground so no section
   shouts louder than its neighbours — the same weight --seen and --gap carry.
   They run cool to warm down the page rather than being picked one at a time,
   which is what stops five accent colours reading as five decisions. */
.section--family[data-family='volume'] {
  --fam: #5dadbd;
}
.section--family[data-family='reach'] {
  --fam: #90a0d2;
}
.section--family[data-family='time'] {
  --fam: #ab97d4;
}
.section--family[data-family='endurance'] {
  --fam: #cf89cf;
}
/* Terracotta rather than the rose this started as: at H340 it sat 40 degrees
   off Endurance's orchid and the two read as one pink. Swinging it past the
   warm end puts 75 degrees between them, and ending the page hot suits the
   family that takes the longest to earn anything in. */
.section--family[data-family='depth'] {
  --fam: #cc9481;
}
/* Palate and Ritual close the wall, so they step off the cool-to-warm run onto
   two earthy tones held to the same ~7:1 weight: a gold for taste, an olive for
   the calendar. Distinct from the five above without any of them shouting. */
.section--family[data-family='palate'] {
  --fam: #c6a052;
}
.section--family[data-family='ritual'] {
  --fam: #9fb06a;
}

.section--family .section__title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--fam);
}

/* The glyph is the family's face. Hidden from assistive tech in the markup,
   because the word it sits beside already says it. */
.section__icon {
  display: inline-grid;
  place-items: center;
  color: var(--fam);
}

.section__icon svg {
  width: 18px;
  height: 18px;
}

.section--family .section__tally b {
  color: var(--fam);
  font-weight: 500;
}

/* The block wears its family outright: a solid rule across the top, and the
   hairlines between cards tinted so the grid itself belongs to the section
   rather than sitting in a neutral cage. */
.section--family .badges {
  border-top: 3px solid var(--fam);
  border-right-color: color-mix(in srgb, var(--fam) 28%, var(--rule));
  border-bottom-color: color-mix(in srgb, var(--fam) 28%, var(--rule));
  border-left-color: color-mix(in srgb, var(--fam) 28%, var(--rule));
  background: color-mix(in srgb, var(--fam) 20%, var(--rule));
}

.section__tally b {
  color: var(--muted);
  font-weight: 500;
}

/* A family the plan cannot reveal (site/render.js lockedFamily): the real grid
   is replaced by blurred card silhouettes with an unlock prompt centred over
   them — the same bargain the locked stats bands make. It still wears its family
   hue, so a Free wall reads as a specific "the Reach badges are behind Film Buff"
   rather than an empty gap. --hue feeds the shared .stats-lock chip and the
   shimmer, both of which key off it. */
.section--family.section--locked {
  --hue: var(--fam);
}

.badges-locked {
  position: relative;
}

.badges--blurred {
  filter: blur(6px);
  opacity: 0.55;
  pointer-events: none;
  user-select: none;
}

/* The shared .stats-lock clears a band's own head (3.5rem down); here the head
   sits outside this wrapper, so the prompt covers the whole blurred grid. */
.badges-locked .stats-lock {
  inset-block: 0;
}

/* A ghost card: the badge shell with shimmer bars where its text would be, so
   the blur has the wall's shape under it rather than a flat panel. */
.badge--ghost {
  gap: 0.55rem;
  min-height: 7.5rem;
}

.badges {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1px;
  /* One-pixel gaps over the rule colour: the cards tile into a single block
     with hairlines between them rather than floating as separate chips, which
     is how the board reads and how a wall should. */
  background: var(--rule);
  border: 1px solid var(--rule);
}

/* Family hue carries which section a card is in; fill and weight carry whether
   it is earned. Splitting the two jobs is what lets the wall be this colourful
   without the state becoming a guess — an earned card is the lit one whatever
   hue it happens to be, and the difference survives being read in greyscale. */
.badge {
  display: grid;
  align-content: start;
  gap: 0.4rem;
  padding: 1rem;
  /* Locked is the resting state and the majority of the wall, so it is held
     back rather than struck out: the thresholds are meant to be a map of what
     is ahead, not a list of failures. A trace of the family hue keeps it part
     of its section rather than dead. */
  background: color-mix(in srgb, var(--fam, var(--muted)) 4%, var(--panel));
  color: var(--faint);
}

.badge--earned {
  background: color-mix(in srgb, var(--fam, var(--seen)) 16%, var(--panel));
  color: var(--muted);
}

.badge__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.badge__name {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--muted);
  text-wrap: balance;
}

.badge--earned .badge__name {
  color: var(--ink);
}

/* The mark keeps the site's two forms — filled for on, outline for off — but
   takes the family hue rather than the seen green. A badge is not a film: it
   never appears in the grid beside a poster, so there is nothing here for the
   green to stay consistent with, and the form alone already says which state
   this is. */
.badge .mark {
  flex: none;
  font-size: var(--step--2);
  align-self: start;
  color: color-mix(in srgb, var(--fam, var(--gap)) 70%, var(--ink));
  /* The earned mark is a glyph beside a word, so the chip has to lay them out
     rather than rely on the space between two characters. */
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
}

/* Sized in em so it tracks the chip's text rather than the 16px the icon
   module hands every glyph by default. */
.badge .mark svg {
  width: 1.05em;
  height: 1.05em;
}

.badge--earned .mark {
  /* Dark text on the hue rather than white: these are light, and at 7:1 on the
     ground the ground itself is the legible ink on top of them. */
  color: var(--ground);
  background: var(--fam, var(--seen-solid));
  padding: 0.2rem 0.45rem;
  font-weight: 600;
}

/* What it takes. Stated on every card, earned or not: a badge whose
   requirement disappears once you have it is a badge you cannot explain to
   anyone later. */
.badge__req {
  margin: 0;
  font-size: var(--step--1);
  color: var(--faint);
  text-wrap: pretty;
}

/* An earned card's tint lifts the background enough that --faint stops
   clearing 4.5:1 on it. The supporting text moves up a step rather than the
   tint coming down, because the tint is the point. */
.badge--earned .badge__req,
.badge--earned .badge__detail {
  color: var(--muted);
}

/* A full bar in the family hue: this one is done. */
.badge__bar {
  height: 3px;
  margin-top: 0.15rem;
  background-image: linear-gradient(
    to right,
    var(--fam, var(--seen)) 0 var(--pct),
    color-mix(in srgb, var(--fam, var(--seen)) 14%, var(--panel)) var(--pct) 100%
  );
  --pct: var(--target);
}

/* Part way there. Same hue, held at half strength, so a locked card reads as
   the quieter version of its own section rather than as a different colour
   scheme — the distance to the target is the message, not the ink. */
.badge:not(.badge--earned) .badge__bar {
  background-image: linear-gradient(
    to right,
    color-mix(in srgb, var(--fam, var(--gap)) 55%, transparent) 0 var(--pct),
    color-mix(in srgb, var(--fam, var(--gap)) 12%, var(--panel)) var(--pct) 100%
  );
}

.badge__value {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.badge--earned .badge__value {
  color: var(--fam, var(--seen));
  font-weight: 500;
}

/* The thing a count cannot say: which decade is missing, whose career is your
   largest. Only some badges have one, so it never leaves a gap. */
.badge__detail {
  margin: 0;
  font-size: var(--step--2);
  color: var(--faint);
  text-wrap: pretty;
}

@media (prefers-reduced-motion: no-preference) {
  .badge__bar,
  .tally__bar {
    transition: --pct 700ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }
}

@media (max-width: 34rem) {
  .badges {
    grid-template-columns: 1fr;
  }
}

/* ---- Import report ---- */

.import-report__heading {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--gap);
  margin: 1rem 0 0.5rem;
}

.import-fails {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  max-height: 16rem;
  overflow-y: auto;
  border: 1px solid var(--rule);
  border-radius: 8px;
}

.import-fails__row {
  display: block;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--rule);
}

.import-fails__row:last-child {
  border-bottom: 0;
}

.import-fails__raw {
  display: block;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--ink);
  overflow-wrap: anywhere;
}

.import-fails__why {
  display: block;
  font-size: var(--step--2);
  color: var(--muted);
  margin-top: 0.15rem;
}

.import-report__actions {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* ================================================================
   Stats
   The one page allowed to be loud. Where the rest of the site holds
   colour to two data inks, the dashboard runs the whole family
   spectrum: each band owns a hue, the headline burns through all of
   them, and the extremes get to shimmer. Identity and delight, not
   state — no number here means seen or missing.
   ================================================================ */

/* The animated stops the hero gradient rides on. Registered so the sweep is
   animatable at all; two of them, offset, so the light travels across. */
@property --aurora {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 0%;
}

.stats {
  /* A touch more air below the headline than the shell gives by default: the
     hero is the loudest thing on the site and wants room to land. */
  padding-bottom: clamp(2rem, 5vw, 4rem);
}

/* Every band's hue, keyed the same way the badge families are, so a Stats
   section and its sibling on the wall read as one colour. Cool → warm down
   the page. */
.stats-section[data-hue='volume'],
.stat-tile:nth-child(6n + 1) { --hue: #5dadbd; }
.stats-section[data-hue='reach'],
.stat-tile:nth-child(6n + 2) { --hue: #90a0d2; }
.stats-section[data-hue='time'],
.stat-tile:nth-child(6n + 3) { --hue: #ab97d4; }
.stats-section[data-hue='endurance'],
.stat-tile:nth-child(6n + 4) { --hue: #cf89cf; }
.stats-section[data-hue='depth'],
.stat-tile:nth-child(6n + 5) { --hue: #cc9481; }
.stats-section[data-hue='palate'],
.stat-tile:nth-child(6n + 6) { --hue: #c6a052; }
.stats-section[data-hue='ritual'] { --hue: #9fb06a; }
/* Aurora — the collaborations band's own family. Its arcs run a green→violet
   gradient; the chrome (lead, captions, counts) takes the teal-cyan the aurora
   opens on, so the band reads as one thing. */
.stats-section[data-hue='aurora'] { --hue: #57c4c9; }

/* ---------- Hero ---------- */

.stats-hero {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: end;
  margin-block: clamp(1rem, 3vw, 2.25rem) clamp(1.5rem, 4vw, 3rem);
}

.stats-hero__stat {
  min-width: 0;
}

.stats-hero__value {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-6);
  line-height: 0.95;
  letter-spacing: -0.02em;
  display: flex;
  align-items: baseline;
  gap: 0.35ch;
  flex-wrap: wrap;
}

/* The signature effect: the number is a window onto the whole family spectrum,
   and the light drifts across it. Clipped to the glyphs, so it is the digits
   themselves that burn rather than a box behind them. */
.stats-hero__num {
  background: linear-gradient(
    100deg,
    #5dadbd,
    #90a0d2,
    #ab97d4,
    #cf89cf,
    #cc9481,
    #c6a052,
    #5dadbd
  );
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}

@keyframes stats-aurora {
  from { --aurora: 0%; }
  to { --aurora: 100%; }
}

.stats-hero__unit {
  font-family: var(--display);
  font-size: var(--step-2);
  font-weight: 500;
  color: var(--muted);
  letter-spacing: 0;
}

.stats-hero__label {
  margin: 0.35rem 0 0;
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.stats-hero__sub {
  margin: 0.5rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
}

/* ---------- Headline tiles ---------- */

.stat-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 2px;
  overflow: hidden;
}

.stat-tile {
  position: relative;
  padding: clamp(1rem, 2.2vw, 1.5rem);
  background: var(--panel);
  transition: background 160ms ease, transform 160ms ease;
  /* The hue arrives as a hairline down the leading edge and a wash behind, so a
     tile belongs to a colour without shouting it. */
  box-shadow: inset 3px 0 0 var(--hue);
}

.stat-tile::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--hue) 12%, transparent), transparent 60%);
  opacity: 0.9;
}

.js .stat-tile:hover {
  background: var(--panel-hi);
  transform: translateY(-2px);
}

.stat-tile__value {
  position: relative;
  margin: 0;
  font-family: var(--data);
  font-size: var(--step-3);
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
}
/* A unit that reads level with its number — the star glyph is drawn short, so
   it is sized up to meet the digits it follows. */
.stat-tile__unit {
  font-size: 1.15em;
  line-height: 1;
}

.stat-tile__label {
  position: relative;
  margin: 0.5rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}

.stat-tile__sub {
  position: relative;
  margin: 0.3rem 0 0;
  font-size: var(--step--2);
  color: var(--faint);
}

/* The Free tease: a real number held behind glass. Blurred and unselectable,
   so the shape is there and the value is not quite. */
.stat-tile--locked .stat-tile__value {
  filter: blur(7px);
  opacity: 0.7;
  user-select: none;
}

.stat-tile--locked .stat-tile__sub {
  filter: blur(4px);
  user-select: none;
}

/* ---------- Section bands ---------- */

.stats-section {
  position: relative;
}

.stats-section__title {
  /* Off the uppercase-muted default onto the band's own hue, the way a badge
     family heading wears --fam. Flex so the glyph sits centred against the
     label rather than on its baseline, with a touch more air between them. */
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--hue);
  letter-spacing: 0.14em;
}

.stats-section .section__head {
  border-top-color: color-mix(in srgb, var(--hue) 40%, var(--rule));
}

.stats-section .section__icon {
  color: var(--hue);
}

/* ---------- Placeholders ---------- */

.stats-placeholder {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 0.5rem 0 0.25rem;
}

.skel {
  height: 0.9rem;
  width: var(--w, 80%);
  border-radius: 999px;
  /* A hue-tinted track with a brighter band travelling across it: content that
     is on its way rather than absent. */
  background:
    linear-gradient(
      90deg,
      color-mix(in srgb, var(--hue) 14%, var(--panel)) 0%,
      color-mix(in srgb, var(--hue) 32%, var(--panel)) 40%,
      color-mix(in srgb, var(--hue) 14%, var(--panel)) 80%
    )
    color-mix(in srgb, var(--hue) 14%, var(--panel));
  background-size: 220% 100%;
  animation: stats-shimmer 1.8s ease-in-out infinite;
}

.skel:nth-child(2) { animation-delay: 0.15s; }
.skel:nth-child(3) { animation-delay: 0.3s; }
.skel:nth-child(4) { animation-delay: 0.45s; }

@keyframes stats-shimmer {
  0% { background-position: 120% 0; }
  100% { background-position: -120% 0; }
}

.stats-placeholder__note {
  margin: 0.75rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ---------- Lazy band loader ---------- */

/* A lazy band ships as this shimmer; stats.js swaps the built band into the same
   [data-stats-body] slot when the section nears the fold. A held min-height keeps
   the page below from jumping as the real content lands. */
.stats-loading {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 0.5rem 0 0.25rem;
  min-height: 12rem;
}

/* The chart-shaped block atop the loader's text rows. */
.skel--chart {
  height: 8rem;
  border-radius: 10px;
}

/* ---------- Locked bands ---------- */

.stats-placeholder--blurred {
  filter: blur(6px);
  opacity: 0.6;
}

/* The unlock prompt sits centred over the blurred shape, so the band reads as a
   specific thing held back rather than an empty gap. */
.stats-lock {
  position: absolute;
  inset-block: 3.5rem 0;
  inset-inline: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  text-align: center;
  padding: 1rem;
}

.stats-lock__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--data);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 16%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 40%, var(--rule));
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
}

.stats-lock__chip svg {
  width: 14px;
  height: 14px;
}

.stats-lock__note {
  margin: 0;
  font-size: var(--step--1);
  color: var(--muted);
}

.stats-lock__note b {
  color: var(--ink);
  font-weight: 600;
}

/* Motion off: the light stops travelling, but the spectrum and the tints stay,
   so the page is still in full colour — just still. */
@media (prefers-reduced-motion: reduce) {
  .stats-hero__num,
  .tally__count b {
    animation: none;
    --aurora: 30%;
  }
  .skel {
    animation: none;
  }
}

/* ---------- Watch time & pace ---------- */

.pace {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* Each control rises in on load, staggered, so the band assembles itself rather
   than snapping in whole. The global reduced-motion rule stills it. */
.pace > *,
.pace__rhythm > * {
  animation: stats-rise 0.5s ease both;
}
.pace__aside { animation-delay: 0.08s; }
.pace__chips { animation-delay: 0.16s; }

@keyframes stats-rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.pace__rhythm {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 46rem) {
  .pace__rhythm {
    grid-template-columns: minmax(0, 1fr) 12.5rem;
    /* Stretch, so the calendar fills the whole height the aside sets rather than
       sitting short with a gap beneath it. */
    align-items: stretch;
  }
}

.pace__aside {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.pace__aside-cap,
.heatmap__cap > span:first-child {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}

/* ---- Calendar heatmap ---- */

.heatmap {
  --cell: 13px;
  margin: 0;
  padding: 1rem 1.1rem 0.85rem;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  border-radius: 3px;
  /* A column so the grid between caption and legend can flex to fill the panel. */
  display: flex;
  flex-direction: column;
}

.heatmap__cap {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.25rem 1rem;
  margin-bottom: 0.85rem;
}

.heatmap__range {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}

/* Fill the space between caption and legend, and centre the plot in it: the
   cells stay square, and the height the aside sets becomes balanced padding
   above and below rather than an empty gap underneath. */
/* The plot fills the panel: caption above, legend below, this the stretch in
   between. No centering — the grid itself grows to fill the height. */
.heatmap__scroll {
  flex: 1;
  display: flex;
  min-height: 7rem;
}

.heatmap__plot {
  flex: 1;
  min-width: 0;
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
}

/* Seven equal rows, matched to the grid's, so the weekday labels line up with
   their cells however tall the panel is. */
.heatmap__days {
  display: grid;
  grid-template-rows: repeat(7, 1fr);
  align-items: center;
  font-family: var(--data);
  font-size: 8px;
  color: var(--faint);
  flex: none;
}

/* Only the active year is shown; the rest stay in the DOM but hidden, so the
   pills page between them with no rebuild. */
.heatmap__years {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
}

.heatmap__grid {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: grid;
  grid-auto-flow: column;
  /* Rows fill the height as equal tracks; columns fill the width one per week —
     so the whole year fits the panel in both directions, never scrolling. The
     square cells sit centred in the taller row tracks, and that breathing room
     between them is what the extra height becomes instead of dead space. */
  grid-template-rows: repeat(7, 1fr);
  grid-template-columns: repeat(var(--weeks, 53), minmax(0, 1fr));
  column-gap: 3px;
}

.heatmap__grid[hidden] {
  display: none;
}

.heatmap__cell {
  aspect-ratio: 1;
  width: 100%;
  align-self: center;
  border-radius: 2px;
}

.heatmap__cell,
.heatmap__key {
  /* A single-hue ramp: more films, more of the section's colour. Level 0 is the
     track — the hue held right back so an empty day still belongs to the grid. */
  background: color-mix(in srgb, var(--hue) 7%, var(--panel-hi));
}

.heatmap__key {
  width: var(--cell);
  height: var(--cell);
  border-radius: 2px;
}

/* Days spilling out of the year, kept only to square off the first and last
   weeks. Dimmed so the eye reads the Jan–Dec block, not the ragged edges. */
.heatmap__cell[data-out] {
  opacity: 0.25;
}

/* ---- Year pills ---- */

.heatmap__years-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-right: auto;
  margin-left: 0.25rem;
}

.heatmap__year-pill {
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--2);
  line-height: 1;
  color: var(--faint);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0.2rem 0.5rem;
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}

.heatmap__year-pill:hover {
  color: var(--ink);
}

.heatmap__year-pill[aria-pressed='true'] {
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 14%, var(--panel));
  border-color: color-mix(in srgb, var(--hue) 40%, var(--rule));
}
.heatmap__cell[data-level='1'], .heatmap__key[data-level='1'] { background: color-mix(in srgb, var(--hue) 28%, var(--panel)); }
.heatmap__cell[data-level='2'], .heatmap__key[data-level='2'] { background: color-mix(in srgb, var(--hue) 48%, var(--panel)); }
.heatmap__cell[data-level='3'], .heatmap__key[data-level='3'] { background: color-mix(in srgb, var(--hue) 70%, var(--panel)); }
.heatmap__cell[data-level='4'], .heatmap__key[data-level='4'] { background: color-mix(in srgb, var(--hue) 92%, var(--panel)); }

.heatmap__cell {
  transition: transform 100ms ease, outline-color 100ms ease;
  outline: 1px solid transparent;
}
.js .heatmap__cell:hover {
  transform: scale(1.35);
  outline-color: var(--ink);
}

.heatmap__legend {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 0.8rem;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}
.heatmap__legend .heatmap__key { --cell: 11px; }
.heatmap__legend > span:first-child { margin-right: 2px; }
.heatmap__legend > span:last-child { margin-left: 2px; }

/* ---- Weekday rose ---- */

.rose {
  margin: 0;
  text-align: center;
}

.rose__svg {
  width: 100%;
  max-width: 140px;
  height: auto;
  margin: 0.3rem auto 0.15rem;
  display: block;
}

.rose__ring {
  fill: none;
  stroke: color-mix(in srgb, var(--hue) 18%, var(--rule));
  stroke-width: 1;
}

.rose__hub {
  fill: color-mix(in srgb, var(--hue) 14%, var(--panel));
  stroke: color-mix(in srgb, var(--hue) 30%, var(--rule));
  stroke-width: 1;
}

.rose__spoke {
  stroke: color-mix(in srgb, var(--hue) 60%, var(--panel-hi));
  stroke-width: 9;
  stroke-linecap: round;
}

.rose__spoke--peak {
  stroke: var(--hue);
}

.rose__label {
  fill: var(--faint);
  font-family: var(--data);
  font-size: 8px;
  text-anchor: middle;
  dominant-baseline: middle;
}

.rose__peak {
  margin: 0.2rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--muted);
}
.rose__peak b {
  color: var(--hue);
  font-weight: 500;
}

/* ---- Streak / records meter ---- */

.streak {
  padding: 0.9rem 1rem;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  border-radius: 3px;
  box-shadow: inset 3px 0 0 var(--hue);
}

.streak__hero {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}

.streak__num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-4);
  color: var(--hue);
}

.streak__unit {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  margin-top: 0.1rem;
}

.streak__cap {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 0.35rem;
}

.streak__line {
  margin: 0.7rem 0 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}
.streak__line b { color: var(--ink); font-weight: 500; }
.streak__line span { color: var(--faint); }

/* ---- Screen-time chips (shared by the ratings chip strip) ---- */

.pace__chips,
.ratings__chips,
.eras__chips,
.world__chips,
.genres__chips,
.people__chips,
.completion__chips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1px;
  margin: 0;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
}

.chip {
  padding: 0.85rem 1rem;
  background: var(--panel);
}

.chip__label {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 50%, var(--muted));
}

.chip__value {
  margin: 0.3rem 0 0;
  font-family: var(--data);
  font-size: var(--step-2);
  font-weight: 500;
  color: var(--ink);
}
.chip__unit {
  font-size: var(--step--1);
  color: var(--muted);
  margin-left: 0.25rem;
}
/* The star reads level with its number, not shrunk like a word unit. */
.chip__star {
  font-size: 1.2em;
  line-height: 1;
}

.chip__note {
  margin: 0.25rem 0 0;
  font-size: var(--step--2);
  color: var(--faint);
}

/* ---- Rhythm empty state ---- */

.pace__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}

.pace__empty-art {
  display: grid;
  grid-template-columns: repeat(7, 12px);
  gap: 3px;
  flex: none;
}
.pace__empty-art span {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--hue) 8%, var(--panel-hi));
}
.pace__empty-art span[data-level='2'] {
  background: color-mix(in srgb, var(--hue) 42%, var(--panel));
}

.pace__empty-text { flex: 1 1 16rem; }

.pace__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}

.pace__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}

/* ================================================================
   Ratings band
   The same two-column split the pace band uses — a main figure beside
   a narrow aside — then a full-width picks row and the chip strip.
   ================================================================ */

.ratings,
.eras,
.world,
.genres,
.people,
.completion,
.gaps,
.collab {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* The same staggered rise-in as the pace band; reduced-motion stills it. */
.ratings > *,
.eras > *,
.world > *,
.genres > *,
.people > *,
.completion > *,
.gaps > *,
.collab > * { animation: stats-rise 0.5s ease both; }
.ratings__picks { animation-delay: 0.08s; }
.ratings__chips,
.eras__chips,
.world__chips,
.genres__peak,
.people__switch { animation-delay: 0.08s; }
.genres__chips,
.people__chips { animation-delay: 0.16s; }

.ratings__split {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 46rem) {
  .ratings__split {
    grid-template-columns: minmax(0, 1fr) 12.5rem;
    align-items: stretch;
  }
  /* With no crowd data there is no aside, so the histogram takes the width. */
  .ratings__split--solo { grid-template-columns: 1fr; }
}

.ratings__aside {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* ---- Distribution histogram; the eras timeline reuses the framed panel ---- */

.ratings__hist,
.eras__timeline,
.world__map,
.world__reach,
.genres__map,
.completion__field,
.completion__gauge,
.completion__roll {
  margin: 0;
  padding: 1rem 1.1rem 0.85rem;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  border-radius: 3px;
  display: flex;
  flex-direction: column;
}

.hist {
  flex: 1;
  min-height: 7rem;
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  align-items: end;
  gap: 4px;
  margin: 0.85rem 0 0.5rem;
}

/* ---- Eras timeline: a ridgeline bookended by the oldest and newest posters ---- */

.timeline {
  flex: 1;
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  margin: 0.85rem 0 0.5rem;
}

/* The two ends: a poster with its year and which end it marks. */
.timeline__end {
  flex: none;
  width: 46px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  text-decoration: none;
  color: inherit;
}
.timeline__poster {
  width: 46px;
  height: 69px;
  border-radius: 2px;
  object-fit: cover;
  background: var(--panel-hi);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.timeline__poster--empty { display: block; }
.timeline__end-year {
  font-family: var(--data);
  font-size: var(--step--2);
  font-weight: 500;
  color: var(--ink);
  line-height: 1;
}
.timeline__end-cap {
  font-family: var(--data);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
  line-height: 1;
}
.timeline__end:hover .timeline__end-year { color: var(--hue); }

.timeline__chart {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.ridge {
  display: block;
  width: 100%;
  height: 120px;
  /* the floor the range stands on */
  border-bottom: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
}
.ridge__line {
  fill: none;
  stroke: var(--hue);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.ridge__stop-top { stop-color: var(--hue); stop-opacity: 0.5; }
.ridge__stop-bot { stop-color: var(--hue); stop-opacity: 0; }

/* Ends of the axis anchor to the chart's edges; the rest space out between. */
.timeline__axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--data);
  font-size: 0.66rem;
  line-height: 1;
  color: color-mix(in srgb, var(--faint) 65%, transparent);
}
.timeline__tick--century { color: var(--faint); }

.hist__col {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  gap: 0.35rem;
}

.hist__bar {
  width: 100%;
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--hue) 34%, var(--panel-hi));
}
.hist__bar--peak { background: var(--hue); }
/* A zero bucket is only a faint baseline nub, so the gap in your scoring shows. */
.hist__bar--empty {
  height: 2px;
  background: color-mix(in srgb, var(--hue) 14%, var(--rule));
}

.hist__tick {
  font-family: var(--data);
  font-size: 0.7rem;
  color: color-mix(in srgb, var(--faint) 70%, transparent);
  line-height: 1;
  white-space: nowrap;
}
/* The whole stars anchor the axis, so they sit a shade brighter than the halves. */
.hist__tick--whole { color: var(--faint); }

.ratings__avg,
.eras__peak,
.genres__peak,
.people__lead,
.records__lead,
.completion__lead,
.gaps__lead,
.collab__lead {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}
.ratings__avg b,
.eras__peak b,
.genres__peak b,
.people__lead b,
.records__lead b,
.completion__lead b,
.gaps__lead b,
.collab__lead b {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-2);
  color: var(--hue);
}
.ratings__avg span,
.eras__peak span,
.genres__peak span,
.people__lead span,
.records__lead span,
.completion__lead span,
.gaps__lead span,
.collab__lead span { color: var(--faint); }

/* ---- Grader meter (you vs the crowd) ---- */

.grader {
  flex: 1;
  padding: 0.9rem 1rem;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  border-radius: 3px;
  box-shadow: inset 3px 0 0 var(--hue);
  display: flex;
  flex-direction: column;
}

.grader__hero {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}

.grader__num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-4);
  color: var(--hue);
}
/* The star matches the number's height, not a shrunk superscript. */
.grader__star {
  font-size: 1em;
  margin-left: 0.1em;
}

.grader__unit {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  margin-top: 0.2rem;
}

/* The box's title, heading it like the histogram's caption sits atop its plot. */
.grader__cap {
  display: block;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
  margin: 0 0 0.5rem;
}

.grader__line {
  margin: auto 0 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}
.grader__line b { color: var(--ink); font-weight: 500; }

/* ---- Divergence picks ---- */

.ratings__picks {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
}
@media (min-width: 34rem) {
  .ratings__picks { grid-template-columns: 1fr 1fr; }
}

.pick {
  margin: 0;
  padding: 0.85rem 1rem;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.pick__cap {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}

.pick__film {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  text-decoration: none;
  color: inherit;
}

.pick__poster {
  flex: none;
  width: 46px;
  height: 69px;
  border-radius: 2px;
  object-fit: cover;
  background: var(--panel-hi);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.pick__poster--empty { display: block; }

.pick__meta {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.pick__title {
  font-size: var(--step--1);
  color: var(--ink);
  font-weight: 500;
  line-height: 1.2;
}
.pick__year { color: var(--faint); font-weight: 400; }

.pick__scores {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}
.pick__scores b { color: var(--hue); font-weight: 600; }
/* At this small size the star glyph renders shorter than the digits, so it is
   nudged up to sit level with the score it follows. */
.pick__star { font-size: 1.2em; line-height: 1; }

.pick__film:hover .pick__title { color: var(--hue); }

/* ---- Ratings empty state ---- */

.ratings__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}

.ratings__empty-art {
  display: grid;
  grid-template-columns: repeat(8, 10px);
  align-items: end;
  gap: 3px;
  height: 64px;
  flex: none;
}
.ratings__empty-art span {
  width: 10px;
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--hue) 18%, var(--panel-hi));
}

.ratings__empty-text { flex: 1 1 16rem; }
.ratings__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}
.ratings__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}

/* ================================================================
   Around the world band
   A choropleth world map — every seen country shaded up the band hue —
   beside a six-continent reach, a strip of languages beneath. One hue,
   five steps; unvisited land stays neutral, no second palette.
   ================================================================ */

.world__split {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) {
  .world__split {
    grid-template-columns: minmax(0, 1fr) 13rem;
    align-items: stretch;
  }
}

.world__aside {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* ---- The map ---- */

/* Five steps up the band hue over a neutral land; the legend swatches read from
   the same custom properties, so the map and its key can never drift apart. */
.world__map {
  --wm-land: color-mix(in srgb, var(--rule) 55%, var(--panel-hi));
  --wm-1: color-mix(in srgb, var(--hue) 26%, var(--wm-land));
  --wm-2: color-mix(in srgb, var(--hue) 45%, var(--wm-land));
  --wm-3: color-mix(in srgb, var(--hue) 64%, var(--wm-land));
  --wm-4: color-mix(in srgb, var(--hue) 82%, var(--wm-land));
  --wm-5: var(--hue);
}

.worldmap {
  margin: 0.85rem 0 0;
  /* Hold the cropped map's aspect while its outline is still on the way, so the
     panel does not resize when it lands. */
  aspect-ratio: 1000 / 394;
  border-radius: 5px;
  overflow: hidden;
  background: color-mix(in srgb, var(--hue) 5%, transparent);
}
.worldmap__svg { display: block; width: 100%; height: auto; }

.worldmap__land path {
  fill: var(--wm-land);
  stroke: var(--panel);
  stroke-width: 0.4;
  vector-effect: non-scaling-stroke;
}
.worldmap__land path.wm-1 { fill: var(--wm-1); }
.worldmap__land path.wm-2 { fill: var(--wm-2); }
.worldmap__land path.wm-3 { fill: var(--wm-3); }
.worldmap__land path.wm-4 { fill: var(--wm-4); }
.worldmap__land path.wm-5 { fill: var(--wm-5); }

.worldmap__legend {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.6rem;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--faint);
}
.worldmap__ramp { display: inline-flex; gap: 2px; }
.worldmap__ramp span { width: 1.4rem; height: 0.5rem; border-radius: 2px; }
.worldmap__ramp .wm-1 { background: var(--wm-1); }
.worldmap__ramp .wm-2 { background: var(--wm-2); }
.worldmap__ramp .wm-3 { background: var(--wm-3); }
.worldmap__ramp .wm-4 { background: var(--wm-4); }
.worldmap__ramp .wm-5 { background: var(--wm-5); }

/* ---- Continent reach ---- */

.world__reach-hero {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  margin: 0.85rem 0 0.75rem;
}
.world__reach-num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-4);
  line-height: 1;
  color: var(--hue);
}
.world__reach-of {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}

.continents {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
/* A reached continent stands lit against a hue-tinted ground with a hue edge. */
.continent {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  font-size: var(--step--1);
  padding: 0.3rem 0.55rem;
  border-radius: 6px;
  background: color-mix(in srgb, var(--hue) 10%, transparent);
  border-left: 2px solid var(--hue);
}
.continent__name { color: var(--ink); }
.continent__count { font-family: var(--data); color: var(--muted); }
/* One still to reach: no ground, a neutral edge, dimmed — a visible gap. */
.continent--off {
  background: none;
  border-left-color: color-mix(in srgb, var(--rule) 85%, transparent);
  opacity: 0.55;
}
.continent--off .continent__name { color: var(--muted); }

/* ---- Languages strip ---- */

.world__langs { margin: 0; }
.langpills {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.langpill {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--hue) 12%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 28%, var(--rule));
  font-size: var(--step--1);
}
.langpill__name { color: var(--ink); }
.langpill__count { font-family: var(--data); color: var(--hue); font-weight: 500; }
.langpill--more {
  background: none;
  border-color: color-mix(in srgb, var(--rule) 85%, transparent);
  color: var(--faint);
}

/* ================================================================
   Genres & taste band
   A treemap: one tile per genre, area by how often you watch it,
   shade by how highly you rate it. One hue, five steps over a neutral
   tile for genres you have barely rated — the same "one hue, no
   second palette" bargain the world map makes.
   ================================================================ */

/* Five shade steps up the band hue, plus the neutral a not-yet-rated tile
   wears; the legend swatches read from the same custom properties, so the map
   and its key can never drift apart. Capped short of the full hue so even the
   top step stays dark enough to carry a light label. */
.genres__map {
  --tm-land: color-mix(in srgb, var(--rule) 46%, var(--panel-hi));
  --tm-1: color-mix(in srgb, var(--hue) 16%, var(--tm-land));
  --tm-2: color-mix(in srgb, var(--hue) 28%, var(--tm-land));
  --tm-3: color-mix(in srgb, var(--hue) 40%, var(--tm-land));
  --tm-4: color-mix(in srgb, var(--hue) 52%, var(--tm-land));
  --tm-5: color-mix(in srgb, var(--hue) 64%, var(--tm-land));
}

.treemap {
  position: relative;
  width: 100%;
  aspect-ratio: 7 / 2;
  /* A floor on a narrow screen, where the squat ratio would starve small tiles. */
  min-height: 8rem;
  margin: 0.85rem 0 0;
  /* The seams between tiles: the container ground shows through the inset. */
  background: color-mix(in srgb, var(--hue) 6%, transparent);
  border-radius: 5px;
}

/* Each tile is placed by percentage; the inset panel ring is what reads as the
   2px gap between neighbours, and the rounding lets the ground show at corners. */
.tm-cell {
  position: absolute;
  overflow: hidden;
  border-radius: 4px;
  background: var(--tm-land);
  box-shadow: inset 0 0 0 1.5px var(--panel);
  display: flex;
  align-items: flex-end;
}
.tm-cell.tm-1 { background: var(--tm-1); }
.tm-cell.tm-2 { background: var(--tm-2); }
.tm-cell.tm-3 { background: var(--tm-3); }
.tm-cell.tm-4 { background: var(--tm-4); }
.tm-cell.tm-5 { background: var(--tm-5); }

.tm-cell__body {
  padding: 0.3rem 0.45rem;
  min-width: 0;
  /* A whisper of shadow keeps the label legible on the lightest shade step. */
  text-shadow: 0 1px 2px color-mix(in srgb, #000 45%, transparent);
}
.tm-cell__name {
  display: block;
  color: var(--ink);
  line-height: 1.05;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tm-cell__count {
  display: block;
  font-family: var(--data);
  color: color-mix(in srgb, var(--ink) 78%, transparent);
  line-height: 1.15;
}
.tm-cell__stars {
  color: color-mix(in srgb, var(--ink) 92%, transparent);
  margin-left: 0.15rem;
}

/* The large tiles carry a full label; the mid ones a smaller one; the smallest
   are tint alone, telling their story on hover so they never show a clipped word. */
.tm-cell--lg .tm-cell__name { font-size: var(--step-0); }
.tm-cell--lg .tm-cell__count { font-size: var(--step--2); }
.tm-cell--md .tm-cell__name { font-size: var(--step--1); }
.tm-cell--md .tm-cell__count { font-size: var(--step--2); }
.tm-cell--sm .tm-cell__body { display: none; }

.treemap__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin-top: 0.6rem;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--faint);
}
.treemap__key { display: inline-flex; align-items: center; gap: 0.45rem; }
.treemap__ramp { display: inline-flex; gap: 2px; }
.treemap__ramp span { width: 1.4rem; height: 0.5rem; border-radius: 2px; }
.treemap__ramp .tm-1 { background: var(--tm-1); }
.treemap__ramp .tm-2 { background: var(--tm-2); }
.treemap__ramp .tm-3 { background: var(--tm-3); }
.treemap__ramp .tm-4 { background: var(--tm-4); }
.treemap__ramp .tm-5 { background: var(--tm-5); }
.treemap__key--neutral .tm-swatch {
  width: 0.9rem;
  height: 0.5rem;
  border-radius: 2px;
  background: var(--tm-land);
}
.treemap__hint { color: color-mix(in srgb, var(--hue) 55%, var(--muted)); }

/* ---- Genres empty state: a ghost treemap under the invitation ---- */

.genres__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}
.genres__empty-art {
  position: relative;
  flex: none;
  width: 12rem;
  aspect-ratio: 7 / 2;
}
.genres__empty-art span {
  position: absolute;
  border-radius: 3px;
  background: color-mix(in srgb, var(--hue) 12%, var(--panel-hi));
  box-shadow: inset 0 0 0 1.5px var(--panel);
}
.genres__empty-text { flex: 1 1 16rem; }
.genres__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}
.genres__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}

/* ================================================================
   The people band
   Role-tabbed leaderboards of who you have followed the furthest: a
   poster to know each career by, a bar for how deep you have gone, the
   count at the end. The one band read from a precomputed slice, not
   films.json, so it only ever appears once its script has run.
   ================================================================ */

.people__switch {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.people__tab {
  font-family: var(--data);
  font-size: var(--step--1);
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--hue) 8%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
  color: var(--muted);
  cursor: pointer;
}
.people__tab:hover { color: var(--ink); }
/* The chosen role wears the band hue, the way a pressed filter does elsewhere. */
.people__tab[aria-pressed='true'] {
  background: color-mix(in srgb, var(--hue) 22%, var(--panel));
  border-color: color-mix(in srgb, var(--hue) 45%, var(--rule));
  color: var(--ink);
}

.people__board {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.people__board[hidden] { display: none; }

.prow {
  display: grid;
  grid-template-columns: 1.4rem minmax(6rem, 14rem) minmax(3rem, 1fr);
  align-items: center;
  gap: 0.7rem;
  padding: 0.25rem 0.35rem;
  border-radius: 6px;
}
.prow:hover { background: color-mix(in srgb, var(--hue) 6%, transparent); }

.prow__rank {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
  text-align: right;
}
/* The one you have followed furthest in this role leads in the band hue. */
.prow:first-child .prow__rank {
  color: var(--hue);
  font-weight: 600;
}

.prow__person {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.prow__poster {
  width: 30px;
  height: 45px;
  flex: none;
  border-radius: 3px;
  object-fit: cover;
  background: var(--panel-hi);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.prow__poster--empty { display: block; }
.prow__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--step--1);
  color: var(--ink);
}
.prow__person:hover .prow__name { color: var(--hue); }

/* The comparative bar: how far you have followed this career against the role's
   most-followed, drawn as one gradient the way the badge bars are — a hue fill to
   --pct over a faint track beyond it. The leader tops out at the full hue. */
.prow__bar {
  height: 10px;
  border-radius: 999px;
  background-image: linear-gradient(
    to right,
    color-mix(in srgb, var(--hue) 55%, var(--panel-hi)) 0 var(--pct, 0%),
    color-mix(in srgb, var(--hue) 12%, var(--panel-hi)) var(--pct, 0%) 100%
  );
}
.prow:first-child .prow__bar {
  background-image: linear-gradient(
    to right,
    var(--hue) 0 var(--pct, 0%),
    color-mix(in srgb, var(--hue) 12%, var(--panel-hi)) var(--pct, 0%) 100%
  );
}

/* ================================================================
   The records shelf
   The extremes of the collection as a trophy case: a hairline grid of
   poster cards, each with a plate engraved beneath it naming the record
   it holds and the number that won it. Not a chart — the posters are the
   data. The 1px-rule grid is the same inset frame the tiles, chips and
   picks wear, so the shelf reads as one of the family.
   ================================================================ */

.records {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* The same staggered rise-in the other bands make; reduced-motion stills it via
   the global rule. */
.records > * { animation: stats-rise 0.5s ease both; }
.records__shelf { animation-delay: 0.08s; }

.records__shelf { margin: 0; }

.records__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 1px;
  margin-top: 0.7rem;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
}

.record {
  margin: 0;
  background: var(--panel);
}

.record__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  text-decoration: none;
  color: inherit;
  transition: background 160ms ease;
}
.js .record__link:hover { background: var(--panel-hi); }

.record__frame {
  position: relative;
  display: block;
  aspect-ratio: 2 / 3;
  background: var(--panel-hi);
  overflow: hidden;
}
.record__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* No art: the title fills the frame rather than a blank rectangle. */
.record__poster--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem;
  text-align: center;
  font-size: var(--step--2);
  line-height: 1.3;
  color: var(--faint);
}

/* The engraved plate. A hue rule along its top ties it to the poster above and
   the band's colour, the way the tiles wear their hue as a leading edge. */
.record__plate {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.55rem 0.65rem 0.7rem;
  border-top: 2px solid color-mix(in srgb, var(--hue) 55%, var(--rule));
}

.record__label {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}

.record__value {
  display: flex;
  align-items: baseline;
  gap: 0.3ch;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-2);
  line-height: 1.05;
  color: var(--hue);
}
.record__unit {
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--muted);
}

.record__title {
  font-size: var(--step--1);
  color: var(--ink);
  line-height: 1.25;
  /* Two lines, then ellipsis, so a long title never blows the card's height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.record__year { color: var(--faint); }
.record__link:hover .record__title { color: var(--hue); }

/* ---- Records empty state ---- */

.records__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}
.records__empty-art {
  display: grid;
  grid-template-columns: repeat(4, 26px);
  gap: 6px;
  flex: none;
}
.records__empty-art span {
  width: 26px;
  height: 39px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--hue) 14%, var(--panel-hi));
}
.records__empty-text { flex: 1 1 16rem; }
.records__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}
.records__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}

/* ================================================================
   The completion band
   How close every tracked career is to 100%, as a scatter field: one
   dot per career, placed across by completeness and up by how deep the
   filmography runs, brightening up the hue toward the finish. Beside it
   a radial gauge of the whole board's completion, then the honour roll
   of the careers taken all the way. One hue, no second palette — the
   same bargain the rest of the page makes.
   ================================================================ */

/* The same two-column split as ratings/world: the field beside a narrow aside. */
.completion__split {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) {
  .completion__split {
    grid-template-columns: minmax(0, 1fr) 12.5rem;
    align-items: stretch;
  }
}
.completion__aside {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.completion__lead { animation-delay: 0.04s; }
.completion__roll { animation-delay: 0.12s; }
.completion__chips { animation-delay: 0.16s; }

/* ---- The scatter field ---- */

.completion__field { gap: 0.4rem; }

/* Five shade steps up the hue for the near-empty → nearly-done dots, plus the
   full-hue "done" for a finished career, so the field brightens left to right. */
.cfield {
  --cf-land: color-mix(in srgb, var(--rule) 40%, var(--panel-hi));
  width: 100%;
  height: auto;
  margin-top: 0.35rem;
  overflow: visible;
}
.cf-dot { stroke: none; }
.cf-1 { fill: color-mix(in srgb, var(--hue) 26%, var(--cf-land)); }
.cf-2 { fill: color-mix(in srgb, var(--hue) 42%, var(--cf-land)); }
.cf-3 { fill: color-mix(in srgb, var(--hue) 58%, var(--cf-land)); }
.cf-4 { fill: color-mix(in srgb, var(--hue) 74%, var(--cf-land)); }
.cf-5 { fill: color-mix(in srgb, var(--hue) 90%, var(--cf-land)); }
/* A finished career: the full hue, a soft ring, and a whisper of glow so the
   handful on the finish line read over the faint mass at the other end. */
.cf--done {
  fill: var(--hue);
  stroke: color-mix(in srgb, var(--hue) 40%, var(--panel));
  stroke-width: 1.5;
  filter: drop-shadow(0 0 3px color-mix(in srgb, var(--hue) 55%, transparent));
}

.cf-grid { stroke: color-mix(in srgb, var(--hue) 12%, var(--rule)); stroke-width: 1; }
/* The finish line the completed careers sit on, a shade firmer than the rest. */
.cf-grid--finish {
  stroke: color-mix(in srgb, var(--hue) 45%, var(--rule));
  stroke-dasharray: 3 4;
}
.cf-axis {
  fill: var(--faint);
  font-family: var(--data);
  font-size: 20px;
  letter-spacing: 0.02em;
}

.cfield__foot {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font-size: var(--step--2);
  color: var(--faint);
}
.cfield__done-key {
  width: 0.6rem;
  height: 0.6rem;
  flex: none;
  border-radius: 999px;
  background: var(--hue);
  box-shadow: 0 0 3px color-mix(in srgb, var(--hue) 55%, transparent);
}

/* ---- The radial gauge ---- */

.completion__gauge {
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  box-shadow: inset 3px 0 0 var(--hue);
}
.ring {
  position: relative;
  width: 8.25rem;
  max-width: 100%;
  margin: 0.2rem 0;
}
.ring__svg { display: block; width: 100%; height: auto; }
.ring__track { stroke: color-mix(in srgb, var(--hue) 14%, var(--rule)); }
.ring__fill { stroke: var(--hue); }
.ring__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.ring__num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-3);
  color: var(--hue);
}
.ring__pct { font-size: 0.5em; margin-left: 0.1em; }
.ring__cap {
  margin-top: 0.2rem;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}
.ring__line {
  margin: 0;
  font-size: var(--step--1);
  color: var(--muted);
}
.ring__line b { font-family: var(--data); color: var(--ink); font-weight: 500; }

/* ---- The honour roll of finished careers ---- */

.completion__roll { gap: 0.4rem; }
.croll {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.croll__item { margin: 0; }
/* A rounded rectangle, not a full pill: the poster inside is rectangular, and a
   999px cap curves in faster than the poster's inset so its corners overhang. */
.croll__link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.6rem 0.3rem 0.3rem;
  border-radius: 8px;
  background: color-mix(in srgb, var(--hue) 10%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 30%, var(--rule));
  text-decoration: none;
  color: inherit;
}
.js .croll__link:hover { background: color-mix(in srgb, var(--hue) 18%, var(--panel)); }
.croll__poster {
  width: 26px;
  height: 39px;
  flex: none;
  border-radius: 3px;
  object-fit: cover;
  background: var(--panel-hi);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.croll__poster--empty { display: block; }
.croll__meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}
.croll__name {
  font-size: var(--step--1);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.croll__link:hover .croll__name { color: var(--hue); }
.croll__sub {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}
.croll__more {
  align-self: center;
  padding: 0 0.4rem;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
}

/* ---- Completion empty state: a ghost field under the invitation ---- */

.completion__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}
.completion__empty-art {
  position: relative;
  flex: none;
  width: 11rem;
  height: 5rem;
}
.completion__empty-art span {
  position: absolute;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  transform: translate(-50%, -50%);
  background: color-mix(in srgb, var(--hue) 22%, var(--panel-hi));
}
.completion__empty-text { flex: 1 1 16rem; }
.completion__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}

/* ================================================================
   Gap analytics band
   The reachable frontier, in two forms the page has not spent: an
   interactive unlock ladder (a staircase of careers-finished against
   films-watched, scrubbed) and a wall of keystone tiles, each a small
   node-link web from one film or one name out to the careers it moves.
   ================================================================ */

.gaps__lead { animation-delay: 0.04s; }
.gaps__wall { animation-delay: 0.08s; }
.gaps__lists { animation-delay: 0.16s; }

/* ---- The unlock control (a what-if scrubber, no chart) ---- */

.gaps__ladder,
.gaps__wall {
  margin: 0;
}

.ladder {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1rem 1.15rem;
  background: color-mix(in srgb, var(--hue) 4%, var(--panel));
  border: 1px solid var(--rule);
  border-radius: 3px;
}

/* The scrubber: a film budget you drag; the read above it follows. */
.ladder__scrub {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 1.3rem;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.ladder__scrub::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 999px;
  background: var(--rule);
}
.ladder__scrub::-moz-range-track {
  height: 3px;
  border-radius: 999px;
  background: var(--rule);
}
.ladder__scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  margin-top: -6px;
  border-radius: 999px;
  background: var(--hue);
  border: 2px solid var(--panel);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.ladder__scrub::-moz-range-thumb {
  width: 15px;
  height: 15px;
  border-radius: 999px;
  background: var(--hue);
  border: 2px solid var(--panel);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.ladder__scrub:focus-visible { outline: 2px solid color-mix(in srgb, var(--hue) 60%, transparent); outline-offset: 3px; }

.ladder__read {
  margin: 0;
  font-family: var(--data);
  font-size: var(--step-0);
  color: var(--muted);
}
.ladder__read b {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-2);
  color: var(--hue);
}

/* ---- The watchlist poster field ---- */

.ladder__films-cap {
  margin: 0.35rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.02em;
  color: var(--faint);
}
.ladder__films {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 4px;
  margin: 0;
  /* Exactly three rows, never a scrollbar; the "+N" tally flags how many more
     the path holds beyond what fits. */
  max-height: calc(72px * 3 + 4px * 2);
  overflow: hidden;
}
.pfilm {
  position: relative;
  display: block;
  width: 48px;
  height: 72px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--panel-hi);
  opacity: 0.32;
  filter: grayscale(0.7);
  transition: opacity 0.15s ease, filter 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
}
.pfilm img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Lit: inside the current budget — the films the target above actually names. */
.pfilm.is-in {
  opacity: 1;
  filter: none;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--hue) 65%, transparent);
}
.pfilm:hover,
.pfilm:focus-visible {
  opacity: 1;
  filter: none;
  transform: translateY(-2px) scale(1.12);
  z-index: 2;
  outline: none;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.32);
}
.pfilm__empty {
  display: flex;
  align-items: center;
  padding: 0.2rem;
  width: 100%;
  height: 100%;
  font-size: 0.5rem;
  line-height: 1.1;
  color: var(--faint);
  overflow: hidden;
}
.pfilm--more {
  display: flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-width: 48px;
  padding: 0 0.5rem;
  height: 72px;
  opacity: 1;
  filter: none;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--muted);
}
@media (prefers-reduced-motion: reduce) {
  .pfilm { transition: none; }
}

/* ---- The keystone wall ---- */

.ktiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
  gap: 1px;
  margin: 0.65rem 0 0;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
}

.ktile {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 0.9rem 1rem;
  background: var(--panel);
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
/* A tile the ladder budget has reached: tinted, with a hue rail down its edge. */
.ktile.is-reached {
  background: color-mix(in srgb, var(--hue) 9%, var(--panel));
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--hue) 65%, transparent);
}
.ktile:hover {
  background: color-mix(in srgb, var(--hue) 13%, var(--panel));
}

.ktile__hub {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  text-decoration: none;
  min-width: 0;
}
.ktile__poster {
  flex: none;
  width: 48px;
  height: 72px;
  object-fit: cover;
  border-radius: 2px;
  background: var(--panel-hi);
}
.ktile__poster--empty {
  display: block;
  border: 1px dashed var(--rule);
}
.ktile__hub-meta {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}
.ktile__hub-kind {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}
.ktile__hub-title {
  font-size: var(--step-0);
  font-weight: 500;
  line-height: 1.18;
  color: var(--ink);
}
.ktile__hub-year {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}

/* The tile's headline: the people the one film (or name) would finish, named. */
.ktile__moves {
  margin: 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.35;
}
.ktile__moves b {
  color: var(--hue);
  font-weight: 600;
}
.ktile__moves-sub { color: var(--faint); }

/* ---- The connector-films list ---- */

.gaps__list-cap {
  margin: 0 0 0.55rem;
  font-family: var(--data);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--hue) 55%, var(--muted));
}
.gpicks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.gpick {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--panel);
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.gpick:hover {
  background: color-mix(in srgb, var(--hue) 12%, var(--panel));
  border-color: color-mix(in srgb, var(--hue) 40%, var(--rule));
}
.gpick__title {
  font-size: var(--step--1);
  color: var(--ink);
}
.gpick__year {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}
.gpick__tag {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--hue);
  padding-left: 0.4rem;
  border-left: 1px solid var(--rule);
}
.gpick--more {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--muted);
  background: transparent;
  border-style: dashed;
}

/* ---- Gap analytics empty state: a ghost staircase under the invitation ---- */

.gaps__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 3px;
}
.gaps__empty-art {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  width: 11rem;
  height: 5rem;
  flex: none;
}
.gaps__empty-art span {
  flex: 1;
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--hue) 18%, var(--panel-hi));
}
.gaps__empty-text { flex: 1 1 16rem; }
.gaps__empty-title {
  margin: 0;
  font-size: var(--step-1);
  color: var(--ink);
}
.gaps__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}
.completion__empty-note {
  margin: 0.4rem 0 0;
  font-size: var(--step--1);
  color: var(--faint);
  max-width: var(--measure);
}

/* ================================================================
   The company you keep band
   The recurring partnerships, in two forms the page has not spent: a
   wall of creative circles — a hub and its collaborators drawn as a
   nested arc-fan, the connectors lit in an aurora gradient — and a
   shelf of enduring duos. The partner list beside each fan is the
   control: pick a name to light its arc and unfurl the shared films.
   ================================================================ */

.collab__defs { position: absolute; width: 0; height: 0; }
.collab__circles { animation-delay: 0.08s; }
.collab__duos { animation-delay: 0.16s; }
.collab__circles,
.collab__duos { margin: 0; }

/* ---- The wall of creative circles ---- */

.ccards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 0.75rem;
  margin: 0.65rem 0 0;
}
.ccard {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0.95rem 1rem 0.7rem;
  background: color-mix(in srgb, var(--hue) 4%, var(--panel));
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.ccard__head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.ccard__hub {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--ink);
  text-decoration: none;
}
a.ccard__hub:hover { color: var(--hue); }
.ccard__role {
  font-family: var(--data);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in srgb, var(--hue) 55%, var(--faint));
}
.ccard__meta {
  margin-left: auto;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}

/* The fan itself. Fixed viewBox, scaled to the card width. */
.ccard__fan { margin: 0.55rem 0 0.15rem; }
.cfan { display: block; width: 100%; height: auto; overflow: visible; }
.cfan__base { stroke: var(--rule); stroke-width: 1; }
.cfan__arc {
  opacity: 0.42;
  transition: opacity 0.16s ease, filter 0.16s ease;
  cursor: pointer;
}
.cfan__arc.is-on {
  opacity: 1;
  filter: drop-shadow(0 0 3px color-mix(in srgb, #4fb8d4 55%, transparent));
}
.cdot { cursor: pointer; transition: fill 0.16s ease; }
.cdot--hub { fill: #63e0b8; }
.cdot--crew { fill: var(--panel); stroke: #4fb8d4; stroke-width: 1.6; }
.cdot--actor { fill: #8fa294; }
.cdot--crew.is-on { fill: #4fb8d4; }
.cdot--actor.is-on { fill: #d3dcd2; }

/* The partner list — legend and control in one. */
.cparts { list-style: none; margin: 0.35rem 0 0; padding: 0; display: flex; flex-direction: column; }
.cpart { border-top: 1px solid var(--rule); }
.cpart:first-child { border-top: 0; }
.cpart__pick {
  width: 100%;
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.42rem 0.35rem;
  background: none;
  border: 0;
  border-left: 2px solid transparent;
  font: inherit;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.14s ease, border-color 0.14s ease;
}
.cpart__pick:hover { background: color-mix(in srgb, var(--hue) 8%, transparent); }
.cpart__pick[aria-expanded='true'] {
  background: color-mix(in srgb, var(--hue) 11%, transparent);
  border-left-color: #4fb8d4;
}
.cpart__pick:focus-visible { outline: 2px solid color-mix(in srgb, var(--hue) 60%, transparent); outline-offset: -2px; }
.cdotkey {
  align-self: center;
  width: 9px; height: 9px;
  border-radius: 999px;
  flex: none;
}
.cdotkey--crew { background: transparent; box-shadow: inset 0 0 0 1.6px #4fb8d4; }
.cdotkey--actor { background: #8fa294; }
.cpart__name { font-size: var(--step--1); color: var(--ink); min-width: 0; }
.cpart__role {
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
  text-align: right;
}
.cpart__n {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--hue);
  font-variant-numeric: tabular-nums;
}
.cpart__n small { font-family: var(--data); font-weight: 400; font-size: var(--step--2); color: var(--faint); }
.cpart--more {
  padding: 0.5rem 0.35rem 0.2rem;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--faint);
}

/* The shared-film strip a pick reveals. */
.cpart__films { display: flex; flex-wrap: wrap; gap: 4px; padding: 0.15rem 0.35rem 0.55rem; }
.cpart__films[hidden] { display: none; }
.cfilm {
  display: block;
  width: 40px; height: 60px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--panel-hi);
  transition: transform 0.12s ease, box-shadow 0.14s ease;
}
.cfilm img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cfilm:hover,
.cfilm:focus-visible {
  transform: translateY(-2px) scale(1.08);
  outline: none;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.34);
  z-index: 2;
}
.cfilm__empty {
  display: flex;
  align-items: center;
  padding: 0.2rem;
  width: 100%; height: 100%;
  font-size: 0.5rem;
  line-height: 1.1;
  color: var(--faint);
  overflow: hidden;
}

/* ---- The enduring-duos shelf ---- */

.cduos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 1px;
  margin: 0.65rem 0 0;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 4px;
  overflow: hidden;
}
.cduo {
  margin: 0;
  padding: 0.85rem 0.95rem;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.cduo__pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.55rem;
}
.cduo__name {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
  font-size: var(--step--1);
  color: var(--ink);
  text-decoration: none;
}
a.cduo__name:hover { color: var(--hue); }
.cduo__name--b { text-align: right; align-items: flex-end; }
.cduo__name small { font-family: var(--data); font-size: var(--step--2); color: var(--faint); }
/* The aurora link between the two names, the count riding on it. */
.cduo__link {
  position: relative;
  min-width: 3.2rem;
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, #63e0b8, #4fb8d4 34%, #8f9be2 66%, #c98fd0);
}
.cduo--actor .cduo__link { background: var(--faint); }
.cduo__n {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--panel);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.cduo__films { display: flex; flex-wrap: wrap; gap: 4px; }

/* ---- Empty state: an invitation over a ghost fan ---- */

.collab__empty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 4px;
}
.collab__empty-art { flex: none; width: 12rem; }
.collab__empty-art svg { display: block; width: 100%; height: auto; }
.collab__empty-art path { fill: none; stroke: color-mix(in srgb, var(--hue) 34%, var(--panel-hi)); stroke-width: 2; stroke-linecap: round; }
.collab__empty-art line { stroke: var(--rule); stroke-width: 1.5; }
.collab__empty-text { flex: 1 1 16rem; }
.collab__empty-title { margin: 0; font-size: var(--step-1); color: var(--ink); }
.collab__empty-note { margin: 0.4rem 0 0; font-size: var(--step--1); color: var(--faint); max-width: var(--measure); }

@media (prefers-reduced-motion: reduce) {
  .cfan__arc,
  .cdot,
  .cpart__pick,
  .cfilm { transition: none; }
}

/* ================================================================
   RADAR — the Completionist command centre
   The completion layer as a loop rather than a chart: Discover the
   sets to chase (Hidden Sets), Act on the gaps that matter (Now
   Playing), Maintain a finished collection (Stay Complete). The
   standing hero and the nav item wear the aurora — the top tier's
   signature sweep; each zone below takes a warm top-tier hue.
   ================================================================ */

/* The Radar nav item wears the aurora — the same moving spectrum the stats hero
   numbers and the top pricing card carry (stats-aurora over --aurora, both defined
   with the stats page) — set bold so it stands a touch prouder than its siblings.
   So the top tier's command centre is recognisable from the masthead of any page,
   shown to everyone as a standing advertisement for it. */
.nav__radar-text {
  font-weight: 600;
  background: linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd);
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}
.nav__radar[aria-current='page'] {
  border-bottom-color: color-mix(in srgb, #57c4c9 60%, transparent);
}

.radar {
  padding-bottom: clamp(2rem, 5vw, 4rem);
}
/* The page's one-line description: a normal wrapping paragraph below the title,
   not a right-aligned tally — the shared .section__tally is nowrap for short
   counts, so a full sentence there runs off the page. */
.radar-intro__sub {
  margin: 0.4rem 0 0;
  font-size: var(--step-0);
  color: var(--muted);
  max-width: var(--measure);
}

/* ---- The standing hero ---- */
.radar-hero {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: clamp(1rem, 3vw, 2.5rem);
  /* Top-align so every big number sits on the same line, regardless of how many
     subtitle lines hang beneath it (0, 1, or 2). */
  align-items: start;
  margin-block: clamp(1rem, 3vw, 2rem) clamp(1.5rem, 4vw, 2.75rem);
}
.radar-hero__stat { min-width: 0; }
.radar-hero__value {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-6);
  line-height: 0.95;
  letter-spacing: -0.02em;
}
.radar-hero__num {
  background: linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd);
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}
.radar-hero__label {
  margin: 0.5rem 0 0;
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.radar-hero__sub {
  margin: 0.2rem 0 0;
  font-size: var(--step--2);
  color: var(--faint);
}

/* ---- Zones ---- */
.radar-zone { --hue: #57c4c9; position: relative; }
.radar-zone[data-hue='depth'] { --hue: #cc9481; }
.radar-zone[data-hue='palate'] { --hue: #c6a052; }
.radar-zone[data-hue='ritual'] { --hue: #9fb06a; }
.radar-zone .section__icon { color: var(--hue); }
/* The heading wears the zone's hue, the glyph centred against the label with a
   touch of air between them — the same treatment the stats-section titles get. */
.radar-zone__title {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--hue);
  letter-spacing: 0.14em;
}
.radar-zone__body { margin-top: 1rem; }

/* ---- Coming-soon preview: a described feature over a soft shimmer ---- */
.radar-soon { display: flex; flex-direction: column; gap: 1rem; }
.radar-soon__blurb {
  margin: 0;
  font-size: var(--step-0);
  color: var(--muted);
  max-width: var(--measure);
}

/* ---- Hidden Sets: the wall of set cards, on the shared 1px-rule grid ---- */
.hidden-sets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.hset {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 1rem 1.1rem 1.15rem;
  background: var(--panel);
  animation: stats-rise 0.5s ease both;
}
.hset__head { display: flex; gap: 0.85rem; align-items: flex-start; }
.hset__face {
  flex: none;
  width: 58px;
  height: 87px;
  object-fit: cover;
  border-radius: 3px;
  background: var(--panel-hi);
}
.hset__face--empty { display: block; }
.hset__id { min-width: 0; flex: 1; }
.hset__name {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.12;
}
.hset__name a { color: var(--ink); text-decoration: none; }
.hset__name a:hover { color: var(--hue); }
.hset__role {
  margin: 0.2rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.hset__loved {
  display: inline-block;
  margin-top: 0.5rem;
  font-size: var(--step--1);
  color: var(--hue);
}

/* the completion meter — how far into their work you are */
.hset__bar {
  height: 5px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--hue) 14%, var(--panel-hi));
  overflow: hidden;
}
.hset__bar span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--hue);
}
.hset__count {
  margin: 0.4rem 0 0;
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--muted);
}
.hset__of b { color: var(--ink); font-variant-numeric: tabular-nums; }
.hset__togo { color: var(--hue); }

/* the set to finish — the films of theirs still to see */
.hset__missing {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-items: center;
}
.hset__miss {
  flex: none;
  display: block;
  border-radius: 2px;
  overflow: hidden;
  outline: 1px solid transparent;
  transition: outline-color 0.15s ease, transform 0.15s ease;
}
.hset__miss img {
  display: block;
  width: 44px;
  height: 66px;
  object-fit: cover;
  background: var(--panel-hi);
}
.hset__miss:hover { outline-color: var(--hue); transform: translateY(-2px); }
.hset__miss-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 66px;
  padding: 3px;
  font-size: 0.5rem;
  line-height: 1.1;
  text-align: center;
  color: var(--faint);
  background: var(--panel-hi);
  overflow: hidden;
}
.hset__more {
  align-self: center;
  padding-left: 0.2rem;
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--faint);
}

/* ---- Radar empty state: an invitation, never a blank ---- */
.radar-empty {
  padding: 1.5rem 1.4rem;
  background: var(--panel);
  border: 1px dashed color-mix(in srgb, var(--hue) 30%, var(--rule));
  border-radius: 4px;
  max-width: var(--measure);
}
.radar-empty__lead { margin: 0; font-size: var(--step-1); color: var(--ink); }
.radar-empty__note { margin: 0.4rem 0 0; font-size: var(--step--1); color: var(--faint); }

/* ---- Add-all-to-watchlist button ---- */
.hset__add {
  align-self: start;
  margin-top: 0.1rem;
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.03em;
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 12%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 40%, var(--rule));
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.hset__add:hover:not(:disabled) {
  background: color-mix(in srgb, var(--hue) 22%, var(--panel));
  border-color: color-mix(in srgb, var(--hue) 60%, var(--rule));
}
.hset__add:disabled { cursor: default; }
.hset__add--busy { color: var(--muted); }
/* Done: it drops the tier hue for the seen-green, the same positive ink a watched
   film wears, so "added" reads as a completed action rather than another prompt. */
.hset__add--done {
  color: var(--seen);
  background: transparent;
  border-color: color-mix(in srgb, var(--seen) 40%, var(--rule));
}

/* ---- Six Degrees: the corpus as a parlour game, leading the Hidden Sets zone ----
   A wrapping row of stacked tiles — a poster over each name — traced live by
   scripts/radar.js. It sits inside the Hidden Sets zone, so it inherits that
   zone's warm --hue (depth); each tile cycles its own --node hue across the
   aurora palette so the thread reads as a run of distinct posters. The divider
   sits below it, parting the game from the wall of sets beneath. */
.six {
  padding-bottom: 1.75rem;
  margin-bottom: 1.75rem;
  border-bottom: 1px solid color-mix(in srgb, var(--hue) 22%, var(--rule));
}
.six__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem 1.25rem;
}
.six__intro { min-width: min(100%, 18rem); flex: 1; }
.six__title {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--hue);
  letter-spacing: 0.02em;
}
.six__blurb {
  margin: 0.35rem 0 0;
  font-size: var(--step--1);
  color: var(--muted);
  max-width: var(--measure);
}
.six__shuffle {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font: inherit;
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.03em;
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 12%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--hue) 40%, var(--rule));
  border-radius: 999px;
  padding: 0.45rem 0.95rem;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.six__shuffle svg { width: 15px; height: 15px; }
.six__shuffle:hover:not(:disabled) {
  background: color-mix(in srgb, var(--hue) 22%, var(--panel));
  border-color: color-mix(in srgb, var(--hue) 60%, var(--rule));
}
.six__shuffle:disabled { cursor: default; color: var(--muted); }
.six__shuffle--busy svg { animation: six-spin 0.8s linear infinite; }
@keyframes six-spin { to { transform: rotate(360deg); } }

/* The chain wraps to fit the content width — stacked tiles flow onto a second
   row rather than pushing the page sideways, so there is never a scrollbar. */
.six__strip { margin-top: 1.15rem; }
.six-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 1rem 0.35rem;
}
/* A step is one tile plus the arrow onto the next; it wraps as a unit, so a row
   that fills ends on an arrow pointing to where the thread carries on below. */
.six-step {
  display: flex;
  align-items: flex-start;
  gap: 0.35rem;
  animation: stats-rise 0.5s ease both;
}
/* Each tile cycles its own hue across the aurora palette, so the chain is a run
   of distinct posters rather than one flat colour. */
.six-step:nth-child(6n + 1) { --node: #5dadbd; }
.six-step:nth-child(6n + 2) { --node: #90a0d2; }
.six-step:nth-child(6n + 3) { --node: #ab97d4; }
.six-step:nth-child(6n + 4) { --node: #cf89cf; }
.six-step:nth-child(6n + 5) { --node: #cc9481; }
.six-step:nth-child(6n + 6) { --node: #c6a052; }

/* A tile: the poster on top, the person and their role stacked beneath it. */
.six-tile {
  width: 6.75rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.45rem;
}
.six-tile__poster {
  width: 92px;
  height: 138px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  overflow: hidden;
  cursor: pointer;
  background: color-mix(in srgb, var(--node) 12%, var(--panel-hi));
  outline: 1px solid color-mix(in srgb, var(--node) 32%, var(--rule));
  transition: outline-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.six-tile__poster:hover,
.six-tile__poster:focus-visible {
  outline-color: color-mix(in srgb, var(--node) 70%, var(--rule));
  transform: translateY(-3px);
  box-shadow: 0 6px 18px -8px color-mix(in srgb, var(--node) 60%, transparent);
}
.six-tile__poster img { display: block; width: 100%; height: 100%; object-fit: cover; }
.six-tile__noart {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 6px;
  font-size: 0.6rem;
  line-height: 1.2;
  color: var(--faint);
  overflow: hidden;
}
.six-tile__name {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink);
}
.six-tile__role {
  margin: 0;
  font-family: var(--data);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--node) 58%, var(--muted));
}

/* The arrow onto the next tile, held level with the poster it steps from. */
.six-link {
  align-self: flex-start;
  height: 138px;
  display: flex;
  align-items: center;
  font-size: var(--step-1);
  color: color-mix(in srgb, var(--node) 55%, var(--muted));
}

/* Ghost tiles: the resting strip before a walk lands, and while one is in flight. */
.six-tile--ghost {
  width: 92px;
  height: 138px;
  border-radius: 4px;
  background:
    linear-gradient(
      90deg,
      color-mix(in srgb, var(--hue) 12%, var(--panel)) 0%,
      color-mix(in srgb, var(--hue) 28%, var(--panel)) 40%,
      color-mix(in srgb, var(--hue) 12%, var(--panel)) 80%
    )
    color-mix(in srgb, var(--hue) 12%, var(--panel));
  background-size: 220% 100%;
  animation: stats-shimmer 1.8s ease-in-out infinite;
}

.six__hint {
  margin: 1.1rem 0 0;
  text-align: center;
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--faint);
}
.six__hint strong { color: color-mix(in srgb, var(--hue) 70%, var(--ink)); font-weight: 600; }
.six__error {
  margin-top: 1.1rem;
  font-size: var(--step--1);
  color: var(--muted);
}
.six__error a { color: var(--hue); }

/* ---- The film detail dialog (a poster and its facts, opened from a tile) ---- */
.six-detail { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.six-detail__art {
  flex: none;
  width: 140px;
}
.six-detail__art img {
  display: block;
  width: 140px;
  height: auto;
  border-radius: 4px;
  background: var(--panel-hi);
}
.six-detail__noart {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 140px;
  min-height: 210px;
  padding: 0.75rem;
  text-align: center;
  border-radius: 4px;
  color: var(--faint);
  background: var(--panel-hi);
}
.six-detail__body { flex: 1; min-width: min(100%, 15rem); }
.six-detail__facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.9rem;
  font-size: var(--step--1);
}
.six-detail__facts dt {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}
.six-detail__facts dd { margin: 0; color: var(--ink); }
.six-detail__overview {
  margin: 0.9rem 0 0;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--muted);
}
.six-detail__overview--empty { font-style: italic; color: var(--faint); }
.six-detail__source { margin: 0.9rem 0 0; font-size: var(--step--2); }
.six-detail__source a { color: var(--hue); }
.six-detail__actions { flex-basis: 100%; margin-top: 0.4rem; }

/* ---- The aurora badge on the watchlist page ----
   A film queued in bulk from a Radar Hidden Set (site/scripts/watchlist.js reads
   its source) wears the aurora border — the same sweep the Radar hero and nav
   item carry — so you can see at a glance which films came from a set you are
   chasing, and which collaborator they belong to. */
.card--sourced .card__art {
  outline: none;
  border-radius: 5px;
  border: 2px solid transparent;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd) border-box;
  background-size: auto, 240% 100%;
  background-position: 0 0, var(--aurora) 50%;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}
.card__from {
  display: flex;
  align-items: center;
  gap: 0.4em;
  flex-wrap: wrap;
  margin: 0.1rem 0 0;
  font-family: var(--data);
  font-size: var(--step--2);
  color: var(--muted);
}
.card__from-mark {
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: linear-gradient(100deg, #5dadbd, #90a0d2, #ab97d4, #cf89cf, #cc9481, #c6a052, #5dadbd);
  background-size: 240% 100%;
  background-position: var(--aurora) 50%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stats-aurora 9s ease-in-out infinite alternate;
}

@media (prefers-reduced-motion: reduce) {
  .radar-hero__num,
  .nav__radar-text,
  .card--sourced .card__art,
  .card__from-mark { animation: none; --aurora: 30%; }
  .hset { animation: none; }
  .hset__miss,
  .hset__add { transition: none; }
  .six-step,
  .six-tile--ghost,
  .six__shuffle--busy svg { animation: none; }
  .six-tile__poster,
  .six__shuffle { transition: none; }
}
