/* ==========================================================================
   幕启之后 — 舞台作品集主页
   状态由 JS 写在 .theatre 上：
     data-state      closed | opening | open
     data-desk-zoom  存在时表示正在推镜到书桌
   推镜相关的 --desk-* 变量也由 JS 写在 .theatre 上，向下继承。
   ========================================================================== */

:root {
  color-scheme: dark;
  --ink: #f9ebcf;
  --gold: #e4af59;
  --black: #16090b;
  --serif: "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --sans: "Avenir Next", Avenir, "Segoe UI", sans-serif;
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-curtain: cubic-bezier(.76, 0, .24, 1);
  --curtain-time: 1080ms; /* main.js 读取此值，等帷幕拉开后切到 open */
}

* { box-sizing: border-box; }

html, body { width: 100%; height: 100%; margin: 0; }

body {
  overflow: hidden;
  background: var(--black);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

button { font: inherit; color: inherit; }

button:focus-visible, a:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 5px;
}

/* ---------- 舞台容器 ---------- */

.theatre {
  position: relative;
  isolation: isolate;
  width: 100%;
  height: 100svh;
  min-height: 520px;
  overflow: hidden;
  background: radial-gradient(ellipse at 50% 58%, #431619 0, #210d10 48%, #110709 100%);
  cursor: crosshair;
  touch-action: pan-y;
}

.theatre[data-state="closed"] { cursor: grab; }
.theatre[data-state="closed"]:active { cursor: grabbing; }

/* 上下暗角 */
.theatre::after {
  position: absolute;
  z-index: 2;
  inset: 0;
  content: "";
  pointer-events: none;
  background: linear-gradient(180deg, rgba(16, 6, 8, .34), transparent 24%, transparent 74%, rgba(13, 5, 7, .45));
}

/* ---------- 镜头（舞台插画） ---------- */

.camera-wrap {
  position: absolute;
  z-index: 1;
  inset: 0;
  overflow: hidden;
  opacity: var(--desk-camera-opacity, 1);
}

/* 推镜时以书桌为中心向外压黑 */
.camera-wrap::after {
  position: absolute;
  z-index: 8;
  inset: 0;
  content: "";
  pointer-events: none;
  background: radial-gradient(ellipse at var(--desk-focus-x, 18%) var(--desk-focus-y, 69%), transparent 0 34%, rgba(0, 0, 0, .28) 48%, rgba(0, 0, 0, .86) 78%, #000 100%);
  opacity: var(--desk-blackout, 0);
}

.camera {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: 100%;
  transform: translate(calc(-50% + var(--desk-pan-x, 0px)), calc(-50% + var(--desk-pan-y, 0px))) scale(var(--desk-scale, 1));
  will-change: transform;
}

.camera img {
  pointer-events: none;
  -webkit-user-drag: none;
}

.stage-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- 可动画的角色 ----------
   位置、大小和帧数（--frame-count）由 assets/characters/<角色>/actor.css 提供（构建脚本生成）。
   平时显示静态的第一帧 img；播放时 main.js 在 canvas 上逐帧绘制，并加上 .is-playing。 */

.actor {
  position: absolute;
  z-index: 1;
  pointer-events: none;
}

.actor img,
.actor canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.actor canvas { visibility: hidden; }
.actor.is-playing canvas { visibility: visible; }
.actor.is-playing img { visibility: hidden; }

/* ---------- 书桌特写 ---------- */

.desk-scene {
  position: absolute;
  z-index: 1;
  inset: 0;
  background: #000;
  opacity: var(--desk-image-opacity, 0);
  pointer-events: none;
}

/* 特写图按原图比例（1672 × 941）铺满，和舞台插画的 .camera 一致 */
.desk-frame {
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100vw, 177.67vh);
  aspect-ratio: 1672 / 941;
  transform: translate(-50%, -50%);
}

.desk-frame img { display: block; width: 100%; height: 100%; }

/* 桌上可点的物件（书本、花瓶）：推镜完成（data-desk-ready）后才能点，平时轻轻呼吸发光提示可以点 */
.desk-prop {
  position: absolute;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: radial-gradient(ellipse at 50% 55%, rgba(255, 214, 140, .42), transparent 72%);
  opacity: 0;
  cursor: pointer;
  pointer-events: none;
  transition: opacity 300ms ease;
}

.desk-prop--books      { left: 48.9%; top: 48.1%; width: 8.5%; height: 9.1%; }
.desk-prop--books-side { left: 60.4%; top: 53.4%; width: 6.8%; height: 3.9%; }
.desk-prop--vase       { left: 30.8%; top: 40.9%; width: 8%;   height: 16.5%; }

.theatre[data-desk-ready] .desk-prop {
  pointer-events: auto;
  animation: book-glow 2.6s ease-in-out infinite;
}

.theatre[data-desk-ready] .desk-prop:is(:hover, :focus-visible) { opacity: 1; animation: none; }
.theatre[data-desk-ready] .desk-prop--vase { animation-delay: -1.3s; }   /* 和书本错开呼吸 */

@keyframes book-glow {
  0%, 100% { opacity: 0; }
  50% { opacity: .6; }
}

.desk-hint {
  position: absolute;
  left: 50%;
  bottom: max(30px, env(safe-area-inset-bottom));
  margin: 0;
  color: rgba(255, 226, 181, .78);
  font-size: var(--t-label);
  letter-spacing: .2em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 600ms ease;
}

.theatre[data-desk-ready] .desk-hint { opacity: 1; }

/* ---------- 简历线索板 ----------
   一比一按效果图 animation/cv-board/reference.webp 排版：所有元素用效果图的像素坐标（1672 × 941）
   写在 --x/--y/--w/--h 上，--u 是“效果图 1px 在当前板子上有多宽”，整块板按宽度等比缩放。
   卡片按到板上的弹跳、红线拉出来的动画由 main.js 的 CvBoard 驱动。 */

.cv-board {
  --wine: #6b1a22;
  --ink: #2b2220;
  --thread: #b3141e;
  --marker: "Permanent Marker", "Marker Felt", cursive;
  --slab: "Alfa Slab One", "Rockwell", Georgia, serif;
  --hand: "Kalam", "Bradley Hand", cursive;
  --round: "Nunito", "Avenir Next", sans-serif;
  position: absolute;
  z-index: 40;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 16px;
  overflow: auto;
}

.cv-board[hidden] { display: none; }

.cv-board__backdrop {
  position: fixed;
  inset: 0;
  background: radial-gradient(ellipse at 50% 45%, rgba(40, 14, 10, .82), rgba(5, 2, 3, .95));
  opacity: 0;
  transition: opacity 500ms ease;
}

.cv-board.is-open .cv-board__backdrop { opacity: 1; }

/* 板子 + 下面一排按钮；板子按 1672:941 等比，宽度受屏幕高度限制，保证一屏放得下 */
.cv-board__stage {
  position: relative;
  width: min(100%, calc((100svh - 104px) * 1672 / 941));
  display: grid;
  gap: 14px;
}

.cv-board__cork {
  --u: calc(100cqw / 1672);
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1672 / 941;
  overflow: hidden;
  border-radius: var(--r-sm);
  background: url("assets/cv/props/cork.webp") 0 0 / 360px 360px, #96562f;
  box-shadow: 0 40px 90px rgba(0, 0, 0, .6), inset 0 0 70px rgba(40, 18, 6, .55);
}

/* 子元素用容器单位，--u 需要在容器里面重新计算 */
.cv-board__cork > * { --u: calc(100cqw / 1672); }

/* 按效果图坐标摆放 */
.cv-card,
.cv-polaroid,
.cv-prop,
.cv-tape {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u));
  height: calc(var(--h) * var(--u));
  margin: 0;
}

.cv-prop { z-index: 1; object-fit: fill; pointer-events: none; }
.cv-prop--duomo { z-index: 4; }
.cv-prop--magnifier { z-index: 7; filter: drop-shadow(0 10px 14px rgba(20, 8, 2, .45)); }

/* ---- 卡片：撕边的纸 ---- */

.cv-note {
  --tilt: 0deg;
  z-index: 3;
  transform: rotate(var(--tilt));
  transition: transform 300ms var(--ease-out), filter 300ms ease;
  filter: drop-shadow(0 calc(3 * var(--u)) calc(3 * var(--u)) rgba(30, 12, 4, .35)) drop-shadow(0 calc(12 * var(--u)) calc(16 * var(--u)) rgba(30, 12, 4, .3));
}

.cv-note:hover,
.cv-note:focus-within {
  z-index: 6;
  transform: rotate(var(--tilt)) translateY(calc(-6 * var(--u))) scale(1.015);
  filter: drop-shadow(0 calc(5 * var(--u)) calc(4 * var(--u)) rgba(30, 12, 4, .35)) drop-shadow(0 calc(24 * var(--u)) calc(28 * var(--u)) rgba(30, 12, 4, .38));
}

.cv-card__paper {
  height: 100%;
  padding: calc(26 * var(--u)) calc(34 * var(--u));
  clip-path: var(--torn);
  background: url("assets/cv/props/paper.webp") 0 0 / 256px 256px, #f2eadb;
  color: var(--ink);
  font-family: var(--round);
  font-size: max(12px, calc(18 * var(--u)));
  line-height: 1.35;
}

.cv-card--grid .cv-card__paper {
  background:
    linear-gradient(rgba(120, 140, 170, .2) 1px, transparent 1px) 0 0 / calc(20 * var(--u)) calc(20 * var(--u)),
    linear-gradient(90deg, rgba(120, 140, 170, .2) 1px, transparent 1px) 0 0 / calc(20 * var(--u)) calc(20 * var(--u)),
    #f6f3ec;
}

.cv-card--pink .cv-card__paper { padding: calc(12 * var(--u)) calc(28 * var(--u)); background: #f2c4cb; }
.cv-card--name .cv-card__paper { padding: calc(20 * var(--u)) calc(16 * var(--u)); text-align: center; }

/* 胶带 */
.cv-tape {
  z-index: 5;
  background: rgba(236, 206, 128, .82);
  transform: rotate(var(--tilt));
  clip-path: polygon(0 8%, 4% 0, 96% 4%, 100% 0, 98% 50%, 100% 100%, 95% 94%, 4% 100%, 0 92%, 3% 50%);
  box-shadow: 0 1px 2px rgba(60, 40, 10, .2);
  pointer-events: none;
}

/* 红色小图钉、线结 */
.cv-dot {
  position: absolute;
  z-index: 8;
  left: calc((var(--x) - 10) * var(--u));
  top: calc((var(--y) - 10) * var(--u));
  width: calc(20 * var(--u));
  height: calc(20 * var(--u));
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ff7d73, #cf1d26 50%, #7a0b12 100%);
  box-shadow: 0 calc(3 * var(--u)) calc(4 * var(--u)) rgba(30, 5, 5, .55);
  pointer-events: none;
}
.cv-dot--knot { background: radial-gradient(circle at 40% 35%, #f3dcc0, #c79a6c); width: calc(14 * var(--u)); height: calc(14 * var(--u)); left: calc((var(--x) - 7) * var(--u)); top: calc((var(--y) - 7) * var(--u)); }

/* 红线 */
.cv-board__strings {
  position: absolute;
  inset: 0;
  z-index: 7;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.cv-board__strings path {
  fill: none;
  stroke: var(--thread);
  stroke-width: 2.6;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 2px 1.5px rgba(40, 10, 5, .45));
  transition: stroke-width 200ms ease, stroke 200ms ease;
}
.cv-board__strings path.is-hot { stroke: #e3242f; stroke-width: 4; }

/* ---- 标题：马克笔字 + 荧光笔色块 ---- */
.cv-label {
  display: inline-flex;
  align-items: center;
  gap: calc(12 * var(--u));
  margin: 0 0 calc(14 * var(--u));
  color: var(--wine);
  font-family: var(--marker);
  font-size: max(14px, calc(30 * var(--u)));
  font-weight: 400;
  letter-spacing: .05em;
  line-height: 1;
  text-transform: uppercase;
}
.cv-label > span:last-child {
  padding: calc(6 * var(--u)) calc(16 * var(--u)) calc(4 * var(--u));
  background: rgba(240, 196, 80, .78);
  clip-path: polygon(2% 10%, 98% 0, 100% 86%, 3% 100%);
}
.cv-label--pink > span:last-child { background: rgba(236, 150, 160, .8); }

.cv-ico { display: inline-grid; flex: 0 0 auto; width: calc(36 * var(--u)); height: calc(36 * var(--u)); color: var(--wine); }
.cv-ico--lg { width: calc(46 * var(--u)); height: calc(46 * var(--u)); }
.cv-ico svg,
.cv-trophy svg,
.cv-profile__ico svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }

.cv-sparks { position: absolute; width: calc(40 * var(--u)); height: calc(40 * var(--u)); fill: none; stroke: #e2b33c; stroke-width: 3.4; stroke-linecap: round; }
.cv-sparks--red { stroke: #c8303a; left: calc(-30 * var(--u)); top: calc(-28 * var(--u)); transform: rotate(-30deg); }
.cv-sparks--photo { top: calc(30 * var(--u)); right: calc(36 * var(--u)); z-index: 2; }

/* 身份 */
.cv-profile { display: grid; grid-template-columns: calc(84 * var(--u)) 1fr; gap: calc(22 * var(--u)); align-items: center; }
.cv-profile__ico { position: relative; width: calc(76 * var(--u)); height: calc(76 * var(--u)); color: var(--wine); }
.cv-profile p { margin: 0; font-family: var(--hand); font-size: max(13px, calc(21 * var(--u))); line-height: 1.3; }

/* 经历 */
.cv-entry { display: grid; grid-template-columns: auto 1fr; gap: calc(18 * var(--u)); align-items: start; }
.cv-entry__title { margin: 0; color: var(--wine); font-size: max(13px, calc(22 * var(--u))); font-weight: 800; line-height: 1.25; }
.cv-entry__meta { margin: calc(8 * var(--u)) 0 0; color: var(--ink); font-size: max(12px, calc(17 * var(--u))); }
.cv-bullets { display: grid; gap: calc(4 * var(--u)); margin: calc(12 * var(--u)) 0 0; padding-left: calc(30 * var(--u)); font-size: max(12px, calc(17 * var(--u))); line-height: 1.3; }
.cv-bullets li::marker { color: var(--wine); }
.cv-bullets--awards { gap: calc(18 * var(--u)); padding-left: calc(28 * var(--u)); }

/* 照片：拍立得 + 大图钉 */
.cv-polaroid {
  z-index: 4;
  padding: calc(16 * var(--u)) calc(16 * var(--u)) calc(16 * var(--u));
  background: #f6f3ee;
}
.cv-polaroid img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; filter: saturate(.9) contrast(1.02); }
.cv-pushpin {
  position: absolute;
  top: calc(-10 * var(--u));
  left: 50%;
  width: calc(44 * var(--u));
  height: calc(44 * var(--u));
  margin-left: calc(-22 * var(--u));
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #ff9a8f, #d7202b 46%, #85101a 100%);
  box-shadow: calc(6 * var(--u)) calc(10 * var(--u)) calc(10 * var(--u)) rgba(30, 5, 5, .45);
}

/* 名牌 */
.cv-name { margin: 0; color: var(--wine); font-family: var(--slab); font-size: max(20px, calc(48 * var(--u))); white-space: nowrap; font-weight: 400; letter-spacing: .02em; line-height: 1; text-transform: uppercase; }
.cv-underline { display: block; width: 78%; height: calc(14 * var(--u)); margin: calc(4 * var(--u)) auto 0; fill: none; stroke: #c22a33; stroke-width: 3; stroke-linecap: round; }
.cv-underline--loc { width: 62%; margin: calc(-6 * var(--u)) 0 0 calc(56 * var(--u)); }
.cv-role { margin: calc(8 * var(--u)) 0 0; font-family: var(--hand); font-size: max(13px, calc(25 * var(--u))); font-style: italic; letter-spacing: .03em; }

.cv-location { display: flex; align-items: center; gap: calc(12 * var(--u)); margin: 0; font-family: var(--hand); font-size: max(13px, calc(26 * var(--u))); }
.cv-location .cv-ico { width: calc(30 * var(--u)); height: calc(30 * var(--u)); }

/* 联系方式 */
.cv-contact { display: grid; gap: calc(16 * var(--u)); margin: calc(8 * var(--u)) 0 0; padding: 0; list-style: none; font-family: var(--hand); font-size: max(12px, calc(19 * var(--u))); }
.cv-contact li { display: flex; align-items: center; gap: calc(16 * var(--u)); }
.cv-contact .cv-ico { width: calc(30 * var(--u)); height: calc(30 * var(--u)); }
.cv-card a { color: inherit; text-decoration-color: rgba(107, 26, 34, .45); text-underline-offset: 3px; }
.cv-card a:hover { color: #9c1b1f; }

/* 教育：时间轴 */
.cv-timeline { position: relative; display: grid; gap: calc(14 * var(--u)); margin: calc(4 * var(--u)) 0 0; padding: 0 0 0 calc(34 * var(--u)); list-style: none; }
.cv-timeline::before { content: ""; position: absolute; left: calc(9 * var(--u)); top: calc(12 * var(--u)); bottom: calc(40 * var(--u)); width: 2px; background: var(--wine); }
.cv-timeline li { position: relative; }
.cv-timeline li::before { content: ""; position: absolute; left: calc(-32 * var(--u)); top: calc(5 * var(--u)); width: calc(14 * var(--u)); height: calc(14 * var(--u)); border: 2px solid var(--wine); border-radius: 50%; background: #f2eadb; }
.cv-timeline h4 { margin: 0; color: var(--wine); font-size: max(13px, calc(22 * var(--u))); font-weight: 800; line-height: 1.2; }
.cv-timeline p { margin: calc(4 * var(--u)) 0 0; font-family: var(--hand); font-size: max(12px, calc(17 * var(--u))); font-style: italic; line-height: 1.35; }

/* 技能 */
.cv-skills { display: grid; gap: calc(12 * var(--u)); }
.cv-skills > div { display: grid; grid-template-columns: calc(46 * var(--u)) 1fr; gap: calc(18 * var(--u)); align-items: start; }
.cv-skills p { margin: 0; font-size: max(12px, calc(18 * var(--u))); line-height: 1.3; }
.cv-skills strong { color: var(--wine); font-weight: 800; }

/* 奖项 */
.cv-trophy { position: absolute; right: calc(34 * var(--u)); bottom: calc(26 * var(--u)); width: calc(46 * var(--u)); height: calc(46 * var(--u)); color: #d9a62e; }
.cv-trophy .cv-sparks { top: calc(-30 * var(--u)); right: calc(-30 * var(--u)); }

/* 语言：进度条 */
.cv-langs { display: grid; gap: calc(16 * var(--u)); margin: calc(8 * var(--u)) 0 0; padding: 0; list-style: none; }
.cv-langs li { display: grid; grid-template-columns: 1fr calc(165 * var(--u)); gap: calc(20 * var(--u)); align-items: center; font-size: max(13px, calc(21 * var(--u))); }
.cv-langs small { font-size: max(12px, calc(18 * var(--u))); }
.cv-langs i { height: calc(14 * var(--u)); border-radius: var(--r-pill); background: linear-gradient(90deg, #7b1d2c calc(var(--level) * 100%), #d8c3c2 0); }

/* 板子下面：下载 PDF + 关闭 */
.cv-board__controls { display: flex; justify-content: center; gap: 14px; }
.cv-board__stamp,
.cv-board__close {
  padding: 10px 20px;
  border: 0;
  border-radius: var(--r-pill);
  font-family: var(--round);
  font-size: var(--t-small);
  font-weight: 700;
  letter-spacing: .06em;
  text-decoration: none;
  cursor: pointer;
}
.cv-board__stamp { background: #f2eadb; color: var(--wine); }
.cv-board__close { background: rgba(242, 234, 219, .14); color: #f2eadb; box-shadow: inset 0 0 0 1px rgba(242, 234, 219, .4); }
.cv-board__stamp:hover { background: #fff; }
.cv-board__close:hover { background: rgba(242, 234, 219, .26); }
.cv-board :focus-visible { outline: 2px solid #ffd27a; outline-offset: 3px; }

/* 手机：板子太小放不下效果图的排版，改成一列卡片，用一根竖着的红线串起来 */
@media (max-width: 760px) {
  .cv-board { place-items: start center; padding: 10px; }
  .cv-board__stage { width: 100%; }
  .cv-board__cork {
    --u: .62px;
    display: grid;
    gap: 30px;
    aspect-ratio: auto;
    padding: 40px 16px 48px;
    container-type: normal;
  }
  .cv-board__cork > * { --u: .62px; }
  .cv-board__cork::before { content: ""; position: absolute; left: 50%; top: 40px; bottom: 48px; width: 2px; background: var(--thread); box-shadow: 0 2px 2px rgba(40, 10, 5, .4); }
  .cv-prop, .cv-tape, .cv-dot, .cv-board__strings { display: none; }
  .cv-card, .cv-polaroid { position: relative; left: auto; top: auto; width: 100%; height: auto; }
  .cv-polaroid { justify-self: center; width: min(72%, 240px); aspect-ratio: 292 / 312; }
  .cv-card__paper { padding: 22px 20px; }
  .cv-note[data-node="profile"] { order: 1; }
  .cv-polaroid { order: 0; }
  .cv-note[data-node="name"] { order: 1; }
  .cv-note[data-node="location"] { order: 2; justify-self: center; width: auto; }
  .cv-note[data-node="contact"] { order: 3; }
  .cv-note[data-node="profile"] { order: 4; }
  .cv-note[data-node="experience"] { order: 5; }
  .cv-note[data-node="education"] { order: 6; }
  .cv-note[data-node="skills"] { order: 7; }
  .cv-note[data-node="awards"] { order: 8; }
  .cv-note[data-node="languages"] { order: 9; }
  .cv-trophy { display: none; }
}

/* ---------- 芍药花束 ----------
   花束从花瓶处飞到中央（main.js 的 Gift），缎带上的卡片随后像被风吹一样摆动着出现。 */

.gift {
  --bouquet-h: min(78vh, 105vw);
  position: absolute;
  z-index: 40;
  inset: 0;
  display: grid;
  place-items: center;
}

.gift[hidden] { display: none; }

.gift__backdrop {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 45%, rgba(46, 14, 18, .8), rgba(5, 2, 3, .94));
  opacity: 0;
  transition: opacity 500ms ease;
}

.gift.is-open .gift__backdrop { opacity: 1; }

.gift__caption {
  position: absolute;
  z-index: 1;
  top: clamp(90px, 18vh, 180px);
  left: clamp(24px, 7vw, 110px);
  max-width: min(360px, 30vw);
  margin: 0;
  color: #fff0d2;
  font-family: var(--serif);
  font-size: var(--t-h2);
  line-height: 1.1;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 700ms ease 650ms, transform 900ms var(--ease-out) 600ms;
}

.gift__caption em { display: block; color: #edc16b; font-style: italic; }
.gift.is-open .gift__caption { opacity: 1; transform: none; }

.gift__bouquet {
  position: relative;
  height: var(--bouquet-h);
  aspect-ratio: 1009 / 1347;
  margin: 0;
  transform-origin: 50% 50%;
}

.gift__bouquet img { display: block; width: 100%; height: 100%; filter: drop-shadow(0 30px 40px rgba(0, 0, 0, .45)); }

/* 卡片挂在蝴蝶结结扣（花束图 50% / 70% 处）下面，以绳子顶端为轴摆动 */
.gift__card {
  position: absolute;
  top: 76%;
  left: 54%;
  width: clamp(220px, 44%, 280px);
  padding: 22px 20px 18px;
  border-radius: var(--r-xs);
  background: #fbf2df;
  box-shadow: 0 16px 30px rgba(0, 0, 0, .35);
  color: #4a2a19;
  opacity: 0;
  transform-origin: 18px -38px;
  rotate: -4deg;
}

/* 挂绳：从卡片的小孔连到蝴蝶结 */
.gift__card::before {
  position: absolute;
  top: 9px;
  left: 14px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #e2cfa8;
  box-shadow: inset 0 1px 2px rgba(74, 42, 25, .4);
  content: "";
}

.gift__string {
  position: absolute;
  top: -40px;
  left: 17px;
  width: 1.5px;
  height: 53px;
  background: #b3242a;
  transform: rotate(-18deg);
  transform-origin: top;
}

.gift__card-eyebrow { margin: 0 0 8px 14px; color: #a2572a; font-size: var(--t-label); letter-spacing: .24em; text-transform: uppercase; }

.gift__email {
  display: block;
  color: #4a2a19;
  font-family: var(--serif);
  font-size: var(--t-lede);
  text-decoration: none;
  white-space: nowrap;
}

.gift__email:hover { color: #9b1c20; text-decoration: underline; text-underline-offset: 4px; }

.gift__copy {
  margin-top: 12px;
  padding: 0 0 2px;
  border: 0;
  border-bottom: 1px solid rgba(74, 42, 25, .35);
  background: none;
  color: #4a2a19;
  font-size: var(--t-label);
  letter-spacing: .08em;
  cursor: pointer;
}

.gift__copy:hover { border-bottom-color: #9b1c20; color: #9b1c20; }

.gift__close {
  position: absolute;
  z-index: 1;
  top: max(24px, env(safe-area-inset-top));
  right: clamp(22px, 4vw, 62px);
  padding: 6px 0;
  border: 0;
  border-bottom: 1px solid rgba(239, 198, 126, .6);
  background: none;
  color: var(--ink);
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  transition: opacity 500ms ease 400ms;
}

.gift.is-open .gift__close { opacity: 1; }

@media (max-width: 820px) {
  .gift { --bouquet-h: min(62vh, 110vw); }
  .gift__caption { top: 9vh; right: 24px; max-width: none; text-align: center; }
  .gift__bouquet { margin-top: 12vh; }
}

/* ---------- 角色热区 ----------
   每个角色的范围以插画为基准（百分比相对于 1672 × 941 原图），
   镜头在所有屏幕下都保持原图比例，所以这组数字通用。
   追光光束由 main.js 根据热区位置自动计算，无需另外配置。 */

.role-hotspot {
  position: absolute;
  z-index: 5;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: var(--h);
  padding: 0;
  border: 0;
  border-radius: 50% 50% 12% 12% / 22% 22% 4% 4%;
  background: radial-gradient(ellipse at 50% 58%, rgba(248, 193, 92, .2), transparent 70%);
  opacity: 0;
  cursor: pointer;
  pointer-events: none;
  transition: opacity 250ms ease;
}

.role-hotspot[data-role="secretary"] { --x: 20%;   --y: 44%;   --w: 11%;   --h: 42.5%; }
.role-hotspot[data-role="mechanic"]  { --x: 30.5%; --y: 40%;   --w: 15.5%; --h: 46.5%; } /* 上沿到他站起来后的头顶，站起来时鼠标不会移出 */
.role-hotspot[data-role="mother"]    { --x: 46.5%; --y: 43%;   --w: 14%;   --h: 44%; }
.role-hotspot[data-role="guide"]     { --x: 61%;   --y: 43.5%; --w: 19.5%; --h: 43%; }

.theatre[data-state="open"] .role-hotspot { pointer-events: auto; }

.role-hotspot:is(:hover, :focus-visible, [aria-pressed="true"]) { opacity: 1; }

/* ---------- 角色追光 ----------
   --spot-x / --beam-half / --beam-bottom 由 main.js 写在 .camera 上。 */

.camera[data-spotlight] {
  --beam: polygon(
    var(--spot-x) 0,
    calc(var(--spot-x) + var(--beam-half)) var(--beam-bottom),
    calc(var(--spot-x) - var(--beam-half)) var(--beam-bottom)
  );
}

.spotlight-dimmer,
.spotlight-zoom,
.spotlight-beam {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease, clip-path 360ms var(--ease-out);
}

.camera[data-spotlight] :is(.spotlight-dimmer, .spotlight-zoom, .spotlight-beam) { opacity: 1; }

.spotlight-dimmer { z-index: 2; background: rgba(7, 3, 4, .8); }

/* 光束内是一份提亮的舞台和角色（角色由 main.js 复制进来） */
.spotlight-zoom {
  z-index: 3;
  filter: brightness(1.08) saturate(1.06);
  clip-path: var(--beam);
}

.spotlight-beam {
  z-index: 4;
  clip-path: var(--beam);
  background: linear-gradient(180deg, rgba(255, 225, 165, .12), rgba(255, 220, 157, .2) 48%, rgba(255, 208, 133, .42));
  filter: drop-shadow(0 0 30px rgba(255, 207, 134, .38));
  mix-blend-mode: screen;
}

/* ---------- 帷幕 ----------
   --open: 0 闭合 → 1 完全拉开。
   悬停折起效果由 JS 写入两片幕布的 clip-path。 */

.curtain {
  --open: 0;
  position: absolute;
  z-index: 10;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.curtain-panel {
  position: absolute;
  inset: 0;
  background-color: #a71318;
  background-image:
    linear-gradient(90deg, rgba(42, 4, 8, .68), transparent 9%, rgba(255, 78, 50, .2) 17%, rgba(75, 4, 11, .34) 31%, transparent 47%, rgba(40, 2, 6, .38) 59%, rgba(235, 48, 31, .17) 69%, rgba(45, 3, 8, .48) 83%, transparent 94%),
    linear-gradient(180deg, rgba(255, 77, 44, .1), transparent 32%, rgba(49, 2, 8, .18) 78%, rgba(32, 2, 5, .52));
  box-shadow: inset 0 0 50px rgba(32, 0, 4, .4);
  transition: clip-path 170ms cubic-bezier(.22, .61, .36, 1), transform var(--curtain-time) var(--ease-curtain);
  will-change: clip-path, transform;
}

.curtain-panel--left  { clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%);     transform: translateX(calc(var(--open) * -105%)); }
.curtain-panel--right { clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%); transform: translateX(calc(var(--open) * 105%)); }

.curtain-seam {
  position: absolute;
  z-index: 3;
  top: 0;
  bottom: 0;
  left: calc(50% - 2px);
  width: 4px;
  background: linear-gradient(90deg, rgba(65, 8, 13, .7), rgba(248, 208, 137, .45), rgba(82, 11, 14, .7));
  box-shadow: 0 0 11px rgba(40, 5, 8, .6);
  transition: opacity 160ms ease;
}

.curtain.is-preview .curtain-seam { opacity: 0; }
.theatre:not([data-state="closed"]) .curtain-seam { display: none; }

.curtain-folds {
  position: absolute;
  z-index: 4;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  opacity: 0;
  transition: opacity 180ms ease;
}

.curtain.is-preview .curtain-folds { opacity: 1; }
.curtain-fold-shadow { fill: none; stroke: rgba(44, 3, 8, .42); stroke-width: 16; stroke-linecap: round; stroke-linejoin: round; }
.curtain-pinch { fill: none; stroke: rgba(73, 7, 12, .62); stroke-width: 3; stroke-linecap: round; opacity: .8; }
.curtain-pinlight { fill: url(#curtainPinlightGradient); stroke: rgba(255, 226, 166, .55); stroke-width: 1.5; }

.curtain-valance {
  position: absolute;
  top: -1px;
  left: -3%;
  width: 106%;
  height: clamp(68px, 12vh, 126px);
  border-bottom: 7px solid #d59b42;
  border-radius: 0 0 50% 50%;
  background:
    radial-gradient(ellipse at 50% -20%, #ef5a36 0 40%, transparent 41%),
    linear-gradient(180deg, #760d13, #b3181b 62%, #8b1015);
  box-shadow: 0 10px 26px rgba(14, 3, 4, .38);
  transform: translateY(calc(var(--open) * -125%));
  transition: transform var(--curtain-time) var(--ease-curtain);
}

/* 檐幕下沿的灯珠 */
.curtain-valance::after {
  position: absolute;
  right: 0;
  bottom: -8px;
  left: 0;
  height: 14px;
  content: "";
  background: radial-gradient(circle, #f5d083 0 3px, transparent 3.6px) 0 0 / 38px 14px repeat-x;
}

/* ---------- 界面文字与控件 ---------- */

.topbar {
  position: absolute;
  z-index: 14;
  top: max(24px, env(safe-area-inset-top));
  right: clamp(22px, 4vw, 62px);
  left: clamp(22px, 4vw, 62px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
}

.wordmark, .scene-index {
  margin: 0;
  color: #f8e7c5;
  font-size: var(--t-label);
  letter-spacing: .2em;
  text-transform: uppercase;
  text-shadow: 0 2px 12px rgba(0, 0, 0, .5);
}

.wordmark span { color: #edc16b; }

.intro {
  position: absolute;
  z-index: 8;
  top: clamp(100px, 16vh, 162px);
  left: clamp(22px, 7vw, 110px);
  max-width: min(460px, 45vw);
  pointer-events: none;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 650ms ease 250ms, transform 850ms var(--ease-out) 220ms;
}

.theatre[data-state="open"] .intro { opacity: 1; transform: none; }

.intro h1 {
  margin: 0 0 11px;
  color: #fff0d2;
  font-family: var(--serif);
  font-size: var(--t-h2);
  font-weight: 400;
  letter-spacing: .015em;
  line-height: 1.08;
  text-shadow: 0 4px 22px rgba(20, 5, 6, .45);
}

.intro p {
  margin: 0;
  color: #f4d9a9;
  font-size: var(--t-body);
  letter-spacing: .12em;
  line-height: 1.8;
  text-shadow: 0 2px 14px rgba(20, 5, 6, .7);
}

.open-prompt {
  position: absolute;
  z-index: 15;
  left: 50%;
  bottom: max(34px, env(safe-area-inset-bottom));
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 10px 18px;
  border: 0;
  background: transparent;
  cursor: pointer;
  transform: translateX(-50%);
  transition: opacity 380ms ease, visibility 380ms ease;
}

.open-prompt::before {
  position: absolute;
  inset: 0 -24px;
  z-index: -1;
  border: 1px solid rgba(239, 198, 126, .65);
  background: rgba(33, 7, 10, .64);
  content: "";
  transform: skewX(-8deg);
  transition: background 220ms ease, border-color 220ms ease;
}

.open-prompt:hover::before { border-color: #f7d997; background: rgba(50, 9, 12, .84); }
.open-prompt strong { font-family: var(--serif); font-size: var(--t-body); font-weight: 400; letter-spacing: .12em; }
.open-prompt small { color: #eac98e; font-size: var(--t-label); letter-spacing: .13em; }
.theatre:not([data-state="closed"]) .open-prompt { visibility: hidden; opacity: 0; pointer-events: none; }

.selected-caption {
  position: absolute;
  z-index: 8;
  right: clamp(20px, 6vw, 88px);
  top: 22%;
  width: min(320px, 26vw);
  padding-left: 16px;
  border-left: 1px solid rgba(224, 174, 89, .7);
  opacity: 0;
  transform: translateX(10px);
  transition: opacity 350ms ease, transform 500ms ease;
  pointer-events: none;
}

.selected-caption.is-visible { opacity: 1; transform: none; }
.selected-caption span { display: block; margin-bottom: 8px; color: #edc16b; font-size: var(--t-label); letter-spacing: .2em; }
.selected-caption strong { display: block; font-family: var(--serif); font-size: var(--t-h3); font-weight: 400; line-height: 1.15; letter-spacing: .02em; }
.selected-caption p { margin: 10px 0 0; color: #f2d7aa; font-size: var(--t-body); letter-spacing: .03em; line-height: 1.6; }

.sweep-hint {
  position: absolute;
  z-index: 13;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  gap: 12px;
  color: rgba(255, 226, 181, .74);
  font-size: var(--t-label);
  letter-spacing: .15em;
  opacity: 0;
  transform: translate(-50%, -50%);
  pointer-events: none;
  transition: opacity 300ms ease;
}

.theatre[data-state="closed"] .sweep-hint { opacity: .82; }
.sweep-line { width: 36px; height: 1px; background: currentColor; }

/* ---------- 推镜 / 进入项目时隐藏界面 ----------
   必须放在各控件的 open 状态规则之后，否则同优先级会被覆盖。 */

.theatre:is([data-desk-zoom], [data-entering]) :is(.curtain, .topbar, .intro, .selected-caption, .role-hotspot) {
  opacity: 0;
  pointer-events: none;
}

/* ---------- 进入项目页的转场 ----------
   main.js 把 .camera-wrap 的缩放中心设在被点击的角色上；总时长与 main.js 的 ENTER_MS 一致。 */

.theatre[data-entering] .camera-wrap {
  transform: scale(1.7);
  transition: transform 900ms cubic-bezier(.5, 0, .75, 0);
}

/* 全屏黑场 */
.theatre::before {
  position: absolute;
  z-index: 30;
  inset: 0;
  content: "";
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease 400ms;
}

.theatre[data-entering]::before { opacity: 1; }

/* 从项目页返回时直接显示舞台，跳过所有过渡 */
.theatre.is-instant *,
.theatre.is-instant *::before,
.theatre.is-instant *::after { transition: none !important; }

/* ---------- 响应式 ---------- */

/* 宽屏：插画按原比例铺满视口（1672 × 941） */
@media (min-width: 821px) {
  .camera { width: max(100vw, 177.67vh); height: auto; aspect-ratio: 1672 / 941; }
}

@media (min-width: 821px) and (max-aspect-ratio: 4/3) {
  .camera { top: 47%; width: 100vw; }
}

/* 窄屏：插画按宽度完整显示 */
@media (max-width: 820px) {
  .theatre { min-height: 480px; }
  .camera { top: 48%; width: 100vw; height: auto; aspect-ratio: 1672 / 941; }
  .stage-img { object-fit: contain; }
  .desk-frame { width: 100vw; }   /* 和舞台插画一样按宽度完整显示；特写图背景是黑色，上下留黑看不出来 */
  .intro { top: 13%; max-width: 76vw; }
  .intro h1 { font-size: var(--t-h2); }
  .selected-caption { top: auto; right: 22px; bottom: 105px; width: min(220px, 55vw); }
}

@media (max-width: 460px) {
  .topbar { top: max(18px, env(safe-area-inset-top)); }
  .wordmark, .scene-index { font-size: var(--t-label); letter-spacing: .14em; }
  .camera { top: 49%; }
  .intro { top: 15%; left: 23px; }
  .intro p { max-width: 280px; font-size: var(--t-small); }
  .selected-caption { bottom: 142px; }
  .open-prompt { bottom: max(52px, env(safe-area-inset-bottom)); }
}

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