/* PinkyDolly V2 · pd-v2-twins
 * 从定稿原型抽出,参数已实测。⚠️ 图片/视频路径需换成 SHOPLINE CDN 地址。
 * 不要修改数值 —— 见 docs/pd-v2-brief.md
 */

/* ══ B TWIN — 杂志式排版 ══ */
.twin{position:relative;display:flex;min-height:clamp(620px,96svh,940px);overflow:hidden;
  isolation:isolate;touch-action:pan-y}
.side{position:relative;flex:0 0 50%;overflow:hidden;display:block;
  transition:flex-basis .8s var(--e),filter .7s var(--e)}
.side--p{background:#FE0373}
.side--d{background:#C527F3}

/* 第 1 层:跟随指针的光 —— 半径更小、更亮 */
.side__light{position:absolute;inset:0;z-index:1;pointer-events:none;opacity:0;
  background:radial-gradient(230px 230px at var(--lx,50%) var(--ly,50%),
    rgba(255,255,255,.62), rgba(255,255,255,.20) 34%, transparent 62%);
  transition:opacity .45s var(--e)}
.twin[data-near="p"] .side--p .side__light,
.twin[data-near="d"] .side--d .side__light{opacity:1}

/* 大字层 —— 在人物之后 */
/* weight 800→700, letter-spacing -.035em→-.025em: over the level-1 cap
   and tracking band (SPEC §0.1) — logo is the only 900 exception. */
.side__type{position:absolute;z-index:2;display:grid;color:#FFF7F1;
  font-family:var(--disp);font-variation-settings:var(--fr-display);font-weight:650;
  font-size:clamp(58px,10.4vw,168px);line-height:.82;letter-spacing:-.025em;
  pointer-events:none;user-select:none}
.side__type span{display:block}
.side--p .side__type{right:clamp(6px,2.4vw,42px);bottom:clamp(228px,34vh,380px);text-align:right}
.side--d .side__type{left:clamp(6px,2.4vw,42px);bottom:clamp(228px,34vh,380px);text-align:left}

/* 人物层 —— 压在大字之上。F 方案:3D 倾身 + 位移 + 影子 + 级联 + 呼吸加快 */
.fig{position:absolute;z-index:3;bottom:clamp(170px,26vh,290px);
  height:clamp(290px,58vh,600px);pointer-events:none;
  perspective:900px;perspective-origin:50% 60%}
.side--p .fig{left:clamp(10px,7vw,120px)}
.side--d .fig{right:clamp(10px,5vw,96px)}

/* 影子 —— 「离地更近」唯一的物理线索 */
.fig__shadow{position:absolute;left:50%;bottom:-2%;translate:-50% 0;z-index:0;
  width:44%;height:3.4%;border-radius:50%;background:rgba(18,6,14,.5);
  filter:blur(10px);
  transition:transform .95s var(--e-out),opacity .95s var(--e-out),filter .95s var(--e-out)}

/* holder 只负责 3D 旋转。⚠️ 居中用 flex,不要在 transform 里再写 translateX(-50%) ——
   与外层的 translate 属性叠加会变成整体平移半个身位(之前踩过)。 */
.fig__holder{height:100%;display:flex;align-items:flex-end;justify-content:center;
  transform-style:preserve-3d;transform-origin:bottom center;
  transition:transform 1s var(--e-out);will-change:transform}

.fig img{max-height:100%;max-width:100%;width:auto;height:auto;--s:1;
  transform-origin:bottom center;
  filter:drop-shadow(0 26px 34px rgba(20,8,18,.42));
  transition:filter .95s var(--e-out);will-change:transform,filter;
  animation:breathe 5.6s ease-in-out infinite}
.side--d .fig img{animation-duration:6.4s;animation-delay:-1.8s}
@keyframes breathe{
  0%,100%{transform:translate3d(0,var(--ty0,0%),0) scale(var(--s,1))}
  50%    {transform:translate3d(0,calc(var(--ty0,0%) - 0.9%),0) scale(calc(var(--s,1) * 1.008))}
}

.side--p{--rim:rgba(255,241,79,.46)}
.side--d{--rim:rgba(154,245,216,.42)}

/* ── 选中态:级联 0 / 60 / 100 / 120ms ── */
.twin[data-near="p"] .side--p .fig__holder,
.twin[data-near="d"] .side--d .fig__holder{
  transform:rotateY(calc(var(--tx,0) * 3.6deg)) rotateX(calc(var(--ty,0) * -2.4deg));
  transition:transform .42s var(--e-in) 60ms}
.twin[data-near="p"] .side--p .fig img,
.twin[data-near="d"] .side--d .fig img{
  --s:1.045; --ty0:-2%; animation-duration:4.2s;
  filter:drop-shadow(0 26px 36px rgba(20,8,18,.5))
         drop-shadow(0 0 16px rgba(255,255,255,.52))
         drop-shadow(0 0 44px var(--rim));
  transition:filter .5s var(--e-in) 100ms}
.twin[data-near="p"] .side--p .fig__shadow,
.twin[data-near="d"] .side--d .fig__shadow{
  transform:translate(-50%,4px) scale(1.24);opacity:.74;filter:blur(16px);
  transition:transform .5s var(--e-in) 120ms,opacity .5s var(--e-in) 120ms,filter .5s var(--e-in) 120ms}

/* 文案层 */
.side__txt{position:absolute;z-index:4;bottom:clamp(26px,4.4vh,48px);
  left:clamp(16px,3.4vw,60px);right:clamp(16px,3.4vw,60px);color:#FFF7F1}
.side--d .side__txt{text-align:left}
/* White text straight on the raw brand fills (--btn2 #FE0373 / --btn1-ish #C527F3)
   measured 3.62–4.00:1 — contrast audit (feedback item 5), and brief rule #15 is
   explicit that raw brand colour must never sit directly behind text for exactly
   this reason. Controls over photo/art layers keep a fixed colour regardless of
   mode (brief's own rule), so the fix is a scrim behind the text, not a token
   swap: a 20% black floor already clears 5.3+:1 on both sides, this goes well
   past that for margin since the character art behind it varies in local
   darkness. .side__txt is already position:absolute, so its own box is a
   containing block for this negative-inset ::before — the scrim sizes itself to
   the text block without needing its own hardcoded position/width/height, and
   without touching .side__txt's own bottom/left/right placement at all. */
.side__txt::before{content:"";position:absolute;z-index:-1;inset:-14px -20px -18px -20px;
  border-radius:var(--r);
  background:linear-gradient(to bottom, transparent, rgba(0,0,0,.55) 35%, rgba(0,0,0,.62))}
/* Yimei wants each sentence of the lede on its own line — the setting's
   value carries real newlines, white-space:pre-line is what turns those
   into visible breaks (a plain <p> collapses them by default). */
.side__lede{font-size:clamp(14px,1.28vw,19px);line-height:1.55;font-weight:600;white-space:pre-line}
.side__body{margin-top:11px;font-size:clamp(13px,1.05vw,16px);line-height:1.6;
  color:rgba(255,247,241,.88);max-width:46ch}
.side__body b{font-weight:800;color:#FFF7F1}
/* Was background:var(--bk) (the Utility dark pill) — sitting directly on
   .side__txt::before's own dark scrim (see that rule's comment), the dark-on-
   dark button lost its own edge and read as barely-there (feedback: "the black
   shade making the button more dark and unseen"). Swapped to the Primary/
   Secondary brand accents instead — still a documented button colour (brief
   rule #15), and bright enough against the scrim to stay a clearly visible,
   separate shape rather than blending into it. */
.side__go{margin-top:16px;display:inline-flex;align-items:center;min-height:48px;padding:0 24px;
  border-radius:var(--r);background:var(--act);color:var(--bw);font-size:14.5px;font-weight:700;
  border:2px solid transparent;
  transition:background .25s var(--e),color .25s var(--e),border-color .25s var(--e),
    transform .45s var(--e),box-shadow .45s var(--e)}
.side--d .side__go{background:var(--act2)}
@media (hover:hover) and (pointer:fine){
  .side--p .side__go:hover{background:var(--bw);color:var(--act);border-color:var(--act)}
  .side--d .side__go:hover{background:var(--bw);color:var(--act2);border-color:var(--act2)}
}
@media (hover:none){
  .side__go:active{background:var(--bw);color:var(--act)}
  .side--d .side__go:active{color:var(--act2)}
}
.twin[data-near="p"] .side--p .side__go,
.twin[data-near="d"] .side--d .side__go{transform:translateY(-4px);
  box-shadow:0 14px 26px -8px rgba(20,8,18,.55)}

/* 第 3 层:没被选中的一侧 —— 失落 */
.twin[data-near="p"] .side--d,
.twin[data-near="d"] .side--p{filter:saturate(.6) brightness(.8)}
.twin[data-near="p"] .side--d .fig img,
.twin[data-near="d"] .side--p .fig img{animation-play-state:paused}

/* 字标:两半各自贴中缝,随分屏移动 + 随指针视差 */
/* 字标整体跟随中缝。放在 .twin 层级 —— 放在面板内会被 overflow:hidden 裁掉花体,
   而给面板开 overflow:visible 又会让轮廓光的 drop-shadow 露出矩形边。 */
.twin{--hdH:clamp(62px,6vw,96px)}
.twin__logo{position:absolute;z-index:6;top:calc(clamp(18px,4vh,46px) + var(--hdH,0px));
  left:var(--split,50%);translate:-50% 0;display:flex;align-items:flex-start;
  pointer-events:none;transition:left .8s var(--e),transform .55s var(--e);
  transform:translate3d(var(--lgx,0px),var(--lgy,0px),0);will-change:transform}

/* ══ Final headline + collection pills (SPEC-addendum-hero-details-gallery.md
   §7). Replaces the giant up-close/from-above type — hidden below, not
   deleted, since pinky_big_1/2 and dolly_big_1/2 still feed the mobile twin
   slider's own kicker text. Headline sits above the seam/logo and doesn't
   follow the split; collection pills move to an eyebrow label on the side's
   own text block below 1200px instead of overlapping the headline.

   2026-10-11(SPEC-typography-v2-opsz72.md): opsz 72's glyphs run ~12% wider
   than opsz 144's, so "Who gets to ruin you tonight?" needed both a smaller
   clamp (4.1vw/68px → 3.9vw/64px) and the pill breakpoint pushed from 1100
   to 1200 to keep one line with the pills clear of it — checked at
   1100/1199/1200/1280/1440/1920, minimum pill gap 33px at 1200. ══ */
.side__type{display:none}
.tw-hd{position:absolute;z-index:7;top:clamp(18px,4vh,46px);left:0;right:0;
  padding:0 clamp(170px,15vw,240px);text-align:center;pointer-events:none;color:#FFF7F1}
.tw-hd__h{margin:0;font-family:var(--disp);font-variation-settings:var(--fr-display);
  font-weight:650;font-size:clamp(34px,3.9vw,64px);line-height:1.02;letter-spacing:-.02em;
  text-wrap:balance;text-shadow:0 2px 26px rgba(26,17,24,.38)}
.tw-pill{position:absolute;z-index:6;top:clamp(18px,4vh,46px);padding:9px 14px;border-radius:999px;
  background:rgba(26,17,24,.42);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  color:#FFF7F1;font:800 12px/1 var(--ui);letter-spacing:.16em;text-transform:uppercase;white-space:nowrap}
.side--p .tw-pill{left:clamp(16px,3vw,44px)}
.side--d .tw-pill{right:clamp(16px,3vw,44px)}
.tw-lab{display:none;margin-bottom:12px;font:800 12px/1 var(--ui);letter-spacing:.2em;
  text-transform:uppercase;opacity:.92}
@media (max-width:1199px){
  .tw-pill{display:none}
  .tw-lab{display:block}
  .tw-hd{padding:0 clamp(16px,4vw,40px)}
}
.lg{height:clamp(40px,6.4vw,104px);width:auto;display:block;
  filter:drop-shadow(0 6px 20px rgba(20,8,18,.34))}

.seam{position:absolute;top:0;bottom:0;width:2px;z-index:5;pointer-events:none;left:50%;
  background:linear-gradient(to bottom,transparent,rgba(255,247,241,.5) 50%,transparent);
  transition:left .8s var(--e)}

@media (max-width:860px){
  .twin{flex-direction:column;min-height:0}
  .side{flex:0 0 auto!important;min-height:92svh}
  .seam{display:none}
  .twin__logo{top:clamp(14px,2.4vh,26px);font-size:clamp(30px,9vw,54px)}
  .side__type{font-size:clamp(52px,17vw,104px);bottom:auto;top:clamp(88px,13vh,140px)}
  .side--p .side__type{right:clamp(10px,4vw,26px)}
  .side--d .side__type{left:clamp(10px,4vw,26px)}
  .fig{bottom:clamp(180px,30vh,300px);height:clamp(260px,42vh,420px)}
  .side--p .fig{left:50%;translate:-50% 0}
  .side--d .fig{right:auto;left:50%;translate:-50% 0}
  .side__txt{bottom:clamp(20px,3vh,34px)}
  .side[data-act="0"]{filter:saturate(.6) brightness(.8)}
  .side[data-act="0"] .fig img{animation-play-state:paused}
  .side[data-act="1"] .side__light{opacity:1}
  .side[data-act="1"] .fig img{
    filter:drop-shadow(0 22px 30px rgba(20,8,18,.5))
           drop-shadow(0 0 14px rgba(255,255,255,.48))
           drop-shadow(0 0 40px var(--rim))}
}

/* ══ D (SPEC-cart-voices-twoways-mobile.md §D) — mobile-only (<=760px) twin
   picker: split screen + draggable "or" slider. Ported from
   handover/v2/pages/_shared/twin-slider-mobile.css (the approved interactive
   prototype) — numeric values (breathing timing, clip-path math, snap points)
   kept as tested there. Renamed .sw* to .pd-v2-sw* (brief rule 9) and made
   two changes the prototype didn't need: .pd-v2-sw__k/.pd-v2-sw__tg bumped
   from the prototype's 10.5px to 13px (brief rule 17/31 — nothing renders
   under 12px, labels sit at ~13-15px sitewide) and .pd-v2-sw__go got an
   explicit 44px min-height (brief rule 21 — the prototype's own padding only
   produced ~36px). Fixed dark panel in both colour modes, same as the
   collectible cards and this file's own desktop .side colours — not a
   missed mode binding. */
.pd-v2-sw{display:none}
@property --pdswx{syntax:'<number>';inherits:true;initial-value:50}
@media (max-width:760px){
  .twin{display:none}
  .pd-v2-sw{--pdswx:50;display:flex;flex-direction:column;height:92svh;min-height:560px;
    background:#1A1118;color:#FFF7F1;font-family:var(--ui)}
}
.pd-v2-sw__cap{margin:18px 16px 0;color:var(--main1)}
.pd-v2-sw__q{margin:8px 16px 14px;font-family:var(--disp);font-variation-settings:var(--fr-display);
  font-weight:650;font-size:32px;line-height:1.04;letter-spacing:-.012em}
.pd-v2-sw__stage{position:relative;flex:1;min-height:0;overflow:hidden;touch-action:pan-y;
  user-select:none;-webkit-user-select:none}
.pd-v2-sw.is-snap{transition:--pdswx .5s cubic-bezier(.22,1.14,.36,1)}
.pd-v2-sw__half{position:absolute;inset:0;color:#FFF7F1;text-decoration:none;-webkit-tap-highlight-color:transparent}
.pd-v2-sw__half--p{background:radial-gradient(60% 45% at calc(var(--pdswx)*.5%) 38%,#FF5AA8,transparent 70%),#C9005B;
  clip-path:inset(0 calc(100% - var(--pdswx)*1%) 0 0)}
.pd-v2-sw__half--d{background:radial-gradient(60% 45% at calc(var(--pdswx)*1% + (100 - var(--pdswx))*.5%) 38%,#D86BFF,transparent 70%),#8A16B8;
  clip-path:inset(0 0 0 calc(var(--pdswx)*1%))}
/* figure stays centred in its own visible slice; grows as that slice widens */
.pd-v2-sw__fig{position:absolute;top:9%;width:auto;max-width:none;translate:-50% 0;
  filter:drop-shadow(0 12px 22px rgba(0,0,0,.35));pointer-events:none}
.pd-v2-sw__half--p .pd-v2-sw__fig{left:calc(var(--pdswx)*.5%);height:clamp(46%,calc(58% + (var(--pdswx) - 50)*.55%),76%)}
.pd-v2-sw__half--d .pd-v2-sw__fig{left:calc(var(--pdswx)*1% + (100 - var(--pdswx))*.5%);height:clamp(46%,calc(58% + (50 - var(--pdswx))*.55%),76%)}
.pd-v2-sw__copy{position:absolute;bottom:0;padding:70px 14px 16px;display:grid;gap:7px;
  background:linear-gradient(transparent,rgba(26,17,24,.86) 50%)}
.pd-v2-sw__half--p .pd-v2-sw__copy{left:0;width:calc(var(--pdswx)*1%);opacity:clamp(0,(var(--pdswx) - 28)/12,1)}
.pd-v2-sw__half--d .pd-v2-sw__copy{right:0;width:calc(100% - var(--pdswx)*1%);text-align:right;justify-items:end;
  opacity:clamp(0,(72 - var(--pdswx))/12,1)}
.pd-v2-sw__k{font:800 13px/1 var(--ui);letter-spacing:.18em;text-transform:uppercase;opacity:.86}
.pd-v2-sw__nm{font-family:var(--disp);font-variation-settings:var(--fr-display);font-weight:650;
  font-style:normal;line-height:1;letter-spacing:-.01em}
.pd-v2-sw__half--p .pd-v2-sw__nm{font-size:calc(32px + clamp(0,(var(--pdswx) - 50)/35,1)*10px)}
.pd-v2-sw__half--d .pd-v2-sw__nm{font-size:calc(32px + clamp(0,(50 - var(--pdswx))/35,1)*10px)}
.pd-v2-sw__tg{display:flex;flex-wrap:wrap;gap:4px}.pd-v2-sw__half--d .pd-v2-sw__tg{justify-content:flex-end}
.pd-v2-sw__tg i{font:700 13px/1 var(--ui);font-style:normal;padding:5px 8px;border-radius:999px;background:rgba(26,17,24,.38)}
/* expanded content only appears once dragged well into that side */
.pd-v2-sw__more{overflow:hidden;display:grid;gap:8px}
.pd-v2-sw__half--p .pd-v2-sw__more{max-height:calc(clamp(0,(var(--pdswx) - 58)/22,1)*140px);
  opacity:clamp(0,(var(--pdswx) - 58)/22,1)}
.pd-v2-sw__half--d .pd-v2-sw__more{max-height:calc(clamp(0,(42 - var(--pdswx))/22,1)*140px);
  opacity:clamp(0,(42 - var(--pdswx))/22,1)}
.pd-v2-sw__more p{margin:4px 0 0;font-size:14.5px;line-height:1.45;white-space:pre-line}
.pd-v2-sw__go{display:inline-flex;align-items:center;justify-self:start;min-height:44px;padding:0 14px;
  border-radius:6px;background:#FFF7F1;color:#262026;font:700 14px/1 var(--ui)}
.pd-v2-sw__half--d .pd-v2-sw__go{justify-self:end}
/* split line + "or" handle */
.pd-v2-sw__line{position:absolute;top:0;bottom:0;left:calc(var(--pdswx)*1%);width:2px;translate:-1px 0;
  background:rgba(255,247,241,.85);pointer-events:none}
.pd-v2-sw__or{position:absolute;top:50%;left:calc(var(--pdswx)*1%);translate:-50% -50%;width:64px;height:64px;
  border-radius:50%;border:0;padding:0;display:flex;align-items:center;justify-content:center;gap:3px;
  background:#1A1118;color:#FFF7F1;box-shadow:0 0 0 3px #FFF7F1,0 10px 24px rgba(0,0,0,.4);
  font:600 18px/1 var(--disp);font-variation-settings:"SOFT" 0,"WONK" 0;letter-spacing:.02em;
  cursor:grab;touch-action:none}
.pd-v2-sw__or:active{cursor:grabbing;scale:1.06}
.pd-v2-sw__or:focus-visible{outline:3px solid var(--lemon);outline-offset:4px}
.pd-v2-sw__ch{font:700 14px/1 var(--ui);opacity:.7}
.pd-v2-sw__or.is-hint{animation:pdswHint 1.6s ease-in-out .6s 1}
@keyframes pdswHint{0%,100%{translate:-50% -50%}25%{translate:-30% -50%}60%{translate:-70% -50%}}
.pd-v2-sw__hint{margin:10px 16px 14px;text-align:center;font-size:12.5px;color:rgba(255,247,241,.7)}
@media (prefers-reduced-motion:reduce){
  .pd-v2-sw.is-snap{transition:none}
  .pd-v2-sw__or.is-hint{animation:none}
}

/* breathing — same keyframe shape and numbers as the prototype reference
   (5.6s, up 0.9% / scale x1.008, Dolly offset half a cycle at -2.8s); the
   desktop .fig img above uses its own separately-tuned 5.6s/6.4s pairing,
   independent of this mobile-only element. */
.pd-v2-sw__fig{--s:1;--ty0:0%;transform-origin:50% 100%;animation:breathe 5.6s ease-in-out infinite;
  transition:filter .5s cubic-bezier(.22,1.14,.36,1)}
.pd-v2-sw__half--d .pd-v2-sw__fig{animation-delay:-2.8s}
.pd-v2-sw__half--p{--rim:rgba(255,241,79,.46)}.pd-v2-sw__half--d{--rim:rgba(154,245,216,.42)}
.pd-v2-sw[data-focus="p"] .pd-v2-sw__half--p .pd-v2-sw__fig,
.pd-v2-sw[data-focus="d"] .pd-v2-sw__half--d .pd-v2-sw__fig{
  --s:1.045;--ty0:-2%;animation-duration:4.2s;
  filter:drop-shadow(0 22px 30px rgba(20,8,18,.5)) drop-shadow(0 0 14px rgba(255,255,255,.48)) drop-shadow(0 0 40px var(--rim))}
.pd-v2-sw[data-focus="p"] .pd-v2-sw__half--d .pd-v2-sw__fig,
.pd-v2-sw[data-focus="d"] .pd-v2-sw__half--p .pd-v2-sw__fig{
  animation-play-state:paused;filter:saturate(.6) brightness(.8) drop-shadow(0 12px 22px rgba(0,0,0,.35))}
@media (prefers-reduced-motion:reduce){.pd-v2-sw__fig{animation:none}}
