/* =============================================================================
   aiduBOX — core stylesheet
   -----------------------------------------------------------------------------
   Palette inherited from hycoForce Tokyo: white paper + deep blue + gray + black.
   All tunable tokens (colours, widths, easing, fonts) live in :root below — edit
   there first; the rest of the file references the variables.

   Layout map (top → bottom in this file):
     :root / reset / .shell        design tokens + shared helpers
     .hex-bg / .scroll-track       fixed hex backdrop + scroll spacer
     .stage-back / .reveal         the fixed stage (dudu left, reveal right)
     .header-pills                 tagline row inside the hero content
     .dudu / .palm                 the artwork on the left
     .hero-* / .cards / .card      headline + the six nav cards
     .card-pop / .pop-*            the per-card popup and its content blocks
     .site-foot                    fixed footer (timestamp + copyright)
     .slogan                       the layer revealed behind the cards on scroll
     @media (max-width: 1024px)    MOBILE/TABLET: one screen, swipe card curtain
     @media (hover: none) / reduce touch + accessibility guards

   NOTE: bump the ?v= query on this file's <link> in index.html after editing,
   so browsers and the CDN drop the cached version.
   ============================================================================= */

:root {
  --paper: #ffffff;
  --ink: #000000;
  --gray: #525252;
  --blue: #1f4e79;
  --purple: #7030a0;

  --gray-soft: #7c7c7c;
  --gray-line: rgba(82, 82, 82, 0.16);
  --blue-tint: rgba(31, 78, 121, 0.08);
  --blue-line: rgba(31, 78, 121, 0.28);
  --purple-tint: rgba(112, 48, 160, 0.08);
  --purple-line: rgba(112, 48, 160, 0.35);

  --shell: 1240px;
  --gutter: clamp(20px, 4vw, 48px);
  --radius: 14px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  /* Latin glyphs resolve to the mono faces (same as the card labels), then
     fall through to the CJK faces for Chinese — per-glyph fallback means one
     stack covers both scripts. */
  --font: ui-monospace, SFMono-Regular, "SF Mono", Menlo, "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", monospace;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  /* keep the scroll container on the root: putting overflow-x on body
     turns body into its own scroller and breaks the scroll-driven wipe */
  overflow-x: hidden;
}

body {
  background: var(--paper);
  color: var(--gray);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

/* shared content width: header, stage and footer all align to this */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ---------- hexagon background (ported from hycoForce Tokyo) ---------- */
.hex-bg {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

.hex-bg-item {
  position: absolute;
  opacity: 0.3;
}

/* ---------- scroll spacer (drives the wipe, holds no content) ---------- */
.scroll-track {
  height: 200vh;
  pointer-events: none;
}

.stage-back {
  position: fixed;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* fixed stage layout: dudu on the left, reveal column on the right.
   height:100% + border-box padding gives the flex line an explicit height
   equal to the band between the fixed header and footer, which is what lets
   the children cap themselves with max-height instead of overflowing. */
.stage-back__content {
  width: 100%;
  height: 100%;
  max-width: var(--shell);
  padding: 48px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(20px, 3.5vw, 52px);
}

.stage-back .hex-bg {
  position: absolute;
}

/* ---------- reveal column: two stacked layers, mask-wiped ---------- */
.reveal {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 625px;
  max-height: 100%;
  display: grid;
  /* the single auto row must NOT balloon up to the min-content of its
     children (that's what sent the hero 100px past the dudu frame); keep it
     locked to the JS-pinned height so the trio can distribute inside it. */
  grid-template-rows: minmax(0, 1fr);
}

/* both layers occupy the same grid cell so they overlap exactly, completely static */
.reveal__fore,
.reveal__back {
  grid-area: 1 / 1;
  min-height: 0;
  will-change: -webkit-mask-image, mask-image;
}

.reveal__fore {
  pointer-events: auto;
}

.reveal__back {
  pointer-events: none;
}

/* For the handover to feel matched, both layers must span the SAME vertical
   range: if one layer's content stops short of an edge, the seam finishes
   with it early and the other keeps going alone. fore runs 0%→100%
   (eyebrow at the top, cards to the bottom), so the slogan fills the column
   too — Chinese block pinned to the top, English block to the bottom. */
.reveal__back .slogan {
  height: 100%;
}

/* ---------- lower-right brand dock, directly above the footer ---------- */
.brand-dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 38px;
  z-index: 20;
  padding: 6px 0;
  pointer-events: none;
}

.brand-dock .shell {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

.brand-dock .brand {
  pointer-events: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

@media (min-width: 1025px) {
  .brand-dock .brand {
    width: var(--brand-target-w, auto);
  }

  .brand__text {
    white-space: nowrap;
  }
}

/* only the logo carries the mailto; keep it a plain inline box so the image
   below it keeps its own sizing */
.brand__link {
  display: inline-flex;
  align-items: center;
  line-height: 0;
}

.brand__mark {
  width: 118px;
  height: 118px;
  object-fit: contain;
  animation: monkey-play 7s infinite ease-in-out;
  transform-origin: center bottom;
}

@keyframes monkey-play {
  /* 0% ~ 40%: 原地乖巧 */
  0%, 40%, 100% {
    transform: translateY(0) rotate(0deg) scale(1);
  }
  /* 42% ~ 58%: 原地小幅快速抖动（欢快摇头晃脑抖屁股） */
  42% { transform: rotate(-3deg) scale(1.02); }
  45% { transform: rotate(3deg) scale(1.02); }
  48% { transform: rotate(-4deg) scale(1.03); }
  51% { transform: rotate(4deg) scale(1.03); }
  54% { transform: rotate(-2deg) scale(1.01); }
  57% { transform: rotate(0deg) scale(1); }

  /* 58% ~ 75%: 抖完稍停下 */

  /* 78% ~ 94%: 原地小跳跃组合 */
  78% {
    transform: translateY(-5px) rotate(-5deg) scale(1.04);
  }
  82% {
    transform: translateY(-8px) rotate(5deg) scale(1.06);
  }
  86% {
    transform: translateY(-2px) rotate(-2deg) scale(1.02);
  }
  90% {
    transform: translateY(-5px) rotate(3deg) scale(1.04);
  }
  94% {
    transform: translateY(0) rotate(0deg) scale(1);
  }
}

/* aiduBOX: lowercase gray, uppercase deep blue */
.brand__text {
  font-size: var(--brand-fit-fs, 3.74rem);
  letter-spacing: 0.1em;
  font-weight: 800;
  color: var(--gray);
}

.brand__text b {
  color: var(--blue);
  font-weight: 800;
}

.brand__text span {
  font-weight: 800;
  color: var(--gray);
}

/* the ⚕ between the Latin and Chinese halves of the wordmark. It is its own
   element so the mobile layout can move it to the front of the Chinese half,
   where the two halves sit on opposite ends of the row. */
.brand__sep {
  font-style: normal;
  font-weight: 400;
  color: var(--blue);
  /* the glyph carries its own side bearings, so no margin is needed to keep
     "aiduPARK⚕爱嘟乐园" reading as one tight string */
  letter-spacing: 0;
}

.brand__text span b {
  color: var(--blue);
  font-weight: 800;
}

/* tagline pills: first row of the hero, always one line */
.header-pills {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: clamp(5px, 0.7vw, 10px);
  width: 100%;
  min-width: 0;
  margin: 0;
  white-space: nowrap;
}

.header-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  font-size: clamp(0.58rem, 0.85vw, 0.76rem);
  letter-spacing: clamp(0.05em, 0.1vw, 0.12em);
  color: var(--blue);
  border: 1px solid var(--blue-line);
  border-radius: 999px;
  padding: 5px clamp(8px, 0.9vw, 13px);
  background: var(--blue-tint);
  font-weight: 500;
}

.header-pill::before {
  content: "";
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--blue);
}

/* left: dudu holding monkey (fixed stage).
   aspect-ratio matches the artwork, so max-height can shrink the box and the
   image follows without letterboxing. That matters because the monkey is
   positioned in % of this box — box and image must stay the same shape or the
   monkey drifts off her palm. */
.dudu {
  position: relative;
  flex: 0 1 auto;
  width: min(42vw, 490px);
  max-height: 100%;
  aspect-ratio: 1047 / 1297;
}

/* <picture> is an inline box by default; the art and the marks are sized by
   their containers, so the wrapper has to stop being a text-flow element or the
   images below lose their box. */
.dudu > picture,
.palm > picture {
  display: block;
  width: 100%;
  height: 100%;
}

.brand picture,
.slogan > picture {
  display: contents;
}

.dudu__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* hermes holding on the palm with soft aura glow */
.palm {
  position: absolute;
  left: 77.5%;
  top: 58.5%;
  width: 26%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  perspective: 900px;
}

.palm__img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-style: preserve-3d;
  animation: hover 5s ease-in-out infinite alternate;
}

@keyframes hover {
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(-6%);
  }
}

/* ---------- hero content (inside the wiped foreground) ---------- */
.hero-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  min-height: 0;
  gap: clamp(10.4px, 1.495vh, 15.6px);
}

.hero-title {
  /* 4vh cap keeps both lines on two rows even at wide+short viewports;
     at 4.2vh+ "一个专注HERMES AGENT的中文社区" wraps a third line and the
     title alone eats the whole dudu frame. */
  font-size: clamp(1.4rem, min(3.2vw, 4vh), 2.9rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 650;
  color: var(--ink);
}

.hero-title span {
  color: var(--blue);
}

.hero-desc {
  max-width: 58ch;
  color: var(--gray);
  font-size: clamp(1.005rem, calc(1vw + 2px), 1.105rem);
  line-height: 1.55;
}

/* global aiduBOX brand styling rule: aidu (gray bold) + BOX (deep blue bold) */
.aidubox-mark {
  font-weight: 800;
  color: var(--gray);
  font-style: normal;
}

.aidubox-mark span {
  color: var(--blue);
  font-weight: 800;
}

/* card grid: 6 nav cards */
.cards {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(2, clamp(83px, 13.8vh, 136px));
  gap: clamp(8px, 1.2vh, 12px);
}

  .card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  min-height: clamp(83px, 13.8vh, 136px);
  padding: clamp(10px, 1.2vh, 14px) clamp(10px, 1.1vw, 14px);
  border: 1px solid var(--gray-line);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.82);
  overflow: hidden;
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease),
    box-shadow 0.4s var(--ease);
}

.card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    300px circle at var(--mx, 50%) var(--my, 0%),
    rgba(31, 78, 121, 0.09),
    transparent 62%
  );
  opacity: 0;
  transition: opacity 0.4s var(--ease);
}

.card:hover {
  transform: translateY(-4px);
  border-color: var(--blue-line);
  box-shadow: 0 14px 30px rgba(31, 78, 121, 0.1);
}

.card:hover::after {
  opacity: 1;
}

.card__top {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.card__en {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  color: rgba(82, 82, 82, 0.45);
  white-space: nowrap;
}

.card__icon {
  width: 24px;
  height: 24px;
  stroke: var(--blue);
  stroke-width: 1.4;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.45s var(--ease);
}

.card:hover .card__icon {
  transform: translateY(-2px) scale(1.06);
}

.card__body {
  position: relative;
  z-index: 1;
}

.card__title {
  display: block;
  font-size: clamp(1.05rem, 1.22vw, 1.25rem);
  font-weight: 800;
  color: var(--ink);
  margin-bottom: 0.8em;
  transform: translateY(0.25lh);
}

.card__desc {
  display: block;
  font-size: clamp(0.78rem, 0.88vw, 0.92rem);
  font-weight: 400;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--blue);
}

.card__line {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, var(--blue), transparent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease);
}

.card:hover .card__line {
  transform: scaleX(1);
}

/* ---------- card popup ----------
   Covers the whole right-hand column next to dudu (the same area as the
   reveal it lives in) — headline, cards and dashboard all sit behind it.
   Background matches the homepage: white paper + the same random three-colour
   hexagons. Header shows the icon then the four-character title; footer
   carries the subtitle on the left and the English title on the right. */
.card-pop {
  /* Fixed, not absolute: the popup is a body-level layer (it used to live in
     #reveal, which mobile hides, leaving the popup unrenderable). On desktop
     syncPopFrame() pins it to the reveal column's rect; on mobile the media
     query stretches it to the full screen. */
  position: fixed;
  left: var(--pop-l, 0px);
  top: var(--pop-t, 0px);
  width: var(--pop-w, 100%);
  height: var(--pop-h, 100%);
  z-index: 20;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--paper);
  border: 1px solid var(--gray-line);
  box-shadow: 0 22px 60px rgba(31, 78, 121, 0.2);
  display: flex;
  flex-direction: column;
  animation: card-pop 0.28s var(--ease);
}

.card-pop[hidden] {
  display: none;
}

@keyframes card-pop {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.card-pop__hex {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

.card-pop__head,
.card-pop__bd,
.card-pop__ft {
  position: relative;
  z-index: 1;
}

.card-pop__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 44px 6px 16px;
}

.card-pop__icon {
  width: 26px;
  height: 26px;
  stroke: var(--blue);
  stroke-width: 1.6;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.card-pop__title {
  font-size: clamp(1.15rem, 1.3vw, 1.4rem);
  font-weight: 800;
  color: var(--ink);
}

.card-pop__close {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 2;
  width: auto;
  height: auto;
  background: none;
  border: none;
  border-radius: 0;
  color: var(--gray);
  font-family: var(--font);
  font-size: 1.35rem;
  line-height: 1;
  padding: 4px;
  cursor: pointer;
  transition: color 0.3s var(--ease), transform 0.3s var(--ease);
}

.card-pop__close:hover {
  color: var(--blue);
  transform: scale(1.12);
}

.card-pop__bd {
  flex: 1 1 auto;
  min-height: 0;
  display: block;
  padding: 6px 16px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* 驭马江湖 only: no scrollbar, table self-fits the frame */
.card-pop__bd.is-stories {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.card-pop__hint {
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  color: var(--gray-soft);
}

.card-pop__ft {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px 16px;
}

.card-pop__sub {
  font-size: clamp(0.78rem, 0.88vw, 0.92rem) !important;
  letter-spacing: 0.05em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-pop__en {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  color: rgba(82, 82, 82, 0.45);
  white-space: nowrap;
}

/* ---------- footer (fixed, always visible, left-aligned) ---------- */
.site-foot {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  padding: 0 0 20px;
  font-size: 0.78rem;
  color: var(--gray-soft);
}

.site-foot .shell {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* "小猴平方" carries the same mailto as the header brand; it stays in the footer's
   own colour and only picks up the brand blue on hover, so the line still reads
   as one sentence rather than a link glued into it. */
.foot-copy__link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}

.foot-copy__link:hover,
.foot-copy__link:focus-visible {
  color: var(--blue);
  text-underline-offset: 2px;
}

/* ---------- slogan ----------
   Chinese block sits top-left, English block bottom-right. Both are three
   lines, underlined, and share the same colour order top→bottom:
   black → gray → blue.

   All six lines are one size, set by --slogan-size (the `aidu` size).
   At line-height 1.5 those six lines plus the hermes mark stack to roughly
   12.4 × base, so the size is capped by vh as well as vw or the block would
   outgrow the column and break the wipe seam. */
.slogan {
  /* 3.7vh cap keeps all six lines plus the center mark within the reveal height;
     at 4.6vh/2.8rem max, the 15.24em total stack height ballooned to 683px on
     1920x1080 and pushed the bottom row ("智助") 60px past the column bottom. */
  --slogan-size: clamp(1.1rem, min(3.4vw, 3.7vh), 2.35rem);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* keeps the last underline clear of the column edge */
  padding-bottom: 0.12em;
  font-size: var(--slogan-size);
}

.slogan__cn,
.slogan__text {
  display: flex;
  flex-direction: column;
  /* h2 carries a UA font-size of 1.5em — reset so 1em means the base */
  font-size: 1em;
  font-weight: 900;
  line-height: 1.5;
}

/* English: top-right, right-aligned */
.slogan__text {
  align-self: flex-end;
  align-items: flex-end;
  text-align: right;
}

/* logo-favicon mark centred between the English and Chinese blocks. Sized off the
   slogan base so it stays proportional to the type at any viewport. */
.slogan__mark {
  align-self: center;
  width: 6.24em;
  height: auto;
  object-fit: contain;
}

/* Chinese: bottom-left, left-aligned */
.slogan__cn {
  align-self: flex-start;
  align-items: flex-start;
  text-align: left;
}

/* 六行同字号，加粗加下划线 */
.slogan__row {
  display: block;
  font-size: 1em;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 0.1em;
  text-decoration-thickness: 0.055em;
}

/* 中文三行：黑 → 灰 → 蓝 */
.slogan__row--cn1 {
  color: var(--ink);
}

.slogan__row--cn2 {
  color: var(--gray);
}

.slogan__row--cn3 {
  color: var(--blue);
}

/* 英文三行：黑 → 灰 → 蓝（与中文同序、同字号） */
.slogan__row--aidu {
  color: var(--ink);
}

.slogan__row--ido {
  color: var(--gray);
}

.slogan__row--aido {
  color: var(--blue);
}


/* Side-by-side desktop: the hero column is pinned to the dudu artwork's
   height by JS; the title/subtitle/cards keep their natural rhythm on top
   and the updates feed takes over whatever room the cards do not use. */

/* =====================================================================
   MOBILE / TABLET LAYOUT (≤1024px) — one screen, swipe-driven card curtain
   ---------------------------------------------------------------------
   Structure (JS builds .mobile-wrap):

     .mobile-stage                     the band between header and footer
       .msub                           SECONDARY background (never covers
         .msub__half--top              header/footer): top half = dudu artwork
           .mart  (dudu + hermes)      on top, then the two hero text groups;
           .mhero--title (2 lines)     bottom half = the slogan block in PC
           .mhero--desc  (2 lines)     proportions
         .msub__half--bottom
           .slogan
       .mcards                         six cards, 2 cols x 3 rows, exactly
                                       half the band tall. Starts over the
                                       bottom half (hiding the slogan) and
                                       slides up to the top half on swipe.

   The PRIMARY background stays the hexagon field (.hex-bg), thinned out on
   mobile by JS so its density matches the desktop feel.
   PC layout (≥1025px) is untouched.
   ===================================================================== */
@media (max-width: 1024px) {
  /* --- band geometry: everything derives from these --- */
  :root {
    --m-brand-h: 60px;
    --m-footer-h: 52px;
    --m-safe-t: env(safe-area-inset-top, 0px);
    --m-safe-b: env(safe-area-inset-bottom, 0px);
    /* dvh keeps the stage between the remaining brand/footer chrome bands */
    --m-band: calc(100dvh - var(--m-brand-h) - var(--m-footer-h) - var(--m-safe-t) - var(--m-safe-b));
    --m-half: calc(var(--m-band) / 2);
    --m-gap: clamp(5px, 1.6vw, 9px);
    --m-pad: clamp(8px, 3vw, 16px);
  }

  /* --- one screen, no page scrolling: the swipe drives the curtain --- */
  html,
  body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
  }

  .scroll-track {
    display: none;
  }

  /* the desktop stage/reveal machinery steps aside completely */
  .reveal,
  .stage-back__content > .dudu {
    display: none !important;
  }

  /* ---------- lower-right brand dock above the footer ---------- */
  .brand-dock {
    bottom: calc(var(--m-footer-h) + var(--m-safe-b));
    height: var(--m-brand-h);
    padding: 0;
    z-index: 50;
    display: flex;
    align-items: center;
  }

  .brand-dock .shell {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 4px var(--m-pad);
  }

  .brand {
    flex: 0 0 auto;
    gap: 6px;
    width: auto;
    justify-content: flex-end;
  }

  /* the logo is wrapped in its own <a> (only the image is the mail link), so the
     flex child of .brand is that link, not the image */
  .brand__link {
    flex: 0 0 auto;
  }

  .brand__mark {
    flex: 0 0 auto;
    width: clamp(38px, 11.2vw, 51px);
    height: clamp(38px, 11.2vw, 51px);
  }

  .brand__text {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: baseline;
    justify-content: flex-start;
    gap: 0;
    font-size: clamp(1.12rem, 4.48vw, 1.6rem);
    letter-spacing: 0.04em;
    white-space: nowrap;
  }

  .brand__text > span {
    display: inline;
    margin-left: 0;
  }

  .brand__sep {
    display: inline;
  }

  .brand__text > span::before {
    content: none;
  }

  /* pills are the first line inside the mobile hero */
  .msub__half--top .header-pills {
    flex: 0 0 auto;
    min-width: 0;
    display: flex;
    flex-wrap: nowrap;
    justify-content: flex-start;
    align-items: center;
    gap: 3px clamp(3px, 1vw, 8px);
    margin: 0;
    width: 100%;
  }

  .header-pill {
    flex: 0 1 auto;
    min-width: 0;
    gap: 4px;
    font-size: var(--mpill-fs, clamp(0.42rem, 1.9vw, 0.6rem));
    letter-spacing: 0.06em;
    padding: 2px clamp(5px, 1.8vw, 9px);
    border-radius: 999px;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .header-pill::before {
    width: 3px;
    height: 3px;
  }

  /* ---------- footer: update time + one-line copyright, both centred ---------- */
  .site-foot {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 50;
    height: calc(var(--m-footer-h) + var(--m-safe-b));
    padding: 0 0 var(--m-safe-b);
    display: flex;
    align-items: center;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-top: none;
  }

  .site-foot .shell {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 0 8px;
  }

  /* one row: update time left, copyright right — both nowrap, both sized by
     fitMobileChrome() to the half of the row they get */
  .site-foot__last-update {
    flex: 0 0 auto;
    font-size: var(--mfoot-lu-fs, clamp(0.5rem, 2.2vw, 0.7rem));
    line-height: 1.3;
    color: var(--gray-soft);
    text-align: left;
    white-space: nowrap;
  }

  .foot-copy {
    flex: 0 0 auto;
    display: block;
    text-align: right;
    line-height: 1.3;
    white-space: nowrap;
    font-size: var(--mfoot-copy-fs, clamp(0.5rem, 2.2vw, 0.7rem));
    color: var(--gray-soft);
  }

  /* ---------- stage: the band between header and footer ---------- */
  .stage-back {
    position: fixed;
    inset: 0;
    z-index: 1;
    display: block;
    overflow: hidden;
  }

  .stage-back .hex-bg {
    position: absolute;
  }

  .stage-back__content {
    position: absolute;
    top: var(--m-safe-t);
    left: 0;
    right: 0;
    height: var(--m-band);
    width: 100%;
    max-width: none;
    padding: 0;
    display: block;
  }

  .mobile-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    touch-action: pan-y;
  }

  /* ---------- SECONDARY background: two stacked halves ---------- */
  .msub {
    position: absolute;
    inset: 0;
    z-index: 0;
    display: flex;
    flex-direction: column;
  }

  .msub__half {
    flex: 0 0 50%;
    height: 50%;
    min-height: 0;
    padding: var(--m-gap) var(--m-pad);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  /* top half: the artwork sits at the top, both hero text groups beneath it */
  .msub__half--top {
    align-items: center;
    justify-content: flex-start;
    gap: 2px;
  }

  /* one text group = two nowrap lines that share a JS-fitted font size,
     so the widest line sets the size and the narrower one follows */
  .mhero {
    flex: 0 0 auto;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
  }

  .mhero__line {
    display: block;
    white-space: nowrap;
    line-height: 1.3;
    text-align: left;
    font-size: var(--mhero-fs, 0.85rem);
  }

  /* colours inherited from the PC hero: black body + deep blue keywords */
  .mhero--title .mhero__line {
    color: var(--ink);
    font-weight: 650;
  }

  .mhero--title .mhero__line span {
    color: var(--blue);
  }

  .mhero--desc .mhero__line {
    color: var(--gray);
    font-weight: 400;
  }

  /* dudu + hermes: takes whatever height the four text lines leave over,
     but never less than 46% of the half so short landscape bands still show
     the artwork instead of collapsing it to nothing */
  .mart {
    flex: 1 1 auto;
    min-height: 46%;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
  }

  .mart__frame {
    position: relative;
    height: 100%;
    aspect-ratio: 1047 / 1297;
    max-width: 100%;
  }

  /* <picture> wraps the art for the WebP/PNG pair; it is an inline box by
     default, so it has to be told to fill the frame or the image inside it
     collapses. */
  .mart__frame > picture,
  .mart__palm > picture {
    display: block;
    width: 100%;
    height: 100%;
  }

  .mart__dudu {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
  }

  .mart__palm {
    position: absolute;
    left: 77.5%;
    top: 58.5%;
    width: 26%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
  }

  .mart__palm img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    animation: hover 5s ease-in-out infinite alternate;
  }

  /* bottom half: the slogan, same composition as the PC layer.
     The mark is twice the PC-relative size (12.5em vs 6.24em) because the
     phone's half-band is much wider than it is tall, and the Chinese/English
     blocks pull in from the edges instead of hugging them. */
  .msub__half--bottom {
    justify-content: center;
  }

  .msub__half--bottom .slogan {
    --slogan-size: var(--mslogan-fs, 1.1rem);
    height: 100%;
    width: 100%;
    padding-bottom: 0;
  }

  .msub__half--bottom .slogan__mark {
    width: 12.5em;
    /* two caps: the width one keeps the mark clear of the text blocks, the
       height one keeps the stack inside short landscape bands */
    max-width: 66%;
    max-height: 46%;
    object-fit: contain;
  }

  /* pull both text blocks toward the centre (PC hugs the column edges, which
     looks stranded on a phone-width band) */
  .msub__half--bottom .slogan__text {
    margin-right: 8%;
  }

  .msub__half--bottom .slogan__cn {
    margin-left: 8%;
  }

  /* ---------- the six-card curtain ---------- */
  .mcards {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 50%;
    z-index: 2;
    padding: 0 var(--m-pad);
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(3, minmax(0, 1fr));
    grid-auto-flow: column;
    gap: var(--m-gap);
    /* 0 = covering the slogan (bottom half), 1 = covering the artwork (top).
       The position is animated frame-by-frame in JS (drag + momentum glide),
       so there must be no CSS transition here to fight it. */
    transform: translateY(calc((1 - var(--mcards-p, 0)) * 100%));
    will-change: transform;
  }

  /* card internals: the PC card's internal proportions, scaled to the phone.
     PC reference at 1440px — desc/title = 0.72, title→desc gap = 0.8 x title
     font-size, en/title = 0.60, icon/title = 1.37. The mobile card is opaque:
     no translucency, and no hover/lift animation. */
  .mcards .card {
    min-height: 0;
    height: 100%;
    padding: clamp(6px, 2.1vw, 12px) clamp(7px, 2.4vw, 13px);
    border-radius: 11px;
    background: var(--paper);
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 2px;
    transition: none;
  }

  /* no glow sweep, no underline sweep, no lift on mobile */
  .mcards .card::after,
  .mcards .card__line {
    display: none;
  }

  .mcards .card:hover,
  .mcards .card:active {
    transform: none;
    border-color: var(--gray-line);
    box-shadow: none;
  }

  .mcards .card__top {
    display: flex !important;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
  }

  .mcards .card__en {
    font-size: clamp(0.56rem, 2.46vw, 0.72rem);
    letter-spacing: 0.1em;
  }

  .mcards .card__icon {
    width: clamp(17px, 5.6vw, 24px);
    height: clamp(17px, 5.6vw, 24px);
  }

  /* PC keeps a 0.8em gap between the four-character title and the ten-character
     subtitle; mirror that rather than the old 1px */
  .mcards .card__title {
    font-size: var(--mcard-title-fs, 1rem);
    line-height: 1.2;
    margin-bottom: 0.8em;
    white-space: nowrap;
  }

  /* the subtitle is sized RELATIVE to the title (0.72x, the PC ratio) and then
     shrunk further by JS if ten characters still do not fit on one line — it can
     never grow past the title */
  .mcards .card__desc {
    display: block !important;
    font-size: calc(var(--mcard-title-fs, 1rem) * var(--mcard-desc-k, 0.722));
    line-height: 1.25;
    white-space: nowrap;
    color: var(--blue);
  }

  /* ---------- popup: a sheet inside the band, header/footer stay visible ---------- */
  .card-pop {
    left: 0;
    top: var(--m-safe-t);
    width: 100%;
    height: var(--m-band);
    z-index: 40;
    border-radius: 0;
    border: none;
    border-top: 1px solid var(--gray-line);
    border-bottom: 1px solid var(--gray-line);
  }

  .card-pop__close {
    top: 8px;
    right: 12px;
    font-size: 1.6rem;
    padding: 6px 8px;
  }

  .card-pop__head {
    padding: 14px 48px 6px var(--m-pad);
  }

  .card-pop__title {
    font-size: clamp(1.05rem, 4.6vw, 1.35rem);
  }

  .card-pop__bd {
    padding: 6px var(--m-pad);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .card-pop__ft {
    padding: 8px var(--m-pad) 14px;
  }
}

/* Landscape phones: the band is barely 300px tall, so the card grid loses its
   English label row and the text groups tighten up. The card title also has to
   step down or a 43px-tall card cannot hold it. */
@media (max-width: 1024px) and (max-height: 520px) {
  :root {
    --m-brand-h: 54px;
    --m-footer-h: 44px;
  }

  .mcards .card__top {
    display: none !important;
  }

  /* A landscape card is wide but only ~43px tall, so the height caps the type
     long before it could span the width. Centring the pair keeps the left and
     right insets equal instead of leaving a large ragged gap on the right. */
  .mcards .card {
    justify-content: center;
    padding: 4px 8px;
    gap: 0;
    text-align: center;
  }

  /* the ~42px-tall landscape card cannot hold the PC gap; the JS-fitted title
     size still applies, only the breathing room shrinks. line-height is pulled
     in too: at 1.2 the two lines alone exceed the card. */
  .mcards .card__title {
    margin-bottom: 0.2em;
    line-height: 1.05;
  }

  .mcards .card__desc {
    line-height: 1.1;
  }

  .msub__half {
    padding: 3px var(--m-pad);
  }
}

/* Touch devices: no hover state should stick after a tap. */
@media (hover: none) {
  .card:hover {
    transform: none;
    border-color: var(--gray-line);
    box-shadow: none;
  }

  .card:hover::after {
    opacity: 0;
  }

  .card:hover .card__icon {
    transform: none;
  }

  .card:hover .card__line {
    transform: scaleX(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------- pop content layouts ---------- */
.brand-aidu {
  color: var(--gray);
  font-weight: 700;
}

.brand-sub {
  color: var(--blue);
  font-weight: 700;
}

.pop-content {
  width: 100%;
  text-align: left;
  animation: fadeUp 0.3s ease-out forwards;
}

/* 驭马江湖 only: full-height flex column so the table can self-fit */
.card-pop__bd.is-stories .pop-content {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.pop-intro {
  display: none;
}

.pop-header-title {
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--gray);
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px dashed rgba(82, 82, 82, 0.2);
  letter-spacing: 0.02em;
}

.pop-features {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}

.pop-feature {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  background: rgba(31, 78, 121, 0.03);
  border: 1px solid rgba(31, 78, 121, 0.1);
  border-radius: 8px;
}

.pop-feature__header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.pop-feature__icon {
  width: 18px;
  height: 18px;
  stroke: var(--blue);
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

.pop-feature__title {
  font-size: 0.92rem;
  color: var(--blue);
  font-weight: 700;
}

.pop-feature__list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 26px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.pop-feature__list li {
  position: relative;
  font-size: 0.8rem;
  color: var(--gray);
  line-height: 1.45;
}

.pop-feature__list li::before {
  content: "•";
  position: absolute;
  left: -12px;
  color: var(--blue);
  font-weight: bold;
}

.pop-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
}

/* two pills side by side (Gitee + GitHub) with a gap between them, otherwise
   the single-pill fallback just stays centred */
.pop-pills {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
}

/* live release name fetched from the GitHub API, sitting under its pill.
   Release titles are author-written and can be long ("🌈 v15.0.0 Iris · 伊里斯
   — 官方记忆通道 · 静默失效清零"), so the line is allowed to wrap and stays
   centred when it does. */
.pop-release {
  max-width: 100%;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--gray-soft);
  min-height: 1em;
}

.pop-release__tag {
  font-weight: 700;
  color: var(--blue);
}

.pop-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  background: var(--blue);
  border: 1px solid var(--blue);
  color: #ffffff;
  font-size: 0.85rem;
  font-weight: 600;
  border-radius: 6px;
  text-decoration: none;
  transition: all 0.2s ease;
  box-shadow: 0 2px 6px rgba(31, 78, 121, 0.2);
}

.pop-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  background: rgba(31, 78, 121, 0.08);
  border: 1px solid rgba(31, 78, 121, 0.2);
  color: var(--blue);
  font-size: 0.82rem;
  font-weight: 600;
  border-radius: 20px;
  text-decoration: none;
  transition: all 0.2s ease;
  margin-right: 6px;
  vertical-align: middle;
}

.pop-pill:hover {
  background: var(--blue);
  color: #ffffff;
  border-color: var(--blue);
}

.pop-pill .pop-pill__icon {
  width: 14px;
  height: 14px;
  stroke: currentColor;
  fill: none;
}

.pop-btn:hover {
  background: #173d60;
  border-color: #173d60;
  color: #ffffff;
}

.pop-btn__icon {
  width: 16px;
  height: 16px;
  fill: #ffffff;
}

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}




/* ---------- stories forum table & pagination ---------- */
.stories-forum {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  margin-top: 8px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(31, 78, 121, 0.15);
  border-radius: 10px;
  padding: 6px 10px;
}

.stories-table-wrapper {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* measuring pass: let rows fall back to natural height so the row-height
   probe isn't polluted by the stretch below */
.stories-table-wrapper.is-measuring .stories-table {
  height: auto;
}

.stories-table {
  width: 100%;
  /* stretch to eat every leftover pixel of the frame */
  height: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
  text-align: left;
}

.stories-table th {
  padding: 4px 8px;
  border-bottom: 2px solid rgba(31, 78, 121, 0.2);
  color: var(--blue, #1f4e79);
  font-weight: 700;
  text-align: center;
  font-size: 0.8rem;
}

.stories-table th.col-date {
  width: 90px;
  white-space: nowrap;
}

.stories-table td {
  padding: 4px 8px !important;
  border-bottom: 1px dashed rgba(82, 82, 82, 0.15);
  color: var(--gray, #525252);
  font-size: 0.78rem !important;
  line-height: 1.35 !important;
}

.stories-table td.col-date-val {
  text-align: center;
  font-size: 0.78rem !important;
  color: var(--gray, #525252);
  white-space: nowrap;
}

.stories-table tr:last-child td {
  border-bottom: none;
}

.stories-table tr:hover td {
  background: rgba(31, 78, 121, 0.04);
}

/* 占位空行：只占高度，不显示分隔线也不响应 hover */
.stories-table tr.stories-row--empty td {
  border-bottom: 1px dashed rgba(82, 82, 82, 0.07);
}

.stories-table tr.stories-row--empty:hover td {
  background: transparent;
}

.stories-title-link {
  color: var(--gray, #525252);
  text-decoration: none;
  font-weight: 400;
  font-size: 0.75rem !important;
  transition: color 0.2s ease, text-decoration 0.2s ease;
  display: inline-block;
  line-height: 1.25 !important;
}

.stories-title-link.is-pinned {
  font-weight: 600;
  color: var(--ink, #000000);
}

.stories-title-link:hover {
  color: var(--purple, #7030a0);
  text-decoration: underline;
}


.stories-pagination {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed rgba(82, 82, 82, 0.15);
}

.stories-pg-btn {
  background: rgba(31, 78, 121, 0.08);
  color: var(--blue, #1f4e79);
  border: 1px solid rgba(31, 78, 121, 0.2);
  padding: 3px 10px;
  border-radius: 4px;
  font-size: 0.72rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.stories-pg-btn:hover:not(:disabled) {
  background: var(--blue, #1f4e79);
  color: #ffffff;
}

.stories-pg-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.stories-pg-info {
  font-size: 0.68rem !important;
  color: var(--gray-soft, #7c7c7c) !important;
  font-family: inherit;
  font-weight: 400;
}

/* =============================================================================
   aidu小猴 mascot chat — added 2026-08-06
   Entry: the header monkey logo (.brand__link is now a <button>).
   Speech bubble: pops under the logo, spawned by mascot.js on a timer.
   Chat popup: body-level fixed layer opened under the logo (mascot.js
   positionPop), light paper style with blue accents to match the site
   palette (--blue #1f4e79 / --gray #525252 / --paper #ffffff).
   ============================================================================= */

/* the logo was a mailto <a>, now a chat <button>: reset button chrome while
   keeping the exact same box (inline-flex, line-height 0) and the monkey's
   monkey-play animation untouched. */
.brand__link {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.brand__link:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 8px;
}

/* ---------- speech bubble ---------- */
.mascot-bubble {
  position: fixed;
  z-index: 40;
  max-width: 240px;
  padding: 9px 13px;
  /* 大叔 2026-08-06 钦定：别整 chat 圆泡泡，跟主页卡片同款——
     半透明白底 + 灰色细边 + 统一圆角(--radius)，跟 .card 边线/背景一致 */
  background: rgba(255, 255, 255, 0.82);
  color: var(--blue);
  border: 1px solid var(--gray-line);
  border-radius: var(--radius);
  font-size: 0.82rem;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0.02em;
  box-shadow: 0 10px 28px rgba(31, 78, 121, 0.18);
  pointer-events: none;
  white-space: normal;
}

.mascot-bubble::before,
.mascot-bubble::after {
  content: "";
  position: absolute;
  left: auto;
  top: 50%;
  bottom: auto;
  width: 0;
  height: 0;
  border-style: solid;
}

/* compact joined tail: outer triangle continues the gray border, inner triangle
   covers its centre with the bubble fill so the pointer stays clear but subtle */
.mascot-bubble::before {
  right: -10px;
  margin-top: -7px;
  border-width: 7px 0 7px 10px;
  border-color: transparent transparent transparent rgba(82, 82, 82, 0.2);
}

.mascot-bubble::after {
  right: -8px;
  margin-top: -6px;
  border-width: 6px 0 6px 8px;
  border-color: transparent transparent transparent rgba(255, 255, 255, 0.96);
}

.mascot-bubble.is-in {
  animation: mascot-bubble-in 0.32s var(--ease);
}

.mascot-bubble.is-out {
  animation: mascot-bubble-out 0.3s var(--ease) forwards;
}

@keyframes mascot-bubble-in {
  from {
    opacity: 0;
    transform: translateY(6px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes mascot-bubble-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
    transform: translateY(4px) scale(0.94);
  }
}

/* ---------- chat popup ---------- */
.mascot-pop {
  position: fixed;
  left: 20px;
  top: 90px;
  z-index: 30;
  width: 324px;
  height: min(440px, calc(100vh - 88px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--gray-line);
  border-radius: var(--radius);
  box-shadow: 0 18px 50px rgba(31, 78, 121, 0.16);
  animation: card-pop 0.28s var(--ease);
}

.mascot-pop[hidden] {
  display: none;
}

.mascot-pop__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 44px 11px 14px;
  background: var(--paper);
  border-bottom: 1px solid var(--gray-line);
}

.mascot-pop__avatar {
  width: 36px;
  height: 36px;
  border-radius: 0;
  background: transparent;
  object-fit: contain;
  padding: 0;
}

.mascot-pop__titles {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.mascot-pop__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 800;
  color: var(--blue);
  letter-spacing: 0.04em;
}

.mascot-pop__title i {
  margin-right: 0.12em;
  color: var(--blue);
  font-style: normal;
  font-weight: 400;
}

.mascot-pop__title span {
  color: var(--gray);
}

.mascot-pop__sub {
  font-size: 0.72rem;
  color: var(--gray-soft);
  letter-spacing: 0.02em;
}

.mascot-pop__close {
  position: absolute;
  top: 10px;
  right: 12px;
  z-index: 2;
  width: auto;
  height: auto;
  padding: 4px 8px;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--gray);
  font-family: var(--font);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.mascot-pop__close:hover {
  background: var(--blue-tint);
  color: var(--blue);
}

.mascot-pop__msgs {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 14px 14px 8px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overscroll-behavior: contain;
}

.mascot-pop__msgs::-webkit-scrollbar {
  width: 5px;
}

.mascot-pop__msgs::-webkit-scrollbar-thumb {
  background: var(--blue-line);
  border-radius: 3px;
}

.mascot-msg {
  display: flex;
  align-items: flex-end;
  max-width: 88%;
}

.mascot-msg--bot {
  align-self: flex-start;
}

.mascot-msg--user {
  align-self: flex-end;
}

.mascot-msg__bubble {
  padding: 8px 12px;
  border-radius: 12px;
  font-size: 0.85rem;
  line-height: 1.55;
  letter-spacing: 0.01em;
  word-break: break-word;
  white-space: pre-wrap;
}

.mascot-msg--bot .mascot-msg__bubble {
  background: rgba(82, 82, 82, 0.08);
  color: var(--gray);
  border: 1px solid rgba(82, 82, 82, 0.2);
}

.mascot-msg--user .mascot-msg__bubble {
  background: rgba(31, 78, 121, 0.11);
  color: var(--blue);
  border: 1px solid var(--blue-line);
}

.mascot-msg__typing {
  display: inline-flex;
  gap: 4px;
  padding: 4px 2px;
}

.mascot-msg__typing i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gray-soft);
  animation: mascot-typing 1.2s infinite ease-in-out;
}

.mascot-msg__typing i:nth-child(2) {
  animation-delay: 0.15s;
}

.mascot-msg__typing i:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes mascot-typing {
  0%, 60%, 100% {
    opacity: 0.35;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-3px);
  }
}

.mascot-pop__form {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 取消内部间距，因为隐形 turnstile 不需要它了 */
  gap: 0;
  padding: 10px 12px 12px;
  border-top: 1px solid var(--gray-line);
  background: var(--paper);
  width: 100%;
  box-sizing: border-box;
}

.mascot-pop__row {
  display: flex;
  gap: 8px;
  width: 100%;
}

/* 隐形模式：不再需要占据高度，但是为了容纳不可见的 iframe 保留结构 */
.mascot-pop__turnstile-wrap {
  display: block; /* 移除 flex，避免残余渲染 */
  width: 100%;
  min-height: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  height: 0;
  opacity: 0;
  pointer-events: none; /* 彻底切断任何交互残留 */
}

.mascot-pop__turnstile-wrap > div,
.mascot-pop__turnstile-wrap iframe {
  width: 100% !important;
  max-width: none !important;
  margin: 0 !important;
  display: block !important;
}

.mascot-pop__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid var(--gray-line);
  border-radius: 18px;
  background: #f7f8fa;
  color: var(--ink);
  font-family: var(--font);
  font-size: 0.85rem;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.mascot-pop__input:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-tint);
}

.mascot-pop__input::placeholder {
  color: var(--gray-soft);
}

.mascot-pop__send {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--blue-line);
  border-radius: 50%;
  background: var(--paper);
  color: var(--blue);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}

.mascot-pop__send:hover {
  background: var(--blue-tint);
}

.mascot-pop__send:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* mobile: full-width chat sheet, positioned above the lower mascot by JS */
@media (max-width: 720px) {
  .mascot-pop {
    left: 10px;
    right: 10px;
    top: 10px;
    bottom: auto;
    width: calc(100vw - 20px);
    height: min(68vh, calc(100vh - 20px));
  }

  .mascot-bubble {
    max-width: 200px;
    font-size: 0.78rem;
  }
}
