/* ============================================================
   BtGoogle design system - the single source of styling
   Every page links this one file. No <style> blocks, no inline styles.

   Three rules (the whole secret behind "dense but not messy", no exceptions):
   1. One tag style, one button style, one input style. Never a second set.
   2. One background (white). Hierarchy comes from rule weight and indent,
      never from alternating backgrounds.
   3. One piece of data, one representation. A seeder count is a number; it
      does not also get a bar.

   Naming: short, generic, composable. Structural names like .list/.row/.card
   are reused across pages; anything page-specific stays out of this file.
   ============================================================ */

/* ---------- Tokens ---------- */
:root{
  /* Spacing: six steps, all multiples of 4. There is no seventh, and no bare values */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;
  /* Type scale: five steps */
  --t-xs:11px; --t-sm:12px; --t-md:13px; --t-lg:15px; --t-xl:20px;

  --bg:#F6F6F5;
  --surface:#FFFFFF;
  --hover:#F4F4F2;
  --ink:#1A1A1C;
  --ink-2:#5D5D63;
  --ink-3:#96969C;
  --line:#E7E7E4;      /* structural rule: between blocks */
  --line-2:#F0F0ED;    /* content rule: inside a block */
  --accent:#C4491A;
  --accent-bg:#C4491A12;
  --ok:#2E7D50;
  --warn:#B4761B;
  --danger:#B3261E;
  --dead:#B8B8B4;

  --rail:184px;
  --cover:112px;       /* cover width, 16:11 landscape. Breakpoints change it */
  --radius:8px;
  --radius-sm:5px;

  /* Legacy variables used by the old components (lightbox/toast/drawer/
     suggestions), given a light default. They exist on html.light and
     html.dark, but on a first visit <html> has neither class (the inline head
     script only adds one when localStorage says dark), so without a default
     they would all resolve to nothing. */
  --card-bg:#FFFFFF; --card-hover:#FDFDFD; --divider:#F0F0ED;
  --border:#E7E7E4; --text-secondary:#5D5D63; --text-tertiary:#96969C;
  --accent-soft:#C4491A18; --suggestion-bg:#FAFAF8;
  --bg-secondary:#F6F6F5; --color-error:#B3261E;

  --sans:Inter,"Hiragino Sans","PingFang SC","Noto Sans JP",system-ui,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
}

/* ---------- Dark theme ----------
   The site's existing mechanism: <html class="light|dark"> + localStorage,
   switched by toggleTheme() in main.js, with an inline script in base.html
   setting the class early to avoid a flash. Here we only redefine tokens.
   Warm near-black rather than cool blue: the accent is a terracotta orange,
   and it goes muddy over a cool ground. */
html.dark{
  --bg:#161513;
  --surface:#1E1B19;
  --hover:#26221F;
  --ink:#E8E3DE;
  --ink-2:#9A918A;
  --ink-3:#6E655F;
  --line:#2E2926;
  --line-2:#262220;
  --accent:#E0692F;
  --accent-bg:#E0692F1F;
  --ok:#4CA575;
  --warn:#C9924A;
  --danger:#D9645C;
  --dead:#57504B;
}
/* These few spots use fixed black/white and have to flip with the theme */
html.dark .thumb,html.dark .grid .c,html.dark .hero .poster,html.dark .shots a{box-shadow:inset 0 0 0 1px rgba(255,255,255,.07)}
html.dark .pg [aria-current]{color:#1A0F08}
html.dark .btn.pri{color:#1A0F08}
html.dark .zoom{box-shadow:0 8px 28px rgba(0,0,0,.5), inset 0 0 0 1px rgba(255,255,255,.10)}
html {
  scroll-behavior: smooth;
}

[id] {
  scroll-margin-top: 50px;
}
/* ---------- Base ---------- */
*{margin:0;padding:0;box-sizing:border-box}
html{scrollbar-gutter:stable}
/* flex column + .foot{margin-top:auto} = footer sits at the bottom on short
   pages and flows after the content on long ones. .shell/.page carry
   width:100% for exactly this reason - see the note there. */
body{
  min-height:100dvh; display:flex; flex-direction:column;
  background:var(--bg); color:var(--ink);
  font-family:var(--sans); font-size:var(--t-md); line-height:1.45;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
a{color:inherit; text-decoration:none}
button,input,select,textarea{font-family:inherit; font-size:inherit; color:inherit}
img{max-width:100%; display:block}
:focus-visible{outline:2px solid var(--accent); outline-offset:2px; border-radius:3px}

/* ---------- Utilities ---------- */
.m{font-family:var(--mono); font-variant-numeric:tabular-nums}
.label{font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-3)}
.muted{color:var(--ink-3)}
.r{text-align:right}

/* ---------- Topbar (one for the whole site) ----------
   This used to be .top / .nav and collided once: the trending page's
   .rank.top also matched .top, inheriting height:46px and position:sticky,
   which deformed the whole top-three row. Renamed to .topbar / .topnav - the
   names themselves do not collide, so no nesting workaround is needed.
   The old style.css used .top-bar (hyphenated), which also does not clash. */
.topbar{
  display:flex; align-items:center; gap:var(--s4); height:46px; padding:0 var(--s4);
  background:var(--surface); border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:30;
}
.mark{display:flex; align-items:center; gap:7px; font-size:14px; font-weight:600; letter-spacing:-.015em; flex:none}
.mark .d{width:8px; height:8px; border-radius:2px; background:var(--accent)}
.topnav{display:flex; gap:var(--s4); margin-left:auto; color:var(--ink-2)}
.topnav a:hover{color:var(--ink)}
.topnav a[aria-current]{color:var(--accent); font-weight:500}
.burger{display:none; width:30px; height:30px; border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--surface); color:var(--ink-2); place-items:center; cursor:pointer; flex:none}

/* ---------- Inputs (topbar search and forms share one set) ---------- */
.field{position:relative; display:flex; align-items:center}
.field > input{
  width:100%; height:30px; background:var(--bg); border:1px solid var(--line); border-radius:6px;
  padding:0 var(--s3);
}
.field.ic > input{padding-left:30px}
.field > .i{position:absolute; left:9px; color:var(--ink-3); display:flex; pointer-events:none}
.field > input:focus{outline:0; border-color:var(--accent); background:var(--surface)}
.field.grow{flex:1; max-width:460px}
/* Inputs inside forms sit taller: easier to hit with a thumb */
.field.lg > input{height:38px; font-size:var(--t-lg)}

select{border:1px solid var(--line); border-radius:6px; background:var(--surface);
  padding:4px 8px; font-size:var(--t-sm); cursor:pointer}

/* ---------- Buttons (only one, modifiers set the rank) ---------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  height:30px; padding:0 var(--s3); border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--surface); color:var(--ink-2); cursor:pointer; font-size:var(--t-sm);
}
.btn:hover{border-color:var(--accent); color:var(--accent)}
.btn.pri{background:var(--accent); border-color:var(--accent); color:#fff}
.btn.pri:hover{filter:brightness(1.08); color:#fff}
.btn.icon{width:26px; height:24px; padding:0; color:var(--ink-3)}
.btn.lg{height:38px; padding:0 var(--s4); font-size:var(--t-md)}
.btn.danger:hover{border-color:var(--danger); color:var(--danger)}

/* ---------- Skeleton ---------- */
/* width:100% is not optional. body is display:flex/column, and margin:0 auto
   on a flex item shrinks it to its content width, so max-width stops doing
   anything (measured: 933px on a 1440 viewport). Fill first, then let
   max-width pull it back. */
.shell{display:grid; grid-template-columns:var(--rail) 1fr; gap:var(--s5);
  padding:var(--s4); width:100%; max-width:1360px; margin:0 auto}
/* Grid items default to min-width:auto - without this line the sidebar is
   pushed open by its content instead of scrolling sideways, and the whole page
   widens with it (measured: body at 488px on a 390 viewport). */
.shell > *{min-width:0}
/* Single-column pages (home/login/terms) use this */
.page{width:100%; max-width:760px; margin:0 auto; padding:var(--s6) var(--s4) var(--s7)}
.page.wide{max-width:1360px; padding:var(--s4)}
/* Narrow text pages (about/help/terms/privacy/settings/404): the container owns
   the vertical rhythm, so every top-level block is spaced the same.
   It used to come from .card + .card, which breaks the moment a non-card sits
   between two cards - on /about the .feature-row did exactly that and the
   title block, the intro card and the feature row all ended up touching.
   .wide is excluded: those pages stack <section>s on the larger --s5 rhythm. */
.page:not(.wide) > * + *{margin-top:var(--s3)}

/* Left column: filters (search) and navigation (admin) share one set */
.rail{position:sticky; top:62px; align-self:start; display:flex; flex-direction:column; gap:var(--s5)}
.rgroup > .label{display:block; margin-bottom:var(--s2); padding-left:7px}
.rlist{display:flex; flex-direction:column}
.rlist a{display:flex; align-items:center; justify-content:space-between; gap:var(--s2);
  color:var(--ink-2); padding:4px 7px; border-radius:var(--radius-sm)}
.rlist a:hover{background:var(--hover); color:var(--ink)}
.rlist a[aria-current]{background:var(--accent-bg); color:var(--accent); font-weight:500}
.rlist .n{font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3); font-variant-numeric:tabular-nums}
.rlist a[aria-current] .n{color:var(--accent)}
.rlist .n.alert{color:var(--accent); font-weight:500}

/* Sibling sections get their own spacing, so pages never hand-write margin-top */
main > section + section,
main > section + .ad,
main > .ad + section{margin-top:var(--s5)}

/* Same idea for the HTMX-swapped results block: .bar carries its own bottom
   margin, .pager its own padding, but the AV card had nothing - so it sat
   flush against the results list. Let the container space whatever the
   fragment happens to contain (magnet results and the online tab differ). */
#searchResults > * + *{margin-top:var(--s3)}

/* ---------- Page head ---------- */
.bar{display:flex; align-items:baseline; gap:var(--s3); margin-bottom:var(--s3); flex-wrap:wrap}
.bar h1{font-size:var(--t-xl); font-weight:600; letter-spacing:-.02em}
.bar h1 em{font-style:normal; color:var(--accent)}
.bar .cnt{font-family:var(--mono); font-size:var(--t-sm); color:var(--ink-3)}
.bar .end{margin-left:auto; display:flex; align-items:center; gap:var(--s2)}
.bar p{width:100%; color:var(--ink-3); font-size:var(--t-sm); margin-top:calc(var(--s1) * -1)}

/* Breadcrumbs: orientation for users, structure for search engines (paired with BreadcrumbList JSON-LD) */
.crumb{display:flex; align-items:center; gap:6px; font-size:var(--t-sm); color:var(--ink-3); margin-bottom:var(--s3)}
.crumb a:hover{color:var(--accent)}

/* ---------- Surfaces ---------- */
.list{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius)}
.card{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s4)}
.card + .card{margin-top:var(--s3)}
.card > h2{font-size:var(--t-lg); font-weight:600; margin-bottom:var(--s3)}

/* ---------- Tables (search results / admin lists / favourites all use this) ----------
   Column widths are overridden per page with --cols; the structure is identical. */
.head,.row{display:grid; grid-template-columns:var(--cols); gap:var(--s3); align-items:center}
.head{padding:6px var(--s4); border-bottom:1px solid var(--line)}
.head > *{font-family:var(--mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3)}
/* Section variant of .head: a single cell used to split "grouped results" from
   "other results" inside one list. Unlike a column header it carries content,
   so it must not be hidden along with the headers on narrow screens. */
.head.sec{grid-template-columns:1fr}
.row{padding:8px var(--s4); font-size:var(--t-sm)}
.row + .row{border-top:1px solid var(--line-2)}
.row:hover{background:var(--hover)}
.list > .row:last-child{border-bottom-left-radius:var(--radius); border-bottom-right-radius:var(--radius)}

/* ---------- Work-code groups (search / code page / favourites) ---------- */
.work{border-bottom:1px solid var(--line)}
.work:last-child{border-bottom:0}
.whead{display:grid; grid-template-columns:var(--cols); gap:var(--s3); align-items:center;
  padding:var(--s2) var(--s4); cursor:pointer}
.whead:hover{background:var(--hover)}
.wt{display:flex; align-items:flex-start; gap:var(--s3); min-width:0}
.caret{color:var(--ink-3); font-size:9px; width:9px; flex:none; margin-top:5px}
/* Entries without a code have no expand action, but still hold the column so the rest stays aligned */
.caret.none{visibility:hidden}
.wtx{min-width:0}
.code{font-family:var(--mono); font-size:var(--t-sm); font-weight:500; color:var(--accent)}
.name{font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.meta{font-family:var(--mono); font-size:10px; color:var(--ink-3); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* Release rows: indent shows ownership, rules separate them - no vertical bars, no alternating backgrounds */
.rel{display:grid; grid-template-columns:var(--cols); gap:var(--s3); align-items:center;
  padding:5px var(--s4) 5px 46px; font-size:var(--t-sm)}
.whead + .rel,.rel + .rel{border-top:1px solid var(--line-2)}
.rel:hover{background:var(--hover)}
.rname{font-family:var(--mono); color:var(--ink-2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* Tags embedded in the name are for narrow screens only: wide screens have a
   dedicated QUALITY column, and showing both adds a duplicate tag line under
   every release. */
.rname .tags{display:none}
.more{padding:3px var(--s4) 7px 46px; font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3)}

/* Streaming rows.
   Magnets and streams are two ways of getting the same title, not two sets of
   search results - so this lives inside the code group rather than behind a
   top-level tab, and the user never has to guess which tab holds what.
   Looking different from a magnet row is deliberate: there is no size and no
   seeder count, so forcing it into .rel's six columns would just produce a
   row of em dashes. */
.play-row{display:flex; align-items:center; gap:var(--s3); padding:6px var(--s4) 6px 46px;
  border-top:1px solid var(--line-2); font-size:var(--t-sm); color:var(--ink-2)}
.play-row:hover{background:var(--hover); color:var(--accent)}
.play-row .pi{width:18px; height:18px; border-radius:50%; background:var(--accent-bg); color:var(--accent);
  display:grid; place-items:center; font-size:9px; flex:none}
.play-row .n{margin-left:auto; font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3)}
@media (max-width:720px){
  .play-row{padding-left:34px; flex-wrap:wrap}
  .play-row .n{margin-left:0; width:100%}
}
.more a:hover{color:var(--accent)}

/* ---------- Covers ----------
   16:11 landscape, the same ratio as detail-cover and av-grid.
   Small on desktop to keep density, with a hover zoom; large on phones, where
   you see one or two at a time and titles wrap, so the cover is the fastest
   way to recognise something. Taking up proportionally more room on a phone
   is intentional. */
.thumb{width:var(--cover); aspect-ratio:16/11; border-radius:4px; flex:none;
  background:var(--line-2); box-shadow:inset 0 0 0 1px rgba(0,0,0,.08);
  position:relative; overflow:visible; transition:box-shadow .16s}
.thumb > img{width:100%; height:100%; object-fit:cover; border-radius:4px}
/* Small cover inside a streaming row: follows the text line, never raises the row height */
.thumb.sm{width:44px; border-radius:3px; display:inline-block; vertical-align:-9px; margin-right:8px}
.whead:hover .thumb{box-shadow:inset 0 0 0 1px rgba(0,0,0,.14), 0 1px 6px rgba(0,0,0,.10)}
/* ponytail: the zoom layer always pops to the right and gets clipped near the
   right edge of the viewport. Fixing it means measuring bounds in JS and
   flipping - wait for someone to complain first. */
.zoom{position:absolute; left:calc(100% + 10px); top:50%; transform:translateY(-50%) scale(.96);
  width:280px; aspect-ratio:16/11; border-radius:6px; background:inherit;
  box-shadow:0 8px 28px rgba(0,0,0,.18), inset 0 0 0 1px rgba(0,0,0,.10);
  opacity:0; visibility:hidden; pointer-events:none; z-index:40; transition:opacity .16s, transform .16s}
.thumb:hover .zoom{opacity:1; visibility:visible; transform:translateY(-50%) scale(1)}

/* ---------- Tags (one kind, site-wide) ---------- */
.tags{display:flex; gap:4px; overflow:hidden}
.tag{font-family:var(--mono); font-size:10px; letter-spacing:.04em; padding:1px 5px;
  border-radius:3px; background:var(--bg); color:var(--ink-2); white-space:nowrap}
.tag.hi{background:var(--accent-bg); color:var(--accent)}
.tag.ok{background:#2E7D5014; color:var(--ok)}
.tag.warn{background:#B4761B14; color:var(--warn)}
.tag.danger{background:#B3261E14; color:var(--danger)}

/* ---------- Data cells ---------- */
.size,.num{font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--ink-2); text-align:right}
.src{font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3); text-align:right;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* A seeder count is just a number. The column sorts by it; it needs no bar */
.seed{font-family:var(--mono); font-variant-numeric:tabular-nums; font-weight:500; color:var(--ok); text-align:right}
.seed.low{color:var(--ink-2); font-weight:400}
.seed.dead{color:var(--dead); font-weight:400}

/* ---------- Tabs (profile / code page) ---------- */
.tabs{display:flex; gap:var(--s1); border-bottom:1px solid var(--line); margin-bottom:var(--s3)}
.tabs a{padding:7px var(--s3); color:var(--ink-2); border-bottom:2px solid transparent; margin-bottom:-1px}
.tabs a:hover{color:var(--ink)}
.tabs a[aria-current]{color:var(--accent); border-bottom-color:var(--accent); font-weight:500}
.tabs .n{font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3); margin-left:5px}

/* ---------- Stat cards (admin overview) ---------- */
.stats{display:grid; grid-template-columns:repeat(auto-fill,minmax(158px,1fr)); gap:var(--s3); margin-bottom:var(--s4)}
.stat{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s3) var(--s4)}
.stat .v{font-family:var(--mono); font-size:var(--t-xl); font-weight:500; font-variant-numeric:tabular-nums; letter-spacing:-.02em}
.stat .k{font-size:var(--t-sm); color:var(--ink-3); margin-top:2px}
.stat .v small{font-size:var(--t-sm); color:var(--ink-3); font-weight:400; margin-left:4px}
.stat.alert .v{color:var(--accent)}
.stat.bad .v{color:var(--danger)}

/* Cross-page jump card: magnet detail -> code page.
   A thin page pointing at a rich one; users and crawlers both gain. */
a.card.xlink{display:flex; align-items:center; gap:var(--s3)}
a.card.xlink:hover{border-color:var(--accent)}
a.card.xlink:hover .name{color:var(--accent)}
a.card.xlink > .btn{margin-left:auto}

/* The "one line of text + a button on the right" row on the settings page */
.linked{display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap}
.linked > .btn{margin-left:auto}
.linked.top{align-items:flex-start}

/* ---------- Empty state / waiting-on-crawl state ----------
   A keyword that is not in the index queues a job for the worker, and the page
   polls every 2 seconds. This is the most distinctive moment on the site: the
   answer is not "nothing", it is "not yet". It has to say what we are waiting
   for, roughly how long, and that no refresh is needed - otherwise the user
   decides the site is broken and leaves. */
.empty{padding:var(--s7) var(--s4); text-align:center; color:var(--ink-3)}
.empty h2{font-size:var(--t-lg); font-weight:500; color:var(--ink); margin-bottom:var(--s2)}
.empty p{margin-bottom:var(--s3)}
.empty .acts{justify-content:center}
.code404{font-family:var(--mono); font-size:64px; font-weight:500; letter-spacing:.06em;
  color:var(--accent); line-height:1; margin-bottom:var(--s3)}

/* Body typography for the static pages (about/privacy/terms/help).
   These are long-form text and should not inherit the tight line height of
   the list pages. */
.card p{color:var(--ink-2); line-height:1.8; margin-bottom:var(--s3)}
.card p:last-child{margin-bottom:0}
.card ul,.card ol{color:var(--ink-2); line-height:1.8; padding-left:var(--s5); margin-bottom:var(--s3)}
.card a{color:var(--accent)}
.card strong{color:var(--ink)}
.card h3{font-size:var(--t-lg); font-weight:500; color:var(--accent); margin-bottom:var(--s2)}
/* The three side-by-side feature cards on the about page */
.card.feature{text-align:center}
.feature-row{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:var(--s3)}
.feature-row .card{margin-top:0}
/* Key hints on the help page */
.kbd{font-family:var(--mono); font-size:var(--t-sm); padding:2px 6px;
  border:1px solid var(--line); border-radius:4px; background:var(--bg)}
.empty .btn{margin-top:var(--s2)}
.spin{width:18px; height:18px; margin:0 auto var(--s3);
  border:2px solid var(--line); border-top-color:var(--accent); border-radius:50%;
  animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
/* Crawl progress: which sites have answered, so the wait becomes visible */
.probe{display:flex; gap:6px; justify-content:center; flex-wrap:wrap; margin-top:var(--s4)}
.probe .tag{opacity:.45}
.probe .tag.done{opacity:1}

/* ---------- Footer ----------
   The old one was padding:28px 48px on a non-wrapping flex row, and on a 390px
   screen its six links pushed the page out to 489px. That was the only real
   horizontal overflow on the site, and it hit every page. */
.foot{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s3) var(--s5);
  padding:var(--s5) var(--s4); border-top:1px solid var(--line);
  font-family:var(--mono); font-size:var(--t-xs); letter-spacing:.08em;
  text-transform:uppercase; color:var(--ink-3); margin-top:auto}
.foot nav{display:flex; flex-wrap:wrap; gap:var(--s3) var(--s4)}
.foot a:hover{color:var(--accent)}
.foot .end{margin-left:auto}
@media (max-width:720px){
  .foot{padding:var(--s4) var(--s3)}
  .foot .end{margin-left:0; width:100%}
}

/* ---------- Pager ---------- */
.pager{display:flex; align-items:center; gap:var(--s3); padding:var(--s4) 2px var(--s7);
  font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3)}
.pg{display:flex; gap:4px; margin-left:auto}
.end-btn{margin-left:auto}
.pg a,.pg span{min-width:26px; height:26px; display:grid; place-items:center; border-radius:var(--radius-sm);
  font-family:var(--mono); font-size:var(--t-xs); border:1px solid var(--line);
  background:var(--surface); color:var(--ink-2)}
.pg [aria-current]{background:var(--accent); border-color:var(--accent); color:#fff; font-weight:500}
.pg a:hover{border-color:var(--accent); color:var(--accent)}

/* ---------- Ads ----------
   One per screen, with a clear boundary and an AD label. If nothing loads the
   whole slot disappears: never a dashed empty box saying
   "Sponsored Ad / Loading ad...". */
.ad{display:flex; align-items:center; gap:var(--s3); padding:var(--s3) var(--s4);
  background:var(--bg); border-bottom:1px solid var(--line)}
.ad > .frame{flex:1; min-height:50px; border-radius:6px; background:var(--surface); border:1px solid var(--line);
  display:grid; place-items:center; color:var(--ink-3); font-family:var(--mono); font-size:var(--t-sm)}
.ad:empty,.ad.empty{display:none}
/* Standalone ad slots (detail and play pages); the ones embedded in lists get no border */
.ad.boxed{border:1px solid var(--line); border-radius:var(--radius); margin:var(--s5) 0}
/* The box itself gives up all padding and the flex gap, so the creative runs
   edge to edge inside the border. Only the "AD" tag keeps an inset - with none
   the text would sit right on the border. The frame drops its own border and
   fill too: at zero padding it would land exactly on .boxed's border and read
   as a double line.
   Must stay BEFORE .ad.pending - equal specificity (0,2,0), last one wins, and
   pending has to keep its padding:0/border:0/margin:0 (see the note there). */
.ad.boxed{padding:0; gap:0}
.ad.boxed > .label{padding:var(--s2) var(--s3)}
.ad.boxed > .frame{border:0; background:none; border-radius:0}
/* Sidebar square: the rail is ~184px wide, so the "AD" tag cannot sit beside
   the creative - it goes on top, centred, and the 250x250 gets the full column. */
.ad[data-ad="block"]{display:block}
.ad[data-ad="block"] > .label{display:block; text-align:center}
/* Standby state: in the document flow but taking no visual space.
   hidden will not do - IntersectionObserver never reports a display:none
   element as entering the viewport, which deadlocks lazy loading (hit this for
   real). Keep 1px so the observer can see it.

   This MUST stay after .ad.boxed: same specificity (0,2,0), so whichever comes
   last wins. With .boxed last, a pending boxed slot got its border and its
   24px margins back and rendered as a 2px line floating in 48px of space -
   which is exactly the sidebar square slot on desktop. */
.ad.pending{padding:0; border:0; margin:0; background:none; min-height:1px}
.ad.pending > *{display:none}
.ad .narrow{display:none}

/* Sidebar square slot. Desktop only - see .side-ad{display:none} in the breakpoints below */
.side-ad{margin-top:var(--s5)}

/* ---------- Play page ---------- */
.hero.play{grid-template-columns:1fr 360px}
/* 侧栏现在只剩 Episodes（Other servers 已经挪到播放器下方），但长剧集照样能有
   上百条，一样会把 grid 这一行撑得比左栏高。封顶 + 内部滚动。
   只封 .list，不封 aside：广告位在 aside 底部，塞进滚动容器里会被裁掉，
   IntersectionObserver 看不见就不会加载——播放页恰恰是全站停留最长的一页。 */
.hero.play aside .list{max-height:min(34vh, 300px); overflow-y:auto; overscroll-behavior:contain}
/* "Other servers" 在左栏是另起一节，而 .bar 只有 margin-bottom，紧贴在元数据下面。
   给一档 --s5，和 main > section + section 的分节间距对齐。 */
.hero.play .dl + .bar{margin-top:var(--s5)}
/* 16:9 in a 1300px-wide main column is 730px tall, leaving nothing but the
   player on screen. Cap by viewport height and centre, width adapting. */
/* ArtPlayer fills this container, so the container must have a definite
   height. 16:9 in a 1300px-wide main column reaches 730px and fills the whole
   screen with the player, so cap it against the viewport. */
.player{aspect-ratio:16/9; max-height:min(68vh, 620px); margin:0 auto; width:100%;
  border-radius:var(--radius); background:#171514; overflow:hidden}
.player .art-video-player{border-radius:var(--radius)}
.player .label{color:rgba(255,255,255,.4)}
.pi.big{width:52px; height:52px; font-size:18px; background:rgba(255,255,255,.10); color:#fff}
.play-row[aria-current]{background:var(--accent-bg); color:var(--accent)}

/* ---------- Trending ranks ---------- */
.rank{font-family:var(--mono); font-size:var(--t-sm); color:var(--ink-3); text-align:center;
  font-variant-numeric:tabular-nums}
.rank.top{color:var(--accent); font-weight:600}
/* Volume bar: the one place on the site where length expresses quantity -
   a ranking is a size comparison, and a bar reads faster than a number. The
   seeder column gets no bar, because that column answers "can I download
   this", not "which is bigger". */
.track{height:4px; border-radius:2px; background:var(--line-2); overflow:hidden}
.track .fill{display:block; height:100%; background:var(--accent); opacity:.55; border-radius:2px}

/* ---------- Code page: cover + metadata ---------- */
.hero{display:grid; grid-template-columns:340px 1fr; gap:var(--s5); margin-bottom:var(--s4)}
.hero .poster{aspect-ratio:16/11; border-radius:var(--radius); background:var(--line-2);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.08); position:relative; overflow:hidden}
.hero h1{font-size:var(--t-xl); font-weight:600; line-height:1.35; letter-spacing:-.015em; margin:var(--s1) 0 var(--s3)}
/* Metadata is a definition list: semantically right, and easier for search engines */
.dl{display:grid; grid-template-columns:auto 1fr; gap:5px var(--s3); font-size:var(--t-sm)}
.dl dt{font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3); text-transform:uppercase; letter-spacing:.08em; padding-top:2px}
.dl dd a:hover{color:var(--accent)}
/* .seed is right-aligned by default (tables need it for scanning), but in a definition list it starts a sentence */
.dl .seed{text-align:left}
/* Long hex strings like an infoHash must be breakable on narrow screens */
.brk{word-break:break-all}
.acts{display:flex; gap:var(--s2); margin-top:var(--s4); flex-wrap:wrap}

/* Screenshot thumbnails */
.shots{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:var(--s2)}
.shots a{aspect-ratio:16/11; border-radius:var(--radius-sm); background:var(--line-2);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.06); overflow:hidden}

/* ---------- Code grid (/latest and related) ---------- */
.grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:var(--s5) var(--s4)}
/* An <a> is inline by default, so the spans inside would not become blocks and the cover's aspect-ratio would collapse */
.grid > a{display:block; min-width:0}
.grid > a > *{display:block}
.grid .c{aspect-ratio:16/11; border-radius:6px; background:var(--line-2);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.07); overflow:hidden; margin-bottom:var(--s2)}
.grid .c img{width:100%; height:100%; object-fit:cover; transition:transform .25s}
.grid > a:hover .c img{transform:scale(1.04)}
.grid > a:hover .name{color:var(--accent)}
/* 演员名换行是有意的（一行放不下一串名字），但当年只改了 white-space 没给上限，
   四十个演员能把一张卡撑到半屏高。和下面 .name 一样夹成两行。
   /latest 和相似推荐共用这一条。 */
.grid .meta{white-space:normal; margin-top:2px;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
.grid .name{font-size:var(--t-md); font-weight:400; line-height:1.4; white-space:normal;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}

/* ---------- Home ---------- */
.hero-search{text-align:center; padding:var(--s7) 0 var(--s6)}
.hero-search h1{font-size:34px; font-weight:600; letter-spacing:-.03em}
.hero-search h1 em{font-style:normal; color:var(--accent)}
.hero-search p{color:var(--ink-3); margin:var(--s2) 0 var(--s5)}
.hero-search .field{max-width:560px; margin:0 auto}
.chips{display:flex; gap:6px; flex-wrap:wrap; justify-content:center; margin-top:var(--s4)}
.chips a{padding:4px 11px; border:1px solid var(--line); border-radius:20px;
  background:var(--surface); color:var(--ink-2); font-size:var(--t-sm)}
.chips a:hover{border-color:var(--accent); color:var(--accent)}

/* ---------- Form pages (login / settings) ---------- */
.form{display:flex; flex-direction:column; gap:var(--s4)}
.form label{display:block; font-size:var(--t-sm); color:var(--ink-2); margin-bottom:5px}
/* .hint is not scoped to .form - cards, forms and list footers all use it */
.hint{font-size:var(--t-xs); color:var(--ink-3); margin-top:5px}
.hint.center{text-align:center}
.hint a{color:var(--accent)}
.divider{display:flex; align-items:center; gap:var(--s3); color:var(--ink-3); font-size:var(--t-xs)}
.divider::before,.divider::after{content:""; flex:1; height:1px; background:var(--line)}

/* ============================================================
   Responsive: three steps, each one genuinely changing the layout
   rather than just shrinking type
   ============================================================ */

/* Tablet: the left column becomes a horizontally scrolling bar at the top; tables drop columns */
@media (max-width:1040px){
  :root{--cover:96px}
  .shell{grid-template-columns:1fr; gap:var(--s3)}
  .rail{position:static; flex-direction:row; gap:var(--s2); overflow-x:auto;
    padding-bottom:var(--s1); scrollbar-width:none}
  .rail::-webkit-scrollbar{display:none}
  .rgroup{display:flex; align-items:center; gap:var(--s2); flex:none}
  .rgroup > .label{margin:0; padding:0; white-space:nowrap}
  .rlist{flex-direction:row; gap:var(--s1)}
  .rlist a{border:1px solid var(--line); background:var(--surface); white-space:nowrap; padding:3px 9px}
  .rlist a[aria-current]{border-color:transparent}
  /* On narrow screens the sidebar is either a horizontally scrolling filter bar
     or collapses into the column, and a 250x250 square fits neither shape. The
     in-body banner slots are still there and use the 320x50 mobile creative, so
     phone users see no fewer ads. */
  .rail .ad,.side-ad{display:none}
  .hero{grid-template-columns:260px 1fr; gap:var(--s4)}
}

/* Phone */
@media (max-width:720px){
  :root{--cover:120px}   /* Larger, not smaller: you see one or two at a time and the cover is the fastest thing to recognise */
  .topbar{gap:var(--s2); padding:0 var(--s3)}
  .topnav{display:none}
  /* margin-left:auto lives on .topnav and is hidden along with it, which packs
     the two right-hand buttons next to the logo - so hand the auto margin to
     the first button that follows. The home header has no search field, so the
     problem only shows up there. */
  .topnav + .btn{margin-left:auto}
  .burger{display:grid}
  /* Keyboard shortcut hints mean nothing on a phone */
  .shortcut-hint{display:none !important}

  /* 16px is not a taste call: iOS Safari zooms the whole page whenever a
     focused form control is smaller, and never zooms back. That zoom is what
     made the sort dropdown open with tiny options in the wrong place - the
     select was 12px and the topbar search 13px. */
  input,select,textarea{font-size:16px}

  .shell,.page.wide{padding:var(--s3)}
  .page{padding:var(--s5) var(--s3) var(--s6)}

  .zoom{display:none}          /* No hover, so no point */
  .meta{white-space:normal; line-height:1.4}
  .name{white-space:normal}

  .bar{gap:var(--s2)}
  /* Only worth a full row when it holds a dropdown (the search sort);
     a lone "Manage" button on its own row looks bad */
  .bar .end:has(select){margin-left:0; width:100%}
  .bar .end select{flex:1}

  .head{display:none}          /* Column headers mean nothing on a narrow screen */
  .head.sec{display:grid}      /* But section labels are content and stay */
  .whead{grid-template-columns:1fr; padding:10px var(--s3)}
  .whead > :not(.wt){display:none}

  /* Rows stop being columns and become two-line blocks: name + a one-line summary */
  .row,.rel{display:block; position:relative; padding:8px var(--s3)}
  .rel{padding-left:34px}
  .row > :not(.rname):not(.mob):not(.btn),
  .rel > :not(.rname):not(.mob):not(.btn){display:none}
  .rname{white-space:normal; word-break:break-all; margin-bottom:3px}
  .rname .tags{display:inline-flex; vertical-align:-2px; margin-left:6px}
  .row > .btn,.rel > .btn{position:absolute; right:var(--s3); top:50%; transform:translateY(-50%)}
  .more{padding-left:34px}

  .hero,.hero.play{grid-template-columns:1fr}
  .rank{text-align:left}
  .track{display:none}   /* No room for the volume bar on a narrow screen; the number is enough */
  .grid{grid-template-columns:repeat(2,1fr); gap:var(--s4) var(--s3)}
  .stats{grid-template-columns:repeat(2,1fr)}
  .hero-search{padding:var(--s6) 0 var(--s5)}
  .hero-search h1{font-size:26px}

  .ad{padding:var(--s3)}
  .ad .wide{display:none}
  .ad .narrow{display:inline}
}

/* Inline mobile metadata, hidden on desktop */
.mob{display:none}
@media (max-width:720px){
  .mob{display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap;
    font-family:var(--mono); font-size:var(--t-xs); color:var(--ink-3)}
  .mob .s{color:var(--ok); font-weight:500}
  .mob .s.dead{color:var(--dead); font-weight:400}
  .mob .sz{color:var(--ink-2)}
}

/* Print / reduced motion */
@media (prefers-reduced-motion:reduce){
  *{transition:none !important; animation:none !important}
}

/* The shared topbar is carried by a custom element (the header partial in the
   real implementation). display:contents keeps <site-top> from generating a box
   of its own, which would otherwise block position:sticky on the .top inside. */


/* ============================================================
   Components carried over verbatim from the old style.css
   The lightbox, toast, mobile drawer, search suggestions and player overlay
   are fussy about positioning, animation and stacking, so rewriting them
   risked more than it gained. They are kept line for line and only moved into
   this file, so style.css could be deleted outright.
   ============================================================ */


/* ===== Light Theme ===== */
/* Legacy variables still used by the old components (lightbox/toast/drawer...).
   background / color / --accent are deliberately NOT carried over - those come
   from the new tokens above, and bringing them here would override the new
   palette simply by coming later in the file. */
html.light{--accent-soft: #d44a1a18; --border: #e5e5e5; --text-secondary: #666; --text-tertiary: #999; --card-bg: #fff; --card-hover: #fdfdfd; --divider: #f0f0f0; --suggestion-bg: #fafafa}



/* ===== Dark Theme ===== */
/* Legacy variables still used by the old components (lightbox/toast/drawer...).
   background / color / --accent are deliberately NOT carried over - those come
   from the new tokens above, and bringing them here would override the new
   palette simply by coming later in the file. */
html.dark{--accent-soft: #e05a2a18; --border: #2e2e2e; --text-secondary: #777; --text-tertiary: #555; --card-bg: #222; --card-hover: #262626; --divider: #262626; --suggestion-bg: #222}



.top-logo { flex-shrink: 0; }


.user-menu.show { display: flex; align-items: center; }


html.light .hamburger span { background: #1a1a1a; }

html.dark .hamburger span { background: #d4d4d4; }


.hamburger.open span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }

.hamburger.open span:nth-child(2) { opacity: 0; }

.hamburger.open span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }


/* ===== Mobile Overlay ===== */
.mobile-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.2);
    z-index: 40;
    opacity: 0;
    transition: opacity 0.3s;
}


.mobile-overlay.show { display: block; opacity: 1; }


/* ===== Mobile Nav ===== */
.mobile-nav {
    display: none;
    position: fixed;
    top: 0;
    right: 0;
    width: 260px;
    max-width: 80%;
    height: 100%;
    z-index: 50;
    flex-direction: column;
    padding: 16px 24px 32px;
    transform: translateX(100%);
    transition: transform 0.3s ease;
    background: var(--card-bg);
    box-shadow: -4px 0 20px rgba(0,0,0,0.1);
}


.mobile-nav.open { transform: translateX(0); }


.mobile-nav-close {
    align-self: flex-end;
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
    opacity: 0.5;
    transition: opacity 0.2s;
    color: inherit;
    margin-bottom: 8px;
}


.mobile-nav-close:hover { opacity: 1; }


.mobile-nav a {
    font-size: 14px;
    text-decoration: none;
    letter-spacing: 1px;
    text-transform: uppercase;
    padding: 14px 0;
    transition: color 0.2s;
    display: block;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--divider);
}


.mobile-nav a:hover { color: var(--accent); }


html.light .logo { color: #1a1a1a; }

html.dark .logo { color: #d4d4d4; }


/* ===== Search Container ===== */
.search-container {
    width: 100%;
    max-width: 560px;
    position: relative;
}


html.light .search-box {
    background: #fff;
    border: 1px solid #e5e5e5;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}


html.light .search-box:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft), 0 2px 8px rgba(0,0,0,0.06);
}


html.dark .search-box {
    background: #222;
    border: 1px solid #2e2e2e;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}


html.dark .search-box:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft), 0 2px 8px rgba(0,0,0,0.3);
}

html.light .search-icon { color: #aaa; }

html.dark .search-icon { color: #555; }


html.light .search-input { color: #1a1a1a; }

html.dark .search-input { color: #d4d4d4; }

html.light .search-input::placeholder { color: #999; }

html.dark .search-input::placeholder { color: #666; }


/* ===== Suggestions Dropdown ===== */
.suggestions {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    display: none;
    z-index: 10;
    border-radius: 12px;
    overflow: hidden;
}


html.light .suggestions {
    background: #fff;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    border: 1px solid #e5e5e5;
}


html.dark .suggestions {
    background: #222;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    border: 1px solid #2a2a2a;
}


.suggestions.show { display: block; }


.suggestion-item {
    padding: 12px 24px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 300;
    transition: background 0.15s;
}


html.light .suggestion-item { color: #555; }

html.light .suggestion-item:hover { background: #fafafa; }

html.dark .suggestion-item { color: #999; }

html.dark .suggestion-item:hover { background: #222; }


.hot-tag {
    font-size: 13px;
    font-weight: 400;
    letter-spacing: 0.3px;
    cursor: pointer;
    transition: color 0.2s;
    text-decoration: none;
    padding: 5px 0;
    position: relative;
}


html.light .hot-tag { color: #777; }

html.light .hot-tag:hover { color: var(--accent); }

html.dark .hot-tag { color: #555; }

html.dark .hot-tag:hover { color: var(--accent); }


.hot-tag::before {
    content: "";
    display: inline-block;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: middle;
    background: var(--accent);
    opacity: 0;
    transition: opacity 0.2s;
}


.hot-tag:hover::before { opacity: 1; }


html.light .history-title { color: #bbb; }

html.dark .history-title { color: #555; }


html.light .history-manage { color: #ccc; }

html.light .history-manage:hover { color: var(--accent); }

html.dark .history-manage { color: #444; }

html.dark .history-manage:hover { color: var(--accent); }


.history-item {
    font-size: 13px;
    font-weight: 400;
    letter-spacing: 0.3px;
    cursor: pointer;
    transition: color 0.2s;
    text-decoration: none;
    padding: 5px 0;
    position: relative;
}


html.light .history-item { color: #999; }

html.light .history-item:hover { color: var(--accent); }

html.dark .history-item { color: #555; }

html.dark .history-item:hover { color: var(--accent); }


.history-item::before {
    content: "";
    display: inline-block;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: middle;
    background: var(--accent);
    opacity: 0;
    transition: opacity 0.2s;
}


.history-item:hover::before { opacity: 1; }


html.light .history-login { color: #bbb; }

html.dark .history-login { color: #555; }


/* ===== Shortcut Hint ===== */
.shortcut-hint {
    position: fixed;
    bottom: 68px;
    left: 50%;
    transform: translateX(-50%);
    padding: 5px 14px;
    border-radius: 6px;
    font-size: 11px;
    letter-spacing: 1px;
    opacity: 0;
    transition: opacity 0.3s;
    pointer-events: none;
}


html.light .shortcut-hint { background: #f0f0f0; color: #888; }

html.dark .shortcut-hint { background: #222; color: #666; }

.shortcut-hint.show { opacity: 1; }


.play-btn {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(8px);
    border: 2px solid rgba(255,255,255,0.3);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s;
    color: #fff;
}


.play-btn:hover {
    background: var(--accent);
    border-color: var(--accent);
    transform: translate(-50%, -50%) scale(1.08);
}


.play-btn:active { transform: translate(-50%, -50%) scale(0.95); }


/* Preview video */
.detail-video {
    width: 100%;
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 28px;
    aspect-ratio: 16 / 9;
    background: #000;
    display: none;
    position: relative;
}

.detail-video.show { display: block; }

.detail-video video { width: 100%; height: 100%; display: block; }

.video-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(8px);
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    transition: background 0.2s;
    z-index: 2;
}

.video-close:hover { background: rgba(0,0,0,0.7); }


.img-error {
    object-fit: contain !important;
    padding: 16px;
}


@keyframes spin { to { transform: rotate(360deg); } }


/* ===== Lightbox ===== */
.lightbox {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.9);
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 200;
    cursor: zoom-out;
}


.lightbox.show { display: flex; }


.lightbox img {
    max-width: 90vw;
    max-height: 90vh;
    object-fit: contain;
    border-radius: 4px;
}


.lightbox-close {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: rgba(255,255,255,0.1);
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    transition: background 0.2s;
}


.lightbox-close:hover { background: rgba(255,255,255,0.2); }


html.dark .tag-boil { background: #ef5350; color: #1a1a1a; }

html.dark .tag-new { background: #1b3a1b; color: #66bb6a; }


html.dark .btn-logout:hover {
    border-color: #ef5350;
    color: #ef5350;
}


.list-item {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    gap: 12px;
    border-bottom: 1px solid var(--divider);
    transition: background 0.15s;
    cursor: pointer;
}


.list-item:last-child { border-bottom: none; }

.list-item:hover { background: var(--suggestion-bg); }


.list-item-main {
    flex: 1;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}


.list-item-title {
    font-size: 14px;
    letter-spacing: 0.3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-bottom: 2px;
}


.list-item-sub {
    font-size: 11px;
    color: var(--text-tertiary);
    letter-spacing: 0.3px;
    display: flex;
    gap: 12px;
}


.list-item-delete {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    border: none;
    background: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-tertiary);
    transition: all 0.2s;
}


.list-item-delete:hover {
    background: #fce4ec;
    color: #c62828;
}


html.dark .list-item-delete:hover {
    background: #3a1b1b;
    color: #ef5350;
}


.modal-overlay.show { display: flex; }


/* ===== Toast ===== */
.toast {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%) translateY(-60px);
    padding: 12px 20px;
    border-radius: 8px;
    font-size: 13px;
    letter-spacing: 0.3px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    box-shadow: 0 4px 20px rgba(0,0,0,0.12);
    z-index: 200;
    transition: transform 0.3s ease, opacity 0.3s ease;
    pointer-events: none;
    display: flex;
    align-items: center;
    gap: 10px;
    opacity: 0;
    max-width: 400px;
}


.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }

.toast-icon { flex-shrink: 0; width: 18px; height: 18px; }

.toast-success .toast-icon { color: #2e7d32; }

.toast-error .toast-icon { color: #c62828; }

.toast-info .toast-icon { color: var(--accent); }


/* ===== Server Message (hidden, read by JS) ===== */
.server-msg { display: none; }


/* ===== Footer ===== */






.footer-links a {
    text-decoration: none;
    color: inherit;
    text-transform: uppercase;
    letter-spacing: 1px;
    transition: color 0.2s;
}


.footer-links a:hover { color: var(--accent); }


@media (max-width: 480px) {
    .top-bar-left { font-size: 12px; }
    .sign-in-btn { padding: 5px 10px; font-size: 11px; }
    .logo { font-size: 32px; letter-spacing: 6px; margin-bottom: 32px; }
    .search-box { height: 46px; padding: 0 4px 0 18px; }
    .search-input { font-size: 14px; }
    .search-submit { width: 36px; height: 36px; }
    .hot-tags { gap: 4px 12px; }
    .hot-tag { font-size: 12px; }
    .search-bar { height: 44px; padding: 0 4px 0 16px; }
    .search-bar-input { font-size: 14px; }
    .search-bar-submit { width: 34px; height: 34px; }
    .result-meta { gap: 4px 12px; }
    .result-magnet { font-size: 10px; }
    .page-title { font-size: 22px; }
    .time-tab { padding: 8px 14px; font-size: 12px; }
    .trending-heat { display: none; }
    .trending-tag { display: none; }
    .detail-title { font-size: 16px; }
    .screenshots-grid { grid-template-columns: 1fr; }
    .detail-actions { flex-direction: column; }
    .action-btn { justify-content: center; }
    .play-btn { width: 48px; height: 48px; }
    .auth-form-card { padding: 28px 24px; }
    .auth-logo-text { font-size: 28px; letter-spacing: 6px; }
    .user-card-avatar { width: 56px; height: 56px; font-size: 28px; }
    .stat-num { font-size: 16px; }
    .user-card-actions { flex-direction: column; }
    .user-card-actions .btn-outline,
    .user-card-actions .btn-logout { width: 100%; justify-content: center; }
}


/* Visually hidden, still available to screen readers and search engines */
.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;
}

/* Fade-out on delete, matching hx-swap's swap:200ms */
.list-item.htmx-swapping {
    opacity: 0;
    transition: opacity 200ms ease-out;
}


/* ==========================================================================
   Profile: touch targets, responsiveness, accessibility fixes
   ========================================================================== */

/* 1. Delete button touch target grown from 28px to 40px
      44px is the usual floor for a tappable area on mobile; 28px was too
      small, and it sits right next to the full-row link, so deleting by
      accident was easy. Transparent padding widens the hit area while the
      visual size stays the same. */
.list-item-delete {
    width: 40px;
    height: 40px;
    margin: -6px 0;          /* offset the effect of the larger hit area on line height */
}

.list-item-delete svg {
    width: 15px;
    height: 15px;
}


/* 2. Delete button colours moved to variables, matching the rest of the project
      These used to be hard-coded #fce4ec / #c62828, which also meant writing a
      separate set for dark mode */
.list-item-delete:hover,
.list-item-delete:focus-visible {
    /* color-mix is unsupported in older browsers, so give a plain fallback first */
    background: var(--suggestion-bg);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
    outline: none;
}

.list-item-delete:focus-visible {
    box-shadow: 0 0 0 2px var(--accent);
}


/* 3. cursor:pointer pulled back from the whole row, leaving it only on the
      genuinely clickable parts. The entire .list-item used to be pointer, but
      only the inner link and the delete button do anything, so a hand cursor
      over blank space in the row was misleading. */
.list-item { cursor: default; }

.list-item-main { cursor: pointer; }


/* ==========================================================================
   Site-wide usability additions
   ========================================================================== */

/* Theme toggle and the video close button: 32px -> 40px hit area.
   The theme toggle is in every page header and is one of the most used icon
   buttons on the site; the video close button floats over the cover, and
   missing it leaves a page reload as the only way out.
   Padding widens the tappable area here too; the icons do not change size. */
.theme-toggle,
.video-close {
    width: 40px;
    height: 40px;
}

html.dark .cs-ok   { background: rgba(76, 175, 110, .18); color: #7ddba0; }

html.dark .cs-warn { background: rgba(214, 168, 84, .18); color: #e0b76a; }

html.dark .cs-bad  { background: rgba(239, 83, 80, .18);  color: #ef8b88; }


/* Stream-site admin page components (previously inline in admin-resource-sites.html) */
.list-item-action {
    padding: 6px 12px;
    background: var(--bg-secondary);
    color: var(--text-secondary);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
    transition: all 0.2s;
}

.list-item-action:hover {
    background: var(--card-hover);
    border-color: var(--text-tertiary);
}

.list-item-delete {
    padding: 6px;
    background: transparent;
    color: var(--text-tertiary);
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s;
}

.list-item-delete:hover {
    background: var(--color-error);
    color: white;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* ============================================================
   Responsive additions for the old components - MUST stay at the end of the
   file.

   That batch of old components was appended wholesale and contains
   .mobile-nav{display:none}. The rules here have to undo it, and at equal
   specificity that comes down to order, so they can only live after it.
   (Hit this for real: written in an earlier @media, they were overridden by
   the later appended display:none - the burger was clickable and the .open
   class was applied, but the drawer stayed invisible forever.) */
@media (max-width:720px){
  .mobile-nav{display:flex}
  .search-container{max-width:100%}
  .play-btn{width:48px; height:48px}
}
