/* Franchisarr's own styles, layered on top of Pico.
 *
 * Dark is the default (matching Radarr/Sonarr/Overseerr) and is set as `data-theme="dark"` on
 * <html> rather than by overriding Pico's colours here. Pico ships both palettes; Phase 9's
 * light/dark toggle only has to change that one attribute and remember the choice. Nothing in
 * this file may assume a dark background.
 */

:root {
  --franchisarr-header-border: var(--pico-muted-border-color);
  /* The page's colour, resolved here (0.60.1): Pico redefines --pico-background-color on every
     link -- SVG ones included -- as transparent, so the map's linked nodes were drawn hollow. */
  --franchisarr-page: var(--pico-background-color);
}

body > header.container {
  border-bottom: 1px solid var(--franchisarr-header-border);
  margin-bottom: var(--pico-block-spacing-vertical);
}

/* Pico scales the root font size with the viewport, up to 131.25% at 1536px and wider -- so
   on a desktop monitor every nav link and button was a fifth larger than on a laptop. A tool
   that lives in a browser tab next to Radarr and Sonarr should sit at their scale: 16px, flat.
   Buttons and the nav are trimmed a step below that as well. */
:root,
:host {
  --pico-font-size: 100%;
}

@media (min-width: 576px), (min-width: 768px), (min-width: 1024px), (min-width: 1280px), (min-width: 1536px) {
  :root,
  :host {
    --pico-font-size: 100%;
  }
}

.app-nav a,
.app-nav button,
.app-nav small {
  font-size: 0.9375rem;
}

.app-nav button {
  padding: 0.4rem 0.8rem;
}

button,
[role="button"],
input[type="submit"] {
  --pico-form-element-spacing-vertical: 0.55rem;
  --pico-form-element-spacing-horizontal: 0.9rem;
  font-size: 0.9375rem;
  width: auto;   /* Pico stretches form buttons edge to edge; a button is the size of its label */
}

.app-brand {
  align-items: center;
  display: inline-flex;
  font-weight: 700;
  gap: 0.65rem;
  text-decoration: none;
}

.app-brand-icon {
  border-radius: 10px;
  flex: 0 0 auto;
  height: 48px;
  width: 48px;
}

/* The lockup: name over tagline, the same arrangement as the README banner, but built from
   live text so a theme recolours it. The tagline is a line of muted text, not a heading. */
.app-brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.app-brand-name {
  font-size: 1.25rem;
}

.app-brand-tagline {
  color: var(--pico-muted-color);
  font-size: 0.75rem;
  font-weight: 400;
}

@media (max-width: 767px) {
  .app-brand-icon { height: 36px; width: 36px; }
  .app-brand-name { font-size: 1.1rem; }
  .app-brand-tagline { display: none; }
}

/* The nav wraps; it never widens the page. Pico lays nav lists out as a single flex row, and
   with eleven items that row is wider than a phone -- which pushes the whole document wider,
   so every card below it sizes to that width and its text runs off the screen. Wrapping here is
   what makes everything below behave. */
.app-nav {
  flex-wrap: wrap;
  row-gap: 0.25rem;
}

.app-nav ul {
  flex-wrap: wrap;
  margin-bottom: 0;
  row-gap: 0.25rem;
}

/* The menu (0.40.0): Browse / Manage / your name dropdowns and search on a computer; on a phone
   just the brand, search and a ☰ that opens a panel with every section laid out. The phone bar
   holds three things, so it never needs to wrap: the old eleven-link bar scrolled sideways on
   an iPhone even with flex-wrap, and needed plain block flow to stay inside the screen. */
.nav-phone, .nav-panel { display: none; }
/* Pico draws a nav dropdown's summary like a <select>; here it's a link in the bar, like the
   others were. */
.app-nav details.dropdown > summary:not([role]) {
  background: none;
  border-color: transparent;
  box-shadow: none;
  color: var(--pico-primary);
}
.app-nav details.dropdown[open] > summary:not([role]),
.app-nav details.dropdown > summary:not([role]):hover { border-color: var(--pico-muted-border-color); }
.app-nav details.dropdown > summary + ul.nav-end { left: auto; right: 0; }
/* A plain link in the bar (Playlists) sized like the dropdowns' summaries beside it: Pico gives
   nav links 15px text and a -8px margin, which sat it 2px low and a size smaller (0.44.0). */
.app-nav a.nav-link { font-size: 1rem; line-height: 1.5; margin: -1px 0 0; padding: 0.375rem 0.5rem; }
.app-nav details.dropdown.nav-here > summary,
.app-nav .nav-link.nav-here,
.app-nav .nav-search.nav-here { color: var(--pico-primary); font-weight: 600; }
.app-nav details.dropdown [aria-current="page"],
.nav-panel [aria-current="page"] { color: var(--pico-primary); font-weight: 600; }
.nav-menu-button {
  background: none;
  border: none;
  color: var(--pico-dropdown-color);
  margin: 0;
  padding: 0;
  text-align: start;
  width: 100%;
}
.nav-menu-button:hover, .nav-menu-button:focus { background: none; color: var(--pico-primary); }
/* The sign-out form's and the theme toggle's own padding rules are more specific than the class
   above; inside a menu these are menu entries, flush with the links. */
.nav-panel li .nav-menu-button,
.app-nav details.dropdown li .nav-menu-button,
.nav-panel .nav-form .nav-menu-button,
.app-nav details.dropdown .nav-form .nav-menu-button { padding: 0; }
.app-nav details.dropdown .nav-form { display: block; }

@media (max-width: 767px) {
  .nav-desktop { display: none; }
  .nav-phone { display: list-item; }
  .app-nav { flex-wrap: nowrap; align-items: center; }
  .app-nav ul:last-of-type { flex-wrap: nowrap; margin-right: 0; }
  .nav-panel:not([hidden]) {
    display: block;
    border-top: 1px solid var(--pico-muted-border-color);
    padding: 0.5rem 0 0.25rem;
  }
  .nav-panel ul { list-style: none; margin: 0 0 0.5rem; padding: 0; }
  .nav-panel li { display: inline-block; list-style: none; margin: 0 1rem 0.35rem 0; padding: 0; }
  .nav-panel-heading { color: var(--pico-muted-color); font-size: 0.8rem; margin: 0.25rem 0; text-transform: uppercase; }
  .nav-panel .nav-menu-button { color: var(--pico-primary); width: auto; }
}

/* Pico gives any aria-busy element a spinner -- and white-space: nowrap, which is right for a
   button and wrong for a card: the scan panel's text stopped wrapping and ran off the screen.
   The spinner stays; the text wraps. */
article[aria-busy="true"] {
  white-space: normal;
}

/* Belt and braces: nothing else gets to widen the page either. Long unbroken strings -- a
   webhook URL, a Plex path -- wrap instead. */
/* On both, because iOS Safari ignores it on body alone. */
html,
body {
  overflow-x: hidden;
}

main.container,
header.container {
  overflow-wrap: anywhere;
}

/* Shown while an htmx request is in flight. htmx toggles .htmx-request on the element. */
.htmx-indicator {
  opacity: 0;
  transition: opacity 150ms ease-in;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

footer.container {
  color: var(--pico-muted-color);
  font-size: 0.875rem;
}

.login-card {
  margin-inline: auto;
  max-width: 28rem;
}

.login-error {
  color: var(--pico-del-color);
}

/* Keeps the sign-out button inline with the rest of the nav instead of on its own block line. */
.nav-form {
  display: inline;
  margin: 0;
}

.nav-form button {
  margin: 0;
  padding: 0.25rem 0.75rem;
  width: auto;
}

/* ---------------------------------------------------------------------------
 * Movie screens.
 *
 * Every colour below is a Pico variable, never a literal. That is what lets
 * theme-adapter.css repaint the whole UI from a theme.park stylesheet without
 * touching this file. A hex value here would be a hole in the theme.
 * ------------------------------------------------------------------------ */

.collection-grid {
  display: grid;
  gap: var(--pico-spacing);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
}

.collection-card {
  margin-bottom: 0;
}

/* Every card is flat: Pico's article header and footer bands (their own background and a rule)
   made each card a little three-storey building. A card is a title, a body and its actions,
   with nothing drawn between them, the same everywhere in the app. */
article > header,
article > footer {
  background: transparent;
  border: 0;
  margin: 0;
  padding: 0;
}

article > header { margin-bottom: 0.5rem; }
article > footer { margin-top: 0.75rem; }
article > header > strong { font-size: 1rem; }
article > p:last-of-type { margin-bottom: 0; }
article > footer button,
article > footer [role="button"] { margin-bottom: 0.25rem; }

/* The home page: one card per section, packed into a grid, buttons on a common baseline. */
.home-grid {
  display: grid;
  gap: var(--pico-spacing);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  margin-bottom: var(--pico-spacing);
}

.home-grid > article {
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}

.home-grid > article > p { flex: 1 1 auto; }
.home-grid > article > footer { margin-top: auto; padding-top: 0.75rem; }

/* Franchises: a lot of them, so the poster is a thumbnail beside the text rather than the
   whole card, and the grid packs more across. */
.collection-grid--compact {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
}

.collection-card--compact {
  align-content: start;          /* rows hug their content; never spread to the card's height */
  column-gap: var(--pico-spacing);
  display: grid;
  grid-auto-rows: min-content;
  grid-template-columns: 5.5rem 1fr;
  row-gap: 0.35rem;
}

.collection-card--compact .collection-poster {
  grid-row: 1 / span 5;
  margin-bottom: 0;
}

.collection-card--compact > :not(.collection-poster) {
  grid-column: 2;
}

/* No artwork: an empty poster-shaped block, so the text column lines up with its neighbours. */
.collection-poster--empty {
  aspect-ratio: 2 / 3;
  background: var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  width: 100%;
}

/* Pico gives an article's header a band and a rule; on a compact card that is just a bar
   through the title. Plain text, flush with the poster's top. */
.collection-card--compact > header {
  background: transparent;
  border-bottom: 0;
  margin: 0;
  padding: 0;
}

.collection-card--compact > header a {
  font-size: 1rem;
  line-height: 1.3;
}

.collection-card--compact .collection-counts { margin: 0; }
.collection-card--compact > p { margin: 0; }
.collection-card--compact > .missing-preview {
  line-height: 1.35;
  margin: 0;
}

.collection-card--compact > .missing-preview li { margin-bottom: 0.15rem; }

/* Film and show tiles (partials/tile.html): the compact card, one title each. Detail pages,
   Upcoming and Spin-offs all use it, so every list of titles has the Collections page's shape. */
.film-tile > header strong { font-size: 1rem; line-height: 1.3; }
.film-tile > header .rating { margin-left: 0.25em; }

/* Titles already in the library: the same tile, smaller and denser, so the missing ones above
   stay what the eye lands on. */
/* A long list sorted by release gets a slim heading where each decade starts (0.71.0), its
   decade large and faint behind it. A grid row of its own. */
.decade-divider {
  align-items: center;
  color: var(--pico-muted-color);
  display: flex;
  font-size: 0.9rem;
  font-weight: 600;
  gap: 0.75rem;
  grid-column: 1 / -1;
  letter-spacing: 0.08em;
  margin: 0.4rem 0 0;
  overflow: hidden;
  position: relative;
}
.decade-divider::after { border-top: 1px solid var(--pico-muted-border-color); content: ""; flex: 1; }
.decade-divider::before {
  content: attr(data-decade);
  font-size: 2.6rem;
  font-weight: 800;
  line-height: 1;
  opacity: 0.07;
  pointer-events: none;
  position: absolute;
  right: 0;
}

.collection-grid--owned {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
}
.collection-grid--owned .collection-card--compact { grid-template-columns: 3.5rem 1fr; }
.collection-grid--owned .film-tile > header strong { font-size: 0.9rem; }
.film-tile.owned .collection-poster,
.film-tile.hidden .collection-poster { opacity: 0.55; }

.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.25rem 0 0;
}

.card-actions button { margin-bottom: 0; }

.collection-counts { margin-bottom: 0.25rem; }
.collection-card--compact > p:last-child,
.collection-card--compact > .missing-preview { margin-bottom: 0; }

.collection-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  font-size: 0.875rem;
  margin-bottom: var(--pico-spacing);
}

.count-owned { color: var(--pico-muted-color); }
.count-missing { color: var(--pico-primary); font-weight: 600; }
.count-upcoming { color: var(--pico-muted-color); font-style: italic; }

.missing-preview {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.9rem;
}

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

.film-row {
  align-items: center;
  border-bottom: 1px solid var(--pico-muted-border-color);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  padding: 0.6rem 0;
}

.film-row:last-child { border-bottom: none; }

.film-title { flex: 1 1 14rem; }

.film-meta { flex: 0 0 auto; font-size: 0.875rem; }

.film-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-left: auto;
}

.film-actions button {
  font-size: 0.8rem;
  margin: 0;
  padding: 0.3rem 0.7rem;
  width: auto;
}

/* Upcoming films are visually quieter than missing ones on purpose: they are not something the
 * reader can act on today, and giving them equal weight is what made the raw gap list unusable. */
.upcoming-section .film-row,
.upcoming-section .film-tile { opacity: 0.75; }

.owned .film-title::before {
  color: var(--pico-primary);
  content: "✓ ";
}

.muted { color: var(--pico-muted-color); }

.notice-good { color: var(--pico-primary); }
.notice-bad { color: var(--pico-del-color); }
.notice-warn { color: var(--pico-muted-color); }

dialog article { max-width: 32rem; }

.candidates-panel { margin-top: var(--pico-spacing); }

.test-result { flex-basis: 100%; }

details > summary { cursor: pointer; margin-block: var(--pico-spacing); }

/* Only one of the two glyphs is shown, whichever offers the *other* mode. Driven by the same
 * data-theme attribute everything else uses, so it stays correct under an external theme. */
.theme-toggle { margin: 0; padding: 0.25rem 0.6rem; width: auto; }

[data-theme="light"] .theme-toggle-light,
:root:not([data-theme="light"]) .theme-toggle-dark { display: none; }

/* --- Artwork ------------------------------------------------------------------------------
 * Posters are given explicit width and height attributes in the markup and a fixed aspect ratio
 * here, so the layout doesn't jump as images arrive. Without that, a grid of a hundred cards
 * reflows repeatedly while it loads.
 */

.collection-poster {
  display: block;
  margin-bottom: var(--pico-spacing);
}

.collection-poster img,
.collection-heading-poster {
  aspect-ratio: 2 / 3;
  border-radius: var(--pico-border-radius);
  display: block;
  object-fit: cover;
  width: 100%;
}

.collection-heading {
  align-items: start;
  display: flex;
  gap: var(--pico-spacing);
}

/* With a backdrop the heading becomes a band: the image sits behind, the poster and title on
   top. The gradient is what makes the text legible over an arbitrary photograph -- without it,
   a pale backdrop and pale text are a coin toss. It fades to the page background rather than to
   black so a light theme doesn't get a dark bar across it. */
.collection-heading.has-backdrop {
  align-items: end;
  border-radius: var(--pico-border-radius);
  isolation: isolate;
  margin-bottom: var(--pico-block-spacing-vertical);
  min-height: 14rem;
  padding: var(--pico-spacing);
  position: relative;
}

/* The backdrop sits in its own clipping frame: the band itself can't clip (its Playlist menu
   hangs out of it, 0.44.0), and Showcase zooms and moves the image inside (0.47.0). */
.collection-backdrop-frame {
  border-radius: inherit;
  inset: 0;
  overflow: hidden;
  position: absolute;
  z-index: -2;
}
.collection-backdrop {
  height: 100%;
  inset: 0;
  object-fit: cover;
  object-position: center 30%;
  position: absolute;
  width: 100%;
}

/* A director's banner is their photo, softened to a wash of its colours (0.71.0): a portrait
   cropped to a letterbox is a forehead, not a picture. */
.collection-heading--person .collection-backdrop { filter: blur(28px) saturate(0.8) brightness(0.55); object-position: center 20%; scale: 1.15; }

.collection-heading.has-backdrop::after {
  background: linear-gradient(
    to top,
    var(--pico-background-color) 0%,
    color-mix(in srgb, var(--pico-background-color) 82%, transparent) 45%,
    color-mix(in srgb, var(--pico-background-color) 35%, transparent) 100%
  );
  border-radius: inherit;
  content: "";
  inset: 0;
  position: absolute;
  z-index: -1;
}

/* The banner's playlist control (0.44.0), bottom right; its menu opens right-aligned. */
.heading-actions {
  align-items: flex-end;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-left: auto;
  padding-bottom: var(--pico-typography-spacing-vertical);
}
.heading-actions details.dropdown { margin: 0; }
.heading-actions summary[role="button"], .heading-actions > button { width: auto; margin: 0; white-space: nowrap; }
.playlist-kept { color: var(--pico-muted-color); }
#playlist-busy { display: block; }

/* How complete a set is, on every card (0.44.0): Pico's own progress bar, made thin. */
progress.completeness { height: 0.35rem; margin: 0 0 0.5rem; }
/* Watched of owned (0.72.0): thinner, right under the owned bar, in the muted colour so the two
   read as "have" and "seen". */
progress.watched-progress { --pico-progress-color: var(--pico-muted-color); height: 0.2rem; margin: -0.35rem 0 0.5rem; }

.collection-heading-inner {
  align-items: end;
  display: flex;
  gap: var(--pico-spacing);
  width: 100%;
}

.collection-heading:not(.has-backdrop) .collection-heading-inner {
  align-items: start;
}

.collection-heading:not(.has-backdrop) {
  margin-bottom: var(--pico-block-spacing-vertical);
}

/* The wordmark is the title, so it takes the h1's spacing and none of its type styling. */
.collection-logo-title {
  margin-bottom: calc(var(--pico-typography-spacing-vertical) * 0.5);
}

.collection-logo {
  display: block;
  max-height: 5.5rem;
  max-width: min(100%, 22rem);
  object-fit: contain;
  object-position: left bottom;
  width: auto;
}

.collection-heading-poster {
  flex: 0 0 8rem;
  width: 8rem;
}

.film-poster {
  aspect-ratio: 2 / 3;
  border-radius: calc(var(--pico-border-radius) / 2);
  flex: 0 0 2.5rem;
  object-fit: cover;
  width: 2.5rem;
}

/* The poster is decoration; the title beside it carries the meaning. Dimming an owned film's
 * artwork keeps the missing ones the thing the eye lands on. */
.film-row.owned .film-poster { opacity: 0.55; }

/* A film's score: small, after the title, never louder than it. */
.rating {
  color: var(--pico-muted-color);
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.gap-controls {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pico-spacing);
  margin-bottom: var(--pico-block-spacing-vertical);
}
.gap-controls p, .gap-controls form { margin-bottom: 0; }
.gap-filter { align-items: center; display: flex; flex-wrap: wrap; gap: 0.5em; }
.gap-filter input[type="number"] { margin-bottom: 0; width: 5.5em; }
.gap-filter button { margin-bottom: 0; width: auto; }
.gap-controls > button { margin-bottom: 0; margin-left: auto; width: auto; }
/* A page's heading with its one page-wide action beside it (Collections' Rescan, 0.44.0). */
.page-heading { align-items: flex-start; display: flex; flex-wrap: wrap; gap: var(--pico-spacing); }
.page-heading hgroup { flex: 1 1 20rem; }
.page-heading > button { margin: 0 0 var(--pico-spacing); width: auto; }

.film-row.hidden .film-poster { opacity: 0.55; }
.hidden-section summary { color: var(--pico-muted-color); cursor: pointer; }

/* Outbound "what is this?" links sit after the title, quiet until wanted. */
.show-links {
  display: inline-flex;
  gap: 0.6em;
  margin-left: 0.6em;
  font-size: 0.8em;
}
.show-links a { color: var(--pico-muted-color); text-decoration: none; }
.show-links a:hover { color: var(--pico-primary); text-decoration: underline; }

@media (max-width: 480px) {
  .collection-heading { display: block; }
  .collection-heading-inner { display: block; }
  .collection-heading-poster { margin-bottom: var(--pico-spacing); width: 6rem; flex-basis: 6rem; }
  .collection-heading.has-backdrop { min-height: 0; }
  .heading-actions { align-items: flex-start; padding-bottom: 0; }
  .collection-logo { max-height: 4rem; }
}

/* Media servers: the inline edit form sits below the row it belongs to. */
.server-edit { flex-basis: 100%; margin-top: var(--pico-spacing); }
[x-cloak] { display: none !important; }

/* Watched state on owned titles: a quiet label, since the row already carries a tick. */
.watched-mark {
  color: var(--pico-primary);
  border: 1px solid var(--pico-primary);
  border-radius: var(--pico-border-radius);
  padding: 0 0.4em;
  margin-left: 0.4em;
  font-size: 0.7rem;
  vertical-align: middle;
}
.owned-where { white-space: nowrap; }
.server-meta { flex: 1 1 100%; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.plex-signin small { display: block; margin-top: calc(var(--pico-spacing) / 2); }

/* Pager under a card grid. Only rendered when a list is long enough to be cut into pages. */
.pager {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1.5rem 0 0.5rem;
}
.pager a[role="button"] { width: auto; margin: 0; }

/* "Make a playlist" under a detail page's heading. */
.playlist-action { margin: 0 0 var(--pico-spacing); }
.playlist-action > button { margin-bottom: 0.25rem; width: auto; }
.playlist-action p { margin: 0.5rem 0 0; }
.playlist-label { display: block; margin-bottom: 0.25rem; }
.playlist-buttons { display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; }
.playlist-buttons button { width: auto; margin: 0; }

/* Deleting Franchisarr's playlists (Servers page): the warning, then the result. */
.playlist-delete { margin: 0.5rem 0; }
.playlist-delete ul { margin-bottom: 0.5rem; }
.playlist-delete-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.playlist-delete-buttons button { width: auto; margin: 0; }
.playlist-delete-confirm {
  background: var(--pico-del-color);
  border-color: var(--pico-del-color);
}

/* The sort control: "Sort [field ▾] [↓ Most first]". The same on every list page. */
.sort-control {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
}
.sort-control label { margin: 0; }
.sort-control select { margin: 0; min-width: 11rem; width: auto; }
.sort-control button { margin: 0; white-space: nowrap; width: auto; }
@media (max-width: 767px) {
  .sort-control select { flex: 1 1 9rem; min-width: 0; }
}

/* "Showing only Jellyfin" (base.html): the restore button sits on the line when it fits. */
.solo-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
}

/* Search (search.html). */
.search-form input[type="search"] { margin-bottom: var(--pico-spacing); }
.nav-search svg { vertical-align: middle; }
.search-status--missing { color: var(--pico-primary); }
.search-links { margin: 0.25rem 0 0; }

/* Servers page: playlist bulk actions. */
.bulk-actions select { width: auto; }
.bulk-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.bulk-buttons button { width: auto; margin: 0; }
.playlist-bulk fieldset label { display: block; }

/* Playlists page (playlist sync). */
/* It shrinks and wraps: .film-meta doesn't, and a long "blocked" message ran off a phone. */
.sync-meta { display: flex; flex-direction: column; gap: 0.25rem; flex: 1 1 16rem; min-width: 0; }
.sync-copy--blocked, .sync-copy--error { color: var(--pico-del-color); }
.sync-copy details { margin: 0; }
.sync-copy button, .sync-missing button { width: auto; margin: 0 0 0 0.5rem; padding: 0.1rem 0.6rem; font-size: 0.8rem; }
.sync-missing { margin: 0.25rem 0 0; }
.sync-missing li { list-style: none; margin-bottom: 0.25rem; }
.sync-now button { width: auto; margin-right: 0.75rem; }

/* Playlists page (0.42.0): status line, schedule and tabs above every tab. */
.playlists-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: flex-start;
  margin-bottom: var(--pico-spacing);
}
.playlists-actions .sync-now { margin: 0; }
.playlists-actions #sync-status { flex: 1 1 100%; }
.playlists-actions #sync-status:has(.sync-now) { flex: 0 0 auto; }
.sync-schedule { flex: 1 1 20rem; margin: 0; }
.sync-schedule-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.sync-schedule-row select, .sync-schedule-row input, .sync-schedule-row button { width: auto; margin: 0; }
.sync-schedule-row input { flex: 0 1 12rem; min-width: 0; }
nav.tabs { border-bottom: 1px solid var(--pico-muted-border-color); margin-bottom: var(--pico-spacing); }
nav.tabs ul { flex-wrap: wrap; margin-bottom: 0; }
@media (max-width: 575px) {
  nav.tabs li { padding-left: 0.4rem; padding-right: 0.4rem; }
  nav.tabs a { font-size: 0.9rem; }
}
nav.tabs a[aria-current="page"] {
  border-bottom: 2px solid var(--pico-primary);
  border-radius: 0;
  font-weight: 600;
}
.sync-defaults { margin-bottom: var(--pico-spacing); }
.sync-defaults legend { font-weight: 600; }
.sync-defaults label { display: inline-flex; align-items: center; gap: 0.35rem; margin-right: 1rem; }
.sync-defaults small { display: block; margin-bottom: 0.5rem; }
.sync-defaults label.sync-every { display: flex; }
.sync-defaults button { width: auto; }
.sync-change { margin-left: 0.5rem; font-size: 0.875rem; }
.sync-own { display: inline-flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; align-items: baseline; }
.sync-copy--pending, .sync-copy--small { color: var(--pico-muted-color); }
.franchisarr-set form button { width: auto; margin: 0; }
.playlist-add-many { margin-bottom: var(--pico-spacing); }

/* Playlist sync page: the checklist. */
.sync-checklist input[type="search"] { margin-bottom: var(--pico-spacing); }
.sync-pick { flex: 1 1 18rem; min-width: 0; }
.sync-pick label { display: inline; margin: 0 0.75rem 0 0; }
.sync-pick input { margin: 0 0.5rem 0 0; }
.sync-targets { display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; align-items: baseline; }
.sync-targets label { margin: 0; font-size: 0.875rem; }
.sync-select-all { font-size: 0.875rem; margin-bottom: 0.5rem; }
.bulk-on select { width: auto; }

/* Quieter cards (0.44.0): a card's title is a link without an underline until it's pointed at,
   and a rare action waits behind ⋯. */
.collection-card header a { text-decoration: none; }
.collection-card header a:hover, .collection-card header a:focus-visible { text-decoration: underline; }
details.dropdown.more-actions { display: inline-block; margin: 0; vertical-align: middle; }
details.dropdown.more-actions > summary:not([role]) {
  background: none;
  border-color: transparent;
  box-shadow: none;
  color: var(--pico-muted-color);
  height: auto;
  line-height: 1;
  padding: 0.5rem 0.6rem;
  width: auto;
}
details.dropdown.more-actions > summary:not([role]):hover { border-color: var(--pico-muted-border-color); }
details.dropdown.more-actions > summary::after { display: none; }
details.dropdown.more-actions > ul { left: auto; right: 0; min-width: 14rem; }

/* The release-order strip on a franchise or collection page (0.45.0). It scrolls sideways inside
   itself; min-width: 0 is what stops a long strip widening the page on a phone. */
.timeline { margin-bottom: var(--pico-block-spacing-vertical); min-width: 0; }
.timeline-heading { margin-bottom: 0.5rem; }
.timeline-heading small { margin-left: 0.5rem; }
.timeline-strip {
  display: flex;
  gap: 0.6rem;
  list-style: none;
  margin: 0;
  overflow-x: auto;
  padding: 0 0 0.6rem;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}
/* Where a decade starts along the strip (0.71.0): its name above, a hairline to its left. */
.timeline-strip:has(.timeline-step--decade) .timeline-step { padding-top: 1.15rem; position: relative; }
.timeline-step--decade::before {
  color: var(--pico-muted-color);
  content: attr(data-decade);
  font-size: 0.72rem;
  font-weight: 600;
  left: 0;
  letter-spacing: 0.06em;
  position: absolute;
  top: 0;
}
.timeline-step--decade:not(:first-child)::after {
  border-left: 1px solid var(--pico-muted-border-color);
  bottom: 1.4rem;
  content: "";
  left: -0.32rem;
  position: absolute;
  top: 0.1rem;
}
.timeline-step {
  display: flex;
  flex: 0 0 5.25rem;
  flex-direction: column;
  gap: 0.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
  scroll-snap-align: start;
}
.timeline-poster {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  border: none;
  border-radius: var(--pico-border-radius);
  display: block;
  margin: 0;
  overflow: hidden;
  padding: 0;
  position: relative;
  width: 100%;
}
.timeline-poster img { display: block; height: 100%; object-fit: cover; width: 100%; }
.timeline-placeholder {
  align-items: center;
  color: var(--pico-muted-color);
  display: flex;
  font-size: 0.7rem;
  height: 100%;
  justify-content: center;
  padding: 0.3rem;
  text-align: center;
}
.timeline-step--missing .timeline-poster img, .timeline-step--upcoming .timeline-poster img {
  filter: grayscale(1);
  opacity: 0.4;
}
.timeline-step--missing .timeline-poster, .timeline-step--upcoming .timeline-poster {
  outline: 1px dashed var(--pico-muted-border-color);
  outline-offset: -1px;
}
button.timeline-poster { cursor: pointer; }
button.timeline-poster:hover img, button.timeline-poster:focus-visible img { filter: none; opacity: 0.85; }
button.timeline-poster:hover, button.timeline-poster:focus-visible { outline: 2px solid var(--pico-primary); }
.timeline-tick {
  background: var(--pico-primary-background);
  border-radius: 999px;
  bottom: 0.25rem;
  color: var(--pico-primary-inverse);
  font-size: 0.7rem;
  line-height: 1;
  padding: 0.2rem 0.3rem;
  position: absolute;
  right: 0.25rem;
}
.timeline-year { color: var(--pico-muted-color); font-size: 0.75rem; text-align: center; }
.timeline-step--owned .timeline-year { color: var(--pico-color); }

/* Home page poster rows (0.45.0): a row of cards that scrolls sideways inside itself. */
.poster-row { margin-bottom: var(--pico-block-spacing-vertical); min-width: 0; }
.poster-row-heading { align-items: baseline; display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; justify-content: space-between; }
.poster-row-heading h2 { font-size: 1.25rem; margin-bottom: 0.5rem; }
.poster-row-heading h2 small { font-size: 0.8rem; font-weight: 400; margin-left: 0.5rem; }
.poster-row-cards {
  display: flex;
  gap: 0.9rem;
  list-style: none;
  margin: 0;
  overflow-x: auto;
  padding: 0 0 0.6rem;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}
.poster-row-cards > li {
  display: flex;
  flex: 0 0 8.5rem;
  flex-direction: column;
  gap: 0.15rem;
  list-style: none;
  margin: 0;
  scroll-snap-align: start;
}
.poster-row-card {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  border-radius: var(--pico-border-radius);
  display: block;
  margin-bottom: 0.3rem;
  overflow: hidden;
}
.poster-row-card img { display: block; height: 100%; object-fit: cover; transition: transform 0.15s; width: 100%; }
.poster-row-card:hover img, .poster-row-card:focus-visible img { transform: scale(1.04); }
.poster-row-title { font-size: 0.85rem; font-weight: 600; line-height: 1.25; }
.poster-row-cards small { font-size: 0.75rem; line-height: 1.3; }

/* The count cards under the rows: still there, smaller. */
.home-grid--compact > article { font-size: 0.9rem; padding: calc(var(--pico-spacing) * 0.75); }
.home-grid--compact > article > footer { padding-top: 0.5rem; }
.home-grid--compact [role="button"] { font-size: 0.85rem; padding: 0.35rem 0.75rem; }

/* Playlists tabs, 0.46.0: collapsible groups (a server's playlists, a kind of Franchisarr's),
   filter chips, per-server status chips, and a save bar that stays in view. */
.sync-group {
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  margin-bottom: var(--pico-spacing);
  padding: 0.5rem 1rem;
}
.sync-group > summary { padding: 0.35rem 0; }
.sync-group > summary small { margin-left: 0.5rem; }
.sync-group[open] > summary { border-bottom: 1px solid var(--pico-muted-border-color); margin-bottom: 0.75rem; }
.sync-filter { align-items: center; display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; margin-bottom: var(--pico-spacing); }
.sync-filter input[type="search"] { flex: 1 1 16rem; margin: 0; }
.sync-filter-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.sync-filter-chip {
  background: none;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 999px;
  color: var(--pico-muted-color);
  font-size: 0.85rem;
  margin: 0;
  padding: 0.25rem 0.8rem;
  width: auto;
}
.sync-filter-chip[aria-pressed="true"] {
  background: var(--pico-primary-background);
  border-color: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
}
.sync-filter-chip small { opacity: 0.8; }
.sync-chips { align-items: flex-start; display: flex; flex: 1 1 16rem; flex-wrap: wrap; gap: 0.3rem; min-width: 0; }
.sync-chip {
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 999px;
  display: inline-block;
  font-size: 0.78rem;
  margin: 0;
  padding: 0.1rem 0.6rem;
}
details.sync-chip { margin-bottom: 0; }
details.sync-chip > summary { cursor: pointer; line-height: 1.5; list-style: none; margin: 0; padding: 0; }
.sync-chip { line-height: 1.5; }
details.sync-chip > summary::-webkit-details-marker { display: none; }
details.sync-chip > summary::after { display: none; }
details.sync-chip[open] { border-radius: var(--pico-border-radius); flex-basis: 100%; }
.sync-chip-body { font-size: 0.85rem; padding: 0.4rem 0.2rem; }
.sync-chip-body p { margin-bottom: 0.4rem; }
.sync-chip--ok { color: var(--pico-ins-color); }
.sync-chip--warn { color: var(--pico-primary); }
.sync-chip--error { border-color: var(--pico-del-color); color: var(--pico-del-color); }
.sync-chip--pending, .sync-chip--small { color: var(--pico-muted-color); }
.sync-chip-body, .sync-chip-body button { color: var(--pico-color); }
.sync-savebar {
  align-items: center;
  background: var(--pico-card-background-color);
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  bottom: 0.5rem;
  box-shadow: var(--pico-card-box-shadow);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: var(--pico-spacing);
  padding: 0.6rem 1rem;
  position: sticky;
  z-index: 5;
}
.sync-savebar span { flex: 1 1 auto; }
.sync-savebar button { margin: 0; width: auto; }
.set-thumb {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  border-radius: var(--pico-border-radius);
  display: block;
  flex: 0 0 2.9rem;
  overflow: hidden;
  width: 2.9rem;
}
.set-thumb img { display: block; height: 100%; object-fit: cover; width: 100%; }

/* A franchise no one collection stands for gets four of its films' posters as its poster
   (0.46.1): a 2x2 grid in the poster's own 2:3 box. */
.poster-mosaic {
  aspect-ratio: 2 / 3;
  border-radius: var(--pico-border-radius);
  display: grid;
  gap: 2px;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  overflow: hidden;
}
.poster-mosaic img { aspect-ratio: auto; border-radius: 0; display: block; height: 100%; min-height: 0; object-fit: cover; width: 100%; }

/* The Trophy case (0.52.0): milestones as chips, then shelves of posters. Showcase adds the gold. */
.trophy-badges ul { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0 0 var(--pico-block-spacing-vertical); padding: 0; }
.trophy-badge {
  align-items: center;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 999px;
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0.3rem 0.85rem;
}
.trophy-badge--next { border-style: dashed; color: var(--pico-muted-color); }
.trophy-badge progress { height: 0.3rem; margin: 0; width: 4rem; }
.trophy-shelf {
  display: grid;
  gap: 1.25rem 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 8.5rem), 1fr));
  margin-bottom: var(--pico-block-spacing-vertical);
}
.trophy { color: inherit; display: flex; flex-direction: column; gap: 0.2rem; text-decoration: none; }
.trophy strong { font-size: 0.9rem; line-height: 1.25; }
.trophy small { font-size: 0.75rem; }
.trophy-frame {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  border-radius: var(--pico-border-radius);
  display: block;
  margin-bottom: 0.3rem;
  overflow: hidden;
  position: relative;
}
.trophy-frame img, .trophy-blank { display: block; height: 100%; object-fit: cover; width: 100%; }
.trophy-frame .poster-mosaic { border-radius: 0; height: 100%; }
.trophy-new { color: var(--pico-primary); font-weight: 600; }

/* Surprise me (0.53.0): the pick, poster beside its details. */
.surprise-button { margin: 0 0 var(--pico-spacing); white-space: nowrap; width: auto; }
.surprise-card {
  align-items: start;
  display: grid;
  gap: 0 1.25rem;
  grid-template-columns: minmax(6rem, 10rem) 1fr;
}
.surprise-card--empty { display: block; }
.surprise-poster { aspect-ratio: 2 / 3; border-radius: var(--pico-border-radius); overflow: hidden; position: relative; }
.surprise-poster img, .surprise-poster > span { display: block; height: 100%; object-fit: cover; width: 100%; }
.surprise-body h2 { margin-bottom: 0.25rem; }
.surprise-kicker, .surprise-meta { margin-bottom: 0.4rem; }
.surprise-plot { font-size: 0.92rem; }
.surprise-card .card-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.surprise-card .card-actions button { margin: 0; width: auto; }
@media (max-width: 575px) {
  .surprise-card { gap: 0 0.9rem; grid-template-columns: 6.5rem 1fr; }
  .surprise-body h2 { font-size: 1.2rem; }
}

/* Trailers (0.54.0): a wide pop-up with a 16:9 player; ▶ on a tile is a small square button. */
.trailer-dialog > article { max-width: none; width: min(60rem, 94vw); }
.trailer-frame { aspect-ratio: 16 / 9; border-radius: var(--pico-border-radius); overflow: hidden; }
.trailer-frame iframe { border: 0; display: block; height: 100%; width: 100%; }
.card-actions .trailer-button { padding-inline: 0.7rem; }

/* The Franchise map (0.55.0), in three shapes. SVG colours come from these rules, not
   attributes: a presentation attribute can't take a var(). */
.franchise-map { margin-bottom: var(--pico-block-spacing-vertical); }
.franchise-map > summary { margin-bottom: 0.5rem; }
.fm-switch { align-items: center; display: flex; flex-wrap: wrap; gap: 0.4rem 0.5rem; margin-bottom: 0.75rem; }
.fm-switch button { margin: 0; padding: 0.3rem 0.85rem; width: auto; }
.fm-switch button[aria-selected="true"] {
  background: var(--pico-primary-background);
  border-color: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
}
.fm-canvas { overflow-x: auto; }
.fm-key { align-items: center; display: flex; flex-wrap: wrap; font-size: 0.8rem; gap: 0.3rem 0.5rem; margin: 0.4rem 0 0; }
.fm-k { border: 2px solid var(--pico-primary); border-radius: 50%; display: inline-block; height: 0.7rem; width: 0.7rem; }
.fm-k.fm-owned { background: var(--pico-primary); }
.fm-k.fm-upcoming { border-style: dashed; }
.fm-k.fm-k-show { border-color: var(--pico-muted-color); border-radius: 2px; }

.fm-lanes { display: block; font-family: inherit; }
.fm-tick { stroke: var(--pico-muted-border-color); stroke-dasharray: 2 4; }
.fm-year { fill: var(--pico-muted-color); font-size: 11px; }
.fm-lane-name { fill: var(--pico-color); font-size: 13px; }
.fm-lane { stroke: var(--pico-muted-border-color); stroke-linecap: round; stroke-width: 3; }
.fm-node :is(circle, rect) { fill: var(--franchisarr-page); stroke: var(--pico-primary); stroke-width: 2; }
.fm-node.fm-owned :is(circle, rect) { fill: var(--pico-primary); }
.fm-node.fm-missing :is(circle, rect) { stroke: var(--pico-muted-color); }
.fm-node.fm-upcoming :is(circle, rect) { stroke: var(--pico-muted-color); stroke-dasharray: 3 2; }
.fm-node:is(:hover, :focus) :is(circle, rect) { stroke-width: 3.5; }
.fm-edge { fill: none; stroke: var(--pico-primary); stroke-opacity: 0.55; stroke-width: 1.5; }
.fm-edge--continuation { stroke-dasharray: 5 4; }

.fm-tree { text-align: left; }
.fm-root {
  border: 1px solid var(--pico-primary);
  border-radius: 999px;
  font-weight: 700;
  margin: 0 auto 1rem;
  padding: 0.35rem 1.1rem;
  width: max-content;
  max-width: 100%;
}
.fm-branches {
  border-top: 2px solid var(--pico-muted-border-color);
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
  padding-top: 0.9rem;
}
.fm-group { display: block; font-weight: 600; margin-bottom: 0.4rem; }
.fm-tree ul { border-left: 2px solid var(--pico-muted-border-color); list-style: none; margin: 0; padding: 0 0 0 0.75rem; }
.fm-tree li { list-style: none; margin: 0.2rem 0; padding: 0; }
.fm-tree li > ul { margin-left: 0.6rem; }
.fm-tree li a { align-items: center; color: inherit; display: flex; gap: 0.5rem; text-decoration: none; }
.fm-tree li img, .fm-noposter {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  border-radius: 3px;
  flex: none;
  object-fit: cover;
  width: 1.6rem;
}
.fm-tree .fm-name { flex: 0 1 auto; font-size: 0.88rem; line-height: 1.2; min-width: 0; }
.fm-tree li a small { flex: none; white-space: nowrap; }
.fm-tree .fm-missing a { opacity: 0.6; }
.fm-tree .fm-missing img { filter: grayscale(1); }
.fm-tree .fm-upcoming .fm-name { font-style: italic; }

/* A finger on the background still scrolls the page; only a star is dragged. */
.fm-sky { display: block; max-height: 75vh; touch-action: pan-y; user-select: none; width: 100%; }
.fm-star { touch-action: none; }
.fm-wire { stroke: var(--pico-muted-border-color); stroke-width: 1; }
.fm-wire.fm-edge--spin-off, .fm-wire.fm-edge--continuation { stroke: var(--pico-primary); stroke-opacity: 0.6; }
.fm-star circle { cursor: grab; fill: var(--franchisarr-page); stroke: var(--pico-muted-color); stroke-width: 1.5; }
.fm-star--root circle { fill: var(--pico-primary); stroke: var(--pico-primary); }
.fm-star--group circle { fill: var(--pico-card-background-color); stroke: var(--pico-primary); stroke-width: 2; }
.fm-star.fm-owned circle { fill: var(--pico-primary); stroke: var(--pico-primary); }
.fm-star.fm-upcoming circle { stroke-dasharray: 2 2; }
.fm-star text { fill: var(--pico-color); font-size: 12px; pointer-events: none; }
.fm-star--root text { font-size: 15px; font-weight: 700; }

/* Showcase-only pieces that are in the page for everyone (0.48.0): hidden in Classic. */
.spotlight, .timeline-ring, .sc-reel, .poster-wall, .end-credits { display: none; }

/* ---------------------------------------------------------------- upcoming calendar (0.71.0) */
/* Each month with a release in it: seven columns, Monday first, a poster on each film's day. At
   375px a column is ~45px, which still holds a recognisable poster. */
.view-switch { display: flex; gap: 0.25rem; margin: 0; }
.view-switch a {
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  font-size: 0.9rem;
  padding: 0.25rem 0.7rem;
  text-decoration: none;
}
.view-switch a[aria-current="page"] { background: var(--pico-primary-background); border-color: var(--pico-primary-background); color: var(--pico-primary-inverse); }
.cal-month { margin-bottom: calc(var(--pico-block-spacing-vertical) * 1.5); }
.cal-month h2 { font-size: 1.15rem; margin-bottom: 0.6rem; }
.cal-grid {
  display: grid;
  gap: 0.3rem;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}
.cal-grid > li { list-style: none; margin: 0; }
.cal-weekday { color: var(--pico-muted-color); font-size: 0.75rem; text-align: center; }
.cal-day {
  align-items: center;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-height: 4.5rem;
  min-width: 0;
  padding: 0.2rem;
}
.cal-day.is-blank { border-color: transparent; }
.cal-day.has-films { background: var(--pico-card-background-color); }
.cal-day.is-today { border-color: var(--pico-primary); }
.cal-date { align-self: flex-start; color: var(--pico-muted-color); font-size: 0.75rem; line-height: 1; }
.cal-day.has-films .cal-date { color: var(--pico-color); font-weight: 600; }
.cal-film {
  background: none;
  border: none;
  display: block;
  margin: 0;
  max-width: 4.5rem;
  padding: 0;
  width: 100%;
}
.cal-film img { aspect-ratio: 2 / 3; border-radius: calc(var(--pico-border-radius) / 2); display: block; height: auto; object-fit: cover; width: 100%; }
.cal-more { font-size: 0.75rem; }

/* ---------------------------------------------------------------- set runtime (0.72.0) */
.set-runtime { color: var(--pico-muted-color); font-size: 0.9rem; margin: 0.15rem 0 0; }

/* ---------------------------------------------------------------- placeholder posters (0.72.0) */
/* No artwork: the title and year typeset on a card, tinted by a hue from the title (--ph-hue,
   set inline). Lightness and strength come from the theme's card colour, so it sits right in
   light and dark and under a theme.park theme; a browser without relative colours gets the
   theme's plain card colour. Type scales with the card (container units, inside it only). */
.poster-card {
  aspect-ratio: 2 / 3;
  background: var(--pico-card-sectioning-background-color);
  background: linear-gradient(160deg,
    oklch(from var(--pico-card-sectioning-background-color) calc(l + 0.08) 0.07 var(--ph-hue)),
    oklch(from var(--pico-card-sectioning-background-color) calc(l - 0.02) 0.05 calc(var(--ph-hue) + 40)));
  border-radius: var(--pico-border-radius);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pico-contrast) 10%, transparent);
  color: var(--pico-h1-color);
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
  /* Percentages, not cqi: container units on the container itself resolve against an outer
     container (or the viewport), which on a small card is wider than the card. */
  padding: 9% 8%;
  width: 100%;
}
.poster-card-title {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  display: -webkit-box;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 13cqi;
  font-weight: 700;
  hyphens: auto;
  line-height: 1.15;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.poster-card-year {
  border-top: 1px solid color-mix(in srgb, var(--pico-h1-color) 35%, transparent);
  font-size: 10cqi;
  letter-spacing: 0.08em;
  opacity: 0.8;
  padding-top: 4cqi;
}
.timeline-placeholder.poster-card, .cal-film-card.poster-card, .surprise-placeholder.poster-card { height: auto; }

/* ---------------------------------------------------------------- A-Z rail (0.72.0) */
/* Down the right edge of a long list, over the page margin; letters with nothing under them are
   greyed. A jumped-to card stops below the top bar. */
.az-rail {
  backdrop-filter: blur(6px);
  background: color-mix(in srgb, var(--pico-background-color) 70%, transparent);
  border-radius: 999px;
  display: flex;
  flex-direction: column;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.4rem 0.15rem;
  position: fixed;
  right: 0.3rem;
  top: 50%;
  translate: 0 -50%;
  z-index: 30;
}
.az-rail a, .az-rail span { display: block; padding: 0.12rem 0.3rem; text-align: center; text-decoration: none; }
.az-rail span { color: var(--pico-muted-border-color); }
[id^="az-"] { scroll-margin-top: 6rem; }
@media (max-width: 767px) {
  .az-rail { font-size: 0.62rem; padding: 0.3rem 0.05rem; right: 0.1rem; }
  .az-rail a, .az-rail span { padding: 0.08rem 0.25rem; }
  main:has(.az-rail) { padding-right: 2rem; }
}

/* ---------------------------------------------------------------- in numbers (0.72.0) */
.stat-tiles { display: grid; gap: var(--pico-spacing); grid-template-columns: repeat(auto-fill, minmax(min(100%, 10rem), 1fr)); margin-bottom: calc(var(--pico-block-spacing-vertical) * 1.5); }
.stat-tiles article { display: flex; flex-direction: column; gap: 0.2rem; margin: 0; padding: 1rem; }
.stat-tiles strong { color: var(--pico-h1-color); font-size: 1.8rem; line-height: 1.1; }
.stat-tiles span { color: var(--pico-muted-color); font-size: 0.85rem; }
.stat-bars { display: grid; gap: 0.45rem; list-style: none; margin: 0 0 var(--pico-block-spacing-vertical); padding: 0; }
.stat-bars li { align-items: center; display: grid; gap: 0.6rem; grid-template-columns: minmax(5rem, 12rem) 1fr auto; list-style: none; margin: 0; }
.stat-label { font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat-track { background: color-mix(in srgb, var(--pico-muted-color) 15%, transparent); border-radius: 999px; display: block; height: 0.7rem; overflow: hidden; }
.stat-fill { background: color-mix(in srgb, var(--pico-primary) 45%, transparent); border-radius: 999px; display: block; height: 100%; min-width: 2px; }
.stat-part { background: var(--pico-primary); border-radius: 999px; display: block; height: 100%; }
.stat-value { font-size: 0.85rem; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.stat-pair { display: grid; gap: calc(var(--pico-spacing) * 2); grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
@media (max-width: 767px) {
  .stat-bars li { grid-template-columns: 6.5rem 1fr auto; }
}

/* ---------------------------------------------------------------- compact density (0.72.0) */
/* Each person's choice, under their name: smaller cards, about twice as many on a screen. The
   missing-film previews and the peek panels stay on the detail pages. */
[data-density="compact"] .collection-grid--compact { gap: 0.6rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }
[data-density="compact"] .collection-grid--owned { grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); }
[data-density="compact"] .collection-card--compact { column-gap: 0.6rem; font-size: 0.85rem; grid-template-columns: 3.6rem 1fr; padding: 0.6rem; row-gap: 0.15rem; }
[data-density="compact"] .collection-grid--owned .collection-card--compact { grid-template-columns: 2.8rem 1fr; }
[data-density="compact"] .collection-card--compact > .missing-preview { display: none; }
[data-density="compact"] .collection-card--compact .card-actions button { font-size: 0.8rem; padding: 0.25rem 0.55rem; }
[data-density="compact"] .collection-card--compact progress.completeness { margin-bottom: 0.25rem; }

/* ---------------------------------------------------------------- poster marks (0.72.0) */
/* On an owned poster: its best copy's resolution in the corner, and "Next" / "Start here" on
   the film to watch next in release order. */
.collection-poster.has-marks { align-self: start; position: relative; }
.collection-poster.has-marks img { display: block; }
.film-tile.owned .collection-poster:has(.poster-ribbon) { opacity: 1; }   /* the next one isn't dimmed */
.quality-badge {
  background: color-mix(in srgb, var(--pico-background-color) 80%, transparent);
  border-radius: 0.2rem;
  bottom: 0.25rem;
  color: var(--pico-muted-color);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  padding: 0.15rem 0.25rem;
  position: absolute;
  right: 0.25rem;
}
.quality-badge--4k { background: var(--pico-primary-background); color: var(--pico-primary-inverse); }
.poster-ribbon {
  background: var(--pico-primary-background);
  border-radius: 0 0 0.25rem 0;
  color: var(--pico-primary-inverse);
  font-size: 0.6rem;
  font-weight: 700;
  left: 0;
  letter-spacing: 0.05em;
  line-height: 1;
  padding: 0.2rem 0.35rem;
  position: absolute;
  text-transform: uppercase;
  top: 0;
}

/* ---------------------------------------------------------------- dot strip (0.72.0) */
/* A list card's set in release order as dots: filled owned, hollow missing, dashed coming. */
.dot-strip { display: flex; flex-wrap: wrap; gap: 0.18rem; list-style: none; margin: 0 0 0.4rem; padding: 0; }
.dot-strip li { border: 1.5px solid var(--pico-primary); border-radius: 50%; height: 0.5rem; list-style: none; margin: 0; width: 0.5rem; }
.dot-strip .dot--owned { background: var(--pico-primary); }
.dot-strip .dot--upcoming { border-style: dashed; opacity: 0.6; }
.dot-strip .dot-more { border: none; color: var(--pico-muted-color); font-size: 0.65rem; height: auto; line-height: 0.5rem; width: auto; }
[data-density="compact"] .dot-strip { display: none; }

/* ---------------------------------------------------------------- franchise chips (0.72.0) */
.link-chips { align-items: center; display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0 0 var(--pico-block-spacing-vertical); }
.link-chips > span { color: var(--pico-muted-color); font-size: 0.85rem; margin-right: 0.15rem; }
.link-chips a {
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 999px;
  font-size: 0.82rem;
  padding: 0.15rem 0.7rem;
  text-decoration: none;
}
.link-chips a:hover { border-color: var(--pico-primary); }

/* ---------------------------------------------------------------- almost on the shelf (0.72.0) */
.trophy-almost ul { display: grid; gap: var(--pico-spacing); grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); list-style: none; padding: 0; }
.trophy-almost li { align-items: start; display: grid; gap: 0.75rem; grid-template-columns: 3.5rem 1fr; list-style: none; margin: 0; }
.trophy-almost li > div { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.trophy-almost-poster img { border-radius: var(--pico-border-radius); display: block; width: 100%; }
.trophy-almost progress { height: 0.35rem; margin: 0.2rem 0; }

/* ---------------------------------------------------------------- share and shortcuts (0.72.0) */
.timeline-share { font-size: 0.8rem; margin: 0 0 0 auto; padding: 0.2rem 0.7rem; width: auto; }
.timeline-heading:has(.timeline-share:not([hidden])) { align-items: center; display: flex; flex-wrap: wrap; gap: 0 0.5rem; }
.key-sheet article { max-width: 26rem; }
.key-sheet table { margin: 0; }
.key-sheet td { font-size: 0.9rem; padding: 0.35rem 0.5rem; }
.key-sheet kbd { font-size: 0.8rem; }
