/* ==========================================================================
   theme-zhihu — plugin pages ported from theme-end: bangumi, douban, steam,
   lightbox captions and pointer-follow baseline. Markup lives in the page
   templates; the --md-* aliases below map the upstream tokens to this
   theme's Zhihu palette.
   ========================================================================== */

/* ---------- upstream token aliases ---------- */

:root {
  --md-primary: var(--accent);
  --md-on-primary: var(--accent-on);
  --md-primary-container: rgba(var(--accent-rgb), 0.12);
  --md-on-primary-container: var(--accent);
  --md-secondary-container: var(--surface-3);
  --md-on-secondary-container: var(--text);
  --md-surface-container-low: var(--surface-2);
  --md-surface-container: var(--surface-2);
  --md-surface-container-high: var(--surface-2);
  --md-surface-container-highest: var(--surface-3);
  --md-on-surface: var(--text);
  --md-on-surface-variant: var(--muted-strong);
  --md-outline: var(--border-strong);
  --md-outline-variant: var(--border);
  --md-background: var(--bg);
  --md-error-container: #fdecea;
  --md-on-error-container: #b3261e;
  --md-radius-xs: 3px;
  --md-radius-sm: 4px;
  --md-radius-md: 4px;
  --md-radius-lg: 6px;
  --md-radius-xl: 8px;
  --md-radius-full: 999px;
  --md-ease: cubic-bezier(0.2, 0, 0, 1);
  --md-shadow-2: var(--shadow-pop);
}

:root.dark {
  --md-surface-container-low: var(--surface);
  --md-error-container: #4a1f1c;
  --md-on-error-container: #f2b8b5;
}

/* ---------- Bangumi ---------- */
/* The page body is one surface card: the filter row on top, then the wall. */
.bangumi-page {
  margin-bottom: 10px;
  padding: 20px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* The card sits on --surface, so the tonal blocks inside have to step up or
   they vanish into it (same fix as the Steam page). */
.dark .bangumi-page,
.dark .douban-page {
  --md-surface-container-low: var(--surface-2);
  --md-surface-container-high: var(--surface-3);
}

.bangumi-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  margin: 0 0 16px;
  padding: 0 0 14px;
  border-bottom: 1px solid var(--divider);
}

.bangumi-filters__sep {
  width: 1px;
  height: 18px;
  background: var(--border);
}

/* View switch shared by plugin pages (bangumi, douban): every option is a
   button and the thumb slides to the active one, so it needs no wrapper
   markup per option count — `--view-index` is written by the page script.
   Sized to the 32px chip height (2px track padding + 28px targets). */
.view-switch {
  position: relative;
  display: inline-flex;
  flex: none;
  padding: 2px;
  border-radius: var(--md-radius-full);
  background: var(--md-surface-container-high);
}

/* The active segment is a filled choice — solid accent, white glyph — so the
   current view reads at a glance instead of as one more tint. */
.view-switch__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--md-primary);
  transform: translateX(calc(var(--view-index, 0) * 28px));
  transition: transform 0.25s var(--md-ease);
}

/* Restoring the saved view runs after the first style pass, so without this the
   thumb slides across the track on load; only the restore is flat, a click
   still glides. */
.view-switch.is-restoring .view-switch__thumb {
  transition: none;
}

.view-switch button {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--md-on-surface-variant);
  cursor: pointer;
  transition: color 0.15s var(--md-ease);
}

.view-switch button[aria-pressed="true"] {
  color: var(--md-on-primary);
}

@media (hover: hover) {
  .view-switch button:hover {
    color: var(--md-on-surface);
  }

  .view-switch button[aria-pressed="true"]:hover {
    color: var(--md-on-primary);
  }
}

.view-switch .icon {
  width: 16px;
  height: 16px;
}

.bangumi-layout {
  margin-left: auto;
}

/* Same wall as the douban page: the cover is the card, the score sits in a
   frosted chip in the corner and the title rests on a bottom scrim. */
.bangumi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 18px 16px;
}

.bangumi-card {
  position: relative;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--md-radius-lg);
  background: var(--md-surface-container-low);
}

.bangumi-card__cover {
  display: block;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  background: var(--md-surface-container-high);
  cursor: zoom-in;
}

.bangumi-card__cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (hover: hover) {
  .bangumi-card__cover:hover img {
    transform: scale(1.08);
  }
}

/* Corner rating chip, matching the douban wall. */
.bangumi-card__score {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  max-width: calc(100% - 16px);
  padding: 4px 8px;
  border-radius: 10px;
  background: rgba(10, 9, 12, 0.66);
  color: rgba(245, 241, 250, 0.78);
  font-size: 11px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

/* Only the overlay chip is width-capped; the list row sizes to its content. */
.bangumi-grid[data-layout="grid"] .bangumi-card__score {
  overflow: hidden;
}

.bangumi-card__score-value {
  flex: none;
}

/* Mirrors the douban chip: score, a middot, then the region. */
.bangumi-card__area {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bangumi-card__area::before {
  content: "·";
  margin: 0 4px;
}

@supports (backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px)) {
  .bangumi-card__score {
    background: rgba(14, 12, 18, 0.42);
    -webkit-backdrop-filter: blur(14px) saturate(1.5);
    backdrop-filter: blur(14px) saturate(1.5);
  }
}

/* Star strip inside the corner chip, same recipe as the douban wall: a 50px
   five-star track with the gold layer clipped to score x 10%. The percentage
   is written by the page script into `--bangumi-star-fill`. */
.bangumi-card__score.has-stars {
  padding-left: 68px;
}

/* 360px cards are only ~157px wide, where the 68px gutter would ellipsize the
   region; trim it a little so the whole chip still reads. */
@media (max-width: 480px) {
  .bangumi-card__score.has-stars {
    padding-left: 62px;
  }
}

.bangumi-card__score.has-stars::before,
.bangumi-card__score.has-stars::after {
  content: "";
  position: absolute;
  left: 8px;
  top: 50%;
  width: 50px;
  height: 10px;
  margin-top: -5px;
  -webkit-mask-image: var(--star-mask);
  mask-image: var(--star-mask);
  -webkit-mask-size: 10px 10px;
  mask-size: 10px 10px;
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
}

.bangumi-card__score.has-stars::before {
  background: rgba(245, 241, 250, 0.3);
}

.bangumi-card__score.has-stars::after {
  background: #f99b01;
  clip-path: inset(0 calc(100% - var(--bangumi-star-fill, 0%)) 0 0);
}

.bangumi-card__body {
  position: absolute;
  inset: auto 0 0;
  margin: 0;
  padding: 26px 10px 6px;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 12, 0) 0%,
    rgba(10, 9, 12, 0.45) 42%,
    rgba(10, 9, 12, 0.88) 100%
  );
  isolation: isolate;
  pointer-events: none;
}

/* Douban's wall deepens the scrim on hover so the revealed info line keeps its
   contrast; the same layer and transition are used here. */
.bangumi-card__body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* The list rows park the body in the second grid column, so it re-enables
     pointer events for its own links; this decorative layer would then inherit
     that and, having no positioned ancestor of its own in a row, stretch over
     the whole card and swallow the click meant for the cover. */
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 12, 0) 0%,
    rgba(10, 9, 12, 0.7) 52%,
    rgba(10, 9, 12, 0.94) 100%
  );
  opacity: 0;
  transition: opacity 0.3s var(--md-ease);
}

@media (hover: hover) {
  /* Grid only: the list rows are tonal plates, not posters, so the scrim that
     keeps the revealed line readable on a cover has nothing to protect there —
     it painted the whole row dark. Douban scopes its equivalent the same way. */
  .bangumi-grid[data-layout="grid"] .bangumi-card:hover .bangumi-card__body::before {
    opacity: 1;
  }
}

.bangumi-card__title {
  margin: 0;
  font-size: 13px;
  font-weight: 650;
  line-height: 1.5;
}

.bangumi-card__title a {
  display: -webkit-box;
  color: #f5f1fa;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  pointer-events: auto;
}

.bangumi-card__title a:hover {
  color: var(--accent);
}

/* Same reveal as the douban wall: the title rests on the poster edge and the
   info line is parked past the clipped bottom, so hover slides the block up.
   The page script writes the measured info-line height to `--bangumi-more-h`. */
.bangumi-card__text {
  transform: translateY(var(--bangumi-more-h, 22px));
  transition: transform 0.3s var(--md-ease);
}

/* The script writes the exact height a beat after the first paint; easing that
   correction would read as the info line sliding down on load, so the initial
   measurement and the font swap run without a transition. The layout swap parks
   the same block at a different transform, so it is committed the same way. */
.bangumi-grid[data-layout="grid"]:not(.is-measured) .bangumi-card__text,
.bangumi-card.is-measuring .bangumi-card__text,
.bangumi-grid.is-switching .bangumi-card__text {
  transition: none;
}

@media (hover: hover) {
  .bangumi-card:hover .bangumi-card__text {
    transform: translateY(0);
  }
}

.bangumi-card:has(.bangumi-card__title a:focus-visible) .bangumi-card__text {
  transform: translateY(0);
}

/* Touch screens have no hover, so the info line has to stay in place. */
@media (hover: none) {
  .bangumi-card__text {
    transform: none;
  }
}

.bangumi-card__meta {
  display: flex;
  flex-wrap: wrap;
  margin-top: 2px;
  color: rgba(245, 241, 250, 0.66);
  font-size: 11px;
  line-height: 1.5;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* The reveal parks the line exactly one bottom-padding above the edge, so that
   padding has to sit on the line itself; otherwise the glyph tops of the hidden
   line stay visible under the title at rest (the douban card pads its block the
   same way). The row and its meta keep the plain gap. */
.bangumi-grid[data-layout="grid"] .bangumi-card__meta {
  margin-top: 0;
  padding-top: 6px;
}

.bangumi-card__meta > span + span::before {
  content: "·";
  margin: 0 5px;
}

/* The wall only has room for the title and the info line; the intro and the
   play/follow counts live in the lightbox caption and in list mode. */
.bangumi-card__desc,
.bangumi-card__stats {
  display: none;
}

.bangumi-card__stats > span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.bangumi-card__stats .icon {
  width: 14px;
  height: 14px;
}

/* ---------- Bangumi list mode ----------
   Same row as the douban list: a 56px cover and the text block beside it. The
   score chip is taken out of the grid so it only costs the line it actually
   sits on; a third column would shorten every line of the row instead. */
.bangumi-grid[data-layout="list"] {
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
}

.bangumi-grid[data-layout="list"] .bangumi-card {
  position: relative;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  grid-template-areas: "cover body";
  align-items: start;
  column-gap: 14px;
  --bangumi-chip-gap: 14px;
  padding: 10px 12px;
  background: var(--md-surface-container-low);
  transition: background-color 0.15s var(--md-ease);
}

/* Same dead step as the douban list rows: --md-surface-container aliases
   --surface-2, which is what the resting row already paints. -highest is the
   next real tone up, and it matches the douban list rows one-for-one. */
@media (hover: hover) {
  .bangumi-grid[data-layout="list"] .bangumi-card:hover {
    background: var(--md-surface-container-highest);
  }
}

.bangumi-grid[data-layout="list"] .bangumi-card__cover {
  grid-area: cover;
  width: 56px;
  border-radius: var(--md-radius-xs);
}

.bangumi-grid[data-layout="list"] .bangumi-card__cover img {
  transform: none;
}

.bangumi-grid[data-layout="list"] .bangumi-card__body {
  position: static;
  grid-area: body;
  padding: 0;
  background: none;
  pointer-events: auto;
}

.bangumi-grid[data-layout="list"] .bangumi-card__text {
  transform: none;
  /* The info line and the play/follow counts read as one sentence, so they share
     a flex line: whitespace-only text nodes between the two <p> are dropped by
     flexbox, which makes column-gap the only spacing between them. The title
     keeps a line to itself through a full-width basis, and a row too narrow for
     both wraps the counts under the info line instead of clipping them. */
  display: flex;
  flex-wrap: wrap;
  /* Not baseline: the counts are inline-flex boxes that centre a 14px icon
     against their text, so they have no text baseline of their own and flexbox
     synthesises one from the bottom of the border box. Aligning on that parked
     the numbers 2.6px above the info line. Centring the two boxes instead, with
     the same line-height on both, puts their glyphs on one line. */
  align-items: center;
  column-gap: 12px;
}

/* The row chip sits on a tonal background instead of a poster, so the empty
   part of the star track has to darken with it. */
html:not(.dark) .bangumi-grid[data-layout="list"] .bangumi-card__score.has-stars::before {
  background: rgba(26, 28, 30, 0.22);
}

.bangumi-grid[data-layout="list"] .bangumi-card__title {
  flex: 1 0 100%;
  /* Reserve the chip on the title line only. Padding, not margin, because the
     h2 itself is not the link — the clickable box stays the anchor inside it. */
  padding-right: calc(var(--bangumi-score-w, 0px) + var(--bangumi-chip-gap, 14px));
  font-size: 15px;
}

.bangumi-grid[data-layout="list"] .bangumi-card__title a {
  color: var(--md-on-surface);
  text-shadow: none;
  /* Same dead strip as the douban rows: the -webkit-box fills the column, so
     the whitespace after the title was clickable. Fit the box to the text. */
  width: -moz-fit-content;
  width: fit-content;
  max-width: 100%;
}

/* Repeats the generic hover colour: the rest rule above is more specific than
   `.bangumi-card__title a:hover`, so without this the list title never leaves
   --md-on-surface under the pointer. */
.bangumi-grid[data-layout="list"] .bangumi-card__title a:hover {
  color: var(--accent);
}


.bangumi-grid[data-layout="list"] .bangumi-card__meta {
  margin-top: 2px;
  color: var(--muted);
  font-size: 12px;
  text-shadow: none;
}

.bangumi-grid[data-layout="list"] .bangumi-card__desc {
  display: -webkit-box;
  margin-top: 4px;
  color: var(--muted-strong);
  font-size: 12.5px;
  line-height: 1.55;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.bangumi-grid[data-layout="list"] .bangumi-card__stats {
  display: flex;
  gap: 12px;
  /* Same top offset as the meta, so centring the two items leaves no step
     between the info line and the counts. */
  margin-top: 2px;
  /* Match the meta's 12px x 1.5 line box; without this the counts inherit the
     card's 1.6 and the glyphs sit a couple of pixels above the info line. */
  line-height: 1.5;
  color: var(--muted);
  font-size: 12px;
}

.bangumi-grid[data-layout="list"] .bangumi-card__score {
  /* Out of the grid: the page script writes the measured chip width to
     --bangumi-score-w on the card and only the title line pays for it, so the
     info line and the intro reach the row's right padding. */
  position: absolute;
  top: 10px;
  right: 12px;
  grid-area: auto;
  max-width: none;
  padding: 4px 8px;
  background: rgba(40, 42, 47, 0.55);
  color: rgba(245, 241, 250, 0.78);
  text-shadow: none;
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  backdrop-filter: blur(14px) saturate(1.5);
}

/* The row already prints type · episodes · region under the title, so the
   chip keeps just the stars and the score. */
.bangumi-grid[data-layout="list"] .bangumi-card__area {
  display: none;
}

/* The row's padding shorthand above would otherwise wipe the star gutter. */
.bangumi-grid[data-layout="list"] .bangumi-card__score.has-stars {
  padding-left: 62px;
}

html:not(.dark) .bangumi-grid[data-layout="list"] .bangumi-card__score {
  background: rgba(255, 255, 255, 0.58);
  color: rgba(26, 28, 30, 0.7);
}

/* The empty state already sits inside the page card, so it drops its own. */
.bangumi-page .empty-state {
  padding: 24px 0 8px;
  border-radius: 0;
  background: none;
  box-shadow: none;
}


/* Bangumi posters open a caption with title, stats and the plugin intro. */
.lb-caption {
  max-width: min(680px, 88vw);
  margin: 0 auto;
  text-align: left;
}

.lb-caption__title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  color: #f2eef7;
  font-size: 15px;
  font-weight: 650;
  line-height: 1.5;
}

.lb-caption__score {
  flex: none;
  padding: 1px 8px;
  border-radius: var(--md-radius-full);
  background: rgba(208, 188, 255, 0.16);
  color: #d0bcff;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.lb-caption__meta {
  margin: 4px 0 0;
  color: rgba(230, 224, 233, 0.62);
  font-size: 12px;
}

.lb-caption__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin: 6px 0 0;
  color: rgba(230, 224, 233, 0.78);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.lb-caption__stats > span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.lb-caption__stats .icon {
  width: 13px;
  height: 13px;
  opacity: 0.8;
}

.lb-caption__desc {
  margin: 8px 0 0;
  max-height: 24vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  color: rgba(230, 224, 233, 0.78);
  font-size: 13px;
  line-height: 1.65;
  white-space: pre-line;
}

.lb-caption__actions {
  margin: 10px 0 0;
}

/* The preview only ever sits on the dark lightbox backdrop, so the pill keeps
   fixed light colours in both schemes and takes the configured accent on
   hover, exactly like the source links it replaces. */
.lb-caption__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: var(--md-radius-full);
  background: rgba(242, 238, 247, 0.14);
  color: #f2eef7;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  transition: background-color 0.15s var(--md-ease), color 0.15s var(--md-ease);
}

.lb-caption__link:hover {
  background: var(--accent);
  color: #fff;
}

.lb-caption__link .icon {
  width: 14px;
  height: 14px;
  flex: none;
}

/* ==========================================================================
   Steam plugin page
   ========================================================================== */

/* The whole page body sits on one surface card; the profile, the stat tiles
   and the heat map sit straight on it rather than on cards of their own. */
.steam-page {
  display: flex;
  flex-direction: column;
  gap: 26px;
  min-width: 0;
  margin-bottom: 10px;
  padding: 20px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* Keeps the segmented sort control readable against the page card. */
.dark .steam-page {
  --md-surface-container-low: var(--surface-2);
  --md-surface-container-high: var(--surface-3);
}

.steam-alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 14px 16px;
  border-radius: var(--md-radius-md);
  background: var(--md-error-container);
  color: var(--md-on-error-container);
  font-size: 13.5px;
  line-height: 1.6;
}

.steam-alert .icon {
  width: 20px;
  height: 20px;
  margin-top: 1px;
}

.steam-profile {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  min-width: 0;
  --steam-online: #4dd0e1;
  --steam-playing: #7dd8c8;
}

html:not(.dark) .steam-profile {
  --steam-online: #006874;
  --steam-playing: #006a60;
}

.steam-profile__avatar-wrap {
  position: relative;
  flex: none;
  align-self: flex-start;
}

.steam-profile__avatar {
  display: block;
  width: 88px;
  height: 88px;
  border-radius: 24px;
  object-fit: cover;
  background: var(--md-surface-container-high);
}

.steam-profile__dot {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 16px;
  height: 16px;
  border: 3px solid var(--surface);
  border-radius: 50%;
  background: var(--md-outline);
}

.steam-profile__dot.is-online {
  background: #4dd0e1;
}

.steam-profile__dot.is-playing {
  background: #7dd8c8;
}

.steam-profile__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.steam-profile__lines {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.steam-profile__state {
  margin: 0;
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
}

.steam-profile__state.is-online {
  color: var(--steam-online);
}

.steam-profile__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.steam-profile__name {
  margin: 0;
  color: var(--md-on-surface);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}

.steam-button {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 16px;
  border-radius: var(--md-radius-full);
  background: var(--md-primary);
  color: var(--md-on-primary);
  font-size: 13px;
  font-weight: 600;
  transition: opacity 0.15s var(--md-ease);
}

.steam-button:hover {
  opacity: 0.9;
}

.steam-button .icon {
  width: 16px;
  height: 16px;
}

.steam-profile__status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin: 0;
  color: var(--md-on-surface-variant);
  font-size: 13px;
}

.steam-profile__status .is-online {
  color: var(--steam-online);
}

.steam-profile__status .is-playing {
  color: var(--steam-playing);
}

.steam-profile__last {
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
}

.steam-profile__meta {
  display: flex;
  flex: 0 0 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.steam-level {
  padding: 3px 10px;
  border-radius: var(--md-radius-full);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
  font-size: 12px;
  font-weight: 650;
}

.steam-meta-item {
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
}

.steam-meta-item strong {
  color: var(--md-on-surface);
  font-weight: 650;
}

.steam-profile__badges {
  display: flex;
  flex: 0 0 100%;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}

.steam-badge-chip {
  position: relative;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--md-outline-variant);
  border-radius: 8px;
  background: var(--md-surface-container);
  overflow: visible;
}

.steam-badge-chip img {
  width: 100%;
  height: 100%;
  border-radius: 7px;
  object-fit: cover;
}

.steam-badge-chip.has-image {
  border-color: transparent;
  background: transparent;
}

.steam-badge-chip.is-foil {
  border-color: #e0b64a;
  box-shadow: 0 0 0 1px rgb(224 182 74 / 0.25);
}

.steam-badge-chip__level {
  position: absolute;
  right: -4px;
  bottom: -4px;
  padding: 1px 4px;
  border-radius: 5px;
  background: var(--md-primary);
  color: var(--md-on-primary);
  font-size: 9px;
  font-weight: 700;
  line-height: 1.2;
}

.steam-badge-chip--more {
  border-style: dashed;
  color: var(--md-on-surface-variant);
  font-size: 11px;
  font-weight: 650;
}

.steam-badge-chip--more:hover {
  border-color: var(--md-primary);
  color: var(--md-primary);
}

.steam-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.steam-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: center;
  min-width: 0;
}

.steam-stat__value {
  color: var(--md-primary);
  font-size: 25px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}

.steam-stat__label {
  color: var(--md-on-surface-variant);
  font-size: 12px;
  font-weight: 500;
}

.steam-section {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.steam-section__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
}

.steam-section__title {
  margin: 0;
  color: var(--md-on-surface);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.steam-section__meta {
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
}

.steam-section__tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px 14px;
}

.steam-sort {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--md-radius-full);
  background: var(--md-surface-container-low);
}

.steam-sort__btn {
  height: 30px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--md-radius-full);
  background: transparent;
  color: var(--md-on-surface-variant);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s var(--md-ease), color 0.15s var(--md-ease);
}

.steam-sort__btn:hover {
  background: var(--md-surface-container-high);
  color: var(--md-on-surface);
}

.steam-sort__btn.is-active {
  background: var(--md-secondary-container);
  color: var(--md-on-secondary-container);
}

.steam-sort__btn:focus-visible {
  outline: 2px solid var(--md-primary);
  outline-offset: 2px;
}

.steam-grid.is-swapping {
  opacity: 0.4;
  pointer-events: none;
  transition: opacity 0.15s var(--md-ease);
}

.steam-heatmap {
  position: relative;
}

.steam-heatmap.is-loading {
  min-height: 132px;
}

.steam-heatmap__heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
  min-width: 0;
}

.steam-heatmap__legend {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--md-on-surface-variant);
  font-size: 11px;
  line-height: 1;
}

.steam-heatmap__legend-scale {
  display: inline-flex;
  gap: 3px;
}

.steam-heatmap__legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--md-surface-container-high);
}

.steam-heatmap__grid {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(7, 1fr);
  gap: 3px;
}

.steam-heatmap__cell {
  aspect-ratio: 1;
  min-width: 0;
  border-radius: 2px;
  background: var(--hm-empty, var(--md-surface-container-high));
}

.steam-heatmap__cell.is-l1 {
  background: var(--hm-1, #21528f);
}

.steam-heatmap__cell.is-l2 {
  background: var(--hm-2, #3172b8);
}

.steam-heatmap__cell.is-l3 {
  background: var(--hm-3, #5b9cdf);
}

.steam-heatmap__cell.is-l4 {
  background: var(--hm-4, #a8c7fa);
}

.steam-heatmap__cell[data-date]:hover {
  outline: 1.5px solid var(--md-on-surface);
  outline-offset: 1px;
}

.steam-heatmap__months {
  display: grid;
  gap: 3px;
  margin-top: 8px;
  color: var(--md-on-surface-variant);
  font-size: 11px;
  line-height: 1;
}

.steam-heatmap__month {
  overflow: hidden;
  white-space: nowrap;
}

.steam-heatmap__tooltip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1300;
  width: max-content;
  max-width: 260px;
  padding: 10px 12px;
  border-radius: var(--md-radius-sm);
  background: var(--md-surface-container-highest);
  color: var(--md-on-surface);
  box-shadow: var(--md-shadow-2);
  font-size: 12px;
  line-height: 1.5;
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.12s var(--md-ease), transform 0.12s var(--md-ease);
  pointer-events: none;
}

.steam-heatmap__tooltip.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* Same material as the archives heatmap popovers: the tooltip's rest colour is
   already --surface-3, so glass keeps that hue and only lets the page show
   through, riding the single global switch with --pop-veil (a popover parks on
   static card text, where a thin veil would read as ghosting). */
html[data-glass="on"] .steam-heatmap__tooltip {
  -webkit-backdrop-filter: blur(var(--nav-blur)) saturate(115%);
  backdrop-filter: blur(var(--nav-blur)) saturate(115%);
  background: rgba(var(--surface-3-rgb), var(--pop-veil));
}

.steam-heatmap__tt-date {
  font-size: 12.5px;
  font-weight: 700;
}

.steam-heatmap__tt-total {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  margin-top: 2px;
  color: var(--md-on-surface-variant);
}

.steam-heatmap__tt-total strong {
  color: var(--md-primary);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.steam-heatmap__tt-games {
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-height: 148px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--md-outline-variant);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--md-outline-variant) transparent;
}

.steam-heatmap__tt-games::-webkit-scrollbar {
  width: 4px;
}

.steam-heatmap__tt-games::-webkit-scrollbar-thumb {
  border-radius: 2px;
  background: var(--md-outline-variant);
}

.steam-heatmap__tt-game {
  display: flex;
  justify-content: space-between;
  gap: 18px;
}

.steam-heatmap__tt-game > span:first-child {
  max-width: 168px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.steam-heatmap__tt-time {
  flex: none;
  color: var(--md-on-surface-variant);
  font-variant-numeric: tabular-nums;
}

.steam-heatmap__tt-empty {
  margin-top: 6px;
  color: var(--md-on-surface-variant);
}

.steam-heatmap__state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 24px;
  background: var(--md-surface-container-low);
  color: var(--md-on-surface-variant);
  text-align: center;
  font-size: 12.5px;
}

.steam-heatmap__state strong {
  color: var(--md-on-surface);
  font-size: 15px;
}

.steam-heatmap__icon {
  font-size: 32px;
  line-height: 1;
}

.steam-spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--md-outline-variant);
  border-top-color: var(--md-primary);
  border-radius: 50%;
  animation: steam-spin 0.9s linear infinite;
}

@keyframes steam-spin {
  to {
    transform: rotate(360deg);
  }
}

.steam-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px 14px;
}

.steam-grid--recent {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.steam-game {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.steam-game__media {
  position: relative;
  border-radius: var(--md-radius-md);
  background: var(--md-surface-container-low);
  overflow: hidden;
}

.steam-game__cover {
  display: block;
  aspect-ratio: 460 / 215;
  overflow: hidden;
  cursor: zoom-in;
}

.steam-game__cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.35s var(--md-ease);
}

@media (hover: hover) {
  .steam-game__cover:hover img {
    transform: scale(1.08);
  }
}

.steam-game__cover--empty {
  display: grid;
  place-items: center;
  color: var(--md-on-surface-variant);
  font-size: 26px;
  cursor: default;
}

.steam-game__flag {
  position: absolute;
  top: 6px;
  padding: 2px 7px;
  border-radius: var(--md-radius-full);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.4;
  pointer-events: none;
}

.steam-game__flag--delisted {
  left: 6px;
  background: rgba(0, 0, 0, 0.66);
  color: #f1f1f1;
}

.steam-game__flag--library {
  right: 6px;
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}

.steam-game__body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  padding: 0 2px;
}

.steam-game__name {
  display: block;
  color: var(--md-on-surface);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

a.steam-game__name:hover {
  color: var(--md-primary);
}

.steam-game__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
  color: var(--md-on-surface-variant);
  font-size: 12px;
}

.steam-game__time {
  color: var(--md-primary);
  font-weight: 600;
}

.steam-game__last,
.steam-game__achievement {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 768px) {
  .steam-page {
    gap: 18px;
    margin-bottom: 8px;
    padding: 14px 12px 8px;
    border-radius: 0;
    box-shadow: none;
  }

  .steam-profile {
    gap: 10px 14px;
  }

  .steam-profile__avatar {
    width: 72px;
    height: 72px;
    border-radius: 20px;
  }

  .steam-profile__head {
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
  }

  .steam-profile__name {
    font-size: 20px;
  }

  .steam-profile__lines {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
  }

  .steam-grid,
  .steam-grid--recent {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px 12px;
  }

  .steam-heatmap.is-loading {
    min-height: 104px;
  }

  .steam-heatmap__grid,
  .steam-heatmap__months {
    gap: 2px;
  }

  .steam-heatmap__months {
    margin-top: 6px;
    font-size: 10px;
  }
}

@media (max-width: 640px) {
  .steam-stats {
    grid-template-columns: 1fr;
    gap: 8px;
  }

  .steam-stat {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    text-align: left;
  }

  .steam-section__tools {
    justify-content: flex-start;
  }
}

@media (max-width: 480px) {
  .steam-stat__value {
    font-size: 22px;
  }

  .steam-profile__avatar {
    width: 60px;
    height: 60px;
    border-radius: 18px;
  }

  .steam-profile__name {
    font-size: 18px;
  }

  .steam-button {
    width: 36px;
    padding: 0;
    justify-content: center;
  }

  .steam-button span {
    display: none;
  }

  .steam-badge-chip {
    width: 28px;
    height: 28px;
    border-radius: 7px;
  }

  .steam-heatmap__grid,
  .steam-heatmap__months {
    gap: 1.5px;
  }

  .steam-heatmap__months {
    font-size: 9px;
  }

  .steam-game__name {
    font-size: 12.5px;
  }
}

/* ==========================================================================
   Douban plugin page
   ========================================================================== */

.douban-page {
  --db-main-color: var(--md-primary);
  --db-hover-color: var(--md-primary);
  --db--text-color: var(--md-on-surface);
  --db--text-color-light: var(--md-on-surface-variant);
  --db--background-gray: var(--md-surface-container-high);
  --db-border-color: var(--md-outline-variant);
  margin-bottom: 10px;
  padding: 20px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.douban-page .db--container {
  padding-top: 4px;
}

.douban-page .db--nav {
  gap: 8px;
  padding: 0 0 18px;
}

/* The three filter rows are plugin-owned, so they are re-skinned to the theme
   chip: `.chip` (32px) for type and status, `.chip--small` (27px) for genres.
   None of them sets a line-height, for the reason spelled out on `.chip`. */
.douban-page .db--navItem {
  height: 32px;
  margin: 0;
  padding: 0 13px;
  border: 0;
  border-radius: var(--md-radius-full);
  background: var(--md-surface-container-high);
  color: var(--md-on-surface-variant);
  font-size: 13px;
  font-weight: 500;
  text-transform: none;
  transition: background-color 0.15s var(--md-ease), color 0.15s var(--md-ease);
}

.douban-page .db--navItem.current {
  background: var(--accent);
  color: var(--accent-on);
}

@media (hover: hover) {
  .douban-page .db--navItem:hover {
    border-color: transparent;
    background: var(--surface-3-hover);
    color: var(--md-on-surface);
  }

  .douban-page .db--navItem.current:hover {
    background: var(--accent-hover);
    color: var(--accent-on);
  }
}

.douban-page .db--type {
  gap: 8px;
  margin-bottom: 18px;
}

.douban-page .db--typeItem {
  display: inline-flex;
  align-items: center;
  height: 32px;
  margin: 0;
  padding: 0 13px;
  border: 0;
  border-radius: var(--md-radius-full);
  background: var(--md-surface-container-high);
  color: var(--md-on-surface-variant);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s var(--md-ease), color 0.15s var(--md-ease);
}

.douban-page .db--typeItem.is-active {
  background: var(--accent);
  color: var(--accent-on);
  border-color: transparent;
  cursor: pointer;
}

@media (hover: hover) {
  .douban-page .db--typeItem:hover {
    background: var(--surface-3-hover);
    color: var(--md-on-surface);
  }

  .douban-page .db--typeItem.is-active:hover {
    background: var(--accent-hover);
    color: var(--accent-on);
  }
}

.douban-page .db--genres {
  gap: 8px;
  padding-bottom: 18px;
}

.douban-page .db--genreItem {
  display: inline-flex;
  align-items: center;
  height: 27px;
  margin: 0;
  padding: 0 10px;
  border-radius: var(--md-radius-full);
  background: var(--md-surface-container-high);
  color: var(--md-on-surface-variant);
  font-size: 12px;
  transition: background-color 0.15s var(--md-ease), color 0.15s var(--md-ease);
}

@media (hover: hover) {
  .douban-page .db--genreItem:hover {
    background: var(--surface-3-hover);
    color: var(--md-on-surface);
  }

  /* Extra class beats the .is-active rule below, so the selected pill keeps
     the accent family under the cursor instead of turning grey. */
  .douban-page .db--genreItem.is-active:hover {
    background: var(--accent-hover);
    color: var(--accent-on);
  }
}

.douban-page .db--genreItem.is-active {
  background: var(--accent);
  color: var(--accent-on);
}

.douban-page .db--genres.u-hide + .db--list {
  padding-top: 0;
}

.douban-layout {
  margin-left: auto;
}

/* The plugin renders one `.db--listBydate` per year-month; the view switch only
   changes whether those groups are dissolved (grid/list) or kept (timeline). */
.douban-page .db--list {
  display: block;
}

.douban-page .db--listBydate {
  position: relative;
  display: block;
  margin: 0 0 22px;
  padding: 0 0 0 26px;
  border: 0;
}

.douban-page .db--listBydate:last-child {
  margin-bottom: 0;
}

.douban-page[data-douban-layout="timeline"] .db--listBydate::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 12px;
  bottom: -22px;
  width: 2px;
  border-radius: 2px;
  background: var(--md-outline-variant);
}

.douban-page[data-douban-layout="timeline"] .db--listBydate:last-child::before {
  content: none;
}

.douban-page .db--titleDate {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: 4px;
  width: auto;
  margin: 0 0 12px;
}

.douban-page .db--titleDate::before {
  content: "";
  position: absolute;
  left: -26px;
  top: 50%;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--md-primary);
  box-shadow: 0 0 0 3px var(--surface);
}

.douban-page .db--titleDate__day {
  order: 1;
  color: var(--md-on-surface);
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
}

.douban-page .db--titleDate__day::after {
  content: "月";
  margin-left: 2px;
  font-size: 12px;
  font-weight: 600;
  color: var(--md-on-surface-variant);
}

.douban-page .db--titleDate__month,
.douban-page .db--titleDate__year {
  order: 0;
  color: var(--md-on-surface-variant);
  font-size: 13px;
  font-weight: 600;
}

.douban-page .db--titleDate__month::after {
  content: "年";
  margin-left: 1px;
  font-size: 12px;
}

/* Grid and timeline share one card recipe borrowed from the bangumi wall: the
   cover is the card, the community rating is a frosted chip in the corner and
   the title sits on a bottom scrim with my own rating and remark beneath it.
   Grid dissolves the date groups so every cover joins a single wall; timeline
   keeps the groups and the spine and only restyles the cards. */
.douban-page[data-douban-layout="grid"] .db--list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 18px 16px;
}

.douban-page[data-douban-layout="grid"] .db--listBydate,
.douban-page[data-douban-layout="grid"] .db--dateList__card {
  display: contents;
}

.douban-page[data-douban-layout="grid"] .db--titleDate {
  display: none;
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item {
  position: relative;
  overflow: hidden;
  border-radius: var(--md-radius-lg);
  background: var(--md-surface-container-low);
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item > .lightbox-link,
.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item > img.db--image {
  display: block;
  width: 100%;
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--image {
  border-radius: var(--md-radius-lg);
}

/* Corner rating chip on the poster. */
.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--score {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  max-width: calc(100% - 16px);
  margin: 0;
  padding: 4px 8px;
  border-radius: 10px;
  background: rgba(10, 9, 12, 0.66);
  color: rgba(245, 241, 250, 0.78);
  font-size: 11px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

@supports (backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px)) {
  .douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--score {
    background: rgba(14, 12, 18, 0.42);
    -webkit-backdrop-filter: blur(14px) saturate(1.5);
    backdrop-filter: blur(14px) saturate(1.5);
  }
}

/* The plugin prints a single 12px star glyph before the score. It is swapped
   for the bangumi five-star strip — 11px tiles, 30% track, gold layer clipped
   to score x 10% — with the percentage written by the page script into
   `--douban-star-fill`. The strip is placed out of flow so score and year keep
   one baseline, and the chip reserves its width with padding. */
.douban-page .db--score > svg {
  display: none;
}

.douban-page .db--score.has-stars {
  padding-left: 68px;
}

/* A zero score on an entry without a year leaves the chip with no text at all,
   so it has to hold the line box itself; otherwise the bare padding collapses
   to an 8px pill and the absolutely placed strip sticks out of it. */
.douban-page .db--score.has-stars:empty {
  min-height: calc(1.4em + 8px);
}

.douban-page .db--score.has-stars::before,
.douban-page .db--score.has-stars::after {
  content: "";
  position: absolute;
  left: 8px;
  top: 50%;
  width: 50px;
  height: 10px;
  margin-top: -5px;
  -webkit-mask-image: var(--star-mask);
  mask-image: var(--star-mask);
  -webkit-mask-size: 10px 10px;
  mask-size: 10px 10px;
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
}

.douban-page .db--score.has-stars::before {
  background: rgba(245, 241, 250, 0.3);
}

.douban-page .db--score.has-stars::after {
  background: #f99b01;
  clip-path: inset(0 calc(100% - var(--douban-star-fill, 0%)) 0 0);
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--title {
  position: absolute;
  inset: auto 0 0;
  margin: 0;
  padding: 26px 10px 6px;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 12, 0) 0%,
    rgba(10, 9, 12, 0.45) 42%,
    rgba(10, 9, 12, 0.88) 100%
  );
  isolation: isolate;
  pointer-events: none;
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--title::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 12, 0) 0%,
    rgba(10, 9, 12, 0.7) 52%,
    rgba(10, 9, 12, 0.94) 100%
  );
  opacity: 0;
  transition: opacity 0.3s var(--md-ease);
}

@media (hover: hover) {
  .douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item:hover .db--title::before {
    opacity: 1;
  }
}

.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--title a {
  display: -webkit-box;
  color: #f5f1fa;
  font-size: 13px;
  font-weight: 650;
  line-height: 1.5;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  pointer-events: auto;
}

/* The title hover colour is the shared `.db--title a:hover` further down — one
   accent for every layout, so it tracks the accent set in the Console. */

/* Same reveal as the bangumi card: the title rests on the poster edge with the
   info line parked past the clipped bottom, and hover slides the block up. The
   shift is the row's measured height, so its 6px top padding — equal to the
   overlay's bottom padding — is the only part left inside the card at rest.
   No will-change: a promoted block rasterises the text once and then offsets
   the bitmap, which blurs the title on the resting sub-pixel translate. */
.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .douban-text {
  transform: translateY(var(--douban-mine-h, 0px));
  transition: transform 0.3s var(--md-ease);
}

@media (hover: hover) {
  .douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item:hover .douban-text {
    transform: translateY(0);
  }
}

/* Keyboard-only reveal: :focus-within would also fire for the lightbox trigger
   click and keep the line up after the cover closes. */
.douban-page:is([data-douban-layout="grid"], [data-douban-layout="timeline"]) .db--item:has(.db--title a:focus-visible) .douban-text {
  transform: translateY(0);
}

/* My own rating and remark, injected under the title. The stars reuse the gold
   strip at 9px so the line stays quiet next to the community chip; the remark
   gets its own line below them rather than sharing one. */
.douban-page .douban-mine {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  margin: 0;
  padding-top: 6px;
  color: rgba(245, 241, 250, 0.66);
  font-size: 11px;
  line-height: 1.45;
  pointer-events: none;
}

/* Labels, the TMDB subtitle and the watch date only fit on the list row; the
   grid keeps the compact stars-over-remark pair. `display: contents` lets the
   rating/time group flatten back into the column above the remark. */
.douban-page .douban-mine__row {
  display: contents;
}

.douban-page .douban-mine__label,
.douban-page .douban-mine__time,
.douban-page .douban-sub {
  display: none;
}

/* The stars need a box of their own: as a flex item they keep the 45x9 strip,
   while an inline parent in a `display: contents` row would collapse them. */
.douban-page .douban-mine__rating {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.douban-page .douban-mine__stars {
  position: relative;
  flex: none;
  width: 45px;
  height: 9px;
}

.douban-page .douban-mine__stars::before,
.douban-page .douban-mine__stars::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask-image: var(--star-mask);
  mask-image: var(--star-mask);
  -webkit-mask-size: 9px 9px;
  mask-size: 9px 9px;
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
}

.douban-page .douban-mine__stars::before {
  background: rgba(245, 241, 250, 0.28);
}

.douban-page .douban-mine__stars::after {
  background: #f99b01;
  clip-path: inset(0 calc(100% - var(--douban-star-fill, 0%)) 0 0);
}

.douban-page .douban-mine__remark {
  display: -webkit-box;
  align-self: stretch;
  min-width: 0;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* In list mode the title sits on the card surface, not on a scrim. */
.douban-page[data-douban-layout="list"] .douban-mine {
  color: var(--md-on-surface-variant);
}

.douban-page[data-douban-layout="list"] .douban-mine__stars::before {
  background: var(--md-outline-variant);
}

.douban-page[data-douban-layout="list"] .douban-mine__remark {
  font-size: 12px;
  line-height: 1.5;
  text-shadow: none;
}

/* List only: subtitle line, then 我的评分 / 观看时间 on one row, remark below. */
.douban-page[data-douban-layout="list"] .douban-sub {
  display: -webkit-box;
  margin-top: 2px;
  color: var(--md-on-surface-variant);
  font-size: 12px;
  line-height: 1.5;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.douban-page[data-douban-layout="list"] .douban-mine {
  gap: 2px;
  padding-top: 4px;
}

.douban-page[data-douban-layout="list"] .douban-mine__row {
  display: flex;
  align-self: stretch;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 16px;
}

.douban-page[data-douban-layout="list"] .douban-mine__label {
  display: inline;
  font-size: 12px;
}

.douban-page[data-douban-layout="list"] .douban-mine__rating {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.douban-page[data-douban-layout="list"] .douban-mine__time {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

.douban-page[data-douban-layout="list"] .douban-mine__date {
  color: var(--md-on-surface);
  font-variant-numeric: tabular-nums;
}

.douban-page[data-douban-layout="list"] .douban-mine__stars {
  width: 55px;
  height: 11px;
}

.douban-page[data-douban-layout="list"] .douban-mine__stars::before,
.douban-page[data-douban-layout="list"] .douban-mine__stars::after {
  -webkit-mask-size: 11px 11px;
  mask-size: 11px 11px;
}

/* Douban music covers are square; forcing 2:3 would crop the artwork.
   `data-douban-type` is written by the page script from the active nav chip. */
.douban-page[data-douban-type="music"] .db--image {
  aspect-ratio: 1 / 1;
}

.douban-page[data-douban-type="music"][data-douban-layout="list"] .db--image {
  height: 56px;
}

.douban-page[data-douban-layout="grid"] .db--empty {
  grid-column: 1 / -1;
}

/* List: groups dissolve into one row per entry, cover left, title right. */
.douban-page[data-douban-layout="list"] .db--list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
}

.douban-page[data-douban-layout="list"] .db--listBydate,
.douban-page[data-douban-layout="list"] .db--dateList__card {
  display: contents;
}

.douban-page[data-douban-layout="list"] .db--titleDate {
  display: none;
}

.douban-page[data-douban-layout="list"] .db--item {
  position: relative;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  grid-template-areas: "cover title";
  align-items: start;
  column-gap: 14px;
  padding: 10px 12px;
  border-radius: var(--md-radius-md);
  background: var(--md-surface-container-low);
  transition: background-color 0.15s var(--md-ease);
}

/* The upstream tonal step was a no-op in light mode: --md-surface-container-low
   and --md-surface-container both alias --surface-2, so hover painted the same
   grey as rest. --md-surface-container-highest is the one step that stays ahead
   of --surface-2 in both schemes, and it is what the filter chips above already
   hover to. The row background is the whole signal here — the title keeps its
   rest colour so the row does not light up twice. */
@media (hover: hover) {
  .douban-page[data-douban-layout="list"] .db--item:hover {
    background: var(--md-surface-container-highest);
  }
}

.douban-page[data-douban-layout="list"] .db--item > .lightbox-link,
.douban-page[data-douban-layout="list"] .db--item > img.db--image {
  grid-area: cover;
}

.douban-page[data-douban-layout="list"] .db--item > .lightbox-link {
  border-radius: var(--md-radius-xs);
  background: none;
}

.douban-page[data-douban-layout="list"] .db--image {
  width: 56px;
  height: 84px;
  border-radius: var(--md-radius-xs);
  background-size: 22px 22px;
}

.douban-page[data-douban-layout="list"] .db--title {
  grid-area: title;
  margin: 0;
  font-size: 15px;
  line-height: 1.45;
}

.douban-page[data-douban-layout="list"] .db--score {
  /* Out of the grid, like the bangumi row: the page script writes the measured
     chip width to --db-score-w on the item so only the title line reserves it
     and the year / genre lines reach the row's right padding. */
  position: absolute;
  top: 10px;
  right: 12px;
  grid-area: auto;
  margin: 0;
  padding: 4px 8px;
  border-radius: 10px;
  background: rgba(40, 42, 47, 0.55);
  color: rgba(245, 241, 250, 0.78);
  font-size: 11px;
  line-height: 1.4;
  text-shadow: none;
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  backdrop-filter: blur(14px) saturate(1.5);
}

/* The list chip floats on the card surface rather than on a poster, so it keeps
   the absolute corner position and still has to be the containing block for the
   star layers, which reserve their own width inside it. */
.douban-page[data-douban-layout="list"] .db--score.has-stars {
  position: absolute;
  padding-left: 68px;
  white-space: nowrap;
}

html:not(.dark) .douban-page[data-douban-layout="list"] .db--score {
  background: rgba(255, 255, 255, 0.58);
  color: rgba(26, 28, 30, 0.7);
}

/* The list chip floats on the card surface rather than on a poster, so its
   star track has to be dark in the light scheme to stay visible. */
html:not(.dark) .douban-page[data-douban-layout="list"] .db--score.has-stars::before {
  background: rgba(26, 28, 30, 0.22);
}

.douban-page .db--dateList__card {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 18px 16px;
  width: 100%;
  min-width: 0;
}

.douban-page .db--item {
  width: auto;
  min-width: 0;
  margin: 0;
}

.douban-page .db--item > a.lightbox-link {
  display: block;
  overflow: hidden;
  border-radius: var(--md-radius-md);
  background: var(--md-surface-container-low);
  cursor: zoom-in;
}

/* A poster that failed to load has its href dropped by the page script, so the
   zoom affordance would be a lie. */
.douban-page .db--item > a.lightbox-link:not([href]) {
  cursor: default;
}

/* The poster box is a tonal tile with a picture glyph until the file arrives,
   so a slow — or blocked, douban answers 418 to hotlinks — poster never leaves
   a hole. The page script only assigns a source after it has verified the file,
   so a failing URL never reaches the element and the browser's broken-image
   glyph is never painted. */
.douban-page .db--image {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 3;
  border-radius: var(--md-radius-md);
  object-fit: cover;
  background-color: var(--md-surface-container-high);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-opacity='.3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.2' y='4.4' width='17.6' height='15.2' rx='3'/%3E%3Ccircle cx='9' cy='10' r='1.5'/%3E%3Cpath d='M4.6 17.2l4.1-4.1a1.6 1.6 0 0 1 2.3 0l3 3'/%3E%3Cpath d='M14.2 14.9l1.2-1.2a1.6 1.6 0 0 1 2.3 0l1.7 1.7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 32px 32px;
  transition: transform 0.35s var(--md-ease);
}

html:not(.dark) .douban-page .db--image {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a1c1e' stroke-opacity='.26' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.2' y='4.4' width='17.6' height='15.2' rx='3'/%3E%3Ccircle cx='9' cy='10' r='1.5'/%3E%3Cpath d='M4.6 17.2l4.1-4.1a1.6 1.6 0 0 1 2.3 0l3 3'/%3E%3Cpath d='M14.2 14.9l1.2-1.2a1.6 1.6 0 0 1 2.3 0l1.7 1.7'/%3E%3C/svg%3E");
}

@media (hover: hover) {
  .douban-page .db--item > a.lightbox-link:hover .db--image {
    transform: scale(1.08);
  }
}

.douban-page .db--score {
  gap: 4px;
  margin-top: 8px;
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
  line-height: 1.4;
}

/* Entries without a community score or year render an empty node; an empty
   pill reads as a glitch, so drop it — but a zero score is still a real score
   and keeps its empty star track. */
.douban-page .db--score:empty:not(.has-stars) {
  display: none;
}

.douban-page .db--title {
  margin-top: 3px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--md-on-surface);
}

.douban-page .db--title a {
  display: -webkit-box;
  color: var(--md-on-surface);
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.douban-page .db--title a:hover {
  color: var(--md-primary);
  text-decoration: none;
}

/* The title link is a -webkit-box, so at full column width its line box keeps
   the trailing whitespace after the last glyph and that dead strip is
   clickable. Shrink it to the text: fit-content still resolves to the column
   width for a title long enough to wrap, so the 2-line clamp is untouched. */
.douban-page[data-douban-layout="list"] .db--title a {
  width: -moz-fit-content;
  width: fit-content;
  max-width: 100%;
  /* The chip is absolutely positioned, so the title line reserves its width by
     hand. Margin rather than padding: padding would widen the clickable box
     past the last glyph, which is the dead strip this rule exists to remove.
     fit-content resolves against the containing block minus margins, so the
     clamp still breaks the line before the chip. */
  margin-right: calc(var(--db-score-w, 0px) + var(--db-chip-gap, 14px));
}

.douban-page .lds-ripple:before,
.douban-page .lds-ripple:after {
  border-color: var(--md-primary);
}

/* The plugin's bare `<div class="db--empty">` is given `.empty-state` by the
   page script, so it inherits the same plate as the bangumi page. The plugin's
   own 300px box with the broken-picture glyph underneath it has to go. */
.douban-page .db--empty {
  height: auto;
  margin-bottom: 0;
  background-image: none;
}

/* The plugin has no end-of-list state; the page script reveals this marker once
   its own response says the last page is in. */
.douban-end {
  display: none;
  justify-content: center;
  margin: 30px 0 6px;
  color: var(--md-on-surface-variant);
  font-size: 12.5px;
  letter-spacing: 0.04em;
}

.douban-end.is-visible {
  display: flex;
}

@media (max-width: 600px) {
  .douban-page .db--listBydate {
    margin-bottom: 18px;
    padding-left: 22px;
  }

  .douban-page[data-douban-layout="timeline"] .db--listBydate::before {
    left: 4px;
    top: 10px;
    bottom: -18px;
  }

  .douban-page[data-douban-layout="timeline"] .db--titleDate::before {
    left: -22px;
    width: 10px;
    height: 10px;
    margin-top: -5px;
  }

  .douban-page .db--titleDate__day {
    font-size: 17px;
  }

  .douban-page[data-douban-layout="grid"] .db--list,
  .douban-page[data-douban-layout="timeline"] .db--dateList__card {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px 12px;
  }

  .douban-page[data-douban-layout="list"] .db--item {
    grid-template-columns: 48px minmax(0, 1fr);
    column-gap: 12px;
    padding: 8px 10px;
    --db-chip-gap: 12px;
  }

  /* The chip is absolutely positioned, so it has to follow the smaller padding
     by hand; the title reserve shrinks with --db-chip-gap above. */
  .douban-page[data-douban-layout="list"] .db--score,
  .douban-page[data-douban-layout="list"] .db--score.has-stars {
    top: 8px;
    right: 10px;
  }

  .douban-page[data-douban-layout="list"] .db--image {
    width: 48px;
    height: 72px;
  }
}


/* ---------- Jelly pointer follow ---------- */

/* main.js writes pointer offsets into the standalone translate property, which
   composes with the hover scale each card already declares. The eased overshoot
   makes the picture trail the cursor and settle back like jelly. */
@media (hover: hover) and (pointer: fine) {
  .prose .lightbox-link img,
  .prose .lightbox-video video,
  .media-tile img,
  .media-tile video,
  .photo-card__media img,
  .photo-detail__figure img,
  .neighbor-thumb img,
  .equipment-card__cover img,
  .bangumi-card__cover img,
  .steam-game__cover img,
  .douban-page .db--image {
    translate: 0 0;
    transition: transform 0.45s var(--md-ease),
      translate 0.35s cubic-bezier(0.2, 1.35, 0.3, 1);
  }

  .photo-detail__figure > a:hover img,
  .neighbor-thumb:hover img {
    transform: scale(1.08);
  }
}
/* ---------- 订阅动态（链接插件 RSS 公开动态） ---------- */

.feed-intro {
  margin-bottom: 16px;
}

.filter-card .chips.chips--sub {
  margin-top: 12px;
}

.feed-timeline {
  padding: 8px 16px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.feed-timeline .feed-items {
  margin: 0;
}

.feed-timeline .feed-item:first-child {
  border-top: 0;
  padding-top: 4px;
}

.feed-item__source {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
}

.feed-item__avatar {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}

.feed-item__source:hover {
  color: var(--accent);
}

.feed-items {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.feed-item {
  padding: 10px 0;
  border-top: 1px solid var(--border);
}

.feed-item__title {
  display: block;
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
}

.feed-item__title:hover {
  color: var(--accent);
}

/* 瞬间条目选「直接显示内容」时，正文占掉标题那一行，需要像摘要一样截断，
   否则长瞬间会把整条动态撑成一段文章。 */
.feed-item__title--moment {
  display: -webkit-box;
  overflow: hidden;
  font-weight: 400;
  line-height: 1.6;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

.feed-item__summary {
  display: -webkit-box;
  overflow: hidden;
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.6;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.feed-item__meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  color: var(--muted);
  font-size: 12px;
}

.side-feeds {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.side-feeds__link {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.side-feeds__title {
  display: -webkit-box;
  overflow: hidden;
  color: var(--text);
  font-size: 13px;
  line-height: 1.5;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.side-feeds__link:hover .side-feeds__title {
  color: var(--accent);
}

.side-feeds__meta {
  display: flex;
  gap: 8px;
  color: var(--muted);
  font-size: 12px;
}

.feed-more {
  display: block;
  width: 100%;
  margin-top: 2px;
  padding: 9px 0;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 13px;
}

.feed-more:hover {
  color: var(--accent);
}

.feed-more:disabled {
  color: var(--muted);
  cursor: default;
}

/* ---------- Bing 每日一图（plugin-bing-wallpaper） ---------- */

.bw-today {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  margin-bottom: 10px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.bw-today__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
}

.bw-today__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.bw-today__side {
  position: relative;
  min-width: 0;
  min-height: 0;
}

/* 右栏绝对定位贴合图片高度：描述再多也不把卡片撑得比图高。 */
.bw-today__body {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px;
  min-width: 0;
  overflow: hidden;
}

.bw-today__date {
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
}

.bw-today__title {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.4;
}

.bw-today__subtitle {
  color: var(--muted-strong);
  font-size: 14px;
}

.bw-today__desc {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.7;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 0 1 auto;
  min-height: 0;
}

.bw-today__copyright {
  margin-top: auto;
  color: var(--muted);
  font-size: 12px;
}

.bw-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
}

.bw-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  /* 不裁剪：下载菜单需溢出卡片显示；图片缩放/视差由 media 自身裁剪。 */
  overflow: visible;
}

.bw-card__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
  overflow: hidden;
  border-radius: var(--radius) var(--radius) 0 0;
}

.bw-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.bw-card__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  padding: 12px 14px 8px;
  min-width: 0;
}

.bw-card__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bw-card__subtitle {
  color: var(--muted-strong);
  font-size: 13px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.bw-card__desc {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.bw-card__meta {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  color: var(--muted);
  font-size: 12px;
}

.bw-card__meta span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bw-card__actions {
  display: flex;
  gap: 6px;
  padding: 0 14px 12px;
}

.bw-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted-strong);
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.bw-btn:hover {
  background: var(--surface-3-hover);
  color: var(--accent);
}

.bw-btn .icon {
  font-size: 14px;
}

.bw-dl {
  position: relative;
}

.bw-dl summary {
  list-style: none;
}

.bw-dl summary::-webkit-details-marker {
  display: none;
}

.bw-dl__menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 168px;
  max-height: 264px;
  overflow-y: auto;
  padding: 6px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  scrollbar-width: thin;
  scrollbar-color: var(--surface-3-hover) transparent;
}

.bw-dl__menu a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 6px;
  color: var(--text);
  font-size: 12px;
}

.bw-dl__menu a:hover {
  background: var(--surface-3-hover);
  color: var(--accent);
}

.bw-dl__menu .icon {
  font-size: 12px;
  color: var(--muted);
}

/* 下载菜单磨砂玻璃跟随全局设置。 */
html[data-glass="on"] .bw-dl__menu {
  -webkit-backdrop-filter: blur(var(--nav-blur)) saturate(115%);
  backdrop-filter: blur(var(--nav-blur)) saturate(115%);
  background: rgba(var(--surface-rgb), var(--nav-veil-panel));
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html[data-glass="on"] .bw-dl__menu {
    background: var(--surface);
  }
}

/* 今日特写内的操作行与标题/简介/版权左对齐（去掉网格卡的额外内边距）。 */
.bw-today__body .bw-card__actions {
  padding: 0;
  margin-top: 4px;
}

@media (max-width: 768px) {
  .bw-today {
    grid-template-columns: 1fr;
    border-radius: 0;
    box-shadow: none;
  }
  .bw-today__side {
    position: static;
  }
  .bw-today__body {
    position: static;
    inset: auto;
    overflow: visible;
  }
  .bw-grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 8px;
  }
  .bw-card {
    border-radius: 0;
    box-shadow: none;
  }
  .bw-card__media {
    border-radius: 0;
  }
  /* 窄屏单列时菜单改左对齐弹出，避免 right:0 把菜单推出左边界。 */
  .bw-dl__menu {
    left: 0;
    right: auto;
  }
}
