/* ==========================================================================
   v4 · 卡片质感（2026-10-17，按 briefs/v4-ds-material.md「C」）—— 只在 html.ds（默认）时生效；?ds=0 = 改之前的 v4
   放在所有样式表最后。不改结构、不改翻面 / 倾斜（flip.js、peek.js、peek.css 原样），只换材质和影子。

   1. 1px 竖线（飞飞鱼左下圆角）的根因：.edge-l / .edge-r 是「侧过来时才看得到」的片厚，
      但它们一直在：top:0 / bottom:0 的直角细条，绕卡的左右边 rotateY(±90°)。tile 有 perspective: 1600px，
      卡又不在透视中心，所以这条「侧面」永远露出约 0.3–0.5px；卡是 8px 圆角，细条是直角，
      于是在圆角那一截它伸出了卡的轮廓 —— 就是那根竖线（飞飞鱼的片边是 .9 不透明的蓝，最显眼；纸卡夜里也有）。
      修法：片厚只在翻面时出现（opacity 跟 --s = |sin θ|），上下各让出一个圆角半径（真实的圆角卡，直的那段边本来就短一截）。
   2. 影子：每张卡仍然只有一个影子元素 i.sh（宽 = |cos θ| × 卡宽，和翻面同一个时钟），
      里面分三层：contact（贴墙那一圈，最暗最锐，一离墙就迅速消失）/ key（灯的投影，方向 = 全局灯 --vx/--vy）/ ambient（房间的漫射光，很软很淡，略往下）。
      三层都是同一个形状的模糊色块，模糊和浓度都从 z 连续算出来，没有色带；夜里同一套公式，颜色换成台灯下的暖黑。
   3. 透明卡（飞飞鱼）：一片薄亚克力 —— 片本身很淡（能透出墙和窗光）+ 朝灯一侧一道切边高光 + 背光一侧一点片厚；
      投在墙上的是被片染色的光（一圈彩色光斑，中间是空的，所以不把整张卡染成一块浅蓝）+ 片边挡光的一圈细影。没有硬描边。
   4. 纸卡（魔法笔、衣服的家、月光变身湾）：同一种纸白、同一种纸纹、同一个圆角、同一套影子；夜里都是台灯下的亮纸，墨保持深色。
   ========================================================================== */

/* ?ds=0：新加的三层影子元素不画（旧 v4 的 .sh 自己就是影子） */
:root:not(.ds) .sh > i { display: none; }

/* ---------------- 1 · 片厚：只在翻面时出现，不越过圆角 ---------------- */
:root.ds .flip-tile .edge { top: var(--r); bottom: var(--r); opacity: min(1, calc(var(--s) * 4)); }

/* ---------------- 2 · 影子：一个元素，三层 ---------------- */
:root.ds .tile > .sh {
  background: none; box-shadow: none; filter: none; translate: none;
  /* 总浓度：--sh-a 以 .22 为 1（黄金时刻 golden.js 会把它推到 .3），乘灯的强度和昼夜系数 */
  --S: calc(var(--sh-a) / .22 * var(--shadow-strength) * var(--sh-k) * var(--boost, 1));
  --zz: var(--obj-z);
}
:root.ds .sh > i { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
/* contact：几乎贴着卡边；z 一高就迅速淡掉 */
:root.ds .sh > .sh-con {
  --cz: calc(1 + var(--zz) / 3);
  background: rgb(var(--sh-rgb) / calc(var(--S) * .5 / (var(--cz) * var(--cz))));
  translate: calc(var(--zz) * var(--vx) * .22px) calc(var(--zz) * var(--vy) * .22px);
  filter: blur(calc((.55 + var(--zz) * .3) * 1px));
}
/* key：灯的投影。偏移 = z × 灯方向，半影 = z × 光源大小 */
:root.ds .sh > .sh-key {
  background: rgb(var(--c-key, var(--sh-rgb)) / calc(var(--S) * .17 / (1 + var(--zz) / 32)));
  translate: calc(var(--zz) * var(--vx) * var(--sh-off-k) * 1px) calc(var(--zz) * var(--vy) * var(--sh-off-k) * 1px);
  filter: blur(calc((1.6 + var(--zz) * var(--light-soft) * 2.2) * 1px));
}
/* ambient：房间的漫射光，略往下，左右收一点，免得四周起一圈晕 */
:root.ds .sh > .sh-amb {
  inset: 10px 3% 0;
  background: rgb(var(--sh-rgb) / calc(var(--S) * .11 / (1 + var(--zz) / 60)));
  translate: calc(var(--zz) * var(--vx) * .25px) calc((3 + var(--zz) * .45) * 1px);
  filter: blur(calc((12 + var(--zz) * .8) * 1px));
}

/* ---------------- 3 · 透明卡：一片薄亚克力 ---------------- */
:root.ds .m-film { --r: 9px; --thick: 1.5px; }
:root.ds .m-film .sheet {
  border: 0;
  /* 片：很淡的蓝 + 一层朝灯方向渐弱的白色反光（方向跟全局灯：从 -v 指向 v） */
  background:
    linear-gradient(160deg, rgb(255 255 255 / .26) 0%, rgb(255 255 255 / .06) 38%, rgb(255 255 255 / .02) 70%, rgb(255 255 255 / .08) 100%),
    rgb(var(--film-rgb) / var(--ds-film-a));
  background:
    linear-gradient(atan2(var(--vx), calc(var(--vy) * -1)), rgb(255 255 255 / .26) 0%, rgb(255 255 255 / .06) 38%, rgb(255 255 255 / .02) 70%, rgb(255 255 255 / .08) 100%),
    rgb(var(--film-rgb) / var(--ds-film-a));
  box-shadow:
    0 0 0 calc(.5px + var(--s) * 1.2px) rgb(var(--film-edge) / calc(.26 + var(--s) * .45)),          /* 片的外缘：很细、半透明 */
    inset calc(var(--vx) * 1.7px) calc(var(--vy) * 1.7px) 0 -.3px rgb(255 255 255 / .8),            /* 朝灯一侧的切边高光 */
    inset calc(var(--vx) * -1.3px) calc(var(--vy) * -1.3px) 0 -.3px rgb(var(--film-edge) / .20),    /* 背光一侧的片厚 */
    inset 0 0 0 .5px rgb(255 255 255 / .4);
}
:root.ds .m-film .edge { background: rgb(var(--film-edge) / .55); }
/* 片投在墙上：被片染色的光 + 片边挡光的一圈细影。
   彩色光斑用「外」box-shadow 画：box-shadow 不画在元素自己的盒子下面，所以光斑只出现在片的轮廓之外（被灯推出去的那一截），
   透过片看到的仍然是墙和窗光，不会把整张卡染成一块浅蓝（DS 迷你首页那张透明卡也是这个画法）。 */
:root.ds .m-film > .sh > .sh-key {
  --zc: calc(var(--zz) + 7);   /* 彩色光斑落得比卡更远一点：片和墙之间有一点空气，光斑才露得出来 */
  background: none; filter: none; translate: none;
  box-shadow:
    calc(var(--zc) * var(--vx) * var(--sh-off-k) * 1px) calc(var(--zc) * var(--vy) * var(--sh-off-k) * 1px)
    calc((5 + var(--zc) * var(--light-soft) * 1.6) * 1px) calc(-1.5px - var(--zz) * .05px)
    rgb(var(--c-film, var(--film-rgb)) / calc(var(--film-tint) * var(--shadow-strength) * var(--boost, 1) * 2 / (1 + var(--zz) / 30)));
}
:root.ds .m-film > .sh > .sh-con {
  --cz: calc(1 + var(--zz) / 4);
  background: none;
  box-shadow: inset 0 0 0 1px rgb(var(--sh-rgb) / calc(var(--S) * .42 / (var(--cz) * var(--cz))));
  translate: calc(var(--zz) * var(--vx) * .45px) calc(var(--zz) * var(--vy) * .45px);
  filter: blur(calc((.6 + var(--zz) * .35) * 1px));
}
:root.ds .m-film > .sh > .sh-amb { display: none; }   /* 片透光：底下没有一整块漫射的灰 */
/* 夜里：台灯下的片像背光胶片 —— 片更薄，边缘透出一点自己的颜色，高光是暖的 */
:root.ds[data-theme="night"] .m-film .sheet {
  background:
    linear-gradient(160deg, rgb(255 236 210 / .10) 0%, rgb(255 236 210 / .03) 40%, rgb(255 255 255 / 0) 70%),
    rgb(var(--film-rgb) / var(--ds-film-a));
  background:
    linear-gradient(atan2(var(--vx), calc(var(--vy) * -1)), rgb(255 236 210 / .10) 0%, rgb(255 236 210 / .03) 40%, rgb(255 255 255 / 0) 70%),
    rgb(var(--film-rgb) / var(--ds-film-a));
  box-shadow:
    0 0 0 calc(.5px + var(--s) * 1.2px) rgb(var(--film-rgb) / calc(.30 + var(--s) * .4)),
    inset calc(var(--vx) * 1.5px) calc(var(--vy) * 1.5px) 0 -.3px rgb(255 226 190 / .38),
    inset 0 0 22px -8px rgb(var(--film-rgb) / .38);
}

/* ---------------- 4 · 纸卡：魔法笔、衣服的家、月光变身湾 用同一张纸 ---------------- */
:root.ds .m-slide { --r: var(--card-r); }   /* 纸卡的 --r 只在 cards.css 的 .m-paper 写一次 */
:root.ds .m-paper, :root.ds .m-slide { --thick: 1px; --edge-w0: .5px; --edge-wk: 1px; --edge-c: rgb(30 36 48 / .07); }
:root.ds .m-paper .face, :root.ds .m-slide .face {
  background-color: var(--ds-paper);
  /* 纸纹 = Magical Pen app 的「细腻绘图纸」（js/paper.js）；一层很淡的反光从灯那一侧来 */
  background-image: linear-gradient(160deg, rgb(255 255 255 / .34) 0%, rgb(255 255 255 / 0) 45%, rgb(60 48 30 / 0) 72%, rgb(60 48 30 / .025) 100%), var(--paper-tex);
  background-image: linear-gradient(atan2(var(--vx), calc(var(--vy) * -1)), rgb(255 255 255 / .34) 0%, rgb(255 255 255 / 0) 45%, rgb(60 48 30 / 0) 72%, rgb(60 48 30 / .025) 100%), var(--paper-tex);
  background-size: 100% 100%, 384px 384px;
}
:root.ds .m-paper .face.back, :root.ds .m-slide .face.back { background-position: 0 0, 37px 91px; }
/* 夜里：台灯下的亮纸（墙暗，内容不暗）。纸上的墨保持深色；反光换成台灯的暖 */
:root.ds[data-theme="night"] .m-paper .face, :root.ds[data-theme="night"] .m-slide .face {
  background-image: linear-gradient(atan2(var(--vx), calc(var(--vy) * -1)), rgb(255 236 210 / .22) 0%, rgb(255 236 210 / 0) 45%, rgb(40 28 14 / 0) 70%, rgb(40 28 14 / .05) 100%), var(--paper-tex);
}
:root.ds[data-theme="night"] .m-slide, :root.ds[data-theme="night"] .m-paper {
  --ink-rgb: 30 36 48;
  --ink: rgb(30 36 48); --ink-2: rgb(30 36 48 / .80); --ink-3: rgb(30 36 48 / var(--ink-3-a));
  --hair: rgb(30 36 48 / .08); --hair-2: rgb(30 36 48 / .2);
  --edge-c: rgb(30 36 48 / .10);
  --win: #E3DCCE;
  --edge-thick: var(--paper-edge);
  color: var(--ink);           /* 继承下来的 color 是 body 上算好的夜里浅色墨，这里要重新指定 */
  color-scheme: light;
}

/* 夜里翻到背面时，透过片看到的正面镜像（flip.js 的 ghost ≈ 8%）在暗墙上会变成一块发灰的蓝（主要是海的图），压着背面的字。
   夜里把残影压到约 3.6%：正面时 --fo = 1 不受影响，翻面过程照旧连续。白天保持原样（浅墙上 8% 的镜像很轻）。 */
:root.ds[data-theme="night"] .m-film .face.front { opacity: calc(var(--fo) * (var(--fo) * .6 + .4)); }

/* ---------------- 5 · hover 倾斜时，影子跟着卡一起倒（2026-10-17 Wanfang 真机反馈） ----------------
   根因：peek.js 的倾斜只写进 .flip 的 transform（peek.css：translateZ(zf+pz) rotateX(tiltx) rotateY(th+tilty)），
   而影子元素 i.sh 是 .flip 的兄弟元素，平贴在 tile 上，不跟倾斜。卡倒下去 15° 时它在屏幕上的轮廓收窄、变成梯形，
   i.sh 里的三层却还是原来的平矩形 —— 飞飞鱼的片是透明的，所以能直接看到底下那个不动的圆角矩形：
   .sh-con（片边挡光的那一圈细影）就是「第二个轮廓」，.sh-key 的彩色光斑就是「底下多出来的一块」。
   （旧 v4 的 .sh 也不跟倾斜，只是被 50% 不透明的蓝片盖住了，看不出来。纸卡同理：倾斜时影子会从卡缩进去的那一侧露出来。）
   片本身的所有层（.sheet 的底色、外缘、高光、片厚 .edge、印字的两面）本来就都在 .flip 里，跟卡面同一个变换，不用动。
   修法：i.sh 用和卡完全相同的倾斜（同一个 perspective、同一个中心、同样的 rotateX / rotateY），只是不往外 translateZ（影子在墙上）。
   这样影子的轮廓就是卡在墙上的投影：随 --tiltx / --tilty 同步收窄、变梯形，靠墙的那条边严丝合缝；光的偏移照旧在它上面算。
   翻面时 .sh 的宽度仍由 flip.js 的 |cos θ| 收窄；背面朝外时 peek.js 写进来的 --tilty 已经取了反，
   而 rotateY(180° + t) 在墙上的轮廓和 rotateY(t) 一样，所以这里直接用 --tilty，正反面都对得上。 */
:root.ds .flip-tile > .sh {
  transform: rotateX(calc(var(--tiltx) * 1deg)) rotateY(calc(var(--tilty) * 1deg));
}
@media (prefers-reduced-motion: reduce) { :root.ds .flip-tile > .sh { transform: none; } }
