/* Huddelo — design tokens.

   Two skins, one geometry. The dark skin and the light skin were redrawn from
   the same prototype and differ in nothing but their palette: the same radii,
   the same type scale, the same letter-spacing. Every value below is read off
   `Hudello Dark Editorial.dc.html` and `Hudello Light.dc.html` (the operator's
   "Ink & cobalt" option), counted by frequency across the nine screens each.

   Light is the default — the one for everyone; dark is the one for the geeks,
   a tap away at the top right. Everything that sits ON artwork (posters,
   trailers, the swipe card, the tutorial's card) keeps its own dark glass in
   both skins, because the art is dark whatever the page is, and its colours
   are the shared `--art-*` tokens rather than the skin's.

   Two accents, because a colour that fills a button and a colour that sets a
   9.5px mono label are asked for different things. `--accent` is the fill and
   is the prototype's value untouched. `--accent-text` is the same hue one
   step down, and exists because the fill misses AA as small type:
   light #0a66ff reads 4.30:1 on --surface-2 and 4.15:1 on --surface-3, and
   dark #3b82f6 reads 3.90:1 on --surface-3. #0850c9 is the prototype's own
   pressed step and clears every surface at 6.03:1; #6ba1f8 is its own
   `accent-hi` and clears at 5.52:1. Neither is an invented colour.
*/
:root {
  color-scheme: light;
  /* Ink & cobalt, on white. */
  --bg: #ffffff;
  --bg-outer: #f2f2f2;
  --surface: #ffffff;
  --surface-2: #f2f2f2;
  --surface-3: #eeeeee;
  --line: #e2e2e2;
  --line-strong: rgba(10, 10, 10, 0.16);
  --fg: #0a0a0a;
  /* The prototype's muted ladder is .75/.6/.55/.5/.42/.4/.35/.32 and the four
     steps below .57 do not carry text: .4 reads 2.71:1 on white and 2.65:1 on
     --surface-3. These are the smallest values in the same ink that clear
     4.5:1 on every surface the app puts them on, measured on --surface-3,
     which is the worst of the three: --fg-2 8.53:1, --fg-3 6.13:1,
     --fg-4 4.53:1. The ladder is compressed, not the floor lowered. */
  --fg-2: rgba(10, 10, 10, 0.75);
  --fg-3: rgba(10, 10, 10, 0.66);
  --fg-4: rgba(10, 10, 10, 0.57);
  --accent: #0a66ff;
  --accent-text: #0850c9;
  --accent-ink: #ffffff;
  --tint: #f5f8ff;
  --tint-line: #dbe7ff;
  --tint-strong: #cfe0ff;
  --danger: #a32b1c;
  --ok: #2f7a4f;
  --ok-tint: #f2f9f4;
  --ok-tint-line: #cfe6d6;
  --hi: #1f6b3c;
  --lo: #a32b1c;
  /* Tiers are text on a trophy card, so they answer to the same floor: the
     prototype's silver holds, its bronze (#9a5f2c, 4.47:1) and platinum
     (#1d7f88, 4.07:1) do not, and each moved one step. */
  /* The IMDb chip's gold. The prototype's #e8bd3a is 1.53:1 on white — QA
     measured it — and 8.05:1 on the dark glass, so it is kept where it works
     (the swipe card, --art-rate-imdb) and moved one step down for paper,
     where #82631a reads 5.60:1 on white and 4.83:1 on --surface-3. */
  --rate-imdb: #82631a;
  --tier-bronze: #8a5a2e;
  --tier-silver: #5f6a7a;
  --tier-platinum: #156b74;
  --glass: rgba(255, 255, 255, 0.82);
  --glass-border: rgba(10, 10, 10, 0.14);
  --scrim: rgba(0, 0, 0, 0.55);
  --shadow: rgba(10, 10, 10, 0.22);
  --shadow-strong: rgba(10, 10, 10, 0.35);

  /* On artwork, in both skins. The prototype's own dark glass. */
  --art-glass: rgba(10, 10, 10, 0.72);
  --art-glass-strong: rgba(10, 10, 10, 0.82);
  --art-border: rgba(245, 245, 245, 0.25);
  --art-fg: #f5f5f5;
  --art-fg-strong: rgba(245, 245, 245, 0.88);
  --art-fg-2: rgba(245, 245, 245, 0.78);
  --art-fg-3: rgba(245, 245, 245, 0.72);
  --art-line: rgba(245, 245, 245, 0.2);
  /* The groove a score gauge is cut into on artwork: white, not the fg ink,
     because it reads as an absence of light rather than as a border. */
  --art-track: rgba(255, 255, 255, 0.16);
  --art-accent: #6ba1f8;
  --art-rate-imdb: #e8bd3a;
  /* The green a poster wears. `.free-pill`'s ground is the poster, not the
     page, so its ink cannot be the page's green: the light skin's --ok is a
     dark green chosen for white paper, it does not read on dark art, and the
     same badge rendered a different green in each skin. Declared once, with
     the family, so the pill is one green wherever it is drawn. */
  --art-ok: #34c759;
  --art-black: #000000;
  --art-shadow: rgba(0, 0, 0, 0.8);
  /* The artwork's own ink and the overlay laid over it, in white and in black.
     Every wash on a poster is a strength of one of these rather than a value
     mixed by hand at the point of use: the placeholder scrims named three
     near-blacks (5,5,7 / 6,6,8 / 10,10,11) where the design has one ink,
     #0a0a0a, and the hairline weave and the hand's ring named a white and a
     near-white where the design has one overlay white. The value changed with
     the token for the scrims, by design; the weave was already #ffffff and is
     unchanged. Neither follows the skin — the art is dark on a white page, so
     the wash over it is not the page's. The three hand-mixed drop shadows
     (black at .6, .65 and .7) fold onto --art-shadow above, which the score
     glyph already used, so the shadow is one value too. */
  --art-ink: #0a0a0a;
  --art-white: #ffffff;

  /* Shape. Counted across both prototypes: 10px is the primary radius and
     carries cards, panels, sheets and buttons; the ladder below is every other
     value they use. 40px is the prototype's own device frame, which is why it
     is named for the frame and not used on a surface. */
  --r-bar: 2px;
  --r-tag: 5px;
  --r-badge: 6px;
  --r-tile: 8px;
  --r-card: 10px;
  --r-lg: 12px;
  --r-deck: 16px;
  --r-bubble: 16px 16px 16px 4px;
  --r-bubble-mine: 16px 16px 4px 16px;
  --r-tabs: 20px;
  --r-sheet: 24px 24px 0 0;
  --r-frame: 40px;
  --r-pill: 99px;
  --r-round: 50%;

  /* Type. One geometry across the two skins: a system sans for prose and
     headings, a terminal monospace for eyebrows, badges and counts. The
     prototype writes `system-ui,-apple-system,"Segoe UI",sans-serif`; the
     ticket quoted it without the Segoe fallback, and the file is right. */
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, Menlo, monospace;
  /* Named explicitly rather than trusting the default sans to carry emoji: a
     Linux desktop without a colour-emoji font renders every trophy as a tofu
     box, which is how this looked in the headless test browser. */
  --emoji: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji",
           "Twemoji Mozilla", "EmojiOne Color", sans-serif;

  --pad: 22px;
  --rail-gap: 26px;
}
:root[data-theme="dark"] {
  color-scheme: dark;
  /* Dark editorial. */
  --bg: #111111;
  --bg-outer: #0a0a0a;
  --surface: #1a1a1a;
  --surface-2: #181818;
  --surface-3: #2a2a2a;
  --line: rgba(255, 255, 255, 0.12);
  --line-strong: rgba(255, 255, 255, 0.14);
  --fg: #f5f5f5;
  /* .75 and .6 are the prototype's own, and both carry text here. Its .5 step
     reads 4.50:1 on --surface-3 — a hairline pass that rounds either way, so
     --fg-4 is .52, which reads 4.73:1. --fg-2 6.42:1, --fg-3 5.76:1. */
  --fg-2: rgba(245, 245, 245, 0.75);
  --fg-3: rgba(245, 245, 245, 0.6);
  --fg-4: rgba(245, 245, 245, 0.52);
  --accent: #3b82f6;
  --accent-text: #6ba1f8;
  --accent-ink: #0a0a0a;
  --tint: rgba(59, 130, 246, 0.14);
  --tint-line: rgba(107, 161, 248, 0.34);
  --tint-strong: rgba(107, 161, 248, 0.5);
  --danger: #ff6b52;
  --ok: #34c759;
  --ok-tint: rgba(52, 199, 89, 0.14);
  --ok-tint-line: rgba(52, 199, 89, 0.34);
  --hi: #4ade80;
  --lo: #f87171;
  --rate-imdb: #e8bd3a;
  --tier-bronze: #c58a5a;
  --tier-silver: #bec4d0;
  --tier-platinum: #7ed6df;
  --glass: rgba(10, 10, 10, 0.72);
  --glass-border: rgba(245, 245, 245, 0.25);
  --scrim: rgba(0, 0, 0, 0.7);
  --shadow: rgba(0, 0, 0, 0.8);
  --shadow-strong: rgba(0, 0, 0, 0.95);
}


* { box-sizing: border-box; }
html, body { height: 100%; }
/* On the root as well as the body, so the two properties are the skin's even
   for the box that inherits them into nothing — a render sweep that asks the
   document itself what colour and family it carries should hear the token, not
   the browser's own Times New Roman. */
html { color: var(--fg); font-family: var(--sans); overscroll-behavior: none; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
::-webkit-scrollbar { width: 0; height: 0; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; padding: 0; }
a { color: var(--accent-text); }

.app {
  max-width: 480px;
  margin: 0 auto;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  position: relative;
  background: var(--bg);
}
@media (min-width: 520px) {
  body { background: var(--bg-outer); }
  .app {
    min-height: 100dvh;
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
  }
}

/* ---------------------------------------------------------------- atoms */

.mono { font-family: var(--mono); }
.eyebrow {
  font: 600 9.5px/1 var(--mono);
  letter-spacing: 0.1em;
  color: var(--fg-3);
  text-transform: uppercase;
}
.h1 { font: 600 23px/1.18 var(--sans); letter-spacing: -0.02em; margin: 0; text-wrap: pretty; }
.h2 { font: 600 17px/1.1 var(--sans); letter-spacing: -0.015em; margin: 0; }
.h3 { font: 600 14.5px/1.25 var(--sans); letter-spacing: -0.01em; margin: 0; }
.dim { color: var(--fg-3); }
.tiny { font: 400 11.5px/1.45 var(--sans); color: var(--fg-3); }

.btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 15px 20px; border-radius: var(--r-card);
  background: var(--accent); color: var(--accent-ink);
  font: 500 15px/1 var(--sans); letter-spacing: -0.01em;
  transition: opacity 0.15s;
}
.btn:active { opacity: 0.7; }
.btn[disabled] { opacity: 0.35; pointer-events: none; }
.btn.ghost { background: var(--surface-2); color: var(--fg); border: 1px solid var(--line); }
.btn.quiet { background: transparent; color: var(--fg-3); font-size: 13px; padding: 12px; }

.field {
  width: 100%; padding: 15px 16px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 16px/1.2 var(--sans); color: var(--fg);
}
.field:focus { outline: none; border-color: var(--accent-text); }
.field::placeholder { color: var(--fg-4); }
/* A slider. The prototype never draws one, so there was no design value to
   copy and the browser's own painting showed through instead: Chrome's
   dark-mode track is rgb(59,59,59) in BOTH skins, a grey nobody chose, sitting
   on the light skin's white and on the dark skin's #181818. Drawn here in the
   skin's own tokens so the control belongs to the page it is on. */
input[type="range"] {
  -webkit-appearance: none; appearance: none;
  background: transparent; accent-color: var(--accent);
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: var(--r-bar); background: var(--surface-3); border: 1px solid var(--line);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 12px; margin-top: -3px;
  border-radius: var(--r-round); background: var(--accent);
}
input[type="range"]::-moz-range-track {
  height: 4px; border-radius: var(--r-bar); background: var(--surface-3); border: 1px solid var(--line);
}
input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px; border: 0;
  border-radius: var(--r-round); background: var(--accent);
}
/* On a chip that is itself filled with the accent, an accent thumb would be
   invisible: the same trade the chips' own numbers make, so the same answer. */
.genre.on input[type="range"]::-webkit-slider-runnable-track,
.genre.on input[type="range"]::-moz-range-track {
  background: color-mix(in srgb, var(--accent-ink) 30%, transparent); border-color: transparent;
}
.genre.on input[type="range"]::-webkit-slider-thumb,
.genre.on input[type="range"]::-moz-range-thumb { background: var(--accent-ink); }

.screen { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.pad { padding: 0 var(--pad); }

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: 96px; z-index: 90;
  background: var(--fg); color: var(--bg);
  padding: 11px 16px; border-radius: var(--r-card);
  font: 500 13px/1 var(--sans);
  box-shadow: 0 12px 30px -12px var(--shadow-strong);
  animation: rise 0.2s ease-out;
}
@keyframes rise { from { opacity: 0; transform: translate(-50%, 8px); } }

.boot { flex: 1; display: grid; place-content: center; gap: 10px; text-align: center; }
.boot-mark { font: 600 12px/1 var(--mono); letter-spacing: 0.22em; color: var(--accent-text); }
.boot-note { font: 400 13px/1 var(--sans); color: var(--fg-3); }

/* --------------------------------------------------------------- poster */

/* Key art is a labelled placeholder by design — the prototype ships no
   licensed poster imagery. Each title gets a deterministic hue, a stripe
   texture and a ghosted initial so a shelf reads as a shelf of distinct
   things rather than a row of identical dark rectangles. */
.poster {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  background:
    radial-gradient(120% 80% at 22% 8%, hsl(var(--hue) 46% 26%) 0%, transparent 62%),
    repeating-linear-gradient(115deg, color-mix(in srgb, var(--art-white) 3.5%, transparent) 0 11px, transparent 11px 22px),
    linear-gradient(168deg, hsl(var(--hue) 30% 17%) 0%, hsl(var(--hue) 24% 9%) 68%, hsl(var(--hue) 24% 6%) 100%);
  border: 1px solid var(--line);
  display: flex; align-items: flex-end;
  flex: none;
  isolation: isolate;
}
.poster::before {
  content: attr(data-initial);
  position: absolute; right: -6%; top: 46%; transform: translateY(-50%);
  font: 700 clamp(64px, 62%, 190px)/0.78 var(--sans);
  color: hsl(var(--hue) 40% 62%);
  opacity: .13; letter-spacing: -0.06em; pointer-events: none;
}
.poster .poster-name {
  position: relative;
  padding: 10px 10px 30px; max-width: 88%;
  font: 500 10.5px/1.4 var(--mono);
  color: var(--art-fg-2); letter-spacing: 0.02em;
  text-shadow: 0 1px 10px var(--shadow-strong);
}
.card .poster .poster-name { font-size: 9.5px; padding-bottom: 28px; }
.badge {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 4px 7px; border-radius: var(--r-badge);
  background: var(--art-glass-strong); backdrop-filter: blur(6px);
  font: 600 9px/1 var(--mono); letter-spacing: 0.06em; color: var(--art-fg);
}
.badge.rent { color: var(--art-fg-3); }
.score-pill {
  position: absolute; bottom: 8px; right: 8px; z-index: 2;
  padding: 5px 8px; border-radius: var(--r-tag);
  background: var(--accent); color: var(--accent-ink);
  font: 600 10px/1 var(--mono);
}
.urgency-pill {
  position: absolute; top: 8px; right: 8px;
  padding: 4px 6px; border-radius: var(--r-tag);
  background: var(--accent); color: var(--accent-ink);
  font: 600 9px/1 var(--mono);
}

/* ---------------------------------------------------------------- rails */

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(env(safe-area-inset-top) + 16px) var(--pad) 4px;
}
.avatar {
  width: 32px; height: 32px; border-radius: var(--r-round);
  background: var(--accent); color: var(--accent-ink);
  display: grid; place-content: center;
  font: 600 12px/1 var(--sans); flex: none;
}
.avatar.alt { background: var(--surface-3); color: var(--fg-2); }
.avatar.sm { width: 26px; height: 26px; font-size: 10px; }
.services-line { padding: 2px var(--pad) 0; font: 500 11px/1.5 var(--mono); color: var(--fg-4); letter-spacing: .04em; }

.rail { margin-top: var(--rail-gap); }
.rail-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 9px;
  padding: 0 var(--pad) 12px;
}
.rail-head .count { font: 600 9.5px/1 var(--mono); color: var(--fg-3); letter-spacing: .08em; flex: none; }
.rail-head .count.accent { color: var(--accent-text); }
.rail-sub { padding: 0 var(--pad) 10px; margin-top: -8px; font: 500 10.5px/1.4 var(--mono); color: var(--fg-4); letter-spacing: .04em; }

.track { display: flex; gap: 12px; overflow-x: auto; padding: 0 var(--pad) 4px; scroll-padding-inline: var(--pad); scroll-snap-type: x proximity; }
.track > * { scroll-snap-align: start; }
/* Newest first: where a rail passes into an older release-year band. */
.track > .band-mark { flex: none; align-self: center; font: 600 9.5px/1 var(--mono); letter-spacing: .08em;
  color: var(--fg-3); writing-mode: vertical-rl; transform: rotate(180deg); padding: 0 2px; scroll-snap-align: none; }

.card-hero { flex: none; width: 186px; display: flex; flex-direction: column; gap: 9px; text-align: left; }
.card-hero .poster { width: 186px; height: 262px; border-radius: var(--r-lg); }
.card-hero .t { font: 400 14.5px/1.2 var(--sans); }
.card-hero .why { font: 400 11.5px/1.35 var(--sans); color: var(--fg-3); text-wrap: pretty; }

.card { flex: none; width: 124px; display: flex; flex-direction: column; gap: 7px; text-align: left; }
.card .poster { width: 124px; height: 176px; border-radius: var(--r-lg); }
.card .t { font: 400 12.5px/1.25 var(--sans); }
.card .s { font: 500 10px/1 var(--mono); color: var(--accent-text); }

.prompt-card {
  margin: var(--rail-gap) var(--pad) 0;
  padding: 18px 18px 20px; border-radius: var(--r-card);
  background: var(--surface); border: 1px solid var(--line);
}
.prompt-card .headline { margin-top: 10px; font: 400 17px/1.25 var(--sans); letter-spacing: -0.01em; text-wrap: pretty; }
.stack { display: flex; }
.stack .avatar + .avatar { margin-left: -9px; border: 2px solid var(--surface); }

/* ------------------------------------------------------------ mood chip */

.mood-chip {
  position: fixed; bottom: calc(env(safe-area-inset-bottom) + 112px);
  left: 50%; transform: translateX(-50%); z-index: 40;
  display: flex; align-items: center; gap: 9px;
  /* The comp makes this an ochre outlined control, not a floating white pill:
     13px by 19px inside an accent wash and a visible accent border. */
  padding: 13px 19px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--accent) 14%, transparent); backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  font: 400 14.5px/1 var(--sans); color: var(--accent-text);
  box-shadow: 0 14px 34px -14px var(--shadow-strong);
  white-space: nowrap;
}
.mood-chip.on { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.mood-chip .dot { width: 7px; height: 7px; border-radius: var(--r-round); background: var(--accent); }
.mood-chip.on .dot { background: var(--accent-ink); }

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

.nav {
  position: sticky; bottom: 0; z-index: 50;
  padding: 10px var(--pad) calc(env(safe-area-inset-bottom) + 26px);
  background: linear-gradient(to top, var(--bg) 72%, transparent);
}
/* This is the surrounded rectangle visible in the supplied home comp. The old
   nav drew only a divider across the viewport, so the control had no box. */
.nav-inner { display: grid; grid-template-columns: repeat(5, 1fr); }
.nav-inner {
  padding: 14px 26px; border-radius: var(--r-tabs);
  background: var(--surface); border: 1px solid var(--line-strong);
  backdrop-filter: blur(12px);
}
.nav-brand { display: none; }
.nav button {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  font: 500 9.5px/1 var(--mono); letter-spacing: .08em; color: var(--fg-4);
  text-transform: uppercase;
}
.nav button .glyph { font-size: 17px; line-height: 1; filter: grayscale(1) opacity(.55); }
.nav button.on { color: var(--accent-text); }
.nav button.on .glyph { filter: none; }

/* -------------------------------------------------------------- sheets */

.sheet-back {
  position: fixed; inset: 0; z-index: 60;
  background: var(--scrim); backdrop-filter: blur(3px);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade .16s ease-out;
}
@keyframes fade { from { opacity: 0; } }
.sheet {
  width: 100%; max-width: 480px;
  background: var(--surface); border: 1px solid var(--line); border-bottom: 0;
  border-radius: var(--r-sheet);
  padding: 8px 0 calc(env(safe-area-inset-bottom) + 16px);
  animation: slide .2s cubic-bezier(.2,.8,.3,1);
  max-height: 86dvh; overflow-y: auto;
}
@keyframes slide { from { transform: translateY(28px); } }
.sheet .grip { width: 36px; height: 4px; border-radius: var(--r-bar); background: var(--line-strong); margin: 6px auto 16px; }
.sheet-title { padding: 0 var(--pad) 4px; font: 400 19px/1.2 var(--sans); letter-spacing: -.015em; }
.sheet-note { padding: 6px var(--pad) 12px; font: 400 12.5px/1.5 var(--sans); color: var(--fg-3); }

.option-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 15px var(--pad);
  border-top: 1px solid var(--line);
  font: 400 15px/1.3 var(--sans); text-align: left;
}
.option-row:active { background: var(--surface-2); }
.option-row .tick { color: var(--accent-text); font: 600 13px/1 var(--mono); flex: none; }
.option-row.sel { color: var(--accent-text); }

/* ---------------------------------------------------------------- quiz */

.meter-wrap { padding: 0 var(--pad); }
.meter { border: 1px solid var(--line); height: 5px; border-radius: var(--r-bar); background: var(--surface-3); overflow: hidden; box-sizing: border-box; }
.meter i { display: block; height: 100%; background: var(--accent); transition: width .5s cubic-bezier(.2,.8,.3,1); }
.meter-row { display: flex; justify-content: space-between; align-items: baseline; padding: 9px 0 0; }
.meter-row .pct { font: 600 12px/1 var(--mono); color: var(--accent-text); }
.meter-row .lbl { font: 600 9.5px/1 var(--mono); letter-spacing: .12em; color: var(--fg-4); }

.quiz-opt {
  display: block; width: 100%; text-align: left;
  padding: 18px; margin-bottom: 10px; border-radius: var(--r-lg);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 15.5px/1.3 var(--sans); text-wrap: pretty;
}
.quiz-opt:active { border-color: var(--accent-text); }

/* -------------------------------------------------------------- swipe */

.deck { position: relative; flex: 1; margin: 18px var(--pad) 0; min-height: 380px; }
.swipe-card {
  position: absolute; inset: 0; z-index: 1;
  border-radius: var(--r-deck); overflow: hidden;
  background:
    radial-gradient(110% 70% at 24% 6%, hsl(var(--hue) 48% 28%) 0%, transparent 60%),
    repeating-linear-gradient(115deg, color-mix(in srgb, var(--art-white) 3.5%, transparent) 0 13px, transparent 13px 26px),
    linear-gradient(168deg, hsl(var(--hue) 32% 18%) 0%, hsl(var(--hue) 24% 9%) 62%, hsl(var(--hue) 24% 5%) 100%);
  border: 1px solid var(--line-strong);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 20px;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  will-change: transform;
  box-shadow: 0 30px 60px -30px var(--shadow-strong);
}
.swipe-card.behind { transform: scale(.94) translateY(14px); opacity: .55; }
.swipe-card .verdict {
  position: absolute; top: 24px; padding: 7px 12px; border-radius: var(--r-card);
  font: 700 12px/1 var(--mono); letter-spacing: .12em; opacity: 0;
  border: 2px solid currentColor;
}
.swipe-card .verdict.like { right: 20px; color: var(--ok);     transform: rotate(9deg); }
.swipe-card .verdict.pass { left: 20px;  color: var(--danger); transform: rotate(-9deg); }
.swipe-card .verdict.up { left: 50%; transform: translateX(-50%); color: var(--ok); text-transform: uppercase; }
.swipe-actions { display: flex; gap: 10px; padding: 18px var(--pad) 0; }
.swipe-actions .btn { flex: 1; }

/* ------------------------------------------------------------- detail */

.detail-hero {
  height: 400px; margin: 0; border-radius: 0;
  position: relative; overflow: hidden;
  /* The hero's words are page ink and the block is bottom-anchored, so the art
     has to be out of the way where they sit. With the real art that is the
     `--bg` wash above; this is the same promise for the placeholder, and it
     used to arrive too late — the page colour only landed on the last pixel
     row, leaving the eyebrow 37% art under it, 4.4:1 at 11.4px. Fading from
     44% to 72% puts the whole text block on the page in both renders. */
  background:
    radial-gradient(90% 60% at 76% 14%, hsl(var(--hue) 50% 30%) 0%, transparent 66%),
    repeating-linear-gradient(115deg, color-mix(in srgb, var(--art-white) 3.5%, transparent) 0 14px, transparent 14px 28px),
    linear-gradient(180deg, hsl(var(--hue) 30% 16%) 0%, hsl(var(--hue) 20% 9%) 44%, var(--bg) 72%);
  display: flex; align-items: flex-end; padding: 20px;
}
.detail-hero::before, .swipe-card::before {
  content: attr(data-initial);
  position: absolute; right: 4%; top: 42%; transform: translateY(-50%);
  font: 700 190px/0.78 var(--sans); color: hsl(var(--hue) 44% 64%);
  opacity: .12; letter-spacing: -0.06em; pointer-events: none;
}
.detail-hero > div, .swipe-card > * { position: relative; z-index: 1; }
.back-btn {
  position: absolute; top: calc(env(safe-area-inset-top) + 8px); left: 8px;
  width: 44px; height: 44px; padding: 0; border-radius: var(--r-round);
  background: var(--art-glass); backdrop-filter: blur(8px);
  display: grid; place-content: center; font: 500 16px/1 var(--sans);
  color: var(--art-fg);
}
.meta-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 14px var(--pad) 0; }
.detail-themes { display: flex; flex-wrap: wrap; gap: 7px; }
.home-search-label {
  display: block; padding: 12px var(--pad) 0;
  font: 600 10px/1.35 var(--mono); letter-spacing: .08em; color: var(--fg-3);
  text-transform: uppercase; white-space: normal;
}
.home-search { display: flex; gap: 8px; padding: 12px var(--pad) 0; }
.home-search-label + .home-search { padding-top: 6px; }
.home-search .field { min-width: 0; }
.home-search .btn { width: auto; flex: none; }
@media (max-width: 340px) {
  .home-search .btn { padding-left: 12px; padding-right: 12px; }
}
.home-results { margin-top: 8px; border-block: 1px solid var(--line); background: var(--surface); }
.home-results .seen-row { width: 100%; text-align: left; }
.rail-sentinel { height: 2px; }
/* The rails under a title: a caption with See all when there is more, or why
   it is empty and the way back. */
.similar-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.see-all-link { flex: none; padding: 4px 0; font: 600 9.5px/1 var(--mono); letter-spacing: .08em; color: var(--fg-2); }
.similar-empty { margin-top: 12px; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.similar-empty .btn { padding: 11px 16px; }
.rail-more-fallback { margin-top: 12px; }
.chip {
  padding: 5px 9px; border-radius: var(--r-pill); background: var(--surface-2);
  border: 1px solid var(--line);
  font: 500 10px/1 var(--mono); letter-spacing: .06em; color: var(--fg-3);
}
.chip.accent { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

.avail-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 14px 16px; margin-bottom: 8px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
}
.avail-row.included { background: color-mix(in srgb, var(--accent) 10%, transparent); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.avail-row.gone { opacity: .42; }
.avail-row .prov {
  width: 38px; height: 38px; flex: none; border-radius: var(--r-tag);
  display: grid; place-content: center; background: var(--surface-3);
  font: 600 9px/1 var(--mono); letter-spacing: .04em; color: var(--fg-2);
}
.avail-row .name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; font: 400 14px/1.2 var(--sans); }
.avail-row .note { font: 500 9.5px/1.3 var(--mono); letter-spacing: .06em; color: var(--fg-3); }
.avail-row .cta { font: 600 13px/1 var(--sans); color: var(--accent-text); flex: none; }
.avail-row.gone .cta { color: var(--fg-4); }
.availability-hidden { margin-top: 8px; }
.availability-hidden summary {
  min-height: 44px; padding: 0 14px; cursor: pointer;
  display: flex; align-items: center; gap: 9px;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface);
  font: 600 9.5px/1 var(--mono); letter-spacing: .08em; color: var(--fg-3);
}
.availability-hidden summary::before { content: '+'; color: var(--accent-text); font-size: 15px; }
.availability-hidden[open] summary::before { content: '−'; }
.availability-hidden-rows { margin-top: 8px; }

/* --------------------------------------------------------------- group */

.people-row { display: flex; gap: 9px; overflow-x: auto; padding: 4px var(--pad) 0; }
.person {
  flex: none; width: 64px; display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 0; border: 0; border-radius: 0; background: transparent;
  font: 400 11.5px/1.2 var(--sans); color: var(--fg-3);
}
.person.on { color: var(--fg); background: transparent; }
/* Selection surrounds the 54px portrait in the comp. Boxing the whole name and
   portrait produced a different rectangular card that the design never has. */
.person .avatar { width: 54px; height: 54px; font-size: 17px; border: 2px solid transparent; }
.person.on .avatar { border-color: var(--accent-text); }
.overlap-note {
  margin: 16px var(--pad) 0; padding: 14px 16px; border-radius: var(--r-card);
  background: var(--surface); border: 1px solid var(--line);
  display: flex; align-items: center; gap: 12px;
  font: 400 12px/1.45 var(--sans); color: var(--fg-2); text-wrap: pretty;
}
.overlap-note .stat { display: block; margin: 0; font: 500 11px/1.4 var(--mono); letter-spacing: .05em; color: var(--accent-text); }

/* ---------------------------------------------------------------- chat */

.chat-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.chat-scroll { flex: 1; overflow-y: auto; padding: 20px 18px 12px; display: flex; flex-direction: column; gap: 12px; }
.msg { max-width: 78%; display: flex; flex-direction: column; gap: 5px; }
.msg.mine { align-self: flex-end; align-items: flex-end; }
.bubble {
  padding: 12px 15px; border-radius: var(--r-bubble);
  background: var(--surface-2); font: 400 14.5px/1.45 var(--sans); text-wrap: pretty;
}
.msg.mine .bubble { border-radius: var(--r-bubble-mine); background: var(--accent); color: var(--accent-ink); }
.stamp { font: 500 9px/1 var(--mono); letter-spacing: .1em; color: var(--fg-4); }
.share-card {
  display: flex; gap: 12px; padding: 11px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); width: 268px;
  text-align: left;
}
.share-card .poster { width: 60px; height: 86px; border-radius: var(--r-tile); }
.share-card .info { flex: 1; display: flex; flex-direction: column; gap: 4px; justify-content: center; }
.share-card .info .t { font: 400 14px/1.2 var(--sans); }
.share-card .info .m { font: 500 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--fg-3); }
.share-card .info .s { font: 600 10px/1 var(--mono); color: var(--accent-text); }
.composer {
  display: flex; gap: 8px; align-items: center;
  padding: 12px 18px calc(env(safe-area-inset-bottom) + 30px); border-top: 1px solid var(--line); background: var(--bg);
}
.composer .field { border-radius: var(--r-pill); padding: 12px 16px; font-size: 15px; }
.composer .send { width: 42px; height: 42px; border-radius: var(--r-round); background: var(--accent); color: var(--accent-ink); display: grid; place-content: center; font-size: 16px; flex: none; }

/* --------------------------------------------------------------- misc */

.empty { padding: 60px var(--pad); text-align: center; color: var(--fg-3); font: 400 14px/1.6 var(--sans); text-wrap: pretty; }
.list-head { padding: 26px var(--pad) 10px; }
.kv { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--line); font: 400 13.5px/1.3 var(--sans); }
.kv .k { color: var(--fg-3); }
.kv .v { font-family: var(--mono); font-size: 12px; text-align: right; }

/* ------------------------------------------------------- real key art */

/* Enrichment is optional and partial by nature, so `has-art` swaps in real
   imagery and the generated placeholder underneath stays as the fallback.
   An <img> that 404s removes itself and the class, so a dead CDN URL degrades
   to the placeholder instead of a broken-image icon. */
.poster img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.poster.has-art::before { display: none; }
.poster.has-art .poster-name { display: none; }
.poster.has-art::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, color-mix(in srgb, var(--art-ink) 85%, transparent) 0%, color-mix(in srgb, var(--art-ink) 15%, transparent) 34%, transparent 58%);
}

.rate-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 1px; }
.rate {
  padding: 3px 6px; border-radius: var(--r-tag);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 600 9px/1 var(--mono); letter-spacing: .04em; color: var(--fg-3);
  white-space: nowrap;
}
.rate.imdb { color: var(--rate-imdb); border-color: color-mix(in srgb, var(--rate-imdb) 30%, transparent); }
.rate.rt.fresh { color: var(--lo); border-color: color-mix(in srgb, var(--lo) 30%, transparent); }
.rate.rt.rotten { color: var(--fg-3); }

/* One source per row, its name in full on the left and its number on its own
   scale on the right. Four boxes side by side pushed the last one off a phone
   (batch AV5); a row only ever grows downwards. */
.critics { display: grid; gap: 8px; margin-top: 12px; }
@media (min-width: 720px) { .critics.outside { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.critic {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "l v" "n v";
  align-items: center; column-gap: 12px; row-gap: 5px;
  padding: 11px 14px; border-radius: var(--r-card); text-decoration: none;
  background: var(--surface-2); border: 1px solid var(--line);
}
.critic .v { grid-area: v; font: 500 20px/1 var(--sans); letter-spacing: -.02em; color: var(--fg); white-space: nowrap; }
.critic .l { grid-area: l; font: 600 13px/1.25 var(--sans); color: var(--fg-2); overflow-wrap: anywhere; }
.critic .n { grid-area: n; font: 500 9px/1.2 var(--mono); letter-spacing: .06em; color: var(--fg-4); }
.offer-summary { margin-top: 2px; }

.play-trailer {
  display: flex; align-items: center; gap: 7px;
  min-height: 44px; padding: 9px 14px 9px 12px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line-strong);
  font: 500 13px/1 var(--sans); color: var(--fg);
}
.play-trailer span { color: var(--accent-text); font-size: 11px; }
.play-trailer:focus-visible, .detail-hero .back-btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
.trailer-fallback { width: fit-content; margin: 14px var(--pad) 0; }

.detail-hero img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.detail-hero.has-art::before { display: none; }
.detail-hero.has-art::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, var(--bg) 2%, color-mix(in srgb, var(--bg) 55%, transparent) 34%, transparent 70%);
}

.trailer-frame {
  margin: 6px var(--pad) 0; border-radius: var(--r-card); overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--art-black); border: 1px solid var(--line);
}
.trailer-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

.swipe-card img.swipe-art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.swipe-card.has-art::before { display: none; }
.swipe-card.has-art::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, color-mix(in srgb, var(--art-ink) 95%, transparent) 8%, color-mix(in srgb, var(--art-ink) 55%, transparent) 40%, transparent 72%);
}
.swipe-card .swipe-body { position: relative; z-index: 2; }
.session-title-open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  color: inherit;
  background: transparent;
  text-align: left;
  cursor: pointer;
}
.swipe-card .rate-row { margin-top: 9px; }
.card .s-alt { color: var(--fg-4); }
.swipe-card .verdict { z-index: 3; }
.detail-hero .back-btn, .swipe-card .verdict { z-index: 3; }
/* The scrim is a ::after and paints after same-z siblings, so hero text has to
   sit a layer above it or it reads as washed-out on a bright backdrop. */
.detail-hero > div { z-index: 2; }
.swipe-card .swipe-body { z-index: 2; }

/* Wikipedia returns a proper 2:3 poster for films but sometimes a wide title
   card or logo for series. `cover` crops those into unreadable fragments, so
   card art is `contain` — a true poster fills the slot exactly either way, and
   a wide logo letterboxes onto the generated gradient instead of being sliced.
   The detail hero keeps `cover`: it is a 16:9 box and a cropped poster reads
   fine as a backdrop. */
.poster img, .swipe-card img.swipe-art { object-fit: contain; }
.poster.has-art { background-color: var(--art-black); }

/* ------------------------------------------------------ setup: genres */

.genre-wrap { display: flex; flex-wrap: wrap; gap: 8px; }
.genre {
  padding: 11px 14px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 14px/1 var(--sans); color: var(--fg-2);
  transition: transform .12s, background .12s;
}
.genre:active { transform: scale(.95); }
.genre.on { background: var(--accent); border-color: transparent; color: var(--accent-ink); font-weight: 500; }
/* A block is not a weaker like — it is the opposite answer, so it must not
   look like a selected preference. */
.genre.on.block { background: transparent; border-color: var(--danger); color: var(--danger); }
.genre.on.block::before { content: '✕ '; }
.theme-score {
  position: relative; display: grid; grid-template-columns: 1fr auto; gap: 7px 10px;
  align-items: center; min-width: 132px; padding: 9px 11px;
}
.theme-score b { font: 700 12px/1 var(--mono); color: inherit; }
.theme-score .theme-source {
  grid-column: 1 / -1; font: 600 8px/1 var(--mono); letter-spacing: .09em;
  color: var(--fg-4);
}
.theme-score.learned { border-style: dashed; }
.theme-score.learned.on { background: var(--surface-2); border-color: var(--accent-text); color: var(--fg-2); }
.theme-score.explicit .theme-source { color: currentColor; opacity: .78; }
.theme-score input { touch-action: pan-y; grid-column: 1 / -1; width: 100%; height: 14px; accent-color: var(--accent-text); }
.theme-score.zero { border-color: var(--danger); color: var(--danger); background: transparent; }

/* --------------------------------------------------------- swipe: verdicts */

.swipe-card .verdict.skip {
  top: auto; bottom: 24px; left: 50%; transform: translateX(-50%) rotate(0);
  color: var(--danger); text-transform: uppercase;
}
.swipe-card .verdict { white-space: nowrap; }

/* Overlay shown before the first ever swipe. It teaches a mapping that is the
   reverse of the convention most people arrive with, so it is deliberately
   loud and it does not disappear on a timer — only on a real gesture or tap. */

.swipe-actions .btn { flex: 1; flex-direction: column; gap: 5px; padding: 12px 8px; font-size: 11px; letter-spacing: .02em; }
.swipe-actions .btn .g { font-size: 17px; line-height: 1; }
.swipe-actions .act-ok  { background: var(--accent); color: var(--accent-ink); }
.swipe-actions .act-no  { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 32%, transparent); }
.swipe-actions .act-mid { color: var(--fg-3); }

/* The swipe card is a drag surface, so nothing inside it may start a
   BROWSER-native drag. Pressing on the poster and moving fired one pointermove
   and then silence: Chromium had begun an image drag-and-drop and stopped
   dispatching pointer events, so the swipe died after a single frame and the
   card sat frozen mid-gesture. Images need draggable="false" AND the CSS below;
   either alone is not enough across browsers. */
.swipe-card, .swipe-card * { -webkit-user-drag: none; user-select: none; -webkit-user-select: none; }
.swipe-card img.swipe-art { pointer-events: none; }
/* The callout too: on iOS a long press over an image raises the native
   Save Image / Copy bubble, and a long press is precisely what the rail card
   now asks for. That bubble is a separate OS gesture recogniser, so nothing in
   the script can call it off. */
.poster img { -webkit-user-drag: none; user-select: none; -webkit-touch-callout: none; }
/* The whole card, not only its image: a thumb lands on the badge, the price,
   the title under the poster as often as on the picture, and each of those
   raised the phone's own copy bubble instead of the hold. */
.card, .card-hero, .card *, .card-hero * {
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; -webkit-user-drag: none;
}

/* ----------------------------------------------- included vs rent & buy */

.pay-row { display: flex; gap: 8px; padding: 12px var(--pad) 0; }
.pay {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 12px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 12.5px/1 var(--sans); color: var(--fg-3);
}
.pay.on { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: color-mix(in srgb, var(--accent) 42%, transparent); color: var(--fg); }
.pay .n { font: 600 10px/1 var(--mono); color: var(--fg-4); }
.pay.on .n { color: var(--accent-text); }

.price-pill {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  padding: 4px 7px; border-radius: var(--r-tag);
  background: var(--art-glass-strong); backdrop-filter: blur(6px);
  border: 1px solid var(--glass-border);
  font: 600 9px/1 var(--mono); color: var(--art-fg);
}

/* ------------------------------------------------------------- reactions */

/* The dots are gone: a rail card is rated by holding it and sliding it, the
   way the deck works. This mark is the cue that the card can be moved — quiet
   at rest, stronger under a pointer — and carries no words, so it needs no
   translation; the card's own title says what the gesture does. */
.hold-hint {
  position: absolute; bottom: 6px; left: 6px; z-index: 3;
  width: 26px; height: 22px; border-radius: var(--r-tag);
  background: var(--art-glass); backdrop-filter: blur(6px);
  color: var(--art-fg); font: 700 13px/20px var(--sans); text-align: center;
  border: 1px solid var(--art-border); opacity: .75;
  box-shadow: 0 2px 8px -2px var(--art-shadow);
  pointer-events: none;
}
.card:hover .hold-hint, .card-hero:hover .hold-hint { opacity: 1; background: var(--art-glass-strong); }
.card.armed .hold-hint, .card-hero.armed .hold-hint { opacity: 0; }

/* One line, once, above the first rail. */
.hold-tip {
  display: flex; align-items: center; gap: 10px; margin-top: 18px;
  padding: 10px 12px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
}
.hold-tip-mark { flex: none; font: 700 16px/1 var(--sans); color: var(--accent-text); }
.hold-tip-text { flex: 1; min-width: 0; font: 400 13px/1.45 var(--sans); color: var(--fg-2); }
.hold-tip .linkish { flex: none; }

/* The rating under the trailer: the sheet's slider, made to sit in a page. */
.rate-inline { margin-top: 14px; }
.rate-inline .score-slider { margin-top: 8px; padding: 10px 8px 6px; }
.rate-inline .score-read { justify-content: flex-start; padding-left: 4px; }
.rate-inline .score-read b { font-size: 34px; }
.rate-inline .score-slider input[type=range] { margin: 8px 0 4px; }
.rate-inline .score-slider.pending .score-read b { color: var(--fg-4); }
.rate-inline .score-slider.pending input[type=range] { accent-color: var(--fg-4); }

.option-row.react { align-items: flex-start; }
.option-row.react .tiny { color: var(--fg-4); }
.option-row.react.ok { color: var(--ok); }
.option-row.react.no { color: var(--danger); }
.option-row.react.ok .tiny, .option-row.react.no .tiny { color: var(--fg-4); }

.swipe-strong { display: flex; gap: 8px; padding: 8px var(--pad) 0; }
.swipe-strong .strong {
  flex: 1; padding: 9px 6px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 500 11px/1.2 var(--sans); color: var(--fg-3);
}
.swipe-strong .strong.ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.swipe-strong .strong.no { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }

/* ------------------------------------------------------------ taste editor */

.rated {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 11px var(--pad); border-top: 1px solid var(--line);
}
.rated:active { background: var(--surface-2); }
.rated .poster { width: 44px; height: 64px; border-radius: var(--r-lg); flex: none; }
.rated .info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.rated .info .t { font: 400 14.5px/1.2 var(--sans); }
.rated .info .m { font: 500 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--fg-4); }
.rated .info .v { font: 600 10px/1 var(--mono); letter-spacing: .06em; color: var(--fg-3); }
.rated .info .v.ok { color: var(--ok); }
.rated .info .v.no { color: var(--danger); }
.genre .gn { margin-left: 7px; font: 600 9px/1 var(--mono); color: var(--fg-4); }
.genre.on .gn { color: var(--accent-ink); background: color-mix(in srgb, var(--accent-ink) 12%, transparent); }
.genre.on.block .gn { color: var(--danger); opacity: .7; }
/* A theme slider is "on" when its score is above five, but it is not the filled
   accent chip the rule above was written for: the count kept that chip's white
   ink on the slider's light ground, 1.1:1, on every narrower theme in setup. */
.genre.theme-score .gn, .genre.theme-score.on .gn { color: var(--fg-3); background: none; }
/* One the person set above five IS the filled accent chip, and keeps its ink. */
.genre.theme-score.explicit.on .gn { color: var(--accent-ink); }

/* --------------------------------------------------------- deep rail view */

.see-all {
  flex: none; align-self: stretch; width: 92px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border-radius: var(--r-pill); background: var(--surface-2); border: 1px dashed var(--line-strong);
  color: var(--fg-3);
}
.see-all.hero { height: 262px; }
.see-all .n { font: 500 19px/1 var(--sans); color: var(--fg); letter-spacing: -.02em; }
.see-all .l { font: 600 9px/1.3 var(--mono); letter-spacing: .08em; text-align: center; }

.grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 16px 11px; padding: 18px var(--pad) 0;
}
.grid .card { width: auto; }
.grid .card .poster { width: 100%; height: auto; aspect-ratio: 2 / 3; }
.rate.agg { border-color: var(--art-line); color: var(--fg); font-weight: 700; }
.rate.agg.high { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.rate.agg.mid  { color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.rate.agg.low  { color: var(--fg-3); }
/* A Huddelo rating resting on few ratings: the same number, dashed. */
.rate.agg.few { border-style: dashed; }
/* Huddelo's own members, first in a title's list of ratings. */
.critic.ours .l { color: var(--accent-text); }

/* Stale availability is stated, not smoothed over: an out-of-date "included"
   tells the user something is free that may now cost money. */
.stale-note {
  margin: 12px var(--pad) 0; padding: 11px 13px; border-radius: var(--r-card);
  background: color-mix(in srgb, var(--danger) 10%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  font: 400 12px/1.5 var(--sans); color: var(--fg-2); text-wrap: pretty;
}
.stale-note b { color: var(--danger); font-weight: 600; }

/* -------------------------------------------- browsing every service */

.needs-pill {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  padding: 4px 7px; border-radius: var(--r-tag);
  background: var(--art-glass-strong);  backdrop-filter: blur(6px);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  font: 600 8.5px/1 var(--mono); letter-spacing: .05em; color: var(--accent-text);
}
.worth { margin: 12px var(--pad) 0; padding: 13px 14px; border-radius: var(--r-card);
  background: var(--surface); border: 1px solid var(--line); }
.worth-row { display: flex; gap: 8px; overflow-x: auto; margin-top: 10px; }
.worth-chip {
  flex: none; display: flex; align-items: baseline; gap: 7px;
  padding: 8px 11px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
}
.worth-chip b { font: 400 12.5px/1 var(--sans); color: var(--fg); }
.worth-chip i { font: 600 10px/1 var(--mono); color: var(--accent-text); font-style: normal; }

/* Press-and-slide on a rail card. The lift is the only signal that the gesture
   has armed, so it has to be unmistakable. */
.card-hero, .card { transition: transform .18s ease-out; }
.card-hero.armed, .card.armed {
  transition: none;
  filter: drop-shadow(0 16px 26px var(--art-shadow));
}
.card-hero.armed .poster, .card.armed .poster { border-color: var(--line-strong); }
/* What the card actually does, which is not what this used to say. It read
   "▲ LOVED IT · ▼ NOT FOR ME" from the days when the gesture was vertical, and
   went on saying it after the gesture became a sideways grade — so the one
   instruction anybody gets named two directions, one of which did something
   else and the other of which did nothing at all. */
.card-hero.armed::after, .card.armed::after {
  content: '◀▼ NO   ▲ SAVE   YES ▶';
  position: absolute; left: 50%; top: -22px; transform: translateX(-50%);
  white-space: nowrap; padding: 4px 9px; border-radius: var(--r-lg);
  background: var(--art-glass-strong); border: 1px solid var(--art-border);
  font: 600 8.5px/1 var(--mono); letter-spacing: .08em; color: var(--art-fg-3);
}
.card-hero.armed, .card.armed { position: relative; }
.card-hero.up .poster,   .card.up .poster   { border-color: var(--ok); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ok) 50%, transparent); }
.card-hero.down .poster, .card.down .poster { border-color: var(--danger); box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 50%, transparent); }
.card-hero.armed::after { top: -24px; }
.free-pill {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  padding: 4px 7px; border-radius: var(--r-tag);
  background: color-mix(in srgb, var(--art-ok) 16%, transparent); backdrop-filter: blur(6px);
  border: 1px solid color-mix(in srgb, var(--art-ok) 45%, transparent);
  font: 600 8.5px/1 var(--mono); letter-spacing: .06em; color: var(--art-ok);
}

/* ------------------------------------------------------------ filter bar */

.filter-bar {
  display: flex; gap: 6px; align-items: center;
  overflow-x: auto; padding: 12px var(--pad) 2px;
  scrollbar-width: none;
  /* At the largest text size the row runs past the edge; the fade says so
     rather than leaving a chip sliced down the middle. */
  -webkit-mask-image: linear-gradient(to right, black calc(100% - 26px), transparent);
  mask-image: linear-gradient(to right, black calc(100% - 26px), transparent);
  scroll-padding-right: var(--pad);
}
.filter-bar::-webkit-scrollbar { display: none; }
.filter-bar > :last-child { margin-right: 26px; }
.fchip {
  flex: none; display: flex; align-items: baseline; gap: 6px;
  padding: 7px 11px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 12px/1 var(--sans); color: var(--fg-3); white-space: nowrap;
}
.fchip i { font: 600 9px/1 var(--mono); color: var(--fg-4); font-style: normal; }
.fchip.on { background: color-mix(in srgb, var(--accent) 13%, transparent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--fg); }
.fchip.on i { color: var(--accent-text); }
.fsep { flex: none; width: 1px; height: 16px; background: var(--line); margin: 0 2px; }

/* Four controls, on both screens. The closed state stays transparent and
   compact; the choices exist only while somebody is making that decision. */
.filter-controls {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
  padding: 12px var(--pad) 2px;
}
.filter-controls[data-filter-surface="detail"] { margin-top: 22px; }
/* A person's own bar holds seven: the three whose label and answer are long
   (Order, Minimum score, Released within) take a whole row on a phone, so the
   answer is read whole in every language. */
@media (max-width: 767px) {
  .filter-controls:is([data-filter-surface="home"], [data-filter-surface="detail"])
    > .filter-picker:is([data-filter-control="discovery"], [data-filter-control="minscore"], [data-filter-control="lookback"]) {
    grid-column: 1 / -1;
  }
}
.filter-picker { position: relative; min-width: 0; }
.filter-picker[open] { z-index: 40; }
.filter-picker > summary { list-style: none; cursor: pointer; }
.filter-picker > summary::-webkit-details-marker { display: none; }
.filter-picker > .fchip {
  box-sizing: border-box; width: 100%; justify-content: space-between; align-items: center;
  padding: 9px 12px; border-radius: var(--r-pill); background: transparent; color: var(--fg-2);
}
.filter-picker[open] > .fchip {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--fg);
}
.filter-current { display: flex; min-width: 0; align-items: center; color: var(--fg-4); }
.filter-current > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-current i {
  min-width: 19px; padding: 3px 5px; border-radius: var(--r-pill); text-align: center;
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  /* The count sits on the chip's own accent wash, not on the page, and one
     muted step down from this measures 4.48:1 there. */
  color: var(--fg-3);
}
.filter-menu {
  position: absolute; inset-block-start: calc(100% + 6px); inset-inline-start: 0;
  min-width: max(100%, 176px); padding: 5px; border: 1px solid var(--line-strong);
  border-radius: var(--r-card); background: var(--surface); box-shadow: 0 18px 36px -18px var(--shadow-strong);
}
.filter-picker:nth-child(even) .filter-menu { inset-inline-start: auto; inset-inline-end: 0; }
.filter-option {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 10px; border-radius: var(--r-tile); color: var(--fg-2); text-align: start;
  font: 400 12.5px/1.15 var(--sans); white-space: nowrap;
}
.filter-option:hover { background: var(--surface-2); color: var(--fg); }
.filter-option i { visibility: hidden; color: var(--accent-text); font-style: normal; }
.filter-option.on { color: var(--fg); }
.filter-option.on i { visibility: visible; }
/* A score with no critic half behind it is a different number and says so. */
.score-pill.unrated { background: var(--surface-2); color: var(--fg-3); border: 1px solid var(--line-strong); }
.score-pill.unrated::after { content: '*'; }

/* ------------------------------------------------- tell it what you've seen */

.seen-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px var(--pad); border-top: 1px solid var(--line);
}
.seen-row .poster { width: 46px; height: 68px; border-radius: var(--r-lg); flex: none; }
.seen-row .info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.seen-row .info .t { font: 400 14.5px/1.25 var(--sans); }
.seen-row .info .m { font: 500 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--fg-4); }
.seen-row .info .v { font: 600 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--accent-text); }
.seen-row .acts { display: flex; gap: 6px; flex: none; }
.qa {
  width: 36px; height: 36px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 500 15px/1 var(--sans); color: var(--fg-3);
}
.qa.ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 32%, transparent); }
.qa.no { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 32%, transparent); }
.qa:active { transform: scale(.92); }
/* One control instead of three, because the answer is a number now and not a
   choice of three faces. Wide enough for the word RATE, and it shows the score
   itself once there is one, so a list of a hundred films can be read down. */
.qa.score {
  width: auto; min-width: 46px; padding: 0 11px;
  font: 600 12px/1 var(--mono); letter-spacing: .06em;
}
.qa.score.set { color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 40%, transparent); font-size: 15px; }

/* ------------------------------------------------------------- trophy case */

.trophy-head { display: flex; align-items: flex-end; justify-content: space-between; margin-top: 14px; }
.points { text-align: right; }
.points b { display: block; font: 500 28px/1 var(--sans); color: var(--accent-text); letter-spacing: -.02em; }
.points span { font: 600 9px/1 var(--mono); letter-spacing: .12em; color: var(--fg-4); }

.trophy-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 10px; padding: 14px var(--pad) 0;
}
.trophy {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 5px;
  padding: 14px 9px 11px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--line);
}
/* The mark is drawn, not typed: it takes the tier's colour and the same weight
   on every device. See trophies.js for why the emoji had to go. */
.trophy .bi { width: 30px; height: 30px; color: var(--fg-3); }
.trophy .bi svg { width: 100%; height: 100%; display: block; }
.trophy .bl { font: 500 11.5px/1.25 var(--sans); }
.trophy .bb { font: 400 9.5px/1.3 var(--sans); color: var(--fg-4); text-wrap: pretty; }
.trophy .bt { margin-top: 2px; font: 700 8px/1 var(--mono); letter-spacing: .12em; }
.trophy .bn { font: 600 9px/1 var(--mono); color: var(--fg-4); }
.trophy .bp { width: 100%; height: 3px; border-radius: var(--r-bar); background: var(--surface-3); overflow: hidden; margin-top: 4px; }
.trophy .bp i { display: block; height: 100%; background: var(--fg-4); }

/* Locked badges are visibly locked — a wall where everything glows says nothing. */
.trophy.locked { opacity: .62; }
.trophy.locked .bi { color: var(--fg-4); opacity: .55; }
.trophy.earned { background: var(--surface); }
.trophy.earned.bronze   { border-color: color-mix(in srgb, var(--tier-bronze) 55%, transparent);  box-shadow: 0 0 0 1px color-mix(in srgb, var(--tier-bronze) 12%, transparent) inset; }
.trophy.earned.bronze   .bt, .trophy.earned.bronze   .bi { color: var(--tier-bronze); }
.trophy.earned.silver   { border-color: color-mix(in srgb, var(--tier-silver) 55%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--tier-silver) 12%, transparent) inset; }
.trophy.earned.silver   .bt, .trophy.earned.silver   .bi { color: var(--tier-silver); }
.trophy.earned.gold     { border-color: color-mix(in srgb, var(--accent) 60%, transparent);   box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent) inset; }
.trophy.earned.gold     .bt, .trophy.earned.gold     .bi { color: var(--accent-text); }
.trophy.earned.platinum { border-color: color-mix(in srgb, var(--tier-platinum) 65%, transparent); box-shadow: 0 0 18px -6px color-mix(in srgb, var(--tier-platinum) 50%, transparent), 0 0 0 1px color-mix(in srgb, var(--tier-platinum) 16%, transparent) inset; }
.trophy.earned.platinum .bt, .trophy.earned.platinum .bi { color: var(--tier-platinum); }

/* --------------------------------------------------- directors and actors */

.person-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px var(--pad); border-top: 1px solid var(--line);
}
.pface {
  position: relative; width: 46px; height: 46px; border-radius: var(--r-round); flex: none;
  overflow: hidden; display: grid; place-content: center;
  background: linear-gradient(160deg, hsl(var(--hue) 28% 26%), hsl(var(--hue) 20% 14%));
  border: 1px solid var(--line);
  font: 600 15px/1 var(--sans); color: var(--art-fg-2);
}
.pface img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.person-row .info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.person-row .info .t { font: 400 14.5px/1.25 var(--sans); }
.person-row .info .m { font: 500 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--fg-4); }
.person-row .acts { display: flex; gap: 6px; flex: none; }

/* One cursor per person, per the operator's comp: the name and the current
   verdict on one line, a track under it, and the two ends named. Three buttons
   could say which way somebody felt and never how strongly, and the five
   weights the server has always understood had no way to be reached. */
.person-row { align-items: flex-start; padding: 15px var(--pad); }
.person-row .info .head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.person-row .info .t { font: 400 15px/1.2 var(--mono); letter-spacing: -.01em; }
.person-row .info .v {
  flex: none; font: 600 9.5px/1 var(--mono); letter-spacing: .06em; color: var(--fg-4);
}
.person-row .info .v.ok { color: var(--accent-text); }
.person-row .info .v.no { color: var(--danger); }
.person-row .ends {
  display: flex; justify-content: space-between;
  font: 500 9px/1 var(--mono); letter-spacing: .08em; color: var(--fg-4);
}

/* The track is drawn on the input itself so the filled part follows the value
   without a second element to keep in step. --v is the position, 0 to 1. */
.pcursor {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 26px; margin: 4px 0 2px; background: none; cursor: pointer;
}
.pcursor::-webkit-slider-runnable-track {
  height: 5px; border-radius: var(--r-bar);
  background: linear-gradient(to right, var(--accent) 0 calc(var(--v) * 100%), var(--surface-3) calc(var(--v) * 100%) 100%);
}
.pcursor::-moz-range-track {
  height: 5px; border-radius: var(--r-bar);
  background: linear-gradient(to right, var(--accent) 0 calc(var(--v) * 100%), var(--surface-3) calc(var(--v) * 100%) 100%);
}
.pcursor::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; margin-top: -7.5px; border-radius: var(--r-round);
  background: var(--accent); border: 3px solid var(--surface);
  box-shadow: 2px 2px 0 var(--line-strong);
}
.pcursor::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: var(--r-round); box-sizing: border-box;
  background: var(--accent); border: 3px solid var(--surface);
  box-shadow: 2px 2px 0 var(--line-strong);
}
.pcursor:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--r-card); }


/* ------------------------------------------------- verifying a trophy */

.trophy { position: relative; }
.trophy .seal {
  position: absolute; top: 7px; right: 7px; width: 15px; height: 15px;
  color: var(--accent-text);
}
.trophy .seal svg { width: 100%; height: 100%; display: block; }
.trophy.verified { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.trophy .verify {
  margin-top: 6px; padding: 5px 10px; border-radius: var(--r-card);
  background: var(--surface-3); border: 1px solid var(--line);
  font: 600 8.5px/1 var(--mono); letter-spacing: .1em; color: var(--fg-3);
  text-transform: uppercase;
}
.trophy .verify:active { opacity: .6; }

.qbox { padding: 16px var(--pad); border-top: 1px solid var(--line); }
.qbox .qn {
  font: 600 9px/1 var(--mono); letter-spacing: .14em; color: var(--fg-4); margin-bottom: 7px;
}
.qbox .qp { font: 400 15px/1.35 var(--sans); text-wrap: pretty; }
.qopts { display: flex; flex-direction: column; gap: 7px; margin-top: 12px; }
.qopt {
  text-align: left; padding: 12px 14px; border-radius: var(--r-lg);
  background: var(--surface-2); border: 1px solid var(--line);
  font: 400 14px/1.3 var(--sans); color: var(--fg);
}
/* Plot options are paragraphs, not names — they need room to be read. */
.qopts.long .qopt { font-size: 12.5px; line-height: 1.45; color: var(--fg-2); }
.qopt.on { border-color: var(--accent-text); background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--fg); }
.qopt:active { opacity: .7; }

.verdict { text-align: center; padding: 26px 0 8px; }
.verdict .vi { width: 46px; height: 46px; margin: 0 auto 14px; }
.verdict .vi svg { width: 100%; height: 100%; display: block; }
.verdict.won .vi { color: var(--accent-text); }
.verdict.lost .vi { color: var(--fg-4); }

.qreview { padding: 12px var(--pad); border-top: 1px solid var(--line); }
.qreview .qp { font: 400 13px/1.35 var(--sans); color: var(--fg-2); }
.qreview .qa2 { margin-top: 6px; font: 500 12.5px/1.3 var(--mono); }
.qreview.ok .qa2 { color: var(--hi); }
.qreview.no .qa2 { color: var(--lo); }
.qreview .qc { margin-top: 4px; font: 400 12.5px/1.35 var(--sans); color: var(--fg-3); }

/* ------------------------------------------------------ adding a friend */

.addrow { display: flex; align-items: center; gap: 8px; }
.addrow .at {
  font: 500 17px/1 var(--sans); color: var(--fg-4); width: 12px; text-align: center; flex: none;
}
.addrow .field { flex: 1; min-width: 0; }
.addrow .btn { flex: none; padding: 15px 18px; }

/* ------------------------------------------- how hard you swiped */

.calib { padding: 0 var(--pad); }
.verdict .gauge {
  display: block; margin-top: 7px; height: 3px; border-radius: var(--r-bar);
  background: var(--art-track); overflow: hidden;
}
.verdict .gauge u {
  display: block; height: 100%; background: currentColor;
  transform: scaleX(0); transform-origin: left; transition: transform .06s linear;
}
/* Past the strong threshold the label stops being a hint and becomes a verdict. */
.verdict.strong { transform: scale(1.06); }
.verdict.strong .vtxt { letter-spacing: .04em; }

/* ------------------------------------------------- trophies, up front */

.me-head {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
}
.me-head .ava-wrap { position: relative; flex: none; }
/* The level sits on the avatar the way a rank does — the first thing read,
   and the reason the whole wall is worth filling. */
.me-head .lvl {
  position: absolute; right: -4px; bottom: -4px;
  min-width: 21px; height: 21px; padding: 0 4px; border-radius: var(--r-card);
  display: grid; place-content: center;
  background: var(--accent); color: var(--accent-ink);
  font: 700 11px/1 var(--mono); border: 2px solid var(--bg);
}
.me-head .who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.me-head .who .h2 { display: block; }
.me-head .tro { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.me-head .counts { display: flex; gap: 5px; }
.me-head .counts .t {
  min-width: 22px; padding: 3px 5px; border-radius: var(--r-card); text-align: center;
  font: 700 10.5px/1 var(--mono); border: 1px solid;
}
.me-head .counts .plat { color: var(--tier-platinum); border-color: color-mix(in srgb, var(--tier-platinum) 45%, transparent); }
.me-head .counts .gold { color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.me-head .counts .silv { color: var(--tier-silver); border-color: color-mix(in srgb, var(--tier-silver) 45%, transparent); }
.me-head .counts .brnz { color: var(--tier-bronze); border-color: color-mix(in srgb, var(--tier-bronze) 45%, transparent); }

/* The trophy summary is a card with its own label, not a detail beside a name.
   As an ornament next to the handle it read as decoration and got skipped. */
.trophy-bar {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  margin-top: 18px; padding: 14px 14px 14px 12px; border-radius: var(--r-bar);
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 13%, transparent), color-mix(in srgb, var(--accent) 6%, transparent));
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
}
.trophy-bar:active { opacity: .75; }
.lvl-ring {
  flex: none; width: 54px; height: 54px; border-radius: var(--r-round);
  display: grid; place-content: center; gap: 1px;
  background: var(--bg); border: 2px solid var(--accent);
}
.lvl-ring b { font: 600 21px/1 var(--sans); color: var(--accent-text); text-align: center; }
.lvl-ring span { font: 600 7px/1 var(--mono); letter-spacing: .14em; color: var(--fg-4); text-align: center; }
.tb-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.tb-counts { display: flex; gap: 9px; }
.tb-counts .tc {
  display: inline-flex; align-items: center; gap: 3px;
  font: 700 13px/1 var(--sans);
}
.tb-counts .tc i { width: 15px; height: 15px; display: block; }
.tb-counts .tc i svg { width: 100%; height: 100%; display: block; }
.tb-counts .plat { color: var(--tier-platinum); }
.tb-counts .gold { color: var(--accent-text); }
.tb-counts .silv { color: var(--tier-silver); }
.tb-counts .brnz { color: var(--tier-bronze); }
.tb-counts .zero { opacity: .3; }
.tb-go { flex: none; font-size: 22px; color: var(--fg-4); }

/* Says why a lower-scoring card is sitting higher up. On the hero card the
   top-right corner already carries the match number, so it sits under the
   provider badge instead of fighting it. */
.new-pill {
  position: absolute; top: 7px; right: 7px;
  padding: 3px 6px; border-radius: var(--r-tag);
  background: color-mix(in srgb, var(--accent) 70%, transparent); color: var(--art-white);
  font: 700 8px/1 var(--mono); letter-spacing: .1em;
}

.new-pill.hero { top: auto; right: auto; left: 7px; top: 34px; }

/* ------------------------------------------------ tonight, together */

.sess-card {
  position: relative; margin: 14px var(--pad) 0; border-radius: var(--r-lg); overflow: hidden;
  min-height: 300px; display: flex; align-items: flex-end;
  border: 1px solid var(--line);
  background: linear-gradient(150deg, hsl(var(--hue) 32% 24%), hsl(var(--hue) 22% 12%));
}
.sess-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 18%; }
.sess-card .sc-body {
  position: relative; z-index: 1; width: 100%; padding: 16px;
  background: linear-gradient(to top, color-mix(in srgb, var(--art-ink) 95%, transparent) 35%, transparent); color: var(--art-fg);
}
.sess-actions { display: flex; gap: 8px; padding: 12px var(--pad) 0; }
.sess-actions .btn { flex: 1; }
.sess-actions.second { padding-top: 8px; }
.sess-actions.second .btn { font-size: 13px; padding: 12px; }

/* An add-on is a different offer from a subscription and has to say so. */
.option-row .addon {
  display: inline-block; margin-left: 7px; padding: 2px 6px; border-radius: var(--r-tag);
  background: var(--surface-3); border: 1px solid var(--line);
  font: 700 8px/1.4 var(--mono); letter-spacing: .1em; color: var(--fg-4);
  vertical-align: middle;
}

/* ------------------------------------------------- an exact number */

.scorebar {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 10px var(--pad) 0; padding: 9px 12px; border-radius: var(--r-bar);
  background: var(--surface-2); border: 1px solid var(--line);
}
.scorebar .sb-name { font: 400 12px/1.2 var(--sans); color: var(--fg-3); flex: 1; min-width: 0; }
.scorebar .sb-val {
  font: 600 10px/1 var(--mono); letter-spacing: .06em; color: var(--accent-text);
  padding: 5px 8px; border-radius: var(--r-bar); border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.scorebar.open { flex-direction: column; align-items: stretch; gap: 9px; }
.sb-scale { display: flex; gap: 4px; }
.sb-scale .sb-n {
  flex: 1; padding: 9px 0; border-radius: var(--r-bar); text-align: center;
  background: var(--surface-3); border: 1px solid var(--line);
  font: 500 13px/1 var(--sans); color: var(--fg-2);
}
.sb-scale .sb-n.lo { color: var(--lo); }
.sb-scale .sb-n.hi { color: var(--hi); }
.sb-scale .sb-n.on { border-color: var(--accent-text); background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--fg); }

/* Destructive actions look destructive. */
.btn.danger { background: var(--danger); color: var(--accent-ink); }
.btn.quiet.danger { background: transparent; color: var(--lo); }

/* ------------------------------------------------ the equalizer */

.eq { padding: 14px var(--pad) 0; }
.eq-heads, .eq-feet { display: flex; justify-content: space-between; }
.eq-heads { font: 600 8.5px/1 var(--mono); letter-spacing: .12em; margin-bottom: 8px; }
.eq-heads .lo { color: var(--lo); }
.eq-heads .hi { color: var(--hi); }
.eq-row { display: flex; gap: 3px; }
.eq-n {
  flex: 1; padding: 15px 0; border-radius: var(--r-card); text-align: center;
  background: var(--surface-2); border: 1px solid var(--line);
  font: 500 14px/1 var(--sans); color: var(--fg-2);
}
.eq-n.lo { border-color: color-mix(in srgb, var(--lo) 22%, transparent); }
.eq-n.hi { border-color: color-mix(in srgb, var(--hi) 22%, transparent); }
.eq-n.lo:active { background: color-mix(in srgb, var(--lo) 20%, transparent); color: var(--fg); }
.eq-n.hi:active { background: color-mix(in srgb, var(--hi) 20%, transparent); color: var(--fg); }
.eq-n.end { font-weight: 600; }
.eq-feet { margin-top: 7px; font: 400 9.5px/1 var(--sans); color: var(--fg-4); }

/* Something is waiting for you. */
.navdot {
  position: absolute; top: -3px; right: -9px; min-width: 15px; height: 15px;
  padding: 0 3px; border-radius: var(--r-round); background: var(--accent); color: var(--accent-ink);
  font: 700 9px/15px var(--mono); text-align: center;
}
.nav .glyph { position: relative; }
.pending-note {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px; width: 100%;
  margin-top: 12px; padding: 12px 14px; border-radius: var(--r-card); text-align: left;
  background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
}
.pending-note b { font: 500 14px/1.2 var(--sans); color: var(--fg); }
.pending-note span { font: 400 11.5px/1 var(--sans); color: var(--fg-3); }
.pending-note.quiet { background: var(--surface-2); border-color: var(--line); }

/* The second half of a session answer. */
.sess-follow { display: flex; flex-direction: column; gap: 8px; padding: 12px var(--pad) 0; }
.sess-follow .sf-q {
  font: 600 9px/1 var(--mono); letter-spacing: .12em; color: var(--fg-4); padding-bottom: 2px;
}

/* ------------------------------------------------- trailer on hover */

/* RESOLUTION. YouTube picks its quality from the size of the frame it is given,
   so a player laid out at 124 px was served a 144p video and then blown up 2.2
   times — which is exactly what a blurry trailer looks like. The frame is a
   real 640x360 now and is scaled DOWN to cover the card, so the video arrives
   sharp and is shrunk rather than stretched. */
/* YouTube draws the video's title and channel across the top of an embed that
   has no controls — the big white words over a rail preview. The detail hero
   never showed them because it is overscaled 1.35 inside a clipped box, which
   pushes that band out of sight. A preview was scaled only enough to cover its
   card, so the whole height of the frame, band included, stayed in view.
   It is now scaled a quarter past covering and raised 8% of its own height
   BEFORE the scale, so the lift grows with the frame: the top 18% (the band is
   about 17%) and 2% of the bottom fall outside the card on every card size. */
.poster iframe.preview {
  position: absolute; left: 50%; top: 50%;
  width: 640px; height: 360px; border: 0;
  transform: translate(-50%, -50%) scale(calc(var(--pv, .58) * 1.25)) translateY(-8%);
  transform-origin: center;
  pointer-events: none; z-index: 1;
  animation: preview-in .35s ease-out;
}
@keyframes preview-in { from { opacity: 0; } to { opacity: 1; } }
.poster.previewing > img { opacity: 0; }
.poster.previewing .poster-name { opacity: 0; }
.poster .play-mark {
  position: absolute; left: 7px; bottom: 30px; z-index: 2;
  width: 20px; height: 20px; border-radius: var(--r-round);
  display: grid; place-content: center;
  background: var(--art-glass); color: var(--art-fg-strong);
  font-size: 8px; padding-left: 1px;
  opacity: 0; transition: opacity .15s;
}
.card:hover .play-mark, .card-hero:hover .play-mark { opacity: 1; }
.poster.previewing .play-mark { opacity: 0; }

/* ============================================================ desktop
   One codebase for the phone and the laptop. The phone layout is the
   default — it was designed first and it is where most viewing decisions
   are made — and above 900px the same screens widen: rails run the full
   width with larger cards, the swipe deck and the pair-comparison sit
   centred at a readable size, and hover states earn their keep because
   there is a pointer to hover with. Nothing is duplicated: a screen is one
   template, and this is what it does with more room. */

/* =========================================================================
   A LAPTOP GETS A WEBSITE, A PHONE GETS THE APP.

   Everything above this line is the phone: one column, a bar of tabs along the
   bottom, thumbs. On a laptop that column looked like a phone screenshot
   pasted into the middle of a monitor, with the tabs pinned to the far left
   corner and nothing joining them to it — because the 480px cap on .app was
   never lifted, so the "desktop" rail floated off on its own. So here the cap
   goes, the tabs become the header of a website, and the page fills the window
   with its content held to a readable width. The line is at 768px: a tablet held
   upright, a split-screen window and a small laptop are all closer to a desk than
   to a thumb, and below it the phone layout is what a hand actually wants.
   ========================================================================= */
@media (min-width: 768px) {
  :root { --pad: 40px; }
  body { font-size: 15px; }

  .filter-controls { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .filter-picker:nth-child(even) .filter-menu { inset-inline-start: 0; inset-inline-end: auto; }
  .filter-picker:last-child .filter-menu { inset-inline-start: auto; inset-inline-end: 0; }

  /* The phone column becomes a page. */
  .app { max-width: none; border-left: 0; border-right: 0; }
  .screen { max-width: 1220px; margin: 0 auto; width: 100%; }
  .screen:has(.nav) { padding-top: 64px; }
  /* The inline padding on each screen reserves room for a bottom bar that a
     laptop does not have; without this every page ends in 150px of nothing. */
  .screen:has(.nav) > .scroll { padding-bottom: 56px !important; }
  .scroll { padding-left: 0; padding-right: 0; }
  .pad { padding-left: var(--pad); padding-right: var(--pad); }
  .h1 { font-size: 34px; }
  .h2 { font-size: 22px; }
  .h3 { font-size: 17px; }

  /* The tabs become the header: brand on the left, sections on the right,
     across the top of the window, with its contents on the page's own grid. */
  .nav {
    position: fixed; top: 0; left: 0; right: 0; bottom: auto; z-index: 50;
    padding: 0; height: 64px;
    background: var(--surface); border-top: 0; border-bottom: 1px solid var(--line);
    backdrop-filter: blur(12px);
  }
  .nav-inner {
    display: flex; align-items: center; gap: 6px; height: 100%;
    max-width: 1220px; margin: 0 auto; padding: 0 var(--pad);
  }
  .nav-brand {
    display: block; margin-right: auto;
    font: 600 13px/1 var(--mono); letter-spacing: .2em; color: var(--fg);
  }
  .nav button {
    flex-direction: row; gap: 8px; width: auto; padding: 9px 14px; border-radius: var(--r-card);
    font-size: 11px; color: var(--fg-2);
  }
  .nav button:hover { background: var(--surface-2); color: var(--fg); }
  .nav button.on { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent-text); }
  .nav button .glyph { font-size: 15px; filter: none; }
  .nav .navdot { top: -6px; right: -10px; }

  /* Rails: bigger cards and more of them. No visible scrollbar — the rails
     never had one and the thin one added for desktop looked like a defect. */
  .track { gap: 14px; padding-left: var(--pad); padding-right: var(--pad); scrollbar-width: none; }
  .track::-webkit-scrollbar { display: none; }
  .card { width: 168px; }
  .card-hero { width: 252px; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 22px 16px; }
  .card:hover .poster, .card-hero:hover .poster { transform: translateY(-3px); box-shadow: 0 14px 30px -14px var(--shadow); }
  .poster { transition: transform .18s ease, box-shadow .18s ease; }

  /* Decision screens sit centred at a size a pointer can aim at. */
  /* WIDTH IS NOT OPTIONAL HERE. These are flex items in a column, and an auto
     margin on the cross axis stops them stretching: they shrink to their own
     content instead. The deck's only child is absolutely positioned, so its
     content is nothing at all and the card came out a 50 px strip down the
     middle of the screen. */
  .deck, .sess-card, .sess-actions, .sess-follow, .eq, .scorebar, .swipe-actions,
  .choice-row, .match-bar, .demo, .demo-captions {
    width: 100%; max-width: 560px; margin-left: auto; margin-right: auto;
  }
  /* And their contents line up with the card above them. Each of these carries
     its own side padding for the phone, which on a laptop left the row of
     numbers 80 px narrower than the deck it belongs to. */
  .eq, .scorebar, .choice-row, .swipe-actions, .match-bar { padding-left: 0; padding-right: 0; }
  .deck { margin-left: auto; margin-right: auto; }
  .swipe-card { max-width: 520px; }

  /* A sheet on a phone rises from the bottom edge; on a laptop that edge is
     nowhere near the pointer, so it becomes a dialog in the middle. */
  .sheet-back { align-items: center; }
  .sheet {
    max-width: 560px; border-radius: var(--r-sheet); border-bottom: 1px solid var(--line);
    max-height: 82vh; padding-bottom: 18px;
    box-shadow: 0 40px 80px -30px var(--shadow-strong);
  }
  .sheet .grip { display: none; }

  /* Nothing is dodging a bottom bar any more. The consent banner's own rule is
     further down the file, so this one has to out-specify it, not just follow it. */
  .toast { bottom: 28px; }
  .mood-chip { bottom: 24px; }
  .app .consent { bottom: 20px; }

  .filter-bar { padding-left: var(--pad); padding-right: var(--pad); }
  .trophy-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .me-head, .trophy-bar, .legal { max-width: 760px; }
}

@media (min-width: 1400px) {
  :root { --pad: 64px; }
  .card { width: 190px; }
  .card-hero { width: 290px; }
}

/* ----------------------------------------------------- consent notice */

.consent {
  position: fixed; left: 0; right: 0; bottom: calc(64px + env(safe-area-inset-bottom));
  z-index: 60; display: flex; align-items: center; gap: 12px;
  margin: 0 10px; padding: 12px 14px; border-radius: var(--r-card);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: 0 18px 40px -16px var(--shadow);
}
.consent-text { flex: 1; font: 400 12.5px/1.45 var(--sans); color: var(--fg-2); }
.consent-text b { color: var(--fg); }
.linkish { display: inline; padding: 0; margin-left: 4px; background: none; border: 0; color: var(--accent-text); font: inherit; text-decoration: underline; cursor: pointer; }
@media (min-width: 768px) { .consent { max-width: 720px; margin-left: auto; margin-right: auto; } }

/* ----------------------------------------------------------- legal text */

.legal { margin-top: 18px; }
.legal h3 { margin: 18px 0 6px; font: 600 9.5px/1 var(--mono); letter-spacing: .14em; color: var(--fg-4); text-transform: uppercase; }
.legal p { font: 400 13.5px/1.6 var(--sans); color: var(--fg-2); }
.legal p b { color: var(--fg); font-weight: 500; }
.legal-huddelo-mark { display: block; width: 32px; height: 32px; margin-top: 18px; }
.legal-source-notice { display: flex; align-items: flex-start; gap: 12px; }
.legal-source-notice .legal-tmdb-logo { display: block; width: auto; height: 12px; flex: none; margin-top: 5px; }
.legal-source-notice p { margin: 0; }
.legal-license { display: inline-block; margin-top: 8px; font: 500 12px/1.4 var(--sans); color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }

/* -------------------------------------------------- preview sound toggle */

/* A 26 px circle is smaller than a fingertip. The dot people aim at is the
   same size; the button around it is not. */
.preview-sound {
  min-width: 44px; min-height: 44px;
  display: grid; place-content: center;
  background: none; border: 0; padding: 0;
}
.preview-sound::before {
  content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 30px; height: 30px; border-radius: var(--r-round);
  background: var(--art-glass-strong); border: 1px solid var(--art-border);
  backdrop-filter: blur(6px); z-index: -1;
}
/* Fully inside the poster, which clips: a target hanging over the edge is a
   target you cannot press. */
/* No border-radius on the button itself, deliberately, and this is not
   cosmetic: a rounded box is hit-tested as the rounded shape, not the box, so
   `border-radius: 50%` made the four corners of a 44px target dead — a circle
   inscribed in a square is 78.5% of it, and the fifth of the button that is
   left over is exactly where a thumb coming from below and to the right lands.
   Measured: five pixels in from the bottom-right corner, elementFromPoint
   returned the poster, the click bubbled to the card, and the title opened.
   Nothing is lost by removing it — background is none and border is 0, so
   there was never anything for it to round. The circle you can see is the
   ::before. */
.poster .preview-sound {
  position: absolute; right: 0; bottom: 20px; z-index: 3;
  width: 44px; height: 44px; font-size: 14px;
  background: none; border: 0;
}

/* ------------------------------------------- phone: the nearest card plays
   No pointer, so the rail chooses the card nearest the centre for its trailer.
   The rail itself still starts on the page grid: centring through half-track
   padding made 124px and 186px cards begin at different x positions, and made
   a short shelf look indented beside a long one. */
@media (hover: none) and (max-width: 767px) {
  .track { scroll-snap-type: x proximity; padding-left: var(--pad); padding-right: var(--pad); }
  .card, .card-hero { scroll-snap-align: start; scroll-snap-stop: normal; }
  .poster .play-mark { opacity: 1; }
  .poster.previewing .play-mark { opacity: 0; }
}

/* ---------------------------------------------- one way forward, drawn */

.step-nav { display: flex; flex-direction: column; gap: 8px; margin-top: 22px; }
.step-note { color: var(--fg-4); margin-bottom: 4px; }
.btn.step-go { width: 100%; justify-content: space-between; padding-right: 14px; }
.btn.step-go .chev { width: 22px; height: 22px; flex: none; }
.btn.step-alt { width: 100%; }
.btn .chev { display: block; }
/* A dead trailer says so on the card rather than showing a black frame. */
.poster.trailer-dead iframe.preview { display: none; }
.poster.trailer-dead .play-mark { display: none; }
.poster.trailer-dead > img { opacity: 1; }

/* --------------------------------------------------------- a choice, big */

.choice-row { display: flex; flex-direction: column; gap: 8px; }
.choice {
  width: 100%; text-align: left; padding: 15px 18px; border-radius: var(--r-lg);
  background: var(--surface-2); border: 1.5px solid var(--line);
  display: flex; flex-direction: column; gap: 4px;
  transition: transform .12s ease, border-color .12s ease;
}
.choice:active { transform: scale(.985); }
.choice .c-label { font: 500 18px/1.2 var(--sans); letter-spacing: -.012em; color: var(--fg); }
.choice .c-glyph { display: inline-block; min-width: 1.35em; font: inherit; }
.choice .c-note { font: 400 12.5px/1.35 var(--sans); color: var(--fg-3); }
.choice.ok { border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.choice.ok .c-label { color: var(--ok); }
.choice.no { border-color: color-mix(in srgb, var(--lo) 40%, transparent); }
.choice.no .c-label { color: var(--lo); }
.choice.mid { border-color: var(--line); }
.choice.mid .c-label { color: var(--fg-2); }
@media (min-width: 768px) {
  .choice-row { flex-direction: row; }
  .choice { flex: 1; }
  .sess-follow { flex-direction: row; flex-wrap: wrap; }
  .sess-follow .choice { flex: 1 1 40%; }
  .sess-follow .sf-q { flex: 1 1 100%; }
}

/* ------------------------------------------ the deep list, one card at a time */

.track.big { --cw: min(78vw, 360px); scroll-snap-type: x mandatory; gap: 16px;
  padding: 18px var(--pad) 8px; }
.card-big { width: var(--cw); scroll-snap-align: start; }
.card-big .poster { width: var(--cw); height: calc(var(--cw) * 1.42); border-radius: var(--r-lg); }
.card-big .t { font-size: 17px; margin-top: 10px; }
.card-big .why { font-size: 12px; color: var(--fg-3); margin-top: 4px; }
@media (min-width: 768px) { .track.big { --cw: 320px; } }
/* A See all list on a wide screen is a grid the page scrolls down through: a
   mouse cannot swipe a row sideways, and the row shows no scrollbar there. */
@media (min-width: 768px) {
  #rail-scroll .track.big { --cw: 250px; display: grid; grid-template-columns: repeat(auto-fill, var(--cw));
    justify-content: space-between; row-gap: 26px; overflow: visible; scroll-snap-type: none; }
  #rail-scroll .track.big > .rail-sentinel { grid-column: 1 / -1; }
}

/* Hold-and-drag on a card: the number the drag means, drawn on the card. */
.card-gauge {
  position: absolute; left: 50%; top: 42%; transform: translate(-50%, -50%);
  padding: 8px 12px; border-radius: var(--r-bar); background: var(--art-glass-strong);
  border: 1px solid var(--art-border); color: var(--art-fg);
  font: 700 12px/1 var(--mono); letter-spacing: .08em; white-space: nowrap; pointer-events: none; z-index: 5;
}
/* RIGHT IS LIKED, and it is painted that way.
   These two were the wrong way round: right said LIKED IT in red and left said
   NOT FOR ME in green. The direction meanings were flipped in the gesture code
   to match the swipe deck — where right has always been the heart — and the
   colours here were not flipped with them, so the word and the colour argued
   with each other on every drag. */
.card.left .card-gauge, .card-hero.left .card-gauge { border-color: color-mix(in srgb, var(--lo) 70%, transparent); color: var(--lo); }
.card.right .card-gauge, .card-hero.right .card-gauge { border-color: color-mix(in srgb, var(--ok) 70%, transparent); color: var(--ok); }
/* The poster itself reacted to up and down but not to left and right, so the
   strongest signal on screen stayed neutral for the two most common answers. */
.card-hero.right .poster, .card.right .poster { border-color: var(--ok); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ok) 50%, transparent); }
.card-hero.left .poster,  .card.left .poster  { border-color: var(--lo); box-shadow: 0 0 0 2px color-mix(in srgb, var(--lo) 50%, transparent); }
.card.up .card-gauge, .card-hero.up .card-gauge { border-color: color-mix(in srgb, var(--accent) 70%, transparent); color: var(--accent-text); }

/* The detail page opens with the trailer playing where the still was. */
.detail-hero { position: relative; overflow: hidden; }
.detail-hero .detail-trailer {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
  transform: scale(1.35); transform-origin: center; pointer-events: none; z-index: 1;
}
.detail-hero.has-trailer > img { opacity: 0; }
.detail-hero > div { position: relative; z-index: 2; }
.detail-hero .back-btn { position: absolute; z-index: 3; }
.detail-hero .trailer-fullscreen {
  position: absolute; right: 8px; top: calc(env(safe-area-inset-top) + 8px); z-index: 3;
  width: 44px; height: 44px; min-height: 44px; padding: 0; border-radius: var(--r-round);
  display: grid; place-content: center;
  background: var(--art-glass); backdrop-filter: blur(8px);
  border: 0; color: var(--art-fg);
}
.detail-hero .trailer-fullscreen span { color: currentColor; font-size: 20px; line-height: 1; }
/* The two right-hand actions form one vertical stack: full screen first,
   sound directly below it. Both remain clear of the back action at 320px. */
.detail-hero .hero-sound { position: absolute; right: 7px; top: calc(env(safe-area-inset-top) + 56px); z-index: 3; width: 46px; height: 46px; font-size: 15px; }
@media (min-width: 768px) {
  .detail-hero { height: min(56vh, 500px); min-height: 360px; padding-bottom: 30px; }
  .detail-hero > div:last-child { max-width: calc(100% - 190px); }
}
.detail-hero .lang-pill {
  position: absolute; right: 56px; top: calc(env(safe-area-inset-top) + 72px); z-index: 3;
  padding: 3px 7px; border-radius: var(--r-tag); background: var(--art-glass); border: 1px solid var(--art-border);
  font: 700 9px/1 var(--mono); letter-spacing: .1em; color: var(--art-fg-strong);
}

/* ------------------------------------------------ the demo before swiping
   One dummy card, four moves, on a twelve-second loop. The card and the hand
   share a timeline; the captions fade in on the same beats. */

.demo { margin-top: 18px; }
.demo-stage { position: relative; height: 300px; display: flex; justify-content: center; align-items: flex-start; padding-top: 30px; box-sizing: border-box; overflow: hidden; }
.demo-card {
  /* The tutorial's card is a placeholder like any other, so it takes the same
     hue the app sets per title — this one is written down because the demo has
     no title behind it. A number, not a colour: the recipe below is the one
     every placeholder uses. */
  --hue: 212;
  position: relative; width: 168px; height: 236px; border-radius: var(--r-lg); overflow: hidden;
  background: linear-gradient(160deg, hsl(var(--hue) 40% 30%), hsl(var(--hue) 30% 12%)); border: 1px solid var(--line);
  box-shadow: 0 24px 40px -18px var(--shadow);
}
.demo-art { position: absolute; inset: 0; background:
  radial-gradient(90% 60% at 30% 20%, color-mix(in srgb, var(--accent) 35%, transparent), transparent 60%),
  repeating-linear-gradient(115deg, color-mix(in srgb, var(--art-white) 3.5%, transparent) 0 10px, transparent 10px 20px); }
.demo-v {
  position: absolute; top: 16px; padding: 6px 9px; border-radius: var(--r-card); border: 2px solid currentColor;
  font: 700 11px/1 var(--mono); letter-spacing: .1em; background: var(--art-glass); opacity: 0; white-space: nowrap;
}
.demo-v.like { right: 12px; color: var(--ok); transform: rotate(9deg); }
.demo-v.pass { left: 12px; color: var(--danger); transform: rotate(-9deg); }
.demo-v.up { left: 50%; transform: translateX(-50%); color: var(--ok); text-transform: uppercase; }
.demo-v.skip { left: 50%; top: auto; bottom: 16px; transform: translateX(-50%); color: var(--danger); text-transform: uppercase; }
.demo-score {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font: 600 64px/1 var(--sans); color: var(--ok); text-shadow: 0 4px 18px var(--art-shadow); opacity: 0;
}
.demo-hand {
  position: absolute; left: 50%; top: 148px; width: 30px; height: 30px; border-radius: var(--r-round);
  border: 2px solid var(--art-fg-strong); background: var(--art-line);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--art-white) 8%, transparent); pointer-events: none;
}
/* ------------------------------------- the number, big, mid-card, while held */

.swipe-card .swipe-score {
  position: absolute; left: 50%; top: 44%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  opacity: 0; pointer-events: none; z-index: 4; transition: opacity .12s;
}
.swipe-card .swipe-score.on { opacity: 1; }
.swipe-card .swipe-score b { font: 600 96px/1 var(--sans); letter-spacing: -.04em; color: var(--fg); text-shadow: 0 6px 24px var(--art-shadow); }
.swipe-card .swipe-score.ok b { color: var(--ok); }
.swipe-card .swipe-score.no b { color: var(--danger); }
.swipe-card .swipe-score i {
  font: 600 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; font-style: normal;
  padding: 6px 10px; border-radius: var(--r-bar); background: var(--art-glass); color: var(--art-fg-strong);
}
.swipe-head .calib { padding-right: calc(var(--pad) + 38px); } /* room for the ? */

/* The way out, where it can be seen. It lived under the deck, and the deck has
   a 380px minimum: on a 390x844 phone the head, deck, equaliser, choice row and
   score strip together leave the buttons below the fold, so the one moment
   somebody most wants to know they can stop is the one moment the answer is
   off screen. The head is always visible, so it goes there. */
.demo-replay {
  position: absolute; right: var(--pad); top: calc(env(safe-area-inset-top) + 22px);
  width: 28px; height: 28px; border-radius: var(--r-round); border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg-3); font: 600 13px/1 var(--mono);
}

/* ------------------------------------------------ sign-in: what comes next */
.intro-steps { margin: 18px 0 0; padding: 0 0 0 22px; display: flex; flex-direction: column; gap: 8px; }
.intro-steps li { font: 400 13px/1.5 var(--sans); color: var(--fg-2); }
.intro-steps li b { color: var(--fg); font-weight: 500; }
.intro-steps li b::after { content: ': '; }
.intro-steps li::marker { color: var(--accent-text); font: 600 12px var(--mono); }

/* --------------------------------------------- the tour, one move per step
   Each step loops its own three-second move. The card, the hand and the stamp
   share the timeline; the step class on .demo picks which set runs. */
.demo-dots { display: flex; justify-content: center; gap: 6px; margin-top: 6px; }
.demo-dots i { width: 6px; height: 6px; border-radius: var(--r-round); background: var(--line); }
.demo-dots i.on { background: var(--accent); }

.demo.step-left .demo-card  { animation: tour-left 3s ease-in-out infinite; }
.demo.step-left .demo-hand  { animation: tour-left-hand 3s ease-in-out infinite; }
.demo.step-left .demo-v.pass { animation: tour-stamp 3s ease-in-out infinite; }
@keyframes tour-left {
  0%, 15%   { transform: translate(0,0) rotate(0); }
  55%, 70%  { transform: translate(-78px,0) rotate(-8deg); }
  100%      { transform: translate(0,0) rotate(0); }
}
@keyframes tour-left-hand {
  0%, 15%   { transform: translate(-50%,-50%) scale(1); opacity: .9; }
  55%, 70%  { transform: translate(calc(-50% - 78px),-50%) scale(.85); opacity: .9; }
  100%      { transform: translate(-50%,-50%) scale(1); opacity: .9; }
}
@keyframes tour-stamp { 0%,25%{opacity:0} 45%,75%{opacity:1} 90%,100%{opacity:0} }

.demo.step-right .demo-card  { animation: tour-right 3s ease-in-out infinite; }
.demo.step-right .demo-hand  { animation: tour-right-hand 3s ease-in-out infinite; }
.demo.step-right .demo-v.like { animation: tour-stamp 3s ease-in-out infinite; }
@keyframes tour-right {
  0%, 15%   { transform: translate(0,0) rotate(0); }
  55%, 70%  { transform: translate(78px,0) rotate(8deg); }
  100%      { transform: translate(0,0) rotate(0); }
}
@keyframes tour-right-hand {
  0%, 15%   { transform: translate(-50%,-50%) scale(1); opacity: .9; }
  55%, 70%  { transform: translate(calc(-50% + 78px),-50%) scale(.85); opacity: .9; }
  100%      { transform: translate(-50%,-50%) scale(1); opacity: .9; }
}

.demo.step-down .demo-card  { animation: tour-down 3s ease-in-out infinite; }
.demo.step-down .demo-hand  { animation: tour-down-hand 3s ease-in-out infinite; }
.demo.step-down .demo-v.skip { animation: tour-stamp 3s ease-in-out infinite; }
@keyframes tour-down {
  0%, 15%   { transform: translate(0,0) scale(1); }
  55%, 70%  { transform: translate(0,40px) scale(.95); }
  100%      { transform: translate(0,0) scale(1); }
}
@keyframes tour-down-hand {
  0%, 15%   { transform: translate(-50%,-50%) scale(1); opacity: .9; }
  55%, 70%  { transform: translate(-50%,calc(-50% + 40px)) scale(.85); opacity: .9; }
  100%      { transform: translate(-50%,-50%) scale(1); opacity: .9; }
}

.demo.step-up .demo-card  { animation: tour-up 3s ease-in-out infinite; }
.demo.step-up .demo-hand  { animation: tour-up-hand 3s ease-in-out infinite; }
.demo.step-up .demo-v.up { animation: tour-stamp 3s ease-in-out infinite; }
@keyframes tour-up {
  0%, 15%   { transform: translate(0,0) scale(1); }
  55%, 70%  { transform: translate(0,-40px) scale(.95); }
  100%      { transform: translate(0,0) scale(1); }
}
@keyframes tour-up-hand {
  0%, 15%   { transform: translate(-50%,-50%) scale(1); opacity: .9; }
  55%, 70%  { transform: translate(-50%,calc(-50% - 40px)) scale(.85); opacity: .9; }
  100%      { transform: translate(-50%,-50%) scale(1); opacity: .9; }
}

/* hold: out to the left, then up — the number climbs 8, 9, 10, then back */
.demo.step-hold .demo-card  { animation: tour-hold 4s ease-in-out infinite; }
.demo.step-hold .demo-hand  { animation: tour-hold-hand 4s ease-in-out infinite; }
.demo.step-hold .demo-v.like { animation: tour-hold-stamp 4s ease-in-out infinite; }
.demo.step-hold .demo-score { animation: tour-hold-score 4s ease-in-out infinite; }
.demo.step-hold .demo-score .d1 { animation: tour-d1 4s steps(1) infinite; }
.demo.step-hold .demo-score .d2 { animation: tour-d2 4s steps(1) infinite; }
.demo.step-hold .demo-score .d3 { animation: tour-d3 4s steps(1) infinite; }
@keyframes tour-hold {
  0%, 10%   { transform: translate(0,0) rotate(0); }
  25%, 40%  { transform: translate(40px,0) rotate(3deg); }
  70%, 82%  { transform: translate(84px,0) rotate(7deg); }
  100%      { transform: translate(0,0) rotate(0); }
}
@keyframes tour-hold-hand {
  0%, 10%   { transform: translate(-50%,-50%) scale(1); opacity: .9; }
  25%, 40%  { transform: translate(calc(-50% + 40px),-50%) scale(.8); opacity: .9; }
  70%, 82%  { transform: translate(calc(-50% + 84px),-50%) scale(.8); opacity: .9; }
  100%      { transform: translate(-50%,-50%) scale(1); opacity: .9; }
}
@keyframes tour-hold-stamp { 0%,18%{opacity:0} 25%,84%{opacity:1} 94%,100%{opacity:0} }
@keyframes tour-hold-score { 0%,22%{opacity:0} 27%,84%{opacity:1} 92%,100%{opacity:0} }
@keyframes tour-d1 { 0%,24%{opacity:0} 25%,54%{opacity:1} 55%,100%{opacity:0} }
@keyframes tour-d2 { 0%,54%{opacity:0} 55%,66%{opacity:1} 67%,100%{opacity:0} }
@keyframes tour-d3 { 0%,66%{opacity:0} 67%,88%{opacity:1} 89%,100%{opacity:0} }

/* ---------------------------------------------- together: the deck and the count */
.sess-deck { margin-top: 10px; }
.match-bar {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto; column-gap: 12px; align-items: center;
  width: calc(100% - 2 * var(--pad)); margin: 12px var(--pad) 0; padding: 12px 16px; border-radius: var(--r-bar); text-align: left;
  background: var(--surface-2); border: 1px solid var(--line); transition: border-color .3s, background .3s;
}
.match-bar.has { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 10%, var(--surface-2)); }
.match-bar .mb-n { grid-row: 1 / span 2; font: 600 34px/1 var(--sans); letter-spacing: -.03em; color: var(--fg-3); transition: color .3s; }
.match-bar.has .mb-n { color: var(--ok); }
.match-bar .mb-t { font: 500 14px/1.2 var(--sans); color: var(--fg); }
.match-bar .mb-p { font: 500 10px/1.2 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--fg-4); margin-top: 3px; }

/* ------------------------------------------------- a number you slide to */
.score-slider { margin-top: 14px; padding: 14px 6px 6px; border-radius: var(--r-card); background: var(--surface-2); border: 1px solid var(--line); }
.score-read { display: flex; align-items: baseline; justify-content: center; gap: 10px; }
.score-read b { font: 600 56px/1 var(--sans); letter-spacing: -.04em; color: var(--fg); transition: color .15s; }
.score-slider.ok .score-read b { color: var(--ok); }
.score-slider.no .score-read b { color: var(--danger); }
.score-read i { font: 600 11px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; font-style: normal; color: var(--fg-2); }
.score-slider input[type=range] { width: 100%; margin: 16px 0 6px; accent-color: var(--accent-text); height: 28px; }
.score-slider.ok input[type=range] { accent-color: var(--ok); }
.score-slider.no input[type=range] { accent-color: var(--danger); }
.score-ends { display: flex; justify-content: space-between; padding: 0 6px; font: 500 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--fg-4); }

/* critics' number, when the audience's leads */
/* Renamed from .critics, which is also the detail page's container for review
   cards — and that one carries margin-top: 12px. A class name is not a
   namespace: the Tomatometer chip matched both rules, took the stray margin
   into a flex row whose other three chips had none, and sat six pixels below
   the line they shared. It was invisible until a title had both Rotten
   Tomatoes scores at once, which 2,155 titles in the live catalogue do. */
.rate.rt.paired { opacity: .7; }

.card .rate-row, .card-hero .rate-row { gap: 4px; }
.card .rate, .card-hero .rate { padding: 0 4px; gap: 3px; }
/* Wrapped chips sit wholly below the fixed first line. */
:is(.card, .card-big, .card-hero, .swipe-card, .sess-card, .demo-card) .rate-row {
  flex-wrap: wrap; align-content: flex-start; height: 20px; flex: none; overflow: hidden;
}
:is(.card, .card-big, .card-hero, .swipe-card, .sess-card, .demo-card) .rate {
  flex: none;
}
:is(.card, .card-big, .card-hero, .swipe-card, .sess-card, .demo-card) .rate.agg.few {
  letter-spacing: 0;
}
.card .rate.agg.few { padding-inline: 2px; }

/* ---------------------------------------- the trailer on the card to swipe */
/* The whole card, not a band across the top of it: a 16:9 video in a 2:3 card
   left the poster showing underneath and read as a rendering fault. Scaled to
   fill, exactly as a rail card does. */
.swipe-card .swipe-trailer {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 1;
  background: var(--art-black); animation: preview-in .35s ease-out;
}
.swipe-card .swipe-trailer iframe.preview {
  position: absolute; left: 50%; top: 50%;
  width: 640px; height: 360px; border: 0;
  /* Same crop as a rail preview, for the same title band. */
  transform: translate(-50%, -50%) scale(calc(var(--pv, 1.45) * 1.25)) translateY(-8%);
  transform-origin: center;
}
/* The words stay readable over it. */
/* The trailer is the point of the card; the words are a caption on it. Two
   lines of synopsis, and the wash stops where they do. */
.swipe-card .swipe-body { background: linear-gradient(to top, color-mix(in srgb, var(--art-ink) 95%, transparent) 55%, color-mix(in srgb, var(--art-ink) 70%, transparent) 82%, transparent); margin: 0 -20px -20px; padding: 16px 20px 18px; }
.swipe-card .swipe-body p.tiny {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  margin-top: 6px !important;
}
.swipe-card .swipe-body .rate-row { margin-top: 6px; }
.swipe-card .swipe-body .h2 { margin-top: 4px !important; }
/* Square for the same reason as .poster .preview-sound above. */
.swipe-card .deck-sound {
  position: absolute; right: 6px; top: 6px; z-index: 4;
  width: 46px; height: 46px; font-size: 15px;
  background: none; border: 0;
}
.swipe-card .swipe-body { position: relative; z-index: 2; }

/* ------------------------------------------------------------ the skin switch */
.theme-toggle {
  width: 34px; height: 34px; border-radius: var(--r-round); display: grid; place-content: center;
  background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--fg); font-size: 15px;
  font-family: var(--emoji);
}
.theme-toggle.float { position: absolute; right: var(--pad); top: calc(env(safe-area-inset-top) + 14px); z-index: 5; }
/* The way back to English, beside the skin switch and never on top of it. It
   says where it will take you rather than where you are, because a label in a
   language you cannot read is not a label. */
.lang-toggle {
  height: 34px; min-width: 34px; padding: 0 8px; border-radius: var(--r-pill);
  display: grid; place-content: center;
  background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--fg);
  font: 600 10.5px/1 var(--mono); letter-spacing: .08em;
}
.lang-toggle.float { position: absolute; right: calc(var(--pad) + 40px); top: calc(env(safe-area-inset-top) + 14px); z-index: 5; }
.lp-bar-actions .lang-toggle { position: static; }
.screen { position: relative; }
/* text that sits on artwork keeps its own colour whatever the page */
.swipe-card, .swipe-card .swipe-body, .demo-card, .sess-card .sc-body { color: var(--art-fg); }
/* A rule that matches the element beats a colour it would have inherited, so
   every muted class used over artwork has to be named here or it takes the
   page's ink — black on black in the light skin. */
.swipe-card .eyebrow, .sess-card .eyebrow,
.swipe-card .tiny, .sess-card .tiny,
.swipe-card .dim, .sess-card .dim { color: var(--art-fg-2); }
.swipe-card .eyebrow::before, .sess-card .eyebrow::before { color: var(--art-accent); }
.swipe-card .swipe-body .tiny { color: var(--art-fg-3) !important; }
.swipe-card .swipe-score b { color: var(--art-fg); }
.swipe-card .rate.imdb, .sess-card .rate.imdb, .demo-card .rate.imdb { color: var(--art-rate-imdb); }
/* …and the ground that gold was chosen for. The gold is kept on the strength of
   reading 8.05:1 on the dark glass, but the chip kept the page's own --surface-2
   fill, so on the swipe card it measured 1.59:1 in the light skin — the art ink
   over paper. The fill and the hairline are the art family's now. */
.swipe-card .rate.imdb, .sess-card .rate.imdb, .demo-card .rate.imdb { background: var(--art-glass); border-color: var(--art-line); }

/* =========================================================================
   TEXT SIZE

   Every size in this file is in px, chosen against a phone held close. The
   operator asked for a step up by default and more again for older readers,
   so the sizes that carry actual reading are named here and multiplied. The
   step is set on <html> before the first paint (index.html) from the size
   remembered on the device, and from the profile's age when nobody has
   chosen one by hand.
   ========================================================================= */
:root {
  /* The named steps are the prototype's own sizes, from its shorthands:
     600 23px/1.18 and 600 17px/1.1 for the two headings, 600 14.5px/1 on a
     button, 400 13.5px/1.5 for prose, 600 9.5px/1 mono on an eyebrow. */
  --fs: 1.08;
  --t-h1: 23px; --t-h2: 17px; --t-h3: 14.5px;
  --t-body: 13.5px; --t-tiny: 11.5px; --t-eyebrow: 9.5px; --t-label: 14.5px;
  --t-card: 12.5px; --t-chip: 12.5px; --t-nav: 9.5px; --t-small: 11px; --t-micro: 9px;
}
:root[data-fs="l"]  { --fs: 1.2; }
:root[data-fs="xl"] { --fs: 1.34; }
@media (min-width: 768px) {
  :root { --t-h1: 34px; --t-h2: 22px; --t-h3: 17px; --t-body: 15px; }
}
body { font-size: calc(var(--t-body) * var(--fs)); }
.h1 { font-size: calc(var(--t-h1) * var(--fs)); }
.h2 { font-size: calc(var(--t-h2) * var(--fs)); }
.h3 { font-size: calc(var(--t-h3) * var(--fs)); }
.tiny { font-size: calc(var(--t-tiny) * var(--fs)); }
.eyebrow { font-size: calc(var(--t-eyebrow) * var(--fs)); }
.btn, .choice .c-label, .option-row, .sheet-title, .field, .composer .field { font-size: calc(var(--t-label) * var(--fs)); }
.choice .c-note, .sheet-note, .services-line, .legal p, .card .s, .demo-captions .cap { font-size: calc(var(--t-tiny) * var(--fs)); }
.card .t, .card-hero .t { font-size: calc(var(--t-card) * var(--fs)); }
.nav button { font-size: calc(var(--t-nav) * var(--fs)); }
.genre, .fchip { font-size: calc(var(--t-chip) * var(--fs)); }
.rate, .badge { font-size: calc(var(--t-micro) * var(--fs)); }
.sess-order button { font-size: calc(var(--t-small) * var(--fs)); }

/* ------------------------------------------- together: the order of the deck */
.sess-order { display: flex; gap: 6px; justify-content: center; margin: 10px var(--pad) 0; }
.sess-order button {
  padding: 7px 12px; border-radius: var(--r-pill); border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg-3);
  font-family: var(--mono); letter-spacing: .04em;
}
.sess-order button.on { border-color: var(--accent-text); color: var(--accent-text); background: color-mix(in srgb, var(--accent) 12%, transparent); }


/* =========================================================================
   THE LIGHT SKIN'S EDGES

   The palette above is the whole difference between the two skins — same
   radii, same type, same spacing, as the two prototypes are. What is left
   here is the one structural consequence of putting white on white: a surface
   that is the same colour as the page is only a surface if it has an edge.
   ========================================================================= */
:root:not([data-theme="dark"]) .sheet,
:root:not([data-theme="dark"]) .consent,
:root:not([data-theme="dark"]) .mood-chip,
:root:not([data-theme="dark"]) .card .poster,
:root:not([data-theme="dark"]) .card-hero .poster { border: 1px solid var(--line); }
:root:not([data-theme="dark"]) .nav { background: var(--surface); }
:root:not([data-theme="dark"]) .rail-head .count,
:root:not([data-theme="dark"]) .fchip,
:root:not([data-theme="dark"]) .genre { border: 1px solid var(--line); }

/* =========================================================================
   RIGHT TO LEFT

   Arabic turns the page around. Most of the layout is flexbox and grid, which
   mirror themselves, so only the things pinned to a physical edge need saying
   again: the chips on artwork, the back arrow, the fades and the rails.
   ========================================================================= */
[dir="rtl"] .badge { left: auto; right: 8px; }
[dir="rtl"] .hold-hint { left: auto; right: 6px; }
[dir="rtl"] .price-pill, [dir="rtl"] .needs-pill { right: auto; left: 8px; }
[dir="rtl"] .back-btn { left: auto; right: 8px; }
[dir="rtl"] .trailer-fallback { margin-left: var(--pad); margin-right: var(--pad); }
[dir="rtl"] .detail-hero .trailer-fullscreen { right: auto; left: 8px; }
[dir="rtl"] .detail-hero .hero-sound { right: auto; left: 8px; }
[dir="rtl"] .detail-hero .lang-pill { right: auto; left: 56px; }
[dir="rtl"] .poster .preview-sound { right: auto; left: -2px; }
[dir="rtl"] .swipe-card .deck-sound { right: auto; left: 6px; }
[dir="rtl"] .swipe-card .verdict.like { right: auto; left: 20px; transform: rotate(-9deg); }
[dir="rtl"] .swipe-card .verdict.pass { left: auto; right: 20px; transform: rotate(9deg); }
[dir="rtl"] .demo-replay { right: auto; left: var(--pad); }
[dir="rtl"] .theme-toggle.float { right: auto; left: var(--pad); }
[dir="rtl"] .filter-bar {
  -webkit-mask-image: linear-gradient(to left, black calc(100% - 26px), transparent);
  mask-image: linear-gradient(to left, black calc(100% - 26px), transparent);
}
[dir="rtl"] .filter-bar > :last-child { margin-right: 0; margin-left: 26px; }
[dir="rtl"] .eyebrow::before { content: ''; }
[dir="rtl"] .btn.quiet { justify-content: flex-end; }
[dir="rtl"] .match-bar { text-align: right; }
/* The swipe gesture keeps its meaning: right is still liked, because the hand
   learns the motion, not the reading direction. */

/* On white, a surface that is also white is only a surface if it has an edge. */
:root:not([data-theme="dark"]) .sheet,
:root:not([data-theme="dark"]) .consent,
:root:not([data-theme="dark"]) .mood-chip { border: 1px solid var(--line-strong); }
:root:not([data-theme="dark"]) .nav { background: var(--surface); }
:root:not([data-theme="dark"]) .rail-head .count,
:root:not([data-theme="dark"]) .fchip,
:root:not([data-theme="dark"]) .genre { border: 1px solid var(--line); }

/* How far to shrink the 640x360 frame so it covers each surface. A card is
   2:3, so the height decides: scale = card height / 360, with a little over. */
.card .poster { --pv: .58; }
.card-hero .poster { --pv: .86; }
.track.big .poster { --pv: 1.5; }
.grid .poster { --pv: .72; }
@media (min-width: 768px) {
  .card .poster { --pv: .76; }
  .card-hero .poster { --pv: 1.2; }
  .swipe-card .swipe-trailer { --pv: 1.6; }
}

/* ---------------------------------------------- ratings, all the same size
   The Rotten Tomatoes chip carried a popcorn emoji and the IMDb one did not,
   so the two sat at different heights and widths in the same row. Fixed box,
   fixed line, the glyph inside it rather than around it. */
.rate-row { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.rate {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  height: 20px; padding: 0 7px; border-radius: var(--r-tag); box-sizing: border-box;
  font: 600 9px/1 var(--mono); letter-spacing: .04em; white-space: nowrap;
  border: 1px solid var(--line);
}
.rate.rt { font-family: var(--mono); }
.rate.rt::first-letter { font-family: var(--emoji); }

/* ------------------------------------------------- a line under a rail card */
.card .blurb, .card-hero .blurb {
  margin-top: 3px; font: 400 10.5px/1.35 var(--sans); color: var(--fg-3);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.card-hero .blurb { font-size: 11.5px; -webkit-line-clamp: 2; }
@media (min-width: 768px) { .card .blurb { font-size: 11px; } }

.wikipedia-credit {
  display: block;
  margin: 5px var(--pad, 0) 0;
  color: var(--fg-4);
  font: 500 9px/1.3 var(--mono);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ------------------------------------------------------ who made the thing */
.credits-block { display: flex; flex-direction: column; gap: 8px; }
.cred { display: grid; grid-template-columns: 92px 1fr; gap: 10px; align-items: baseline; }
.cred-l { font: 600 9.5px/1.4 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--fg-4); }
.cred-v { font: 400 13.5px/1.45 var(--sans); color: var(--fg); }

/* The opening step of the tour is still: the card sits there while the words
   explain what the whole thing is for. */
.demo.step-none .demo-hand { opacity: 0; }
.demo.step-none .demo-card { animation: tour-breathe 4s ease-in-out infinite; }
@keyframes tour-breathe {
  0%, 100% { transform: translate(0,0) rotate(0); }
  50%      { transform: translate(0,-6px) rotate(-1deg); }
}

/* ------------------------------------------------------ setting up: the plan */
/* A step reads down the page, the way the walkthrough does: the number and the
   words, then the screen underneath at a size somebody can actually see. It was
   a 74 px sliver in a third column, which is a thumbnail of a thumbnail. */
/* Capped, because nothing else caps it. The landing page holds its steps to
   1040px; this column had only the screen's own 1220px, so on a wide monitor
   the instructions ran the full width of the window and the clip sat flush
   against the right-hand boundary of its own row — measured at 1512px, the
   shot's right edge and the step's right edge were the same pixel. A setup
   flow is one column of instructions and reads better narrow. */
.intro-step { display: grid; grid-template-columns: 26px 1fr; gap: 12px 14px; align-items: start; margin-top: 26px;
  max-width: 660px; margin-left: auto; margin-right: auto; }
.intro-step > .intro-shot { grid-column: 1 / -1; justify-self: center; }
.intro-shot {
  width: 100%; max-width: 190px; margin-top: 12px;
  aspect-ratio: 430 / 932; border-radius: var(--r-card); overflow: hidden;
  background: var(--art-black); border: 1px solid var(--line-strong);
  box-shadow: 0 18px 34px -22px var(--shadow-strong);
}
.intro-shot video { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 768px) {
  /* Still underneath, just larger — a 190px clip on a laptop is a thumbnail of
     a thumbnail, which is what the operator saw the first time. */
  .intro-shot { max-width: 240px; margin-top: 16px; }
}
.intro-n {
  width: 26px; height: 26px; border-radius: var(--r-round); display: grid; place-content: center;
  background: var(--accent); color: var(--accent-ink); font: 600 12px/1 var(--mono);
}
.intro-t { font: 500 15px/1.3 var(--sans); color: var(--fg); }
.intro-note { margin-top: 4px; font: 400 12.5px/1.5 var(--sans); color: var(--fg-3); }

/* =========================================================================
   THE WALKTHROUGH

   The first page anybody sees. A bar that stays put with the way in, a
   sentence about what this is, then four steps, each with a few seconds of
   the real app doing that exact thing. Phone-shaped, because that is the
   shape it was recorded in and the shape most people will use it in.
   ========================================================================= */
/* The front page: one screen, as drawn (2026-10-01). White, near-black ink,
   one blue, hairlines; the film in a flat grey box behind a play button; the
   way in fixed at the bottom. The muted inks are the app's own (--fg-2..4),
   which clear 4.5:1; the drawing's .4 and .55 do not. */
.landing { background: var(--bg); }
.lp-bar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px var(--pad); background: var(--surface);
  border-bottom: 1px solid var(--line); backdrop-filter: blur(12px);
}
.lp-brand { display: flex; align-items: center; gap: 9px; font: 700 19px/1 var(--sans); letter-spacing: -.02em; color: var(--fg); }
.lp-logo { width: 26px; height: 26px; border-radius: var(--r-badge); display: block; }
.lp-bar-actions { display: flex; align-items: center; gap: 6px; }
.lp-signin { padding: 9px 12px; }

.lp-hero { width: 100%; max-width: 560px; margin: 0 auto; padding-top: 22px; padding-bottom: 8px; }
.lp-title { margin: 0; font: 700 clamp(34px, 9.5vw, 38px)/1.04 var(--sans); letter-spacing: -.03em; text-wrap: balance; }
.lp-sub { margin: 14px 0 0; font: 400 15px/1.5 var(--sans); color: var(--fg-2); text-wrap: pretty; }
.lp-services { margin: 12px 0 0; font: 500 9.5px/1.5 var(--mono); letter-spacing: .05em; color: var(--fg-4); }

.lp-video {
  position: relative; margin-top: 18px; aspect-ratio: 16 / 9; border-radius: var(--r-lg); overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
}
.lp-video video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; background: var(--surface-2); }
.lp-play {
  position: absolute; left: 50%; top: 50%; width: 52px; height: 52px; margin: -26px 0 0 -26px;
  border-radius: 50%; background: var(--surface); border: 1px solid var(--line); color: var(--fg);
  font: 400 18px/1 var(--sans); padding: 0 0 0 3px; display: flex; align-items: center; justify-content: center;
}
.lp-video-tag { position: absolute; left: 12px; bottom: 11px; font: 500 9.5px/1 var(--mono); letter-spacing: .08em; color: var(--fg-4); pointer-events: none; }
.lp-video.playing .lp-play, .lp-video.playing .lp-video-tag { display: none; }

.lp-points { list-style: none; margin: 18px 0 0; padding: 0; border-top: 1px solid var(--surface-3); }
.lp-points li { display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--surface-3); }
.lp-n { font: 600 11px/1.5 var(--mono); letter-spacing: .04em; color: var(--accent-text); }
.lp-points li div { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.lp-points b { font: 600 15px/1.2 var(--sans); }
.lp-points li div span { font: 400 13px/1.45 var(--sans); color: var(--fg-3); text-wrap: pretty; }

.lp-close { width: 100%; max-width: 560px; margin: 0 auto; padding-top: 10px; padding-bottom: 24px; }
.lp-policy-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 8px; }
.lp-policy-links .btn.quiet { padding: 10px 8px; }

/* The way in stays on the screen: this page flows with the window (the app
   shell is min-height, not height), so the footer sticks to the bottom edge
   while anything above it scrolls past. */
.lp-foot {
  position: sticky; bottom: 0; z-index: 30;
  display: flex; flex-direction: column; gap: 12px; align-items: stretch;
  padding: 14px var(--pad) calc(env(safe-area-inset-bottom) + 18px);
  border-top: 1px solid var(--surface-2); background: var(--bg);
}
.lp-foot .lp-main { width: 100%; max-width: 560px; margin: 0 auto; padding: 16px; font-weight: 600; }
.lp-caption { margin: 0; text-align: center; font: 500 10px/1.5 var(--mono); letter-spacing: .06em; color: var(--fg-4); }

@media (min-width: 768px) {
  .lp-hero { padding-top: 56px; }
  .lp-title { font-size: 48px; }
  .lp-sub { font-size: 17px; }
}

/* ------------------------------------------- the scale, as a control not a list
   Eleven numbers in a row under two shouted words read as debug output. The
   ends are quiet, the numbers sit on a single track, and the one under the
   finger is the only one that raises its voice. */
.eq { padding: 16px var(--pad) 0; }
.eq-heads { font: 600 9px/1 var(--mono); letter-spacing: .14em; margin-bottom: 10px; color: var(--fg-4); }
.eq-row { gap: 4px; }
.eq-n {
  padding: 13px 0; border-radius: var(--r-card); font: 500 13.5px/1 var(--sans);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--fg-3);
  transition: transform .12s ease, background .12s ease, color .12s ease;
}
.eq-n.lo { color: color-mix(in srgb, var(--lo) 78%, var(--fg-3)); }
.eq-n.hi { color: color-mix(in srgb, var(--hi) 78%, var(--fg-3)); }
.eq-n:hover { transform: translateY(-2px); background: var(--surface-3); color: var(--fg); }
.eq-n.end { font-weight: 600; }
.eq-feet { margin-top: 9px; font: 400 10.5px/1.3 var(--sans); color: var(--fg-4); }

/* The choice boxes: a card with a hierarchy, not two lines of text in a border. */
.choice {
  position: relative; text-align: left; padding: 15px 44px 15px 16px;
  border-radius: var(--r-lg); border: 1px solid var(--line); background: var(--surface-2);
  transition: border-color .14s ease, background .14s ease, transform .14s ease;
}
.choice::after {
  content: '›'; position: absolute; right: 16px; top: 50%; transform: translateY(-50%);
  font: 400 18px/1 var(--sans); color: var(--fg-4);
}
.choice:hover { transform: translateY(-1px); background: var(--surface-3); border-color: var(--line-strong); }
.choice .c-label { display: block; font: 500 15px/1.25 var(--sans); color: var(--fg); }
.choice .c-note { display: block; margin-top: 4px; font: 400 12px/1.4 var(--sans); color: var(--fg-3); }
.choice.ok { border-left: 3px solid var(--ok); }
.choice.no { border-left: 3px solid var(--danger); }
.choice.mid { border-left: 3px solid var(--line-strong); }
.choice-row { display: flex; flex-direction: column; gap: 8px; }


/* The tutorial's card is a card, with a title on it, so it reads as a thing
   somebody is judging rather than a coloured rectangle. */
.demo-title { position: absolute; left: 14px; right: 14px; bottom: 14px; z-index: 2; text-align: left; }
.demo-kind { display: block; font: 600 7.5px/1.3 var(--mono); letter-spacing: .1em; color: var(--art-fg-3); }
.demo-name { display: block; margin-top: 3px; font: 400 17px/1.15 var(--sans); color: var(--art-fg); }
.demo-card { display: block; }

/* Armed: the card lifts out of the rail so the hand knows the hold landed and
   the next movement will be read as an answer. */
.card.armed, .card-hero.armed {
  transform: scale(1.04);
  box-shadow: 0 22px 40px -18px var(--shadow-strong);
  z-index: 5;
}
.card.armed .poster, .card-hero.armed .poster { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-lg); }
.card.armed .card-gauge, .card-hero.armed .card-gauge { opacity: 1; }

/* YouTube sometimes refuses a viewer it has decided to challenge. The frame is
   removed rather than left sitting there, the artwork comes back, and the way
   to watch it anyway is one tap. */
.trailer-blocked > img, .trailer-blocked .swipe-art { opacity: 1 !important; }
.trailer-out {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%); z-index: 5;
  padding: 7px 12px; border-radius: var(--r-tag); text-decoration: none;
  background: var(--art-glass-strong); border: 1px solid var(--art-border); color: var(--art-fg);
  font: 600 10px/1 var(--mono); letter-spacing: .06em; white-space: nowrap;
}

/* A friend's name is the way into their profile, so it looks like one. */
.person-row .info { flex: 1; min-width: 0; text-align: left; background: none; border: 0; padding: 0; }
.person-row .info:hover .t { color: var(--accent-text); }

/* The input keeps focus while arrows move the selected search result. */
.seen-row[aria-selected="true"] {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Keep the live-search target mounted without painting an empty box. */
.home-results:empty { display: none; }

/* A rule with the word "or" sitting in it, between the two ways in. */
.auth-or { display: flex; align-items: center; gap: 12px; color: var(--fg-4); }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.auth-or span { font: 600 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; }

/* "Not saved yet." Quiet until it is not. */
.claim-bar {
  margin: 14px 0; padding: 12px 14px; border: 1px solid var(--line-strong);
  border-radius: var(--r-card); background: var(--surface-2);
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
}
.claim-bar b { display: block; font: 600 13px/1.35 var(--sans); }
.claim-bar .tiny { color: var(--fg-3); }
.claim-bar .claim-actions { margin-left: auto; display: flex; gap: 8px; }
.claim-bar.firm { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface-2)); }
.linkish { background: none; border: 0; padding: 0; color: var(--accent-text); font: inherit; text-decoration: underline; cursor: pointer; }

/* "No opinion." Deliberately the quietest control on the deck: it is the right
   answer for a title somebody genuinely does not know, and the wrong one to
   reach for out of hesitation. */
.skip-title { width: 100%; }

/* The rate-it sheet. The two "haven't seen it" rows were bordered in the ok and
   danger colours, which read as warnings for what are ordinary choices — and
   they competed with the score, which is the thing most people opened this for.
   Quiet rows, one hairline between them, no colour. */
.react-sheet .react-quiet { border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; }
.react-sheet .react-quiet .option-row { border: 0; border-radius: 0; margin: 0; background: transparent; }
.react-sheet .react-quiet .option-row + .option-row { border-top: 1px solid var(--line); }
.react-sheet .react-quiet .option-row:hover { background: var(--surface-2); }
.react-sheet .eyebrow { margin-bottom: 8px; }

/* Together's own action. The tab used to build a merged rail and nothing else,
   while the voting deck was started from Friends behind an unlabelled glyph —
   two features with one name, neither pointing at the other. */
.tog-actions { margin-top: 18px; padding: 0 var(--pad); }
.tog-actions .btn { width: 100%; }
.tog-actions .tiny { margin-top: 8px; color: var(--fg-4); }

/* "Tonight's rules", folded. Four filter chips used to sit above the first card
   — configuration before anyone had seen anything. */
.chip-fold {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--surface-2); color: var(--fg-2); font: 600 12px/1 var(--sans);
}
.chip-fold .cf-n {
  /* The accent's own ink: white on the light skin's blue, near-black on the
     dark skin's lighter blue, where white was 3.68:1. */
  min-width: 18px; height: 18px; line-height: 18px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-ink); font: 700 10px/18px var(--mono); text-align: center;
}

/* Four unlabelled 36px icons per friend, and the last one removed them — a
   destructive action with no name, the same size and shape as three harmless
   ones, immediately beside them. It is still there, but it is set apart by a
   gap and a rule, and everything now has a name for a screen reader. */
.qa.wide { width: auto; padding: 0 12px; gap: 6px; display: inline-flex; align-items: center; }
.qa .qa-t { font: 600 11px/1 var(--sans); letter-spacing: .02em; }
.qa.apart { margin-left: 10px; position: relative; }
.qa.apart::before {
  content: ""; position: absolute; left: -6px; top: 6px; bottom: 6px;
  width: 1px; background: var(--line);
}

/* TMDb's terms require this wording and this placement — visible, not buried in
   a file nobody opens. Quiet, because it is a credit and not a message. */
.attribution { margin-top: 10px; color: var(--fg-4); line-height: 1.45; }

/* The notification offer, where a conversation is — not buried on the You
   screen under copy that promised it would never mention messages. */
.push-offer {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
  border-radius: var(--r-card);
}
.push-offer b { display: block; font: 600 13px/1.35 var(--sans); }
.push-offer .tiny { color: var(--fg-3); }
.push-offer .po-actions { margin-left: auto; display: flex; gap: 8px; }
/* A thread with something waiting in it should look different from one without. */
.option-row.thread.has-unread span:first-child { font-weight: 600; }
.option-row.thread .navdot { position: static; }
.theme-score.armed { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.theme-score.armed .theme-source { font-size: 0; }
.theme-score.armed .theme-source::after { content: "SLIDE TO ADJUST"; font-size: 8px; }

/* ------------------------------------------------------------ comments
   What people on Huddelo say about a title. A quiet list, not a feed: the
   writer, their own score, the words, and one small action. Long words and
   long names wrap or clip inside their row, never over the next one. */
.comments .eyebrow { margin-bottom: 10px; }
.comment { padding: 12px 0; border-top: 1px solid var(--line); }
.comment:first-of-type { border-top: 0; }
.comment.held { opacity: .7; }
.comment-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.comment-avatar { width: 28px; height: 28px; font-size: 11px; flex: none; }
.comment-who { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; font: 500 14px/1.3 var(--sans); color: var(--fg); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.comment-handle { font: 400 12px/1.3 var(--mono); color: var(--fg-4); overflow: hidden; text-overflow: ellipsis; }
.comment-score { flex: none; padding: 3px 8px; border-radius: var(--r-tag); font: 600 11px/1 var(--mono); background: var(--surface-2); border: 1px solid var(--line); color: var(--fg); }
.comment-score.ok { color: var(--ok); }
.comment-score.no { color: var(--danger); }
.comment-body { margin: 8px 0 0; font: 400 14px/1.55 var(--sans); color: var(--fg-2); white-space: pre-wrap; overflow-wrap: anywhere; }
.comment-foot { display: flex; align-items: center; gap: 14px; margin-top: 8px; }
.comment-date { font-size: 11px; color: var(--fg-4); margin-right: auto; }
.comment-reported, .comment-held, .comment-empty { font: 400 12px/1.5 var(--sans); color: var(--fg-4); }
.comment-held { margin: 6px 0 0; }
.comment-empty { margin: 6px 0 0; }
.comment-form { margin: 4px 0 8px; }
.comment-form textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 84px;
  padding: 12px 14px; border-radius: var(--r-card); border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg); font: 400 14px/1.5 var(--sans);
}
.comment-form textarea:focus { outline: 2px solid color-mix(in srgb, var(--accent-text) 55%, transparent); outline-offset: 1px; }
.comment-form-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.comment-count { margin-right: auto; font-size: 11px; color: var(--fg-4); }
.comment-all { width: 100%; margin-top: 8px; }

/* ------------------------------------------------ the design pass, round 3
   The operator's design (07 title, 08 group, 09 thread, 10 chats), drawn in
   the tokens both skins already have. Only what the data can back is drawn:
   no online dots, no typing indicator, no per-person scores. */

/* Title: the name and its facts on the left, the match as a ring on the right. */
.detail-hero > div.detail-head {
  width: 100%; max-width: none;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 14px;
}
.detail-head-text { flex: 1; min-width: 0; }
.detail-head .h1 { font-size: 28px; line-height: 1.1; overflow-wrap: anywhere; }
.detail-head .detail-meta { margin-top: 9px; line-height: 1.5; }
@media (min-width: 768px) {
  .detail-head .h1 { font-size: 36px; }
  .detail-head-text { max-width: 760px; }
}
.score-ring {
  flex: none; width: 60px; height: 60px; border-radius: var(--r-round);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: 1.5px solid var(--accent-text);
  background: color-mix(in srgb, var(--bg) 82%, transparent); backdrop-filter: blur(6px);
}
.score-ring b { font: 600 20px/1 var(--sans); color: var(--accent-text); letter-spacing: -.02em; }
.score-ring span { font: 600 7px/1.15 var(--mono); letter-spacing: .1em; color: var(--accent-text); text-transform: uppercase; max-width: 50px; text-align: center; overflow-wrap: anywhere; }

/* The facts under the rating, and the themes, as the design's outlined pills. */
.meta-row .chip, .detail-themes .chip {
  padding: 7px 12px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-text) 28%, transparent);
  color: var(--accent-text); max-width: 100%; overflow-wrap: anywhere;
}
.detail-themes .chip { font: 400 13px/1.2 var(--sans); letter-spacing: 0; }
.meta-row .chip.accent { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

/* Where to watch: one card per service, the service's tile, its name, what it
   costs, and the action. */
.avail-row {
  padding: 15px 16px; margin-bottom: 10px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
}
.avail-row.included { background: var(--surface); border-color: color-mix(in srgb, var(--accent-text) 30%, transparent); }
.avail-row .prov { width: 44px; height: 44px; border-radius: var(--r-card); background: var(--surface-2); text-align: center; overflow: hidden; }
.avail-row .name { font: 500 16px/1.2 var(--sans); }
.avail-row .name > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avail-row .cta { font: 600 12px/1 var(--mono); letter-spacing: .08em; }

/* Who is watching: bigger faces, the chosen ones filled in. */
.people-row { gap: 12px; padding-top: 8px; padding-bottom: 4px; }
.person { width: 70px; }
.person .avatar { width: 60px; height: 60px; font-size: 20px; border-width: 1.5px; }
.person .avatar.alt { background: transparent; border-color: var(--line-strong); color: var(--fg-3); }
.person.on .avatar, .person.on .avatar.alt { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.overlap-note {
  display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); align-items: center; gap: 8px 16px;
  padding: 18px 16px; border-radius: var(--r-lg); border: 0;
  background: var(--surface-2); font-size: 13px; line-height: 1.5;
}
.overlap-note .stat { font-size: 11.5px; letter-spacing: .08em; }
.overlap-note .group-relaxations { grid-column: 1 / -1; }
@media (max-width: 359px) { .overlap-note { grid-template-columns: minmax(0, 1fr); } }
/* The narrowest phones give the thread's name the room the faces took. */
@media (max-width: 359px) { .chat-top .chat-avatars { display: none; } }

/* Chats: one list, newest first. */
.chats-title { font-size: 30px; font-weight: 700; letter-spacing: -.025em; }
.chats-search { border-color: transparent; border-radius: var(--r-lg); padding: 13px 16px; }
.chat-list { display: flex; flex-direction: column; margin-top: 8px; }
.chat-row {
  display: flex; align-items: center; gap: 14px; width: 100%; min-height: 76px;
  padding: 10px var(--pad); background: transparent; border: 0; text-align: left; color: var(--fg);
}
.chat-row[hidden] { display: none; }
.chat-row:active { background: var(--surface-2); }
.chat-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.chat-name { font: 500 16px/1.2 var(--sans); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.chat-preview { font: 400 14px/1.3 var(--sans); color: var(--fg-3); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.chat-preview.quiet { color: var(--fg-4); font-size: 13px; }
.chat-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 7px; align-self: flex-start; padding-top: 4px; }
.chat-time { font: 500 10.5px/1 var(--mono); color: var(--fg-4); white-space: nowrap; }
.chat-row.has-unread .chat-name { font-weight: 600; }
.chat-row.has-unread .chat-time { color: var(--accent-text); }
.chat-badge {
  min-width: 26px; height: 20px; padding: 0 8px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-ink);
  font: 600 11px/20px var(--mono); text-align: center;
}
.chat-row .tick { color: var(--accent-text); font: 600 13px/1 var(--mono); }
.chat-avatars { position: relative; flex: none; width: 52px; height: 52px; }
.chat-avatars .avatar { position: absolute; }
.chat-avatars.n1 .avatar { inset: 0; width: 52px; height: 52px; font-size: 18px; }
.chat-avatars.n2 .avatar { width: 34px; height: 34px; font-size: 12px; }
/* The face behind shows its letter in the corner the front one leaves free. */
.chat-avatars.n2 .avatar:first-child { left: 0; top: 0; padding: 0 5px 5px 0; }
.chat-avatars.n2 .avatar:last-child { right: 0; bottom: 0; box-shadow: 0 0 0 2px var(--bg); }

/* A thread's header: the faces, the name, how many are in it. */
.topbar.chat-top { justify-content: flex-start; gap: 10px; }
.chat-top .chat-avatars { width: 58px; height: 36px; }
.chat-top .chat-avatars.n1 { width: 36px; }
.chat-top .chat-avatars.n1 .avatar { width: 36px; height: 36px; font-size: 13px; }
.chat-top .chat-avatars.n2 .avatar { width: 36px; height: 36px; top: 0; bottom: auto; padding: 0; }
.chat-top .chat-avatars.n2 .avatar:last-child { left: 22px; right: auto; }
.chat-top-name { flex: 1; min-width: 0; }
.chat-top-name .h3 { font-size: 16px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.chat-top-sub { margin-top: 4px; font: 600 9.5px/1 var(--mono); letter-spacing: .1em; color: var(--accent-text); text-transform: uppercase; }

/* A shared title: open it, or put it on tonight's list or for later. */
.share-card { display: block; position: relative; width: min(300px, 100%); padding: 0; }
.share-open { display: flex; gap: 12px; width: 100%; padding: 11px; text-align: left; background: transparent; border: 0; color: var(--fg); }
.share-open .poster { flex: none; width: 60px; height: 86px; border-radius: var(--r-tile); }
.share-open .info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; justify-content: flex-start; padding: 2px 0 44px; }
.share-open .info .t { overflow-wrap: anywhere; }
/* The buttons sit under the words, beside the poster, as drawn. In the flow and
   pulled up into the room the words leave, rather than pinned: a long label in
   Polish or Finnish wraps to a second row and the card grows, and the logical
   margins put them on the correct side in Arabic. */
.share-actions {
  display: flex; flex-wrap: wrap; gap: 6px; position: relative;
  margin-block: -44px 11px; margin-inline: 83px 11px;
}
.btn.small {
  padding: 8px 12px; min-height: 32px; font-size: 13px; border-radius: var(--r-card);
  flex: 0 1 auto; min-width: 0; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The composer: share a title, say something, send. */
.composer-add {
  flex: none; width: 42px; height: 42px; border-radius: var(--r-round);
  display: grid; place-content: center; background: transparent;
  border: 1px solid var(--line-strong); color: var(--fg-2); font: 400 20px/1 var(--sans);
}
.composer .field { border-color: transparent; min-width: 0; text-overflow: ellipsis; }
.composer .field::placeholder { font-size: 14px; }
.share-results .option-row > span:first-child { min-width: 0; overflow-wrap: anywhere; }

/* Words longer than their box wrap or clip in their own box. At 320px the
   four tab names had 56px each, and ZUSAMMEN and FREUNDE printed as one word. */
@media (max-width: 380px) {
  .nav-inner { padding-left: 10px; padding-right: 10px; }
  .nav button { letter-spacing: .03em; min-width: 0; }
}
/* Words longer than their box wrap or clip in their own box. */
.stamp { white-space: nowrap; }
/* A label on a poster never runs off the poster, in any language. */
.badge, .price-pill, .needs-pill, .free-pill, .urgency-pill {
  max-width: calc(100% - 16px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-sizing: border-box;
}

/* ------------------------------------------------ the score under a rail card
   A cell holds the card and its score; the track lays out cells as it laid out
   cards. Subtle: a hairline and a small handle, the number only once given. */
.card-cell { flex: none; display: flex; flex-direction: column; gap: 2px; }
.rail-rate {
  display: flex; align-items: center; gap: 7px; height: 26px; padding: 0 2px;
  cursor: pointer; -webkit-tap-highlight-color: transparent; user-select: none; -webkit-user-select: none;
}
.rr-track { position: relative; flex: 1; height: 2px; border-radius: var(--r-bar); background: var(--line-strong); }
.rr-fill { position: absolute; left: 0; top: 0; bottom: 0; width: calc(var(--v) * 10%); border-radius: var(--r-bar); background: var(--fg-4); }
.rail-rate.ok .rr-fill { background: var(--ok); }
.rail-rate.no .rr-fill { background: var(--lo); }
.rr-thumb {
  position: absolute; top: 50%; left: calc(var(--v) * 10%); width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: var(--r-round); background: var(--surface); border: 2px solid var(--fg-3);
  touch-action: none;
}
/* The handle is small to see and large to hit: the hit area is the pseudo
   element, and a touch that lands on it refuses the rail's scroll. */
.rr-thumb::before { content: ''; position: absolute; inset: -11px; border-radius: var(--r-round); }
.rail-rate.ok .rr-thumb { border-color: var(--ok); }
.rail-rate.no .rr-thumb { border-color: var(--lo); }
.rail-rate.pending .rr-fill { opacity: 0; }
.rail-rate.pending .rr-thumb { opacity: .45; }
.rr-n { min-width: 14px; font: 600 11px/1 var(--mono); color: var(--fg-2); text-align: right; }
.rail-rate:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; border-radius: var(--r-tag); }

/* ------------------------------------------------ setup after the swipes
   A theme no seen title answered is shown as a guess, not as something learned;
   the accuracy screen leads with one number and says what it is. */
.theme-score.unknown { opacity: .62; }
.theme-score.unknown .theme-source { color: var(--fg-4); }
.acc-ring {
  width: 150px; height: 150px; margin: 22px auto 0; border-radius: var(--r-round);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border: 3px solid var(--line-strong);
}
.acc-ring b { font: 700 40px/1 var(--sans); letter-spacing: -.03em; color: var(--fg); }
.acc-ring span { font: 600 9px/1.2 var(--mono); letter-spacing: .1em; color: var(--fg-3); text-align: center; max-width: 110px; }
.acc-ring.good { border-color: var(--ok); }
.acc-ring.good b { color: var(--ok); }
.acc-ring.fair { border-color: var(--accent-text); }
.acc-ring.low { border-color: var(--lo); }
.acc-how { margin-top: 14px; text-align: center; text-wrap: pretty; }
.acc-known { margin-top: 18px; display: flex; align-items: baseline; justify-content: center; gap: 8px; font: 400 14px/1.4 var(--sans); color: var(--fg-2); flex-wrap: wrap; text-align: center; }
.acc-known b { font: 600 18px/1 var(--mono); color: var(--fg); }
.acc-list { margin-top: 22px; }
.acc-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
.swipe-card.setup-done { cursor: default; }

/* Batch AN: the people a profile already has an answer about. */
.person-row .pmine { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 4px 0 6px; }
.chip.learned b, .chip.set b { margin-left: 4px; }
.chip.set { border-color: var(--accent-text); }
.me-people { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }

/* Batch AP: take a rating back — Undo on the toast, "Forget" on a title page. */
.toast-undo {
  margin-left: 12px; padding: 8px 6px; min-height: 32px;
  background: none; border: 0; color: var(--bg);
  font: 600 13px/1 var(--sans); text-decoration: underline; cursor: pointer;
}
.forget-rating {
  display: block; margin-top: 10px; padding: 8px 0;
  background: none; border: 0; color: var(--fg-3);
  font: 500 13px/1.4 var(--sans); text-decoration: underline; cursor: pointer;
}

/* Batch AV6: what friends are watching, and what counts on the Activity tab. */
/* Five tabs on a phone. Each gets a fifth of the bar, so the bar's own inset
   gives way, and a label never grows past what its column holds: at a larger
   text size, or 320 px wide, a tab name shrinks rather than running into its
   neighbour (ZUSAMMENFREUNDE, batch AC). */
@media (max-width: 767px) {
  .nav-inner { padding-left: 10px; padding-right: 10px; container-type: inline-size; }
  .nav button { font-size: min(calc(var(--t-nav) * var(--fs)), 3.3cqi); }
}
.btn.act-prefs-open { padding: 0; margin-top: 12px; justify-content: flex-start; text-decoration: underline; }
.act-prefs { margin-top: 18px; }
.act-prefs .option-row { padding-left: 0; padding-right: 0; }
.act-prefs .option-row[disabled] { opacity: .45; }
.act-prefs .option-row.sel { color: inherit; }
.act-note { margin-top: 2px; margin-bottom: 8px; color: var(--fg-4); }
.act-switch {
  flex: none; position: relative; width: 34px; height: 20px; border-radius: var(--r-round);
  background: var(--surface-3); border: 1px solid var(--line-strong); transition: background .15s;
}
.act-switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
  border-radius: var(--r-round); background: var(--fg-3); transition: transform .15s;
}
.act-pref.sel .act-switch { background: var(--accent); border-color: transparent; }
.act-pref.sel .act-switch::after { transform: translateX(14px); background: var(--accent-ink); }
[dir="rtl"] .act-pref.sel .act-switch::after { transform: translateX(-14px); }
[dir="rtl"] .act-switch::after { left: auto; right: 2px; }
.act-list { margin-top: 18px; }
.act-row { display: flex; align-items: flex-start; gap: 12px; padding: 14px var(--pad); border-top: 1px solid var(--line); }
.act-row.unseen { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.act-face { flex: none; padding: 0; background: none; border: 0; cursor: pointer; }
.act-face .avatar { width: 40px; height: 40px; font-size: 15px; }
.act-main { flex: 1; min-width: 0; }
.act-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.act-who {
  min-width: 0; padding: 0; background: none; border: 0; cursor: pointer; text-align: start;
  font: 600 15px/1.25 var(--sans); color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.act-new { flex: none; width: 8px; height: 8px; border-radius: var(--r-round); background: var(--accent); }
.act-when { flex: none; margin-inline-start: auto; font: 500 10px/1 var(--mono); letter-spacing: .04em; color: var(--fg-4); white-space: nowrap; }
.act-what { margin-top: 3px; font: 400 13.5px/1.35 var(--sans); color: var(--fg-2); }
.act-score { margin-inline-start: 8px; font: 600 11px/1 var(--mono); color: var(--accent-text); white-space: nowrap; }
.act-titles { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; margin-top: 10px; }
.act-title {
  display: flex; flex-direction: column; gap: 5px; width: 62px; padding: 0;
  background: none; border: 0; cursor: pointer; text-align: start; color: var(--fg);
}
.act-title .poster { width: 62px; height: 88px; border-radius: var(--r-card); }
.act-title .poster .poster-name { font-size: 8px; padding-bottom: 6px; }
.act-name { font: 400 11px/1.25 var(--sans); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.act-titles.one .act-title { flex-direction: row; align-items: center; gap: 12px; width: 100%; }
.act-titles.one .act-name { font-size: 14px; }
.act-titles .act-score { margin-inline-start: 0; }
.act-more { align-self: center; font: 600 12px/1 var(--mono); color: var(--fg-3); }
.act-body { margin-top: 8px; font: 400 13.5px/1.45 var(--sans); color: var(--fg-2); overflow-wrap: anywhere; }
.act-empty p + p { margin-top: 6px; }
