// lvpai-works.jsx — 作品页（V3：创意摄影 → 赠送个性化文创 → 赠送全部机位高清底片）
const { LV_T: W_T, Icon: WIcon, Photo: WPhoto, Btn: WBtn, Card: WCard, Eyebrow: WEye,
  Price: WPrice, FreeTag: WFree, Divider: WDiv, WeChatNav: WNav, Screen: WScreen,
  StatusChip: WChip, SectionHead: WSection } = window;

// ── 金色（突出购买与赠送权益） ──────────────────────────────────────
const GOLD = {
  grad: 'linear-gradient(135deg, #F4DC9A 0%, #E6BE68 46%, #CC9C40 100%)',
  solid: '#D6AC54',
  deep: '#9A7322',
  text: '#4A3209',
  hi: '#E8C87A',
  tint: 'rgba(214,172,84,0.12)',
  line: 'rgba(214,172,84,0.42)' };


// ── 机位（演示数据：前两个有照片，第三个已绑卡照片未到） ────────────────
// 机位卡「第二排」统一文字样式（本组共xx张照片 / 正在等待同步 / 前往机位…）
const POS_ROW2 = { fontFamily: W_T.sans, fontSize: 14, fontWeight: 700, color: W_T.ink, margin: '7px 0 11px' };
const POS_PHOTO_OVERRIDES = { 1: ['assets/pos1-0.jpg', 'assets/pos1-1.jpg', 'assets/pos1-2.jpg', 'assets/pos1-3.jpg'], 2: ['assets/pos2-0.jpg', 'assets/pos2-1.jpg', 'assets/pos2-2.jpg'] };
function posThumbSrc(no, i) { const ov = POS_PHOTO_OVERRIDES[no]; return (ov && ov[i]) || null; }

const POSITIONS = [
{ no: 1, sc: 'ylxs', name: '蓝月谷入口', time: '今日 12:11', count: 4 },
{ no: 2, sc: 'ylxs', name: '蓝月谷出口', time: '今日 14:11', count: 3 },
{ no: 3, sc: 'ylxs', name: '云杉坪观景台', waiting: true, time: '今日 13:02', src: 'assets/p5.jpg', card: '8 4 2 7 1 5' },
{ no: 6, sc: 'ylxs', name: '甘海子草甸', ready: true, time: '今日 13:40', card: '2 7 4 8 3 1' },
{ no: 4, sc: 'ylxs', name: '玉液湖栈道', scenic: '玉龙雪山·蓝月谷', src: 'assets/p4.jpg', timeout: true, time: '今日 15:24', card: '5 1 9 3 6 2', walk: '步行约 6 分钟', landmark: '蓝月谷主步道中段',
  guide: [
  { img: 'assets/p1.jpg', t: '从蓝月谷观光车「白水河」站下车，面向雪山方向' },
  { img: 'assets/p6.jpg', t: '沿木栈道向上游步行约 6 分钟，留意右侧湖景' },
  { img: 'assets/p4.jpg', t: '见「玉液湖」指示牌后右转下栈道，即到拍摄平台' }] },
{ no: 5, sc: 'ylxs', name: '听涛台', scenic: '玉龙雪山·云杉坪', src: 'assets/p9.jpg', unbound: true, walk: '步行约 10 分钟', landmark: '云杉坪环山步道',
  guide: [
  { img: 'assets/p3.jpg', t: '乘云杉坪索道至山上站，出站后向左' },
  { img: 'assets/p9.jpg', t: '沿环山步道步行约 10 分钟至「听涛台」' },
  { img: 'assets/p10.jpg', t: '拍摄点在平台东侧，向工作人员出示套餐' }] },
{ no: 1, sc: 'gucheng', name: '四方街机位', time: '昨日 19:26', count: 4 },
{ no: 2, sc: 'gucheng', name: '五一街灯火机位', scenic: '丽江古城', src: 'assets/p7.jpg', unbound: true, walk: '步行约 4 分钟', landmark: '五一街兴仁巷口',
  guide: [{ img: 'assets/p8.jpg', t: '从四方街向东沿五一街步行约 4 分钟' }] },
{ no: 1, sc: 'lugu', name: '情人滩机位', time: '7 月 18 日 17:40', count: 3 },
{ no: 2, sc: 'lugu', name: '里格半岛机位', time: '7 月 18 日 18:52', count: 3 }];

const SHOT_TOTAL = 23;

// ── 个性化文创赠品（购买创意摄影随单送达） ─────────────────────────────
const GIFTS = [
{ id: 'moon', name: '星月音乐相框', src: 'assets/g-moon.jpg', spec: '定制月相 · 扫码听歌', was: 39.9, tier: '随 1 套起赠' },
{ id: 'lamp', name: '极光氛围灯', src: 'assets/g-lamp.jpg', spec: '旋转灯罩 · 触控调光', was: 49.9, tier: '随 2 套起赠' },
{ id: 'ball', name: '星河玻璃球灯', src: 'assets/g-ball.jpg', spec: '手作星河 · 暖光木座', was: 59.9, tier: '随 3 套赠' },
{ id: 'mountain', name: '日照金山灯箱', src: 'assets/g-mountain.jpg', spec: '雪山光影 · 实木灯箱', was: 89, tier: '随 3 套赠' }];


// ── 赠送区段标题（金色 限时赠送 徽标） ─────────────────────────────────
function GiftSectionHead({ title, right }) {
  const T = W_T;
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', padding: '0 2px', marginBottom: 14 }}>
      <div>
        <div style={{ fontFamily: T.sans, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: 0.2, lineHeight: 1.2 }}>{title}</div>
      </div>
      {right && <span style={{ fontFamily: T.sans, fontSize: 12.5, color: T.muted, flexShrink: 0, marginBottom: 2 }}>{right}</span>}
    </div>);

}

// ════════════════════════════════════════════════════════════════
// 1) 创意摄影 HERO —— 作品页核心业务
// ════════════════════════════════════════════════════════════════
function ChuangyiHero({ nav, mode, onBuy, store }) {
  const T = W_T;
  const C = window.LV_DATA.CHUANGYI;
  const themes = window.LV_DATA.THEMES;
  const [noSets, setNoSets] = React.useState(false);
  const availableSets = store && store.stage === 'bought' ? 1 : 0;
  const onCTA = () => { if (availableSets > 0) nav.push('customize', { sel: [] }); else setNoSets(true); };
  return (
    <>
    <div style={{ position: 'relative', margin: '4px 18px 0', borderRadius: 8, overflow: 'hidden', background: T.ink, boxShadow: '0 16px 36px rgba(26,25,22,0.24)' }}>
      {/* 顶部金色微光 */}
      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 180, background: 'radial-gradient(125% 100% at 50% -10%, rgba(232,200,122,0.16), transparent 62%)', pointerEvents: 'none' }} />

      <div style={{ position: 'relative', padding: '24px 18px 20px' }}>
        {/* 标题 */}
        <div style={{ fontFamily: T.sans, fontSize: 11, fontWeight: 700, letterSpacing: 2, color: GOLD.hi }}>玉龙雪山限定</div>
        <div style={{ fontFamily: T.sans, color: '#fff', fontWeight: 700, fontSize: 27, letterSpacing: 0.5, marginTop: 8 }}>创意摄影</div>
        <div style={{ fontFamily: T.serif, fontSize: 13.5, color: 'rgba(255,255,255,0.74)', letterSpacing: 0.3, marginTop: 7 }}>1 分钟拍大片 · 精华机位通通打卡</div>

        {/* 主题网格 3×（共 5 个） */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 9, marginTop: 18 }}>
          {themes.slice(0, 3).map((th) =>
          <WPhoto key={th.id} src={th.src} ratio="3 / 4" radius={7} label={th.name}
          onClick={() => nav.push('themeDetail', { themeId: th.id, mode })} />
          )}
        </div>

        {/* 金色 CTA —— 购买套餐 */}
        <button onClick={onCTA} style={{
          width: '100%', marginTop: 18, fontFamily: T.sans, fontSize: 16.5, fontWeight: 700, letterSpacing: 0.4,
          color: GOLD.text, background: GOLD.grad, border: 'none', borderRadius: 8, padding: '15px 0', cursor: 'pointer',
          boxShadow: '0 8px 22px rgba(204,156,64,0.42), inset 0 1px 0 rgba(255,255,255,0.5)' }}
        onMouseDown={(e) => e.currentTarget.style.transform = 'scale(0.985)'}
        onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
        onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}>
          定制我的创意摄影
        </button>

        {/* 赠送提示条 */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, marginTop: 14, padding: '10px 12px', borderRadius: 7, background: 'rgba(232,200,122,0.11)', boxShadow: 'inset 0 0 0 1px rgba(232,200,122,0.22)' }}>
          <WIcon name="gift" size={14} color={GOLD.hi} stroke={1.8} />
          <span style={{ fontFamily: T.sans, fontSize: 12, fontWeight: 600, color: GOLD.hi, letterSpacing: 0.2 }}>限时赠送 · 本景区全部机位高清底片</span>
        </div>
      </div>
    </div>
    <WChDialog open={noSets}
      body={<>你还没有可用的创意摄影套数，需先购买套餐才能开始定制。</>}
      okText="立即购买" okGold
      onCancel={() => setNoSets(false)}
      onOk={() => { setNoSets(false); onBuy(); }} />
    </>);

}

// ════════════════════════════════════════════════════════════════
// 2) 限时赠送 · 个性化文创（横向滑动）
// ════════════════════════════════════════════════════════════════
function GiftCard({ g, nav, paid, onBuy, showcase, store }) {
  const T = W_T;
  // 该商品对应的文创订单状态 → 决定按钮与跳转
  const ord = !showcase && store ? (store.orders || []).find((o) => o.cat === '文创' && o.gift && o.gift.name === g.name) : null;
  const st = ord && ord.state;
  let label = '开始定制', kind = 'primary', act = () => nav.push('wcCustomize', { gift: g });
  if (st === '等待制作' || st === '待制作') { label = '修改内容'; kind = 'outline'; act = () => nav.push('wcCustomize', { gift: g, orderId: ord.id, mode: 'edit' }); }
  else if (st === '正在制作' || st === '等待领取' || st === '已完成') { label = '查看内容'; kind = 'outline'; act = () => nav.push('wcCustomize', { gift: g, orderId: ord.id, mode: 'view' }); }
  else if (st === '已超时') { label = '定制已失效'; kind = 'disabled'; act = null; }
  const btnStyle = kind === 'primary' ?
    { width: '100%', marginTop: 13, fontFamily: T.sans, fontSize: 14, fontWeight: 700, color: GOLD.text, background: GOLD.grad, border: 'none', borderRadius: 6, padding: '11px 0', cursor: 'pointer', boxShadow: '0 5px 14px rgba(204,156,64,0.36), inset 0 1px 0 rgba(255,255,255,0.5)' } :
    kind === 'disabled' ?
    { width: '100%', marginTop: 13, fontFamily: T.sans, fontSize: 14, fontWeight: 600, color: T.faint, background: T.field, border: 'none', boxShadow: 'inset 0 0 0 1px ' + T.line, borderRadius: 6, padding: '10px 0', cursor: 'default' } :
    { width: '100%', marginTop: 13, fontFamily: T.sans, fontSize: 14, fontWeight: 600, color: T.ink, background: '#fff', border: 'none', boxShadow: 'inset 0 0 0 1.5px ' + T.ink, borderRadius: 6, padding: '10px 0', cursor: 'pointer' };
  const photoClick = showcase ? () => nav.push('wenchuangDetail', { gift: g }) : act || undefined;
  return (
    <div style={{ width: 170, flexShrink: 0, background: T.card, borderRadius: 8, overflow: 'hidden', display: 'flex', flexDirection: 'column',
      boxShadow: '0 1px 2px rgba(26,25,22,0.04), 0 10px 24px rgba(26,25,22,0.05)' }}>
      <WPhoto src={g.src} ratio="1 / 1" pos="center" radius={0} onClick={nav ? photoClick : undefined} />
      <div style={{ padding: '12px 13px 14px' }}>
        <div style={{ fontFamily: T.sans, fontSize: 14.5, fontWeight: 700, color: T.ink, lineHeight: 1.3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{g.name}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginTop: 7, flexWrap: 'nowrap' }}>
          <span style={{ fontFamily: T.serif, fontSize: 11, color: T.faint, textDecoration: 'line-through', whiteSpace: 'nowrap' }}>原价 ¥{g.was}</span>
          <span style={{ width: 1, height: 10, background: T.line, flexShrink: 0 }} />
          {showcase ?
          <button onClick={onBuy} style={{ display: 'inline-flex', alignItems: 'center', gap: 0, fontFamily: T.sans, fontWeight: 700, color: GOLD.deep, fontSize: 10.5, background: 'none', border: 'none', padding: 0, cursor: 'pointer', whiteSpace: 'nowrap', WebkitTapHighlightColor: 'transparent' }}>购买套餐赠送<WIcon name="chevR" size={11} color={GOLD.deep} stroke={2.2} /></button> :
          st ?
          <span style={{ fontFamily: T.sans, fontWeight: 700, color: st === '已超时' ? T.faint : GOLD.deep, fontSize: 10.5, whiteSpace: 'nowrap' }}>{st}</span> :
          <span style={{ fontFamily: T.sans, fontWeight: 700, color: T.ink, fontSize: 10.5, whiteSpace: 'nowrap' }}>购买套餐赠送</span>}
        </div>
        {!showcase &&
        <button onClick={act || undefined} disabled={kind === 'disabled'} style={btnStyle}>{label}</button>}
      </div>
    </div>);

}

function giftRank(store, g) {
  const ord = store ? (store.orders || []).find((o) => o.cat === '文创' && o.gift && o.gift.name === g.name) : null;
  const st = ord && ord.state;
  if (st === '等待制作' || st === '待制作') return 1;          // 修改内容
  if (st === '正在制作' || st === '等待领取' || st === '已完成') return 2; // 查看内容
  if (st === '已超时') return 3;                              // 已失效
  return 0;                                                   // 开始定制（含待定制/无订单）
}

function GiftShowcase({ nav, paid, store }) {
  const T = W_T;
  // 需要定制的排最前（开始定制 → 修改内容 → 查看内容 → 已失效）
  const sorted = [...GIFTS].sort((a, b) => giftRank(store, a) - giftRank(store, b));
  return (
    <div>
      <div style={{ padding: '0 18px' }}>
        <GiftSectionHead title="惊喜个性化文创" right="左右滑动 ›" />
      </div>
      <div className="lv-hscroll" style={{ display: 'flex', gap: 12, overflowX: 'auto', padding: '0 18px 4px' }}>
        {sorted.map((g) => <GiftCard key={g.id} g={g} nav={nav} paid={paid} store={store} />)}
      </div>
    </div>);

}

// ════════════════════════════════════════════════════════════════
// 3) 限时赠送 · 全部机位高清底片
// ════════════════════════════════════════════════════════════════
function PositionCard({ p, nav, onView, hidden, unread }) {
  const T = W_T;
  const [card, setCard] = React.useState(false);
  const hd = hidden || new Set();
  const thumbs = Array.from({ length: p.count }, (_, i) => ({ i, seed: window.lvSeed('pos' + p.no + '-' + i), src: posThumbSrc(p.no, i) }))
  .filter((t) => !hd.has(p.no + '|' + t.seed));
  return (
    <>
    <WCard pad={0} style={{ overflow: 'hidden', marginBottom: 13, position: 'relative' }}>
      {unread &&
      <span style={{ position: 'absolute', right: 12, top: 12, padding: '2px 8px', borderRadius: 4, background: '#2FA84F', color: '#fff', fontFamily: T.sans, fontSize: 10, fontWeight: 700, letterSpacing: 0.3, boxShadow: '0 2px 6px rgba(0,0,0,0.24)', zIndex: 1 }}>新</span>}
      <div style={{ padding: '14px 15px 15px' }}>
        <PosMetaRow p={p} onCard={() => setCard(true)} />
        <div style={POS_ROW2}>本组共 {thumbs.length} 张照片</div>
        <div className="lv-hscroll" style={{ display: 'flex', gap: 8, overflowX: 'auto' }}>
          {thumbs.map(({ i, seed, src }) =>
          <div key={i} style={{ width: 92, flexShrink: 0 }}>
              <WPhoto src={src} seed={src ? undefined : seed} ratio="1 / 1" radius={6} onClick={() => onView({ seed, src, posNo: p.no, spot: p.no + ' 号机位 · ' + p.name, idx: i + 1, count: p.count })} />
            </div>)}
        </div>
      </div>
    </WCard>
    {card && <CardCodeDialog p={p} onClose={() => setCard(false)} />}
    </>);

}

function UnboundPositionCard({ p, nav }) {
  const T = W_T;
  return (
    <WCard pad={0} style={{ overflow: 'hidden', marginBottom: 13 }}>
      <div style={{ padding: '14px 15px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontFamily: T.sans, fontSize: 12, color: T.muted }}>
          <span>{p.no} 号机位 · {p.name}</span>
          <span style={{ width: 1, height: 11, background: T.line }} />
          <span>未绑卡</span>
        </div>
        <PosStatus icon="camera" text="你尚未在本机位拍照" />
      </div>
    </WCard>);

}

function CardCodeDialog({ p, onClose }) {
  const T = W_T;
  const code = p.card || '8 4 2 7 1 5';
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 300, background: 'rgba(20,22,20,0.55)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 28 }}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: '100%', maxWidth: 300, background: T.card, borderRadius: 12, padding: '24px 22px', textAlign: 'center' }}>
        <div style={{ fontFamily: T.sans, fontSize: 16, fontWeight: 700, color: T.ink }}>取片卡</div>
        <div style={{ fontFamily: T.sans, fontSize: 12.5, color: T.muted, marginTop: 6, lineHeight: 1.6 }}>{p.no} 号机位 · {p.name}</div>
        <div style={{ margin: '16px auto 12px', width: 132, height: 132, background: '#fff', borderRadius: 8, padding: 8, boxShadow: '0 0 0 1px ' + T.line }}>{qrSvg(code)}</div>
        <div style={{ margin: '0 0 4px', fontFamily: T.sans, fontSize: 26, fontWeight: 800, letterSpacing: 3, color: T.ink }}>{code}</div>
        <div style={{ fontFamily: T.sans, fontSize: 11.5, color: T.faint, lineHeight: 1.6, marginTop: 8 }}>纸质取片卡丢失时，现场出示此码同样有效</div>
        <button onClick={onClose} style={{ width: '100%', marginTop: 18, padding: '11px 0', border: 'none', borderRadius: 8, background: T.ink, color: '#fff', fontFamily: T.sans, fontSize: 14.5, fontWeight: 700, cursor: 'pointer' }}>知道了</button>
      </div>
    </div>);
}

function PosStatus({ icon, text, color }) {
  const T = W_T;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 5, margin: '9px 0 0' }}>
      {icon === 'warn' ?
      <svg width="15" height="15" viewBox="0 0 16 16" fill="none"><path d="M8 2.4 14.6 13.4H1.4L8 2.4Z" fill="#F0B429" /><path d="M8 6.2v3.3M8 11.1h.01" stroke="#6B4A05" strokeWidth="1.5" strokeLinecap="round" /></svg> :
      <WIcon name={icon} size={14} color={color || T.muted} stroke={1.9} />}
      <span style={{ fontFamily: T.sans, fontSize: 13, color: color || T.muted }}>{text}</span>
    </div>);
}

function PosMetaRow({ p, onCard }) {
  const T = W_T;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontFamily: T.sans, fontSize: 12, color: T.muted }}>
      <span>{p.no} 号机位 · {p.name}</span>
      <span style={{ width: 1, height: 11, background: T.line }} />
      <span>{p.time}</span>
      <span style={{ width: 1, height: 11, background: T.line }} />
      <button onClick={onCard} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, background: T.field, border: 'none', borderRadius: 4, padding: '2px 7px 2px 6px', cursor: 'pointer', fontFamily: T.sans, fontSize: 11.5, fontWeight: 600, color: T.muted }}>
        <svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke={T.muted} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
          <rect x="2" y="3.5" width="12" height="9" rx="1.6" />
          <path d="M2 6.6h12M4.6 9.6h3.2" />
        </svg>取片卡<WIcon name="chevR" size={13} color={T.faint} stroke={2} />
      </button>
    </div>);
}

function qrSvg(seedStr) {
  const N = 25;
  let h = 2166136261;
  for (let i = 0; i < seedStr.length; i++) { h ^= seedStr.charCodeAt(i); h = Math.imul(h, 16777619); }
  const rnd = () => { h ^= h << 13; h ^= h >>> 17; h ^= h << 5; return ((h >>> 0) % 1000) / 1000; };
  const finder = (x, y) => (x < 7 && y < 7) || (x >= N - 7 && y < 7) || (x < 7 && y >= N - 7);
  const cells = [];
  for (let y = 0; y < N; y++) for (let x = 0; x < N; x++) { if (finder(x, y)) continue; if (rnd() > 0.52) cells.push(<rect key={x + '-' + y} x={x} y={y} width="1" height="1" />); }
  const eye = (ox, oy) => <g key={ox + ':' + oy}><rect x={ox} y={oy} width="7" height="7" /><rect x={ox + 1} y={oy + 1} width="5" height="5" fill="#fff" /><rect x={ox + 2} y={oy + 2} width="3" height="3" /></g>;
  return (
    <svg viewBox={'0 0 ' + N + ' ' + N} width="100%" height="100%" shapeRendering="crispEdges" fill="#141614">
      {cells}{eye(0, 0)}{eye(N - 7, 0)}{eye(0, N - 7)}
    </svg>);
}

function WaitingPositionCard({ p }) {
  const T = W_T;
  const [showCode, setShowCode] = React.useState(false);
  return (
    <>
    <WCard pad={0} style={{ overflow: 'hidden', marginBottom: 13 }}>
      <div style={{ padding: '14px 15px 14px' }}>
        <PosMetaRow p={p} onCard={() => setShowCode(true)} />
        <PosStatus icon="clock" text="正在等待照片同步" />
      </div>
    </WCard>
    {showCode && <CardCodeDialog p={p} onClose={() => setShowCode(false)} />}
    </>);

}

function ReadyPositionCard({ p }) {
  const T = W_T;
  const [showCode, setShowCode] = React.useState(false);
  return (
    <>
    <WCard pad={0} style={{ overflow: 'hidden', marginBottom: 13 }}>
      <div style={{ padding: '14px 15px 14px' }}>
        <PosMetaRow p={p} onCard={() => setShowCode(true)} />
        <PosStatus icon="check" text="照片已就绪，请前往取片点扫码自助领取" color="#2FA84F" />
      </div>
    </WCard>
    {showCode && <CardCodeDialog p={p} onClose={() => setShowCode(false)} />}
    </>);

}

function TimeoutPositionCard({ p, nav, claimed = [], onClaim, onView, hidden }) {
  const T = W_T;
  const [card, setCard] = React.useState(false);
  const hd = hidden || new Set();
  claimed = claimed.filter((seed) => !hd.has(p.no + '|' + seed));
  const has = claimed.length > 0;
  return (
    <>
    <WCard pad={0} style={{ overflow: 'hidden', marginBottom: 13 }}>
      <div style={{ padding: '14px 15px 15px' }}>
        <PosMetaRow p={p} onCard={() => setCard(true)} />
        {has ?
        <>
          <div style={POS_ROW2}>已认领 {claimed.length} 张照片</div>
          <div className="lv-hscroll" style={{ display: 'flex', gap: 8, overflowX: 'auto' }}>
            {claimed.map((seed, i) =>
            <div key={i} style={{ width: 92, flexShrink: 0 }}>
              <WPhoto seed={seed} ratio="1 / 1" radius={6} onClick={() => onView && onView({ seed, posNo: p.no, spot: p.no + ' 号机位 · ' + p.name, idx: i + 1, count: claimed.length })} />
            </div>)}
          </div>
        </> :
        <PosStatus icon="warn" text="同步照片超时" color="#D9791B" />}
        <button onClick={onClaim} style={{ display: 'flex', alignItems: 'center', gap: 6, width: '100%', textAlign: 'left', background: 'none', border: 'none', cursor: 'pointer', padding: 0, marginTop: 12 }}>
          <WIcon name="user" size={14} color={T.muted} stroke={1.9} />
          <span style={{ fontFamily: T.sans, fontSize: 12.5, fontWeight: 500, color: T.muted }}>{has ? '继续通过人脸识别找回照片' : '通过人脸识别找回照片'}</span>
          <WIcon name="chevR" size={15} color={T.faint} stroke={2} style={{ marginLeft: 'auto' }} />
        </button>
      </div>
    </WCard>
    {card && <CardCodeDialog p={p} onClose={() => setCard(false)} />}
    </>);

}

// 景区切换（仅影集页：分景区浏览已拍摄的底片）
function ScenicSwitch({ value, onChange }) {
  const T = W_T;
  const list = window.LV_DATA.SCENICS.filter((s) => s.bound);
  return (
    <div className="lv-hscroll" style={{ display: 'flex', gap: 8, overflowX: 'auto', margin: '0 -18px 14px', padding: '2px 18px' }}>
      {list.map((s) => {
        const on = s.id === value;
        return (
          <button key={s.id} onClick={() => onChange(s.id)} style={{ flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 7, border: 'none', cursor: 'pointer',
            fontFamily: T.sans, fontSize: 13.5, fontWeight: on ? 700 : 500, color: on ? '#fff' : T.inkSoft,
            background: on ? T.ink : T.card, boxShadow: on ? 'none' : 'inset 0 0 0 1px ' + T.line, borderRadius: 5, padding: '9px 14px', transition: 'all .15s' }}>
            {s.short}
            <span style={{ fontFamily: T.sans, fontSize: 11.5, fontWeight: 500, color: on ? 'rgba(255,255,255,0.6)' : T.faint }}>{s.photos} 张</span>
          </button>);
      })}
    </div>);
}

function PositionList({ nav, onView, onBind, focusPos, claimed = {}, onClaim, hidden }) {
  const T = W_T;
  const SC = window.LV_DATA.SCENICS;
  const cur = SC.find((x) => x.current) || SC[0];
  const [sc, setSc] = React.useState(cur.id);
  const scenic = SC.find((x) => x.id === sc) || cur;
  const list = POSITIONS.filter((p) => p.sc === sc);
  const [readPos, setReadPos] = React.useState(() => { try { return new Set(JSON.parse(localStorage.getItem('lv_read_positions') || '[]')); } catch (e) { return new Set(); } });
  const markPosRead = (no) => setReadPos((prev) => { if (prev.has(no)) return prev; const n = new Set(prev); n.add(no); try { localStorage.setItem('lv_read_positions', JSON.stringify([...n])); } catch (e) {} return n; });
  const viewAndMark = (info) => { if (info && info.posNo != null) markPosRead(info.posNo); onView(info); };
  const [ring, setRing] = React.useState(focusPos || null);
  React.useEffect(() => {
    setRing(focusPos || null);
    if (focusPos) {const t = setTimeout(() => setRing(null), 2200);return () => clearTimeout(t);}
  }, [focusPos]);
  return (
    <div style={{ padding: '0 18px' }}>
      <GiftSectionHead title="高清底片" />
      <ScenicSwitch value={sc} onChange={setSc} />
      {list.map((p) =>
      <div key={p.sc + p.no} id={'wpos-' + p.no} style={{ borderRadius: 8, transition: 'box-shadow .35s', boxShadow: ring === p.no ? '0 0 0 2.5px ' + GOLD.solid : 'none' }}>
        {p.waiting ?
        <WaitingPositionCard p={p} /> :
        p.ready ?
        <ReadyPositionCard p={p} /> :
        p.timeout ?
        <TimeoutPositionCard p={p} nav={nav} claimed={claimed[p.no] || []} onClaim={() => onClaim && onClaim(p.no)} onView={viewAndMark} hidden={hidden} /> :
        p.unbound ?
        <UnboundPositionCard p={p} nav={nav} /> :
        <PositionCard p={p} nav={nav} onView={viewAndMark} hidden={hidden} unread={!readPos.has(p.no)} />}
      </div>)}
    </div>);

}

// ════════════════════════════════════════════════════════════════
// 确认创意摄影套餐 —— 底部弹窗
// ════════════════════════════════════════════════════════════════
function ChuangyiBuySheet({ open, onClose, store, onPaid }) {
  const T = W_T;
  const P = window.LV_DATA.PRICING;
  const themes = window.LV_DATA.THEMES;
  const [sel, setSel] = React.useState(1);
  const [customQty, setCustomQty] = React.useState(4);

  React.useEffect(() => {if (open) {setSel(1);setCustomQty(4);}}, [open]);
  if (!open) return null;

  const isCustom = sel === 'custom';
  const tier = isCustom ? { sets: customQty, price: customQty * P.sets[0].price, name: '自定义' } : (P.sets.find((t) => t.sets === sel) || P.sets[0]);
  const theme = themes[0];
  const SC = window.LV_DATA.SCENICS;
  const curScenic = SC.find((x) => x.current) || SC[0];
  const posCount = POSITIONS.filter((p) => p.sc === curScenic.id).length;
  const ownedOriginals = Object.values((store && store.photos) || {}).filter((s) => s === 'bought' || s === 'free').length;
  const origPrice = P.sets[0].price * tier.sets;
  const zhe = isCustom ? 7.5 : (tier.price < origPrice ? Math.round(tier.price / origPrice * 100) / 10 : null);
  const showPrice = isCustom ? Math.round(origPrice * 0.75) : tier.price;

  const pay = () => {
    onClose();
    setTimeout(() => onPaid({ name: '创意摄影 · ' + tier.sets + ' 套', sub: tier.sets * 6 + ' 张精选成片 · 含本次全部底片', amount: showPrice, tone: theme.tone, sets: tier.sets }), 180);
  };

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 200, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(20,22,20,0.42)', animation: 'lvFade .2s ease' }} />
      <div style={{ position: 'relative', background: T.paper, borderRadius: '12px 12px 0 0', maxHeight: '90%', display: 'flex', flexDirection: 'column',
        animation: 'lvSlideUp .26s cubic-bezier(.2,.8,.2,1)', boxShadow: '0 -10px 40px rgba(0,0,0,0.2)' }}>

        {/* 固定头部 */}
        <div style={{ padding: '18px 18px 12px', flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: T.sans, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: 0.2 }}>购买创意摄影套餐</div>
              <div style={{ fontFamily: T.sans, fontSize: 12.5, color: T.muted, marginTop: 6 }}>请选择数量</div>
            </div>
            <button onClick={onClose} style={{ background: T.field, border: 'none', borderRadius: 999, width: 30, height: 30, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', marginTop: 1, flexShrink: 0 }}>
              <WIcon name="close" size={16} color={T.muted} stroke={2} />
            </button>
          </div>
        </div>

        {/* 滚动主体 · 套餐卡片 */}
        <div className="lv-scroll" style={{ overflowY: 'auto', padding: '4px 18px 10px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          {P.sets.map((t) => {
            const on = sel === t.sets;
            const badge = t.sets === 1 ? { text: '限时特惠', bg: '#ED9A2E', color: '#fff' } :
              t.sets === 2 ? { text: '近期热卖', bg: '#FCEACB', color: '#9A6B1E' } :
              { text: '多人出游最佳', bg: '#FCEACB', color: '#9A6B1E' };
            const discount = t.sets === 2 ? '8.8折' : t.sets === 3 ? '8折' : null;
            return (
              <button key={t.sets} onClick={() => setSel(t.sets)} style={{ position: 'relative', width: '100%', textAlign: 'left',
                background: T.card, border: 'none', cursor: 'pointer', borderRadius: 8, padding: '16px 18px', overflow: 'hidden',
                boxShadow: on ? 'inset 0 0 0 2px ' + GOLD.solid : 'inset 0 0 0 1px ' + T.line, transition: 'box-shadow .15s' }}>
                <span style={{ position: 'absolute', top: 0, right: 16, background: badge.bg, color: badge.color, whiteSpace: 'nowrap', fontFamily: T.sans, fontSize: 10.5, fontWeight: 700, padding: '3px 8px', borderRadius: '0 0 4px 4px', letterSpacing: 0.3 }}>{badge.text}</span>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
                  <div>
                    <span style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 700, color: T.ink, whiteSpace: 'nowrap' }}>{t.sets} 套（{t.sets * 6} 张）</span>
                    <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 5 }}>{t.sets}套共{t.sets * 6}张创意摄影 + 赠全部高清底片</div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}><WPrice value={t.price} size={28} /></div>
                </div>
              </button>);
          })}

          <div style={{ position: 'relative', width: '100%', background: T.card, border: 'none', borderRadius: 8, padding: '18px 18px 16px',
            boxShadow: isCustom ? 'inset 0 0 0 2px ' + GOLD.solid : 'inset 0 0 0 1px ' + T.line, transition: 'box-shadow .15s' }}>
            <div onClick={() => setSel('custom')} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, cursor: 'pointer' }}>
              <div style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 700, color: T.ink }}>自定义</div>
              {isCustom ?
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }} onClick={(e) => e.stopPropagation()}>
                <button onClick={() => setCustomQty((q) => Math.max(4, q - 1))} style={{ width: 26, height: 26, borderRadius: 6, border: 'none', background: T.field, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <div style={{ width: 10, height: 2, background: T.ink, borderRadius: 2 }} />
                </button>
                <span style={{ fontFamily: T.sans, fontSize: 14, fontWeight: 700, color: T.ink, minWidth: 14, textAlign: 'center' }}>{customQty}</span>
                <button onClick={() => setCustomQty((q) => q + 1)} style={{ width: 26, height: 26, borderRadius: 6, border: 'none', background: GOLD.tint, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <WIcon name="plus" size={13} color={GOLD.deep} stroke={2.4} />
                </button>
                <WPrice value={customQty * P.sets[0].price} size={22} />
              </div> :
              null}
            </div>
            {isCustom && <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 5 }}>{customQty}套共{customQty * 6}张+赠全部高清底片</div>}
          </div>
        </div>

        {/* 固定底部 */}
        <div style={{ flexShrink: 0, borderTop: '1px solid ' + T.line, background: T.paper, padding: '13px 18px calc(16px + env(safe-area-inset-bottom))', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginLeft: 6 }}>
            <div style={{ marginTop: -3 }}><WPrice value={showPrice} size={22} /></div>
            {zhe != null && <div style={{ fontFamily: T.sans, fontSize: 11.5, display: 'flex', flexDirection: 'column', gap: 1 }}>
              <span style={{ fontFamily: T.serif, color: T.muted, textDecoration: 'line-through' }}>¥{origPrice}</span>
              <span style={{ color: T.muted }}>（{zhe}折）</span>
            </div>}
          </div>
          <button onClick={pay} style={{ minWidth: 148, fontFamily: T.sans, fontSize: 16, fontWeight: 700, color: GOLD.text, background: GOLD.grad, border: 'none', borderRadius: 8, padding: '15px 22px', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 7, boxShadow: '0 6px 18px rgba(204,156,64,0.4), inset 0 1px 0 rgba(255,255,255,0.5)' }}>立即购买</button>
        </div>
      </div>
    </div>);

}

// ════════════════════════════════════════════════════════════════
// 机位照片·单张放大
// ════════════════════════════════════════════════════════════════
function WorksPhotoViewer({ photo, onClose, onDownload, onDownloadAll, onUpgrade, onRemove, onRemoveAll, unlocked }) {
  const T = W_T;
  const [idx, setIdx] = React.useState(0);
  const [sheetType, setSheetType] = React.useState(null); // 'download' | 'more'
  const [confirmAction, setConfirmAction] = React.useState(null); // 'delete' | 'deleteAll' | 'notmine'
  const [localHidden, setLocalHidden] = React.useState(() => new Set());
  React.useEffect(() => {setIdx(photo && photo.idx ? photo.idx - 1 : 0);setSheetType(null);setConfirmAction(null);setLocalHidden(new Set());}, [photo]);
  const count = (photo && photo.count) || 1;
  const { dx, dragging, handlers } = window.lvUseSwipe({
    enabled: count > 1,
    onPrev: () => setIdx((i) => Math.max(0, i - 1)),
    onNext: () => setIdx((i) => Math.min(count - 1, i + 1)) });
  const allSeeds = Array.from({ length: count }, (_, i) => photo && photo.posNo != null ? window.lvSeed('pos' + photo.posNo + '-' + i) : photo && photo.seed);
  const seeds = allSeeds.filter((sd) => !localHidden.has(sd));
  const idxClamped = Math.min(idx, Math.max(0, seeds.length - 1));
  React.useEffect(() => {if (photo && seeds.length === 0) onClose();}, [seeds.length, photo]);
  if (!photo) return null;
  const seedIdx = (sd) => allSeeds.indexOf(sd);
  const srcFor = (sd) => (photo.posNo != null ? posThumbSrc(photo.posNo, seedIdx(sd)) : null);
  const curSeed = seeds[idxClamped] != null ? seeds[idxClamped] : photo.seed;
  const img = srcFor(curSeed) || photo.src || window.pickPhoto(curSeed);
  const doRemove = () => {
    const posNo = photo.posNo;
    setConfirmAction(null);
    setLocalHidden((prev) => {const n = new Set(prev);n.add(curSeed);return n;});
    onRemove && onRemove({ posNo, seed: curSeed });
  };
  const doRemoveAll = () => {
    const posNo = photo.posNo;
    setConfirmAction(null);
    onRemoveAll && onRemoveAll({ posNo, seeds });
    onClose();
  };
  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 9, background: 'rgba(14,15,13,0.97)', display: 'flex', flexDirection: 'column', animation: 'lvFade .2s ease' }}>
<WNav title="底片详情" onBack={onClose} />
      <div {...handlers} style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '4px 0', position: 'relative', touchAction: 'pan-y', cursor: seeds.length > 1 ? 'grab' : 'default' }}>
        {window.lvNavArrow({ dir: 'prev', show: seeds.length > 1 && idxClamped > 0, onClick: () => setIdx((i) => Math.max(0, i - 1)) })}
        {window.lvNavArrow({ dir: 'next', show: seeds.length > 1 && idxClamped < seeds.length - 1, onClick: () => setIdx((i) => Math.min(seeds.length - 1, i + 1)) })}
        <div style={{ position: 'relative', display: 'block', width: '100%', transform: 'translateX(' + dx + 'px)', transition: dragging ? 'none' : 'transform .22s ease' }}>
          <img src={window.lvAsset(img)} alt="" draggable="false" style={{ display: 'block', width: '100%', height: 'auto', objectFit: 'contain' }} />
          {!unlocked &&
          <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', backgroundImage: 'url(\'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140"><text x="0" y="80" font-size="22" font-family="sans-serif" fill="rgba(255,255,255,0.28)" transform="rotate(-45 70 70)">样片</text></svg>') + '\')', backgroundRepeat: 'repeat', backgroundSize: '140px 140px' }} />}
        </div>
      </div>
      {seeds.length > 1 &&
      <div className="lv-hscroll" style={{ flexShrink: 0, display: 'flex', gap: 8, overflowX: 'auto', padding: '8px 18px 2px', justifyContent: seeds.length > 5 ? 'flex-start' : 'center' }}>
        {seeds.map((sd, i) =>
        <button key={sd} onClick={() => setIdx(i)} style={{ width: 52, height: 52, flexShrink: 0, borderRadius: 5, overflow: 'hidden', border: 'none', padding: 0, cursor: 'pointer', boxShadow: i === idxClamped ? '0 0 0 2px #fff' : '0 0 0 1px rgba(255,255,255,0.22)', opacity: i === idxClamped ? 1 : 0.55, transition: 'opacity .15s' }}>
          <img src={window.lvAsset(srcFor(sd) || window.pickPhoto(sd))} alt="" draggable="false" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
        </button>)}
      </div>}
      <div style={{ flexShrink: 0, padding: '14px 18px calc(22px + env(safe-area-inset-bottom))' }}>
        <div style={{ display: 'flex', gap: 10 }}>
          <button onClick={() => setSheetType('download')}
          style={{ flex: 2.2, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 7, fontFamily: T.sans, fontSize: 14.5, fontWeight: 700, color: '#fff', background: 'rgba(255,255,255,0.16)', border: 'none', boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.28)', borderRadius: 7, padding: '14px 0', cursor: 'pointer' }}>
            <WIcon name="download" size={17} color="#fff" stroke={1.9} />下载高清原图
          </button>
          <button onClick={() => setSheetType('more')}
          style={{ flex: 0.6, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6, fontFamily: T.sans, fontSize: 14.5, fontWeight: 600, color: '#fff', background: 'rgba(255,255,255,0.12)', border: 'none', boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.22)', borderRadius: 7, padding: '14px 0', cursor: 'pointer' }}>
            <WIcon name="moreH" size={17} color="#fff" stroke={0} fill="#fff" />
          </button>
        </div>
      </div>
      {sheetType &&
      <div onClick={() => setSheetType(null)} style={{ position: 'absolute', inset: 0, zIndex: 230, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(20,22,20,0.45)', animation: 'lvFade .18s ease' }} />
        <div onClick={(e) => e.stopPropagation()} style={{ position: 'relative', display: 'flex', flexDirection: 'column', gap: 8, padding: '0 12px calc(12px + env(safe-area-inset-bottom))', animation: 'lvSlideUp .24s cubic-bezier(.2,.8,.2,1)' }}>
          <div style={{ background: '#fff', borderRadius: 10, overflow: 'hidden' }}>
            {sheetType === 'download' ?
            <>
              <button onClick={() => {setSheetType(null);onDownload && onDownload();}}
              style={{ display: 'block', width: '100%', textAlign: 'center', fontFamily: T.sans, fontSize: 16, fontWeight: 500, color: T.ink, background: 'none', border: 'none', padding: '16px 16px', cursor: 'pointer' }}>下载本张</button>
              <div style={{ height: 1, background: T.line }} />
              <button onClick={() => {setSheetType(null);onDownloadAll && onDownloadAll();}}
              style={{ display: 'block', width: '100%', textAlign: 'center', fontFamily: T.sans, fontSize: 16, fontWeight: 500, color: T.ink, background: 'none', border: 'none', padding: '16px 16px', cursor: 'pointer' }}>下载本套全部（{count} 张）</button>
            </> :
            <>
              <button onClick={() => {setSheetType(null);setConfirmAction('delete');}}
              style={{ display: 'block', width: '100%', textAlign: 'center', fontFamily: T.sans, fontSize: 16, fontWeight: 500, color: '#E0463D', background: 'none', border: 'none', padding: '16px 16px', cursor: 'pointer' }}>删除本张</button>
              <div style={{ height: 1, background: T.line }} />
              <button onClick={() => {setSheetType(null);setConfirmAction('notmine');}}
              style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6, width: '100%', textAlign: 'center', fontFamily: T.sans, fontSize: 16, fontWeight: 500, color: T.ink, background: 'none', border: 'none', padding: '16px 16px', cursor: 'pointer' }}>
              <WIcon name="flag" size={16} color={T.ink} stroke={1.9} />这不是我的照片</button>
            </>}
          </div>
          <button onClick={() => setSheetType(null)}
          style={{ background: '#fff', borderRadius: 10, padding: '15px 0', fontFamily: T.sans, fontSize: 16, fontWeight: 600, color: T.ink, border: 'none', cursor: 'pointer' }}>取消</button>
        </div>
      </div>}
      <WChDialog open={confirmAction === 'delete'}
        body={<>确定要删除这张照片吗？<br />删除后将无法恢复。</>}
        okText="删除"
        onCancel={() => setConfirmAction(null)}
        onOk={doRemove} />
      <WChDialog open={confirmAction === 'deleteAll'}
        body={<>确定要删除本套全部 {count} 张照片吗？<br />删除后将无法恢复。</>}
        okText="删除"
        onCancel={() => setConfirmAction(null)}
        onOk={doRemoveAll} />
      <WChDialog open={confirmAction === 'notmine'}
        body={<>确认这不是您的照片吗？<br />我们将从您的照片中移除这一张。</>}
        okText="确定"
        onCancel={() => setConfirmAction(null)}
        onOk={doRemove} />
    </div>);

}

// ── 微信风格·二次确认弹框 ────────────────────────────────────────
function WChDialog({ open, body, cancelText = '取消', okText = '确定', okGold, onCancel, onOk }) {
  const T = W_T;
  if (!open) return null;
  return (
    <div onClick={onCancel} style={{ position: 'absolute', inset: 0, zIndex: 240, background: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 42px', animation: 'lvFade .18s ease' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: '100%', maxWidth: 300, background: '#fff', borderRadius: 8, overflow: 'hidden', boxShadow: '0 24px 56px rgba(0,0,0,0.32)' }}>
        <div style={{ padding: '27px 24px 23px', fontFamily: T.sans, fontSize: 14.5, lineHeight: 1.75, color: T.ink, textAlign: 'center' }}>{body}</div>
        <div style={{ display: 'flex', borderTop: '1px solid ' + T.line }}>
          <button onClick={onCancel} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 500, color: T.muted, background: 'none', border: 'none', borderRight: '1px solid ' + T.line, cursor: 'pointer' }}>{cancelText}</button>
          <button onClick={onOk} style={{ ...{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 600, color: okGold ? GOLD.deep : '#07c160', background: 'none', border: 'none', cursor: 'pointer' }, color: "rgb(0, 0, 0)" }}>{okText}</button>
        </div>
      </div>
    </div>);
}

// ════════════════════════════════════════════════════════════════
function WorksScreen({ store, nav, onBind, pushed = false, focusPos, focusSection, openTheme, openPos }) {
  const T = W_T;
  const [buyOpen, setBuyOpen] = React.useState(false);
  const [pay, setPay] = React.useState(null);
  const [view, setView] = React.useState(null);
  const [downloaded, setDownloaded] = React.useState(() => new Set()); // 已免费下载过底片的机位号
  const [dlConfirm, setDlConfirm] = React.useState(null); // 首次下载二次确认 { posNo }
  const [dlBlock, setDlBlock] = React.useState(false); // 本机位已下载 → 引导升级
  const [dlAllConfirm, setDlAllConfirm] = React.useState(false); // 下载本套全部 → 提示套餐购买
  const [paidOrderId, setPaidOrderId] = React.useState(null);
  const purchased = store.stage === 'bought';
  // 已删除 / 标记为“不是我的照片”的底片，key 为 `机位号|seed`
  const [hiddenPhotos, setHiddenPhotos] = React.useState(() => new Set());
  const removePhoto = ({ posNo, seed }) => {
    setHiddenPhotos((prev) => {const n = new Set(prev);n.add(posNo + '|' + seed);return n;});
  };
  const removeAllPhotos = ({ posNo, seeds }) => {
    setHiddenPhotos((prev) => {const n = new Set(prev);(seeds || []).forEach((sd) => n.add(posNo + '|' + sd));return n;});
  };

  // 人脸识别找回照片：claimed = { 机位号: [seed,...] }
  const [claimed, setClaimed] = React.useState({});
  const [claimSheet, setClaimSheet] = React.useState(null); // 当前认领的机位号
  const [scanning, setScanning] = React.useState(false);
  const doClaim = () => {
    const posNo = claimSheet;
    setClaimSheet(null);
    setScanning(true);
    setTimeout(() => {
      setScanning(false);
      setClaimed((prev) => ({ ...prev, [posNo]: [...(prev[posNo] || []), window.lvSeed('claim-' + posNo + '-' + Date.now())] }));
    }, 1700);
  };

  // 从首页“你的照片到了”进入时，滚动定位到对应机位
  React.useEffect(() => {
    if (!focusPos) return;
    let tries = 0;
    const tick = () => {
      const el = document.getElementById('wpos-' + focusPos);
      const sc = el && el.closest('.lv-scroll');
      if (el && sc) {
        const r = el.getBoundingClientRect();
        const sr = sc.getBoundingClientRect();
        sc.scrollTop += r.top - sr.top - 18;
        return;
      }
      if (tries++ < 30) requestAnimationFrame(tick);
    };
    requestAnimationFrame(tick);
  }, [focusPos]);

  // 从「我的·影像资产」进入时，滚动定位到对应区段（持续校正，直到布局稳定）
  React.useEffect(() => {
    if (!focusSection) return;
    let raf, tries = 0;
    const tick = () => {
      const el = document.getElementById('wsec-' + focusSection);
      const sc = el && el.closest('.lv-scroll');
      if (el && sc) {
        const delta = el.getBoundingClientRect().top - sc.getBoundingClientRect().top - 12;
        if (Math.abs(delta) > 2) sc.scrollTop += delta;
        // 继续校正：上方图片/区段加载后位置会变化
        if (Math.abs(delta) <= 2 && tries > 6) return;
      }
      if (tries++ < 60) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [focusSection]);

  const onPaid = (item) => {
    const sets = item.sets || 3;
    const id = store.addOrder({
      cat: '套餐', name: '创意摄影套餐 · ' + sets + ' 套', tone: 'dusk', amount: item.amount,
      state: '待确认', group: 'going', need: true,
      statusTitle: '等待确认', statusSub: '已交付的订单将在 3 天后自动确认', eta: '已交付的订单将在 3 天后自动确认',
      timeline: [
      { t: '支付', done: true, time: '14:10' },
      { t: '完成定制创意摄影', done: true, time: '14:18' },
      { t: '完成交付高清底片', done: true, time: '今天 18:30' },
      { t: '完成交付个性文创', done: false, time: '今天 18:30' }],
      items: [
      { name: '创意摄影', sub: sets + '/' + sets + ' 套待制作', state: '待定制', accent: true, src: 'assets/p8.jpg' },
      { name: '赠品: 高清底片', sub: '全部机位', state: '已完成', src: 'assets/p1.jpg' },
      { name: '赠品: 个性化文创', sub: '精美相册', state: '制作中', src: 'assets/g-moon.jpg' }]
    });
    store.set({ stage: 'bought' });
    setPaidOrderId(id);
    setPay({ name: item.name, sub: item.sub, amount: item.amount, tone: item.tone });
  };

  // 从「订单详情」进入时，直接打开指定机位的底片详情
  React.useEffect(() => {
    if (openPos == null) return;
    const p = POSITIONS.find((x) => x.no === openPos);
    if (!p) return;
    const seed = window.lvSeed('pos' + p.no + '-0');
    setView({ seed, src: posThumbSrc(p.no, 0), posNo: p.no, spot: p.no + ' 号机位 · ' + p.name, idx: 1, count: p.count });
  }, [openPos]);

  const unbound = store.stage === 'unbound';

  return (
    <>
    <WScreen
        nav={<WNav title="影集" onBack={pushed ? () => nav.pop() : undefined} />}
        body={<div style={{ paddingBottom: 28 }}>
        <div id="wsec-customized"><window.CustomizedSection store={store} nav={nav} openTheme={openTheme} /></div>

        <div style={{ height: 32 }} />
        <div id="wsec-yuanpian"><PositionList nav={nav} onView={setView} onBind={onBind} focusPos={focusPos} claimed={claimed} onClaim={setClaimSheet} hidden={hiddenPhotos} /></div>
      </div>} />

    <ChuangyiBuySheet open={buyOpen} onClose={() => setBuyOpen(false)} store={store} onPaid={onPaid} />
    <WorksPhotoViewer photo={view} onClose={() => openPos != null ? nav.pop() : setView(null)}
      unlocked={purchased || downloaded.has(view && view.posNo)}
      onRemove={removePhoto}
      onRemoveAll={removeAllPhotos}
      onDownload={() => {
        if (purchased) return;
        const posNo = view && view.posNo;
        if (downloaded.has(posNo)) setDlBlock(true);else
        setDlConfirm({ posNo });
      }}
      onDownloadAll={() => {
        if (purchased) return;
        setDlAllConfirm(true);
      }}
      onUpgrade={() => {setView(null);setBuyOpen(true);}} />
    <WChDialog open={!!dlConfirm}
      body={<>您尚未购买套餐，每个机位仅可<b style={{ color: GOLD.deep }}><span style={{ color: '#000' }}>免费下载 1 张</span></b>高清底片，是否确定选择本张？</>}
      okText="确定"
      onCancel={() => setDlConfirm(null)}
      onOk={() => {
        setDownloaded((prev) => {const n = new Set(prev);n.add(dlConfirm.posNo);return n;});
        setDlConfirm(null);
      }} />
    <WChDialog open={dlAllConfirm}
      body={<>本套高清底片可<b style={{ color: GOLD.deep }}><span style={{ color: '#000' }}>免费下载 1 张</span></b>，其余需购买套餐后解锁下载。</>}
      okText="立即购买" okGold
      onCancel={() => setDlAllConfirm(false)}
      onOk={() => {setDlAllConfirm(false);setView(null);setBuyOpen(true);}} />
    <WChDialog open={dlBlock}
      body={<>本机位已下载过 1 张高清底片。<br />升级套餐可解锁创意摄影，且所有机位高清底片<b style={{ color: GOLD.deep }}>免费赠送</b>。</>}
      okText="立即升级" okGold
      onCancel={() => setDlBlock(false)}
      onOk={() => {setDlBlock(false);setView(null);setBuyOpen(true);}} />
    {window.PaymentSheet && <window.PaymentSheet open={!!pay} item={pay} onClose={() => setPay(null)} onPaid={() => {setPay(null);if (paidOrderId) {nav.push('orderDetail', { orderId: paidOrderId });setPaidOrderId(null);} else nav.reset('orders');}} />}

    {/* 人脸识别找回照片 · 选择来源 */}
    <window.Sheet open={claimSheet != null} onClose={() => setClaimSheet(null)} title="通过人脸识别找回照片" maxH="62%">
      <div style={{ fontFamily: T.sans, fontSize: 12.5, color: T.muted, lineHeight: 1.65, marginBottom: 16 }}>
        请提供一张带有清晰人脸的照片，系统将自动识别并把该机位拍到的照片找回到此处。可重复操作多次。
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {[['camera', '拍照', '用相机拍摄一张正脸照片'], ['image', '从相册选择', '选择带有清晰人脸的照片']].map(([icon, title, sub]) =>
        <button key={icon} onClick={doClaim} style={{ display: 'flex', alignItems: 'center', gap: 13, width: '100%', textAlign: 'left', background: T.field, border: 'none', borderRadius: 8, padding: '14px 15px', cursor: 'pointer', boxShadow: 'inset 0 0 0 1px ' + T.line }}>
          <div style={{ width: 40, height: 40, flexShrink: 0, borderRadius: 6, background: T.card, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: 'inset 0 0 0 1px ' + T.line }}>
            <WIcon name={icon} size={20} color={T.ink} stroke={1.7} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: T.ink }}>{title}</div>
            <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 2 }}>{sub}</div>
          </div>
          <WIcon name="chevR" size={16} color={T.faint} stroke={2} />
        </button>)}
      </div>
    </window.Sheet>

    {/* 人脸识别处理中 */}
    {scanning &&
      <div style={{ position: 'absolute', inset: 0, zIndex: 260, background: 'rgba(12,13,11,0.74)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 15, animation: 'lvFade .2s ease' }}>
        <div style={{ width: 44, height: 44, border: '3px solid rgba(255,255,255,0.25)', borderTopColor: '#fff', borderRadius: 999, animation: 'lvSpin .8s linear infinite' }} />
        <div style={{ fontFamily: T.sans, fontSize: 14.5, color: '#fff', fontWeight: 600 }}>人脸识别中…</div>
        <div style={{ fontFamily: T.sans, fontSize: 12, color: 'rgba(255,255,255,0.62)' }}>正在为你匹配该机位拍到的照片</div>
      </div>}
    </>);

}


Object.assign(window, { WorksScreen, ChuangyiBuySheet, ChuangyiHero, GiftCard, WChDialog, LV_GIFTS: GIFTS, LV_GOLD: GOLD });