/* kama.tube — design system
 *
 * Dark because this gets watched in the dark and a light UI beside video
 * washes out the picture. Depth comes from layered surfaces, one warm accent,
 * and a hair of film grain — not from dropping a shadow on everything.
 *
 * Self-hosted Lato: subset to Latin, three weights, ~190KB, cached a year. A
 * CDN font would add a third-party connection to every first paint.
 */

@font-face {
  font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/static/fonts/lato-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('/static/fonts/lato-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Lato'; font-style: normal; font-weight: 900; font-display: swap;
  src: url('/static/fonts/lato-900.woff2') format('woff2');
}

/* Both themes are explicit. `color-scheme` is set per theme so form controls,
   scrollbars and the like follow along without being restyled by hand. */
:root, :root[data-theme="dark"] {
  color-scheme: dark;

  --bg:        #06060a;
  --bg-2:      #0a0a10;
  --surface:   #101017;
  --surface-2: #17171f;
  --surface-3: #1e1e28;
  --line:      #23232e;
  --line-soft: #1a1a23;
  --line-lit:  #33333f;

  --text:   #f6f6f8;
  --text-2: #b9b9c4;
  --muted:  #7c7c8a;

  --accent:      #ff4d6d;
  --accent-2:    #ffa94d;
  --accent-grad: linear-gradient(112deg, #ff4d6d 0%, #ff7a4d 52%, #ffa94d 100%);
  --accent-soft: rgba(255, 77, 109, .13);
  --accent-line: rgba(255, 77, 109, .38);

  --star:   #ffc247;
  --danger: #ff6b6b;
  --ok:     #46c98b;

  --r-sm: 8px;
  --r:    14px;
  --r-lg: 20px;

  --lift: 0 22px 50px -18px rgba(0,0,0,.85);
  --glow: 0 0 0 1px var(--accent-line), 0 20px 46px -20px rgba(255,77,109,.4);

  --ease:     cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
  --maxw: 1560px;

  --font: 'Lato', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --grain: .035;
  --scrim-top: rgba(0,0,0,.32);
  --scrim-bottom: rgba(0,0,0,.62);
  --badge-bg: rgba(6,6,10,.78);
}

:root[data-theme="light"] {
  color-scheme: light;

  /* Warm off-white rather than pure #fff: a full-white page next to video
     thumbnails is glarey, and the warmth keeps the coral accent coherent. */
  --bg:        #faf8f7;
  --bg-2:      #f2efee;
  --surface:   #ffffff;
  --surface-2: #f5f2f1;
  --surface-3: #ebe7e5;
  --line:      #e0dbd9;
  --line-soft: #ebe7e5;
  --line-lit:  #cfc8c5;

  --text:   #16141a;
  --text-2: #4d4952;
  --muted:  #7a747f;

  /* Darkened so it passes contrast on white — the dark-mode coral is too
     light against a pale background for body-size text. */
  --accent:      #e0294f;
  --accent-2:    #c2610a;
  --accent-grad: linear-gradient(112deg, #e0294f 0%, #e35a2a 52%, #d98a1a 100%);
  --accent-soft: rgba(224, 41, 79, .09);
  --accent-line: rgba(224, 41, 79, .32);

  --star:   #d99b12;
  --danger: #c62828;
  --ok:     #1f7a4d;

  --lift: 0 18px 40px -20px rgba(30,20,25,.28);
  --glow: 0 0 0 1px var(--accent-line), 0 18px 40px -22px rgba(224,41,79,.35);

  --grain: .02;
  --scrim-top: rgba(0,0,0,.22);
  --scrim-bottom: rgba(0,0,0,.5);
  --badge-bg: rgba(20,16,22,.82);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 15.5px/1.6 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Dilute pools of colour so a near-black page has somewhere to breathe, plus
   a grain layer. Grain is what stops large flat darks reading as a default
   template — it registers as texture rather than as an effect. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(1200px 700px at 8% -14%, var(--pool-1), transparent 62%),
    radial-gradient(1000px 600px at 96% -8%, var(--pool-2), transparent 60%),
    radial-gradient(900px 900px at 50% 120%, var(--pool-3), transparent 60%);
}
:root, :root[data-theme="dark"] {
  --pool-1: rgba(255,77,109,.10);
  --pool-2: rgba(255,169,77,.07);
  --pool-3: rgba(120,80,255,.05);
}
:root[data-theme="light"] {
  --pool-1: rgba(224,41,79,.07);
  --pool-2: rgba(217,138,26,.06);
  --pool-3: rgba(120,80,255,.04);
}
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

a { color: inherit; text-decoration: none; }
img, video { display: block; max-width: 100%; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 5px; }
::selection { background: var(--accent); color: #14050a; }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg-2); }
::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 99px;
  border: 3px solid var(--bg-2); }
::-webkit-scrollbar-thumb:hover { background: var(--line-lit); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------- utility */

.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(1rem, 3.5vw, 2.5rem); }
.muted { color: var(--muted); }
.gradient-text {
  background: var(--accent-grad);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* Reveal on scroll, driven by IntersectionObserver in site.js. The old
   nth-child stagger only covered the first ten cards; everything below the
   fold either animated off-screen or sat there already visible. */
/* Hidden ONLY once JS has confirmed it can reveal again. Without the
   .js-reveal gate, anything that stops the observer firing — no JS, an old
   browser, an error earlier in the file — leaves the page permanently blank.
   An animation must never be load-bearing for visibility. */
.reveal { opacity: 1; transform: none; }
.js-reveal .reveal { opacity: 0; transform: translateY(18px); }
.js-reveal .reveal.in { opacity: 1; transform: none;
  transition: opacity .55s var(--ease-out), transform .55s var(--ease-out); }
.js-reveal .grid > .reveal:nth-child(4n+2).in { transition-delay: .04s }
.js-reveal .grid > .reveal:nth-child(4n+3).in { transition-delay: .08s }
.js-reveal .grid > .reveal:nth-child(4n+4).in { transition-delay: .12s }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- head */

.site-header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border-bottom: 1px solid var(--line-soft);
}
.site-header .bar {
  display: flex; align-items: center; gap: clamp(.7rem, 2vw, 1.75rem); padding-block: .8rem;
}
/* Sits just outside the search pill, so it needs an edge of its own — as bare
   text beside a large rounded input it read as a stray label rather than a
   control. Matches the input's height and radius so the two look like one
   group. */
.search-adv {
  flex: none; margin-left: .5rem; padding: .5rem .95rem; border-radius: 999px;
  font-size: .82rem; font-weight: 800; letter-spacing: -.01em;
  color: var(--text-2); text-decoration: none; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line);
  display: inline-flex; align-items: center; gap: .35rem;
  transition: color .2s var(--ease), border-color .2s var(--ease),
              background .2s var(--ease);
}
.search-adv::before {
  /* Three sliders, drawn rather than an icon font. */
  content: ''; width: 13px; height: 13px; flex: none;
  background: currentColor;
  -webkit-mask: var(--adv-ico) center/contain no-repeat;
  mask: var(--adv-ico) center/contain no-repeat;
}
:root {
  --adv-ico: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'><path d='M4 7h10M18 7h2M4 12h4M12 12h8M4 17h12M20 17h0'/><circle cx='16' cy='7' r='2'/><circle cx='10' cy='12' r='2'/><circle cx='18' cy='17' r='2'/></svg>");
}
.search-adv:hover,
.search-adv:focus-visible {
  color: var(--text); border-color: var(--accent-line); background: var(--accent-soft);
}
@media (max-width: 620px) {
  /* The search field needs the width more than this label does. */
  .search-adv { padding: .5rem .7rem; font-size: 0; gap: 0; }
  .search-adv::before { width: 16px; height: 16px; }
}

/* Advanced search form. Signed-in only, so it never has to look good to a
   crawler — but it does have to be usable on a phone, where it is a column. */
.adv { display: grid; gap: 1rem; margin: 1.2rem 0; }
.adv-row { display: flex; gap: .8rem; flex-wrap: wrap; }
.adv-field { display: flex; flex-direction: column; gap: .3rem; flex: 1 1 11rem; min-width: 0; }
.adv-field.adv-wide { flex: 3 1 20rem; }
.adv-field > span { font-size: .8rem; font-weight: 700; color: var(--muted); }
/* Native selects render as the OS control — grey, square, nothing like the
   rest of the form. Appearance:none plus our own chevron makes them match the
   text inputs beside them. */
.adv select,
.adv input[type="search"],
.adv input[type="number"] {
  width: 100%; box-sizing: border-box;
  padding: .58rem .8rem; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--text); font: inherit; font-size: .9rem;
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.adv select {
  -webkit-appearance: none; appearance: none;
  padding-right: 2.1rem; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237c7c8a' stroke-width='2.4' stroke-linecap='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right .7rem center;
  background-size: 15px;
}
.adv select:hover { border-color: var(--line-lit); }
.adv select:focus, .adv input:focus {
  outline: none; border-color: var(--accent-line);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* The dropdown list itself is drawn by the OS, so its colours have to be set
   here or a dark page opens a white menu. */
.adv select option { background: var(--surface-2); color: var(--text); }

.adv-tags { border: 1px solid var(--line); border-radius: 10px; padding: .8rem; }
.adv-tagfilter {
  width: 100%; box-sizing: border-box; margin: .2rem 0 .6rem;
  padding: .45rem .7rem; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--text); font: inherit; font-size: .84rem;
}
.adv-tagfilter:focus {
  outline: none; border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft);
}
.adv-tagcount { margin: 0 0 .5rem; font-size: .78rem; color: var(--muted); }
.adv-tags legend { font-size: .82rem; font-weight: 700; padding: 0 .4rem; }
/* Capped and scrollable: a catalogue with three hundred tags would otherwise
   push the results button off the bottom of the page. */
.adv-tagwrap {
  display: flex; flex-wrap: wrap; gap: .4rem;
  max-height: 11rem; overflow-y: auto;
}
.adv-tag {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .22rem .6rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: .82rem; cursor: pointer;
}
/* A class that sets display outranks the UA stylesheet's [hidden] rule, so
   el.hidden = true did nothing and the filter changed only the counter. Fourth
   time this has bitten in this codebase — .kp-big, .kp-preview and .ad-slot
   were the others. If a rule here sets display, it needs its own [hidden]. */
.adv-tag[hidden] { display: none; }
.adv-tag:hover { border-color: var(--accent-line); }
.adv-tag input { width: auto; margin: 0; }
.adv-tag:has(input:checked) { background: var(--accent-soft); border-color: var(--accent-line); }
.adv-actions { display: flex; gap: .6rem; align-items: center; }
.pager { display: flex; gap: .6rem; justify-content: center; margin: 1.5rem 0; }

/* "Was the person's face visible?" — under the player and again on the end
   screen. The two share a class so they cannot drift apart. */
.facevote {
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  padding: .6rem .8rem; margin: .8rem 0;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel, transparent);
}
/* Set by an admin means there is nothing to vote on, so the widget goes
   entirely — see facevote.js. The display above outranks the UA's
   `[hidden] { display: none }`, so hiding it needs this line as well. */
.facevote[hidden] { display: none; }
.facevote-q { font-weight: 700; font-size: .92rem; }
.facevote-buttons { display: flex; gap: .4rem; }
.facevote-btn {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .3rem .7rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: transparent; color: var(--text);
  font: 700 .84rem/1 var(--font);
  transition: background .14s var(--ease), border-color .14s var(--ease);
}
.facevote-btn:hover:not(:disabled) { border-color: var(--accent-line); background: var(--accent-soft); }
.facevote-btn.on { background: var(--accent-grad); color: #180508; border-color: transparent; }
.facevote-btn:disabled { opacity: .5; cursor: default; }
.facevote-btn [data-face-count] { font-variant-numeric: tabular-nums; opacity: .8; }
.facevote-status { color: var(--muted); font-size: .82rem; margin-left: auto; }

@media (max-width: 560px) {
  .facevote-status { margin-left: 0; width: 100%; }
}

/* Visible to a screen reader, invisible to everyone else. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The "more tubes coming" tile.

   Still dashed, so it never reads as a real tube that lost its artwork — but
   unlit and transparent it read as a hole in the grid instead of a promise.
   It is the only tile with no poster behind it, so it gets its own light: a
   slow sweep across an accent wash, and a live badge rather than a line of
   small orange type nobody could read against the dark. */
.tile.tile-soon {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
  cursor: default;
  /* Fixed dark base rather than var(--surface): in light mode that variable is
     near-white, and under the scrim it came out as the flat grey this tile was
     being criticised for. The other tiles get their darkness from a poster;
     this one has to bring its own. */
  background:
    radial-gradient(130% 105% at 6% -12%, rgba(255, 77, 109, .58), transparent 58%),
    radial-gradient(115% 95% at 106% 110%, rgba(255, 169, 77, .46), transparent 56%),
    #120b16;
}
/* .tile::after is a near-opaque scrim, there so body text stays readable over a
   poster mosaic. This tile has no mosaic — but the text is still near-white in
   BOTH themes, so the scrim cannot simply be removed: in light mode that leaves
   white on white. It is recoloured instead. Same darkness, brand colour rather
   than the flat grey-to-black that made this read as a dead slab. */
.tile.tile-soon::after {
  background: linear-gradient(180deg,
    rgba(6,6,10,.24) 0%, rgba(6,6,10,.58) 52%, rgba(6,6,10,.86) 100%);
}

/* The sweep. Above the scrim, below the text — a lower layer would be buried by
   the scrim. Inset far enough that rotation never drags a corner into view. */
.tile.tile-soon::before {
  content: ''; position: absolute; inset: -45%; z-index: 1; pointer-events: none;
  background: conic-gradient(from 0deg,
    transparent 0 40%,
    rgba(255, 169, 77, .22) 50%,
    transparent 60% 100%);
  animation: soon-sweep 16s linear infinite;
}
.tile.tile-soon .body { position: relative; z-index: 2; }
.tile.tile-soon:hover { transform: none; box-shadow: none; }
.tile.tile-soon:hover { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); }
.tile.tile-soon .tile-host { opacity: 1; }

/* "In the works" as a badge with a live dot. The pill gives the small type a
   dark backing of its own, which is what it was missing — accent-2 on the
   gradient was near-illegible at this size. */
.tile.tile-soon .tile-tagline {
  display: inline-flex; align-items: center; gap: .45rem;
  margin-bottom: .7rem; padding: .3rem .66rem .3rem .58rem;
  color: var(--accent-2);
  background: rgba(6,6,10,.55);
  border: 1px solid color-mix(in srgb, var(--accent-2) 42%, transparent);
  border-radius: 999px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.tile.tile-soon .tile-tagline::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--accent-2);
  animation: soon-pulse 2.4s ease-out infinite;
}

@keyframes soon-sweep { to { transform: rotate(360deg); } }
@keyframes soon-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-2) 65%, transparent); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
/* Motion is decoration here; the tile says the same thing without it. */
@media (prefers-reduced-motion: reduce) {
  .tile.tile-soon::before, .tile.tile.tile-soon .tile-tagline::before { animation: none; }
}
.soon-form { display: flex; gap: .4rem; margin-top: .9rem; }
.soon-form input { flex: 1; min-width: 0; }
.soon-note { margin: .55rem 0 0; font-size: .84rem; color: var(--muted); }

.brand {
  font-size: 1.28rem; font-weight: 900; letter-spacing: -.045em;
  white-space: nowrap; display: inline-flex; align-items: baseline;
}
.brand .dot {
  background: var(--accent-grad); -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.brand small {
  font-size: .58rem; font-weight: 900; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent-2); margin-left: .55rem; padding: .25em .6em;
  border: 1px solid var(--accent-line); border-radius: 999px;
  background: var(--accent-soft); position: relative; top: -1px;
}

.search { flex: 1; max-width: 580px; position: relative; display: flex; }
.search input {
  width: 100%; padding: .62rem 1rem .62rem 2.5rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px; color: var(--text); font: inherit; font-size: .94rem;
  transition: border-color .2s var(--ease), background .2s var(--ease), box-shadow .2s var(--ease);
}
.search input::placeholder { color: var(--muted); }
.search input:hover { border-color: var(--line-lit); }
.search input:focus {
  outline: none; border-color: var(--accent);
  background: var(--surface-2); box-shadow: 0 0 0 4px var(--accent-soft);
}
.search svg { position: absolute; left: .95rem; top: 50%; translate: 0 -50%;
  width: 16px; height: 16px; color: var(--muted); pointer-events: none; }

.header-actions { margin-left: auto; display: flex; align-items: center; gap: .55rem; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .52rem 1.05rem; border-radius: 999px;
  font: 700 .875rem/1 var(--font); letter-spacing: -.005em;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  cursor: pointer; white-space: nowrap;
  transition: background .2s var(--ease), border-color .2s var(--ease),
              transform .12s var(--ease), box-shadow .2s var(--ease);
}
.btn:hover { background: var(--surface-2); border-color: var(--line-lit); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-primary {
  background: var(--accent-grad); border-color: transparent; color: #180508;
  box-shadow: 0 8px 22px -10px rgba(255,77,109,.7);
}
.btn-primary:hover { background: var(--accent-grad); filter: brightness(1.08); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
/* Ghost is for a SECONDARY action sitting beside a primary one. Used as the
   only control in a section it reads as unstyled text — which is exactly how
   the account page's Save and Download buttons looked. */
.btn-danger { background: var(--danger); border-color: transparent; color: #fff; }
.btn-danger:hover { background: var(--danger); filter: brightness(1.08); }
.btn-ghost:hover { color: var(--text); background: var(--surface); border-color: var(--line); }

.tabs { display: flex; gap: .3rem; padding-bottom: .7rem; overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  /* Flex items stretch to the tallest sibling — the "Most viewed" group. A
     plain block link would keep its text pinned to the top of the stretched
     box, so each link centres its own content instead. */
  display: inline-flex; align-items: center; justify-content: center;
  padding: .38rem .9rem; border-radius: 999px; font-size: .885rem; font-weight: 700;
  color: var(--muted); border: 1px solid transparent; white-space: nowrap;
  transition: color .18s var(--ease), background .18s var(--ease);
}
.tabs a:hover { color: var(--text); background: var(--surface); }
.tabs a.on {
  color: var(--text); background: var(--surface-2); border-color: var(--line);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05);
}

/* One control, four periods. The label is decorative — the links inside carry
   their own text, so a screen reader gets the group name from aria-label
   instead and does not hear "Most viewed" read twice. */
.tabgroup {
  display: inline-flex; align-items: center; gap: .15rem; flex: none;
  padding: .18rem .22rem .18rem .7rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
}
.tabgroup-label {
  font-size: .68rem; font-weight: 900; text-transform: uppercase;
  letter-spacing: .1em; color: var(--muted); padding-right: .5rem;
  border-right: 1px solid var(--line); margin-right: .25rem; white-space: nowrap;
}
.tabgroup a { padding: .3rem .7rem; font-size: .85rem; }

/*
 * Tags is not a sort. Every other pill reorders the same catalogue; this one
 * leaves for a different page, and as the sixth identical pill in the row it
 * read as a seventh way to sort.
 *
 * An accent outline and a leading glyph, pushed to the end of the row — near
 * enough to stay part of the control, distinct enough to read as a door. Not
 * a filled pill, which would outrank the sort the visitor is actually on.
 */
.tabs a.tab-tags {
  margin-left: auto; color: var(--accent);
  border-color: var(--accent-line); background: var(--accent-soft);
}
.tabs a.tab-tags::before {
  content: "#"; font-weight: 900; opacity: .6; margin-right: .28rem;
}
.tabs a.tab-tags:hover {
  color: var(--accent); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.tabs a.tab-tags.on {
  color: var(--bg); background: var(--accent); border-color: var(--accent);
}
/* When the country filter is present it is the real end of the row, and the
   auto margin above would otherwise push Tags past it. */
.tabs .country-filter { margin-left: 0; }

/* ------------------------------------------------------- recently viewed */

.recent-note { color: var(--muted); font-size: .85rem; max-width: 46ch; margin-inline: auto; }
.recent-clear { margin-top: 1.6rem; text-align: center; }
/* Sits directly above the grid it is explaining, so it reads as a caption on
   the takeover rather than as another section heading. */
.recent-took-over { margin-top: 0; }

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

main { padding-block: clamp(1.5rem, 3vw, 2.25rem) 5rem; }

.page-head { margin-bottom: 1.6rem; }
.page-head h1 {
  font-size: clamp(1.6rem, 3.6vw, 2.5rem); margin: 0 0 .3rem;
  letter-spacing: -.04em; font-weight: 900; line-height: 1.05;
}
.page-head p { margin: 0; color: var(--muted); font-size: 1rem; }
.host-head { margin-bottom: 2.25rem; }
.host-head h1 { font-size: clamp(2rem, 5.5vw, 3.6rem); letter-spacing: -.05em; margin-bottom: .45rem; }
.host-head p { color: var(--text-2); font-size: clamp(.98rem, 1.6vw, 1.1rem); max-width: 62ch; }
.host-head .eyebrow {
  display: inline-flex; align-items: center; gap: .5rem; margin-bottom: .7rem;
  font-size: .7rem; font-weight: 900; letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent-2);
}
.host-head .eyebrow::before {
  content: ""; width: 26px; height: 2px; border-radius: 2px; background: var(--accent-grad);
}

.section-label {
  display: flex; align-items: center; gap: .8rem;
  font-size: .72rem; font-weight: 900; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 1.1rem;
}
/* A label sitting directly under the previous grid's captions reads as part of
   that grid. Anything following a grid starts a genuinely new block: real air
   above it, and a rule to draw the line. */
.grid + .section-label {
  /* Modest, because the grid above already ends with its own row gap and the
     rule carries most of the separation on its own. */
  margin-top: clamp(1.5rem, 3vw, 2.2rem);
  padding-top: clamp(1.1rem, 2vw, 1.5rem);
  border-top: 1px solid var(--line-soft);
}
.section-label::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--line) 0%, transparent 70%);
}

/* ---------------------------------------------------------------- cards */

.grid {
  display: grid; gap: 1.9rem 1.15rem;
  grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
}
.grid.tight { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.grid.single { grid-template-columns: 1fr; gap: 1.1rem; }

.card { position: relative; }
.card .thumb {
  position: relative; aspect-ratio: 16/9; border-radius: var(--r);
  /* Black rather than a surface tint: it is the letterbox behind anything
     that is not 16:9, and it has to match the video's own bars exactly or the
     seam shows at the edges. */
  overflow: hidden; background: #000; border: 1px solid var(--line-soft);
  transition: transform .3s var(--ease-out), box-shadow .3s var(--ease-out),
              border-color .3s var(--ease);
}
/*
 * The still and the preview must fit the same way.
 *
 * The still was `cover` and the preview `contain`, so hovering a vertical
 * video showed the cropped still for the length of the video's fade-in and
 * then snapped to a letterboxed one — a visible jolt on every card, and the
 * "unproportioned still before the preview" that it looked like from outside.
 *
 * `contain` for both. For the 16:9 majority the two are identical and nothing
 * changes; only a portrait source letterboxes, and it did that a moment later
 * anyway.
 */
/* Thumbnail A/B: the candidate frame shown at rest, above the poster img but
   below the badges (z-index 2). Fades out on hover so the existing hover
   preview — sprite scrub, then clip — takes over untouched. */
.card .thumb-abframe {
  /* Same contain-fit as the scrub: portrait A/B frames letterbox, not squash. */
  position: absolute; top: 50%; left: 50%; translate: -50% -50%; z-index: 1;
  height: 100%; width: auto; max-width: 100%; max-height: 100%;
  aspect-ratio: var(--ar, 16 / 9);
  background-repeat: no-repeat; background-position: center;
  transition: opacity .2s var(--ease);
}
.card:hover .thumb-abframe { opacity: 0; }

.card .thumb img, .card .thumb video {
  width: 100%; height: 100%; object-fit: contain;
  transition: transform .55s var(--ease-out);
}
/*
 * The preview fits; the poster crops.
 *
 * They can differ because they are different things. The poster and the strip
 * are generated at 16:9, so `cover` on them crops nothing. The preview clip is
 * whatever shape the source was, and a lot of this catalogue is shot vertical
 * — `cover` on a 9:16 clip in a 16:9 box shows a narrow band through the
 * middle and cuts the head off, which is what it was doing.
 *
 * `contain` letterboxes it against the thumb's own background instead, so a
 * portrait preview is a small upright video rather than a cropped sliver.
 * Same fix as the scraper review player, for the same reason.
 */
.card .thumb video {
  position: absolute; inset: 0; opacity: 0;
  object-fit: contain; background: #000;
  transition: opacity .35s var(--ease);
}
.card .thumb video.on { opacity: 1; }

/* Sprite scrub: one already-generated JPEG, stepped through by cursor
   position. No stepping transition — it must track the pointer exactly. */
.thumb .scrub {
  /* Fit to the frame's real shape (JS sets --ar from the sprite) so a portrait
     clip is letterboxed like the preview video's object-fit:contain, not
     stretched into 16:9. Defaults to 16:9 until the sprite's aspect is known. */
  position: absolute; top: 50%; left: 50%; translate: -50% -50%; z-index: 1;
  height: 100%; width: auto; max-width: 100%; max-height: 100%;
  aspect-ratio: var(--ar, 16 / 9);
  background-repeat: no-repeat; background-position: 0 0;
  opacity: 0; transition: opacity .18s var(--ease);
}
.thumb .scrub.on { opacity: 1; }
.card .thumb video { z-index: 2; }
/* A hairline that fills as you sweep, so the scrub reads as a timeline. */
.thumb .scrub::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--accent-grad); opacity: .9;
}

/* Scrim so light badges stay readable over a bright poster. */
.card .thumb::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, var(--scrim-top) 0%, transparent 26%,
                                       transparent 62%, var(--scrim-bottom) 100%);
  opacity: .9; transition: opacity .3s var(--ease);
}
.card:hover .thumb { transform: translateY(-6px); border-color: var(--accent-line); box-shadow: var(--glow); }
/* No inner zoom. The still scaled to 1.06 on hover and the preview that
   replaces it did not, so the picture jumped 6% at the exact moment of the
   swap — the other half of the flash. The card already lifts, glows and
   changes border on hover; it does not need the image to move as well. */
.card:hover .thumb img { transform: none; }
.card:hover .thumb::after { opacity: .55; }

/* Play affordance — what makes a still read as a video rather than a photo. */
.card .play {
  position: absolute; inset: 0; margin: auto; width: 54px; height: 54px; z-index: 2;
  border-radius: 50%; display: grid; place-items: center;
  background: rgba(10,10,14,.55); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,.22);
  opacity: 0; transform: scale(.82); pointer-events: none;
  transition: opacity .28s var(--ease), transform .28s var(--ease-out);
}
.card .play svg { width: 19px; height: 19px; margin-left: 3px; fill: #fff; }
.card:hover .play { opacity: 1; transform: scale(1); }

.badge {
  position: absolute; bottom: .55rem; right: .55rem; z-index: 2;
  background: var(--badge-bg); backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.12); color: #fff;
  padding: .16rem .45rem; border-radius: 6px;
  font-size: .74rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.badge.quality {
  left: .55rem; right: auto; bottom: auto; top: .55rem;
  color: var(--accent-2); border-color: var(--accent-line);
  background: var(--badge-bg); letter-spacing: .03em;
}

.card h3 {
  font-size: .97rem; font-weight: 700; line-height: 1.35; letter-spacing: -.014em;
  margin: .72rem 0 .34rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  transition: color .2s var(--ease);
}
/* The accent, not #fff. White was a brighten against a dark card and became
   invisible the moment light mode existed — white text on a near-white
   background, readable only by selecting it. The accent reads as emphasis in
   both themes, which is what the hover was for. */
.card:hover h3 { color: var(--accent); }
.card .meta {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  color: var(--muted); font-size: .815rem;
}
.card .meta .sep { opacity: .35; }

.stars { display: inline-flex; gap: 1px; color: var(--star); font-size: .82rem; }
.stars .off { color: color-mix(in srgb, var(--muted) 45%, transparent); }

/* ------------------------------------------------------------- featured */

/* One large item ahead of the grid. A wall of identical thumbnails has no
   focal point, which is most of what makes a catalogue feel flat. */
.featured {
  display: grid; gap: clamp(1.2rem, 3vw, 2.4rem); margin-bottom: 3.25rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 900px) {
  .featured { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); align-items: center; }
}

.featured .shot {
  position: relative; aspect-ratio: 16/9; border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--lift);
}
.featured .shot img { width: 100%; height: 100%; object-fit: cover;
  transition: transform .7s var(--ease-out); }
.featured:hover .shot img { transform: scale(1.04); }
.featured .shot::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(160deg, transparent 40%, rgba(6,6,10,.55) 100%);
}
.featured .shot .play {
  position: absolute; inset: 0; margin: auto; width: 74px; height: 74px; z-index: 2;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--accent-grad); box-shadow: 0 14px 40px -12px rgba(255,77,109,.8);
  transition: transform .3s var(--ease-out);
}
.featured .shot .play svg { width: 26px; height: 26px; margin-left: 4px; fill: #180508; }
.featured:hover .shot .play { transform: scale(1.07); }

.featured .info .eyebrow {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: .7rem; font-weight: 900; letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: .85rem;
}
.featured .info .eyebrow::before {
  content: ""; width: 26px; height: 2px; border-radius: 2px; background: var(--accent-grad);
}
.featured .info h2 {
  font-size: clamp(1.5rem, 3.4vw, 2.5rem); font-weight: 900; letter-spacing: -.042em;
  line-height: 1.06; margin: 0 0 .85rem;
}
.featured .info p {
  color: var(--text-2); margin: 0 0 1.25rem; font-size: .98rem;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.featured .info .facts {
  display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; margin-bottom: 1.5rem;
  color: var(--muted); font-size: .85rem;
}
.featured .info .facts .pill {
  border: 1px solid var(--line); border-radius: 999px; padding: .22rem .68rem;
  background: var(--surface); font-weight: 700; font-size: .78rem; color: var(--text-2);
}

/* ------------------------------------------------------------- hub tiles */

/* Slow-drifting colour behind the hero. Two blurred blobs on long, offset
   cycles never visibly loop, and it is cheap: no canvas, no JS. */
@keyframes drift-a {
  0%,100% { transform: translate3d(-8%, -6%, 0) scale(1);   }
  50%     { transform: translate3d(10%,  6%, 0) scale(1.18);}
}
@keyframes drift-b {
  0%,100% { transform: translate3d(8%, 6%, 0) scale(1.12); }
  50%     { transform: translate3d(-9%, -5%, 0) scale(.94); }
}
.hub-hero {
  padding: clamp(3rem, 10vw, 7rem) 0 clamp(2rem, 5vw, 3.5rem);
  text-align: center; position: relative; isolation: isolate;
}
/* Full-bleed and feathered rather than clipped.
   Clipping with overflow:hidden left a hard-edged colour rectangle inset by
   the page gutter — it read as a box sitting on the page, not as ambient
   light. Breaking out to the full viewport width and letting a soft mask do
   all the fading means there is no edge to see. */
.hub-hero .mesh {
  position: absolute; z-index: -1; pointer-events: none;
  left: 50%; translate: -50% 0; width: 100vw;
  top: -45%; height: 190%;
  filter: blur(90px); opacity: .34;
  -webkit-mask-image: radial-gradient(58% 52% at 50% 42%, #000 0%, rgba(0,0,0,.55) 45%, transparent 72%);
  mask-image: radial-gradient(58% 52% at 50% 42%, #000 0%, rgba(0,0,0,.55) 45%, transparent 72%);
}
.hub-hero .mesh i {
  position: absolute; display: block; border-radius: 50%;
  width: 38vw; height: 38vw; max-width: 520px; max-height: 520px;
}
.hub-hero .mesh i:nth-child(1) { left: 6%;  top: -8%; background: var(--accent);
  animation: drift-a 26s ease-in-out infinite; }
.hub-hero .mesh i:nth-child(2) { right: 4%; top: -14%; background: var(--accent-2);
  animation: drift-b 32s ease-in-out infinite; }
:root[data-theme="light"] .hub-hero .mesh { opacity: .2; }
@media (prefers-reduced-motion: reduce) { .hub-hero .mesh i { animation: none; } }
.hub-hero h1 {
  font-size: clamp(2.6rem, 9vw, 5.5rem); font-weight: 900; letter-spacing: -.055em;
  margin: 0 0 .7rem; line-height: .98;
}
.hub-hero p { margin: 0 auto; max-width: 48ch; color: var(--text-2);
  font-size: clamp(1rem, 1.8vw, 1.2rem); }

.tiles { display: grid; gap: 1.35rem; justify-content: center;
  grid-template-columns: repeat(auto-fit, minmax(300px, 440px)); }
.tile {
  position: relative; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); overflow: hidden; min-height: 280px;
  transition: transform .32s var(--ease-out), border-color .32s var(--ease),
              box-shadow .32s var(--ease-out);
}
.tile:hover { transform: translateY(-7px); border-color: var(--accent-line); box-shadow: var(--glow); }
.tile .mosaic { position: absolute; inset: 0; display: flex; opacity: .6; filter: saturate(.95);
  transition: opacity .4s var(--ease), transform .7s var(--ease-out); }
.tile:hover .mosaic { opacity: .8; transform: scale(1.05); }
.tile .mosaic img { flex: 1 1 0; min-width: 0; height: 100%; object-fit: cover; }
.tile::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6,6,10,.12) 0%, rgba(6,6,10,.75) 48%,
                                       rgba(6,6,10,.96) 100%);
}
.tile .body { position: relative; z-index: 1; margin-top: auto; padding: 1.4rem 1.5rem 1.5rem; }
.tile { color: #f6f6f8; }
.tile p { color: #c9c6d0 !important; }
.tile h2 { margin: 0 0 .4rem; font-size: 1.55rem; letter-spacing: -.04em; font-weight: 900; }
/* Matches the wordmark: the dots carry the gradient, the words don't. */
.tile-host .dot {
  background: var(--accent-grad); -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.tile-tagline {
  display: block; font-size: .7rem; font-weight: 900; letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent-2); margin-bottom: .45rem;
}
.tile p { margin: 0 0 1rem; color: var(--text-2); font-size: .92rem; }
.tile .count {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .76rem; font-weight: 900; letter-spacing: .04em; color: #fff;
  border: 1px solid rgba(255,255,255,.28); border-radius: 999px; padding: .26rem .7rem;
  background: rgba(6,6,10,.55); backdrop-filter: blur(6px);
}
/* The plain-language name, where the eye lands first on a tile. */
.tile-badge {
  position: absolute; top: 1.1rem; right: 1.15rem; z-index: 2;
  padding: .34rem .8rem; border-radius: 999px;
  font-size: .78rem; font-weight: 900; letter-spacing: .03em;
  background: var(--accent-grad); color: #180508;
  box-shadow: 0 8px 22px -10px rgba(0,0,0,.9);
}

.tile-foot { display: flex; align-items: center; gap: .8rem; }
.tile .enter {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .8rem; font-weight: 900; letter-spacing: .04em; color: #fff;
  opacity: 0; translate: -6px 0;
  transition: opacity .28s var(--ease), translate .28s var(--ease-out);
}
.tile .enter svg { width: 14px; height: 14px; }
.tile:hover .enter { opacity: 1; translate: 0 0; }

/* Subdomain carries the weight; the shared domain recedes so two tiles do not
   read as the same word twice. */
.tile-host .lead { font-weight: 900; }
.tile-host .rest { font-weight: 700; opacity: .62; font-size: .82em; letter-spacing: -.02em; }

/* ------------------------------------------------------------ empty/pager */

.empty {
  border: 1px dashed var(--line); border-radius: var(--r-lg);
  padding: 4rem 1.5rem; text-align: center; color: var(--muted);
  background: linear-gradient(180deg, var(--surface) 0%, transparent 100%);
}

/* "Being watched" needs to read as live without animating anything expensive —
   one small pulsing dot, and nothing at all for reduced-motion. */
.live-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); margin-right: .5rem; vertical-align: middle;
  box-shadow: 0 0 0 0 var(--accent-line); animation: livePulse 2.2s var(--ease) infinite;
}
@keyframes livePulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-line); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }

.section-more {
  margin-left: auto; font-size: .8rem; font-weight: 700;
  color: var(--muted); text-transform: none; letter-spacing: 0;
}
.section-more:hover { color: var(--accent); }

.pager-feed { flex-direction: column; gap: .8rem; }
.pager-feed .load-more { padding: .7rem 1.6rem; font-size: .95rem; }
.pager-status { margin: 0; color: var(--muted); font-size: .86rem; min-height: 1.2em; }

.pager { display: flex; gap: .6rem; justify-content: center; align-items: center; margin-top: 3rem; }
.pager a, .pager span {
  padding: .58rem 1.15rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: .885rem; font-weight: 700;
}
.pager a:hover { background: var(--surface-2); border-color: var(--accent-line); }
.pager span { color: var(--muted); border-color: transparent; }

/* ---------------------------------------------------------------- video */

.watch { display: grid; gap: 2.4rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1160px) { .watch { grid-template-columns: minmax(0,1fr) 360px; } }

.player-shell {
  position: relative; background: #000; border-radius: var(--r);
  overflow: hidden; aspect-ratio: 16/9; border: 1px solid var(--line); box-shadow: var(--lift);
}
.player-shell video { width: 100%; height: 100%; }

.watch h1 { font-size: clamp(1.25rem, 2.6vw, 1.85rem); letter-spacing: -.035em;
  margin: 1.2rem 0 .7rem; font-weight: 900; line-height: 1.15; }
.watch-meta { display: flex; flex-wrap: wrap; gap: .85rem; align-items: center;
  color: var(--muted); font-size: .9rem; }
.watch-meta .pill {
  border: 1px solid var(--line); border-radius: 999px; padding: .22rem .7rem;
  background: var(--surface); font-size: .79rem; font-weight: 700; color: var(--text-2);
}

.taglist { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1.25rem; }
.taglist a {
  padding: .34rem .85rem; border-radius: 999px; font-size: .85rem; font-weight: 700;
  background: var(--surface); border: 1px solid var(--line); color: var(--text-2);
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.taglist a:hover { color: var(--text); border-color: var(--accent-line); background: var(--accent-soft); }

/* People also searched — search-demand chips, quieter than tags so the tags
   stay the primary row, with a small magnifier to read as searches. */
.also-searched { margin-top: 1.15rem; }
.also-h { display: block; font-size: .78rem; font-weight: 800; letter-spacing: .02em;
  text-transform: uppercase; color: var(--muted); margin-bottom: .5rem; }
.also-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.also-chips a {
  padding: .3rem .7rem .3rem .62rem; border-radius: 999px; font-size: .82rem; font-weight: 600;
  color: var(--text-2); background: transparent; border: 1px dashed var(--line);
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.also-chips a::before { content: "\1F50D"; margin-right: .32rem; font-size: .72rem; opacity: .6; }
.also-chips a:hover { color: var(--text); border-color: var(--accent-line); background: var(--accent-soft); }

.desc {
  margin-top: 1.4rem; padding: 1.2rem 1.35rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  white-space: pre-wrap; color: var(--text-2); font-size: .94rem;
}

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

.legal { max-width: 72ch; margin-inline: auto; }
.legal h1 { font-size: clamp(1.7rem, 3.4vw, 2.4rem); letter-spacing: -.04em; margin: 0 0 .4rem;
  font-weight: 900; }
.legal .updated { color: var(--muted); font-size: .85rem; margin: 0 0 2.5rem; }
.legal h2 {
  font-size: 1.1rem; margin: 2.6rem 0 .7rem; letter-spacing: -.022em; font-weight: 900;
  padding-top: 1.5rem; border-top: 1px solid var(--line-soft);
}
.legal h2:first-of-type { border-top: 0; padding-top: 0; }
.legal h3 { font-size: .96rem; margin: 1.6rem 0 .45rem; font-weight: 700; }
.legal p, .legal li { color: var(--text-2); font-size: .97rem; line-height: 1.75; }
.legal ul, .legal ol { padding-left: 1.2rem; }
.legal li + li { margin-top: .45rem; }
.legal a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.legal strong { color: var(--text); font-weight: 700; }
.legal address {
  font-style: normal; background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--accent); border-radius: var(--r-sm);
  padding: 1rem 1.2rem; color: var(--text-2); font-size: .93rem;
}
.legal .notice {
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--accent); border-radius: var(--r-sm);
  padding: 1.1rem 1.3rem; margin: 1.6rem 0; font-size: .93rem; color: var(--text-2);
}

/* ---------------------------------------------------------------- footer */

.site-footer { border-top: 1px solid var(--line-soft); margin-top: 5rem; background: var(--bg-2); }
.site-footer .inner { padding-block: 2.75rem; display: grid; gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.site-footer h4 { font-size: .7rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 .85rem; font-weight: 900; }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.site-footer a { color: var(--text-2); font-size: .9rem; }
.site-footer a:hover { color: var(--accent); }
.site-footer .legal-line {
  border-top: 1px solid var(--line-soft); padding-block: 1.4rem;
  color: var(--muted); font-size: .8rem; display: flex; gap: 1.1rem; flex-wrap: wrap;
  align-items: center;
}
.rta { border: 1px solid var(--line); border-radius: 5px; padding: .16rem .5rem;
  font-size: .66rem; letter-spacing: .1em; font-weight: 900; color: var(--muted); }

/* ----------------------------------------------------------- auth forms */

.authbox { max-width: 400px; margin: 7vh auto 0; }
.authbox h1 { font-size: 1.85rem; letter-spacing: -.04em; margin: 0 0 .45rem; font-weight: 900; }
.authsub { color: var(--muted); margin: 0 0 1.7rem; font-size: .95rem; }
.authform {
  display: grid; gap: .35rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 1.6rem; box-shadow: var(--lift);
}
.authform label { font-size: .8rem; color: var(--muted); margin-top: .75rem; font-weight: 700; }
.authform label:first-child { margin-top: 0; }
.authform input {
  padding: .68rem .85rem; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--text); font: inherit; font-size: .95rem;
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.authform input:focus { outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft); }
.authform .hint { color: var(--muted); font-size: .79rem; margin: .35rem 0 0; }
.authform button { margin-top: 1.35rem; width: 100%; padding-block: .68rem; }
.authalt { text-align: center; color: var(--muted); font-size: .89rem; margin: 1.1rem 0 0; }
.authalt a { color: var(--accent); font-weight: 700; }

.flash { padding: .85rem 1rem; border-radius: var(--r-sm); font-size: .9rem; margin-bottom: 1.2rem; }
.flash ul { margin: 0; padding-left: 1.1rem; }
.flash.err { background: rgba(255,107,107,.1); border: 1px solid rgba(255,107,107,.35); color: #ffb3b3; }
.flash.ok  { background: rgba(70,201,139,.09); border: 1px solid rgba(70,201,139,.3); color: #a9e8c8; }

/* --------------------------------------------------- rating + comments */

/* Reversed in the DOM so plain CSS sibling selectors light the stars to the
   LEFT of the hovered one — no JS for the hover state, and it still degrades
   to a real form without scripting. */
.rate-widget { display: inline-flex; flex-direction: row-reverse; gap: .1rem; }
.rate-widget input { position: absolute; opacity: 0; width: 0; height: 0; }
.rate-widget label {
  cursor: pointer; font-size: 1.65rem; line-height: 1; padding: 0 .04em;
  color: color-mix(in srgb, var(--muted) 45%, transparent);
  transition: color .14s var(--ease), transform .14s var(--ease-out);
}
.rate-widget input:checked ~ label,
.rate-widget label:hover,
.rate-widget label:hover ~ label { color: var(--star); }
.rate-widget label:hover { transform: scale(1.2) translateY(-2px); }


.comments { margin-top: 2.75rem; }
/* Bubbles, not rules.
   These used to be rows separated by a hairline, which on a light background
   read as one continuous block of text — the name and the words had nothing
   holding them together. The surface does that job, and the separators come
   out: two devices for the same purpose is what made it look busy and still
   blend. */
.comment {
  display: grid; grid-template-columns: 40px 1fr; gap: .8rem;
  padding: .4rem 0;
}
.comment .bubble {
  /* White on the warm off-white page. --surface-2 was #f5f2f1 against a #faf8f7
     background — five values apart out of 255, which is not a surface, it is
     the same colour. That is what "blends in" meant. */
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 4px 16px 16px 16px; padding: .6rem .9rem .7rem;
  box-shadow: 0 1px 2px rgb(0 0 0 / .04);

  /* Hug the words. A bubble that always spans the column is a slab: "Wow hot!"
     rendered 970px wide, which reads as an empty box with a few words parked in
     the corner. inline-block lets a short remark look short. */
  display: inline-block; max-width: 100%;
}
.comment .av {
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  background: var(--accent-grad); color: #180508; font-weight: 900; font-size: .95rem;
}
.comment .who { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.comment .who strong { font-size: .92rem; font-weight: 700; }
.comment .who time { color: var(--muted); font-size: .79rem; }
.comment .bubble p { margin: .3rem 0 0; color: var(--text-2); font-size: .94rem;
  white-space: pre-wrap; overflow-wrap: anywhere; }
.comment-form textarea {
  width: 100%; min-height: 92px; resize: vertical; padding: .8rem .95rem;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  color: var(--text); font: inherit; font-size: .94rem;
}
.comment-form textarea:focus { outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft); }
.comment-form .row { display: flex; justify-content: space-between; align-items: center;
  margin-top: .7rem; gap: .8rem; }
.signin-prompt {
  padding: 1.15rem 1.3rem; border: 1px dashed var(--line); border-radius: var(--r);
  color: var(--muted); font-size: .92rem; background: var(--surface);
}
.signin-prompt a { color: var(--accent); font-weight: 700; }


.country-filter { display: inline-flex; margin-left: .3rem; }
.country-filter select {
  background: var(--surface); border: 1px solid var(--line); color: var(--text-2);
  border-radius: 999px; padding: .3rem .7rem; font: 700 .84rem/1 var(--font);
  cursor: pointer;
}
.country-filter select:focus { outline: none; border-color: var(--accent); }

.report-row { margin-top: 1.4rem; font-size: .9rem; }
.report-row .note { color: var(--muted); font-size: .86rem; }
.report-row .note a { color: var(--accent); font-weight: 700; }
.report-row summary {
  cursor: pointer; color: var(--muted); font-size: .88rem; font-weight: 700;
  display: inline-block; padding: .3rem 0;
}
.report-row summary:hover { color: var(--text); }
.report-row form {
  display: grid; gap: .6rem; margin-top: .7rem; max-width: 520px;
  padding: 1.1rem; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r);
}
.report-row select, .report-row textarea {
  width: 100%; padding: .55rem .7rem; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text);
  font: inherit; font-size: .9rem;
}
.report-row select:focus, .report-row textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.report-row .row { display: flex; justify-content: space-between; align-items: center; gap: .8rem; }

/* ---------------------------------------------------------- theme toggle */

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
  transition: color .18s var(--ease), border-color .18s var(--ease),
              background .18s var(--ease);
}
.theme-toggle:hover { color: var(--text); border-color: var(--line-lit); background: var(--surface-2); }
.theme-toggle svg { width: 17px; height: 17px; }
/* One button, three states — show the icon matching the current preference. */
.theme-toggle [data-icon] { display: none; }
[data-theme-pref="auto"]  .theme-toggle [data-icon="auto"],
[data-theme-pref="light"] .theme-toggle [data-icon="light"],
[data-theme-pref="dark"]  .theme-toggle [data-icon="dark"] { display: block; }
:root:not([data-theme-pref]) .theme-toggle [data-icon="auto"] { display: block; }


/* -------------------------------------------------------------- mobile */

@media (max-width: 720px) {
  .site-header .bar { flex-wrap: wrap; row-gap: .55rem; padding-block: .65rem; }
  .search { order: 3; flex-basis: 100%; max-width: none; }
  .header-actions { gap: .35rem; }
  .header-actions .btn { padding: .45rem .75rem; font-size: .82rem; }
  .brand { font-size: 1.1rem; }

  .grid { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1.4rem .8rem; }
  /* Below this a two-up grid gives thumbnails too small to judge; one-up with
     a bigger image is the better trade on a phone. */
  @media (max-width: 460px) { .grid { grid-template-columns: 1fr; } }

  .featured { gap: 1rem; }
  .featured .info h2 { font-size: clamp(1.4rem, 7vw, 2rem); }
  .hub-hero h1 { font-size: clamp(2.2rem, 12vw, 3.2rem); }
  .tiles { grid-template-columns: 1fr; }
  .tile { min-height: 220px; }

  .watch { gap: 1.5rem; }
  .site-footer .inner { gap: 1.25rem; }
  .authbox { margin-top: 3vh; }
}

/* Touch devices get no hover layers at all — they fire on tap and fight the
   navigation the tap was for. */
@media (hover: none) {
  .card .play { opacity: .9; transform: none; width: 44px; height: 44px; }
  .card:hover .thumb { transform: none; box-shadow: none; }
}

/* ------------------------------------------------------------------ ads */

/* Slots are empty until js/ads.js fills them, and stay empty when it is
   blocked. Nothing here may reserve space before a fill: a permanent grey
   rectangle where an ad failed to load is worse than no ad at all. */
.ad-slot { display: block; }
.ad-slot:empty, .ad-slot.ad-empty { display: none; }
.ad-box { display: flex; justify-content: center; margin-inline: auto; max-width: 100%; }
.ad-box img, .ad-box iframe { max-width: 100%; border: 0; display: block; }

.ad-header { margin: 1.1rem auto 0; }
.ad-footer { margin: 2.5rem auto 1rem; }
.ad-under-player { margin: 1rem 0; }
.ad-sidebar { margin-bottom: 1.4rem; }

/*
 * The under-player ad gets a column, not a band.
 *
 * It was a full-width row between the player and the title: a 300px unit
 * centred in a 1000px space, so most of that row was empty margin and the
 * title was pushed a screen further down for nothing. It sits in a narrow
 * left-hand column now with the title, meta and tags beside it, which fills
 * the space that was blank and pulls everything else up.
 *
 * Explicit columns rather than auto-flow: the ad claims column one on its own
 * row, everything after it claims column two, and the player and comments —
 * the two things that genuinely want the full width — span both.
 */
/*
 * Two children, not a dozen.
 *
 * The first version made .watch-main itself the grid and gave every child an
 * explicit column. Auto-placement then put each one on its own row, so the row
 * holding the 500px ad pushed everything after it down — the meta ended up
 * 800px below the title, the tags 365px below that, the whole column strung
 * out down the page. Wrapping the body means the grid has exactly two items to
 * place and the content inside stacks the way it always did.
 */
@media (min-width: 900px) {
  .watch-below {
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr);
    gap: 1.6rem;
    align-items: start;
  }
  .watch-below > .ad-under-player {
    /* Matches .watch h1's top margin, so the ad and the title start on the
       same line instead of the ad butting straight into the player. */
    margin: 1.2rem 0 0;
    /* Follows the reader down past the title and tags rather than scrolling
       out of view a second after it arrives. */
    position: sticky;
    top: 1rem;
  }
}

/* In-feed native. Shaped like a card so it sits in the grid without breaking
   the rhythm, and labelled so it is still recognisably an ad. */
.card-ad { position: relative; display: flex; align-items: center; justify-content: center;
  min-height: 180px; border-radius: var(--r); background: var(--surface);
  border: 1px solid var(--line-soft); overflow: hidden; }
.card-ad .ad-slot-native { width: 100%; display: flex; justify-content: center; }
/*
 * Holds the space while the creative loads.
 *
 * The card is now inserted before it is filled, so without this it would land
 * at min-height and grow — trading a late insertion for a late resize, which
 * is the same jolt. 16/9 matches a video card's thumb, so the grid row is its
 * final height from the moment the batch appears. The slot's own `:empty`
 * collapse has to be overridden or the box would be flat.
 */
.card-ad.ad-reserved { aspect-ratio: 16 / 9; min-height: 0; }
.card-ad.ad-reserved .ad-slot-native { height: 100%; align-items: center; }
.card-ad.ad-reserved .ad-slot:empty { display: block; width: 100%; height: 100%; }
.ad-label {
  position: absolute; top: .5rem; left: .5rem; z-index: 2;
  padding: .1rem .45rem; border-radius: 4px;
  font-size: .64rem; font-weight: 900; letter-spacing: .1em; text-transform: uppercase;
  background: rgba(0, 0, 0, .6); color: #fff; backdrop-filter: blur(4px);
}

/*
 * A blocked slot carries a whitelist notice, not an ad.
 *
 * adblock.js drops the notice in as a sibling of the slot, and the notice
 * brings its OWN card — border, background, rounded corners, padding. The unit
 * it lands in (an in-feed card, the floating instant-message box) draws a box
 * of its own for a real creative, so with a notice inside you get a box inside
 * a box, the inner one adrift in the middle of the outer. When the slot is
 * marked [data-noticed], the container sheds its chrome and lets the notice be
 * the only box. Matched on the slot the notice sits beside, so a real ad in the
 * same unit keeps its frame.
 */
.card-ad:has(.ad-slot[data-noticed]) {
  min-height: 0; background: none; border: 0; border-radius: 0;
  overflow: visible; display: block;
}
/* Not an ad, so drop the "Ad" label with the frame. */
.card-ad:has(.ad-slot[data-noticed]) .ad-label { display: none; }
/* The float keeps its lift (shadow) and rounded clip, loses the second frame. */
.ad-im:has(.ad-slot[data-noticed]) { background: none; border: 0; }
/* The notice card carries a 1rem bottom margin (right for an in-flow slot);
   trapped inside the float's overflow:hidden it became a 16px transparent strip
   under the card that the float's shadow drew a box around. Drop it here — the
   card is the whole float. (Inline style on the notice, so !important.) */
.ad-im:has(.ad-slot[data-noticed]) > :last-child { margin-bottom: 0 !important; }

/*
 * Restore the gap a collapsed slot took with it.
 *
 * The under-player and footer slots reserve a top margin so the ad does not
 * butt against the player or the content above. When the slot is blocked it
 * goes display:none and that margin vanishes, leaving the whitelist notice
 * flush against the player. The notice carries inline styles, so !important is
 * needed to reach past them and put the breathing room back. (The header is
 * handled by the tight notice's own top margin; the float and in-feed card
 * place their notice themselves.)
 */
.ad-under-player[data-noticed] + *,
.ad-footer[data-noticed] + * { margin-top: 1.2rem !important; }

/* ------------------------------------------------------------ player ads */

.player-shell { position: relative; }

.ad-preroll {
  position: absolute; inset: 0; z-index: 5; background: #000;
  display: flex; flex-direction: column;
}
/*
 * No height: 100% here, and min-height: 0 instead.
 *
 * The layer is a flex column of video-then-bar. `height: 100%` made the video
 * exactly as tall as the whole layer, so the bar was laid out immediately
 * after it — past the bottom edge, off the player entirely. The skip button
 * was there the whole time, enabled and counting down, just not on screen; the
 * only way to get past a pre-roll was to sit through it.
 *
 * min-height: 0 is the other half. A flex item's default min-height is auto,
 * which refuses to shrink below the content's intrinsic size, so a tall ad
 * would push the bar off again even without the explicit height.
 */
.ad-preroll video {
  flex: 1; min-height: 0; width: 100%;
  object-fit: contain; background: #000;
}
.ad-preroll-bar {
  display: flex; align-items: center; gap: .6rem; padding: .5rem .7rem;
  background: rgba(0, 0, 0, .82); color: #fff; font-size: .8rem; font-weight: 700;
}
.ad-preroll-bar > span:first-child {
  padding: .1rem .4rem; border-radius: 3px; background: rgba(255, 255, 255, .16);
  font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
}
.ad-btn {
  margin-left: auto; padding: .32rem .7rem; border-radius: 999px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .3); background: transparent; color: #fff;
  font: 700 .78rem/1 var(--font);
}
.ad-btn + .ad-btn { margin-left: 0; }
.ad-btn:disabled { opacity: .55; cursor: default; }
.ad-btn:not(:disabled):hover { background: rgba(255, 255, 255, .14); }

/* Overlay sits across the lower third, clear of the native controls. */
.ad-overlay {
  position: absolute; left: 50%; translate: -50% 0; bottom: 3.6rem; z-index: 4;
  max-width: 92%; display: flex; align-items: flex-start; gap: .3rem;
  background: rgba(0, 0, 0, .55); border-radius: var(--r-sm); padding: .3rem;
  backdrop-filter: blur(6px);
}
.ad-close {
  width: 22px; height: 22px; flex: none; border-radius: 50%; cursor: pointer;
  border: 0; background: rgba(0, 0, 0, .7); color: #fff; font-size: 1rem; line-height: 1;
}
.ad-close:hover { background: #000; }

/* Spans the player only so the card can be centred. No background and no
   pointer events: everything around the ad — play, scrubber, controls — stays
   live. Dimming the whole video made dismissing the ad the only possible
   action, and then hid the control that did it. */
.ad-pause {
  position: absolute; inset: 0; z-index: 4;
  display: grid; place-items: center;
  background: none; pointer-events: none;
}
/* The ad and its own close button, as one block. */
.ad-pause-card {
  pointer-events: auto;
  display: flex; flex-direction: column; align-items: flex-end; gap: .4rem;
  max-width: min(92%, 340px);
  padding: .5rem; border-radius: .8rem;
  background: rgba(6, 6, 10, .82);
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: 0 18px 50px -16px rgba(0, 0, 0, .9);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
/* Directly above the ad, labelled, and big enough for a fingertip. */
.ad-pause-close {
  display: inline-flex; align-items: center; gap: .35rem; cursor: pointer;
  padding: .35rem .7rem; border-radius: 999px;
  background: rgba(255, 255, 255, .1); color: #fff;
  border: 1px solid rgba(255, 255, 255, .34);
  font: inherit; font-size: .78rem; font-weight: 700; line-height: 1;
}
.ad-pause-close:hover,
.ad-pause-close:focus-visible { background: rgba(255, 255, 255, .2); border-color: #fff; }
.ad-pause-card .ad-slot { pointer-events: auto; }
/* Shown in place of an ad that a blocker stopped. Deliberately quiet: it is a
   line of text where an ad would have been, not a wall. The player stays fully
   usable behind it, and Close ad still dismisses it. */
.ad-pause-card.ad-pause-note { align-items: stretch; max-width: min(94%, 380px); }
.ad-pause-note .ad-slot { padding: .2rem .6rem .5rem; text-align: left; }
.ad-pause-note p { margin: 0 0 .4rem; font-size: .82rem; line-height: 1.45; color: var(--text-2, #c9c6d0); }
.ad-pause-note p:last-child { margin-bottom: 0; }
.ad-pause-note strong { color: #fff; }

@media (max-width: 620px) {
  .ad-overlay { bottom: 3rem; }
}

/* ------------------------------------------------- link exchange (public) */

.linkform { display: grid; gap: 1rem; max-width: 640px; }
/* Sections on the account page are label / body / action stacked, and the
   action was landing hard against the hint above it while the next section
   label crowded in underneath. Give the block real air at the bottom and let
   the label supply its own space above. */
.linkform + .section-label, .acct-cards + .section-label { margin-top: 2.4rem; }
.linkform > div > .btn, .linkform > div > button.btn { margin-top: .2rem; }
.linkform:last-of-type { margin-bottom: 2.5rem; }
.linkform .row { display: grid; gap: 1rem; grid-template-columns: 1fr 1fr; }
.linkform label { display: block; font-size: .82rem; font-weight: 700; color: var(--text-2);
                  margin-bottom: .3rem; }
.linkform input, .linkform textarea, .linkform select {
  width: 100%; padding: .62rem .75rem; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line); color: var(--text);
  font: inherit; font-size: .92rem;
}
.linkform textarea { resize: vertical; min-height: 84px; }
.linkform input:focus, .linkform textarea:focus, .linkform select:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.linkform .hint { color: var(--muted); font-size: .8rem; margin: .3rem 0 0; line-height: 1.5; }
.linkform .radios { display: flex; gap: 1.2rem; }
.linkform .radios label { display: flex; align-items: center; gap: .4rem; font-weight: 400;
                          color: var(--text); margin: 0; }
.linkform .radios input { width: auto; }

.copybox {
  display: flex; gap: .5rem; align-items: stretch; margin: .5rem 0 0;
}
.copybox code {
  flex: 1; padding: .6rem .7rem; border-radius: var(--r-sm); overflow-x: auto;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
  font-size: .78rem; white-space: pre; line-height: 1.5;
}

.partner-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.partner {
  display: block; padding: 1rem 1.1rem; border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--line-soft);
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.partner:hover { border-color: var(--accent-line); transform: translateY(-2px); }
.partner strong { display: block; color: var(--text); font-size: .98rem; }
.partner p { margin: .3rem 0 0; color: var(--muted); font-size: .85rem; line-height: 1.5; }
.partner img { max-width: 100%; height: auto; border-radius: var(--r-sm); margin-bottom: .6rem; }

/* ------------------------------------------------------------- account */

.acct-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
             margin-bottom: 2.5rem; }
.acct-card {
  display: block; padding: 1.2rem 1.3rem; border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--line-soft);
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.acct-card:hover { border-color: var(--accent-line); transform: translateY(-2px); }
.acct-card strong { display: block; color: var(--text); font-size: 1rem; }
.acct-card p { margin: .3rem 0 0; color: var(--muted); font-size: .87rem; }

/* The save button sits over the thumbnail, so it needs to survive both a dark
   poster and a light one. */
.fav {
  position: absolute; top: .5rem; right: .5rem; z-index: 3;
  width: 34px; height: 34px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; padding: 0;
  border: 0; background: rgba(0, 0, 0, .55); color: #fff;
  backdrop-filter: blur(4px);
  opacity: 0; transition: opacity .18s var(--ease), background .18s var(--ease);
}
.card:hover .fav, .fav.on, .fav:focus-visible { opacity: 1; }
.fav svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; }
.fav.on { color: var(--accent); background: rgba(0, 0, 0, .7); }
.fav.on svg { fill: currentColor; stroke: currentColor; }
.fav:hover { background: rgba(0, 0, 0, .8); }

/* On the watch page it is a normal button in the meta row, not an overlay. */
.fav-inline {
  position: static; width: auto; height: auto; opacity: 1; border-radius: 999px;
  padding: .4rem .85rem; gap: .4rem; display: inline-flex; align-items: center;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
  font: 700 .84rem/1 var(--font);
}
.fav-inline.on { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }

/* Share cluster — one pill per network, each wearing its brand logo and colour
   at REST (not only on hover) so the row reads unmistakably as "share this",
   set apart from the neutral Save / Playlist / Copy actions beside it. */
.share { display: inline-flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
/* A leading label so the purpose is stated, not inferred. */
.share-label {
  display: inline-flex; align-items: center; gap: .35rem;
  color: var(--text-2); font: 800 .82rem/1 var(--font); margin-right: .1rem;
}
.share-label svg { width: 15px; height: 15px; fill: currentColor; }
.share-btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .45rem .8rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
  font: 700 .82rem/1 var(--font); cursor: pointer; text-decoration: none;
  transition: background .12s, color .12s, border-color .12s;
}
/* The copy-link icon is a stroked glyph; brand logos are solid fills. */
.share-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.share-btn.brand svg { width: 17px; height: 17px; fill: currentColor; stroke: none; }
/* Brand pills at rest: brand-coloured logo + text on a faint brand tint. */
.share-tg { color: #1f93cc; border-color: rgba(34, 158, 217, .4); background: rgba(34, 158, 217, .1); }
.share-wa { color: #17a34a; border-color: rgba(37, 211, 102, .42); background: rgba(37, 211, 102, .12); }
.share-rd { color: #e23d00; border-color: rgba(255, 69, 0, .4); background: rgba(255, 69, 0, .1); }
.share-x  { color: var(--text); border-color: var(--line); background: var(--surface-2); }
/* On hover each fills with its full brand colour. */
.share-btn.brand:hover { color: #fff; border-color: transparent; }
.share-tg:hover { background: #229ED9; }
.share-wa:hover { background: #25D366; }
.share-rd:hover { background: #FF4500; }
.share-x:hover  { background: #0f1419; color: #fff; }
.share-copy:hover { background: var(--accent); color: #fff; border-color: transparent; }
.share-copy.ok { background: #25a35a; color: #fff; border-color: transparent; }
@media (max-width: 560px) {
  .share-btn { padding: .42rem .7rem; font-size: .78rem; }
}

/* Playlists page — encourage a free account so the reader can build and share
   their own collections. Signed-in members get the same card pointing at their
   playlist manager instead. */
.pl-cta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem 1.4rem; margin: 0 0 1.8rem;
  padding: 1.1rem 1.3rem; border-radius: var(--r);
  border: 1px solid var(--accent-line, var(--line));
  background: linear-gradient(120deg, var(--accent-soft, rgba(255,77,109,.1)), transparent 70%), var(--surface);
}
.pl-cta-text { display: flex; flex-direction: column; gap: .25rem; max-width: 42rem; }
.pl-cta-text strong { font-size: 1.05rem; font-weight: 800; }
.pl-cta-text span { color: var(--text-2); font-size: .9rem; line-height: 1.5; }
.pl-cta-acts { display: flex; flex-wrap: wrap; gap: .5rem; }

/* ---- Add-to-playlist menu (watch page) ---- */
.plsave { position: relative; display: inline-block; }
.plsave-menu {
  position: absolute; z-index: 40; top: calc(100% + .4rem); left: 0;
  min-width: 15rem; max-width: 20rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: .7rem;
  box-shadow: 0 14px 40px -12px rgba(0,0,0,.6); padding: .5rem;
}
.plsave-hd { font-weight: 700; font-size: .82rem; color: var(--text-2); padding: .2rem .4rem .4rem; }
.plsave-rows { max-height: 15rem; overflow-y: auto; }
.plsave-row {
  display: flex; align-items: center; gap: .55rem; padding: .4rem .45rem; border-radius: .45rem;
  cursor: pointer; font-size: .88rem;
}
.plsave-row:hover { background: var(--surface-2); }
.plsave-row input { accent-color: var(--accent); width: 1rem; height: 1rem; flex: none; }
.plsave-row .plsave-t { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plsave-row .plsave-n { color: var(--muted); font-size: .78rem; flex: none; }
.plsave-empty { color: var(--muted); font-size: .84rem; margin: .3rem .45rem; }
.plsave-new { display: flex; gap: .35rem; margin-top: .45rem; padding-top: .45rem; border-top: 1px solid var(--line); }
.plsave-new input {
  flex: 1; min-width: 0; padding: .4rem .5rem; border-radius: .45rem;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text); font-size: .84rem;
}
.plsave-new button {
  border: 0; border-radius: .45rem; background: var(--accent); color: #fff;
  padding: .4rem .7rem; font: 700 .82rem/1 var(--font); cursor: pointer;
}

/* ---- Playlist pages (discovery sections + My playlists) ---- */
.section-h { font-size: 1.05rem; margin: 1.6rem 0 .8rem; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: 1.6rem 0 .8rem; }
.section-head .section-h { margin: 0; }
.sort-tabs { display: inline-flex; gap: .3rem; }
.sort-tabs a {
  padding: .3rem .7rem; border-radius: 999px; font: 600 .82rem/1 var(--font);
  color: var(--text-2); border: 1px solid var(--line); text-decoration: none;
}
.sort-tabs a.on { background: var(--accent); color: #fff; border-color: transparent; }

.pl-create { display: flex; gap: .5rem; max-width: 32rem; margin: 0 0 1.4rem; }
.pl-create input {
  flex: 1; min-width: 0; padding: .6rem .75rem; border-radius: .55rem;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text);
}
.pl-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.pl-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: .7rem .9rem; border: 1px solid var(--line); border-radius: .7rem; background: var(--surface);
}
.pl-main { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.pl-title { font-weight: 700; text-decoration: none; color: var(--text); }
.pl-meta { color: var(--muted); font-size: .82rem; }
.pl-actions { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.pl-edit { position: relative; }
.pl-edit > summary {
  list-style: none; cursor: pointer; padding: .3rem .6rem; border-radius: 999px;
  border: 1px solid var(--line); font: 600 .78rem/1 var(--font); color: var(--text-2);
}
.pl-edit > summary::-webkit-details-marker { display: none; }
.pl-edit > summary.danger { color: var(--danger, #e0245e); }
.pl-rename, .pl-del {
  position: absolute; z-index: 20; top: calc(100% + .3rem); right: 0;
  display: flex; gap: .4rem; align-items: center; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line); border-radius: .55rem; padding: .5rem;
  box-shadow: 0 12px 30px -10px rgba(0,0,0,.5);
}
.pl-rename input { padding: .4rem .5rem; border-radius: .45rem; border: 1px solid var(--line); background: var(--surface-2); color: var(--text); }
.pl-del span { font-size: .84rem; color: var(--text-2); }
.btn.tiny { padding: .3rem .6rem; font-size: .78rem; border-radius: 999px; }
.btn.tiny.danger { background: var(--danger, #e0245e); color: #fff; border-color: transparent; }

/* ---- Uploads ---- */
.watch-meta .uploader { color: var(--text-2); }
/* The upload page, redesigned as a card. On desktop it's two columns — the
   drop zone on the left, the details on the right — so it fills the width
   instead of stranding a narrow form against a wall of empty page. */
.upload-card {
  display: flex; flex-direction: column; gap: 1.4rem;
  max-width: 44rem; padding: 1.5rem; margin: 0 auto 2rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
}
/* Header shares the card's width and centring, so the page reads as one
   centred column rather than a left-aligned title over a centred card. */
.up-head { max-width: 58rem; margin-left: auto; margin-right: auto; }
.up-right { display: flex; flex-direction: column; gap: 1.4rem; }
@media (min-width: 820px) {
  .upload-card {
    display: grid; grid-template-columns: minmax(0, 19rem) minmax(0, 1fr);
    gap: 1.75rem; max-width: 58rem; align-items: start;
  }
  /* Natural height (not stretched to the form's), and it follows the reader
     down the form rather than towering beside it. */
  .up-drop { align-self: start; position: sticky; top: 1rem; }
}

/* Drag-and-drop / click-to-browse zone. */
.up-drop {
  display: block; cursor: pointer; text-align: center;
  border: 2px dashed var(--line-lit, var(--line)); border-radius: var(--r);
  background: var(--surface-2); padding: 2rem 1.5rem;
  transition: border-color .15s, background .15s;
}
.up-drop:hover, .up-drop:focus-visible { border-color: var(--accent); outline: none; }
.up-drop.is-drag { border-color: var(--accent); background: var(--accent-soft); }
.up-drop.has-file { text-align: left; padding: 1rem; cursor: default; }
.up-drop-empty { display: flex; flex-direction: column; align-items: center; gap: .4rem; color: var(--text-2); }
.up-drop-empty svg {
  width: 40px; height: 40px; fill: none; stroke: var(--accent); stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; margin-bottom: .3rem;
}
.up-drop-empty strong { font-size: 1.05rem; color: var(--text); }
.up-drop-empty b { color: var(--accent); }
.up-change {
  align-self: flex-start; margin-top: .2rem; padding: .35rem .8rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  color: var(--text-2); font: 600 .8rem/1 var(--font);
}
.up-change:hover { color: var(--text); border-color: var(--accent-line); }

.up-fields { display: flex; flex-direction: column; gap: 1.1rem; }
.up-field { display: flex; flex-direction: column; gap: .4rem; }
.up-field > span { font-weight: 700; font-size: .92rem; }
.up-field em { color: var(--text-2); font-weight: 400; font-style: normal; }
.up-field input[type=text], .up-field input[type=email], .up-field textarea {
  padding: .65rem .8rem; border-radius: .6rem; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text); font: inherit; font-size: .95rem;
}
.up-field input:focus, .up-field textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Tubes as toggle chips rather than a row of bare checkboxes. */
.up-tubes { border: 0; padding: 0; margin: 0; }
.up-tube-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.up-tube { position: relative; }
.up-tube input { position: absolute; opacity: 0; pointer-events: none; }
.up-tube span {
  display: inline-block; padding: .45rem .9rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  font-weight: 700; font-size: .85rem; transition: all .12s;
}
.up-tube input:checked + span {
  background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line);
}
.up-tube input:focus-visible + span { box-shadow: 0 0 0 3px var(--accent-soft); }

.up-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }
.up-submit { font-size: .95rem; padding: .7rem 1.4rem; }

.up-mine { max-width: 58rem; margin: 0 auto; }
.sub-list { list-style: none; margin: .6rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.sub-row { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; padding: .55rem .8rem; border: 1px solid var(--line); border-radius: .6rem; }
.sub-title { font-weight: 600; }
.sub-status { font-size: .74rem; font-weight: 700; text-transform: uppercase; padding: .15rem .5rem; border-radius: 999px; letter-spacing: .02em; }
.sub-pending { background: var(--surface-2); color: var(--muted); }
.sub-approved { background: rgba(55,214,122,.16); color: #37d67a; }
.sub-rejected { background: rgba(224,36,94,.14); color: var(--danger, #e0245e); }
.sub-note { color: var(--muted); font-size: .82rem; }
.flash { padding: .7rem .9rem; border-radius: .55rem; margin: 0 0 1.2rem; font-size: .92rem; }
.flash.ok { background: rgba(55,214,122,.14); color: #2fae63; border: 1px solid rgba(55,214,122,.3); }
.flash.bad { background: rgba(224,36,94,.12); color: var(--danger, #e0245e); border: 1px solid rgba(224,36,94,.3); }

/* Upload preview + progress. Explicit display would defeat the [hidden]
   attribute these are toggled with, so restore it. */
.up-preview[hidden], .up-progress[hidden], .up-drop-empty[hidden] { display: none; }
.up-preview { display: flex; flex-direction: column; gap: .4rem; }
.up-preview video { width: 100%; max-width: 30rem; border-radius: .6rem; background: #000; aspect-ratio: 16/9; }
.up-fileinfo { margin: 0; font-size: .84rem; color: var(--text-2); }
.up-fileinfo.over { color: var(--danger, #e0245e); font-weight: 600; }
.up-progress { display: flex; flex-direction: column; gap: .4rem; }
.up-bar { height: .55rem; border-radius: 999px; background: var(--surface-2); overflow: hidden; max-width: 30rem; }
.up-bar span { display: block; height: 100%; width: 0; background: var(--accent, #e0245e); border-radius: 999px; transition: width .2s ease; }
.up-status { margin: 0; font-size: .85rem; color: var(--text-2); font-variant-numeric: tabular-nums; }

/* The download button.
 *
 * Deliberately not styled like Save next to it. Downloads are now open to
 * everyone with a daily allowance, which makes this the strongest reason a
 * visitor has to come back or to register — so it reads as the primary action
 * on the page rather than as one more grey pill in the meta row.
 *
 * On a narrow screen it takes the full width and moves to its own line: the
 * meta row wraps, and a full-width target is both easier to hit and clearly
 * the thing to press. */
.dl-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .55rem 1.1rem; border-radius: 999px;
  background: var(--accent); color: #fff; border: 1px solid transparent;
  font: 800 .9rem/1 var(--font); text-decoration: none;
  transition: filter .18s var(--ease), transform .18s var(--ease);
}
.dl-btn svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}
.dl-btn:hover { filter: brightness(1.08); color: #fff; }
.dl-btn:active { transform: translateY(1px); }
@media (max-width: 560px) {
  .dl-btn { width: 100%; padding: .7rem 1.1rem; font-size: .95rem; }
}

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

.consent {
  position: fixed; left: 50%; translate: -50% 0; bottom: 1rem; z-index: 90;
  width: min(680px, calc(100vw - 2rem));
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .95rem 1.1rem; border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--line-lit);
  box-shadow: 0 22px 50px -18px rgba(0, 0, 0, .75);
}
.consent p { margin: 0; flex: 1 1 300px; color: var(--text-2); font-size: .87rem; line-height: 1.55; }
.consent a { color: var(--accent); }
.consent-actions { display: flex; gap: .5rem; margin-left: auto; }
/* Both choices are the same size and weight. A reject styled as an afterthought
   is the pattern regulators single out, and it is not consent. */
.consent-actions .btn { padding: .5rem 1.1rem; font-size: .86rem; }
@media (max-width: 520px) {
  .consent-actions { width: 100%; }
  .consent-actions .btn { flex: 1; }
}

/* ------------------------------------------------- third-party sign-in */

/* Google's brand guidelines: their mark, unaltered, on a neutral surface with
   the wordmark legible. Deliberately not restyled to match the site. */
.gbtn {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  width: 100%; padding: .68rem 1rem; border-radius: var(--r-sm);
  background: #fff; color: #1f1f1f; border: 1px solid #dadce0;
  font: 600 .92rem/1 var(--font); letter-spacing: -.005em;
  transition: box-shadow .15s var(--ease), background .15s var(--ease);
}
.gbtn:hover { background: #f7f8f8; box-shadow: 0 1px 3px rgba(0,0,0,.25); }
.gbtn svg { flex: none; }

.or {
  display: flex; align-items: center; gap: .8rem;
  margin: 1.1rem 0; color: var(--muted); font-size: .78rem;
  text-transform: uppercase; letter-spacing: .12em; font-weight: 700;
}
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* ---------------------------------------------------------- tag cards */

.tagcards {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.tagcard {
  position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden;
  border-radius: var(--r); background: var(--surface);
  border: 1px solid var(--line-soft);
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.tagcard:hover { border-color: var(--accent-line); transform: translateY(-2px); }
.tagcard .shot { position: absolute; inset: 0; display: block; }
.tagcard img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* Muted so the name stays readable over whatever the thumbnail happens to
     be, and lifted on hover so the card still reacts. */
  filter: saturate(.85) brightness(.62);
  transition: filter .25s var(--ease), scale .5s var(--ease-out);
}
.tagcard:hover img { filter: saturate(1) brightness(.78); scale: 1.04; }
.tagcard .label {
  position: absolute; inset: auto 0 0 0; z-index: 1;
  display: flex; flex-direction: column; gap: .1rem;
  padding: 1.6rem .9rem .8rem;
  background: linear-gradient(to top, rgba(0,0,0,.85) 20%, transparent);
}
.tagcard strong { color: #fff; font-size: 1rem; font-weight: 800; letter-spacing: -.01em; }
.tagcard .n { color: rgba(255,255,255,.72); font-size: .78rem; }
/* No poster yet: fall back to the accent wash rather than an empty box. */
.tagcard .shot:empty { background: var(--accent-grad); opacity: .22; }

/* Auto-advance countdown, in the end screen's header row.
   Sits beside "Up next" rather than over the card, so the thing it is counting
   down to stays fully readable while it runs. */
.kp-auto {
  display: inline-flex; align-items: center; gap: .5rem; margin-left: auto;
  font-size: .82rem; color: var(--text-2);
}
.kp-auto b { font-variant-numeric: tabular-nums; color: var(--text); }
/* Deliberately a plain, obvious control rather than a subtle one — the escape
   from an automatic navigation should be the easiest thing in the row to hit. */
.kp-auto-x {
  border: 1px solid rgba(255, 255, 255, .35); background: transparent; color: inherit;
  border-radius: 999px; padding: .2rem .6rem; cursor: pointer;
  font: 700 .78rem/1 var(--font);
}
.kp-auto-x:hover { background: rgba(255, 255, 255, .14); }

/* ---------------------------------------------------- instant message
   Bottom right, above everything except the ad wall. Only ever on a page load
   where the popunder did not arm — see initInstantMessage(). */
.ad-im {
  position: fixed; right: 12px; bottom: 12px; z-index: 60;
  max-width: min(320px, calc(100vw - 24px));
  border-radius: 10px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
  box-shadow: 0 12px 34px -12px rgba(0, 0, 0, .6);
}
/* Our own close, not the advertiser's. Sized to be hit with a thumb: a
   dismiss control smaller than the creative's own is worse than none. */
.ad-im-x {
  position: absolute; top: 4px; right: 4px; z-index: 2;
  width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
  border: 0; background: rgba(0, 0, 0, .62); color: #fff;
  font-size: 1.05rem; line-height: 1; padding: 0;
}
.ad-im-x:hover { background: rgba(0, 0, 0, .8); }
/* On a phone it sits above the bottom edge rather than flush to it, clear of
   the browser's own toolbar. */
@media (max-width: 560px) { .ad-im { right: 8px; bottom: 56px; } }

/* --------------------------------------------------------- join bar
   Signed-out only, and the first thing in the body — above the site header,
   because a pitch under the logo and the search box reads as furniture.

   The dark accent band is deliberate: the first version used the pale
   accent-soft wash, which on a light theme came out as a wide pink strip with
   the text jammed into the corners and looked like a cookie notice. */
.joinbar {
  background: var(--accent-grad, var(--accent));
  color: #fff;
}
.joinbar-in {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  /* padding-BLOCK, not the `padding` shorthand.
     The shorthand resets all four sides, which wiped out the padding-inline
     .wrap supplies — that is why the text sat flush against the window edge
     and the whole bar looked unfinished. */
  padding-block: .5rem;
}
.joinbar-text {
  flex: 1 1 18rem; min-width: 0;
  font-size: .86rem; line-height: 1.4; color: rgba(255, 255, 255, .82);
}
.joinbar-text strong { color: #fff; font-weight: 800; }
.joinbar-sub { opacity: .92; }
.joinbar-acts { display: flex; align-items: center; gap: .8rem; flex: 0 0 auto; }

/* Solid white pill on the accent band: the one thing here that should look
   pressable, and it has to survive whatever colour the tube's accent is. */
.joinbar-cta {
  background: #fff; color: #111; text-decoration: none;
  padding: .32rem .85rem; border-radius: 999px;
  font: 800 .82rem/1.5 var(--font); white-space: nowrap;
}
.joinbar-cta:hover { background: rgba(255, 255, 255, .88); color: #111; }
.joinbar-signin { font-size: .82rem; color: rgba(255, 255, 255, .82); white-space: nowrap; }
.joinbar-signin:hover { color: #fff; }
.joinbar-x {
  border: 0; background: transparent; cursor: pointer; padding: 0 .1rem;
  color: rgba(255, 255, 255, .7); font-size: 1.15rem; line-height: 1;
}
.joinbar-x:hover { color: #fff; }

/* On a phone the headline is what earns the space; the supporting clause is
   what pushes the buttons onto a third line. */
@media (max-width: 620px) {
  .joinbar-sub { display: none; }
  .joinbar-text { flex: 1 1 auto; font-size: .82rem; }
}

/* ------------------------------------------------------------ ad wall
   Shown to visitors blocking ads once they are past the free allowance.
   Deliberately hard to ignore: full-screen, scroll locked behind it, and it
   returns on every page load because the dismissal is never stored. */
/*
 * Flex with `margin: auto` on the box, not `place-items: center`.
 *
 * Centring is right until the box is taller than the screen, and on a phone it
 * is: heading, two paragraphs and two buttons, one of them a sentence long. A
 * centred child that overflows its container overflows in BOTH directions, so
 * the top of the dialog ended up above the viewport — and since the overlay
 * did not scroll and body scrolling is locked behind it, there was no way to
 * reach it. The heading and the first paragraph were simply gone.
 *
 * `margin: auto` on a flex child does the same centring while there is room
 * and collapses to zero when there is not, which leaves the box starting at
 * the top and the overlay scrolling it. overscroll-behavior keeps that scroll
 * from chaining to the page underneath.
 */
.abwall {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; padding: 1.2rem;
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: rgba(4,4,7,.88);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
/*
 * --surface-2, not --panel. There is no --panel: it was never defined in
 * either theme, so this always fell through to the hardcoded dark hex while
 * the text kept following the theme — which in light mode is dark ink on a
 * dark box, i.e. an unreadable wall.
 */
.abwall-box {
  max-width: 32rem; width: 100%;
  /* Both the centring and the overflow escape — see the note on .abwall. */
  margin: auto;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--line);
  border-radius: 1rem; padding: 1.8rem 1.5rem; text-align: center;
  box-shadow: var(--shadow, 0 30px 90px -20px rgba(0,0,0,.9));
}
.abwall-h { margin: 0 0 .8rem; font-size: 1.3rem; color: var(--text); }
.abwall-box p { color: var(--muted); margin: 0 0 .8rem; line-height: 1.55; }
/* The "which blocker are you using" chooser.
   Small chips rather than a list: the point is to be scanned in a second and
   tapped, not read. */
.abwall-lead { margin-top: 1rem; font-weight: 700; color: var(--text) !important; }
.abwall-picks {
  display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center;
  margin: .2rem 0 .6rem;
}
.abwall-pick {
  padding: .35rem .7rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: transparent; color: var(--text);
  font: 600 .8rem/1 var(--font);
}
.abwall-pick:hover { background: var(--surface-2); }
.abwall-pick.is-on { border-color: var(--accent); background: var(--accent-soft); }
.abwall-steps {
  margin: 0 0 .4rem; padding: .6rem .8rem; border-radius: .6rem;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text) !important; font-size: .88rem; line-height: 1.5;
}
.abwall-primary { width: 100%; margin-top: .5rem; font-size: 1rem; padding: .8rem 1rem; }
/* The escape hatch is available, unstyled as the primary action, and small —
   it is not hidden, it is simply not the thing being asked for. */
.abwall-dismiss {
  width: 100%; margin-top: .7rem;
  font-size: .78rem; font-weight: 500; line-height: 1.35;
  padding: .6rem .8rem; color: var(--muted);
  /* A one-sentence label in a button that is only as wide as the phone. */
  white-space: normal; height: auto;
}

/* Phone. Every measurement here is smaller for the same reason: the box has to
   fit a screen a third the height, and the less of it that overflows the less
   anyone has to scroll to find the button. */
@media (max-width: 560px) {
  .abwall { padding: .8rem; }
  .abwall-box { padding: 1.3rem 1.05rem; border-radius: .85rem; }
  .abwall-h { font-size: 1.12rem; margin-bottom: .6rem; }
  .abwall-box p { font-size: .9rem; margin-bottom: .65rem; }
  .abwall-primary { font-size: .95rem; padding: .75rem .9rem; }
  .abwall-dismiss { font-size: .74rem; }
}

/* The ad-block gate over the PLAYER (not a full-screen wall). Covers the
   player-shell only, so the rest of the page — sign-in included — stays live.
   overflow:auto because the player box can be shorter than the message. */
.abgate {
  position: absolute; inset: 0; z-index: 30;
  display: flex; padding: 1rem;
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  background: rgba(4,4,7,.92);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.abgate-box {
  max-width: 30rem; width: 100%; margin: auto;
  color: var(--text); text-align: center;
}
.abgate-h { margin: 0 0 .6rem; font-size: 1.15rem; color: #fff; }
.abgate-box p { color: rgba(255,255,255,.82); margin: 0 0 .7rem; line-height: 1.5; font-size: .92rem; }
.abgate-lead { margin-top: .8rem !important; font-weight: 700; color: #fff !important; }
.abgate-picks { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: center; margin: .2rem 0 .5rem; }
.abgate-pick {
  padding: .3rem .65rem; border-radius: 999px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.28); background: transparent; color: #fff;
  font: 600 .78rem/1 var(--font);
}
.abgate-pick:hover { background: rgba(255,255,255,.1); }
.abgate-pick.is-on { border-color: var(--accent); background: var(--accent-soft); }
.abgate-steps {
  margin: 0 0 .5rem; padding: .55rem .75rem; border-radius: .55rem;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2);
  color: #fff !important; font-size: .85rem; line-height: 1.45;
}
.abgate-actions { display: flex; flex-direction: column; gap: .5rem; align-items: center; margin-top: .5rem; }
.abgate-primary { width: 100%; max-width: 20rem; font-size: .95rem; padding: .7rem 1rem; }
.abgate-signin { color: rgba(255,255,255,.85); font-size: .85rem; text-decoration: underline; }
.abgate-signin:hover { color: #fff; }
@media (max-width: 560px) {
  .abgate { padding: .7rem; }
  .abgate-h { font-size: 1rem; }
  .abgate-box p { font-size: .85rem; margin-bottom: .55rem; }
  .abgate-lead { margin-top: .6rem !important; }
  .abgate-primary { font-size: .88rem; padding: .6rem .8rem; }
}

/* Advanced search shown to a signed-out visitor: the form is live, the results
   are not. Sits where the results would be so the page does not just end. */
.adv-locked {
  margin-top: 2rem; padding: 1.6rem 1.4rem;
  border: 1px solid var(--line); border-radius: 1rem; text-align: center;
}
.adv-locked h2 { margin: .2rem 0 .6rem; font-size: 1.25rem; }
/* The reason someone would bother. Free is the whole pitch, so it leads. */
.adv-locked-flag {
  display: inline-block; margin-bottom: .5rem;
  padding: .3rem .8rem; border-radius: 999px;
  background: var(--accent-grad); color: #180508;
  font-size: .74rem; font-weight: 900; letter-spacing: .06em; text-transform: uppercase;
}
.adv-locked p { color: var(--muted); margin: 0 auto .9rem; max-width: 34rem; line-height: 1.55; }
.adv-locked-actions { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; }

/* Last watched, pushed to the end of the meta row so it sits bottom-right of
   the card under the rating. margin-left:auto rather than a spacer, so it stays
   at the edge when the row wraps on a narrow card. */
/* Views and rating on the left, the times on the right, one row. */
.card .meta .meta-when {
  margin-left: auto; display: inline-flex; gap: .35rem; align-items: baseline;
  opacity: .78; white-space: nowrap;
}
.card .meta .meta-watched { margin-left: auto; opacity: .8; white-space: nowrap; }
.hero .facts .meta-watched { margin-left: auto; opacity: .8; white-space: nowrap; }


/* ------------------------------------------------- blocked ad placeholder
   Shown in a slot an ad blocker emptied. Sized to the unit it replaces so the
   page does not reflow when it appears — a placeholder that shifts the layout
   is worse than the hole it fills. */
.ad-house {
  display: flex; flex-direction: column; justify-content: center; gap: .4rem;
  width: 100%; padding: 1rem 1.1rem; box-sizing: border-box;
  border-radius: .8rem; text-align: left;
  background:
    radial-gradient(120% 100% at 0% 0%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 62%),
    var(--surface, #101017);
  border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line));
}
.ad-house-head {
  margin: 0; font-size: .95rem; font-weight: 900; line-height: 1.25;
  color: var(--text, #f6f6f8); letter-spacing: -.01em;
}
.ad-house-body { margin: 0; font-size: .8rem; line-height: 1.5; color: var(--text-2, #b9b9c4); }
.ad-house-body strong { color: var(--text, #f6f6f8); }
/* One line, for a 468x60 or 728x90 where there is no room for a paragraph. */
.ad-house-tight {
  margin: 0; font-size: .82rem; line-height: 1.4; color: var(--text-2, #b9b9c4);
}
.ad-house-tight strong { color: var(--text, #f6f6f8); }
.ad-house-btn {
  align-self: flex-start; margin-top: .35rem; cursor: pointer;
  padding: .45rem .8rem; border-radius: 999px; border: 0;
  background: var(--accent-grad, linear-gradient(112deg, #ff4d6d, #ffa94d));
  color: #180508; font: inherit; font-size: .78rem; font-weight: 900; line-height: 1;
}
.ad-house-btn:hover { filter: brightness(1.08); }
/* The slot itself must not stay collapsed once it holds a placeholder. */
.ad-slot.ad-blocked { display: block; min-height: 0; }

/* The Everyone/Mine switch on /recent. Reuses .tabs for the pill shape, but
   sits under its own heading rather than in the header rail, so it needs the
   spacing the rail was providing. */
.recent-tabs { margin: 0 0 1.4rem; }

/* The comment list used to butt straight up against the form's button row, so
   the first comment's top rule ran into the Post button and read as though the
   button were sitting on a line. The form is separation enough on its own. */
#comment-list { margin-top: 1.75rem; }
#comment-list > .comment:first-child { border-top: 0; padding-top: 0; }

.reply-btn {
  margin-top: .45rem; padding: 0; border: 0; background: none;
  color: var(--muted); font: 700 .8rem/1 var(--font); cursor: pointer;
}
.reply-btn:hover { color: var(--accent); }

/* Replies are indented to the width of the avatar column above them, so a reply
   lines up under the body of what it answers rather than floating loose. */
.comment-replies { margin-left: calc(40px + .95rem); }
.comment.is-reply { padding: .9rem 0; }
.comment.is-reply .av { width: 30px; height: 30px; font-size: .8rem; }
.comment.is-reply { grid-template-columns: 30px 1fr; gap: .75rem; }

.reply-form { margin: .2rem 0 1rem calc(40px + .95rem); }
.reply-form textarea { min-height: 68px; }
.btn-quiet {
  background: none; border: 1px solid var(--line); color: var(--text-2);
}

/* The reply button belongs to the comment, not to the bubble, so it sits just
   under it and lines up with the text rather than floating in the gutter. */
.reply-btn { margin: .35rem 0 0 .9rem; }

/* space-between with an empty status note pushed Cancel and Reply to opposite
   ends of the form. The note takes the slack instead, so the buttons stay
   together on the right where a pair of buttons belongs. */
.comment-form .row { justify-content: flex-end; }
.comment-form .row .note { margin-right: auto; }
.comment-form .row .btn + .btn { margin-left: .55rem; }

/* ------------------------------------------------------- one rating control
   The stars in the meta row are the input AND the at-a-glance average, so the
   page no longer carries two star rows that could disagree.

   Order matters below. The average fill is declared FIRST so that :hover and
   :checked, declared after it, win — otherwise the average would keep painting
   over the star you are pointing at. */
.rate-inline { display: inline-flex; align-items: center; gap: .45rem; }
.rate-figure { color: var(--text-2); font-weight: 700; }
.rate-figure .dim { color: var(--muted); font-weight: 400; }
.rate-note { color: var(--muted); font-size: .82rem; }

/* The widget is reversed in the DOM (5→1), so "N and everything after it" is
   "N and every lower star" — which is exactly the fill wanted. */
.rate-widget.avg-1 label[for="s1"],
.rate-widget.avg-2 label[for="s2"], .rate-widget.avg-2 label[for="s2"] ~ label,
.rate-widget.avg-3 label[for="s3"], .rate-widget.avg-3 label[for="s3"] ~ label,
.rate-widget.avg-4 label[for="s4"], .rate-widget.avg-4 label[for="s4"] ~ label,
.rate-widget.avg-5 label[for="s5"], .rate-widget.avg-5 label[for="s5"] ~ label {
  color: var(--star);
}

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

.chat-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem 1.5rem; flex-wrap: wrap; }
.chat-online {
  display: inline-flex; align-items: center; gap: .4rem; white-space: nowrap;
  padding: .35rem .7rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text-2); font-size: .84rem; font-weight: 700;
}
.chat-online b { color: var(--text); }
.chat-dot {
  width: .55rem; height: .55rem; border-radius: 50%; background: #25c26a;
  box-shadow: 0 0 0 0 rgba(37, 194, 106, .6); animation: chatpulse 2s infinite;
}
@keyframes chatpulse {
  0% { box-shadow: 0 0 0 0 rgba(37, 194, 106, .5); }
  70% { box-shadow: 0 0 0 .5rem rgba(37, 194, 106, 0); }
  100% { box-shadow: 0 0 0 0 rgba(37, 194, 106, 0); }
}

.chat {
  display: flex; flex-direction: column;
  /* Fill the screen from just under the header to near the bottom, so the
     composer is always on screen — nobody should have to scroll down to type.
     dvh follows the mobile browser's shrinking toolbar; the offset is the nav
     plus this page's heading, and min-height keeps it usable on a short window. */
  height: calc(100dvh - 13rem); min-height: 320px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); overflow: hidden;
}
.chat-log {
  /* Takes all the room the composer doesn't; scrolls its own overflow. min-height:0
     is what lets a flex child actually shrink and scroll instead of pushing the
     composer off the bottom. */
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: .8rem 1rem;
  display: flex; flex-direction: column; gap: .1rem;
  overscroll-behavior: contain;
}
.chat-empty { color: var(--text-2); text-align: center; margin: auto 0; font-size: .9rem; }

/* Each message is a nick label above a rounded bubble. */
.cm { display: flex; flex-direction: column; align-items: flex-start; gap: .1rem; margin: .35rem 0; }
.cm-nick { font-weight: 800; color: var(--text-2); font-size: .76rem; padding-left: .2rem; }
.cm-nick.cm-member { color: var(--accent); }
.cm-bubble {
  position: relative; max-width: min(88%, 46rem);
  padding: .5rem 2.1rem .5rem .8rem; border-radius: .3rem 1rem 1rem 1rem;
  background: var(--surface-2); border: 1px solid var(--line);
  line-height: 1.45; font-size: .92rem; word-break: break-word; overflow-wrap: anywhere;
}
/* A member (staff/registered) message gets a faint accent bubble so it stands
   out from the guest crowd. */
.cm-mine .cm-bubble { background: var(--accent-soft); border-color: var(--accent-line); }
.cm-body { color: var(--text); }
.cm-link { color: var(--accent); text-decoration: underline; font-weight: 600; }
.cm-report {
  position: absolute; top: .3rem; right: .3rem; opacity: 0;
  border: 0; background: none; cursor: pointer; padding: .05rem .25rem;
  color: var(--text-2); font-size: .78rem; border-radius: 6px; transition: opacity .12s;
}
.cm-bubble:hover .cm-report, .cm-report:focus { opacity: .65; }
.cm-report:hover { opacity: 1; color: #ff5a5a; }
.cm-report.done { opacity: .85; color: #ff5a5a; cursor: default; font-size: .7rem; }

.chat-compose {
  display: flex; flex-direction: column; gap: .4rem;
  padding: .7rem 1rem .9rem; border-top: 1px solid var(--line); background: var(--surface-2);
}
.chat-me { font-size: .78rem; color: var(--text-2); font-weight: 700; }
.chat-me .cm-member, .chat-upgrade { color: var(--accent); }
.chat-upgrade { font-weight: 700; text-decoration: underline; }
.chat-input-row { display: flex; gap: .5rem; position: relative; align-items: center; }
.chat-emoji-btn {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); cursor: pointer;
  font-size: 1.15rem; line-height: 1; display: grid; place-items: center;
}
.chat-emoji-btn:hover { border-color: var(--accent-line); }
.chat-emoji-pop {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 20;
  width: min(320px, 84vw); max-height: 240px; overflow-y: auto;
  display: grid; grid-template-columns: repeat(8, 1fr); gap: .1rem;
  padding: .5rem; border-radius: .8rem;
  background: var(--surface); border: 1px solid var(--line-lit);
  box-shadow: 0 16px 40px -14px rgba(0, 0, 0, .6);
}
.chat-emoji {
  border: 0; background: none; cursor: pointer; font-size: 1.25rem;
  padding: .2rem; border-radius: 8px; line-height: 1;
}
.chat-emoji:hover { background: var(--surface-2); }
/* An explicit display would otherwise defeat the [hidden] attribute these
   toggle with, so restore it where display is set. */
.chat-emoji-pop[hidden] { display: none; }
.chat-input {
  flex: 1 1 auto; min-width: 0; padding: .6rem .85rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  font: inherit; font-size: .92rem;
}
.chat-input:focus { outline: 2px solid var(--accent-line, var(--accent)); outline-offset: 0; border-color: transparent; }
.chat-send { flex: 0 0 auto; }
.chat-send.cooling { opacity: .6; }
.chat-error { min-height: 1.1em; color: #ff5a5a; font-size: .82rem; font-weight: 600; }
.chat-closed { padding: 1.4rem 1rem; text-align: center; color: var(--text-2); border-top: 1px solid var(--line); }

/* Admin right-click menu inside the public room. */
.chat-ctx {
  position: fixed; z-index: 80; min-width: 170px; padding: .3rem;
  background: var(--surface); border: 1px solid var(--line-lit); border-radius: .6rem;
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, .6);
}
.chat-ctx button {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  padding: .5rem .6rem; border-radius: 6px; cursor: pointer;
  color: var(--text); font: 600 .86rem/1 var(--font);
}
.chat-ctx button:hover { background: var(--surface-2); }
.chat-ctx [data-ctx-ban] { color: #ff5a5a; }

/* Unread badge on the "Chat" nav link. */
.chat-nav { position: relative; }
.chat-nav-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.15rem; height: 1.15rem; margin-left: .35rem; padding: 0 .3rem;
  border-radius: 999px; background: var(--accent, #ff4d6d); color: #fff;
  font-size: .68rem; font-weight: 800; line-height: 1; vertical-align: middle;
}
.chat-nav-badge[hidden] { display: none; }

@media (max-width: 560px) {
  .chat { height: calc(100dvh - 11rem); }
  .cm { font-size: .9rem; }
}

/* Floating launcher into the live chat, on every page but the chat itself. */
.chat-fab {
  position: fixed; left: 16px; bottom: 16px; z-index: 55;
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .6rem .95rem .6rem .8rem; border-radius: 999px;
  background: linear-gradient(120deg, var(--accent, #ff4d6d), var(--accent-2, #ff8a3d));
  color: #fff; text-decoration: none; font: 800 .88rem/1 var(--font);
  box-shadow: 0 10px 26px -8px rgba(0, 0, 0, .5); border: 0;
  transition: transform .12s, box-shadow .12s;
}
.chat-fab:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -8px rgba(0, 0, 0, .55); }
.chat-fab svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chat-fab-label { white-space: nowrap; }
/* A live pulse so it reads as "something is happening in here". */
.chat-fab-dot {
  position: absolute; top: 6px; left: 6px; width: .5rem; height: .5rem;
  border-radius: 50%; background: #38e07b; box-shadow: 0 0 0 0 rgba(56, 224, 123, .6);
  animation: chatpulse 2s infinite;
}
/* On a phone, collapse to just the bubble so it never covers content. */
@media (max-width: 560px) {
  .chat-fab { padding: .7rem; left: 12px; bottom: 12px; }
  .chat-fab-label { display: none; }
}

/* ------------------------------------------------------------ tag picker */
/* Ported from the admin panel (admin.css) so the public upload page gets the
   same token/chip field. --warn isn't in the public theme, so it carries a
   literal amber fallback. */
.tp { position: relative; }
.tp-field {
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  min-height: 44px; padding: .35rem .45rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm, .5rem); cursor: text;
}
.tp-field:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.tp-entry {
  flex: 1 0 100%; min-width: 0; width: 100%;
  border: 0; background: none; padding: .2rem .25rem;
  color: var(--text); font: inherit; font-size: .95rem;
}
.tp-entry:focus { outline: none; box-shadow: none; border: 0; }
.tp-actions { display: flex; gap: .4rem; margin-top: .4rem; }
.tp-clear {
  display: inline-block; padding: .2rem .55rem;
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
  background: transparent; color: var(--text-2); font: 600 .76rem/1.4 inherit;
}
.tp-clear:hover { color: var(--text); border-color: var(--line-lit); }
.tp-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .22rem .35rem .22rem .6rem; border-radius: 999px;
  font-size: .82rem; font-weight: 700; white-space: nowrap;
  background: var(--accent-soft); color: var(--accent); border: 1px solid var(--accent-line);
}
.tp-chip.is-new {
  background: color-mix(in srgb, var(--warn, #d98a00) 14%, transparent);
  color: var(--warn, #b06f00);
  border-color: color-mix(in srgb, var(--warn, #d98a00) 40%, transparent);
  border-style: dashed;
}
.tp-x {
  width: 17px; height: 17px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: transparent; color: inherit; font-size: 1rem; line-height: 1;
}
.tp-x:hover { background: rgba(0, 0, 0, .18); }
.tp-menu {
  position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 260px; overflow-y: auto; margin: 0; padding: .25rem; list-style: none;
  background: var(--surface); border: 1px solid var(--line-lit);
  border-radius: var(--r-sm, .5rem); box-shadow: 0 16px 40px -14px rgba(0, 0, 0, .5);
}
.tp-opt {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .55rem; border-radius: 6px; cursor: pointer;
  font-size: .9rem; color: var(--text);
}
.tp-opt.on { background: var(--surface-2); }
.tp-opt.is-new { color: var(--warn, #b06f00); font-style: italic; }
.tp-count { margin-left: auto; color: var(--text-2); font-size: .78rem; font-weight: 700; }

/* ---------------------------------------------------------- coming soon */

.soon-strip { margin-bottom: 1.4rem; }
.soon-label { display: flex; align-items: center; gap: .5rem; }
.soon-dot {
  width: .55rem; height: .55rem; border-radius: 50%; background: var(--accent, #ff4d6d);
  box-shadow: 0 0 0 0 rgba(255, 77, 109, .5); animation: chatpulse 2s infinite; flex: 0 0 auto;
}
.soon-queue {
  margin-left: auto; font-size: .8rem; font-weight: 700; color: var(--text-2);
  padding: .2rem .6rem; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line);
}

/* The strip shows exactly one row, whatever the screen fits — extra cards flow
   into zero-height rows and are clipped, so the row is always full and never
   spills a lonely second row. */
.soon-strip .grid { grid-template-rows: auto; grid-auto-rows: 0; row-gap: 0; overflow: hidden; }

/* A queued card is a normal poster wearing a "Coming soon" badge — the picture
   is the draw, so it is not covered. right/bottom:auto because the base .badge
   anchors bottom-right, which would otherwise stretch this across the whole
   thumbnail. */
.soon-badge {
  position: absolute; top: .5rem; left: .5rem; right: auto; bottom: auto; z-index: 2;
  background: linear-gradient(112deg, var(--accent, #ff4d6d), var(--accent-2, #ff8a3d));
  color: #fff; font-weight: 800; letter-spacing: .02em;
}
.soon-cd {
  position: absolute; bottom: .5rem; left: .5rem; z-index: 2;
  padding: .18rem .5rem; border-radius: 6px;
  background: rgba(0, 0, 0, .66); color: #fff; backdrop-filter: blur(4px);
  font-size: .72rem; font-weight: 800; font-variant-numeric: tabular-nums;
}
.card-soon .soon-when { color: var(--accent); font-weight: 700; }

/* The countdown page. */
.soon-page {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem;
  max-width: 60rem; margin: 1rem auto 3rem;
}
@media (min-width: 780px) {
  .soon-page { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); align-items: center; }
}
.soon-poster {
  position: relative; border-radius: var(--r); overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--surface-2);
}
.soon-poster img, .soon-preview { width: 100%; height: 100%; object-fit: cover; display: block; }
.soon-poster-badge {
  position: absolute; top: .8rem; left: .8rem;
  padding: .3rem .7rem; border-radius: 999px; font-size: .78rem; font-weight: 800;
  background: linear-gradient(112deg, var(--accent, #ff4d6d), var(--accent-2, #ff8a3d)); color: #fff;
}
.soon-info { display: flex; flex-direction: column; gap: .8rem; }
.soon-info .eyebrow { display: inline-flex; align-items: center; gap: .45rem; color: var(--text-2); font-weight: 700; }
.soon-info h1 { margin: 0; font-size: clamp(1.4rem, 3vw, 2rem); line-height: 1.15; }
.soon-count {
  display: flex; flex-direction: column; gap: .1rem;
  padding: 1rem 1.2rem; border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--accent-line, var(--line));
}
.soon-count-label { font-size: .8rem; font-weight: 700; color: var(--text-2); text-transform: uppercase; letter-spacing: .06em; }
.soon-count-clock {
  font-size: clamp(2rem, 6vw, 3rem); font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--accent);
}
.soon-queue-line { margin: 0; color: var(--text-2); line-height: 1.6; }
.soon-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .3rem; }
.soon-notify { display: flex; flex-direction: column; gap: .4rem; align-items: flex-start; }
.soon-notify-btn { font-size: .95rem; }
.soon-notify-done {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .6rem .9rem; border-radius: var(--r);
  background: var(--accent-soft); color: var(--accent); font-weight: 700;
  border: 1px solid var(--accent-line, var(--line));
}
.soon-waiting { font-size: .82rem; color: var(--text-2); }

/* ---------------------------------------------- tag-page SEO content block */
.tag-seo { margin: 2.4rem 0 1rem; padding-top: 1.6rem; border-top: 1px solid var(--line); }
.tag-seo-copy { max-width: 62rem; }
.tag-seo-copy p { color: var(--text-2); line-height: 1.7; margin: 0 0 .85rem; font-size: .95rem; }
.tag-seo-related { margin-top: 1.5rem; }
.tag-seo-related h2 { font-size: 1.02rem; margin: 0 0 .75rem; }
.tag-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.tag-chips a {
  display: inline-block; padding: .4rem .9rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text);
  text-decoration: none; font-size: .85rem; font-weight: 600; transition: all .12s;
}
.tag-chips a:hover { border-color: var(--accent-line, var(--accent)); color: var(--accent); }
