:root {
  --bg: #0e1116;
  --panel: #161b22;
  --panel-2: #1c2230;
  --border: #2a3140;
  --text: #e6edf3;
  --muted: #8b98a9;
  --accent: #4f8cff;
  --accent-2: #6f5bff;
  --good: #2ea043;
  --warn: #d29922;
  --bad: #f85149;
  --radius: 10px;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f4f6fa;
    --panel: #ffffff;
    --panel-2: #f0f3f8;
    --border: #d8dee9;
    --text: #1a1f28;
    --muted: #5b6675;
  }
}

* { box-sizing: border-box; }

/**
 * The browser's own [hidden] rule loses to ANY author rule that sets display,
 * whatever the specificity, because author sheets beat the user-agent sheet.
 * Every `.btn`, `.filter-row` and so on therefore ignored the hidden attribute
 * and showed anyway - which is why "Allow for…" appeared on installs with no
 * supervised accounts and did nothing when pressed: the element was never meant
 * to be on screen, so it had no click handler.
 *
 * One global rule rather than a guard per class, because the next element with
 * a display of its own would hit exactly the same thing.
 */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { margin: 0 0 .4rem; line-height: 1.25; }
h2 { font-size: 1.05rem; }
small { color: var(--muted); font-size: .8rem; display: block; margin-top: .25rem; }

/* ---------------------------------------------------------------- topbar */
.topbar {
  display: flex; align-items: center; gap: 1rem;
  padding: .6rem 1rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
  /* Deliberately no wrap: the bar is one line at every width, and the nav
     itself gives way first. A wrapping header pushed the page down and moved
     the content under it. */
  flex-wrap: nowrap;
}
.brand { font-weight: 700; font-size: 1.15rem; color: var(--text); white-space: nowrap; }
.brand span { color: var(--accent); }
.brand:hover { text-decoration: none; }

.nav { display: flex; gap: .25rem; min-width: 0; }
.nav a {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .7rem; border-radius: var(--radius);
  color: var(--muted); font-weight: 500; white-space: nowrap;
  position: relative;
}
.nav a:hover { background: var(--panel-2); text-decoration: none; color: var(--text); }
.nav a.active { background: var(--accent); color: #fff; }
.nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-icon { font-size: .95em; line-height: 1; }
.nav-badge {
  background: var(--accent); color: #fff;
  font-size: .7rem; font-weight: 700; line-height: 1;
  padding: .12rem .3rem; border-radius: 999px; min-width: 1.1em; text-align: center;
}
.nav a.active .nav-badge { background: rgba(0,0,0,.35); }

.nav-toggle {
  display: none;
  background: none; border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius); padding: .3rem .55rem; font-size: 1.1rem;
  cursor: pointer; line-height: 1;
}

/* The account and Sign out links duplicated inside the drawer. Above the
   drawer breakpoint the top bar carries them, so these stay hidden. Declared
   here rather than after the media query, where it would override it. */
.nav-extra { display: none; }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: .6rem; }
.who {
  color: var(--muted); font-size: .85rem; max-width: 10rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Free space on the shared put.io account, beside the account name. A button
   rather than a label because clicking it re-checks: the figure is cached for
   five minutes server-side and this is the way past that. Hidden until the
   figure is known - see the global [hidden] rule at the top of this file. */
.quota {
  display: inline-flex; align-items: center; gap: .35rem;
  flex: 0 0 auto; white-space: nowrap;
  background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px;
  padding: .2rem .55rem; font: inherit; font-size: .78rem; font-weight: 600;
  cursor: pointer;
}
.quota:hover { color: var(--text); }
.quota::before {
  content: ''; flex: 0 0 auto;
  width: .5rem; height: .5rem; border-radius: 50%; background: var(--good);
}
.quota.low  { color: var(--warn); border-color: var(--warn); }
.quota.full { color: var(--bad);  border-color: var(--bad); }
.quota.low::before  { background: var(--warn); }
.quota.full::before { background: var(--bad); }
/* The last good reading, put.io unreachable just now. Said quietly rather than
   hidden, because "couldn't check" is not "no space left" (gotcha 2). */
.quota.stale, .quota.busy { opacity: .55; }

/* Today's AI spend, beside it. No status dot: the put.io dot means "running
   out", and there is no equivalent threshold for money the user has not set. */
.quota.spend { text-decoration: none; color: var(--accent); }
.quota.spend::before { display: none; }
.quota.spend:hover { color: var(--text); }
/* Some of today's calls have no price, so the figure is a floor, not a total. */
.quota.spend.unpriced { border-style: dashed; }

/* The same figure in the phone drawer, where it sits among the account links
   and so is shaped like them rather than like a pill. */
.nav-quota {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .6rem .7rem; border-radius: 6px;
  background: none; border: 0; color: var(--muted);
  font: inherit; font-weight: 500; text-align: left; cursor: pointer;
}
.nav-quota::before {
  content: '\1F4BE'; /* floppy disk - the same icon Downloads uses */
  width: 1.4rem; text-align: center; flex: 0 0 auto;
}
.nav-quota.low  { color: var(--warn); }
.nav-quota.full { color: var(--bad); }
.nav-quota.stale, .nav-quota.busy { opacity: .55; }

/* Between these widths the labels go and the icons carry the nav, which buys
   back roughly half its width without losing any destination. */
@media (max-width: 1060px) {
  .nav-label { display: none; }
  .nav a { padding: .4rem .55rem; font-size: 1.05rem; }
}

/* Below this the bar cannot hold seven destinations at all, so the nav becomes
   a drawer under the header rather than something that overflows or wraps. */
@media (max-width: 720px) {
  .nav-toggle { display: inline-flex; }
  .nav {
    display: none;
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; gap: 0;
    background: var(--panel); border-bottom: 1px solid var(--border);
    padding: .35rem;
  }
  .nav.open { display: flex; }
  .nav a { padding: .6rem .7rem; font-size: 1rem; border-radius: 6px; }
  .nav-label { display: inline; }
  .nav-icon { width: 1.4rem; text-align: center; }
  .topbar { position: sticky; }

  /* Everything menu-ish belongs in the menu. The account link and Sign out
     used to sit in the bar beside the hamburger, so the phone header carried
     three separate things and the drawer was only part of the story. */
  .topbar-right { display: none; }
  .nav-extra {
    display: flex; flex-direction: column;
    margin-top: .35rem; padding-top: .35rem;
    border-top: 1px solid var(--border);
  }
  .nav-extra a {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .6rem .7rem; border-radius: 6px;
    color: var(--muted); font-weight: 500;
  }
  .nav-extra a:hover { background: var(--panel-2); text-decoration: none; color: var(--text); }
}

.wrap { max-width: 1150px; margin: 0 auto; padding: 1.25rem; }

/* ---------------------------------------------------------------- panels */
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  margin-bottom: 1rem;
}
.panel-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.spacer { flex: 1; }
.hint { color: var(--muted); font-size: .85rem; margin: .25rem 0 0; }

/* ---------------------------------------------------------------- forms */
input, select, textarea {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: .55rem .7rem;
  border-radius: var(--radius);
  font: inherit;
  width: 100%;
}
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
label { display: block; font-size: .85rem; color: var(--muted); margin-bottom: .75rem; }
label input, label select { margin-top: .3rem; color: var(--text); }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0 1rem; }
.search-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
/* Inputs are width:100% globally, which pushed the button onto its own line.
   Every field in a search row grows instead, so the row stays a row. */
.search-row input[type="search"],
.search-row input[type="text"] { flex: 1 1 260px; width: auto; min-width: 0; }
.search-row select { flex: 0 0 120px; }
/* The home hero has only a field and a button, and must never wrap. */
.search-row-single { flex-wrap: nowrap; }
.search-row-single input { flex: 1 1 auto; }
.search-row-single .btn { flex: 0 0 auto; }
.toggle { display: flex; align-items: center; gap: .4rem; margin: 0; white-space: nowrap; }
.toggle input { width: auto; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .55rem .95rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit; font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover:not(:disabled) { filter: brightness(1.1); }
.btn-ghost { background: transparent; border-color: var(--border); color: var(--text); }
.btn-ghost:hover:not(:disabled) { background: var(--panel-2); }
.btn-danger { background: var(--bad); color: #fff; }
.btn-sm { padding: .3rem .6rem; font-size: .82rem; }
.btn-block { width: 100%; margin-top: .5rem; }

/* ---------------------------------------------------------------- alerts */
.alert {
  padding: .65rem .85rem; border-radius: var(--radius);
  margin: .75rem 0; font-size: .9rem;
  border: 1px solid var(--border); background: var(--panel-2);
}
.alert-error   { border-color: var(--bad);  color: var(--bad); }
.alert-success { border-color: var(--good); color: var(--good); }
.alert-info    { border-color: var(--accent); }

/* ---------------------------------------------------------------- results */
.result {
  display: flex; align-items: flex-start; gap: .9rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .8rem .9rem;
  margin-bottom: .55rem;
}
.result.blocked { opacity: .5; }
.result.top { border-color: var(--accent); }
.score {
  flex: 0 0 46px; height: 46px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--panel-2);
  border: 2px solid var(--border);
  font-weight: 700; font-size: .9rem;
}
.result.top .score { border-color: var(--accent); color: var(--accent); }
.result-main { flex: 1; min-width: 0; }
.result-title {
  font-weight: 600; word-break: break-word; margin-bottom: .3rem;
}
.meta { display: flex; flex-wrap: wrap; gap: .4rem; font-size: .8rem; color: var(--muted); }
.tag {
  background: var(--panel-2); border: 1px solid var(--border);
  padding: .1rem .45rem; border-radius: 6px;
}
.tag.res  { color: var(--accent); border-color: var(--accent); }
.tag.seed { color: var(--good); }
.tag.magnet { color: var(--accent-2); border-color: var(--accent-2); }
.tag.warn { color: var(--warn); border-color: var(--warn); }
/* A copy on this server rather than on put.io. */
.tag.local { color: var(--good); border-color: var(--good); }
.tag.bad  { color: var(--bad); border-color: var(--bad); }
.result-actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: .35rem; }

/* ---------------------------------------------------------------- transfers */
.transfer {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .8rem .9rem;
  margin-bottom: .55rem;
}
.transfer-head { display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap; }
.transfer-title { font-weight: 600; flex: 1; min-width: 200px; word-break: break-word; }
.bar { height: 6px; background: var(--panel-2); border-radius: 3px; overflow: hidden; margin: .55rem 0 .35rem; }
.bar > i { display: block; height: 100%; background: var(--accent); transition: width .4s ease; }
.bar.done > i { background: var(--good); }
.bar.err  > i { background: var(--bad); width: 100% !important; }
.status { font-size: .78rem; font-weight: 700; letter-spacing: .03em; }
.status.COMPLETED { color: var(--good); }
.status.ERROR, .status.GONE { color: var(--bad); }
.status.SEEDING { color: var(--accent-2); }
.status.DOWNLOADING, .status.IN_QUEUE, .status.WAITING { color: var(--warn); }
.transfer-actions { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .5rem; }

/* ---------------------------------------------------------------- folders */
.folder-browser {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .7rem;
  background: var(--panel-2);
  margin-top: .6rem;
}
#folder-trail { margin-bottom: .5rem; font-size: .85rem; }
#folder-trail a { color: var(--accent); }
#folder-list { display: flex; flex-wrap: wrap; gap: .4rem; max-height: 240px; overflow-y: auto; }
.folder-row .btn { font-weight: 500; }
.status.DONE { color: var(--good); }
.status.PENDING { color: var(--muted); }

.empty { text-align: center; color: var(--muted); padding: 2.5rem 1rem; }
.spinner {
  display: inline-block; width: 15px; height: 15px;
  border: 2px solid var(--border); border-top-color: var(--accent);
  border-radius: 50%; animation: spin .7s linear infinite;
  vertical-align: -2px; margin-right: .45rem;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------- filters and grouping */
.filter-row {
  display: flex; flex-wrap: wrap; gap: 1rem;
  margin-top: .7rem; padding-top: .7rem;
  border-top: 1px solid var(--border);
}
.filter-row .toggle { font-size: .85rem; }
.tag.bg { color: #22c55e; border-color: #22c55e; }

.result.seen { opacity: .55; }
.result.seen .result-title { text-decoration: line-through; text-decoration-thickness: 1px; }

.series {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); margin-bottom: .6rem;
}
.series-head, .season-head {
  display: flex; align-items: center; gap: .6rem;
  padding: .7rem .9rem; cursor: pointer; list-style: none;
}
.series-head::-webkit-details-marker, .season-head::-webkit-details-marker { display: none; }
.series-head:hover, .season-head:hover { background: var(--panel-2); }
.series-title { font-weight: 700; flex: 1; min-width: 0; }
.series-head .poster-thumb { width: 34px; flex-basis: 34px; }

.season { border-top: 1px solid var(--border); }
.season-head { padding-left: 1.6rem; font-size: .9rem; }
.season-body { padding: 0 .6rem .6rem 1.6rem; }
.season-body .result { background: var(--panel-2); }

.group-label { margin: 1.2rem 0 .5rem; color: var(--muted); font-size: .9rem; }

/* ---------------------------------------------------------------- calendar */
.cal-day {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); margin-bottom: .6rem; overflow: hidden;
}
.cal-day.today { border-color: var(--accent); }
.cal-day.past { opacity: .7; }
.cal-date {
  display: flex; align-items: center; gap: .6rem;
  padding: .55rem .9rem; background: var(--panel-2);
  border-bottom: 1px solid var(--border); font-size: .9rem;
}
.cal-eps { padding: .5rem; display: flex; flex-direction: column; gap: .45rem; }
.cal-ep {
  display: flex; align-items: center; gap: .75rem;
  padding: .5rem; border-radius: var(--radius);
  background: var(--panel-2);
  flex-wrap: wrap;
}
.cal-ep.seen { opacity: .5; }
.cal-ep .poster-thumb { width: 38px; flex-basis: 38px; }
.cal-ep-main { flex: 1; min-width: 160px; }
.cal-ep-title { font-weight: 600; margin-bottom: .2rem; }

/* ---------------------------------------------------------------- player */
.player-back {
  position: fixed; inset: 0; background: rgba(0,0,0,.85);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; z-index: 60;
}
.player {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius);
  width: 100%; max-width: 1000px;
  max-height: 92vh; overflow-y: auto;
}
.player-head {
  display: flex; align-items: center; gap: 1rem;
  padding: .7rem .9rem; border-bottom: 1px solid var(--border);
}
.player-title { font-weight: 600; flex: 1; word-break: break-word; }
.player-body { padding: .9rem; }
.player-body video {
  width: 100%; max-height: 70vh;
  background: #000; border-radius: var(--radius); display: block;
}
/* ------------------------------------------------------------ title info */
.tag.star { color: #f0b429; border-color: #f0b429; font-weight: 700; }

.info-panel { display: flex; gap: 1rem; align-items: flex-start; }
.info-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .55rem; }
.info-body h3 { margin: 0; }
.info-plot {
  margin: 0; font-size: .9rem; line-height: 1.5;
  color: var(--text);
}
.info-rows { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; }
.info-row { display: flex; gap: .5rem; }
.info-label {
  flex: 0 0 66px; color: var(--muted); font-weight: 600;
}
@media (max-width: 560px) {
  .info-panel { flex-direction: column; }
}

.trailer-frame {
  width: 100%; aspect-ratio: 16 / 9;
  border: 0; border-radius: var(--radius);
  background: #000; display: block;
}
.convert-box { padding: .5rem 0; }
.convert-box p { margin: 0 0 .5rem; }

/* ---------------------------------------------------------------- posters */
.poster {
  width: 120px; flex: 0 0 120px;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--panel-2);
  border: 1px solid var(--border);
}
.poster-empty {
  display: flex; align-items: center; justify-content: center;
  font-size: 2rem; color: var(--muted);
}
/* Small thumbnail on each search result row. */
.poster-thumb {
  width: 46px; flex: 0 0 46px;
  border-radius: 6px;
}
.poster-thumb.poster-empty { font-size: 1.1rem; }
.result-sub { color: var(--muted); font-size: .82rem; margin-bottom: .3rem; }

.meta-card { display: flex; gap: 1rem; align-items: flex-start; }
.meta-info { flex: 1; min-width: 0; }
.meta-info h2 { margin-bottom: .4rem; }
.meta-alts {
  display: flex; flex-wrap: wrap; gap: .35rem; align-items: center;
  margin: -.5rem 0 1rem; padding: 0 .25rem;
}

/* ------------------------------------------------------------- favourites */
.fav-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: .75rem;
}
.fav-card {
  display: flex; gap: .8rem;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .8rem;
}
.fav-card .poster { width: 92px; flex-basis: 92px; }
.fav-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .4rem; }
.fav-title { font-weight: 600; word-break: break-word; }

@media (max-width: 560px) {
  .meta-card { flex-direction: column; }
  /* :not(.poster-thumb) matters. A search result's thumbnail carries both
     classes, so this rule was blowing a 46px thumbnail up to 200x300 on every
     result row — one result filled the whole phone screen. */
  .poster:not(.poster-thumb),
  .fav-card .poster { width: 100%; flex-basis: auto; max-width: 200px; }
}

/* ---------------------------------------------------------------- modal */
.modal-back {
  position: fixed; inset: 0; background: rgba(0,0,0,.6);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; z-index: 50;
}
.modal {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.25rem;
  max-width: 460px; width: 100%;
}
.modal h3 { margin-bottom: .5rem; }
.modal p { color: var(--muted); font-size: .9rem; }
.modal-actions { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1rem; }

/* ---------------------------------------------------------------- login */
.login-body { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 1rem; }
.login-card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.75rem;
  width: 100%; max-width: 340px;
}
/* The "Prowlarr → put.io" line under this is gone: the sign-in page named two
   pieces of plumbing to somebody who has not signed in yet. The logo carries
   the spacing it used to provide. */
.login-logo { text-align: center; font-size: 1.5rem; margin-bottom: 1.25rem; }
.login-logo span { color: var(--accent); }
/* .login-sub is gone from the markup: the sign-in page named two pieces of
   plumbing to somebody who has not signed in yet. The logo keeps the spacing
   the tagline used to provide. */

/* ---------------------------------------------------------------- installer */
.install-wrap { max-width: 640px; margin: 2rem auto; padding: 0 1rem; }
.check { display: flex; gap: .6rem; padding: .4rem 0; font-size: .9rem; align-items: baseline; }
.check .ok  { color: var(--good); font-weight: 700; }
.check .no  { color: var(--bad);  font-weight: 700; }
.check .opt { color: var(--warn); font-weight: 700; }
code {
  background: var(--panel-2); padding: .12rem .35rem;
  border-radius: 5px; font-size: .85em; word-break: break-all;
}
pre {
  background: var(--panel-2); border: 1px solid var(--border);
  padding: .75rem; border-radius: var(--radius);
  overflow-x: auto; font-size: .82rem;
}

/* --------------------------------------------------- Bulgarian subtitles */
.tag.bg-avail { color: #7dd3a0; border-color: #7dd3a0; }
.sub-panel { display: flex; flex-direction: column; gap: .4rem; }
/* The panel heading now carries the "show all in the player" switch beside it,
   so it is a row rather than a line of text. Wraps on a phone rather than
   squeezing the label (gotcha 16). */
.sub-head {
  font-weight: 600; margin-bottom: .2rem;
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.sub-showall {
  font-weight: 400; font-size: .78rem; margin: 0;
  display: flex; align-items: center; gap: .35rem;
  white-space: normal;               /* never nowrap: gotcha 16 lives here */
}
.sub-showall input { width: auto; flex: 0 0 auto; }

/* Our own captions picker. It exists because Chrome's Captions menu is a
   user-agent control in shadow DOM that no CSS can widen — it cut "BG ·
   Librarians" to "BG · Libr…". A <select> has no such limit, so this one is
   allowed to be wide, and truncates with an ellipsis only when the panel
   itself is narrower than the longest release name. */
.sub-picker {
  max-width: min(28rem, 100%);
  text-overflow: ellipsis;
}
@media (max-width: 720px) {
  /* 16px, or iOS zooms the page on focus and stays zoomed (gotcha 15). */
  .sub-picker { font-size: 16px; max-width: 100%; }
}

/* Which track is on screen right now. Pressing Show changes something outside
   the panel, so the row it was pressed on has to say so. */
.sub-row.is-showing {
  outline: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--panel-2));
}
.sub-row.is-showing .sub-title::after {
  content: ' · showing';
  font-weight: 400; color: var(--accent); font-size: .78rem;
}
.sub-row {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .35rem .5rem; border-radius: 6px; background: var(--panel-2);
}
.sub-name { flex: 1; min-width: 180px; font-size: .85rem; word-break: break-word; }

/* A track is now three lines: what it is, the facts read out of it, and the
   filename it arrived under. The row is `align-items: center` for the buttons,
   so this block has to start its own column. */
.sub-name { display: flex; flex-direction: column; gap: .2rem; }
.sub-title { font-weight: 600; }
.sub-facts { gap: .3rem; row-gap: .25rem; }
.sub-facts .tag { font-size: .72rem; padding: .05rem .35rem; }
/* The filename is for recognising the release, not for reading. One line,
   clipped - a 60-character release name would otherwise be the tallest thing
   in the panel on a phone. */
.sub-raw {
  font-size: .72rem; opacity: .65;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}

/* Adding your own subtitles, and having one translated. Lives under the video
   in the player, so it has to sit in a narrow column without pushing the page
   sideways - hence the wrapping rows and the auto-width selects. */
.subs-panel {
  margin-top: .7rem; padding: .7rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel);
  display: flex; flex-direction: column; gap: .5rem;
}
.subs-panel .sub-row { flex-wrap: wrap; }
.subs-panel .profile-select { min-width: 0; max-width: 100%; flex: 0 1 auto; }
.subs-progress .bar { height: 6px; }
.subs-status:empty { display: none; }

/* ---------------------------------------------------------- profiles */
.profile-select { width: auto; min-width: 150px; padding: .25rem .5rem; font-size: .85rem; }
#profile-select { flex: 1 1 200px; }

/* ------------------------------------------------ episode-level grouping */
.episode {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); margin-bottom: .45rem;
}
.episode-head {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .5rem .7rem; cursor: pointer; list-style: none; font-size: .88rem;
}
.episode-head::-webkit-details-marker { display: none; }
.episode-head:hover { background: var(--panel-2); }
.ep-count { color: var(--muted); font-size: .82rem; }
.episode-body { padding: 0 .5rem .5rem; }
.episode-body .result { background: var(--bg); }

/* ------------------------------------------------------- advanced filters */
.adv {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: .5rem .7rem; margin-top: .6rem; background: var(--bg);
}
.adv > summary { cursor: pointer; list-style: none; font-size: .9rem; }
.adv > summary::-webkit-details-marker { display: none; }
.adv > summary::before { content: '▸ '; color: var(--muted); }
.adv[open] > summary::before { content: '▾ '; }
.adv > summary .hint { font-weight: 400; }
.adv-grid {
  display: grid; gap: .5rem .7rem; margin: .6rem 0;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.adv-grid label {
  display: flex; flex-direction: column; gap: .2rem;
  font-size: .78rem; color: var(--muted);
}
.adv-grid input, .adv-grid select {
  padding: .35rem .45rem; font-size: .85rem;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px;
}
.filter-note { margin: .2rem 0 .6rem; }

/* ------------------------------------------------ search by cast or crew */
.person-row {
  display: flex; align-items: center; gap: .6rem;
  padding: .4rem; border-bottom: 1px solid var(--border);
}
.person-row:last-child { border-bottom: 0; }
.person-info { flex: 1; min-width: 0; }
.person-name { font-weight: 600; font-size: .9rem; }
.person-grid {
  display: grid; gap: .7rem; margin-top: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  max-height: 65vh; overflow-y: auto;
  /* The shelves hide their scrollbars; this grid did not, so the watchlist
     carried a bright track down its right edge that nothing else on the page
     had. Still scrolls, by wheel, drag and keyboard - just not painted. */
  scrollbar-width: none;              /* Firefox */
  -ms-overflow-style: none;           /* old Edge */
}
.person-grid::-webkit-scrollbar { display: none; }
.person-grid .card { width: 100%; }
.tag.match { color: var(--muted); border-style: dashed; max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* -------------------------------------------------------------- home page */
.hero { padding: 1.1rem; }
.hero .meta { margin-top: .7rem; }
.tag-link { text-decoration: none; }
.tag-link:hover { border-color: var(--accent); color: var(--accent); }

/* Today and the next two days. Fixed columns, so a quiet day reads as a quiet
   day rather than simply not appearing. */
.upnext { padding: .9rem 1rem 1rem; }
.upnext h2 { font-size: 1rem; margin: 0; }
.upnext-days {
  display: grid; gap: .6rem; margin-top: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.upnext-day {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: .5rem .6rem; background: var(--bg);
}
.upnext-day.quiet { opacity: .6; }
.upnext-head {
  display: flex; align-items: baseline; gap: .45rem;
  padding-bottom: .35rem; margin-bottom: .35rem;
  border-bottom: 1px solid var(--border);
}
.upnext-ep {
  display: flex; align-items: center; gap: .5rem;
  padding: .3rem 0; font-size: .84rem;
}
.upnext-ep + .upnext-ep { border-top: 1px solid var(--border); }
.upnext-ep.seen { opacity: .55; }
.upnext-ep-info { flex: 1; min-width: 0; }
.upnext-show { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upnext-ep .hint { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.shelf { margin-bottom: 1.6rem; }

/* A shelf that has not been filled yet still takes up a card's worth of room.
   Without this the whole page jumps down as each shelf lands, and the topmost
   shelf is now New releases - which comes from TMDb and is the slowest thing on
   the page. The value is one card: 150px of poster at 2:3, plus its two lines
   of caption. `:empty` matches only before setShelf() has put anything in,
   including before an empty-state message, so a genuinely empty shelf still
   collapses to its "nothing here" line. */
.shelf-body:empty { min-height: 268px; }
.shelf-head {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-bottom: .6rem;
}
.shelf-head h2 { font-size: 1rem; margin: 0; }
.shelf-head .hint { margin: 0; }
.shelf.is-empty { opacity: .75; }

/* Filters above a browsable shelf. */
.shelf-filters {
  display: flex; flex-wrap: wrap; gap: .4rem .7rem; align-items: flex-end;
  margin-bottom: .6rem;
}
.shelf-filters label {
  display: flex; flex-direction: column; gap: .15rem;
  font-size: .75rem; margin: 0;
}
.shelf-filters input, .shelf-filters select {
  padding: .3rem .4rem; font-size: .82rem; width: auto; min-width: 6.5rem;
}
.shelf-filters input[type="text"] { min-width: 11rem; }
.shelf-filter-actions { display: flex; gap: .4rem; }
.shelf-filters.disabled { opacity: .5; }
.person-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin-bottom: .6rem; }

/* ------------------------------------------------------------- accounts */
.user-row {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  padding: .6rem .2rem; border-bottom: 1px solid var(--border);
}
.user-row:last-child { border-bottom: 0; }
.user-row.disabled { opacity: .55; }
.user-main { flex: 1; min-width: 12rem; }
.user-name { font-weight: 600; }
.user-actions { display: flex; gap: .35rem; flex-wrap: wrap; }
.who-role {
  background: var(--accent); color: #fff; font-size: .65rem; font-weight: 700;
  padding: .05rem .3rem; border-radius: 4px; margin-left: .35rem; vertical-align: middle;
}
/* A title a supervised account cannot open, but can ask about. */
.card.locked { opacity: .72; }
.card.locked .card-art { border-style: dashed; }
.card.locked .btn { margin-top: .35rem; width: 100%; }
.locked-row {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  width: auto; margin-bottom: .5rem; padding: .5rem .7rem;
  border: 1px dashed var(--border); border-radius: var(--radius); background: var(--panel);
}
.locked-row .card-art { width: 46px; flex: 0 0 46px; }
.locked-row .card-title { margin: 0; flex: 1; min-width: 8rem; }
.locked-row .btn { width: auto; margin: 0; }

.request-row {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .5rem .2rem; border-bottom: 1px solid var(--border);
}
.request-row:last-child { border-bottom: 0; }
.request-main { flex: 1; min-width: 12rem; }
.request-title { font-weight: 600; }

.allowed-list { margin: 0 0 .8rem 1rem; padding-left: .6rem; border-left: 2px solid var(--border); }
.allowed-row {
  display: flex; align-items: center; gap: .5rem;
  padding: .25rem 0; font-size: .84rem;
}
.allowed-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

a.who { text-decoration: none; }
a.who:hover { color: var(--text); }
.person-chips .alert { margin: 0; }

/* The row still scrolls; the scrollbar itself is gone. Arrows, dragging and
   the arrow keys drive it instead, and touch keeps its native swipe. */
.shelf-scroller { position: relative; }
.shelf-body {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 150px;
  gap: .7rem;
  overflow-x: auto;
  padding-bottom: .3rem;
  scroll-snap-type: x proximity;
  scrollbar-width: none;              /* Firefox */
  -ms-overflow-style: none;           /* old Edge */
  scroll-behavior: smooth;
  outline: none;
}
.shelf-body::-webkit-scrollbar { display: none; }
.shelf-body > * { scroll-snap-align: start; }
.shelf-body.dragging { scroll-behavior: auto; cursor: grabbing; user-select: none; }
.shelf-body.dragging .card { pointer-events: none; }
.shelf-body:focus-visible { box-shadow: 0 0 0 2px var(--accent); border-radius: var(--radius); }

.shelf-nav {
  position: absolute; top: 0; bottom: .3rem; width: 46px;
  display: flex; align-items: center; justify-content: center;
  border: 0; padding: 0; cursor: pointer;
  color: #fff; font-size: 1.6rem; line-height: 1;
  opacity: 0; transition: opacity .15s ease;
  z-index: 2;
}
.shelf-nav-prev {
  left: 0; justify-content: flex-start; padding-left: .2rem;
  background: linear-gradient(to right, var(--bg) 35%, transparent);
}
.shelf-nav-next {
  right: 0; justify-content: flex-end; padding-right: .2rem;
  background: linear-gradient(to left, var(--bg) 35%, transparent);
}
.shelf-nav::before {
  content: ''; position: absolute; inset: 50% auto auto 50%;
  width: 30px; height: 30px; transform: translate(-50%, -50%);
  border-radius: 50%; background: rgba(0,0,0,.65); z-index: -1;
}
.shelf-nav:hover::before { background: var(--accent); }

/* Only reveal on hover, and never when there is nothing to scroll to. */
@media (hover: hover) {
  .shelf-scroller:hover .shelf-nav { opacity: 1; }
}
.shelf-nav:focus-visible { opacity: 1; }
.shelf-scroller.at-start .shelf-nav-prev,
.shelf-scroller.at-end   .shelf-nav-next,
.shelf-scroller.no-scroll .shelf-nav { opacity: 0; pointer-events: none; }

/* An empty shelf is a sentence, not a card.
   .shelf-body is a column grid with grid-auto-columns:150px, and that sizes the
   implicit column whatever the child says — so a long message was wrapping into
   a 150px ribbon down the page. The parent has to stop being a card grid. */
.shelf.is-empty .shelf-body {
  display: block;
  overflow-x: visible;
}
.shelf-empty {
  color: var(--muted);
  font-size: .88rem; padding: 1.2rem .2rem;
  max-width: 60ch;                    /* long enough to read, short enough to scan */
}

.card { width: 150px; }
.card.clickable { cursor: pointer; }
.card.clickable:hover .card-art { border-color: var(--accent); }
.card.dim { opacity: .6; }
.card-art {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel-2);
  aspect-ratio: 2 / 3;
}
.card-poster { width: 100%; height: 100%; flex: none; border: 0; border-radius: 0; }
.card-badges {
  position: absolute; top: .35rem; left: .35rem;
  display: flex; flex-direction: column; gap: .2rem; align-items: flex-start;
}
.card-badge {
  background: rgba(0,0,0,.75); color: #fff;
  font-size: .7rem; padding: .1rem .35rem; border-radius: 5px;
}
.card-badge.star { color: #f0b429; font-weight: 700; }
.card-corner {
  position: absolute; right: .35rem; bottom: .35rem;
  background: rgba(0,0,0,.75); color: #fff;
  width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: .8rem;
}
.card-progress {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 4px; background: rgba(0,0,0,.6);
}
.card-progress > i { display: block; height: 100%; background: var(--accent); }
.card-title {
  font-size: .82rem; font-weight: 600; margin-top: .35rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-sub { font-size: .75rem; color: var(--muted); }
.tag.bg-unknown { color: var(--muted); border-style: dashed; }

/* ------------------------------------------------ favourites, grouped */
.fav-section { margin-bottom: 1rem; }
.fav-section-head {
  display: flex; align-items: center; gap: .5rem;
  padding: .5rem .2rem; cursor: pointer; list-style: none;
  font-size: 1rem; border-bottom: 1px solid var(--border); margin-bottom: .7rem;
}
.fav-section-head::-webkit-details-marker { display: none; }
.fav-card.seen { opacity: .6; }

/* The trace behind a failed episode lookup — present, but folded away. */
.lookup-detail { margin: .5rem 0; font-size: .8rem; }
.lookup-detail summary { cursor: pointer; color: var(--muted); }
.lookup-detail code {
  display: block; margin-top: .35rem; padding: .4rem .5rem;
  background: var(--bg); border-radius: 6px;
  white-space: pre-wrap; word-break: break-word; color: var(--muted);
}
.fav-episodes { margin-top: .6rem; }
.fav-episodes .season { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: .35rem; }
.fav-episodes .season-body { padding: .3rem .5rem .5rem; }

.ep-row {
  display: flex; align-items: center; gap: .5rem;
  padding: .3rem .4rem; border-radius: 6px; font-size: .82rem;
  flex-wrap: wrap;
}
.ep-row:hover { background: var(--panel-2); }
.ep-row.seen { opacity: .5; }
.ep-row.unaired { opacity: .65; font-style: italic; }
.ep-num { color: var(--muted); font-weight: 700; flex: 0 0 34px; }
.ep-name { flex: 1; min-width: 120px; }
.ep-date { color: var(--muted); font-size: .76rem; }
.ep-actions { display: flex; gap: .25rem; }

/* -------------------------------------------------------------------- AI */

.tag.ok { color: var(--good); border-color: var(--good); }

/* The hero is nowrap so the field and button stay together, but a third
   button will not fit beside them on a phone. Below that width it wraps. */
@media (max-width: 560px) {
  .search-row-single { flex-wrap: wrap; }
  .search-row-single input { flex: 1 1 100%; }
}

.ai-panel {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); padding: .7rem .8rem; margin-top: .6rem;
}
.ai-intro { margin: 0 0 .6rem; }

/* The box and its button, side by side on a desktop and stacked on a phone. */
.ai-ask { display: flex; gap: .5rem; align-items: flex-start; flex-wrap: wrap; }
.ai-ask .btn { flex: 0 0 auto; }
.ai-input {
  flex: 1 1 320px; min-width: 0; width: auto;
  padding: .55rem .7rem; font: inherit; font-size: .92rem;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  resize: vertical;
}
.ai-status { margin-top: .5rem; }
.ai-results { margin-top: .5rem; }

.ai-list {
  display: flex; flex-direction: column; gap: .1rem;
  margin: .4rem 0;
}
.ai-row {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .55rem .4rem; border-bottom: 1px solid var(--border);
}
.ai-row:last-child { border-bottom: 0; }
/* Dimmed rather than hidden: unconfirmed is not the same as wrong, and these
   are still worth searching for. */
.ai-row.unverified { opacity: .82; }
.ai-info { flex: 1; min-width: 0; }
.ai-title { font-size: .95rem; }
.ai-bg { color: var(--muted); font-weight: 400; }
/* The reason a title was suggested is the point of the card, so it gets room. */
.ai-why { color: var(--muted); font-size: .85rem; margin: .25rem 0 .4rem; }
.ai-row .transfer-actions { flex-direction: row; flex-wrap: wrap; gap: .35rem; }
.ai-row .poster-thumb { width: 54px; flex-basis: 54px; }

#shelf-recommend .card-sub {
  /* A recommendation's reason runs longer than a normal card subtitle. */
  white-space: normal; line-height: 1.3; max-height: 2.6em; overflow: hidden;
}

/* ------------------------------------------- quick actions on a browse card */

/* Top-right, opposite the rating badges. Always visible rather than shown on
   hover: half the viewing here happens on a phone, where there is no hover. */
.card-quick {
  position: absolute; top: .35rem; right: .35rem;
  display: flex; flex-direction: column; gap: .25rem;
}
.quick-btn {
  position: relative;
  width: 28px; height: 28px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: .85rem; line-height: 1;
  color: #fff; background: rgba(0,0,0,.72);
  border: 1px solid rgba(255,255,255,.22); border-radius: 50%;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, transform .12s ease;
}
.quick-btn:hover  { background: rgba(0,0,0,.9); transform: scale(1.08); }
.quick-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.quick-btn.on     { background: var(--accent); border-color: var(--accent); color: #fff; }
.quick-btn.working { opacity: .55; cursor: progress; }
.quick-btn.failed { background: var(--bad); border-color: var(--bad); }
.quick-btn:disabled { cursor: default; }

/* Touch targets need to be bigger than a mouse needs them to be. */
@media (max-width: 560px) {
  .quick-btn { width: 32px; height: 32px; font-size: .95rem; }

  /* The circle stays small - two 44px discs would cover a third of a 150px
     poster - and the target grows underneath it instead, to 40px.
     The 4px expansion and the 8px gap are a pair: any more and the two targets
     would overlap, leaving a band where the tap goes to the wrong button. */
  .card-quick { gap: .5rem; }
  .quick-btn::after {
    content: ''; position: absolute; top: -4px; right: -4px; bottom: -4px; left: -4px;
  }
}

/* Matches what the watched filter already does to a card elsewhere. */
.card.seen .card-art { opacity: .55; }

/* =======================================================================
   Phone layout

   Measured at 375px, page by page, rather than guessed at. Four things were
   genuinely broken — each one made the whole document scroll sideways, which
   is the worst kind of mobile bug because it moves everything else too:

     search      .result-actions is a fixed column that will not shrink, so a
                 result row ran 54px past the screen
     settings    .toggle is white-space:nowrap, and "Require Bulgarian — sink
                 everything without BG audio or subtitles to the bottom" is a
                 very long label: 547px wide
     account     the same nowrap toggle
     favourites  .profile-select has min-width:150px inside a narrow card

   They watch on phone and tablet as much as anywhere, so this is a first-class
   layout, not a fallback.
   ======================================================================= */

@media (max-width: 560px) {

  /* Reclaim the ~1rem of padding a phone cannot spare. */
  .wrap  { padding: .8rem .7rem; }
  .panel { padding: .8rem .75rem; }

  /* A search result stacks: score and title, then the buttons on their own
     full-width row rather than a column that cannot shrink. */
  .result { flex-wrap: wrap; gap: .6rem; padding: .7rem; }
  .result-main { flex: 1 1 60%; }
  .result-actions {
    flex: 1 1 100%;
    flex-direction: row; flex-wrap: wrap;
    gap: .4rem;
  }
  .result-actions .btn { flex: 1 1 auto; }
  .score { flex-basis: 38px; width: 38px; height: 38px; font-size: .8rem; }

  /* A long checkbox label has to be allowed to wrap. */
  .toggle { white-space: normal; align-items: flex-start; }
  .toggle input { margin-top: .2rem; flex: 0 0 auto; }

  /* Any select in a narrow card, rather than one fixed 150px wide. */
  .profile-select { min-width: 0; max-width: 100%; }

  /* Touch targets. 30px is comfortable with a mouse and fiddly with a thumb. */
  .btn      { min-height: 40px; }
  .btn-sm   { min-height: 36px; padding: .4rem .7rem; }
  select    { min-height: 38px; }
  input[type="checkbox"] { width: 18px; height: 18px; }
  .nav-toggle { min-height: 40px; min-width: 44px; justify-content: center; }
  .quick-btn { width: 34px; height: 34px; }

  /* Panel headers stop fighting for one line. */
  .panel-row, .shelf-head { flex-wrap: wrap; gap: .5rem; }
  .shelf-head h2 { flex: 1 1 auto; }

  /* The describe-it box takes the width; its button takes the next line whole
     rather than sitting as a stub under a full-width field. */
  .ai-input { flex: 1 1 100%; }
  .ai-ask .btn { flex: 1 1 100%; }
  .ai-row { flex-wrap: wrap; }
  .ai-row .poster-thumb { width: 46px; flex-basis: 46px; }
  .ai-row .transfer-actions .btn { flex: 1 1 auto; }

  /* Filter rows read better stacked than squeezed. */
  .filter-row { gap: .5rem; }
  .adv-grid { grid-template-columns: 1fr; }

  /* Cards a little narrower so two fit across a 375px screen. */
  .shelf-body { grid-auto-columns: 132px; }
  .card { width: 132px; }
  .person-grid { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }

  /* The player modal keeps its chrome off the video. */
  .player { max-height: 92vh; }
  .player-head { padding: .5rem .6rem; }

  /* A favourite card is a poster beside text; below this it is too tight. */
  .fav-card { flex-wrap: wrap; }
  .fav-card .poster { width: 76px; flex-basis: 76px; }

  /* Long release names and paths must break rather than push. */
  .transfer-title, .result-title, .fav-title { overflow-wrap: anywhere; }
}

/* Long checkbox labels overflow well before the phone breakpoint — the
   Bulgarian ranking toggle is 60-odd characters. Allowed to wrap from the width
   where the nav already gives way. */
@media (max-width: 720px) {
  .toggle { white-space: normal; align-items: flex-start; }
}

/* Very narrow phones (iPhone SE and similar). */
@media (max-width: 380px) {
  .shelf-body { grid-auto-columns: 124px; }
  .card { width: 124px; }
  .wrap { padding: .7rem .55rem; }
}

/* ------------------------------------------- watchlist availability marks */

/* "Out there" and "available now" used to be grey subtitle text, sitting in
   the same slot and the same colour as a genre list — the one fact worth
   knowing on this shelf, styled exactly like the one that is not. A banner
   across the foot of the poster is the point of the card, so it looks like it. */
.card-flag {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: .22rem .4rem;
  font-size: .68rem; font-weight: 700; letter-spacing: .04em;
  text-align: center; text-transform: uppercase;
  background: rgba(0,0,0,.75); color: #fff;
  backdrop-filter: blur(2px);
}
.card-flag.new  { background: var(--good); color: #fff; }
.card-flag.have { background: var(--accent); color: #fff; }

/* A card with buttons under it is wrapped, so the buttons are not part of the
   clickable poster. */
.card-wrap { display: flex; flex-direction: column; gap: .35rem; }
.card-wrap .card { width: 100%; }
.card-actions { display: flex; gap: .3rem; }
.card-actions .btn { flex: 1 1 auto; width: 100%; }

/* The same two states on the favourites page, where nothing was shown at all. */
.tag.avail-new  { background: var(--good); border-color: var(--good); color: #fff; font-weight: 700; }
.tag.avail-have { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }

/* ------------------------------------------- one scrolling direction on phones

   The home page mixed two behaviours: the shelves scrolled sideways, while
   "On your watchlist" and "Up next" grew downwards. On a phone that meant the
   page ran to several screens of poster grid before reaching anything else,
   and the same gesture did different things in adjacent sections.

   Below the drawer breakpoint everything on the page scrolls the same way:
   sideways, one row per section.                                             */

@media (max-width: 720px) {

  .watchlist-grid {
    display: flex;                    /* not grid: the columns must not wrap */
    gap: .6rem;
    overflow-x: auto;
    padding-bottom: .4rem;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
  }
  .watchlist-grid::-webkit-scrollbar { display: none; }
  .watchlist-grid > * {
    flex: 0 0 132px;
    scroll-snap-align: start;
  }

  /* Up next is three day-columns. Stacked they were three screens tall; side
     by side they are one swipe. */
  .upnext-days {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
    padding-bottom: .4rem;
  }
  .upnext-days::-webkit-scrollbar { display: none; }
  .upnext-day {
    flex: 0 0 86%;                    /* a peek of the next day invites the swipe */
    scroll-snap-align: start;
  }

  /* Same for a filmography and the AI suggestion grid, so nothing on a phone
     is a wrapping grid while its neighbours are rows. */
  .person-grid:not(.watchlist-grid) {
    max-height: none;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  }
}

@media (max-width: 380px) {
  .watchlist-grid > * { flex-basis: 124px; }
}

/* =======================================================================
   Phone application shell

   The pieces that make this feel like an app on a phone rather than a
   desktop page that happens to fit: navigation under the thumb, chrome
   that respects the notch and the home indicator, and inputs that do not
   make iOS zoom the page.
   ======================================================================= */

.tabbar { display: none; }

/* Hidden by default; the tab bar and drawer sheet only exist on phones. */
@media (max-width: 720px) {

  /* ---------------------------------------------------------- tab bar */

  /* One number, three users: the bar's own height, the padding that keeps
     content clear of it, and where the More sheet stops. Hardcoding it three
     times left the sheet overlapping the bar by a border width. */
  :root { --tabbar-h: 55px; }          /* 54px of tab + 1px top border */

  .tabbar {
    display: flex;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    background: var(--panel);
    border-top: 1px solid var(--border);
    /* The home indicator on a modern iPhone sits over the bottom of the
       screen; without this the last row of labels lives underneath it. */
    padding-bottom: env(safe-area-inset-bottom);
  }
  .tabbar a,
  .tab-more {
    flex: 1 1 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .15rem;
    min-height: 54px; padding: .35rem .2rem;
    background: none; border: 0; cursor: pointer;
    color: var(--muted); font: inherit; font-size: .68rem; font-weight: 600;
    text-decoration: none; position: relative;
    -webkit-tap-highlight-color: transparent;
  }
  .tabbar a.active,
  .tab-more.on { color: var(--accent); }
  /* A bar above the active tab, rather than a filled pill: at this size a
     block of colour swallows the icon. */
  .tabbar a.active::before {
    content: ''; position: absolute; top: 0; left: 22%; right: 22%;
    height: 2px; border-radius: 0 0 2px 2px; background: var(--accent);
  }
  .tabbar a:hover, .tab-more:hover { text-decoration: none; color: var(--text); }
  .tab-icon  { font-size: 1.15rem; line-height: 1; }
  .tab-label { line-height: 1; }
  .tab-badge {
    position: absolute; top: .25rem; left: 50%; margin-left: .25rem;
    font-size: .62rem; padding: .05rem .28rem;
  }

  /* Content must not end underneath the bar. */
  body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }

  /* ------------------------------------------------- drawer as a sheet */

  /* More is at the bottom, so the menu it opens comes from the bottom too —
     a drawer dropping from a header the thumb cannot reach is a desktop
     idea wearing a phone's clothes. */
  .nav {
    position: fixed; top: auto; left: 0; right: 0;
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    max-height: 65vh; overflow-y: auto;
    border: 1px solid var(--border); border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    box-shadow: 0 -12px 32px rgba(0, 0, 0, .45);
    padding: .45rem;
    z-index: 41;
    overscroll-behavior: contain;
  }

  /* The header hamburger is redundant once More exists, and two controls for
     one drawer is one too many. */
  .nav-toggle { display: none; }

  /* With navigation moved to the bottom, the header is only a brand and a
     bit of context, so it can be slimmer — and it has to clear the notch. */
  .topbar { padding: .45rem .8rem; padding-top: calc(.45rem + env(safe-area-inset-top)); }
  .brand  { font-size: 1.05rem; }

  /* --------------------------------------------------- touch behaviour */

  /* iOS zooms the whole page when a focused input's text is under 16px, and
     then leaves it zoomed at that width until you pinch back out. Every field
     in this app was 15px, and the two rules below it were 13.6px.
     The specific selectors are named because .adv-grid input and
     .profile-select both beat a bare `input` on specificity — the general rule
     alone left nine fields on the search page still zooming. */
  input, select, textarea,
  .ai-input,
  .search-row input,
  .adv-grid input, .adv-grid select,
  .shelf-filters input, .shelf-filters select,
  .profile-select { font-size: 16px; }

  /* A grey flash on every tap looks broken on custom controls. */
  a, button, .btn, .card, .quick-btn { -webkit-tap-highlight-color: transparent; }

  /* ------------------------------------------------------ touch targets */

  /* A finger is not a mouse pointer: the guidance is 44px, and every button
     here was sized for a cursor. Height only - the widths already come from
     the text - so nothing reflows, rows just get a little taller. */
  .btn { min-height: 44px; }
  .btn-sm { min-height: 40px; padding: .4rem .7rem; font-size: .84rem; }

  /* A checkbox at 18px is the smallest target in the app and there are dozens
     of them in Settings. The label is clickable too, so this is about the
     obvious target rather than the only one. */
  .toggle { padding: .2rem 0; }
  .toggle input[type="checkbox"], .toggle input[type="radio"] { width: 20px; height: 20px; }

  /* Tapping a button should look like something happened, now that the grey
     flash is gone. */
  .btn:active, .quick-btn:active { transform: scale(.97); }

  /* Reaching the end of a shelf should not start scrolling the page, and
     pulling down inside one should not trigger pull-to-refresh. */
  .shelf-body, .watchlist-grid, .upnext-days, .person-grid,
  .player-body, .modal { overscroll-behavior: contain; }
}

/* Landscape phones have almost no height to spare; the labels go first. */
@media (max-width: 900px) and (max-height: 460px) {
  :root { --tabbar-h: 45px; }
  .tabbar a, .tab-more { min-height: 44px; }
  .tab-label { display: none; }
  body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
  .nav { bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); max-height: 78vh; }
}

/* Installed to the home screen there is no browser chrome, so the header
   carries the whole top of the screen. */
@media (display-mode: standalone) {
  .topbar { padding-top: calc(.6rem + env(safe-area-inset-top)); }
}

html {
  /* Stops iOS inflating text in landscape on its own initiative. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* ------------------------------------------------ modals as bottom sheets */

@media (max-width: 720px) {

  /* A centred dialog with a 1rem gutter wastes the sides of a phone screen and
     puts the close button at the top, furthest from the thumb. These become
     sheets: full width, anchored to the bottom, rounded only at the top. */
  .player-back, .modal-back {
    align-items: flex-end;
    padding: 0;
  }
  .player, .modal {
    width: 100%; max-width: none;
    max-height: 90vh;
    border-radius: var(--radius) var(--radius) 0 0;
    border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom);
    animation: mn-sheet-up .18s ease-out;
  }
  /* The header stays put while the body scrolls, so Close is always in reach. */
  .player-head {
    position: sticky; top: 0; z-index: 1;
    background: var(--panel);
  }
  .player-head .btn { min-height: 40px; }

  /* A grab handle: the affordance that says this panel came from the bottom. */
  .player::before, .modal::before {
    content: ''; display: block;
    width: 36px; height: 4px; margin: .45rem auto .1rem;
    border-radius: 999px; background: var(--border);
  }

  .modal { padding: .9rem .8rem calc(.9rem + env(safe-area-inset-bottom)); }
  .modal-actions { flex-wrap: wrap; }
  .modal-actions .btn { flex: 1 1 auto; min-height: 44px; }

  /* Video fills the width and is capped so the controls and the title stay
     on screen together on a short phone. */
  .player-body { padding: .7rem; }
  .player-body video { max-height: 42vh; }

  /* The page behind an open sheet must not scroll. Dragging on the backdrop
     otherwise moves the list underneath, and closing the sheet leaves you
     somewhere you never chose to go.

     `position: fixed` rather than `overflow: hidden`, because iOS Safari
     ignores overflow on the body; app.js stores the scroll position in `top`
     and puts it back on close. */
  body.sheet-open {
    position: fixed; left: 0; right: 0; width: 100%;
    overflow: hidden;
  }
}

/* A phone turned sideways is 400px tall and most of it should be picture.
   The rule above assumes portrait, where the title and the buttons under the
   video are the point; here they are not. */
@media (max-width: 900px) and (max-height: 460px) {
  .player, .modal { max-height: 100vh; }
  .player-body video { max-height: 74vh; }
  .player::before, .modal::before { margin: .2rem auto 0; }
}

/* Opacity only, deliberately.
   The obvious version of this animates translateY from 12% to 0 — and if the
   animation never advances (a backgrounded tab, a throttled renderer) the sheet
   is left sitting 12% below where it belongs, half off the screen. An opacity
   frame that fails to advance leaves a slightly pale sheet in the right place,
   which is a failure nobody would notice. */
@keyframes mn-sheet-up {
  from { opacity: .55; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .player, .modal { animation: none; }
}

/* -------------------------------------------- Settings as an accordion */

@media (max-width: 720px) {

  .panel-foldable > h2 {
    display: flex; align-items: center; gap: .5rem;
    margin: 0; padding: .3rem 0;
    cursor: pointer; user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  .panel-foldable > h2::after {
    content: '▸';
    margin-left: auto; color: var(--muted); font-weight: 400;
    transition: transform .15s ease;
  }
  .panel-foldable.open > h2::after { transform: rotate(90deg); }
  .panel-foldable > h2:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }

  .panel-foldable > .panel-collapse { display: none; }
  .panel-foldable.open > .panel-collapse { display: block; padding-top: .6rem; }

  /* Panels shrink to a row each when closed, so the page reads as a list. */
  .panel-foldable { padding: .7rem .75rem; margin-bottom: .55rem; }

  /* Save, lifted out of the last panel and pinned above the tab bar. Without
     this, saving a key typed six panels up means finding and opening the panel
     that happens to contain the button. */
  .save-bar {
    position: fixed; left: 0; right: 0;
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    z-index: 39;
    padding: .5rem .7rem;
    background: var(--panel);
    border-top: 1px solid var(--border);
    box-shadow: 0 -6px 18px rgba(0, 0, 0, .35);
  }
  .save-bar .btn { width: 100%; min-height: 44px; }
  /* The result message can be several lines; it scrolls rather than pushing
     the button off the screen. */
  .save-bar .alert { margin: .5rem 0 0; max-height: 30vh; overflow-y: auto; }

  /* And the page needs room for it. */
  body[data-page="settings"] { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 68px); }
}

/* =================================================== ratings and sharing */

/* The verdict, wherever it appears. Colour carries the meaning, because these
   sit next to genre tags that must not read as an opinion. */
.verdict {
  display: inline-flex; align-items: center;
  padding: .1rem .45rem; border-radius: 6px;
  font-size: .78rem; font-weight: 700;
  border: 1px solid;
}
.verdict.great { color: #f0b429; border-color: #f0b429; background: rgba(240,180,41,.12); }
.verdict.good  { color: var(--good); border-color: var(--good); background: rgba(46,160,67,.12); }
.verdict.nope  { color: var(--bad);  border-color: var(--bad);  background: rgba(248,81,73,.12); }

.rating-box {
  margin-top: .8rem; padding-top: .7rem;
  border-top: 1px solid var(--border);
}
.rating-head { margin: 0 0 .45rem; }
.rating-row { display: flex; gap: .4rem; flex-wrap: wrap; }
.rating-btn { border-color: var(--border); }
/* Only the chosen one is filled in — three coloured buttons would compete with
   each other and with the poster beside them. */
.rating-btn.great.on { background: #f0b429; border-color: #f0b429; color: #1a1f28; }
.rating-btn.good.on  { background: var(--good); border-color: var(--good); color: #fff; }
.rating-btn.nope.on  { background: var(--bad);  border-color: var(--bad);  color: #fff; }
.rating-status { display: block; margin-top: .4rem; min-height: 1.2em; }

/* ------------------------------------------------------- share dialogs */

.share-block { padding: .8rem 0; border-bottom: 1px solid var(--border); }
.share-block:last-child { border-bottom: 0; }
.share-block h3 { font-size: .95rem; margin-bottom: .3rem; }
.share-block input[type="text"] { margin-top: .4rem; }
.share-block .btn { margin-top: .5rem; }

.share-url { display: flex; gap: .4rem; margin-top: .6rem; }
.share-url input {
  flex: 1 1 auto; min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem;
}

/* ------------------------------------------------- inbox and link lists */

.share-row {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .6rem .2rem; border-bottom: 1px solid var(--border);
}
.share-row:last-child { border-bottom: 0; }
.share-row.unseen { border-left: 3px solid var(--accent); padding-left: .5rem; }
.share-row-body { flex: 1; min-width: 0; }
.share-note { color: var(--muted); font-size: .85rem; margin: .35rem 0; font-style: italic; }

.link-row {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .6rem .2rem; border-bottom: 1px solid var(--border);
}
.link-row:last-child { border-bottom: 0; }
.link-row.dim { opacity: .55; }
.link-row-body { flex: 1; min-width: 0; }

/* ------------------------------------------------- the public share page */

.share-page .topbar { justify-content: center; }
.share-message { margin: .5rem 0 0; }
.share-list { display: flex; flex-direction: column; gap: .5rem; }
.share-item {
  display: flex; gap: .8rem; align-items: flex-start;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .7rem .8rem;
}
.share-item-body { flex: 1; min-width: 0; }
.share-item-title { font-weight: 600; margin-bottom: .35rem; overflow-wrap: anywhere; }
.share-item .poster-thumb { width: 54px; flex-basis: 54px; }
.share-foot { margin-top: 1.2rem; text-align: center; }

@media (max-width: 720px) {
  .rating-row .btn { flex: 1 1 auto; }
  .share-url { flex-wrap: wrap; }
  .share-url input { flex: 1 1 100%; }
  .link-row { flex-wrap: wrap; }
  /* No tab bar on the public page, so nothing to clear. */
  .share-page { padding-bottom: 0; }
}

/* Playback offered on a share link. Boxed off because it is the one option in
   this dialog that hands a stranger a video rather than a sentence. */
.play-option {
  margin-top: .7rem; padding: .6rem .7rem;
  border: 1px solid var(--warn); border-radius: var(--radius);
  background: rgba(210, 153, 34, .08);
}
.play-option .toggle { white-space: normal; align-items: flex-start; }
.play-option .hint { margin-top: .35rem; }

/* The player on a shared page.
   `width: 100%` alone is not enough armour: a video element carries the
   intrinsic size of its media, and any rule that fails to apply — or arrives
   late — leaves a 1920-wide box bursting out of its panel. max-width and
   overflow on the parent make that impossible rather than unlikely.
   It is also capped: on a wide monitor a full-width player filled the screen,
   which is not what a page of one recommendation should look like. */
.share-play { overflow: hidden; }
.share-play video {
  display: block;
  width: 100%;
  max-width: min(860px, 100%);
  margin: 0 auto;
  height: auto;
  max-height: 70vh;
  aspect-ratio: 16 / 9;          /* a sensible box before metadata arrives */
  object-fit: contain;
  background: #000;
  border-radius: var(--radius);
}

.share-converting {
  padding: 1.4rem 1rem; text-align: center;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--bg);
}
.share-converting .bar { max-width: 320px; margin: .6rem auto 0; }

@media (max-width: 720px) {
  .share-play video { max-height: 45vh; }
}

/* Revoke and Delete side by side; on a phone they wrap under the link. */
.link-row-actions { display: flex; gap: .35rem; flex-wrap: wrap; flex: 0 0 auto; }
@media (max-width: 720px) {
  .link-row-actions { flex: 1 1 100%; }
  .link-row-actions .btn { flex: 1 1 auto; }
}

/* ======================================================= AI usage and cost */

.usage-cards {
  display: grid; gap: .6rem; margin-bottom: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.usage-card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .8rem .9rem;
}
.usage-card-label { color: var(--muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; }
.usage-card-value { font-size: 1.5rem; font-weight: 700; margin: .15rem 0 .1rem; }
.usage-card-sub   { color: var(--muted); font-size: .8rem; }
/* Unpriced calls are the one thing on this page that must not read as zero. */
.usage-card-warn  { color: var(--warn); font-size: .8rem; font-weight: 600; }
.usage-warn       { color: var(--warn); font-weight: 600; }

/* A bar per day. Deliberately not a charting library: it is one series and the
   tooltip carries the detail. */
.usage-chart {
  display: flex; align-items: flex-end; gap: .25rem;
  height: 130px; overflow-x: auto; padding-bottom: .2rem;
}
.usage-bar {
  flex: 1 0 22px; min-width: 22px; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: .25rem;
}
.usage-bar i {
  display: block; width: 100%; border-radius: 3px 3px 0 0;
  background: var(--accent); opacity: .85;
}
.usage-bar:hover i { opacity: 1; }
.usage-bar span { color: var(--muted); font-size: .62rem; white-space: nowrap; }

.usage-tables { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

/* Tables scroll inside their panel rather than widening the page. */
.usage-table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.usage-table th, .usage-table td { padding: .35rem .4rem; border-bottom: 1px solid var(--border); }
.usage-table th { color: var(--muted); font-weight: 600; text-align: left; white-space: nowrap; }
.usage-table td.right, .usage-table th.right { text-align: right; }
.usage-table tr:last-child td { border-bottom: 0; }

.price-row {
  display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap;
  padding: .35rem 0; border-bottom: 1px solid var(--border);
}
.price-row:last-child { border-bottom: 0; }
.price-model { flex: 1 1 12rem; width: auto; min-width: 0; }
.price-model-fixed { flex: 1 1 12rem; color: var(--muted); font-weight: 600; }
.price-field { display: flex; flex-direction: column; gap: .1rem; margin: 0; font-size: .72rem; color: var(--muted); }
.price-field input { width: 6.5rem; }
.price-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .7rem; }

@media (max-width: 720px) {
  /* A seven-column table does not fit a phone; it scrolls on its own rather
     than taking the page sideways with it. */
  #usage-recent, #usage-kind, #usage-user, #usage-model { overflow-x: auto; }
  .usage-table { min-width: 420px; }
  .price-model { flex: 1 1 100%; }
  .price-field input { width: 5.5rem; }
}

/* ================================================ the home page on a phone */

@media (max-width: 720px) {

  /* The shelf arrows are a pointer affordance: they fade in on hover, and a
     touch screen has no hover, so on a phone they never appear — but the one
     on a shelf that CAN scroll keeps `pointer-events: auto`, which leaves an
     invisible 46px control sitting on top of the last card. Tapping a poster
     near the right edge opened nothing at all.

     Touch scrolls by swiping and does not need them. */
  .shelf-nav { display: none; }

  /* An empty shelf was 81px of padding around one sentence, and a fresh
     account has three of them stacked above the first real row. */
  .shelf-empty { padding: .5rem .2rem; font-size: .85rem; }
  .shelf .empty { padding: 1rem .5rem; }

  /* Tighter vertical rhythm. Nine sections at 1.6rem apart spend a third of a
     screen on gaps alone. */
  .shelf { margin-bottom: 1.05rem; }
  .shelf-head { margin-bottom: .45rem; }
  .panel { padding: .7rem .75rem; margin-bottom: .6rem; }
  .hero { padding-bottom: .55rem; }

  /* The panel headings carry a title, a hint and two buttons, and at 1.05rem
     the title alone claims a line of its own. */
  .panel-row { gap: .4rem; }
  .panel-row h2, .upnext h2 { font-size: 1rem; }

  /* The sentence under a panel heading sits between the title and its buttons
     in the markup, which on a narrow screen pushes the buttons onto a third
     line. Moved last, the title and its buttons share the top line and the
     explanation reads underneath them. */
  .panel-row > .hint { order: 3; flex: 1 1 100%; margin: 0; }

  .alert { padding: .55rem .7rem; margin: .5rem 0; }
}

/* Subtitle timing: a nudge for a track written against another release. */
.sub-offset {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: .5rem 0; border-top: 1px solid var(--border); margin-top: .4rem;
}
.sub-offset-value {
  min-width: 3.4rem; text-align: center;
  font-weight: 600; font-variant-numeric: tabular-nums;
}
.share-offset { display: inline-flex; align-items: center; gap: .35rem; margin-left: .5rem; flex-wrap: wrap; }
.share-offset #share-offset-value { font-variant-numeric: tabular-nums; min-width: 3.2rem; text-align: center; }

/* "To this device": the film and its subtitle files, offered as a list of
   links because a browser cannot be handed a folder and zipping a 4 GB video
   through shared hosting is not an option. Wraps rather than nowraps — a
   release name is long and this sits inside a transfer card (gotcha 16). */
.takeaway {
  margin-top: .6rem;
  padding: .6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel-2);
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  align-items: center;
}
.takeaway .hint { flex: 1 1 100%; margin: 0; }
.takeaway .btn { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A track that looks like it belongs to another film is still offered, just
   not encouraged. */
.takeaway .btn.dim { opacity: .55; }
