/* Cineview drag demo (/drag/): the drag experience styles (site/src/styles/temporal-drag.css and
   temporal-scenes-03-05.css) on the 2.0 runtime. v1's lane wrapper div.cineview-animate[data-
   cineview-animate-id] is v2's <cv-animate data-cineview-animate-id>; everything else is v1's. */

/* Temporal Drag is deliberately self-contained so the dark edit-suite palette
 * cannot leak into the warm official-site surfaces. */
.drag-temporal {
  /* Micro-cool black body (v3 §2.1): replaces the warm #0c0a08 so cyan/blue
   * signals read clean on top instead of being muddied by warmth. */
  --tp-bg-deep: #08070a;
  --tp-bg: #0c0a0c;
  --tp-bg-elevated: #15131a;
  --tp-bg-glass: rgba(12, 10, 12, 0.72);
  --tp-ink: #ede8e0;
  --tp-ink-soft: #b4ada1;
  /* Muted ink for small mono labels. Lifted twice: first from #5c564e, which sat UNDER the
   * z:50 vignette and read as near-invisible on a 390px phone; then from #8b8479, whose
   * 5.33:1 against bare --tp-bg still measured only 4.00 / 3.88 in real composited pixels
   * on ACT 1 — the ambient rig lifts the local background, so the on-paper ratio is not
   * what ships. (Those two measurements were taken on the since-deleted `.s01-slate` and
   * `.tp-rec` labels; the ratio they establish for muted ink over the lit act-1 plate still
   * holds for whatever sits there.) Raising labels above the rig (z 46) fixed the elements
   * that sit in their own boxes but could not help ones directly over the lit plate.
   * #9a9287 is 6.42:1 bare, leaving headroom for that lift. Verified from pixels. */
  --tp-ink-mute: #9a9287;
  --tp-ink-faint: rgba(237, 232, 224, 0.1);

  /* Film-grade three-color system (the teal-&-orange color-grade the subject
   * literally lives in). Three signal colors, each with ONE semantic role held
   * across every scene — not one-color-per-scene:
   *   - amber  = warm pole: tungsten light, the resting / base / calibration tone
   *   - teal   = cool pole: technical readout, the ACTIVE / measuring / in-flight tone
   *   - rec    = tally red: record light, the terminal / commit / CTA signal
   * warm-white is a neutral (brightest ink), not a fourth signal. No blue, no
   * magenta — they read as arbitrary the moment they appear next to the grade. */
  --tp-sig-amber: #d8a24a;
  --tp-sig-amber-glow: rgba(
    216,
    162,
    74,
    0.22
  ); /* base tone: glow dialed DOWN so amber reads as the sunk resting pole */
  /* Cool pole. 2026-08-15 主题审计 A-1 裁决 (a)：act4 的另立蓝家族（#72bfff hue≈207°
   * + 紫伴随）收编回 teal（hue≈187°）——五幕色温行程恢复「amber×3 → teal(4) →
   * amber(5)」，契约「No blue, no magenta」重新成立。act4 的 .tp-scene--04 局部
   * palette 现以 teal 提亮档消费这套冷极（见 temporal-scenes-03-05.css）。 */
  --tp-sig-teal: #409ba8; /* instrument cyan: desaturated toward a scope/monitor readout, away from brand-cyan */
  --tp-sig-teal-glow: rgba(64, 155, 168, 0.28);
  --tp-sig-rec: #e64536; /* 2026-08-15 用户翻案后暂无消费者：保留为契约终局/commit 信号的文档化角色 */
  --tp-sig-rec-glow: rgba(230, 69, 54, 0.38); /* 同上：随 rec 一并文档化保留，待终局幕消费者 */
  /* neutral brightest ink (canvas highlight / warm white), NOT a signal */
  --tp-sig-warm: #ece3d0;
  --tp-sig-warm-glow: rgba(236, 227, 208, 0.22);

  /* Legacy aliases → mapped onto the new palette so existing components keep
   * working during the incremental scene-by-scene migration. amber is the
   * base/calibration color; rec is brightened to the new signal red. */
  --tp-accent: var(--tp-sig-amber);
  --tp-accent-soft: #a08050;
  --tp-accent-dim: rgba(216, 162, 74, 0.18);
  --tp-accent-glow: var(--tp-sig-amber-glow);
  /* (--tp-rec-glow alias deleted 2026-08-15: its only consumer, the act1 CTA rec
   * glow, was reverted to amber with no glow — zero consumers left.) */
  --tp-rule: rgba(237, 232, 224, 0.08);
  --tp-rule-strong: rgba(237, 232, 224, 0.14);
  --tp-border-glass: rgba(237, 232, 224, 0.06);

  /* Type scale (2026 rework): mobile floors raised so nothing reads thin on a
   * 390px phone, and the display steps widened to a ~1.45 ratio so the hero
   * genuinely dominates instead of sitting one notch above body. 2xs/xs are
   * mono utility (labels, timecode); lg/xl/2xl are the Fraunces display range. */
  --tp-text-2xs: clamp(10px, 2.4cqw, 12px);
  --tp-text-xs: clamp(11px, 2.8cqw, 14px);
  --tp-text-sm: clamp(14px, 3.4cqw, 17px);
  --tp-text-md: clamp(17px, 4.6cqw, 24px);
  --tp-text-lg: clamp(28px, 7.6cqw, 46px);
  --tp-text-xl: clamp(42px, 12cqw, 74px);
  --tp-text-2xl: clamp(56px, 18cqw, 116px);

  --tp-space-1: clamp(2px, 0.6cqw, 4px);
  --tp-space-2: clamp(4px, 1.2cqw, 8px);
  --tp-space-3: clamp(8px, 2cqw, 14px);
  --tp-space-4: clamp(12px, 3cqw, 22px);
  --tp-space-5: clamp(18px, 4.5cqw, 32px);
  --tp-space-6: clamp(24px, 6cqw, 42px);
  --tp-space-7: clamp(36px, 9cqw, 64px);
  --tp-space-8: clamp(48px, 12cqw, 86px);

  --tp-radius-pill: 999px;
  --tp-shadow-glow: 0 0 20px 4px var(--tp-accent-glow);
  --tp-shadow-subtle: 0 4px 24px rgba(0, 0, 0, 0.4);
  --tp-font-display: 'Fraunces', 'Songti SC', Georgia, serif;
  --tp-font-body:
    'Inter', 'PingFang SC', 'Source Han Sans SC', 'Microsoft YaHei', system-ui, sans-serif;
  --tp-font-mono: 'Roboto Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --tp-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --tp-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --tp-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --tp-ease-linear-out: cubic-bezier(0.4, 0, 0.2, 1);

  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  container-type: inline-size;
  isolation: isolate;
  color: var(--tp-ink);
  background: var(--tp-bg);
  font-family: var(--tp-font-body);
}

.drag-temporal > .cineview-responsive-container,
.drag-temporal .cineview-stage,
.drag-temporal .cineview-container {
  width: 100%;
  height: 100%;
  min-height: 100%;
}

.drag-temporal > .cineview-responsive-container,
.drag-temporal .cineview-container {
  /* The site shell owns the visible height; override the framework's inline
   * 100vh default, which includes the collapsed browser toolbar on iOS. */
  height: 100% !important;
  min-height: 100% !important;
  background: var(--tp-bg) !important;
}

/* 2026-08-13 第五幕黑幕根级层（task-flow 2026-08-13-act5-black-exit-bg-follow）。
 * 黑幕原为 .tp-scene--05 的场景背景 —— 挂在 DragSceneStack 的场景框上，退场 settle
 * 补间（useDragSceneEngine.ts:708-743 写 dragProgressMotion → 框 translate3d）会把
 * 整块黑幕滑出屏幕下方，读作「黑幕被动画拖下去」。本层挂在 .drag-temporal 根，
 * 不随任何场景框平移：
 *   z-index 1 —— 高于容器背景、低于全部场景框（DragSceneStack 给框 z 10/1）；
 *   .drag-temporal 自身 isolation:isolate 建层叠上下文，框 z 与 1 同上下文比较。
 * ⚠️ z 序不变量（code-review 发现 15）：`.cineview-container` 必须保持**不建立
 * 层叠上下文**（当前仅 position:relative + background，无 z/transform/filter/
 * will-change）——否则场景框的 z 10/1 会被囚禁在容器内、本层将盖住全部场景。
 * opacity 由 TemporalDragExperience 三态编排（手势跟手直写 / 松手 700ms settle
 * 淡入淡出 / commit snap），transition 由 JS 内联接管（本规则不声明 transition）；
 * prefers-reduced-motion 下 settle 时长收 80ms（见 tsx）。 */
.drag-temporal .tp-act5-black {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(ellipse 66% 58% at 14% 4%, rgba(255, 226, 183, 0.2), transparent 62%),
    radial-gradient(ellipse 64% 40% at 50% 24%, rgba(225, 164, 91, 0.08), transparent 68%),
    linear-gradient(180deg, #050506, #08090c 68%, #0c0d11);
}

.drag-temporal .tp-scene {
  color: var(--tp-ink);
  /* Lit-room base (N1 背景太暗单调 fix): a soft warm key glow at upper-center
   * plus a faint cool floor lift, over the base black. Gives the
   * black structure/depth so it never reads as flat dead black. Per-scene --tp-sig
   * tints the top glow so each act's floor feels distinct without a new layer. */
  background:
    radial-gradient(
      ellipse 90% 60% at 50% 8%,
      color-mix(in srgb, var(--tp-sig, var(--tp-sig-amber)) 12%, transparent) 0%,
      transparent 58%
    ),
    radial-gradient(ellipse 120% 80% at 50% 108%, rgba(60, 66, 82, 0.14) 0%, transparent 60%),
    var(--tp-bg);
}

/* 2026-08-13：scene 5 移出本共享背景组 —— 其黑幕背景迁到根级 `.tp-act5-black` 层
 * （见下方该规则与 temporal-scenes-03-05.css 的 .tp-scene--05 注释），场景自身背景
 * 必须透明，否则黑幕仍随场景框退场补间下移。 */
.drag-temporal .tp-scene--03 {
  background:
    radial-gradient(
      ellipse 90% 60% at 50% 8%,
      color-mix(in srgb, var(--tp-sig, var(--tp-sig-amber)) 14%, transparent) 0%,
      transparent 58%
    ),
    radial-gradient(ellipse 120% 80% at 50% 108%, rgba(60, 66, 82, 0.16) 0%, transparent 60%),
    var(--tp-bg-elevated);
}

/* Per-scene signal-color aliases (调色台：每屏叙事偏重色，非独占).
 * 因自定义属性继承，在屏级 wrapper 覆写 --tp-accent 家族即可让全部
 * 硬编码 --tp-accent 的元素一次性吃到该屏主导色；再按 §2.4 语义叠加副色。 */
.drag-temporal .tp-scene--01 {
  --tp-sig: var(--tp-sig-amber);
  --tp-sig-glow: var(--tp-sig-amber-glow);
}
/* Act 2 is a tungsten set: dark gold only, no teal anywhere (user decree
 * 「颜色改为暗金色不要用青色」). This block is what actually decided the colour of the
 * eyebrow, the HUD signal and every --tp-accent consumer inside the act — recolouring
 * the light rig alone left the copy cyan. */
.drag-temporal .tp-scene--02 {
  --tp-sig: var(--tp-sig-amber);
  --tp-sig-glow: var(--tp-sig-amber-glow);
  --tp-accent: #c9a05a;
  --tp-accent-soft: #96703a;
  --tp-accent-dim: rgba(190, 146, 74, 0.18);
  --tp-accent-glow: rgba(190, 146, 74, 0.24);
}
.drag-temporal .tp-scene--03 {
  --tp-sig: var(--tp-sig-amber);
  --tp-sig-glow: var(--tp-sig-amber-glow);
}
/* Acts 04 and 05 declare their own palettes in `temporal-scenes-03-05.css`, which is
 * imported after this file. Duplicate blocks used to sit here as well, with DIFFERENT
 * values (04 teal vs the act's fluorescent green, 05 rec-red vs its warm amber): same
 * specificity, so the later file silently won and this copy was a decoy that made the
 * palette look like it was authored in two places. Both are deleted; each act's colour
 * now has exactly one declaration site. */

/* One row, and only one. The chrome bands (HUD / footer) that used to own the first
 * and third tracks are gone, so every act's inner now holds a single <main> stage —
 * plus, on act 2, an out-of-flow light rig (position:absolute) that claims no track.
 * `minmax(0, 1fr)` rather than `display: block` because the stages take their height
 * from the track: s02-stage is itself a grid with a `minmax(0,1fr)` row, and
 * s04/s05-stage are relative boxes whose whole content is absolutely positioned, so
 * all three collapse to zero height the moment they have to shrink-wrap. Grid stretch
 * hands them the full height for free; block would need a `height: 100%` added to each. */
.drag-temporal .tp-scene__inner {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  width: 100%;
  height: 100%;
  padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
}

.drag-temporal .tp-timecode {
  display: inline-flex;
  color: var(--tp-ink-soft);
  font-family: var(--tp-font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.drag-temporal .tp-timecode__characters {
  display: inline-flex;
}

.drag-temporal .tp-timecode__characters > span {
  display: inline-block;
  min-width: 0.62em;
  text-align: center;
}

.drag-temporal .tp-timecode__characters > span:nth-child(3n) {
  min-width: 0.42em;
  color: var(--tp-ink-mute);
}

.drag-temporal .tp-placeholder {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--tp-space-3);
  min-height: 0;
  color: var(--tp-ink-mute);
  text-align: center;
}

.drag-temporal .tp-placeholder > span {
  color: var(--tp-ink);
  font-family: var(--tp-font-display);
  font-size: var(--tp-text-2xl);
  font-weight: 600;
  line-height: 0.9;
}

.drag-temporal .tp-placeholder > p {
  margin: 0;
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-xs);
  letter-spacing: 0.18em;
}

/* The language toggle is no longer page chrome: it is mounted INSIDE act 1's <Scene>
 * (SceneRolling's GatedLangToggle) so it scrubs with the act's own timeline instead of
 * sitting at full opacity over all five acts.
 *
 * Consequence for the geometry: `.lang-toggle`'s base rule in design/global.css is
 * `position: absolute` with its own top/right, which would now resolve against whichever
 * positioned ancestor the drag tree happens to provide — and its self-positioning would
 * fight the <Animate> holder that owns its transform. The slot below owns the placement;
 * the button itself goes back into normal flow inside it. */
.drag-temporal .s01-lang-slot {
  position: absolute;
  top: clamp(10px, 3cqw, 20px);
  right: clamp(10px, 3cqw, 20px);
  /* Above the ambient rig (z 45) for the same reason .s01-stage is: small mono type over
   * the lit plate loses contrast under a screen-blended layer. */
  z-index: 47;
}

.drag-temporal .lang-toggle.lang-toggle--drag {
  position: relative;
  top: auto;
  right: auto;
  min-width: 44px;
  min-height: 44px;
  /* 2026-08-15 主题审计 C-1：手写近同值消双源，改用页面自己的 mute token。 */
  color: var(--tp-ink-mute);
  background: rgba(12, 10, 8, 0.78);
  border-color: rgba(237, 232, 224, 0.1);
}

.drag-temporal .s01-stage {
  position: relative;
  z-index: 46;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--tp-space-5), 6cqw, var(--tp-space-7));
  min-height: 0;
  padding: clamp(var(--tp-space-5), 7cqw, var(--tp-space-7)) 0
    clamp(var(--tp-space-6), 8cqw, var(--tp-space-8));
  overflow: hidden;
}

.drag-temporal .s01-dial-shell {
  position: relative;
  --s01-dial-size: min(68cqw, 340px);
  width: var(--s01-dial-size);
  aspect-ratio: 1;
  flex: 0 0 auto;
}

.drag-temporal .s01-dial-shell > cv-animate,
.drag-temporal .s01-dial-shell > cv-animate > * {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
}

.drag-temporal .s01-dial {
  position: relative;
  /* Single size base for the whole dial. Both the tick ring AND the inner ring
   * derive their radius from this one token, so they can never drift onto two
   * different circles the way the old min(58cqw,300px) vs 45cqw/234px split did. */
  --s01-ring-radius: calc(var(--s01-dial-size) * 0.4);
  width: 100%;
  height: 100%;
}

.drag-temporal .s01-dial > cv-animate,
.drag-temporal .s01-dial__ticks,
.drag-temporal .s01-hands {
  position: absolute;
  inset: 0;
}

.drag-temporal .s01-dial > cv-animate {
  display: grid;
  place-items: center;
}

.drag-temporal .s01-dial__ticks {
  border-radius: 50%;
}

/* One framework <Animate> PER TICK (60 lanes, `s01-tick-0` … `s01-tick-59`), each on an
 * absolute delay equal to the moment the second hand crosses it. Every one of those
 * cv-animate hosts must FILL the dial: the tick anchor inside is parked at the dial
 * centre and seats itself with `rotate(--tick-angle) translateY(-radius)`, so a
 * shrink-wrapped host would measure that polar offset from the wrong origin and the whole
 * ring would collapse toward the top-left.
 *
 * This selector used to also name `.s01-tick-segment`, the wrapper of the old twelve-segment
 * grouping. That element no longer exists — the ticks are 60 independent lanes now — so the
 * dead half is gone. The `> cv-animate` half is unchanged and still load-bearing: it
 * matches all 60 hosts, because the lane count is invisible to CSS. */
.drag-temporal .s01-dial__ticks > cv-animate {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
}

.drag-temporal .s01-tick {
  /* Zero-size anchor parked at the dial center. cineview's <Animate stagger>
   * writes an inline transform (scale/opacity entrance + drag breathing) onto
   * THIS element, so we must NOT put positioning transforms here — they'd be
   * overwritten (the earlier build did exactly that and every tick collapsed to
   * center). Polar seating lives on the untouched children below. Scaling a
   * zero-size point around its own origin (0 0 = dial center) can't shift the
   * arm, it only scales the children — which is the intended tick "breathing". */
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  transform-origin: 0 0;
}

.drag-temporal .s01-tick__bar {
  /* Solid neutral ink is intentional. A translucent bar exposes the amber room
   * light below it and turns whichever arc crosses the key light gold, recreating
   * a false directional sector even when every tick has the same CSS rule. */
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 1px;
  height: clamp(7px, 2.6cqw, 12px);
  background: #4b4741;
  transform-origin: 0 0;
  transform: translateX(-50%) rotate(var(--tick-angle))
    translateY(calc(-1 * var(--s01-ring-radius)));
}

.drag-temporal .s01-tick--major .s01-tick__bar {
  height: clamp(12px, 4.2cqw, 20px);
  background: #9a9287;
}

.drag-temporal .s01-tick__label {
  /* Seated on a smaller radius (inside the tick ring), then un-rotated by its own
   * angle so the digits stay upright, and finally self-centered on that point. */
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  transform: rotate(var(--tick-angle))
    translateY(calc(-1 * (var(--s01-ring-radius) - clamp(18px, 6cqw, 30px))))
    rotate(calc(-1 * var(--tick-angle))) translate(-50%, -50%);
  color: var(--tp-ink-mute);
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-2xs);
  font-weight: 400;
  line-height: 1;
}

.drag-temporal .s01-inner-ring {
  /* Diameter = 2 × the tick-seat radius, so the ring line passes exactly through
   * where every tick bar meets it. Shares --s01-ring-radius with the ticks — the
   * old 45cqw/234px base drifted onto a different circle than the ticks. */
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--s01-ring-radius) * 2);
  aspect-ratio: 1;
  border: 1px solid var(--tp-rule-strong);
  border-radius: 50%;
  translate: -50% -50%;
  box-shadow:
    inset 0 0 0 clamp(8px, 2.5cqw, 14px) rgba(237, 232, 224, 0.012),
    0 0 40px rgba(201, 161, 108, 0.05);
}

.drag-temporal .s01-center-number {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2.2ch;
  height: 0.9em;
  overflow: hidden;
  color: var(--tp-ink);
  font-family: var(--tp-font-display);
  font-size: clamp(52px, 17cqw, 92px);
  font-weight: 600;
  line-height: 0.82;
  letter-spacing: 0;
  translate: -50% -50%;
}

.drag-temporal .s01-center-number__value,
.drag-temporal .s01-center-number > cv-animate {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
}

.drag-temporal .s01-center-number__value {
  display: block;
}

.drag-temporal .s01-hands {
  pointer-events: none;
}

/* RunningHand renders this real chain (act-1 redesign — it is now FIVE boxes deep, not
 * three, because the hand needs two nested framework lanes: an 880ms opacity fade on the
 * outside and the 3000ms calibration sweep inside it. One lane cannot do both, since the
 * drag driver lerps every property by a single localProgress and ignores `times`):
 *
 *   .s01-hands
 *     > cv-animate            outer opacity lane (no infinite → no anon wrapper)
 *       > .s01-hand-sweep            plain span, the seam between the two lanes
 *         > cv-animate        sweep lane (rotate; HAS loopAnimation)
 *           > anonymous motion.div   the infinite lane's own layer (live wall-clock spin)
 *             > .s01-hand-pivot
 *               > .s01-hand
 *
 * EVERY box in that chain must fill the dial, and the reason is the same at each level: a
 * rotate is applied about the box's own centre, so a box that shrink-wraps around a 1px
 * hand spins the hand around a point that is not the dial centre — the historical "gold
 * hand orbits a point above the dial" defect. The previous selector stopped at
 * `> cv-animate > * > .s01-hand-pivot`, a depth that described the OLD three-box
 * chain; with the sweep layer inserted, that third step now lands on the inner
 * `cv-animate` and the pivot rule silently stopped matching. Listing the chain
 * explicitly rather than with a descendant selector keeps that failure visible: if the
 * depth changes again, the selector stops matching and the hand visibly mis-pivots, which
 * is preferable to a `*` that always matches and quietly styles the wrong node. */
.drag-temporal .s01-hands > cv-animate,
.drag-temporal .s01-hands .s01-hand-sweep,
.drag-temporal .s01-hands .s01-hand-sweep > cv-animate,
.drag-temporal .s01-hands .s01-hand-sweep > cv-animate > *,
.drag-temporal .s01-hands .s01-hand-sweep > cv-animate > * > .s01-hand-pivot,
.drag-temporal .s01-hands .s01-hand-minute-offset > cv-animate,
.drag-temporal .s01-hands .s01-hand-minute-offset > cv-animate > *,
.drag-temporal .s01-hands .s01-hand-minute-offset > cv-animate > * > .s01-hand-pivot {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
}

/* Pivot layer: fills the dial, centered, so a rotate written by framer-motion
 * spins around the dial center. This carries the DRAG-driven offset only. The
 * continuous running-hand spin lives on the bar inside (see below), so the two
 * compose: drag lean + never-stopping tick. */
.drag-temporal .s01-hand-pivot {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
  pointer-events: none;
}

.drag-temporal .s01-hand {
  position: absolute;
  bottom: 50%;
  left: 50%;
  display: block;
  border-radius: var(--tp-radius-pill);
  transform: translateX(-50%);
  transform-origin: 50% 100%;
}

/* Static geometry only. The continuous running tick is NOT a CSS loop: it is the
 * framework's infinite lane (<Animate loopAnimation> in SceneRolling), which
 * the framework stops via shouldRunInfinite when the scene leaves its phase. A
 * CSS `animation: … infinite` here would keep spinning through exit — the
 * "everything faded but the hand kept moving" bug. See rule 6 in AGENTS.md.
 * reduced-motion is handled in the component (loopAnimation → undefined). */
.drag-temporal .s01-hand--second {
  width: 1px;
  height: 22cqw;
  max-height: 114px;
  background: var(--tp-accent);
  box-shadow: 0 0 8px var(--tp-accent-glow);
}

.drag-temporal .s01-hand--minute {
  width: 2px;
  height: 15cqw;
  max-height: 78px;
  background: var(--tp-ink-soft);
}

.drag-temporal .s01-hands__pin {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  width: clamp(7px, 2cqw, 11px);
  aspect-ratio: 1;
  border: 1px solid var(--tp-accent-soft);
  border-radius: 50%;
  background: var(--tp-bg);
  box-shadow: 0 0 0 var(--tp-space-1) rgba(201, 161, 108, 0.08);
  translate: -50% -50%;
}

.drag-temporal .s01-title-area {
  position: relative;
  display: grid;
  justify-items: center;
  width: min(88cqw, 458px);
  text-align: center;
  flex: 0 0 auto;
}

/* `.s01-subtitle` is gone with the lane that rendered it (act 1 no longer carries a
 * subtitle — the dial IS the explanation). Its own block below was deleted with it. */
.drag-temporal .s01-eyebrow,
.drag-temporal .s01-title {
  margin: 0;
}

.drag-temporal .s01-eyebrow {
  color: var(--tp-ink-mute);
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-xs);
  letter-spacing: 0.22em;
}

.drag-temporal .s01-title {
  margin-top: var(--tp-space-2);
  color: var(--tp-ink);
  font-family: var(--tp-font-display);
  font-size: var(--tp-text-2xl);
  font-weight: 600;
  line-height: 0.94;
  letter-spacing: 0;
}

@media (max-width: 320px) {
  .drag-temporal .s01-title {
    font-size: 48px;
  }
  .drag-temporal .s01-eyebrow {
    font-size: 9px;
    letter-spacing: 0;
  }
}

@media (max-width: 230px) {
  .drag-temporal .s01-title {
    font-size: 28px;
  }
  .drag-temporal .s01-eyebrow {
    font-size: 8px;
    max-width: 120px;
  }
  .drag-temporal .s01-actions .tp-btn {
    font-size: 11px;
    letter-spacing: 0;
    padding-inline: 8px;
    line-height: 1.4;
  }
}

.drag-temporal .s01-date {
  position: absolute;
  /* 2026-08-16 用户指令「月份和天数的时间能贴近数字一点」：距中心小时数字的
   * 间距收紧（42/12cqw/64 → 30/10cqw/52）。 */
  top: calc(50% + clamp(30px, 10cqw, 52px));
  left: 50%;
  margin: 0;
  color: var(--tp-accent-soft);
  font-family: var(--tp-font-mono);
  font-size: clamp(10px, 2.8cqw, 14px);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  translate: -50% 0;
  white-space: nowrap;
}

.drag-temporal .s01-actions-band {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(var(--tp-space-3), 3cqw, var(--tp-space-4));
  flex: 0 0 auto;
  width: min(88cqw, 458px);
}

.drag-temporal .s01-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--tp-space-3), 3cqw, var(--tp-space-4));
  flex-wrap: wrap;
}

.drag-temporal .tp-scene--02 {
  /* Scene 02 owns a practical stage, not the calibration axis from act 1. */
  background:
    radial-gradient(ellipse 84% 56% at 50% 43%, rgba(150, 112, 54, 0.12), transparent 64%),
    radial-gradient(ellipse 100% 42% at 50% 102%, rgba(216, 162, 74, 0.09), transparent 64%),
    var(--tp-bg);
}

.drag-temporal .s02-stage {
  position: relative;
  display: grid;
  /* One row: the board. ACTION is not DOM copy — it is drawn as particles inside the canvas,
   * in a band just under the board (locked: 「action 也改为粒子组成，放在板子下面一点」), and
   * the `02 / SLATE` label that used to own an `auto` first row is gone.
   *
   * `minmax(0, 1fr)`, never a width-derived fixed height. This container is
   * `container-type: inline-size`, so `cqw` cannot see viewport height: an earlier
   * `clamp(420px, 138cqw, 580px)` board row overflowed the stage in BOTH directions on short
   * viewports (measured 1280x720: stage row ~602px against ~600+ of content). `minmax(0, 1fr)`
   * makes that impossible rather than unlikely — the canvas fits its art to whatever box it
   * receives (see boxOf), so a shorter row costs board size, never clipping. */
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  padding: clamp(var(--tp-space-3), 4cqw, var(--tp-space-5)) var(--tp-space-4)
    clamp(var(--tp-space-4), 5cqw, var(--tp-space-6));
  overflow: hidden;
  isolation: isolate;
}

/* Scene 02 light rig (see SlateLightRig.tsx for the temporal side).
 *
 * The previous dressing keyed the set with `clip-path: polygon()` + `filter: blur()`.
 * CSS applies the filter FIRST and clips afterwards, so the blur was sliced off along
 * two straight diagonals — the hard edges the review called out. Nothing in this block
 * clips any more: every shape ends in a gradient that reaches full transparency before
 * its own box, so there is no boundary to see.
 *
 * Depth of field is a STATIC blur, graded by distance (scene number furthest / most
 * defocused → stands nearest / almost sharp). Static so the browser rasterises it once;
 * the layers themselves only ever animate opacity/transform. */
.drag-temporal .s02-scene {
  /* Containing block for the scene-level light rig below. */
  position: relative;
}

.drag-temporal .s02-light {
  position: absolute;
  inset: 0;
  /* Scene level, so it spans the full frame with no clip edge inside it. z 0 keeps it under
   * the stage content, which follows it in DOM order, and under the chrome band that other
   * acts still raise to z 46 (.s01-stage / .s03-cut-stage). It used to be z:-1 inside
   * .s02-stage, whose overflow:hidden cut the beam on a visible horizontal line near the top
   * of the stage box. */
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Real chain rendered by <Animate> in drag mode:
 *   .s02-light > cv-animate (drag lane, enter/exit scrub) > .s02-light__layer
 * Every transform-bearing box fills the stage so percentage translations resolve against
 * the scene rather than a shrink-wrapped child. */
.drag-temporal .s02-light > cv-animate,
.drag-temporal .s02-light > cv-animate > *,
.drag-temporal .s02-light > cv-animate > * > .s02-light__layer {
  position: absolute;
  inset: 0;
}

.drag-temporal .s02-light__layer {
  position: absolute;
  inset: 0;
}

/* Far atmosphere: two wide, low-alpha pools plus a soft floor plane, so the void has a
 * gradient and a ground instead of being flat black. Multi-stop falloff, i.e. soft by
 * construction rather than by blur. Dark gold only — the teal wash that used to sit on
 * the left was struck by decree (act 2 is a tungsten set, not a monitor readout). */
.drag-temporal .s02-light__haze {
  background:
    radial-gradient(
      58% 42% at 24% 28%,
      rgba(190, 146, 74, 0.1),
      rgba(190, 146, 74, 0.045) 38%,
      rgba(150, 112, 54, 0.016) 62%,
      transparent 82%
    ),
    radial-gradient(
      66% 46% at 78% 64%,
      rgba(216, 162, 74, 0.09),
      rgba(216, 162, 74, 0.04) 40%,
      rgba(216, 162, 74, 0.012) 64%,
      transparent 84%
    ),
    linear-gradient(
      180deg,
      transparent 58%,
      rgba(154, 146, 135, 0.03) 78%,
      rgba(154, 146, 135, 0.055) 92%,
      rgba(154, 146, 135, 0.02) 100%
    );
}

/* Key beam. A 28deg conic wedge whose apex sits above the frame, with a narrower hot
 * core inside it: the angular stops ramp over several degrees on each side, so the beam
 * has no edge — and a radial mask fades it with distance, so it does not stop at the box
 * either. Two nested wedges (not one) because a single flat wedge reads as a painted
 * triangle; a hot core inside a wider falloff is what makes it read as volume. */
/* Origin sits at the apex above the frame, so the beam's enter scale reads as light reaching
 * down rather than as the painted layer inflating. Scoped to the beam by id. */
.drag-temporal .s02-light > cv-animate[data-cineview-animate-id='s02-light-beam'],
.drag-temporal .s02-light > cv-animate[data-cineview-animate-id='s02-light-beam'] > * {
  transform-origin: 50% -12%;
}

.drag-temporal .s02-light__beam {
  background:
    conic-gradient(
      from 166deg at 50% -12%,
      transparent 0deg,
      rgba(236, 227, 208, 0.055) 5deg,
      rgba(236, 227, 208, 0.115) 11deg,
      rgba(236, 227, 208, 0.115) 17deg,
      rgba(216, 162, 74, 0.05) 23deg,
      transparent 28deg
    ),
    conic-gradient(
      from 174deg at 50% -12%,
      transparent 0deg,
      rgba(236, 227, 208, 0.075) 3deg,
      rgba(255, 248, 232, 0.16) 6deg,
      rgba(236, 227, 208, 0.075) 9deg,
      transparent 12deg
    );
  -webkit-mask-image: radial-gradient(
    92% 104% at 50% -6%,
    #000 10%,
    rgba(0, 0, 0, 0.72) 46%,
    rgba(0, 0, 0, 0.3) 74%,
    transparent 96%
  );
  mask-image: radial-gradient(
    92% 104% at 50% -6%,
    #000 10%,
    rgba(0, 0, 0, 0.72) 46%,
    rgba(0, 0, 0, 0.3) 74%,
    transparent 96%
  );
}

/* `.s02-light__spill` (the lamp-head glow across the top of the frame) is GONE, together
 * with its layer in SlateLightRig. It was measured as the single largest contributor to the
 * reported 内容明显分层 — a bright horizontal stratum with a visible floor under it.
 * Measured on a settled act 2 (390x844, luminance smoothed over a 10px baseline to remove
 * the scanline's own 3px ripple, band = cssY 14-32 vs floor = cssY 44-58):
 *   all layers on ....... band 41.13  floor 33.74  delta 7.39
 *   spill stretched 70% .. band 40.49  floor 35.55  delta 4.94
 *   spill HIDDEN ......... band 29.81  floor 26.05  delta 3.76   <- best
 * Stretching the gradient (the previous fix, kept here for a while) halved the step;
 * deleting the layer beats it outright. Beam + pool already carry the key light, so nothing
 * was lost and the flat-void problem the layer was added for does not return — haze still
 * supplies the gradient and the ground plane.
 *
 * Deleted in the same pass, for the same reason their JSX was: `.s02-light__viewfinder`
 * (plus its ::before / ::after) and `.s02-light__scene-number`. Both NAMED the act instead
 * of lighting it, and they left with the rest of the scene-labelling chrome (HUD, footer,
 * the five `s0N` slates). The scene number's size×blur sweep is archived in the task flow:
 * a landed-frame probe read 0% occlusion at 30-54px while a multi-frame probe read 5.08%,
 * because the binding pose is the board OPEN, not settled. */

/* Key pool: the light the board SITS IN. Flattened radial, zeroed well inside its own box.
 *
 * This centre used to be `at 50% 72%`, which is not the board — it is the ACTION band.
 * Measured on a landed act 2 (390x844): mean row luminance over the word band was 49.18
 * against 45.87 over the board itself, i.e. the brightest thing in the frame was the
 * caption and the subject was in relative shadow. That is the reported 灯光没有指向板子,
 * and it was a coordinate, not a taste question.
 *
 * The board's drawn body spans y≈214-547 css in an 844 frame, so its centre is at ~45%.
 * The pool is aimed there and tightened (58%->52% wide, 14%->16% tall) so the falloff has
 * decayed to nothing before it reaches the word band at 71-77%: ACTION is now lit by
 * spill, which is what a caption under a lit subject should get. */
.drag-temporal .s02-light__pool {
  background: radial-gradient(
    52% 16% at 50% 45%,
    rgba(236, 227, 208, 0.2),
    rgba(236, 227, 208, 0.085) 34%,
    rgba(216, 162, 74, 0.03) 56%,
    transparent 78%
  );
}

/* Out-of-focus lamp highlights closest to the lens. Blur is static (rasterised once);
 * only the parent layer drifts, on the framework's infinite lane. */
.drag-temporal .s02-light__bokeh {
  opacity: 0.9;
}

.drag-temporal .s02-light__mote {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(236, 227, 208, 0.16), transparent 68%);
}

.drag-temporal .s02-light__mote--1 {
  top: 16%;
  left: 11%;
  width: 46px;
  height: 46px;
  filter: blur(7px);
}

.drag-temporal .s02-light__mote--2 {
  top: 63%;
  left: 82%;
  width: 62px;
  height: 62px;
  background: radial-gradient(circle, rgba(216, 162, 74, 0.15), transparent 68%);
  filter: blur(11px);
}

.drag-temporal .s02-light__mote--3 {
  top: 78%;
  left: 24%;
  width: 34px;
  height: 34px;
  filter: blur(6px);
}

.drag-temporal .s02-light__mote--4 {
  top: 34%;
  left: 88%;
  width: 28px;
  height: 28px;
  background: radial-gradient(circle, rgba(190, 146, 74, 0.15), transparent 68%);
  filter: blur(8px);
}

/* Hardware silhouettes — the spatial layer. Defocused as a group (they are metres
 * behind the slate), then graded per element below so the frame reads as having depth
 * rather than as one blurred plate. */
.drag-temporal .s02-light__set {
  filter: blur(1.6px);
}

.drag-temporal .s02-light__stand {
  position: absolute;
  top: 20%;
  width: 1px;
  height: 49%;
  background: linear-gradient(180deg, rgba(154, 146, 135, 0.24), rgba(154, 146, 135, 0.04));
  filter: blur(1.1px);
}

.drag-temporal .s02-light__stand::before {
  position: absolute;
  top: 0;
  left: 50%;
  width: 30px;
  height: 18px;
  border: 1px solid rgba(154, 146, 135, 0.18);
  border-radius: 50%;
  content: '';
  translate: -50% -50%;
}

.drag-temporal .s02-light__stand::after {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 62px;
  height: 1px;
  background: rgba(154, 146, 135, 0.1);
  box-shadow:
    -22px -7px 0 -0.3px rgba(154, 146, 135, 0.08),
    22px -7px 0 -0.3px rgba(154, 146, 135, 0.08);
  content: '';
  translate: -50% 0;
}

.drag-temporal .s02-light__stand--left {
  left: 7%;
  rotate: -3deg;
}

.drag-temporal .s02-light__stand--right {
  right: 7%;
  rotate: 3deg;
}

/* The canvas receives the central budget; copy sits immediately below it rather
 * than being separated by a second flex gap. */
/* The canvas takes the central row. With the copy gone the row would stretch to ~650px
 * while the board itself is width-bound at ~350px, leaving ~150px of dead space above and
 * below it; the max-height keeps the slate close to the ACTION line instead of floating in
 * the middle of an empty column. */
.drag-temporal .s02-stage > cv-animate[data-cineview-animate-id='s02-clapper'] {
  position: relative;
  width: 100%;
  /* Fills the 1fr row. NOT a fixed height any more: `height: clamp(420px, 138cqw, 580px)`
   * was width-derived (see the .s02-stage note above) and overflowed the stage on short
   * viewports, pushing the slate label under the HUD. The canvas fits its own art to
   * whatever box it receives, so height is the row's business, not this element's. */
  height: 100%;
  min-height: 0;
  margin-top: clamp(4px, 1.5cqw, 10px);
}

/* The board now declares an loopAnimation, so <Animate> renders an EXTRA anonymous
 * motion.div between cv-animate and .s02-clapper (see Animate.tsx's drag branch).
 * That wrapper shrink-wraps: with an absolutely-positioned canvas inside it, its height
 * resolves to 0, the canvas gets no box, and the percentage translate on the idle lane
 * resolves against 0px so the drift moves nothing. Same defect already documented for
 * .s02-light and .s04-streams — every transform-bearing box in the chain must fill the
 * holder. */
.drag-temporal .s02-stage > cv-animate[data-cineview-animate-id='s02-clapper'] > * {
  position: absolute;
  inset: 0;
}

.drag-temporal .s02-clapper {
  position: absolute;
  inset: 0;
}

.drag-temporal .s02-clapper-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Scene 04 — 日全食. The background timecode columns scroll on the framework infinite
 * lane (see TimeStreams); the eclipse arc, its limb glow and the headline digits all
 * consume Cineview-owned MotionValues. Geometry and layout live here; every act-04
 * COLOUR lives in temporal-scenes-03-05.css, which is the act's single palette owner —
 * the two files used to declare `.tp-scene--04` twice and the values drifted apart. */
.drag-temporal .s04-stage {
  --s04-lens-center-y: 58%;
  --s04-clock-radius: min(42cqw, 22svh, 235px);

  position: relative;
  min-height: 0;
  overflow: hidden;
}

.drag-temporal .s04-streams {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.drag-temporal .s04-stream {
  position: absolute;
  top: -10%;
  display: grid;
  gap: var(--tp-space-3);
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-2xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  white-space: nowrap;
  will-change: transform;
}

/* The framework renders TWO nested nodes per <Animate>: the cv-animate host
 * and, inside it, an anonymous div carrying the infinite lane's transform. That inner
 * div is position:static with no inset, so it shrink-wraps — and because .s04-stream
 * below is position:absolute, it wraps to height 0. `translateY(-50%)` of 0 is 0px, so
 * the scroll loop ran every frame and moved the columns exactly nowhere: measured
 * innerH=0 and a translateY span of 0.00px across 2.4s on all three columns. That is
 * the "背景动画不动了" regression, and it appeared the moment the loop moved off
 * .s04-stream (which had height) onto the framework lane.
 * Same trap, same fix as the act-1 hands at .s01-hand-pivot above: force the transform
 * layer to fill the host so the percentage has a box to resolve against.
 *
 * The descendant selector (not a child one) is load-bearing after the 2026-07-29 rework:
 * each column is now TWO nested lanes — an outer `s04-stream-rise-*` carrying the
 * enterAnimation rise (y 12% -> 0) and an inner `s04-stream-*` carrying the infinite
 * scroll (y 0 -> -50%) — because both animate `y` and a single node cannot host two
 * writers. That is four transform-bearing boxes deep, and EVERY one of them needs a box:
 * a percentage `y` on any shrink-wrapped ancestor resolves to 0px and the lane animates
 * nothing at all. */
.drag-temporal .s04-streams cv-animate,
.drag-temporal .s04-streams cv-animate > * {
  position: absolute;
  inset: 0;
}

/* Scroll loop moved to the framework infinite lane (<Animate loopAnimation>
 * in TimeStreams) so it pauses when scene 04 leaves its phase. POSITION ONLY here.
 * See rule 6 in AGENTS.md / CLAUDE.md.
 *
 * Colour lives in temporal-scenes-03-05.css, and only there. It used to be declared in
 * BOTH files — teal here, green there — with the later import silently winning. That is a
 * drift trap: recolouring the act (2026-07-29: teal -> night fluorescent green) would have
 * left a dead teal triple behind that looks authoritative in this file. One owner per
 * property. The three columns still carry three DIFFERENT alphas, which is where the
 * depth reading comes from; the alphas are part of the colour declaration, so they moved
 * with it. */
.drag-temporal .s04-stream--left {
  left: 6%;
}

.drag-temporal .s04-stream--center {
  left: 50%;
  translate: -50% 0;
}

.drag-temporal .s04-stream--right {
  right: 6%;
}

/* Vertical falloff so the columns fade out at the top and bottom edges instead of
 * being clipped mid-glyph by the stage's overflow, and so they read as depth behind
 * the centre readout rather than a flat wall of text. */
.drag-temporal .s04-streams {
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 18%, #000 82%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 18%, #000 82%, transparent 100%);
}

.drag-temporal .s04-center {
  position: absolute;
  top: var(--s04-lens-center-y);
  left: 50%;
  z-index: 2;
  display: grid;
  justify-items: center;
  width: min(92cqw, 478px);
  translate: -50% -50%;
}

.drag-temporal .s04-caption {
  position: absolute;
  bottom: calc(100% - var(--s04-lens-center-y) + var(--s04-clock-radius) + clamp(24px, 5cqw, 38px));
  left: 50%;
  z-index: 3;
  display: grid;
  justify-items: center;
  gap: clamp(6px, 1.4cqw, 10px);
  width: min(92cqw, 478px);
  translate: -50% 0;
}

.drag-temporal .s04-caption > cv-animate {
  display: grid;
  place-items: center;
  width: 100%;
}

.drag-temporal .s04-title {
  margin: 0 0 clamp(10px, 2.5cqw, 18px);
  color: var(--tp-ink);
  font-family: var(--tp-font-display);
  font-size: var(--tp-text-md);
  font-weight: 400;
  line-height: 1.3;
  text-align: center;
  text-wrap: balance;
}

/* `.s04-equation` shared this rule until the eclipse redesign removed the three-line
 * 「时间 = 位移 × 系数」 lane from the act. The label is the only mono line left in the
 * centre column. */
.drag-temporal .s04-label {
  margin: 0;
  color: var(--tp-ink-mute);
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-xs);
  letter-spacing: 0.22em;
  text-align: center;
}

.drag-temporal .s04-clock {
  position: relative;
  display: grid;
  place-items: center;
  width: calc(var(--s04-clock-radius) + var(--s04-clock-radius));
  aspect-ratio: 1;
}

.drag-temporal .s04-clock > cv-animate {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

/* `.s04-ring` / `.s04-ring__track` / `.s04-ring__progress` are gone with the plain
 * progress ring: act 04 is an eclipse now, and its arc/disc/limb live on `.s04-eclipse*`
 * in temporal-scenes-03-05.css next to the rest of the act's paint. */

.drag-temporal .s04-main-timecode {
  display: flex;
  align-items: baseline;
  justify-content: center;
  color: var(--tp-accent);
  font-family: var(--tp-font-mono);
  font-size: clamp(30px, 10.8cqw, 58px);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  letter-spacing: -0.06em;
  text-shadow: 0 0 24px var(--tp-accent-glow);
  white-space: nowrap;
}

.drag-temporal .s04-main-timecode > span {
  display: inline-block;
  min-width: 0.58em;
  text-align: center;
}

.drag-temporal .s04-main-timecode > span:nth-child(3n) {
  min-width: 0.34em;
  color: var(--tp-accent-soft);
}

/* DELETED with act 04's redesign:
 *   - `.s04-equation*`   — the 「拖拽 x scale = 元素时间」 explainer lane. The lane and its
 *                          copy are gone from SceneFlux; the ring is the only progress
 *                          statement the act makes now.
 *   - `.s04-tickbar*`    — the 20-tick bottom ruler plus its follower marker. `TickBar.tsx`
 *                          was deleted outright (设计档 §3.4): a ruler beside a ring states
 *                          progress twice.
 *
 * DELETED with act 05's redesign (its type now lives in `.s05-curtain-call` /
 * `.s05-credit*` / `.s05-the-end` in temporal-scenes-03-05.css):
 *   - `.s05-center`      — the old centred CUT column.
 *   - `.s05-cut-title`   — the 「CUT」 hero, replaced by the credits block.
 *   - `.s05-accent-line` / `.s05-actions` / `.s05-sprocket*` / `.s05-leader*` — the CTA and
 *                          reel-leader dressing that left with the act's rewrite. `.tp-btn`
 *                          SURVIVES: act 01's actions band still uses it. */

/* Base box for act 05's stage. The act's own paint (auditorium, beam, credits) is in
 * temporal-scenes-03-05.css, which also re-declares this selector to add `perspective`. */
.drag-temporal .s05-stage {
  position: relative;
  min-height: 0;
  overflow: hidden;
}

/* Only the family lives here; act 05's own block sets every other property (and is the
 * later of the two files, so it wins where they overlap). */
.drag-temporal .s05-the-end {
  font-family: var(--tp-font-mono);
}

.drag-temporal .tp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: clamp(112px, 31cqw, 150px);
  min-height: 44px;
  padding: var(--tp-space-3) var(--tp-space-5);
  border-radius: var(--tp-radius-pill);
  font-family: var(--tp-font-mono);
  font-size: var(--tp-text-xs);
  line-height: 1;
  letter-spacing: 0.08em;
  text-decoration: none;
  transition:
    color 0.3s var(--tp-ease-out),
    border-color 0.3s var(--tp-ease-out),
    background 0.3s var(--tp-ease-out),
    transform 0.3s var(--tp-ease-out);
}

.drag-temporal .tp-btn:hover,
.drag-temporal .tp-btn:focus-visible {
  transform: translateY(-2px);
}

.drag-temporal .tp-btn--ghost {
  border: 1px solid var(--tp-rule-strong);
  color: var(--tp-ink-soft);
  background: transparent;
}

.drag-temporal .tp-btn--ghost:hover,
.drag-temporal .tp-btn--ghost:focus-visible {
  border-color: var(--tp-accent);
  color: var(--tp-accent);
}

.drag-temporal .tp-btn--primary {
  /* 2026-08-15 用户看过 rec 实物后翻案，CTA 回归 amber（A-3 裁决 (a) 的红实底 +
   * glow 撤销，恢复 amber 时代原写法）。rec 信号色保留为契约冷极/终局的文档化
   * 角色，暂无消费者。 */
  border: 1px solid var(--tp-accent);
  color: var(--tp-bg);
  background: var(--tp-accent);
}

.drag-temporal .tp-btn--primary:hover,
.drag-temporal .tp-btn--primary:focus-visible {
  border-color: var(--tp-accent-soft);
  background: var(--tp-accent-soft);
}

/* s04-stream-up / tp-rec-pulse keyframes removed: those loops now ride the
 * framework's infinite lane (<Animate loopAnimation> in TimeStreams) so
 * they stop when the scene leaves its phase. See rule 6.
 * (RecBadge 及其 tp-rec* 规则已随 2026-08-15 主题审计 A-3 删除——零消费者死代码；
 * 2026-08-15 用户翻案后 CTA 也回归 amber，rec 色仅存于 :root 文档化 token。) */

@media (max-width: 430px) {
  /* The centre column stays VISIBLE at phone widths. Hiding it (the previous rule)
   * removed the middle of the three depth planes exactly at the 390px acceptance
   * viewport, so the three-column depth this act's background is built on could never
   * be seen in review — the design was being judged on two columns. It is not a
   * crowding risk: it is the faintest of the three and sits behind the eclipse and the
   * readout. Narrow it instead of deleting it. */
  .drag-temporal .s04-stream--center {
    font-size: clamp(7px, 1.9cqw, 9px);
    opacity: 0.72;
  }
}

@media (prefers-reduced-motion: reduce) {
  .drag-temporal,
  .drag-temporal *,
  .drag-temporal *::before,
  .drag-temporal *::after {
    animation: none !important;
    transition-delay: 0s !important;
    transition-duration: 0.08s !important;
  }
}

/* ── Texture layer (v3 §3): film grain + scanlines + vignette. Sits above the
 * Cineview scenes as a pointer-events:none overlay inside .drag-temporal.
 * Strength = "明显" (grain 0.11, deeper vignette, stronger scanline). */
/* ── Ambient light layer (2026 rework). Sits ABOVE the opaque scenes but below
 * the grain texture, mix-blend-mode:screen so it only ADDS light — dark pixels
 * contribute nothing (center type stays crisp) while empty voids fill with a
 * drifting key light, a slow projector-ray sweep, and floating dust. Tint rides
 * the inherited --tp-sig from the active scene wrapper. Pure decoration. */
.drag-temporal .s01-scene {
  position: relative;
  isolation: isolate;
}

.drag-temporal .s01-scene > cv-animate[data-cineview-animate-id='s01-ambient'] {
  position: absolute;
  inset: 0;
  z-index: 45;
  pointer-events: none;
  transform-origin: 50% 100%;
}

.drag-temporal .tp-ambient {
  position: absolute;
  inset: 0;
  mix-blend-mode: screen;
  /* NO `overflow: hidden` here, deliberately. It used to clip this box, and once the
   * sink transform (scale 0.96 + y 7%) was applied the clip edge itself pulled in from
   * the viewport — on a screen-blended layer that rendered as a hard bright line
   * sliding down the frame (measured 19.6 → 57.6 luminance across 4 device px at the
   * top edge). Nothing needs clipping: every child overscans by 20% and its gradients
   * fade to transparent well before their own edges, so there is no hard boundary to
   * hide. Keeping the box unclipped is also why the sink can ride an inner layer
   * without rescaling each child's percentage coordinate space. */
  /* Fallback tint if --tp-sig hasn't cascaded (e.g. root); amber resting pole. */
  --tp-ambient-sig: var(--tp-sig, var(--tp-sig-amber));
}

/* The sink transform rides THIS layer rather than .tp-ambient, so the blend/stacking
 * context above stays untransformed and the light is the only thing that moves.
 * Viewport-sized on purpose: the children position themselves in % against it, so
 * resizing this box would shift the whole lighting geometry. */
.drag-temporal .tp-ambient__rig {
  position: absolute;
  inset: 0;
  will-change: transform, opacity;
}

.drag-temporal .tp-ambient__rig > cv-animate,
.drag-temporal .tp-ambient__rig > cv-animate > * {
  position: absolute;
  inset: 0;
}

/* Key light: a large soft off-center cone breathing in intensity, the dominant
 * source. Positioned upper-left so it rakes across the frame like a lamp. */
.drag-temporal .tp-ambient__key {
  position: absolute;
  inset: -20%;
  background: radial-gradient(
    ellipse 60% 55% at 32% 22%,
    color-mix(in srgb, var(--tp-ambient-sig) 55%, transparent) 0%,
    color-mix(in srgb, var(--tp-ambient-sig) 20%, transparent) 32%,
    transparent 62%
  );
}

/* Fill light: a cooler, dimmer counter-source lower-right so the frame isn't lit
 * from one side only. Breathes out of phase with the key. */
.drag-temporal .tp-ambient__fill {
  position: absolute;
  inset: -20%;
  background: radial-gradient(
    ellipse 55% 50% at 74% 82%,
    color-mix(in srgb, var(--tp-sig-warm) 22%, transparent) 0%,
    color-mix(in srgb, var(--tp-sig-warm) 8%, transparent) 34%,
    transparent 60%
  );
}

/* Projector rays: two thin volumetric beams sweeping slowly across the frame,
 * evoking light through a dusty projector booth. */
.drag-temporal .tp-ambient__ray {
  position: absolute;
  top: -30%;
  left: 8%;
  width: 42%;
  height: 160%;
  background: linear-gradient(
    102deg,
    transparent 42%,
    color-mix(in srgb, var(--tp-ambient-sig) 16%, transparent) 49%,
    color-mix(in srgb, var(--tp-sig-warm) 22%, transparent) 50%,
    color-mix(in srgb, var(--tp-ambient-sig) 16%, transparent) 51%,
    transparent 58%
  );
  filter: blur(6px);
  transform-origin: top center;
}

.drag-temporal .tp-ambient__ray--2 {
  left: 52%;
  width: 34%;
  opacity: 0.7;
}

.drag-temporal .tp-ambient__dust {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.drag-temporal .tp-texture {
  position: absolute;
  inset: 0;
  z-index: 50;
  pointer-events: none;
}

.drag-temporal .tp-texture > cv-animate {
  position: absolute;
  /* The largest grain translation is 3% of this box. An 8% margin covers it
   * without allocating a texture four times the visible scene area on phones. */
  inset: -8%;
}

.drag-temporal .tp-texture > cv-animate > *,
.drag-temporal .tp-texture > cv-animate > * > .tp-texture__grain {
  position: absolute;
  inset: 0;
}

.drag-temporal .tp-texture__grain {
  position: absolute;
  inset: -8%;
  opacity: 0.11;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.drag-temporal .tp-texture > cv-animate .tp-texture__grain {
  inset: 0;
}

.drag-temporal .tp-texture__scanline {
  position: absolute;
  inset: 0;
  /* Softened (2026 N1): 0.7 stacked with the grain read as a muddy CRT haze over
   * the whole frame; 0.4 keeps the scanline texture without dulling content. */
  opacity: 0.4;
  background: repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.03) 0 1px, transparent 1px 3px);
}

.drag-temporal .tp-texture__vignette {
  position: absolute;
  inset: 0;
  /* Softened (2026 N1): the old rgba(0,0,0,0.72) terminal stop, sitting at z:50
   * over chrome at z:10, crushed the corner/footer labels to near-black. Pushed
   * the transparent core out to 58% and the edge stop down to 0.46 so the frame
   * still reads cinematic but the corners stay legible. */
  background: radial-gradient(
    ellipse 120% 92% at 50% 42%,
    transparent 58%,
    rgba(0, 0, 0, 0.46) 100%
  );
}

/* `.s04-context` (act 04's explanatory paragraph) and `.s05-leader` / `.s05-leader__frame`
 * (act 05's reel-leader countdown strip above the old CUT hero) were deleted here: both
 * acts were rewritten and neither element is rendered any more. They were the only rules
 * left in this file after the texture keyframes, and being physically separated from their
 * own acts is part of why they outlived them. */


/* ---- temporal-scenes-03-05.css ---- */

/* Scene-specific finish layer for acts 03–05.
 *
 * Split out from temporal-drag.css so the three rebuilt acts can be read as units. Every
 * continuous motion below lives on an <Animate loopAnimation> lane or inside a canvas
 * that gates its own rAF on `timeline.phase`; this file only supplies geometry and paint.
 *
 * Two rules govern most of what looks unusual here, both from the 2026-07-29 task flow:
 *
 *  §1.5 rule 4 — an <Animate> that declares `loopAnimation` renders an EXTRA anonymous
 *    motion.div between `cv-animate` and the authored child. That wrapper is
 *    position:static with no inset, so it shrink-wraps; around absolutely-positioned content
 *    it wraps to height 0 and every percentage translate on the loop resolves to 0px — the
 *    animation runs and moves nothing. Every such holder below is given
 *    `position: absolute; inset: 0`.
 *
 *  §8.4 — a `mix-blend-mode: screen` layer must never be clipped. Once a transform is applied
 *    the clip edge itself pulls in from the viewport and renders as a hard bright line
 *    sliding across the frame (measured 19.6 -> 57.6 luminance over 4 device px). The
 *    additive layers here (act 04's corona, act 05's beam) therefore carry NO
 *    `overflow: hidden` and no `clip-path`; they are shaped by soft gradient masks whose
 *    ramps span whole degrees/percents, so there is no boundary to see in the first place.
 */

/* ── Act 03 · 剪辑台 (the cutting room) ───────────────────────────────────────
 *
 * REWRITTEN with the act. The five taped-up code strips are gone and so is every rule that
 * dressed them: `.s03-bench` / `.s03-slot` / `.s03-scatter` / `.s03-strip*` (tape, label,
 * code) / `.s03-timeline-bed` / `.s03-final-shell` / `.s03-track-cluster*` /
 * `.s03-track-label` / `.s03-clip--amber|--short|--long` / `.s03-copy` / `.s03-eyebrow` /
 * `.s03-title` / `.s03-body` / `.s03-signoff` / the `s03-copy-gate` holder. Their JSX no
 * longer exists (SceneSync.tsx is a full rewrite), so they were dead rules, not a fallback.
 *
 * The act is now one object — an edit bay — and the DOM is:
 *
 *   main.s03-cut-stage
 *     [s03-preview]  section.s03-preview        surface / two letterbox bars / PGM tag
 *     section.s03-bed
 *       [s03-ruler]  div.s03-ruler              three timecode marks
 *       div.s03-tracks
 *         [s03-track-v1] .s03-track-row > label + .s03-track   > 3 x .s03-clip-slot
 *         [s03-track-a1] .s03-track-row > label + .s03-track--audio > canvas
 *         [s03-track-v2] .s03-track-row > label + .s03-track   > 3 x .s03-clip-slot
 *       [s03-playhead] .s03-playhead > .s03-playhead__rail > beam + readout
 */
.drag-temporal .s03-scene > cv-animate[data-cineview-animate-id='s03-media-clock'],
.drag-temporal .s03-scene > cv-animate[data-cineview-animate-id='s03-media-clock'] > .s03-cut-stage {
  position: absolute;
  inset: 0;
}

.drag-temporal .s03-cut-stage {
  position: relative;
  z-index: 46;
  min-height: 0;
  padding: 0;
  overflow: hidden;
  background: #050506;
}

/* The picture is the stage, not a monitor inside it. The timeline is composited over the
 * lower edge, so the video remains a true viewport-sized plane at every aspect ratio. */
.drag-temporal .s03-cut-stage > cv-animate[data-cineview-animate-id='s03-preview'] {
  position: absolute;
  inset: 0;
  z-index: 0;
  min-width: 0;
  min-height: 0;
}

.drag-temporal .s03-preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #050506;
}

/* Full-plate mount for the site-owned preview video. */
.drag-temporal .s03-preview__surface {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.drag-temporal .s03-preview__surface > video,
.drag-temporal .s03-preview__surface > cv-animate[data-cineview-animate-id='s03-preview-media'],
.drag-temporal .s03-preview__surface > cv-animate[data-cineview-animate-id='s03-preview-media'] > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  background: #050506;
}

.drag-temporal .s03-preview__subtitles {
  position: absolute;
  right: 0;
  bottom: clamp(226px, 31vh, 320px);
  left: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: 0 clamp(18px, 6cqw, 42px);
  pointer-events: none;
}

.drag-temporal .s03-preview__subtitles > span {
  grid-area: 1 / 1;
  max-width: min(82cqw, 760px);
  color: #fff;
  font-family: var(--tp-font-mono);
  font-size: clamp(16px, 4.4cqw, 34px);
  line-height: 1;
  letter-spacing: 0.08em;
  text-align: center;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
  opacity: 0;
}

/* The reveal selector list must cover EVERY index rendered by SceneSync's V2_SUBTITLES
 * (currently five). The 2026-08-18 five-clip rework extended the i18n keys and the DOM to
 * five but left these lists at three, so PACE/TIMING never lit on the picture
 * (user report 2026-08-20: 「v2前三个会出现字母后面两个不会」). Coverage of both lists
 * below is pinned by sceneSync.motion-contract.test.tsx against V2_IDS. */
.drag-temporal .s03-cut-stage[data-active-media='0'] [data-s03-preview-subtitle='0'],
.drag-temporal .s03-cut-stage[data-active-media='1'] [data-s03-preview-subtitle='1'],
.drag-temporal .s03-cut-stage[data-active-media='2'] [data-s03-preview-subtitle='2'],
.drag-temporal .s03-cut-stage[data-active-media='3'] [data-s03-preview-subtitle='3'],
.drag-temporal .s03-cut-stage[data-active-media='4'] [data-s03-preview-subtitle='4'] {
  opacity: 1;
}

/* Letterbox bars: the widescreen framing inside the monitor. Static geometry. */
.drag-temporal .s03-preview__bar {
  display: none;
}

.drag-temporal .s03-preview__bar--top {
  top: 0;
}

.drag-temporal .s03-preview__bar--bottom {
  bottom: 0;
}

.drag-temporal .s03-preview__tag {
  position: absolute;
  top: clamp(9px, 2.6cqw, 14px);
  left: clamp(8px, 2.4cqw, 13px);
  z-index: 4;
  /* 2026-08-16 用户还原指令「我就是这么设计的」：撤销 2026-08-15 主题审计 A-5
   * 的 alpha 抬档与字号下限改动，回到设计原值。 */
  color: rgba(232, 183, 102, 0.72);
  font-family: var(--tp-font-mono);
  font-size: clamp(7px, 1.8cqw, 9px);
  letter-spacing: 0.16em;
  text-shadow: 0 0 10px rgba(216, 162, 74, 0.2);
}

/* ── Timeline bed: ruler, three tracks, one playhead across all of them. ──── */
.drag-temporal .s03-bed {
  position: absolute;
  right: clamp(10px, 2.8cqw, 24px);
  bottom: clamp(10px, 2.8cqw, 24px);
  left: clamp(10px, 2.8cqw, 24px);
  z-index: 4;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(8px, 2.4cqw, 14px);
  min-height: 0;
  margin: 0;
  padding: clamp(8px, 2.4cqw, 14px) clamp(8px, 2.4cqw, 13px) clamp(26px, 7cqw, 38px);
  border: 1px solid rgba(216, 162, 74, 0.2);
  border-radius: 0;
  background:
    linear-gradient(90deg, rgba(216, 162, 74, 0.08) 1px, transparent 1px) 0 0 / 12.5% 100%,
    linear-gradient(180deg, rgba(18, 15, 19, 0.88), rgba(8, 7, 10, 0.96));
  backdrop-filter: blur(8px);
  box-shadow:
    0 16px 44px rgba(0, 0, 0, 0.28),
    inset 0 0 0 1px rgba(255, 255, 255, 0.028);
}

/* `position: relative` is what the sub-second tick strip below hangs off. */
.drag-temporal .s03-ruler {
  position: relative;
  display: flex;
  justify-content: space-between;
  padding-top: clamp(7px, 2cqw, 11px);
  border-top: 1px solid rgba(216, 162, 74, 0.4);
  color: rgba(232, 183, 102, 0.6);
  font-family: var(--tp-font-mono);
  font-size: clamp(7px, 1.9cqw, 10px);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  text-shadow: 0 0 12px rgba(216, 162, 74, 0.14);
}

/* Sub-second ticks hanging off the ruler's own rule. `4.16%` is 1/24 of the bed, i.e. one
 * tick per two frames at the act's 25fps base — dense enough to read as a scale. */
.drag-temporal .s03-ruler::before {
  position: absolute;
  top: -5px;
  right: 0;
  left: 0;
  height: 8px;
  background: repeating-linear-gradient(
    90deg,
    rgba(232, 183, 102, 0.46) 0 1px,
    transparent 1px 4.16%
  );
  content: '';
}

.drag-temporal .s03-ruler {
  position: relative;
}

.drag-temporal .s03-ruler__mark {
  display: block;
}

.drag-temporal .s03-tracks {
  display: grid;
  /* WIDTH-derived, not `1fr`. Under `repeat(3, minmax(0,1fr))` the three lanes split whatever
   * height the viewport had left — measured 181.6px each at 844 tall, a 1.73:1 box that reads
   * as a slab rather than a track, and one that grows taller on every taller screen while the
   * cqw-sized detail inside it does not. Deriving the lane height from the same ruler as its
   * own borders, labels and sprockets keeps the strip proportional at every viewport height:
   * ~46.8px at 390 wide, i.e. about 6.7:1. The clamp floor/ceiling bound it on extreme widths.
   * §8.5 still holds: the clamp's 30px floor is what a short viewport collapses to, and the
   * slack now lives in the monitor's `1fr` row on the stage, which is free to shrink. */
  grid-template-rows: repeat(3, clamp(30px, 12cqw, 56px));
  gap: clamp(6px, 1.8cqw, 10px);
  min-height: 0;
}

/* The track bases animate a UNIFORM `scale` (SceneSync's note: `scaleX` is not one of the
 * drag driver's ten properties and is dropped silently). Origin at the left edge is what
 * turns that uniform scale into "the lane widens rightward" — without it the row inflates
 * about its own centre and reads as a zoom. Set on the host AND on any wrapper inside it,
 * because the transform can be written on either depending on lane composition. */
.drag-temporal .s03-tracks > cv-animate[data-cineview-animate-id^='s03-track-'],
.drag-temporal .s03-tracks > cv-animate[data-cineview-animate-id^='s03-track-'] > * {
  min-height: 0;
  transform-origin: left center;
}

.drag-temporal .s03-track-row {
  display: grid;
  grid-template-columns: clamp(18px, 5cqw, 26px) minmax(0, 1fr);
  align-items: center;
  gap: clamp(6px, 1.8cqw, 10px);
  height: 100%;
  min-height: 0;
}

.drag-temporal .s03-track-row__label {
  color: rgba(232, 183, 102, 0.66);
  font-family: var(--tp-font-mono);
  font-size: clamp(7px, 1.9cqw, 10px);
  letter-spacing: 0.1em;
  text-shadow: 0 0 10px rgba(216, 162, 74, 0.16);
}

/* The lane itself. `position: relative` is load-bearing: every clip and subtitle inside is
 * an absolutely-positioned slot whose left/width are AUTHORED FRACTIONS of this box
 * (--s03-slot-left / --s03-slot-width, written by SceneSync). That is what keeps a clip's
 * position independent of any flex/grid measurement — no JS layout read anywhere. */
.drag-temporal .s03-track {
  position: relative;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  border: 1px solid rgba(216, 162, 74, 0.26);
  border-radius: 0;
  background: rgba(9, 8, 11, 0.7);
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.26),
    inset 0 0 0 1px rgba(255, 255, 255, 0.022);
}

.drag-temporal .s03-track--audio {
  background: rgba(7, 6, 9, 0.78);
}

.drag-temporal .s03-clip-slot {
  position: absolute;
  top: clamp(3px, 0.9cqw, 5px);
  bottom: clamp(3px, 0.9cqw, 5px);
  left: var(--s03-slot-left, 0%);
  width: var(--s03-slot-width, 10%);
}

/* Fill every nested clip/subtitle Animate holder so absolute children keep the slot footprint. */
.drag-temporal .s03-clip-slot cv-animate,
.drag-temporal .s03-clip-slot cv-animate > * {
  position: absolute;
  inset: 0;
}

.drag-temporal .s03-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border: 1px solid rgba(177, 189, 202, 0.32);
  border-radius: 0;
  background: rgba(177, 189, 202, 0.12);
}

.drag-temporal .s03-clip__select {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: block;
  border: 1px solid rgba(240, 198, 126, 0.92);
  border-radius: 0;
  background: rgba(232, 183, 102, 0.2);
  box-shadow:
    0 0 0 1px rgba(240, 198, 126, 0.42),
    0 0 16px rgba(216, 162, 74, 0.34),
    inset 0 0 12px rgba(232, 183, 102, 0.16);
  pointer-events: none;
}

/* The grab handles on the selected block's two edges. Inside the clip's own `overflow:
 * hidden` box, so they read as part of the block rather than as decoration floating
 * beside it. */
.drag-temporal .s03-clip__handle {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 2;
  display: block;
  width: clamp(2px, 0.7cqw, 3px);
  background: rgba(247, 226, 186, 0.94);
  box-shadow: 0 0 8px rgba(232, 183, 102, 0.6);
}

.drag-temporal .s03-clip__handle--left {
  left: 0;
}

.drag-temporal .s03-clip__handle--right {
  right: 0;
}

/* Clip picture box. */
.drag-temporal .s03-clip__surface {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.drag-temporal .s03-clip__surface > video,
.drag-temporal .s03-clip__surface > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.drag-temporal .s03-clip__poster {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The perforation strip along the clip's bottom edge — what marks the block as film rather
 * than as a generic rectangle. */
.drag-temporal .s03-clip__sprockets {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: block;
  height: clamp(3px, 1cqw, 5px);
  background: repeating-linear-gradient(
    90deg,
    rgba(199, 209, 220, 0.34) 0 2px,
    transparent 2px 7px
  );
}

/* The waveform lane: opacity-only, and the canvas draws its own reveal from the lane's
 * progress. The holder has to fill the track or the canvas gets a zero box. */
.drag-temporal .s03-track--audio > cv-animate[data-cineview-animate-id='s03-waveform'],
.drag-temporal .s03-track--audio > cv-animate[data-cineview-animate-id='s03-waveform'] > * {
  position: absolute;
  inset: 0;
}

.drag-temporal .s03-wave-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* V2 subtitle blocks.
 *
 * 返工:「v2 把模糊去掉。」These carried `filter: blur(2.4px)` on the word, authored against
 * 设计档 §3.3「模糊不可辨风格」 — subtitle blocks seen on a timeline rather than copy to read.
 * The words are legible now; that design note is superseded for this track.
 *
 * NOTE for anyone re-adding a blur here: it must stay a STATIC property on the word and never
 * an animated `filter`. `filter` IS one of the drag driver's ten animatable properties, so an
 * animated one on this subtree composes with the lane's own transform stack and the two fight.
 * That is why the blur lived on the inner span and not on a lane in the first place. */
.drag-temporal .s03-subtitle {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 1px solid rgba(216, 162, 74, 0.24);
  border-radius: 0;
  background: rgba(216, 162, 74, 0.1);
  opacity: 0.34;
}

/* Same coverage rule as the picture-subtitle list above: one entry per V2_SUBTITLES index —
 * the active segment's track block lights up. Extending SceneSync's subtitle count without
 * extending this list leaves the new blocks permanently dim (the exact 2026-08-20 defect). */
.drag-temporal .s03-cut-stage[data-active-media='0'] [data-s03-subtitle-index='0'],
.drag-temporal .s03-cut-stage[data-active-media='1'] [data-s03-subtitle-index='1'],
.drag-temporal .s03-cut-stage[data-active-media='2'] [data-s03-subtitle-index='2'],
.drag-temporal .s03-cut-stage[data-active-media='3'] [data-s03-subtitle-index='3'],
.drag-temporal .s03-cut-stage[data-active-media='4'] [data-s03-subtitle-index='4'] {
  border-color: rgba(240, 198, 126, 0.78);
  background: rgba(216, 162, 74, 0.22);
  opacity: 1;
}

.drag-temporal .s03-subtitle__word {
  color: rgba(243, 231, 213, 0.82);
  font-family: var(--tp-font-body);
  font-size: clamp(9px, 2.6cqw, 13px);
  line-height: 1;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

/* ── Playhead ──────────────────────────────────────────────────────────────
 * `Act3Media` writes inherited `--s03-play` from the preview's single transport time.
 * Nothing here reads it as `left`: a `left` write invalidates layout every
 * frame, so the offset is consumed as a `translate` on a BED-WIDTH box. That is the whole
 * reason the rail exists — a percentage translate on the 1px line itself would resolve
 * against 1px.
 *
 * `translate` (the standalone property), not `transform`: framer-motion owns `transform` on
 * these holders, and the two compose in the spec's fixed order (translate, then transform)
 * instead of overwriting each other. */
.drag-temporal .s03-bed > cv-animate[data-cineview-animate-id='s03-playhead'] {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.drag-temporal .s03-playhead {
  position: absolute;
  inset: 0;
}

.drag-temporal .s03-playhead__rail {
  position: absolute;
  /* Spans the three tracks and reaches into the bed's bottom gutter, where the readout
   * sits. Inset horizontally by the bed's own padding so 0% and 100% of --s03-play line up
   * with the tracks' own edges. */
  top: clamp(28px, 8cqw, 40px);
  right: clamp(8px, 2.4cqw, 13px);
  bottom: clamp(4px, 1.2cqw, 7px);
  left: calc(clamp(8px, 2.4cqw, 13px) + clamp(18px, 5cqw, 26px) + clamp(6px, 1.8cqw, 10px));
}

/* The beam holder is bed-width and carries the horizontal offset; the breath scale rides
 * the anonymous infinite wrapper INSIDE it, so a pulsing scale can never displace the
 * head — it only breathes the line's own thickness and glow. */
.drag-temporal .s03-playhead__rail > cv-animate[data-cineview-animate-id='s03-playhead-breath'] {
  position: absolute;
  inset: 0;
  translate: var(--s03-play, 0%) 0;
}

.drag-temporal .s03-playhead__rail > cv-animate[data-cineview-animate-id='s03-playhead-breath'] > * {
  position: absolute;
  inset: 0;
  transform-origin: left center;
}

.drag-temporal .s03-playhead__beam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: block;
  width: 1px;
}

.drag-temporal .s03-playhead__line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: block;
  width: 1px;
  background: linear-gradient(
    180deg,
    transparent,
    rgba(232, 183, 102, 0.72) 10%,
    rgba(232, 183, 102, 0.5) 88%,
    transparent
  );
  box-shadow: 0 0 12px rgba(216, 162, 74, 0.22);
}

/* The diamond marker at the top of the line — the grab handle shape a deck's playhead has. */
.drag-temporal .s03-playhead__head {
  position: absolute;
  top: 0;
  left: 50%;
  display: block;
  width: 7px;
  height: 7px;
  border: 1px solid rgba(232, 183, 102, 0.66);
  background: rgba(20, 15, 11, 0.94);
  box-shadow: 0 0 10px rgba(216, 162, 74, 0.24);
  translate: -50% -50%;
  rotate: 45deg;
}

/* The readout rides the same offset on its own bed-width holder, so the digits cannot drift
 * away from the line they describe. */
.drag-temporal .s03-playhead__rail > cv-animate[data-cineview-animate-id='s03-timecode'] {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  translate: var(--s03-play, 0%) 0;
}

.drag-temporal .s03-playhead__readout {
  position: absolute;
  bottom: 0;
  left: clamp(4px, 1.4cqw, 7px);
  translate: calc(-1 * var(--s03-play, 0%)) 0;
  color: rgba(232, 183, 102, 0.78);
  font-family: var(--tp-font-mono);
  font-size: clamp(8px, 2.1cqw, 11px);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  white-space: nowrap;
  text-shadow: 0 0 12px rgba(216, 162, 74, 0.22);
}

/* ── The seam element (stage 2 of the clip demo) ───────────────────────────
 * Lives in the TRACK, not in a clip slot: it belongs to the boundary between two blocks
 * rather than to either one. Positioned by the same two custom properties the slots use
 * (--s03-slot-left / --s03-slot-width, written by ClipDemoSeam) so the seam's geometry is
 * stated in the same units as the clips whose overlap it marks.
 *
 * `z-index` is load-bearing, not decoration: the seam is the last child of the track and
 * has to paint OVER the two blocks it sits between, or a 6% band under an opaque clip is
 * invisible no matter what its own lane does. */
.drag-temporal .s03-seam {
  position: absolute;
  top: clamp(3px, 0.9cqw, 5px);
  bottom: clamp(3px, 0.9cqw, 5px);
  left: var(--s03-slot-left, 25%);
  z-index: 3;
  width: var(--s03-slot-width, 6%);
  pointer-events: none;
}

/* The lane holder. `> *` is deliberately NOT here any more, and that is a real change rather
 * than a tidy-up: the badge lane used to declare `loopAnimation`, which makes <Animate>
 * render an extra anonymous motion.div (this file's header, §1.5 rule 4) that needed its own
 * box. The lane is now a plain enter (one stroke, held), so there is exactly one node and
 * `.s03-seam__band` is its direct child — already `inset: 0` on its own rule below. */
.drag-temporal .s03-seam cv-animate {
  position: absolute;
  inset: 0;
}

/* The `opacity: 0` PRE-START RULE THAT USED TO SIT HERE IS DELETED, and it had to be: with the
 * infinite wrapper gone, `.s03-seam cv-animate > *` no longer matched an anonymous
 * motion.div — it matched `.s03-seam__band` itself. Nothing writes inline opacity onto the band
 * (the drag lane writes the HOST), so the rule would have pinned every badge invisible for the
 * whole act, with no error anywhere: three lanes running correctly on an element CSS had
 * switched off. The old rule existed only because the loop-driven host had to stay at opacity 1;
 * a plain enter lane animates its host 0 → 1 directly, so the pre-start state is the lane's
 * `initial` and needs no CSS at all. */

/* The highlight over the shared band: a cross-dissolve X drawn as two opposing ramps, so
 * the overlap reads as one clip handing over to the other rather than as a lit rectangle. */
.drag-temporal .s03-seam__band {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-left: 1px solid rgba(232, 183, 102, 0.62);
  border-right: 1px solid rgba(232, 183, 102, 0.62);
  background:
    linear-gradient(90deg, rgba(232, 183, 102, 0.34), transparent 82%),
    linear-gradient(270deg, rgba(232, 183, 102, 0.34), transparent 82%), rgba(216, 162, 74, 0.16);
  box-shadow: 0 0 14px rgba(216, 162, 74, 0.3);
}

/* The transition marker a cross-dissolve carries on a timeline. Sized off the track's own
 * ruler (cqw) like every other detail in the bed, so it stays proportional at every
 * viewport width instead of being a fixed glyph in a width-derived lane. */
.drag-temporal .s03-seam__mark {
  color: rgba(247, 226, 186, 0.94);
  font-family: var(--tp-font-mono);
  font-size: clamp(8px, 2.4cqw, 12px);
  line-height: 1;
  text-shadow: 0 0 10px rgba(216, 162, 74, 0.55);
}

/* ── Act 04 · Optical shutter ────────────────────────────────────────────────
 * Layered after the supplied photographic lens: black barrel, cool coated glass, steel
 * leaves, and restrained teal/cyan reflections. The canvas owns only moving blade paint;
 * all static lens material lives here.
 * 2026-08-15 主题审计 A-1 裁决 (a)：原蓝紫家族（#72bfff hue≈207° + 紫伴随）收编回
 * teal（hue≈187°，契约冷极 --tp-sig-teal #409ba8 的提亮/加深档）——「No blue, no
 * magenta」重新成立，五幕色温行程恢复 amber×3 → teal(4) → amber(5)。 */
.drag-temporal .tp-scene--04 {
  --tp-sig: #5cb8c9;
  --tp-sig-glow: rgba(92, 184, 201, 0.34);
  --tp-accent: #a8dce4;
  --tp-accent-soft: #4f8d99;
  --tp-accent-dim: rgba(79, 141, 153, 0.18);
  --tp-accent-glow: rgba(64, 155, 168, 0.32);
  background:
    radial-gradient(circle at 50% 47%, rgba(23, 74, 82, 0.18), transparent 38%),
    radial-gradient(circle at 38% 28%, rgba(64, 155, 168, 0.08), transparent 30%), #03080a;
}

/* Background columns. Alphas unchanged (0.16 / 0.11 / 0.21) — depth comes from the three
 * columns sitting at different alphas, and that relationship is what was tuned. Only the
 * hue moves (blue→teal; the violet center column folds into a deeper teal step). */
.drag-temporal .s04-stream--left {
  color: rgba(92, 184, 201, 0.16);
}

.drag-temporal .s04-stream--center {
  color: rgba(45, 120, 132, 0.1);
}

.drag-temporal .s04-stream--right {
  color: rgba(92, 184, 201, 0.2);
}

.drag-temporal .s04-clock {
  isolation: isolate;
}

/* Stacking inside the clock: eclipse disc/arc at the bottom and the iris above it.
 *
 * The z 2 rule is CORRECTNESS, not cosmetics: `.s04-eclipse__disc` is a 94%-opaque near-black
 * circle covering exactly the barrel area, and the ring's own lane is z 1 — a lane left at
 * `auto` loses to 1 regardless of document order, so the iris would be drawn and then hidden.
 * It has to live here, keyed by animate-id, because `Animate` exposes no style/className for
 * its wrapper and that wrapper is a `motion.div` carrying the drag transform stack — a
 * stacking context, so an inner z-index is trapped inside it. */
.drag-temporal .s04-clock > cv-animate[data-cineview-animate-id='s04-progress-ring'] {
  z-index: 1;
}

.drag-temporal .s04-clock > cv-animate[data-cineview-animate-id='s04-aperture'] {
  z-index: 2;
}

/* The eclipse itself. `.s04-halo*` (wash / core / breath / orbit / progress-arc) is gone:
 * that was the old progress ring's five-layer glow stack, and the act no longer has a
 * progress ring. */
.drag-temporal .s04-eclipse-shell,
.drag-temporal .s04-eclipse {
  position: absolute;
  inset: 0;
}

.drag-temporal .s04-eclipse-shell {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 22px 24px rgba(0, 0, 0, 0.54));
}

.drag-temporal .s04-lens__knurl,
.drag-temporal .s04-lens__barrel,
.drag-temporal .s04-lens__inner-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  display: block;
  aspect-ratio: 1;
  border-radius: 50%;
  translate: -50% -50%;
}

.drag-temporal .s04-lens__knurl {
  width: 98%;
  background: repeating-conic-gradient(#a7abb3 0 1deg, #1a1c20 1deg 2.4deg);
  -webkit-mask-image: radial-gradient(circle, transparent 0 91%, #000 91.5% 100%);
  mask-image: radial-gradient(circle, transparent 0 91%, #000 91.5% 100%);
  filter: drop-shadow(0 0 1px rgba(255, 255, 255, 0.38));
}

.drag-temporal .s04-lens__barrel {
  width: 94%;
  border: clamp(11px, 3cqw, 17px) solid #090a0d;
  background: radial-gradient(circle, transparent 0 64%, #111319 65% 72%, #050609 73% 100%);
  box-shadow:
    inset 0 0 0 2px rgba(198, 205, 220, 0.3),
    inset 0 0 0 clamp(8px, 2.1cqw, 12px) #191c22,
    inset 0 0 0 clamp(12px, 3cqw, 18px) #050609,
    0 0 0 2px #343840,
    0 0 0 5px #090a0d;
}

.drag-temporal .s04-lens__inner-ring {
  width: 79%;
  border: clamp(7px, 1.8cqw, 11px) solid rgba(80, 91, 109, 0.9);
  background: transparent;
  box-shadow:
    0 0 0 2px rgba(7, 9, 15, 0.96),
    0 0 0 clamp(9px, 2.2cqw, 13px) rgba(18, 22, 31, 0.98),
    inset 0 0 12px rgba(92, 184, 201, 0.24);
}

/* Coated glass behind the blades. The moving canvas roots sit at 72% diameter, leaving the
 * barrel rings visible around it. */
.drag-temporal .s04-eclipse__disc {
  position: absolute;
  top: 50%;
  left: 50%;
  display: block;
  width: 72%;
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 24%, rgba(140, 214, 226, 0.7), transparent 24%),
    radial-gradient(circle at 62% 66%, rgba(21, 128, 143, 0.72), transparent 44%),
    radial-gradient(circle, #0a3a44 0, #062a31 58%, #020c0e 100%);
  box-shadow:
    inset 0 0 52px rgba(0, 10, 12, 0.78),
    0 0 30px rgba(64, 155, 168, 0.22);
  translate: -50% -50%;
}

/* The limb: the hot inner edge that lights up as totality approaches. The framework lane
 * owns opacity and scale; this holder owns the exact ring geometry the old inline transform
 * acted on. A ring, not a disc: the glow blooms outward and leaves the readout clean. */
.drag-temporal .s04-eclipse > cv-animate[data-cineview-animate-id='s04-eclipse-limb'] {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 73%;
  aspect-ratio: 1;
  translate: -50% -50%;
  transform-origin: center;
  will-change: opacity, transform;
}

.drag-temporal .s04-eclipse__limb {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid rgba(168, 220, 228, 0.58);
  border-radius: 50%;
  box-shadow:
    0 0 24px 3px rgba(92, 184, 201, 0.36),
    0 0 52px 10px rgba(64, 155, 168, 0.14);
}

.drag-temporal .s04-eclipse__arc-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* The arc's drop-shadow reaches past the viewBox; `visible` keeps SVG from cutting it. */
  overflow: visible;
  /* Start the dash at 12 o'clock and run clockwise, so the shadow's leading edge travels
   * the way the finger pushes it. */
  rotate: -90deg;
}

.drag-temporal .s04-eclipse__arc {
  fill: none;
  stroke: #a8dce4;
  stroke-width: 1.25;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px rgba(92, 184, 201, 0.66));
}

/* Canvas and optical overlay share the exact clock box. Neither is clipped: the pure geometry
 * keeps every blade inside its 36% barrel radius, and the DOM reflections fade within it. */
.drag-temporal .s04-aperture-shell {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.drag-temporal .s04-aperture-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.drag-temporal .s04-lens-reflections {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.drag-temporal .s04-lens-reflection,
.drag-temporal .s04-lens__mark {
  position: absolute;
  display: block;
}

.drag-temporal .s04-lens-reflection--broad {
  top: 22%;
  left: 25%;
  width: 47%;
  height: 17%;
  border-radius: 58% 42% 54% 46%;
  background: linear-gradient(
    105deg,
    rgba(214, 240, 244, 0.62),
    rgba(120, 200, 212, 0.2) 64%,
    transparent
  );
  filter: blur(2px);
  rotate: -10deg;
}

.drag-temporal .s04-lens-reflection--strip {
  top: 27%;
  left: 31%;
  width: 38%;
  height: 7%;
  border-radius: 50%;
  background: repeating-linear-gradient(
    180deg,
    rgba(224, 246, 248, 0.28) 0 2px,
    transparent 2px 6px
  );
  filter: blur(0.6px);
  rotate: -9deg;
}

.drag-temporal .s04-lens-reflection--flare {
  top: 19%;
  left: 46%;
  width: 17%;
  height: 30%;
  /* 原 pink flare（magenta 系）→ warm 白（tp-sig-warm 族）：teal 镀膜上的一点暖
   * 反射，与「No magenta」契约一致且与 amber 主极呼应。 */
  background: linear-gradient(120deg, rgba(236, 227, 208, 0.28), transparent 72%);
  filter: blur(3px);
  rotate: -18deg;
}

.drag-temporal .s04-lens__mark {
  z-index: 3;
  color: rgba(198, 205, 220, 0.5);
  font-family: var(--tp-font-mono);
  font-size: clamp(6px, 1.5cqw, 9px);
  letter-spacing: 0.12em;
  text-shadow: 0 1px 0 #000;
  white-space: nowrap;
}

.drag-temporal .s04-lens__mark--top {
  top: 11.5%;
  left: 50%;
  translate: -50% 0;
}

.drag-temporal .s04-lens__mark--left {
  top: 50%;
  left: 8.5%;
  rotate: -82deg;
  translate: -50% -50%;
}

.drag-temporal .s04-lens__mark--right {
  top: 50%;
  right: 10%;
  rotate: 84deg;
  translate: 50% -50%;
}

.drag-temporal .s04-main-timecode {
  position: relative;
  z-index: 3;
  color: #d7eef2;
  text-shadow:
    0 0 12px rgba(92, 184, 201, 0.52),
    0 0 28px rgba(64, 155, 168, 0.24);
}

.drag-temporal .s04-main-timecode > cv-animate {
  display: inline-block;
  min-width: 0.58em;
  text-align: center;
}

.drag-temporal .s04-main-timecode > cv-animate:nth-child(3n) {
  min-width: 0.34em;
  color: var(--tp-accent-soft);
}

.drag-temporal .s04-main-timecode [data-timecode-char] {
  display: inline-block;
}

/* ── Act 05 · Curtain call ──────────────────────────────────────────────────
 *
 * DELETED with the act's rewrite, because their JSX is gone: `.s05-kicker` (FINAL CUT),
 * `.s05-title` / `.s05-title__word` / `.s05-title__char`, `.s05-spotlight*`, `.s05-floor`,
 * and all auditorium screen/wall/seat rules. The final frame is intentionally unframed:
 * projector light and dust behind the curtain-call type.
 *
 * The beam is painted behind the type; there is no intervening panel or room geometry. */
.drag-temporal .tp-scene.tp-scene--05 {
  --tp-sig: #e1a45b;
  --tp-sig-glow: rgba(225, 164, 91, 0.34);
  --tp-accent: #e1a45b;
  --tp-accent-soft: #eac18d;
  --tp-accent-dim: rgba(225, 164, 91, 0.17);
  --tp-accent-glow: rgba(225, 164, 91, 0.32);
  /* 2026-08-13 黑幕退场整改（task-flow 2026-08-13-act5-black-exit-bg-follow）：
   * 本幕黑色渐变背景迁出到 .drag-temporal 根级的 `.tp-act5-black` 层（temporal-drag.css），
   * 本场景背景改透明。理由：场景背景画在 DragSceneStack 的场景框上，退场 settle 补间
   * 会把整框 translate3d 滑出屏幕下方 ⇒ 黑幕被「时间轴动画」带着下移，而用户要的是
   * 「黑幕只淡出、不下移」。黑层挂在根上不随任何框平移，退场只做 opacity 变化。
   * 层叠安全（对抗验收修正）：选择器 `.tp-scene.tp-scene--05` 提特异性到 0,3,0，
   * 压过 temporal-drag.css 的 lit-room 基座 `.drag-temporal .tp-scene`（0,2,0）——
   * 透明覆盖**不依赖文件导入顺序**（初版 0,2,0 同级选择器曾被验收判为
   * 「胜负由导入顺序决定」）；scene 5 也已从 03/05 共享背景组移除。 */
  background: transparent;
}

.drag-temporal .s05-stage {
  position: relative;
  min-height: 0;
  overflow: hidden;
}

/* ── Projector beam ────────────────────────────────────────────────────────
 * Origin at the projection port: the enter scale reads as the beam THROWING further into
 * the room rather than as a box inflating. Must be on the holder, since that is the box
 * framer-motion transforms. */
.drag-temporal .s05-stage > cv-animate[data-cineview-animate-id='s05-beam'] {
  position: absolute;
  inset: 0;
  z-index: 2;
  transform-origin: 50% 100%;
}

.drag-temporal .s05-stage > cv-animate[data-cineview-animate-id='s05-copy-exit'] {
  position: absolute;
  inset: 0;
  z-index: 3;
}

/* Additive light, and therefore UNCLIPPED (§8.4). The wedge shape comes from a conic MASK
 * whose transparent→opaque ramp spans 20 degrees on each side — a soft boundary by
 * construction, so there is no edge to render as a bright line when the layer is scaled. */
.drag-temporal .s05-beam {
  position: absolute;
  inset: 0;
  mix-blend-mode: screen;
  -webkit-mask-image: conic-gradient(
    from -30deg at 50% 100%,
    transparent 0deg,
    #000 20deg,
    #000 40deg,
    transparent 60deg
  );
  mask-image: conic-gradient(
    from -30deg at 50% 100%,
    transparent 0deg,
    #000 20deg,
    #000 40deg,
    transparent 60deg
  );
  pointer-events: none;
}

/* The hot lens at the beam origin. */
.drag-temporal .s05-beam__port {
  position: absolute;
  bottom: 0;
  left: 50%;
  display: block;
  width: clamp(26px, 7cqw, 40px);
  height: clamp(8px, 2.2cqw, 12px);
  border-radius: 50%;
  background: radial-gradient(
    ellipse,
    rgba(255, 244, 224, 0.5),
    rgba(225, 164, 91, 0.14) 56%,
    transparent 82%
  );
  filter: blur(3px);
  translate: -50% 30%;
}

/* The volumetric throw. Two nested conic wedges (a hot core inside a wider falloff) plus a
 * radial distance mask — the same construction as act 2's key beam, and for the same reason:
 * a single flat wedge reads as a painted triangle, while a hot core inside a soft falloff
 * reads as volume. Every stop ends in full transparency before the box, so the layer has no
 * boundary of its own. */
.drag-temporal .s05-beam__cone {
  position: absolute;
  inset: 0;
  display: block;
  background:
    conic-gradient(
      from -22deg at 50% 100%,
      transparent 0deg,
      rgba(225, 164, 91, 0.05) 8deg,
      rgba(255, 225, 178, 0.1) 18deg,
      rgba(255, 225, 178, 0.1) 26deg,
      rgba(225, 164, 91, 0.05) 36deg,
      transparent 44deg
    ),
    conic-gradient(
      from -10deg at 50% 100%,
      transparent 0deg,
      rgba(255, 236, 202, 0.09) 4deg,
      rgba(255, 248, 232, 0.16) 10deg,
      rgba(255, 236, 202, 0.09) 16deg,
      transparent 20deg
    );
  -webkit-mask-image: radial-gradient(
    120% 104% at 50% 100%,
    #000 8%,
    rgba(0, 0, 0, 0.7) 48%,
    rgba(0, 0, 0, 0.26) 76%,
    transparent 97%
  );
  mask-image: radial-gradient(
    120% 104% at 50% 100%,
    #000 8%,
    rgba(0, 0, 0, 0.7) 48%,
    rgba(0, 0, 0, 0.26) 76%,
    transparent 97%
  );
}

/* Dust track. 200% TALL, and that is what makes the loop seamless: ProjectorBeam renders two
 * stacked copies of the same scatter and translates by -50%, so copy 2 lands exactly where
 * copy 1 began. The percentage resolves against the animated box's OWN height, so the
 * holder and the anonymous infinite wrapper inside it must both be 200% tall — a
 * shrink-wrapped wrapper would make `-50%` mean 0px and the dust would sit still. */
.drag-temporal .s05-beam > cv-animate[data-cineview-animate-id='s05-beam-dust'] {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 200%;
}

.drag-temporal .s05-beam > cv-animate[data-cineview-animate-id='s05-beam-dust'] > * {
  position: absolute;
  inset: 0;
}

.drag-temporal .s05-beam__dust {
  position: absolute;
  inset: 0;
}

.drag-temporal .s05-beam__mote {
  position: absolute;
  display: block;
  border-radius: 50%;
  background: rgba(255, 244, 224, 0.9);
}

.drag-temporal .s05-curtain-call {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 16px;
  overflow: hidden;
  padding: 24px;
  text-align: center;
  pointer-events: none;
}

.drag-temporal .s05-credit,
.drag-temporal .s05-the-end {
  margin: 0;
}

.drag-temporal .s05-credits {
  display: grid;
  gap: clamp(9px, 2.4cqw, 14px);
  width: min(82cqw, 620px);
  max-width: 100%;
}

.drag-temporal .s05-credit {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  align-items: baseline;
  gap: clamp(8px, 2.2cqw, 14px);
  color: rgba(243, 231, 213, 0.92);
  text-align: left;
}

.drag-temporal .s05-credit > span {
  color: rgba(234, 193, 141, 0.64);
  font-family: var(--tp-font-mono);
  font-size: 12px;
  letter-spacing: 0;
  text-align: right;
  text-transform: uppercase;
}

.drag-temporal .s05-credit > strong {
  font-family: var(--tp-font-display);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.2;
}

.drag-temporal .s05-the-end {
  margin-top: clamp(6px, 1.6cqw, 10px);
  color: #f3e7d5;
  font-family: var(--tp-font-mono);
  font-size: 16px;
  letter-spacing: 0;
  text-shadow: 0 0 20px rgba(225, 164, 91, 0.35);
}

@media (max-width: 230px) {
  .drag-temporal .s05-curtain-call {
    gap: 4px;
    padding: 8px;
  }

  .drag-temporal .s05-credits {
    width: 100%;
    gap: 3px;
    margin: 0;
  }

  .drag-temporal .s05-credit {
    grid-template-columns: 1fr;
    gap: 1px;
    text-align: center;
  }

  .drag-temporal .s05-credit > span {
    font-size: 7px;
    line-height: 1;
    text-align: center;
  }

  .drag-temporal .s05-credit > strong {
    font-size: 10px;
    line-height: 1.05;
    text-align: center;
  }

  .drag-temporal .s05-the-end {
    margin-top: 0;
    font-size: 12px;
  }
}

@media (max-width: 480px) {
  .drag-temporal .s05-credit > strong {
    font-size: 16px;
  }
  .drag-temporal .s05-curtain-call {
    padding: 16px;
  }
}

@media (max-height: 720px) {
  .drag-temporal .s03-cut-stage {
    padding-top: clamp(18px, 5cqw, 32px);
  }

  .drag-temporal .s05-curtain-call {
    gap: 5px;
  }

  .drag-temporal .s05-credits {
    gap: 8px;
    margin-top: 5px;
  }

  .drag-temporal .s05-the-end {
    margin-top: 6px;
  }
}
