@layer reset, tokens, base, components, utilities;

@layer tokens {

:root {
  color-scheme: dark light;
  accent-color: var(--accent);
  --bg: #0a0a0b;
  --surface-1: #131315;
  --surface-2: #1a1a1d;
  --ink: #f7f8f8;
  --ink-muted: #8a8f98;
  --ink-subtle: #62666d;
  /* The accent carries THREE jobs, not one, and until 2026-10-01 it was asked to
     do two of them at once with a single value -- which is why it could not pass.

     --accent       graphics and glyphs, floor 3:1 (icons, arrows, dots, borders,
                    focus rings). Emerald, hue 155.
     --accent-focus the search box's outer ring, one step further from the text.
     --accent-fill  a FILL that --ink is painted ON, floor 4.5:1: the four
                    :active buttons and .skip-link. It must be DARK in the dark
                    theme (--ink is near-white there) and LIGHT in the light one
                    (--ink is black), so it needs a value per theme.

     The split is not stylistic. One value for both roles in both themes is
     impossible, and that was searched rather than argued: an exhaustive sweep of
     360 hue x 12 saturation x 71 lightness returned 0 of 306720 candidates. The
     old #5e6ad2 was not an exception -- it measured 4.42:1 dark and 4.47:1 light
     as a fill, so .skip-link and every :active button were ALREADY under the
     4.5:1 text floor before any hue was chosen. Same shape as --telegram, --kp
     and --imdb, which are all a value per theme for the same reason.

     Hue: emerald sits 46deg from the nearest occupied hue (--telegram, 200deg).
     The old #5e6ad2 sat 18deg from --max (252deg), inside the range where a
     second colour reads as a slightly wrong version of the first; --telegram and
     --max appear as glyphs in the header on every page. The brands are not
     touched: --kp, --imdb, --telegram, --max, --signal and --rating each have
     their own token and none of them reads --accent.

     Measured, worst of the four backgrounds a 1px stroke can land on (body
     carries a 1px dot grid on a 20px step, so a dot pixel is a real surface:
     rgb(22,22,23) dark, rgb(228,229,229) light):

       dark   --accent        5.15:1   --accent-focus  6.25:1
       light  --accent        5.17:1   --accent-focus  6.37:1
       dark   --accent-fill  11.49:1 against --ink #f7f8f8
       light  --accent-fill  12.23:1 against --ink #000000 */
  --accent: #2f9e6d;
  --accent-focus: #3fae7d;
  --accent-fill: #0e3d29;
  --rating: #ff6b6b;
  --line: #23252a;
  --line-strong: #34343a;
  --line-control: #62646c;
  --focus: #f2eee8;
  --kp: #ff5500;
  --imdb: #f6c600;
  --signal: #ff6b6f;
  /* The Telegram brand blue, and the same shape of decision as --kp/--imdb one
     line up: the brand hex verbatim in the dark theme, a darker value of the SAME
     hue in the light one, because the brand hex cannot carry text on a light
     surface. Measured against the rendered header, label and glyph together,
     worst of the label's own background and `body` (which carries a 1px dot grid,
     so the two are different colours and the worse one is what a reader can hit):

       #26a5e4  dark  7.15:1   light  2.56:1   FAILS 4.5 text AND 3.0 glyph
       #156f9f  dark  3.59:1   light  5.10:1   passes 3.0 in both, 4.5 in light

     So: brand in dark, #156f9f in light. Hue 200deg against the brand's 200deg --
     a value shift, not a different colour, which is what --kp/#c74000 does. The
     same hex the telegram SHARE button already uses on hover
     (`.share-btn[data-net="telegram"]:hover .share-ico { color: #26a5e4 }`), so
     the brand value is the one already in this stylesheet rather than a new one
     brought in from outside. */
  --telegram: #26a5e4;
  /* The MAX brand colour, and it is ONE value for both themes rather than the
     pair --telegram needs, because here the pair has nothing to solve. MAX
     ships a GRADIENT, not a hex, so "the brand colour" was a judgement and the
     three stops were measured rather than picked. The gradient runs #44ccff
     (cyan) -> #5533ee (blue) -> #9933dd (violet), and every one of them fails
     a floor in one theme or the other:

       #44ccff  dark 10.67:1   light  1.71:1   FAILS light
       #5533ee  dark  2.92:1   light  6.27:1   FAILS dark
       #9933dd  dark  3.65:1   light  5.01:1   passes both

     Walking VALUE up the mid-stop hue with hue and saturation held fixed tops
     out at 3.19:1, because that hue has no more headroom on a near-black
     ground: the first value that technically clears the 3:1 floor clears it by
     0.01, which is a value sitting ON the floor rather than above it. So this
     is the decision --telegram already makes, pointed the other way: preserve
     the brand hue, shift the value to clear the floor on the ground it fails
     on. --telegram darkens for light; this lightens for dark.

       #6743f1  dark  3.44:1   light  5.31:1   clears 3:1 glyph in BOTH

     Hue 252deg against the mid-stop's 251deg: a 1.5deg shift, the same size as
     --telegram's documented 201 against 200. Declared once and overridden in
     NEITHER theme, like --on-media, because one value passes both; a second
     declaration would be a value shipped without a defect to justify it. */
  --max: #6743f1;
  /* Text that sits on photographic media (the featured card's scrim), not on a
     themed surface. Declared once and overridden in NEITHER theme: --ink is
     dark ink in the light theme and would be invisible on a black gradient, so
     a themed token is the wrong name for this role. It is a distinct value from
     every other token here -- --focus is #f2eee8 and --ink is #f7f8f8 in the
     dark theme -- so it is a new value, not a second name for either. */
  --on-media: #ffffff;
  /* The film badge is a LIGHT plate carrying DARK text, and it sits on a
     photograph rather than on a themed surface, so it is the --on-media
     precedent inverted: same one-value-in-both-themes shape, opposite polarity.
     It does not use --accent-fill, which is dark in the dark theme precisely
     because --ink is near-white there. The library decided this: 156 poster
     masters, luminance min 9 / p10 38 / median 72 / p90 139 / max 212, measured
     with Pillow over static/posters. A badge has to be light to keep its edge
     against a dark photograph, and its own ink has to be dark because there is
     no --ink that is dark in BOTH themes. Against the median poster this plate
     reads 6.07:1 against 1.95:1 for the #5e6ad2 it replaces; against the three
     brightest masters (luminance >= 200, 1.9% of the library) it reads 1.02:1
     where the old value read 3.17:1. That trade is deliberate and recorded: the
     badge wins on 153 of 156 and loses on 3. --on-media is #ffffff and --ink is
     #f7f8f8 in the dark theme, so #101412 is a new value and not a second name
     for either. */
  --media-plate: #85e5bd;
  --media-plate-ink: #101412;
  --paper: #131315;
  --th-bg: rgba(247, 248, 248, 0.04);
  --glass: rgba(19, 19, 21, 0.74);
  --glass-card: rgba(19, 19, 21, 0.92);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
  --hover: rgba(247, 248, 248, 0.07);
  --dot: rgba(247, 248, 248, 0.05);
  --fs-xs: 0.75rem;
  --fs-sm: 0.875rem;
  --fs-md: 1rem;
  --fs-lg: 1.125rem;
  --fs-xl: 1.5rem;
  --fs-display: 3rem;
  --tap: 44px;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;
  --space-8: 3rem;
  --space-9: 4.5rem;
  --space-0-2: 0.2rem;
  --space-0-35: 0.35rem;
  --space-1-25: 1.25rem;
  --space-2-25: 2.25rem;
  /* radii, one token per distinct value the stylesheet uses, ascending by name */
  --radius-xs: 4px;
  --radius-s: 6px;
  --radius-m: 8px;
  --radius-l: 12px;
  --radius-xl: 16px;
  --radius-circle: 50%;
  /* container */
  --container: 1280px;
  --measure: 68ch;
  /* z-index scale; .poster-modal is a top-layer dialog, so its z-index is a
     declaration of intent rather than the mechanism that keeps it on top */
  --z-raised: 100;
  --z-modal: 1000;
  --z-skip: 1200;
  /* focus ring */
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
  --focus-ring-offset-inset: -4px;
  --focus-ring-color: var(--accent);
  --focus-ring-color-ink: var(--focus);
  /* motion */
  --transition-fast: 0.15s;
}

@media (prefers-color-scheme: dark) {
  :root:not(.light):not(.dark) {
    color-scheme: dark;
    --bg: #0a0a0b;
    --surface-1: #131315;
    --surface-2: #1a1a1d;
    --ink: #f7f8f8;
    --ink-muted: #8a8f98;
    --ink-subtle: #62666d;
    --accent: #2f9e6d;
    --accent-focus: #3fae7d;
    --accent-fill: #0e3d29;
    --rating: #ff6b6b;
    --line: #23252a;
    --line-strong: #34343a;
    --line-control: #62646c;
    --focus: #f2eee8;
    --kp: #ff5500;
    --imdb: #f6c600;
    --signal: #ff6b6f;
    --paper: #131315;
    --th-bg: rgba(247, 248, 248, 0.04);
    --glass: rgba(19, 19, 21, 0.74);
    --glass-card: rgba(19, 19, 21, 0.92);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
    --hover: rgba(247, 248, 248, 0.07);
    --dot: rgba(247, 248, 248, 0.05);
  }
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../static/fonts/ubuntu-cyrillic-400-normal.woff2") format("woff2");
  unicode-range: U+0020, U+00A0, U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../static/fonts/ubuntu-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303, U+0309, U+0323, U+2002, U+2009, U+200B, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2032-2033, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../static/fonts/ubuntu-cyrillic-500-normal.woff2") format("woff2");
  unicode-range: U+0020, U+00A0, U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../static/fonts/ubuntu-latin-500-normal.woff2") format("woff2");
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303, U+0309, U+0323, U+2002, U+2009, U+200B, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2032-2033, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../static/fonts/ubuntu-cyrillic-700-normal.woff2") format("woff2");
  unicode-range: U+0020, U+00A0, U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../static/fonts/ubuntu-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303, U+0309, U+0323, U+2002, U+2009, U+200B, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2032-2033, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}

:root.light {
  color-scheme: light;
  --bg: #f5f6f6;
  --surface-1: #ffffff;
  --surface-2: #f6f7f7;
  --ink: #000000;
  --ink-muted: #62666d;
  --ink-subtle: #8a8f98;
  --telegram: #156f9f;
  --accent: #0e6b48;
  --accent-focus: #0a5c3d;
  --accent-fill: #7fd7ae;
  --rating: #c92c34;
  --line: #e5e6e8;
  --line-strong: #c9cbce;
  --line-control: #8a8d94;
  --focus: #171923;
  --kp: #c74000;
  --imdb: #8a6d00;
  --signal: #d9363e;
  --paper: #ffffff;
  --th-bg: rgba(0, 0, 0, 0.035);
  --glass: rgba(255, 255, 255, 0.72);
  --glass-card: rgba(255, 255, 255, 0.92);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.10);
  --hover: rgba(0, 0, 0, 0.06);
  --dot: rgba(0, 0, 0, 0.07);
}
}

@layer reset {

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

@layer base {

/* No `scroll-padding-top` here, and the absence is the point. It existed only
   to keep a nav target from landing under the sticky header; with the header no
   longer sticky, an offset would push every section down the viewport for no
   reason. `verify.py` asserts the property is absent, and `js/app.js` no longer
   measures a header. */
html {
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
  container-type: scroll-state;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Ubuntu", "Segoe UI", sans-serif;
  font-size: var(--fs-md);
  line-height: 1.5;
  background: var(--bg);
  background-image: radial-gradient(circle at 1px 1px, var(--dot) 1px, transparent 0);
  background-size: 20px 20px;
  color: var(--ink);
}

a {
  color: inherit;
}

h1,
h2 {
  line-height: 1.2;
}

#app {
  max-width: var(--container);
  margin: 0 auto;
  padding: clamp(1rem, 1rem + 2vw, 2.25rem) clamp(0.75rem, 0.75rem + 2vw, 1.25rem);
}
}

@layer utilities {

#app[v-cloak] > :not(noscript) {
  visibility: hidden;
}

#app[v-cloak] .brand {
  visibility: visible;
}

#app[v-cloak] noscript {
  visibility: visible;
}

html:not(.js) #app[v-cloak] nav.nav:not(.nav--dialog) {
  visibility: visible;
}

.pre-mount {
  display: none;
}

#app[v-cloak] span.pre-mount {
  display: inline;
}

#app[v-cloak] p.pre-mount {
  display: block;
}

#app[v-cloak] .post-mount {
  display: none;
}
}

@layer components {

/* The header does not stick. Partner ruling, 2026-09-30: a sticky header is
   not wanted. Removing it is not a three-line deletion -- `position: sticky`
   was the only reason three other mechanisms existed, and each one is now
   load-bearing on its absence:

   - `scroll-padding-top: var(--header-offset)` parked every nav target below
     the viewport top by the header's height. With no header over the content
     that offset only pushed sections down, so the token, the token's CSS
     fallback, the ResizeObserver in app.js and the 5rem default are all gone.
   - the `backdrop-filter: blur(12px)` and `--glass` background existed to make
     content readable *through* a translucent bar. Static, they are just a
     tinted box over the page background, so the header is opaque now.
   - `z-index: var(--z-raised)` kept the bar above the scrolling table.

   What stays is the layout: the header is still the first block in `.wrap`, so
   the page still opens with the logo and the controls, and every control keeps
   its size and its tap target. */

/* The panel is gone too, and that is a second ruling rather than a consequence
   of the first. `.top` painted `background: var(--surface-1)`, which is not the
   page colour in either theme (dark #131315 on #0a0a0b, light #ffffff on
   #f5f6f6), so the header read as a raised block. Removing the declaration does
   NOT reveal a flat `--bg`: `body` carries a 1px dot grid at 20px
   (`background-image: radial-gradient(circle at 1px 1px, var(--dot) 1px,
   transparent 0)`), and `.top` was the only paint masking it at the top of the
   page. A dot pixel composites to rgb(22,22,23) dark and rgb(228,229,229)
   light.

   Measured over both arms on the same run, 4 pages, every icon in the document
   and every text element inside the header (`_tmp/probe_header_nobg.py`):
   the 8 icons painted directly on the header go 3.95 -> 4.02 dark and
   4.70 -> 4.60 light; the tightest header text (16px/400, the subtitle and
   the RU label) goes 5.71 -> 5.82 dark and 5.77 -> 5.33 light; the brand h1
   (25.6px/700) 17.44 -> 18.6 and 21 -> 19.4. So the panel was worth up to
   +0.44:1 and nothing crosses a floor -- the tightest figure anywhere in the
   header afterwards is 4.60 (icons, light, floor 3) and 5.33 (16px text, light,
   floor 4.5). Light is the losing theme in both cases because `--surface-1` is
   white there and `--bg` is not.

   The dots are 1px and cannot host a whole icon, but a 1px stroke can land on
   one, so that case is computed rather than assumed: the worst icon on the
   whole page (`footer .heart`, which is `--signal`) drops 7.15 -> 6.54 dark and
   4.27 -> 3.65 light when read against a dot instead of flat `--bg`. That case
   is unchanged by this edit -- it is the footer, and it was already the real
   background everywhere outside the header.

   `border-bottom` is deliberately kept. It is a rule under the header block,
   not part of the panel, and `--line` is the token the project uses for exactly
   this. Whether the header wants a rule at all with no panel behind it is a
   design call, not a consequence of the ruling. */
.top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}

.logo {
  flex-shrink: 0;
}

.logo img {
  display: block;
  object-fit: contain;
}

.brand-text {
  flex: 1;
  min-width: 0;
}

.brand h1,
.brand .brand-name {
  margin: 0;
  font-size: clamp(1.3rem, 2vw, 1.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.brand p {
  margin: var(--space-2) 0 0;
  color: var(--ink-muted);
  font-size: var(--fs-md);
}

/* The channel link, under the brand's subtitle.

   Placement and the sprite are recorded above; this rule is the link's own
   appearance, and the partner ruled on it THREE times.

   It is an ordinary link in the site's own idiom -- the same treatment
   `.title-cell a` gives a film title: no underline, a 1px `--line-strong`
   bottom border that goes to `--ink` on hover, and NO `color` of its own so the
   label inherits the surrounding text. That was the second ruling: painting the
   label itself in Telegram's blue was tried first and rejected, because a
   blue word beside the site's own link styling reads as a different kind of
   thing entirely rather than as a link to a channel.

   The MARK is outside the anchor, and that is the third ruling, taken after
   seeing the second one render. The glyph was a CHILD of the `<a>`, and a
   `border-bottom` on an `inline-flex` anchor spans the whole box -- glyph
   included -- so the underline the second ruling asked for ran under the
   Telegram mark as well as under the label. `.title-cell a` underlines text.
   Moving the mark out was the partner's call and it is not free, so the cost is
   recorded rather than argued: the mark is no longer clickable and no longer in
   the focus ring, and clicking it lands on nothing. `.ch-row` now owns the
   layout the anchor used to own, and `.ch-link` holds the label only.

   Only the MARK carries the brand colour. It has to be named outright rather
   than left as `currentColor`: on the <svg> element `currentColor` resolves to
   THAT element's own `color`, and `svg:not(.heart) { color: var(--accent) }`
   sets it, so with `currentColor` the mark rendered --accent -- a purple plane
   -- and with no fill at all it fell back to the SVG default and rendered BLACK
   in both themes, because the share sprite declares no fill on any of its seven
   symbols and every consumer inherits one from `.share-ico`, which this mark is
   not a member of. Both shipped.

   THE SELECTOR IS `.ch-mark`, NOT `.ch-link svg`, and that is the third defect
   this file records rather than the third fix. The mark is no longer a
   DESCENDANT of the anchor, so a descendant selector written for the old shape
   matches nothing -- SILENTLY, and with every other gate green, which is
   precisely how both a black mark and an --accent mark came to look like colour
   problems rather than like two dead rules. A selector that names a shape stops
   working when the shape moves, and nothing else in the project notices. */
/* The ROW holds both channels and the ITEM holds one, and the two levels exist
   because the gaps are not the same: within an item the mark sits against its
   own label at --space-0-35, between items the labels need --space-3. A single
   gap on one level would have to compromise between the two, and the tighter
   one is what keeps a mark from reading as detached from its label. The wrap
   is deliberate and measured, not defensive: at 300-340 the two links do not
   fit on one line, they wrap to two, and they are FLUSH (0.0px overflow) at
   every width in that band, with no document-level overflow at any width. */
.ch-row {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  row-gap: var(--space-1);
  margin-top: var(--space-1);
}

.ch-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0-35);
}

.ch-link {
  display: inline;
  font-size: var(--fs-sm);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--transition-fast) ease;
}

/* The fill is NOT optional, and it is NOT `currentColor`.

   Three defects this closes, all of which shipped at least once:

   1. There was no fill at all. `static/share.svg` declares none on any of its
      seven symbols; every consumer inherits `fill: currentColor` from
      `.share-ico`, and the mark is not a member of that class. With only
      `class="icon"` the path fell back to the SVG default and painted BLACK in
      both themes, which the partner read as a wrong colour rather than a
      missing rule.

   2. `currentColor` is the wrong value even once a fill exists. On the `<svg>`
      element `currentColor` resolves to THAT element's own computed `color`,
      and `svg:not(.heart) { color: var(--accent) }` sets it -- so the mark came
      out --accent, a purple plane, beside a label in the site's own link
      colour. The token is named outright instead.

   3. The selector is `.ch-mark` and NOT `.ch-link svg`, because the mark is no
      longer a descendant of the anchor -- it is its previous sibling. A
      descendant selector written for the old shape matches nothing once the
      markup moves, and it matches nothing SILENTLY, which is how the black
      glyph and the --accent glyph both looked like colour problems rather than
      dead rules. `check_channel_colour()` requires the class, not the shape.

   The fill is on a MODIFIER, `.ch-mark--tg` / `.ch-mark--max`, not on
   `.ch-mark` itself. Two channels means two brands, and a bare `.ch-mark` rule
   holding `var(--telegram)` would paint the MAX glyph Telegram blue on every
   page with the mark of the defect above: a rule that exists, that matches,
   and that is wrong. A modifier names the brand it paints, so a third channel
   has to state its own colour or render uncoloured. */
.ch-mark--tg {
  fill: var(--telegram);
}

/* The optical size correction, and it is the one non-obvious value here.

   `icon-max` paints 76% of its own 1000-unit viewBox; `icon-telegram` paints
   100% of its 16. At one CSS size the MAX mark therefore reads about a quarter
   SMALLER than the Telegram one beside it, even though both are the same
   nominal size. Measured by rasterising each glyph and scanning for the ink
   BOUNDS, not its area: the two differ in solidity as well, so the area ratio
   (1.92x, i.e. 1.39 linear) disagrees with the extent ratio (1.32) and the eye
   compares extent. Hence 17.59 * 1.32 = 23.22.

   Cost of the correction, measured at every width in both themes: the row grows
   1.22px (22 -> 23.22), the header 0.61px at 375 and nothing at all from 767 up,
   and the two marks' centres stay level to 0.01px because the row is
   `align-items: center` and both items are themselves centred. */
.ch-mark--max {
  fill: var(--max);
}

/* The SIZE correction needs the compounded selector, and the reason is a dead
   rule rather than a missing one. `.icon { width: 1.1rem; height: 1.1rem }` sits
   LATER in this file, and both selectors are (0,1,0), so the declaration below
   was computed away and the mark rendered at exactly Telegram's 17.6px -- the
   fill worked, because `.icon` does not set `fill`, so the defect was invisible
   in the one property anyone was checking. Compounding to (0,2,0) settles it
   against reordering, which is the same treatment `.tool-buttons .lang` gets for
   the same reason: a bare (0,1,0) class loses to a later blanket and the loss
   has no other symptom.

   1.1rem * 1.32 = 1.452rem. Expressed as a RATIO of the glyph's own size rather
   than as the measured 23.22px, because the invariant is the 1.32 factor: it
   must survive a change of root font-size, and a hardcoded pixel value silently
   breaks the moment `.icon` is rescaled. */
.ch-mark.ch-mark--max {
  width: 1.452rem;
  height: 1.452rem;
}

.ch-link:hover {
  border-bottom-color: var(--ink);
}

.ch-link:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-xs);
}

.invite {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-md);
}

.invite a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}

.invite a:hover {
  text-decoration-color: var(--ink);
}

.invite .tg-icon {
  display: inline-block;
  width: 16px;
  height: 16px;
  vertical-align: text-top;
}

.toolbar {
  display: flex;
  flex-shrink: 0;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-start;
  gap: var(--space-2);
}

.icon {
  display: block;
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
}

/* Icons wear the accent. Human partner,
   2026-09-30: the icons had been demoted to `--ink-muted` on the argument that
   a cool hue in a warm palette "reads as a foreign colour rather than as an
   icon", and that is exactly the reading the partner did not want. The rule
   below is where `--icon` used to be: a token with one consumer is a second
   name for the same colour, so the value is named here once. `footer .heart`
   is excluded and keeps `--signal`, and `.heart` is the only svg carrying that
   class. */
svg:not(.heart) {
  color: var(--accent);
}

.tool-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tool-buttons button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font: inherit;
  cursor: pointer;
  transition:
    background-color var(--transition-fast) ease,
    color var(--transition-fast) ease,
    border-color var(--transition-fast) ease;
}

.result-count {
  flex: none;
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.reset-filters {
  min-height: 40px;
  padding: 0 var(--space-3);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-l);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--transition-fast) ease, border-color var(--transition-fast) ease, color var(--transition-fast) ease;
}

.reset-filters:hover {
  border-color: var(--accent);
  background: var(--hover);
}

.reset-filters:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.lang,
.theme-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-0-35);
  border: 1px solid var(--line-control);
  background: var(--glass);
  color: var(--ink-muted);
  border-radius: var(--radius-s);
  font: inherit;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease;
}

/* Boxes, declared separately from the paint above. The rule above used to hold
   `height: 40px; padding: 0 var(--space-3)` and a `min-width` shared with
   `.theme-toggle`, which made the theme button 76px wide -- the same class of
   mistake as the (0,1,1) `.tool-buttons button` blanket, one level up. The
   human partner's requirement is one row of identical squares with the language
   toggle allowed to be wider, because it is the only control carrying a label.
   `.theme-toggle` therefore joins `.fav-filter`/`.lucky` as an explicit square,
   and only `.lang` gets the padded label box. The xs copy of the language
   min-width needs the `.tool-buttons ` prefix to tie the mobile min-* floor. */
.theme-toggle {
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
}

.lang {
  justify-content: center;
  height: 40px;
  padding: 0 var(--space-3);
  min-width: calc(var(--tap) + var(--space-6));
}

.lang:hover,
.theme-toggle:hover {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--hover);
}

.lang:active,
.theme-toggle:active,
.fav-filter:active,
.lucky:active {
  background: var(--accent-fill);
  color: var(--ink);
  /* Same token as the fill, and that is a correction rather than a detail: the
     border read --accent while the fill beside it read --accent-fill, which
     would have drawn a bright 1px ring around a dark plate in the dark theme.
     Before the split the two declarations were the same value, so keeping them
     the same is what preserves the pressed button's appearance. */
  border-color: var(--accent-fill);
}

.lang:focus-visible,
.theme-toggle:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.search {
  flex: none;
  width: 210px;
  min-width: 160px;
  max-width: 100%;
  height: 40px;
  border-radius: var(--radius-m);
  border: 1px solid var(--line-control);
  background: var(--surface-1);
  color: var(--ink);
  font-size: var(--fs-sm);
  padding: 0 var(--space-3);
  transition: border-color var(--transition-fast) ease;
}

.search:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-focus);
  outline: none;
}

.genre-filter {
  width: 160px;
  max-width: 100%;
  height: 40px;
  padding: 0 var(--space-6) 0 var(--space-3);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: var(--glass);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: border-color var(--transition-fast) ease;
}

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

.genre-filter:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  border-color: var(--accent);
}

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

.search:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  border-color: var(--accent);
}

.tool-buttons .menu-toggle {
  appearance: none;
  display: none;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: var(--glass);
  color: var(--ink-muted);
  font: inherit;
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease;
}

.menu-toggle:hover {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--hover);
}

.menu-toggle:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.nav-dialog {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: none;
  max-height: none;
  width: 100%;
  height: 100%;
}

.nav-dialog::backdrop {
  background: rgba(23, 25, 35, 0.5);
}

.nav-dialog-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(100% - var(--space-4) * 2, 20rem);
  margin: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.nav-dialog-close {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-end;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: var(--glass);
  color: var(--ink-muted);
  font: inherit;
  cursor: pointer;
}

.nav-dialog-close:hover {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--hover);
}

.nav-dialog-close:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.fav-filter {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: var(--glass);
  color: var(--ink-muted);
  font: inherit;
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease;
}

.fav-filter .fav-icon-svg {
  width: 1.2rem;
  height: 1.2rem;
}

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

.fav-filter.active {
  background: var(--glass);
  color: var(--accent);
  border-color: var(--accent);
}

.fav-filter:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.lucky {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: var(--glass);
  font-size: var(--fs-xl);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease;
}

.lucky:hover {
  border-color: var(--ink);
  background: var(--hover);
}

.lucky:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0;
  font-size: var(--fs-sm);
}

.catalog-tools {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-5);
}

.nav a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 40px;
  padding: var(--space-2) var(--space-3);
  text-decoration: none;
  border-radius: var(--radius-s);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-muted);
  transition: color 0.15s ease, background-color 0.15s ease;
}

.nav a:hover {
  color: var(--ink);
  background: var(--surface-1);
}

.nav span.count {
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.catalog-status {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin: -0.5rem 0 var(--space-4);
}

.featured {
  margin: 0 0 var(--space-9);
  padding: var(--space-1-25) 0 var(--space-5);
  border-block: 1px solid var(--line);
}

.section-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: 0.9rem;
}

.section-heading h2 {
  margin: 0;
  font-size: clamp(1.35rem, 2.2vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.section-heading p {
  max-width: 42ch;
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--fs-sm);
  text-align: end;
}

.featured-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.featured-grid li {
  display: flex;
  min-width: 0;
}

.featured-card {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  /* NO `justify-content`, deliberately. `.featured-card-media` is `flex: none`
     (see its own comment), which is the whole fix, and adding `space-between`
     on top of it was measured and rejected: it levels the copy block's BOTTOM
     and so the alt line, at the cost of a NEW 18.2px spread in the gap between
     the poster and the title. `flex: none` alone leaves the copy hanging from
     the top, which lines up the poster, the scrim, the chips, the title's top
     and the gap between poster and title all at once; what it gives up is the
     alt line's baseline on the one-line cards, and the slack it does not absorb
     is empty card surface at the bottom -- the same shape the related card
     already has. Measured at 1280, forced two-line title in card 1:
     chips 18.19 -> 0.02, title top 18.19 -> 0.01, poster-to-title gap
     18.19 -> 0. */
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface-1);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.featured-card:hover {
  border-color: var(--accent);
  background: var(--surface-2);
}

.featured-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.featured-card-media {
  position: relative;
  display: block;
  /* NOT `flex: 1`, and this one declaration is the entire fix for a card whose
     title runs to two lines. At `flex: 1` the media absorbed whatever the copy
     block did not use, and the copy block is 18.2px taller on a two-line title
     -- 37 of 155 titles at 1280, 65 at 1024 -- so the media box differed by
     18.2px across a row. Two things are anchored to that box's bottom and both
     rode with it: `.featured-card-overlay` (`inset: auto 0 0 0`), which carries
     the `★ year genre` meta AND the KP/IMDb chips, so the chips gained an 18.2px
     spread -- the "dancing" that was reported. The poster itself never moved
     (its height is the card's width x 3/2, measured spread 0.1px), so what read
     as a shorter poster was the scrim sitting at a different height on the same
     image.

     Out of the flex flow, the media is exactly the image's own height, so its
     height is a function of the card's WIDTH alone and the scrim is on the same
     line in every card whatever the title does. Not a font change: `--fs-xs`
     removes only 11 of the 37 two-line titles at 1280 and leaves 26, while
     clamp 1 removes all of them at the cost of cutting the second line off
     titles that read in full today. */
  flex: none;
  min-width: 0;
}

.featured-card-media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  background: var(--surface-2);
}

.featured-card-overlay {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  padding: var(--space-5) var(--space-3) var(--space-3);
  /* The 0.8 stop sits ABOVE the meta line on purpose. Text over a photograph has
     no worst case anyone can compute -- the poster decides it -- so the scrim has
     to carry the text with margin rather than to exactly the AA floor. Measured
     with the 0.55 stop where this one was: the top of the year line composites
     to 119 grey under a bright poster, and white on 119 grey is 4.43:1, which is
     0.07 under the 4.5 floor for 12px text. The poster that produces it is not
     a hypothetical, so the scrim is the control. */
  background: linear-gradient(to top, rgb(0 0 0 / 0.9), rgb(0 0 0 / 0.8) 78%, rgb(0 0 0 / 0) 100%);
  pointer-events: none;
}

.film-badge {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  max-width: calc(100% - var(--space-3));
  overflow: hidden;
  border-radius: var(--radius-s);
  padding: 0.1em 0.5em;
  background: var(--media-plate);
  box-shadow: var(--shadow);
  color: var(--media-plate-ink);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.35;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.featured-card-title {
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.01em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: normal;
  text-overflow: ellipsis;
  hyphens: auto;
}

.featured-card-alt {
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.featured-card-year,
.featured-card-genre {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.featured-card-year {
  flex: none;
}

.featured-card-genre {
  min-width: 0;
}

/* `star . year . genre` sits on the scrim, so it wears --on-media rather than
   --ink. --ink is a theme-dependent token: light theme's is dark ink and would
   be invisible on a black gradient. --on-media is declared once in :root and
   overridden in neither theme, because it names a surface that does not change
   with the theme -- the photograph underneath.

   The star inherits this and has NO rule of its own, which is the decision:
   measured on the scrim at 6 widths x 2 themes, `color: var(--accent)` read
   2.81-3.34:1 (mostly ~2.9), under the 3:1 floor WCAG 1.4.11 sets for
   graphics. --accent was ruled on twice, but both rulings were measured on a
   flat --surface-1, where it read 4.21:1; on photography that measurement does
   not exist. Human partner, 2026-09-30: repoint it at --on-media. The glyph
   now reads 13.2-15.5:1, the same as the year beside it. Do not repaint it. */
.featured-card-meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  max-width: 100%;
  font-size: var(--fs-xs);
  color: var(--on-media);
  line-height: 1.35;
}

.featured-ratings {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  max-width: 100%;
}

/* The rating chips keep the background they were measured against. Over a
   photograph their brand colours would sit on pixels that differ per poster, so
   the contrast figures recorded for them would describe nothing; on --surface-1
   they are the same numbers verify.py and e2e already check. */
.featured-card .rating-chip {
  background: var(--surface-1);
}

.featured-card-copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-0-35);
  padding: var(--space-3);
  min-width: 0;
}

/* Six posters do not fit below lg, so the block is a rail there -- the same
   mechanism and the same 1024px boundary `.related` uses for its own strip,
   because the arithmetic is identical. Six cards at 156px plus gaps need
   996px; the container is 1040px at 1024 and 713px at 768, so from lg up the
   six columns shrink to fit (194px each at 1225px) and below it the strip
   scrolls. There is no third option that is honest: shrinking the card further
   to fit six at 768 would put the poster under 106px, and widening the page to
   fit them would move the container off --container.
   `flex: 0 0 156px` is a bare px on purpose -- box sizing is outside the 6d
   gate, and `.related li` sets its track the same way. */
@media (max-width: 1023px) {
  .featured-grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: var(--space-3);
    padding-bottom: var(--space-1);
  }

  .featured-grid li {
    flex: 0 0 156px;
    scroll-snap-align: start;
  }
}

section {
  margin-bottom: var(--space-2-25);
}

section h2 {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-xl);
  font-weight: 500;
  letter-spacing: -0.01em;
}

.mobile-sort {
  display: none;
}

.table-wrap {
  overflow-x: auto;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  line-height: 1.3;
  table-layout: fixed;
}

th:nth-child(1),
td:nth-child(1) {
  width: 34%;
}

th:nth-child(2),
td:nth-child(2) {
  width: 30%;
}

th:nth-child(3),
td:nth-child(3) {
  width: 9%;
}

th:nth-child(4),
td:nth-child(4) {
  width: 13.5%;
}

th:nth-child(5),
td:nth-child(5) {
  width: 13.5%;
}

th,
td {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
}

th {
  background: var(--th-bg);
  font-weight: 500;
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  user-select: none;
}

thead th {
  border-bottom-color: var(--line-strong);
}

th.sortable .th-sort {
  display: inline-block;
  vertical-align: baseline;
  margin: -4px;
  padding: 4px;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  white-space: nowrap;
  text-align: inherit;
  cursor: pointer;
}

th.sortable .th-sort:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color-ink);
  outline-offset: var(--focus-ring-offset-inset);
}

th.sortable:hover,
th.sortable .th-sort:hover {
  color: var(--ink);
}

th .th-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

th .arrow {
  display: inline-block;
  margin-left: var(--space-1);
  color: var(--line-strong);
}

th.is-asc .arrow,
th.is-desc .arrow {
  color: var(--accent);
}

tbody tr:hover {
  background: var(--hover);
}

tbody tr:hover td:first-child {
  box-shadow: inset 3px 0 0 var(--line-strong);
}

.title-cell {
  min-width: 0;
  position: relative;
  overflow-wrap: normal;
}

.title-layout {
  display: flex;
  /* The title cell carries two lines -- the title and the alt title in the other
     language -- while genre, year and ratings carry one each. Top-aligned, the
     single-line cells landed between the two: measured 17.6px below the primary
     title and only 2.6px above the alt, so the row read as "title, then the
     numbers belong to the second line". Centring the two-line block halves the
     gap (10.2px) and centres the poster icon with it. Top-aligning the cells
     instead would put the numbers on the primary title line exactly (0.5px), but
     the block reads better centred, and that is the call the partner made. */
  align-items: center;
  gap: var(--space-2);
  min-height: 3.75em;
}

.title-layout .poster-wrap {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
}

.title-layout .fav-icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-top: 0;
}

.poster-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: text-bottom;
}

.poster-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-l);
  background: transparent;
  color: var(--ink-muted);
  line-height: 0;
  cursor: pointer;
  vertical-align: text-bottom;
  transition: background-color var(--transition-fast) ease, border-color var(--transition-fast) ease, color var(--transition-fast) ease;
}

.poster-icon::before {
  content: "";
  position: absolute;
  inset: -6px;
}

.poster-icon-svg {
  width: 1.2rem;
  height: 1.2rem;
  transition: transform var(--transition-fast) ease;
}

.poster-icon:hover .poster-icon-svg {
  transform: scale(1.1);
}

.poster-icon:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color-ink);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-xs);
}

.poster-modal {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  max-width: none;
  max-height: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.65);
  z-index: var(--z-modal);
  padding: clamp(1rem, 1rem + 4vw, 2rem);
}

.poster-modal:not([open]) {
  display: none;
}

.poster-modal::backdrop {
  background: transparent;
}

.poster-modal-inner {
  position: relative;
}

.poster-modal img {
  display: block;
  max-width: 90vw;
  max-height: 90vh;
  height: auto;
  border-radius: var(--radius-l);
  box-shadow: var(--shadow);
}

.poster-close {
  position: absolute;
  top: -10px;
  right: -10px;
  margin: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  line-height: 0;
  transition: transform var(--transition-fast) ease, opacity var(--transition-fast) ease;
}

.poster-close:hover {
  transform: scale(1.08);
}

.poster-close:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color-ink);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-xs);
}

.poster-close img {
  display: block;
  width: 24px;
  height: 24px;
  object-fit: contain;
}

.fav-icon {
  display: inline-flex;
  align-items: center;
  vertical-align: text-bottom;
}

.fav-icon .fav-icon-svg {
  width: 1.2rem;
  height: 1.2rem;
}

.title-copy {
  display: grid;
  flex: 1;
  min-width: 0;
  gap: var(--space-0-2);
}

.mobile-meta {
  display: none;
}

.title-primary {
  display: -webkit-box;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.25;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.title-cell a {
  display: inline;
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--transition-fast) ease;
  word-break: normal;
}

.title-cell a:hover {
  border-bottom-color: var(--ink);
}

.alt-title {
  display: block;
  max-width: 100%;
  overflow: hidden;
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.genre {
  color: var(--ink-muted);
  max-width: 220px;
}

.year,
.num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

a.kp,
a.imdb {
  text-decoration: none;
}

a.kp {
  color: var(--kp);
}

a.imdb {
  color: var(--imdb);
}

a.kp .star,
a.imdb .star,
.rc-rating .star {
  display: inline-block;
  margin-left: 0.2em;
  font-size: 0.9em;
  line-height: 1;
  vertical-align: 0.12em;
}

.num a::before {
  content: "";
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-right: 0.25em;
  vertical-align: middle;
  background: currentColor;
  mask: url(../static/external_link_icon.png) center / contain no-repeat;
}

.num .no-data {
  color: var(--ink-muted);
  font-size: 0.9em;
  line-height: 1.2;
  white-space: normal;
}

.num a:hover .no-data {
  color: inherit;
}

.rating-chip {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-s);
  border: 1px solid var(--line-strong);
  padding: 0.1em 0.5em;
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--rating);
  background: transparent;
}

.rating-chip--kp {
  color: var(--kp);
  border-color: var(--kp);
}

.rating-chip--imdb {
  color: var(--imdb);
  border-color: var(--imdb);
}


.empty {
  color: var(--ink-subtle);
  padding: var(--space-4) var(--space-3);
  text-align: center;
}

.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--space-4);
  margin: var(--space-5) 0;
  padding: var(--space-6) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--paper);
  color: var(--ink-muted);
  text-align: center;
}

.empty-state p {
  margin: 0;
}

.boot-fallback {
  max-width: var(--container);
  margin: 15vh auto 0;
  padding: var(--space-6) var(--space-1-25);
  text-align: center;
  color: var(--ink-muted);
  line-height: 1.6;
}

.no-js-catalog ul {
  margin: 0 0 var(--space-1);
  padding-left: var(--space-1-25);
}

.no-js-catalog li {
  margin-bottom: var(--space-1);
}

.catalog-end {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
}

.catalog-total {
  color: var(--ink-muted);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}

footer {
  margin-top: var(--space-9);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  text-align: center;
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

footer a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}

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

footer .heart {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  color: var(--signal);
  margin-inline: 0.15rem;
  vertical-align: -0.15em;
}

footer .footer-line + .footer-line {
  margin-top: var(--space-1);
}

footer .footer-privacy {
  font-size: var(--fs-xs);
  color: var(--ink-muted);
}

.scroll-top {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  width: 40px;
  height: 40px;
  border: 1px solid var(--line-control);
  background: var(--glass);
  color: var(--ink);
  font-size: var(--fs-lg);
  cursor: pointer;
  border-radius: var(--radius-s);
  opacity: 0.7;
  z-index: var(--z-raised);
  transition:
    background 0.15s ease,
    border-color 0.15s ease,
    visibility 0.15s,
    opacity 0.15s ease,
    translate 0.15s ease;
}

.scroll-top:hover {
  background: var(--hover);
  border-color: var(--ink);
}

@supports (container-type: scroll-state) {
  .scroll-top {
    visibility: hidden;
    opacity: 0;
    translate: 0 20px;
  }

  @container scroll-state(scrollable: top) {
    .scroll-top {
      visibility: visible;
      opacity: 0.7;
      translate: 0 0;
    }
  }
}

html.no-scroll-state .scroll-top {
  visibility: hidden;
  opacity: 0;
  translate: 0 20px;
}

html.no-scroll-state.scrolled .scroll-top {
  visibility: visible;
  opacity: 0.7;
  translate: 0 0;
}

.skip-link {
  position: fixed;
  top: -50px;
  left: 8px;
  z-index: var(--z-skip);
  padding: var(--space-2) var(--space-3);
  background: var(--accent-fill);
  color: var(--ink);
  border-radius: var(--radius-s);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-decoration: none;
}

.skip-link:focus {
  top: 8px;
  outline: var(--focus-ring-width) solid var(--focus-ring-color-ink);
  outline-offset: var(--focus-ring-offset);
}

#main:focus {
  outline: none;
}
}

@layer base {

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

@layer components {

@media (min-width: 768px) and (max-width: 1023px) {
  .top {
    flex-wrap: wrap;
  }

  .toolbar {
    align-items: flex-start;
  }
}

@media (min-width: 576px) and (max-width: 767px) {
  th:nth-child(1),
  td:nth-child(1) {
    width: 33%;
  }

  th:nth-child(2),
  td:nth-child(2) {
    width: 28%;
  }

  th:nth-child(3),
  td:nth-child(3) {
    width: 12%;
  }

  th:nth-child(4),
  td:nth-child(4) {
    width: 13.5%;
  }

  th:nth-child(5),
  td:nth-child(5) {
    width: 13.5%;
  }
}

@media (max-width: 767px) {
  .top {
    flex-direction: column;
  }

  .brand {
    width: 100%;
    min-width: 0;
    flex-direction: row;
  }

  .toolbar {
    width: 100%;
    align-items: stretch;
    justify-content: flex-start;
  }

  .catalog-tools {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
  }

  .search {
    width: 100%;
    min-width: 0;
  }

  .genre-filter {
    width: 100%;
    height: 44px;
  }

  .catalog-status {
    justify-content: flex-start;
    margin-top: -0.5rem;
  }

  .section-heading {
    display: grid;
    gap: var(--space-0-35);
  }

  .section-heading p {
    max-width: 38ch;
    text-align: start;
  }

  .search,
  .nav a,
  .reset-filters {
    min-height: var(--tap);
  }

  .ratings a {
    min-height: var(--tap);
  }

  .scroll-top,
  .fav-filter,
  .lucky,
  .menu-toggle,
  .nav-dialog-close,
  .theme-toggle,
  .tool-buttons button {
    min-width: var(--tap);
    min-height: var(--tap);
  }

  /* `.lang` carries a label, so it is a floor and not a square. It needs the
     `.tool-buttons ` prefix to tie the blanket floor's (0,1,1) rather than lose
     to it at (0,1,0), and it is declared after that floor so source order
     settles it. A bare `.lang` here silently measures 44px wide. */
  .tool-buttons .lang {
    min-width: calc(var(--tap) + var(--space-6));
    min-height: var(--tap);
  }

  .share-btn {
    min-width: var(--tap);
    min-height: var(--tap);
  }

  .scroll-top {
    bottom: var(--space-4);
    right: var(--space-4);
  }

  .table-wrap table {
    width: 100%;
    min-width: 0;
    table-layout: fixed;
    font-size: 0.8125rem;
  }

  th.sortable .th-sort {
    margin: -10px -4px;
    padding: 10px 4px;
  }

  .nav span.count {
    background: var(--th-bg);
    border-radius: var(--radius-xs);
    padding: var(--space-1) var(--space-2);
    font-size: var(--fs-xs);
  }

  .genre {
    max-width: none;
  }

  th,
  td {
    padding: 0.4rem var(--space-1);
  }
}

@media (max-width: 575px) {
  .tool-buttons .menu-toggle {
    display: inline-flex;
  }

  .nav:not(.nav--dialog) {
    display: none;
  }

  html.js .catalog-tools {
    margin-top: var(--space-4);
  }

  .nav--dialog {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    margin: 0;
  }

  .nav--dialog a {
    justify-content: space-between;
  }

  html:not(.js) .tool-buttons .menu-toggle {
    display: none;
  }

  html:not(.js) .nav:not(.nav--dialog) {
    display: flex;
  }

  th {
    letter-spacing: 0.04em;
  }

  .mobile-sort {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    min-height: var(--tap);
    margin: -0.25rem 0 var(--space-2) auto;
    padding: 0 var(--space-3);
    border: 1px solid var(--line-control);
    border-radius: var(--radius-l);
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: var(--fs-xs);
    cursor: pointer;
  }

  .mobile-sort .arrow {
    margin-left: var(--space-2);
    color: var(--accent);
  }

  th:nth-child(2),
  td:nth-child(2) {
    display: none;
  }

  th:nth-child(1),
  td:nth-child(1) {
    width: 45%;
  }

  th:nth-child(3),
  td:nth-child(3) {
    width: 12%;
  }

  th:nth-child(4),
  td:nth-child(4) {
    width: 14%;
  }

  th:nth-child(5),
  td:nth-child(5) {
    width: 14%;
  }

  th:nth-child(4),
  th:nth-child(5) {
    text-align: center;
  }

  th:nth-child(4) .th-label,
  th:nth-child(5) .th-label {
    justify-content: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .poster-close,
  .scroll-top,
  .share-btn,
  .lang,
  .theme-toggle,
  .search,
  .fav-filter,
  .lucky,
  .menu-toggle,
  .nav-dialog-close,
  .nav a,
  .title-cell a,
  .poster-icon-svg {
    transition: none;
  }

  .poster-icon:hover .poster-icon-svg {
    transform: none;
  }
}

/* Film pages: shared block, matches the main page design */
.film-main {
  display: grid;
  gap: var(--space-5);
  margin: var(--space-4) 0;
  padding: clamp(1rem, 2vw, 1.5rem);
  background: var(--glass-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow);
}

.film-info {
  min-width: 0;
}

.film-title {
  display: -webkit-box;
  max-width: min(var(--measure), 100%);
  margin: 0 0 var(--space-1);
  overflow: hidden;
  font-size: clamp(1.5rem, 4vw, var(--fs-display));
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.15;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.film-alt {
  max-width: min(60ch, 100%);
  margin: 0 0 var(--space-3);
  overflow: hidden;
  color: var(--ink-muted);
  font-size: var(--fs-sm);
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
}


.film-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  margin: 0 0 var(--space-4);
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  line-height: 1.4;
}

.film-meta-item {
  display: inline-flex;
  align-items: center;
}

.film-meta-item + .film-meta-item::before {
  content: "·";
  margin-right: var(--space-3);
  color: var(--line-strong);
}

.film-desc-alt-wrap {
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
}

.film-desc-alt-wrap > summary {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-bottom: var(--space-2);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.2em;
}

.film-desc-alt-wrap > summary:hover {
  color: var(--ink);
  text-decoration-color: var(--ink);
}

.film-poster {
  position: relative;
  margin: 0;
}

.film-poster img {
  display: block;
  max-width: 320px;
  height: auto;
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow);
}

.film-desc {
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
  line-height: 1.6;
}

.film-desc-alt {
  margin-top: 0;
  color: var(--ink-muted);
  font-size: var(--fs-md);
  padding-left: var(--space-3);
  border-left: 1px solid var(--accent);
}

.ratings {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: 0 0 var(--space-4);
}

.ratings a {
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding: 0 var(--space-3);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  text-decoration: none;
  background: var(--glass);
  font-size: var(--fs-md);
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease;
}

.ratings a:hover {
  background: var(--hover);
  border-color: var(--ink);
}

.ratings a.kp {
  color: var(--kp);
}

.ratings a.imdb {
  color: var(--imdb);
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-0-35);
  margin: var(--space-3) 0 var(--space-4);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

.breadcrumb a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}

.breadcrumb a:hover {
  text-decoration-color: var(--ink);
}

.bc-current {
  display: inline-flex;
  min-width: 0;
  color: var(--ink);
}

.bc-type {
  color: var(--ink-muted);
}

.back-catalog {
  margin: 0 0 var(--space-5);
  text-align: center;
}

.back-catalog a {
  display: inline-block;
  padding: var(--space-2);
  color: var(--ink-muted);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  font-size: var(--fs-md);
  transition:
    color 0.15s ease,
    text-decoration-color 0.15s ease;
}

.back-catalog a:hover {
  color: var(--ink);
  text-decoration-color: var(--ink);
}

.related {
  margin: var(--space-9) 0 var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.related h2 {
  margin: 0 0 var(--space-4);
  font-size: clamp(1.35rem, 2.2vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* The related strip's base is the SCROLLER, and it is the whole below-lg tier.
   `ul` here is also `.related-track` -- the element carries both classes -- so
   `display: flex` plus `.related-track`'s `overflow-x: auto` is the strip, with
   no media block needed to build it. Before the 2026-09-30 rework the base said
   170px and three media blocks re-stated it: a six-column grid above 1024, a
   FOUR-column grid from 768 to 1023 (which put six cards in two rows with two
   orphans, an 807px block at 768), and a 160px scroller below 768. The md block
   is gone and the base is now the canonical card, so the two tiers are the base
   and the single lg grid below.

   160px is the one width this card has at every viewport, which is what "the
   same size on every resolution" means. The partner's ruling was that the
   posters must never break into a second row: where six 160px cards do not fit,
   the strip scrolls. Six of them plus gaps need 1020px, so that is a 1024px
   boundary, and it is the same edge the grid below turns on. */
.related ul {
  display: flex;
  gap: var(--space-3);
  margin: 0;
  /* `padding: 0` first, then a bottom padding IN THE SAME RULE. This is not
     redundancy: the element carries both `.related ul` and `.related-track`, and
     `.related ul` is (0,1,1) while a bare `.related-track` is (0,1,0), so a
     `padding-bottom` declared on `.related-track` LOSES to the reset on the same
     element and never applied. It read as the gap between the cards and the
     horizontal scrollbar, and it was worth 0px: the cards sat flush against
     the bar. Declared here it is the same rule that resets the list padding, so
     no specificity contest is possible. */
  padding: 0;
  padding-bottom: var(--space-2);
  list-style: none;
}

.related li {
  display: flex;
  flex: 0 0 160px;
  /* min-width: 0 is load-bearing: a flex item's default min-width is auto, so
     without it the card's min-content (the poster's intrinsic width) holds the
     track open at ~210px and the 160px basis is a no-op. */
  min-width: 0;
  scroll-snap-align: start;
}

.related-track {
  min-width: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-1);
}

.related-dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-1);
  /* No negative margin here. It was there so the dot row would sit over the
     strip's own trailing whitespace rather than add a band under it -- correct
     while the buttons were 44px, because the 8px dot then sat 18px below the
     row's top edge. At 24px the dot sits 8px down, the same -8px pulled it onto
     the cards, and the partner reported the dots stuck to the cards. The row now
     takes its own space with an ordinary gap. */
  margin-top: var(--space-2);
}

.related-dot {
  /* 24px, not the project's 44px --tap. Four dots on a 44px button were 49.6px
     centre to centre, which reads as four separate controls strung along the
     page rather than as one pagination strip. 24px is the WCAG 2.2 SC 2.5.8 AA
     floor, and it is the same floor this project already sets on
     `.breadcrumb a` and `summary`; going below it would be worse than the
     spacing problem. The target is genuinely harder to hit with a thumb than
     44px -- the partner chose the spacing, and this records what was traded. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-5);
  height: var(--space-5);
  border: 0;
  border-radius: var(--radius-circle);
  padding: 0;
  background: transparent;
  cursor: pointer;
}

.related-dot::after {
  content: "";
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-circle);
  background: var(--line-strong);
  transition: background-color 0.15s ease;
}

.related-dot.is-active::after {
  background: var(--accent);
}

/* One row at EVERY width. There used to be three bands -- a flex scroller below
   768, a FOUR-column grid from 768 to 1023, and a six-column grid above -- and
   the middle one was a defect rather than a tier: six cards in four columns put
   two of them on a second row, so at 768 the block rendered two rows with two
   orphans and measured 807px tall against 419px for a single row. The partner
   asked for the posters never to break into two rows, scrolling instead.

   So there are two bands now, and the card is 160px in both. `flex: 0 0 160px`
   is the track below 1024; from 1024 up it is `minmax(0, 160px)`, a CAP rather
   than `1fr`. That cap is the load-bearing part: `1fr` grew the columns with the
   container, so the desktop cards measured 194-197px against the phone's 160 --
   the same film, a different size, at the same width. Six cards at 160px plus
   gaps need 1020px, which a 969px track at 1024 cannot give, so the six shrink
   to 151.5 there (measured, and the poster to 125.5) instead of wrapping. That
   is the whole reason for the cap: `minmax(0, 160px)` is allowed to be narrower,
   so six always fit on one row and the card stops growing.

   Below lg the strip stays a scroller and keeps its dots, because six 160px
   cards genuinely do not fit below 1024 and there is no honest way to show them
   all without either clipping a poster or scrolling.

   There is deliberately NO block from 768 to 1023. It used to hold a four-column
   grid, and a four-column grid is the defect: six cards do not fit in four
   columns, so two of them took a second row. The base rule is already the
   scroller, so this tier needs nothing declared, and a card width must be stated
   in exactly one place -- an earlier version of this kept a 768-1023 block that
   re-stated the same 160px the base already had, and deleting the 767 block while
   keeping it meant the strip quietly fell back to the base's old 170px below
   768. The card size is checked in tools/e2e.py by reading the rendered width
   at 375 and at 1280 and comparing them. */
@media (min-width: 1024px) {
  .related ul {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 160px));
    /* `justify-content` is the DEFAULT (`start`) and is stated nowhere, on
       purpose. Six 160px cards plus gaps need 1020px, so on a 1225px track at
       1280 there is 205px of slack, and this is where it goes: to the RIGHT.
       Centring it -- which an earlier version of this rule did -- split the
       slack evenly and indented the first card 102px from the content edge, so
       the strip no longer started under its own heading. The partner's ruling
       is that the row starts flush with "Похожее в каталоге", which is the
       section's left edge, so the slack is dead space at the end of the row and
       not a margin before it. `start` is also what the scroller tier below 1024
       already does, so the two tiers agree on which edge the row begins. */
    gap: var(--space-3);
    overflow-x: visible;
    scroll-snap-type: none;
  }

  .related li {
    flex: none;
    min-width: 0;
  }

  /* No scrollbar exists at this tier -- the grid does not overflow -- so the gap
     the bottom padding bought is dead space here. Reset it on the SAME selector
     that declares it, `.related ul`, because a `.related-track` reset at (0,1,0)
     would lose to the (0,1,1) base it is cancelling. */
  .related ul {
    padding-bottom: 0;
  }

  .related-dots {
    display: none;
  }
}

.related a.rc {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface-1);
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.related a.rc:hover {
  border-color: var(--accent);
  background: var(--surface-2);
}

.related a.rc:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.rc-poster {
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius-s);
  background: var(--surface-2);
}

.rc-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-2);
  min-width: 0;
}

.rc-title {
  /* --fs-sm, not --fs-lg: the title is clamped to ONE line, so its width is
     what decides whether a long Russian title is cut. At 18px the one line
     clipped 3.9% of the library (36 of 924 cards at 375/768/1280, 62 at 1024
     where the cards are narrowest); at 14px it clips NONE at 375, 768 or 1280
     and 2.3% at 1024. The smaller size buys the ellipsis almost everywhere for
     3.5px less card height. The same token the featured card's title uses. */
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1.25;
  overflow-wrap: normal;
  text-overflow: ellipsis;
  hyphens: auto;
  letter-spacing: -0.01em;
  display: -webkit-box;
  /* ONE line, not two, and this is the fix for the chips "dancing": a two-line
     title pushed its chips 22-23px lower than a one-line neighbour's, so the
     chip row across six cards had no shared baseline. Clamping to one makes
     every card's content the same height (measured: 17.5px title, 36px info,
     22.4px chips, identical across all six) and the row is level at every
     width. It also takes 22px off the card. */
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.rc-info {
  /* --fs-xs, 12px, and NOT --fs-sm: this label is uppercase with 0.035em of
     tracking, so 14px is 2px WIDER than 12px rather than narrower, and the
     measurement is not marginal -- at 14px this line clipped in 307 of 462
     cards (66%) while the card grew from 328px to 350px. The title was moved
     down to --fs-sm; moving this one up would undo the vertical gain. */
  font-size: var(--fs-xs);
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.035em;
  overflow: hidden;
  overflow-wrap: normal;
  text-overflow: ellipsis;
  hyphens: auto;
}

.rc-meta {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2);
  /* `margin-top: auto` is INERT here, and it is not load-bearing: a flex item's
     auto margin only pushes when the container has slack, and `.rc-content` is
     content-sized -- it has no `flex: 1`, so there is never slack to push into.
     It reads like the rule that keeps the chips aligned, and it is not: the
     chips share a baseline because `.rc-title` is clamped to one line, so every
     card's content is the same height. Left as-is rather than deleted, because
     the next person to find it is better served by a comment than by a
     re-derivation; if `.rc-content` ever grows, this becomes live and pins the
     chips to the card's bottom edge, which is the same visual result. */
  margin-top: auto;
  padding-top: var(--space-1);
}

.rc-rating {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.rc-rating.kp {
  color: var(--kp);
}

.rc-rating.imdb {
  color: var(--imdb);
}

.share-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: var(--space-4) 0 0;
}

.share-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
  transition:
    background 0.15s ease,
    border-color 0.15s ease,
    color 0.15s ease;
}

.share-btn:hover {
  background: var(--hover);
  border-color: var(--ink);
  color: var(--ink);
}

:root.dark .share-btn {
  background: var(--glass);
  color: var(--ink);
}

.share-ico {
  width: 1.25rem;
  height: 1.25rem;
  fill: currentColor;
}

.share-btn[data-net="telegram"]:hover .share-ico { color: #26a5e4; }
.share-btn[data-net="whatsapp"]:hover .share-ico { color: #25d366; }
.share-btn[data-net="vk"]:hover .share-ico { color: #0077ff; }
.share-btn[data-net="x"]:hover .share-ico { color: var(--ink); }
.share-btn[data-net="linkedin"]:hover .share-ico { color: #0a66c2; }
.share-btn[data-net="native"]:hover .share-ico { color: var(--accent); }

@media (min-width: 768px) {
  .film-main {
    grid-template-columns: auto 1fr;
    gap: var(--space-6);
  }
}

@media (max-width: 767px) {
  .film-main {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
  .film-poster {
    max-width: 240px;
    margin: 0 auto;
  }
  .film-poster img {
    width: 100%;
    max-width: none;
  }
}

.about-article {
  max-width: 900px;
  margin: var(--space-7) auto var(--space-8);
  padding: var(--space-6) var(--space-5);
  background: var(--glass-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow);
}

.about-lead {
  max-width: 70ch;
  margin: 0;
  font-size: 1.05em;
  line-height: 1.6;
}

.about-section {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.about-section h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-lg);
  font-weight: 500;
}

.about-section p {
  max-width: 72ch;
  margin: 0;
  line-height: 1.6;
}

.about-article a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}

.about-article a:hover {
  text-decoration-color: var(--ink);
}

@media (max-width: 767px) {
  .about-article {
    margin-top: var(--space-5);
    margin-bottom: var(--space-6);
    padding: var(--space-1-25);
  }

  .about-section {
    margin-top: var(--space-5);
    padding-top: var(--space-1-25);
  }
}

.privacy {
  margin: var(--space-7) 0 var(--space-8);
  padding: var(--space-6) var(--space-5);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
}
.privacy h1,
.privacy h2:first-child {
  margin: 0 0 var(--space-1);
  font-size: clamp(1.5rem, 4vw, 2rem);
  font-weight: 500;
}
.privacy h2 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--fs-lg);
  font-weight: 500;
}
.privacy p, .privacy ul {
  margin: var(--space-2) 0;
  line-height: 1.6;
}
.privacy ul {
  padding-left: var(--space-1-25);
}
.privacy li {
  margin: var(--space-1) 0;
}
.privacy code {
  font-family: inherit;
}
.privacy a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}
.privacy a:hover {
  text-decoration-color: var(--ink);
}
.profile {
  display: flex;
  align-items: center;
  gap: var(--space-1-25);
  margin: var(--space-5) 0 var(--space-2);
  padding: var(--space-1-25);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--paper);
}
.profile-avatar {
  flex: none;
  width: clamp(88px, 20vw, 120px);
  height: clamp(88px, 20vw, 120px);
  border-radius: var(--radius-circle);
  border: 2px solid var(--line-strong);
  object-fit: cover;
}
.profile-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.profile-links a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-s);
  color: var(--ink);
  text-decoration: none;
}
.profile-links a:hover,
.profile-links a:focus-visible {
  background: var(--hover);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}
.profile-links svg {
  flex: none;
  width: 22px;
  height: 22px;
  fill: currentColor;
}
.profile-links span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 575px) {
  .profile {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
  .profile-links {
    width: 100%;
  }
  .profile-links a {
    justify-content: center;
  }
}

}
