/* DeepDive · Category Banners ─────────────────────────────────────────────
 * Geometric motion banners for the 6 category entry cards and category heroes.
 * Pairs with deepdive/banners.svg.html (inline the sprite once per page).
 *
 * Contract
 *   <a class="cat-card" data-cat-theme="agents">
 *     <svg class="cat-bnr"><use href="#bnr-agents"></use></svg>
 *     <div class="cat-card-body">…</div>
 *   </a>
 *   <div class="cat-hero" data-cat-theme="agents">
 *     <svg class="cat-bnr"><use href="#bnr-agents"></use></svg>
 *   </div>
 *
 * Rules this file obeys
 *   · Zero JavaScript. All motion is @keyframes; no SMIL, no script hooks.
 *   · prefers-reduced-motion: reduce → every banner animation is switched off
 *     and each symbol still reads as a finished static composition.
 *   · Colour only ever comes from --cat-accent (per-category) and the site
 *     surface tokens in colors_and_type.css. Light mode is handled by
 *     [data-mode="light"] overrides, not by a second set of markup.
 *   · Motion budget: transform + opacity only. No filter animation, no
 *     feTurbulence, no box-shadow tweening — six of these render at once.
 *
 * Shadow-tree note (important if you edit the sprite)
 *   Symbol content is cloned into a <use> shadow tree. Descendant selectors
 *   do NOT cross that boundary, so every rule that reaches inside a banner is
 *   a bare class selector (.bnr-*). Never write `.cat-bnr .bnr-a-ring` — it
 *   will silently stop matching. Static colour is set inline in the sprite
 *   via style="stroke:var(--cat-accent,…)", because inherited custom
 *   properties DO cross the boundary while selector matching does not.
 * ------------------------------------------------------------------------ */

/* ============ 1 · CATEGORY ACCENTS ============
 * Dark (default). --cat-accent is the only colour token a banner reads.
 */
.cat-card,
.cat-hero                              { --cat-accent: var(--accent, #f5a524); }

[data-cat-theme="agents"]              { --cat-accent: #d1402c; }  /* 智能体与模型 */
[data-cat-theme="compute"]             { --cat-accent: #f5a524; }  /* 算力与商业 */
[data-cat-theme="governance"]          { --cat-accent: #e84040; }  /* 治理与地缘 */
[data-cat-theme="society"]             { --cat-accent: #7fb88b; }  /* 劳动与社会 */
[data-cat-theme="labs"]                { --cat-accent: #c9a24b; }  /* 实验室与建设者 */
[data-cat-theme="probes"]              { --cat-accent: #6fa5b3; }  /* 实验与文化 */

/* Light mode — deepened so hairlines hold up on parchment (#f3ede0).
 * Double-attribute selectors, matching the specificity pattern used by
 * colors_and_type.css for [data-mode="light"][data-theme="…"].
 */
[data-mode="light"] [data-cat-theme="agents"],
.mode-light [data-cat-theme="agents"]      { --cat-accent: #b22a17; }
[data-mode="light"] [data-cat-theme="compute"],
.mode-light [data-cat-theme="compute"]     { --cat-accent: #b8761a; }
[data-mode="light"] [data-cat-theme="governance"],
.mode-light [data-cat-theme="governance"]  { --cat-accent: #b8302e; }
[data-mode="light"] [data-cat-theme="society"],
.mode-light [data-cat-theme="society"]     { --cat-accent: #4a7a55; }
[data-mode="light"] [data-cat-theme="labs"],
.mode-light [data-cat-theme="labs"]        { --cat-accent: #8a6a1f; }
[data-mode="light"] [data-cat-theme="probes"],
.mode-light [data-cat-theme="probes"]      { --cat-accent: #3f6f7d; }


/* ============ 2 · GRID ============
 * Collapsed hairline grid: the 1px gaps are the rules. Cards carry no border
 * of their own here; use .cat-card--framed for a standalone card.
 */
.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}


/* ============ 3 · CARD ============ */
.cat-card {
  display: flex;
  flex-direction: column;
  position: relative;
  background: var(--bg);
  color: inherit;
  text-decoration: none;
  border: 0;
  border-radius: 0;                    /* this brand never rounds */
  transition: background var(--d-base, 300ms) var(--ease-out, ease);
}

.cat-card--framed { border: 1px solid var(--line); }

.cat-card:hover,
.cat-card:focus-visible {
  background: var(--bg-2);
  outline: 0;
}

/* Hairline highlight instead of a shadow — no layout shift, no repaint cost. */
.cat-card::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px var(--cat-accent);
  opacity: 0;
  transition: opacity var(--d-base, 300ms) var(--ease-out, ease);
}
.cat-card:hover::after,
.cat-card:focus-visible::after { opacity: 0.55; }


/* ============ 4 · BANNER CANVAS ============
 * 16:9 in a card, 3:1 in a hero. The symbol's own
 * preserveAspectRatio="xMidYMid slice" does the cropping, so one viewBox
 * serves both; load-bearing geometry lives in the y 55–215 safe band.
 */
.cat-bnr {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--bg-2);
  overflow: hidden;
  contain: paint;
  opacity: 0.86;
  transition: opacity var(--d-base, 300ms) var(--ease-out, ease);
}

.cat-card:hover .cat-bnr,
.cat-card:focus-visible .cat-bnr { opacity: 1; }

/* Light mode: dark ink on parchment needs slightly less veiling. */
[data-mode="light"] .cat-bnr,
.mode-light .cat-bnr { opacity: 0.94; }


/* ============ 5 · CARD BODY ============ */
.cat-card-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px 18px;
  border-top: 1px solid var(--line);
}

.cat-card-kicker {
  font-family: var(--mono);
  font-size: var(--micro, 10px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cat-accent);
}

.cat-card-title {
  font-family: var(--display);
  font-weight: 400;
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: 0;                   /* never negative-track CJK */
  color: var(--fg);
  margin: 0;
  transition: color var(--d-fast, 150ms) var(--ease-out, ease);
}
.cat-card:hover .cat-card-title { color: var(--cat-accent); }

.cat-card-desc {
  font-family: var(--serif);
  font-weight: 300;
  font-size: var(--body-sm, 15px);
  line-height: 1.7;
  color: var(--fg-dim);
  margin: 0;
}

.cat-card-meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
  font-family: var(--mono);
  font-size: var(--micro, 10px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
  font-variant-numeric: tabular-nums;
}
.cat-card-meta .n { color: var(--cat-accent); }


/* ============ 6 · CATEGORY HERO ============ */
.cat-hero {
  position: relative;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-left: 0;
  border-right: 0;
  overflow: hidden;
}

.cat-hero .cat-bnr {
  aspect-ratio: 3 / 1;
  opacity: 0.9;
  border: 0;
}

@media (max-width: 640px) {
  .cat-hero .cat-bnr { aspect-ratio: 2.1 / 1; }
}

/* Body sits below the banner by default (hairline rule, no scrim). */
.cat-hero-body {
  padding: 18px 24px 20px;
  border-top: 1px solid var(--line);
  background: var(--bg);
}

/* …or as a solid instrument plate over the banner's lower-left corner. */
.cat-hero--overlay .cat-hero-body {
  position: absolute;
  left: 0;
  bottom: 0;
  max-width: min(560px, 76%);
  border-top: 1px solid var(--line);
  border-right: 1px solid var(--line);
}

.cat-hero-title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(24px, 4.5vw, 42px);
  line-height: 1.15;
  letter-spacing: 0;
  color: var(--fg);
  margin: 6px 0 0;
}
.cat-hero-title em { font-style: normal; color: var(--cat-accent); }


/* ============ 7 · MOTION ============
 * Every rule below is a bare class selector so it survives the <use> shadow
 * boundary. Durations are long and desynchronised on purpose — six banners
 * animating in one viewport must read as ambient instrumentation, not motion
 * graphics. transform/opacity only.
 */

/* ---- bnr-agents · recursive convergence -------------------------------
 * Eight identical oversized rects, exponentially scaled down and staggered
 * by exactly T/8 so the geometric ladder is seamless and loops invisibly.
 * The static (reduced-motion) ladder comes from inline transform:scale().
 */
.bnr-a-ring {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: bnr-recede 20s linear infinite;
  animation-delay: calc(var(--i, 0) * -2s);
}
/* scale(t) = 0.0448^t, sampled every 10% — one sample per ring, so the ten
 * staggered copies always sit on the same geometric ladder. */
@keyframes bnr-recede {
  0%   { transform: scale(1);      opacity: 0; }
  5%   {                           opacity: 0.45; }
  10%  { transform: scale(0.733);  }
  20%  { transform: scale(0.5373); }
  30%  { transform: scale(0.3938); }
  40%  { transform: scale(0.2886); }
  50%  { transform: scale(0.2115); }
  60%  { transform: scale(0.155);  }
  70%  { transform: scale(0.1136); }
  80%  { transform: scale(0.0833); opacity: 0.45; }
  90%  { transform: scale(0.0611); }
  100% { transform: scale(0.0448); opacity: 0; }
}

.bnr-a-scan {
  transform-box: view-box;
  animation: bnr-scan 9s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes bnr-scan {
  0%   { transform: translateY(-92px); opacity: 0; }
  10%  { opacity: 0.5; }
  90%  { opacity: 0.5; }
  100% { transform: translateY(92px);  opacity: 0; }
}

/* ---- bnr-compute · isometric load array ------------------------------- */
.bnr-c-bar {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: bnr-load var(--dur, 4.5s) ease-in-out infinite alternate;
  animation-delay: calc(var(--i, 0) * -0.41s);
}
@keyframes bnr-load {
  from { transform: scaleY(1); }
  to   { transform: scaleY(0.52); }
}

.bnr-c-cursor {
  transform-box: view-box;
  animation: bnr-sweep 13s cubic-bezier(0.4, 0, 0.6, 1) infinite alternate;
}
@keyframes bnr-sweep {
  from { transform: translateX(-176px); }
  to   { transform: translateX(176px); }
}

/* ---- bnr-governance · graticule + fault slip --------------------------
 * Long hold, then a fast discrete slip along the fault trace — a seismic
 * event, not a float. Two elements share the class: the clipped half of the
 * grid and its displacement marker, so they move as one block.
 */
.bnr-g-slip {
  transform-box: view-box;
  animation: bnr-slip 11s linear infinite;
}
@keyframes bnr-slip {
  0%, 44%   { transform: translate(0, 0); }
  48%, 92%  { transform: translate(7px, -3.5px); }
  96%, 100% { transform: translate(0, 0); }
}

/* ---- bnr-society · emergent clusters ---------------------------------- */
.bnr-s-cluster {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: bnr-breathe calc(9s + var(--i, 0) * 2.3s) ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -1.7s);
}
@keyframes bnr-breathe {
  0%, 100% { transform: scale(0.96); opacity: 0.6; }
  50%      { transform: scale(1.06); opacity: 1; }
}

.bnr-s-ring {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: bnr-detect 7.5s var(--ease-out, cubic-bezier(0.2, 0.7, 0.2, 1)) infinite;
}
@keyframes bnr-detect {
  0%   { transform: scale(0.55); opacity: 0; }
  15%  { opacity: 0.45; }
  100% { transform: scale(1.45); opacity: 0; }
}

/* ---- bnr-labs · node constellation ------------------------------------ */
.bnr-l-link {
  animation: bnr-flicker calc(7s + var(--i, 0) * 1.3s) ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -2.1s);
}
@keyframes bnr-flicker {
  0%, 100% { opacity: 0.07; }
  45%      { opacity: 0.46; }
  60%      { opacity: 0.3; }
}

.bnr-l-star {
  animation: bnr-twinkle calc(4.5s + var(--i, 0) * 0.9s) ease-in-out infinite alternate;
  animation-delay: calc(var(--i, 0) * -1.3s);
}
@keyframes bnr-twinkle {
  from { opacity: 0.28; }
  to   { opacity: 0.95; }
}

/* ---- bnr-probes · wave interference -----------------------------------
 * Two circular wavefront sources (pitch 14) sitting on one axis. The moiré
 * fringes between them are pure geometry — no painted bands, no gradients.
 * Each source drifts a few units against the other on its own period, so
 * the fringe field swims slowly. Two transforms for the whole banner.
 */
.bnr-p-a {
  transform-box: view-box;
  animation: bnr-drift-a 17s ease-in-out infinite alternate;
}
.bnr-p-b {
  transform-box: view-box;
  animation: bnr-drift-b 13s ease-in-out infinite alternate;
}
@keyframes bnr-drift-a {
  from { transform: translateX(0); }
  to   { transform: translateX(-6px); }
}
@keyframes bnr-drift-b {
  from { transform: translateX(0); }
  to   { transform: translateX(7px); }
}


/* ============ 8 · REDUCED MOTION ============
 * Bare class selectors again — a descendant selector would not reach into
 * the <use> shadow tree and the banners would keep animating.
 * Each symbol was authored so that its zero-state is a complete composition:
 * agents keeps its nested ladder (inline transforms), compute keeps its
 * authored bar heights, governance shows the grid unslipped, probes keeps
 * the static moiré, labs/society keep their attribute opacities.
 */
@media (prefers-reduced-motion: reduce) {
  .bnr-a-ring, .bnr-a-scan,
  .bnr-c-bar, .bnr-c-cursor,
  .bnr-g-slip,
  .bnr-s-cluster, .bnr-s-ring,
  .bnr-l-link, .bnr-l-star,
  .bnr-p-a, .bnr-p-b {
    animation: none !important;
  }
  .cat-card, .cat-card::after, .cat-bnr, .cat-card-title {
    transition: none !important;
  }
}

/* Print / high-contrast: drop the decorative layer entirely. */
@media print {
  .cat-bnr { display: none; }
}
