// lvpai-customize.jsx — 作品页·创意摄影定制流程
// 1) 我的创意摄影（选择主题 → 开始定制）  2) 已定制 区段
// 3) 开始定制页（选主题/数量 + 确定人像）  4) 人像管理详情页
const { LV_T: CT, Icon: CIcon, Photo: CPhoto, Btn: CBtn, Card: CCard,
  WeChatNav: CNav, Screen: CScreen } = window;
const CGOLD = window.LV_GOLD;
const CGREEN = '#3DA56E'; // 浅绿 · 随套餐赠送

// 可定制总套数（演示：已购套餐内的额度）
const TOTAL_SETS = 3;

// 人像 / 合影（演示：人脸识别已自动归集机位照片）
const C_PORTRAITS = [
{ id: 'r1', name: '人像1', front: ['assets/p1.jpg', 'assets/p6.jpg', 'assets/p2.jpg'], side: ['assets/p4.jpg', 'assets/p9.jpg'], back: ['assets/p8.jpg'] },
{ id: 'r2', name: '人像2', front: ['assets/p3.jpg', 'assets/p5.jpg'], side: ['assets/p10.jpg'], back: [] },
{ id: 'g1', name: '多人合影1', group: true, front: ['assets/p7.jpg', 'assets/p2.jpg'], side: ['assets/p10.jpg'], back: [] }];


// 主题名 → 实拍图（用于「已定制」卡片缩略）
function themeSrc(name) {
  const th = (window.LV_DATA.THEMES || []).find((t) => name && name.indexOf(t.name) > -1);
  return th ? th.src : null;
}
const CTHEME_COVER_OVERRIDES = { '蓝月秘境': 'assets/theme-lanyue-cover.jpg', '雪山旅人': 'assets/theme-xueshan-cover.jpg' };
function themeCover(name) {
  for (const k in CTHEME_COVER_OVERRIDES) { if (name && name.indexOf(k) > -1) return CTHEME_COVER_OVERRIDES[k]; }
  return themeSrc(name);
}
const CTHEME_PHOTO_OVERRIDES = { '东巴神话': ['assets/theme-dongba-0.jpg', 'assets/theme-dongba-1.jpg', 'assets/theme-dongba-2.jpg', 'assets/theme-dongba-3.jpg', 'assets/theme-dongba-4.jpg', 'assets/theme-dongba-5.jpg'],
  '古城入梦': ['assets/theme-gucheng-0.jpg', 'assets/theme-gucheng-1.jpg', 'assets/theme-gucheng-2.jpg', 'assets/theme-gucheng-3.jpg', 'assets/theme-gucheng-4.jpg', 'assets/theme-gucheng-5.jpg'],
  '茶马风云': ['assets/theme-chama-0.jpg', 'assets/theme-chama-1.jpg', 'assets/theme-chama-2.jpg', 'assets/theme-chama-3.jpg', 'assets/theme-chama-4.jpg', 'assets/theme-chama-5.jpg'] };
function themePhotos(name) {
  for (const k in CTHEME_PHOTO_OVERRIDES) { if (name && name.indexOf(k) > -1) return CTHEME_PHOTO_OVERRIDES[k]; }
  return null;
}

// 订单 → 主题名（先按订单名匹配主题，否则按色调归属）
function themeName(o) {
  const THEMES = window.LV_DATA.THEMES || [];
  const byName = THEMES.find((t) => o && o.name && o.name.indexOf(t.name) > -1);
  if (byName) return byName.name;
  const byTone = THEMES.find((t) => t.tone === (o && o.tone));
  return byTone ? byTone.name : o && o.name || '创意摄影';
}

// ════════════════════════════════════════════════════════════════
// 1) 我的创意摄影 —— 作品页上半部分（选择主题 → 开始定制）
// ════════════════════════════════════════════════════════════════
function MyChuangyiSection({ nav, paid }) {
  const T = CT;
  const themes = window.LV_DATA.THEMES;
  const [sel, setSel] = React.useState(() => new Set([themes[0].id]));
  const toggle = (id) => setSel((prev) => {
    const n = new Set(prev);
    n.has(id) ? n.delete(id) : n.add(id);
    return n;
  });

  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: 170, background: 'radial-gradient(125% 100% at 50% -10%, rgba(232,200,122,0.16), transparent 62%)', pointerEvents: 'none' }} />

      <div style={{ position: 'relative', padding: '22px 18px 20px' }}>
        <div style={{ fontFamily: T.sans, color: '#fff', fontWeight: 700, fontSize: 22, letterSpacing: 0.4 }}>我的创意摄影</div>
        <div style={{ fontFamily: T.sans, fontSize: 12.5, color: 'rgba(255,255,255,0.62)', letterSpacing: 0.3, marginTop: 6 }}>玉龙雪山限定系列主题 · 选择主题开始定制</div>

        <CBtn full onClick={() => nav.push('customize', { sel: [...sel] })} disabled={sel.size === 0}
        style={paid ?
        { marginTop: 18, fontSize: 16, fontWeight: 600, color: T.ink, background: '#fff', padding: '15px 0', borderRadius: 8, boxShadow: 'inset 0 0 0 1.5px ' + T.ink } :
        { marginTop: 18, fontSize: 16.5, fontWeight: 700, color: CGOLD.text, background: CGOLD.grad, padding: '15px 0', borderRadius: 8, boxShadow: '0 8px 22px rgba(204,156,64,0.42), inset 0 1px 0 rgba(255,255,255,0.5)' }}>
          开始定制
        </CBtn>
      </div>
    </div>);
}

// ════════════════════════════════════════════════════════════════
// 2) 已定制 —— 正在定制中 / 已完成的创意摄影
// ════════════════════════════════════════════════════════════════
// 已定制·全屏大图查看（下载本张 / 下载本套）
function CustomizedViewer({ work, onClose, onRemoveAll }) {
  const T = CT;
  const [idx, setIdx] = React.useState(0);
  const [toast, setToast] = React.useState('');
  const [sheetType, setSheetType] = React.useState(null); // 'download' | 'more'
  const [confirmAction, setConfirmAction] = React.useState(null); // 'delete' | 'deleteAll'
  const [localHidden, setLocalHidden] = React.useState(() => new Set());
  const [aiOpen, setAiOpen] = React.useState(false);
  const [aiDone, setAiDone] = React.useState(false);
  const [ver, setVer] = React.useState(null); // 'marked' | 'plain'
  const [ack, setAck] = React.useState(false);
  const [howTo, setHowTo] = React.useState(false);
  const [remember, setRemember] = React.useState(false);
  const canDownload = ver === 'marked' || (ver === 'plain' && ack);
  React.useEffect(() => {if (work) {setIdx(0);setSheetType(null);setConfirmAction(null);setLocalHidden(new Set());}}, [work]);
  const PN = 6;
  const { dx, dragging, handlers } = window.lvUseSwipe({
    onPrev: () => setIdx((i) => Math.max(0, i - 1)),
    onNext: () => setIdx((i) => Math.min(PN - 1, i + 1)) });
  const baseSrc = work ? themeCover(work.name) : null;
  const override = work ? themePhotos(work.name) : null;
  const allPhotos = work ? (override || Array.from({ length: 6 }).map((_, i) => i === 0 && baseSrc ? baseSrc : window.pickPhoto(window.lvSeed(work.id + '-' + i)))) : [];
  const photos = allPhotos.filter((_, i) => !localHidden.has(i));
  const idxClamped = Math.min(idx, Math.max(0, photos.length - 1));
  React.useEffect(() => {if (work && photos.length === 0) onClose();}, [photos.length, work]);
  if (!work) return null;
  const done = work.state === '已完成';
  const fire = (msg) => {setToast(msg);setTimeout(() => setToast(''), 1400);};
  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 9, background: 'rgba(12,13,11,0.98)', display: 'flex', flexDirection: 'column', animation: 'lvFade .2s ease' }}>
<CNav 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: 'grab' }}>
        {window.lvNavArrow({ dir: 'prev', show: idxClamped > 0, onClick: () => setIdx((i) => Math.max(0, i - 1)) })}
        {window.lvNavArrow({ dir: 'next', show: idxClamped < photos.length - 1, onClick: () => setIdx((i) => Math.min(photos.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(photos[idxClamped])} alt="" draggable="false" style={{ display: 'block', width: '100%', height: 'auto', objectFit: 'contain' }} />
        </div>
        <div style={{ position: 'absolute', left: 10, bottom: 10, pointerEvents: 'none' }}>
          <span style={{ fontFamily: T.sans, fontSize: 10, fontWeight: 600, color: 'rgba(255,255,255,0.78)', letterSpacing: 0.3, background: 'rgba(255,255,255,0.16)', padding: '3px 7px', borderRadius: 4 }}>AI 创作</span>
        </div>
      </div>
      {aiOpen &&
      <div onClick={() => setAiOpen(false)} style={{ position: 'absolute', inset: 0, zIndex: 320, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 26px', animation: 'lvFade .18s ease' }}>
        <div onClick={(e) => e.stopPropagation()} style={{ width: '100%', maxWidth: 320, maxHeight: '86%', overflowY: 'auto', background: '#fff', borderRadius: 8, boxShadow: '0 24px 56px rgba(0,0,0,0.32)' }}>
          <div style={{ padding: '22px 20px 4px' }}>
            <div style={{ fontFamily: T.sans, fontSize: 16.5, fontWeight: 700, color: T.ink }}>下载高清原图</div>
            <div style={{ fontFamily: T.sans, fontSize: 13, color: T.inkSoft, lineHeight: 1.7, marginTop: 10 }}>本作品为 AI 生成内容，请选择下载版本。</div>
          </div>
          <div style={{ padding: '14px 20px 0', display: 'flex', flexDirection: 'column', gap: 8 }}>
            {[{ k: 'plain', t: '原图（无显式 AI 标识）', d: '图片中不显示「AI 生成」字样，但仍保留隐式标识和追溯信息' },
              { k: 'marked', t: '带水印版（有显式 AI 标识）', d: '图片中包含「AI 生成」显式标识' }].map((o) =>
            <React.Fragment key={o.k}>
            <button onClick={() => setVer(o.k)} style={{ display: 'flex', gap: 10, width: '100%', textAlign: 'left', background: ver === o.k ? 'rgba(232,200,122,0.13)' : T.field, border: 'none', borderRadius: 7, padding: '12px 13px', cursor: 'pointer', boxShadow: ver === o.k ? 'inset 0 0 0 1.5px ' + CGOLD.solid : 'inset 0 0 0 1px ' + T.line }}>
              <span style={{ flexShrink: 0, marginTop: 2, width: 15, height: 15, borderRadius: 999, boxShadow: 'inset 0 0 0 1.5px ' + (ver === o.k ? CGOLD.deep : T.faint), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                {ver === o.k && <span style={{ width: 7, height: 7, borderRadius: 999, background: CGOLD.deep }} />}
              </span>
              <span style={{ minWidth: 0 }}>
                <span style={{ display: 'block', fontFamily: T.sans, fontSize: 13.5, fontWeight: 700, color: T.ink }}>{o.t}</span>
                <span style={{ display: 'block', fontFamily: T.sans, fontSize: 11.5, color: T.muted, lineHeight: 1.6, marginTop: 3 }}>{o.d}</span>
              </span>
            </button>
            {o.k === 'plain' && ver === 'plain' &&
            <div style={{ background: T.field, borderRadius: 7, padding: '12px 13px' }}>
              <button onClick={() => setAck((v) => !v)} style={{ display: 'flex', gap: 8, width: '100%', textAlign: 'left', background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}>
                <span style={{ flexShrink: 0, marginTop: 1, width: 15, height: 15, borderRadius: 3, background: ack ? T.ink : '#fff', boxShadow: 'inset 0 0 0 1.4px ' + (ack ? T.ink : T.faint), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {ack && <CIcon name="checkSm" size={11} color="#fff" stroke={3} />}
                </span>
                <span style={{ fontFamily: T.sans, fontSize: 11.5, lineHeight: 1.7, color: T.inkSoft }}>我已知悉该作品属于 AI 生成内容，并承诺在公开发布、传播或上传至网络平台时依法声明 AI 生成属性，不将其用于欺诈、冒充、虚假宣传、伪造事实或其他违法用途。</span>
              </button>
              <button onClick={() => setHowTo(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 2, marginTop: 9, marginLeft: 23, background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontFamily: T.sans, fontSize: 11.5, color: '#576B95' }}>
                查看《无显式 AI 标识版本使用说明》<CIcon name="chevR" size={13} color="#576B95" stroke={2} />
              </button>
              <button onClick={() => setRemember((v) => !v)} style={{ display: 'flex', gap: 8, width: '100%', textAlign: 'left', background: 'none', border: 'none', padding: 0, marginTop: 11, cursor: 'pointer' }}>
                <span style={{ flexShrink: 0, marginTop: 1, width: 15, height: 15, borderRadius: 3, background: remember ? T.ink : '#fff', boxShadow: 'inset 0 0 0 1.4px ' + (remember ? T.ink : T.faint), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {remember && <CIcon name="checkSm" size={11} color="#fff" stroke={3} />}
                </span>
                <span style={{ fontFamily: T.sans, fontSize: 11.5, lineHeight: 1.7, color: T.inkSoft }}>记住我的偏好，下次不再询问</span>
              </button>
            </div>}
            </React.Fragment>)}
          </div>
          <div style={{ display: 'flex', borderTop: '1px solid ' + T.line, marginTop: 18 }}>
            <button onClick={() => setAiOpen(false)} 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' }}>取消</button>
            <button disabled={!canDownload} onClick={() => { setAiOpen(false); if (remember) setAiDone(true); setSheetType('download'); }} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: canDownload ? T.ink : T.faint, background: 'none', border: 'none', cursor: canDownload ? 'pointer' : 'default' }}>确认下载</button>
          </div>
        </div>
      </div>}

      {howTo &&
      <div onClick={() => setHowTo(false)} style={{ position: 'absolute', inset: 0, zIndex: 330, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 30px', animation: 'lvFade .18s ease' }}>
        <div onClick={(e) => e.stopPropagation()} style={{ width: '100%', maxWidth: 310, background: '#fff', borderRadius: 8, boxShadow: '0 24px 56px rgba(0,0,0,0.32)' }}>
          <div style={{ padding: '22px 20px 18px' }}>
            <div style={{ fontFamily: T.sans, fontSize: 15.5, fontWeight: 700, color: T.ink }}>无显式 AI 标识版本使用说明</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 13 }}>
              {['该版本不包含「AI 生成」等显式标识，但仍保留符合要求的隐式标识和追溯信息。',
                '你在公开发布、传播或上传至网络平台时，应依法主动声明其属于 AI 生成内容，并使用相关平台提供的 AI 标识功能。',
                '不得将该版本用于欺诈、冒充他人、虚假宣传、伪造事实等违法用途。'].map((t, i) =>
              <div key={i} style={{ display: 'flex', gap: 7, fontFamily: T.sans, fontSize: 12, color: T.muted, lineHeight: 1.7 }}>
                <span style={{ flexShrink: 0, color: T.faint }}>{i + 1}.</span><span>{t}</span>
              </div>)}
            </div>
          </div>
          <div style={{ borderTop: '1px solid ' + T.line }}>
            <button onClick={() => setHowTo(false)} style={{ width: '100%', padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: T.ink, background: 'none', border: 'none', cursor: 'pointer' }}>知道了</button>
          </div>
        </div>
      </div>}

      {/* 缩略条 */}
      <div className="lv-hscroll" style={{ flexShrink: 0, display: 'flex', gap: 8, overflowX: 'auto', padding: '6px 18px 2px', justifyContent: 'center' }}>
        {photos.map((p, i) =>
        <button key={i} onClick={() => setIdx(i)} style={{ width: 48, height: 48, flexShrink: 0, borderRadius: 5, overflow: 'hidden', border: 'none', padding: 0, cursor: 'pointer', boxShadow: i === idxClamped ? '0 0 0 2px ' + CGOLD.solid : 'inset 0 0 0 1px rgba(255,255,255,0.18)', opacity: i === idxClamped ? 1 : 0.55 }}>
          <img src={window.lvAsset(p)} alt="" draggable="false" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
        </button>)}
      </div>
      <div style={{ flexShrink: 0, padding: '12px 18px calc(20px + env(safe-area-inset-bottom))' }}>
        <div style={{ display: 'flex', gap: 10 }}>
          <button onClick={() => aiDone ? setSheetType('download') : setAiOpen(true)} style={{ flex: 2.2, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 7, fontFamily: T.sans, fontSize: 15.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' }}>
            <CIcon name="download" size={18} 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' }}>
            <CIcon name="moreH" size={17} color="#fff" stroke={0} fill="#fff" />
          </button>
        </div>
      </div>
      {sheetType &&
      <div onClick={() => setSheetType(null)} style={{ position: 'absolute', inset: 0, zIndex: 240, 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);fire('已下载本张');}}
              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);fire('已下载本套 · ' + photos.length + ' 张');}}
              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' }}>下载本套全部（{photos.length} 张）</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>
          <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>}
      <CChDialog open={confirmAction === 'delete'}
        body={<>确定要删除这张照片吗？<br />删除后将无法恢复。</>}
        okText="删除"
        onCancel={() => setConfirmAction(null)}
        onOk={() => {
          const realIdx = allPhotos.indexOf(photos[idxClamped]);
          setLocalHidden((prev) => {const n = new Set(prev);n.add(realIdx);return n;});
          setConfirmAction(null);
        }} />
      <CChDialog open={confirmAction === 'deleteAll'}
        body={<>确定要删除本套全部 {photos.length} 张照片吗？<br />删除后将无法恢复。</>}
        okText="删除"
        onCancel={() => setConfirmAction(null)}
        onOk={() => {setConfirmAction(null);onRemoveAll ? onRemoveAll(work.id) : onClose();}} />
      {toast &&
      <div style={{ position: 'absolute', left: '50%', bottom: 'calc(96px + env(safe-area-inset-bottom))', transform: 'translateX(-50%)', display: 'inline-flex', alignItems: 'center', gap: 6, padding: '9px 16px', borderRadius: 4, background: 'rgba(20,22,20,0.82)', backdropFilter: 'blur(6px)', fontFamily: T.sans, fontSize: 12.5, fontWeight: 600, color: '#fff', animation: 'lvFade .18s ease' }}>
        <CIcon name="check" size={14} color={CGOLD.hi} stroke={2.4} />{toast}
      </div>}
    </div>);
}

function CustomizedCard({ item, onOpen, unread }) {
  const T = CT;
  return (
    <div onClick={() => !item.customizing && onOpen(item)} style={{ width: '100%', background: T.card, borderRadius: 8, overflow: 'hidden', cursor: item.customizing ? 'default' : 'pointer', boxShadow: '0 1px 2px rgba(26,25,22,0.04), 0 8px 20px rgba(26,25,22,0.05)' }}>
      <div style={{ position: 'relative' }}>
        <CPhoto src={item.src} tone={item.tone} ratio="1 / 1" />
        {item.customizing &&
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(20,22,20,0.42)' }} />}
        {item.customizing &&
        <span style={{ position: 'absolute', left: 7, top: 7, display: 'inline-flex', alignItems: 'center', gap: 3, padding: '2px 7px', borderRadius: 4, background: 'rgba(214,172,84,0.95)', color: CGOLD.text, fontFamily: T.sans, fontSize: 9.5, fontWeight: 700, letterSpacing: 0.3, boxShadow: '0 2px 6px rgba(0,0,0,0.18)' }}>
          <CIcon name="clock" size={10} color={CGOLD.text} stroke={2.2} />定制中
        </span>}
        {unread &&
        <span style={{ position: 'absolute', right: 7, top: 7, padding: '2px 7px', borderRadius: 4, background: '#2FA84F', color: '#fff', fontFamily: T.sans, fontSize: 9.5, fontWeight: 700, letterSpacing: 0.3, boxShadow: '0 2px 6px rgba(0,0,0,0.24)' }}>新</span>}
      </div>
      <div style={{ padding: '9px 10px 11px' }}>
        <div style={{ fontFamily: T.sans, fontSize: 12.5, fontWeight: 700, color: T.ink, lineHeight: 1.3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.name}</div>
        <div style={{ fontFamily: T.sans, fontSize: 10, color: T.muted, marginTop: 3, lineHeight: 1.4, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.created}</div>
      </div>
    </div>);
}

function CustomizedSection({ store, nav, openTheme }) {
  const T = CT;
  const [view, setView] = React.useState(null);
  const [removedIds, setRemovedIds] = React.useState(() => new Set());
  const [readIds, setReadIds] = React.useState(() => { try { return new Set(JSON.parse(localStorage.getItem('lv_read_customized') || '[]')); } catch (e) { return new Set(); } });
  const markRead = (id) => setReadIds((prev) => { if (prev.has(id)) return prev; const n = new Set(prev); n.add(id); try { localStorage.setItem('lv_read_customized', JSON.stringify([...n])); } catch (e) {} return n; });
  const THEMES = window.LV_DATA.THEMES || [];
  const dates = ['2026-7-22 12:26', '2026-7-22 12:26', '2026-7-21 16:40', '2026-7-21 16:40', '2026-7-20 10:12'];
  const items = THEMES.slice(0, 5).map((t, i) => ({
    id: 'cz-' + t.id, name: t.name, tone: t.tone, src: (themePhotos(t.name) && themePhotos(t.name)[0]) || themeCover(t.name),
    created: dates[i] || dates[dates.length - 1], customizing: i < 2,
    state: i < 2 ? '定制中' : '已完成' })).filter((it) => !removedIds.has(it.id));
  React.useEffect(() => {
    if (!openTheme) return;
    const hit = items.find((it) => it.name === openTheme && !it.customizing);
    if (hit) { setView(hit); markRead(hit.id); }
  }, [openTheme]);
  if (items.length === 0) return null;
  return (
    <div>
      <div style={{ padding: '0 18px', marginBottom: 13 }}>
        <div style={{ fontFamily: T.sans, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: 0.2 }}>创意摄影</div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, padding: '0 18px' }}>
        {items.map((it) => <CustomizedCard key={it.id} item={it} unread={!it.customizing && !readIds.has(it.id)} onOpen={(w) => { markRead(w.id); setView(w); }} />)}
      </div>
      <CustomizedViewer work={view} onClose={() => openTheme ? nav.pop() : setView(null)}
        onRemoveAll={(id) => {setRemovedIds((prev) => {const n = new Set(prev);n.add(id);return n;});setView(null);}} />
    </div>);}
// ════════════════════════════════════════════════════════════════
// 3) 开始定制页 —— 选主题/数量 + 确定人像
// ════════════════════════════════════════════════════════════════
function StepDots({ steps = ['选择主题', '确定人像', '提交定制'] }) {
  const T = CT;
  return (
    <div style={{ margin: '4px 18px 0', borderRadius: 8, background: T.ink, padding: '20px 22px 17px' }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        {steps.map((s, i) =>
        <React.Fragment key={s}>
          <div style={{ width: 13, height: 13, borderRadius: 999, background: CGOLD.grad, boxShadow: '0 0 0 4px rgba(214,172,84,0.18)', flexShrink: 0 }} />
          {i < steps.length - 1 && <div style={{ flex: 1, height: 2, background: 'linear-gradient(90deg, ' + CGOLD.solid + ', rgba(214,172,84,0.45))', margin: '0 2px' }} />}
        </React.Fragment>)}
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 11 }}>
        {steps.map((s) =>
        <span key={s} style={{ fontFamily: T.sans, fontSize: 12, fontWeight: 600, color: 'rgba(255,255,255,0.86)' }}>{s}</span>)}
      </div>
    </div>);
}

function ThemeQtyCard({ th, qty, onDec, onInc, canInc }) {
  const T = CT;
  return (
    <div style={{ width: 138, flexShrink: 0 }}>
      <div style={{ borderRadius: 7, overflow: 'hidden', boxShadow: '0 0 0 2px ' + CGOLD.solid }}>
        <CPhoto src={th.src} ratio="3 / 4" label={th.name} />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 9, background: T.card, borderRadius: 6, boxShadow: 'inset 0 0 0 1px ' + T.line, padding: 4 }}>
        <button onClick={onDec} disabled={qty <= 1} style={{ width: 30, height: 30, borderRadius: 5, border: 'none', background: T.field, cursor: qty <= 1 ? 'default' : 'pointer', opacity: qty <= 1 ? 0.4 : 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ width: 12, height: 2, background: T.ink, borderRadius: 2 }} />
        </button>
        <span style={{ fontFamily: T.sans, fontSize: 14, fontWeight: 700, color: T.ink }}>{qty} 套</span>
        <button onClick={onInc} disabled={!canInc} style={{ width: 30, height: 30, borderRadius: 5, border: 'none', background: canInc ? CGOLD.tint : T.field, cursor: canInc ? 'pointer' : 'default', opacity: canInc ? 1 : 0.4, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <CIcon name="plus" size={16} color={canInc ? CGOLD.deep : T.faint} stroke={2.2} />
        </button>
      </div>
    </div>);
}

function PortraitDot({ p, selected, onSelect, onManage }) {
  const T = CT;
  const cover = p.front && p.front[0] || p.side && p.side[0] || null;
  return (
    <div style={{ width: 66, flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
      <button onClick={onSelect} style={{ width: 62, height: 62, borderRadius: 999, border: 'none', padding: 0, cursor: 'pointer', overflow: 'hidden', position: 'relative', background: T.field, boxShadow: selected ? '0 0 0 2.5px ' + CGOLD.solid : 'inset 0 0 0 1px ' + T.line }}>
        {cover ?
        <img src={window.lvAsset(cover)} alt="" draggable="false" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> :
        <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><CIcon name="user" size={26} color={T.faint} stroke={1.7} /></div>}
        {selected &&
        <div style={{ position: 'absolute', right: 1, bottom: 1, width: 18, height: 18, borderRadius: 999, background: CGOLD.solid, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 1px 3px rgba(0,0,0,0.3)' }}>
          <CIcon name="checkSm" size={11} color={CGOLD.text} stroke={2.6} />
        </div>}
      </button>
      <span style={{ fontFamily: T.sans, fontSize: 11.5, fontWeight: 600, color: T.ink, whiteSpace: 'nowrap' }}>{p.name}</span>
      <button onClick={onManage} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontFamily: T.sans, fontSize: 11, color: T.muted }}>管理</button>
    </div>);
}

function CustomizeScreen({ store, nav, sel = [], orderId, mode, scenic, scenicSets }) {
  const T = CT;
  const themes = window.LV_DATA.THEMES;
  const [selSet, setSelSet] = React.useState(() => new Set());
  const [qty, setQty] = React.useState(() => {
    const q = {};sel.forEach((id) => q[id] = 1);return q;
  });
  const [portraits, setPortraits] = React.useState(C_PORTRAITS);
  const [picked, setPicked] = React.useState(() => new Set());
  const [confirmOpen, setConfirmOpen] = React.useState(false);
  const [manageFor, setManageFor] = React.useState(null);
  const [overLimit, setOverLimit] = React.useState(false);
  const [buyOpen, setBuyOpen] = React.useState(false);
  const [addTip, setAddTip] = React.useState(false);
  const noTipAgain = true;
  const addFileRef = React.useRef(null);
  const [tipOff, setTipOff] = React.useState(false);
  const skipTip = () => tipOff;
  const openPicker = () => { if (addFileRef.current) addFileRef.current.click(); };
  const startAdd = () => { if (skipTip()) openPicker(); else setAddTip(true); };
  const confirmTip = () => {
    if (noTipAgain) setTipOff(true);
    setAddTip(false);
    setTimeout(openPicker, 60);
  };
  const onAddFiles = (e) => {
    const files = [...(e.target.files || [])];
    e.target.value = '';
    if (!files.length) return;
    const id = 'new-' + Date.now();
    const urls = files.map((f) => URL.createObjectURL(f));
    const np = { id, name: '人像' + (portraits.filter((p) => !p.group).length + 1), front: urls, side: [], back: [] };
    setPortraits((prev) => [...prev, np]);
    setPicked(new Set([id]));
  };

  // 已确认套数（“修改内容”时视为重新定制，不累加）
  const baseOrder = (Array.isArray(store.orders) ? store.orders : []).find((o) => o.id === orderId) ||
    (Array.isArray(store.orders) ? store.orders : []).find((o) => o.cat === '创意摄影' && ['部分主题待定制', '等待制作', '待定制'].includes(o.state));
  const alreadyConfirmed = mode === 'edit' ? 0 : (baseOrder && baseOrder.confirmedSets ? baseOrder.confirmedSets : 0);

  const TOTAL = scenicSets || TOTAL_SETS;
  const chosen = themes.filter((t) => selSet.has(t.id));
  const used = chosen.reduce((a, t) => a + (qty[t.id] || 1), 0);
  const remaining = TOTAL - alreadyConfirmed - used;

  const setOne = (id, d) => setQty((prev) => ({ ...prev, [id]: Math.max(1, (prev[id] || 1) + d) }));
  const budget = TOTAL - alreadyConfirmed;
  const toggleTheme = (id) => setSelSet((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const togglePick = (id) => setPicked(new Set([id]));

  const doSubmit = () => {
    const confirmed = Math.min(TOTAL, alreadyConfirmed + used);
    const allDone = confirmed >= TOTAL;
    const patch = {
      cat: '创意摄影', name: '创意摄影', sub: (scenic ? scenic + ' · ' : '') + TOTAL + '套', tone: chosen[0] ? chosen[0].tone : 'lake',
      confirmedSets: confirmed, need: !allDone, created: '刚刚', group: 'going',
      progress: { label: '已定制', value: confirmed + '/' + TOTAL + '套' },
      state: allDone ? '正在制作' : '部分主题待定制',
      statusSub: allDone ?
        '内容已全部确认并提交，供应商正在为你制作，订单不可再修改内容' :
        '尚有主题未确认定制内容，请继续定制',
      timeline: allDone ?
        [{ t: '已确认内容', done: true, time: '刚刚' }, { t: '正在制作', done: true, time: '刚刚', cur: true }, { t: '已定制', done: false }, { t: '已完成', done: false }] :
        [{ t: '部分主题待定制', done: true, time: '刚刚', cur: true }, { t: '正在制作', done: false }, { t: '已定制', done: false }, { t: '已完成', done: false }]
    };
    if (baseOrder && store.updateOrder) {
      store.updateOrder(baseOrder.id, { ...patch, action: undefined });
    } else {
      store.addOrder({ amount: (window.LV_DATA.PRICING.sets.find((s) => s.sets === TOTAL) || window.LV_DATA.PRICING.sets[0]).price, ...patch });
    }
    setConfirmOpen(false);
    nav.reset('works');
    nav.push('product', { focusSection: 'customized' });
  };

  // 提交前均二次确认
  const handleSubmit = () => { if (used > budget) { setOverLimit(true); return; } setConfirmOpen(true); };

  const canSubmit = chosen.length > 0 && picked.size > 0;

  return (
    <>
    <CScreen
        nav={<CNav title="定制创意摄影" onBack={() => nav.pop()} />}
        footer={
        <div style={{ flexShrink: 0, background: T.paper, padding: '6px 18px calc(12px + env(safe-area-inset-bottom))' }}>
          <CBtn full kind="dark" disabled={!canSubmit} onClick={handleSubmit} style={{ fontSize: 16, fontWeight: 700, padding: '15px 0', opacity: canSubmit ? 1 : 0.4 }}>提交</CBtn>
          <div style={{ textAlign: 'center', marginTop: 9, fontFamily: T.sans, fontSize: 12.5, color: T.muted }}>
            剩余可定制套数：<span style={{ fontWeight: 700, color: remaining > 0 ? CGOLD.deep : '#C0492E' }}>{Math.max(0, remaining)}</span>
          </div>
        </div>}
        body={<div style={{ paddingBottom: 20 }}>

        {/* Step 1 · 选择主题 / 数量 */}
        <div style={{ padding: '24px 18px 0' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 800, color: T.ink }}>Step 1</span>
            <span style={{ fontFamily: T.sans, fontSize: 15, fontWeight: 600, color: T.inkSoft }}>选择主题</span>
          </div>
          <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 6 }}>每套主题 6 张高清大片</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10, padding: '14px 18px 4px' }}>
          {themes.map((th) => {
              const on = selSet.has(th.id);
              return (
                <div key={th.id}>
                <button onClick={() => toggleTheme(th.id)} style={{ width: '100%', border: 'none', padding: 0, background: 'none', cursor: 'pointer', position: 'relative', borderRadius: 7, overflow: 'hidden', boxShadow: on ? '0 0 0 2px ' + CGOLD.solid : 'inset 0 0 0 1px ' + T.line, display: 'block' }}>
                  <CPhoto src={th.src} ratio="3 / 4" label={th.name} />
                  {on &&
                    <div style={{ position: 'absolute', right: 6, top: 6, width: 20, height: 20, borderRadius: 999, background: CGOLD.solid, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 1px 3px rgba(0,0,0,0.3)' }}>
                    <CIcon name="checkSm" size={12} color={CGOLD.text} stroke={2.6} />
                  </div>}
                </button>
              </div>);

            })}
        </div>

        {/* Step 2 · 确定人像 */}
        <div style={{ padding: '26px 18px 0' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 800, color: T.ink }}>Step 2</span>
            <span style={{ fontFamily: T.sans, fontSize: 15, fontWeight: 600, color: T.inkSoft }}>确定人像</span>
          </div>
          <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.55 }}>你可以使用在景区机位拍摄的人像照片，或自行上传新的照片进行定制</div>
        </div>
        <div className="lv-hscroll" style={{ display: 'flex', gap: 14, overflowX: 'auto', padding: '16px 18px 6px', alignItems: 'flex-start' }}>
          {/* 新增人像 / 合影 */}
          <div style={{ width: 66, flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
            <button onClick={startAdd}
              style={{ width: 62, height: 62, borderRadius: 999, border: '1.5px dashed ' + T.faint, background: T.field, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <CIcon name="plus" size={24} color={T.muted} stroke={2} />
            </button>
            <span style={{ fontFamily: T.sans, fontSize: 11, fontWeight: 500, color: T.muted, textAlign: 'center', lineHeight: 1.3 }}>新增</span>
          </div>
          {portraits.filter((p) => !p.group).map((p) =>
            <PortraitDot key={p.id} p={p} selected={picked.has(p.id)} onSelect={() => togglePick(p.id)}
            onManage={() => setManageFor(p)} />)}
        </div>
      </div>} />

    <input ref={addFileRef} type="file" accept="image/*" multiple onChange={onAddFiles} style={{ display: 'none' }} />

    {/* 管理 · 下拉操作层 */}
    {manageFor &&
      <div onClick={() => setManageFor(null)} style={{ position: 'absolute', inset: 0, zIndex: 250, background: 'rgba(0,0,0,0.42)', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', animation: 'lvFade .18s ease' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: T.paper, borderTopLeftRadius: 12, borderTopRightRadius: 12, padding: '8px 12px calc(10px + env(safe-area-inset-bottom))', animation: 'lvSlideUp .24s cubic-bezier(.2,.8,.2,1)' }}>
        <div style={{ textAlign: 'center', fontFamily: T.sans, fontSize: 12.5, color: T.muted, padding: '10px 0 12px' }}>{manageFor.name}</div>
        <button onClick={() => { const id = manageFor.id; setPortraits((prev) => prev.filter((x) => x.id !== id)); setPicked((prev) => { const n = new Set(prev); n.delete(id); return n; }); setManageFor(null); }}
          style={{ display: 'block', width: '100%', background: T.card, border: 'none', borderRadius: 8, padding: '15px 0', cursor: 'pointer', fontFamily: T.sans, fontSize: 16, fontWeight: 600, color: '#C0492E' }}>删除人像</button>
        <button onClick={() => setManageFor(null)}
          style={{ display: 'block', width: '100%', marginTop: 8, background: T.card, border: 'none', borderRadius: 8, padding: '15px 0', cursor: 'pointer', fontFamily: T.sans, fontSize: 16, fontWeight: 600, color: T.ink }}>取消</button>
      </div>
    </div>}

    {window.ChuangyiBuySheet && <window.ChuangyiBuySheet open={buyOpen} onClose={() => setBuyOpen(false)} store={store} onPaid={() => setBuyOpen(false)} />}

    {/* 超出可定制套数提醒 */}
    {overLimit &&
      <div onClick={() => setOverLimit(false)} style={{ position: 'absolute', inset: 0, zIndex: 250, 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' }}>你当前剩余 {Math.max(0, budget)} 套可定制，已选 {used} 套。<br />如需解锁更多主题，可购买套餐。</div>
        <div style={{ display: 'flex', borderTop: '1px solid ' + T.line }}>
          <button onClick={() => setOverLimit(false)} 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' }}>取消</button>
          <button onClick={() => { setOverLimit(false); setBuyOpen(true); }} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: CGOLD.deep, background: 'none', border: 'none', cursor: 'pointer' }}>去购买</button>
        </div>
      </div>
    </div>}

    {/* 新增人像 · 上传提示 */}
    {addTip &&
      <div onClick={() => setAddTip(false)} style={{ position: 'absolute', inset: 0, zIndex: 250, background: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 34px', animation: 'lvFade .18s ease' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: '100%', maxWidth: 310, background: '#fff', borderRadius: 10, overflow: 'hidden', boxShadow: '0 24px 56px rgba(0,0,0,0.32)' }}>
        <div style={{ padding: '28px 24px 22px' }}>
          <div style={{ fontFamily: T.sans, fontSize: 14, lineHeight: 1.85, color: T.ink, textAlign: 'center' }}>请上传一张或多张人物照片，为达到最佳效果建议正脸、全身、侧身各一张。</div>
        </div>
        <div style={{ display: 'flex', borderTop: '1px solid ' + T.line }}>
          <button onClick={() => setAddTip(false)} 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' }}>取消</button>
          <button onClick={confirmTip} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: T.ink, background: 'none', border: 'none', cursor: 'pointer' }}>下一步</button>
        </div>
      </div>
    </div>}

    {/* 提交前二次确认（确认全部主题后） */}
    {confirmOpen &&
      <div onClick={() => setConfirmOpen(false)} style={{ position: 'absolute', inset: 0, zIndex: 250, 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' }}>开始定制后将不可修改，是否确定提交？</div>
        <div style={{ display: 'flex', borderTop: '1px solid ' + T.line }}>
          <button onClick={() => setConfirmOpen(false)} 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' }}>取消</button>
          <button onClick={doSubmit} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: T.ink, background: 'none', border: 'none', cursor: 'pointer' }}>确定</button>
        </div>
      </div>
    </div>}

    </>);
}

// ════════════════════════════════════════════════════════════════
// 3b) 定制内容详情 —— 只读展示已选主题 + 已确定人像
// ════════════════════════════════════════════════════════════════
function CustomizeDetailScreen({ nav, themeId, portraitId }) {
  const T = CT;
  const themes = window.LV_DATA.THEMES || [];
  const theme = themes.find((t) => t.id === themeId) || themes[0];
  const portrait = (window.LV_PORTRAITS || []).find((p) => p.id === portraitId) || C_PORTRAITS[0];
  const cover = portrait && (portrait.front && portrait.front[0] || portrait.side && portrait.side[0] || portrait.back && portrait.back[0]);
  return (
    <CScreen
      nav={<CNav title="定制内容详情" onBack={() => nav.pop()} />}
      body={<div style={{ padding: '20px 18px 24px' }}>
        <div style={{ fontFamily: T.sans, fontSize: 13.5, fontWeight: 700, color: T.inkSoft, marginBottom: 11 }}>已选主题</div>
        {theme &&
        <div style={{ width: 138, borderRadius: 7, overflow: 'hidden' }}>
          <CPhoto src={theme.src} ratio="3 / 4" label={theme.name} radius={7} />
        </div>}

        <div style={{ fontFamily: T.sans, fontSize: 13.5, fontWeight: 700, color: T.inkSoft, margin: '26px 0 11px' }}>已确定人像</div>
        {portrait &&
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, width: 66 }}>
          <div style={{ width: 62, height: 62, borderRadius: 999, overflow: 'hidden', background: T.field, boxShadow: 'inset 0 0 0 1px ' + T.line }}>
            {cover ?
            <img src={window.lvAsset(cover)} alt="" draggable="false" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> :
            <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><CIcon name="user" size={26} color={T.faint} stroke={1.7} /></div>}
          </div>
          <span style={{ fontFamily: T.sans, fontSize: 11.5, fontWeight: 600, color: T.ink, whiteSpace: 'nowrap' }}>{portrait.name}</span>
        </div>}
      </div>} />);
}

// ════════════════════════════════════════════════════════════════
// 4) 人像管理 —— 正面 / 侧面 / 背面 照片归集
// ════════════════════════════════════════════════════════════════
function AngleBlock({ title, note, photos, onAdd, onRemove }) {
  const T = CT;
  return (
    <div style={{ marginTop: 22 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, minWidth: 0 }}>
          <span style={{ fontFamily: T.sans, fontSize: 14.5, fontWeight: 700, color: T.ink, flexShrink: 0 }}>{title}</span>
          {note && <span style={{ fontFamily: T.sans, fontSize: 11, color: T.faint, lineHeight: 1.3 }}>{note}</span>}
        </div>
        <span style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, flexShrink: 0 }}>{photos.length} 张</span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, marginTop: 11 }}>
        {photos.map((src, i) =>
        <div key={i} style={{ position: 'relative', borderRadius: 7, overflow: 'hidden' }}>
          <CPhoto src={src} ratio="1 / 1" radius={7} />
          <button onClick={() => onRemove(i)} style={{ position: 'absolute', right: 5, top: 5, width: 22, height: 22, borderRadius: 999, border: 'none', background: 'rgba(20,22,20,0.55)', backdropFilter: 'blur(4px)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <CIcon name="close" size={13} color="#fff" stroke={2.2} />
          </button>
        </div>)}
        <button onClick={onAdd} style={{ aspectRatio: '1 / 1', borderRadius: 7, border: '1.5px dashed ' + T.faint, background: T.field, cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 5 }}>
          <CIcon name="camera" size={22} color={T.muted} stroke={1.7} />
          <span style={{ fontFamily: T.sans, fontSize: 11, fontWeight: 500, color: T.muted }}>上传</span>
        </button>
      </div>
    </div>);
}

function PortraitManageScreen({ nav, portrait }) {
  const T = CT;
  const [name, setName] = React.useState(portrait.name);
  const [front, setFront] = React.useState(portrait.front || []);
  const [side, setSide] = React.useState(portrait.side || []);
  const [back, setBack] = React.useState(portrait.back || []);
  const [del, setDel] = React.useState(false);
  const [editingName, setEditingName] = React.useState(false);
  const [saved, setSaved] = React.useState(false);
  const POOL = window.LV_PHOTOS;
  const addTo = (setter) => setter((arr) => [...arr, POOL[Math.floor(Math.random() * POOL.length)]]);
  const rmFrom = (setter) => (i) => setter((arr) => arr.filter((_, k) => k !== i));

  return (
    <>
    <CScreen
        nav={<CNav title="人像管理" onBack={() => nav.pop()} />}
        footer={
        <div style={{ flexShrink: 0, borderTop: '1px solid ' + T.line, background: T.paper, padding: '12px 18px calc(10px + env(safe-area-inset-bottom))' }}>
          <CBtn full kind="dark" onClick={() => {setSaved(true);setTimeout(() => {setSaved(false);nav.pop();}, 850);}} style={{ fontSize: 16, fontWeight: 700, padding: '15px 0' }}>保存</CBtn>
          <button onClick={() => setDel(true)} style={{ display: 'block', margin: '11px auto 0', background: 'none', border: 'none', cursor: 'pointer', fontFamily: T.sans, fontSize: 13.5, color: '#C0492E', fontWeight: 500 }}>删除此人像</button>
        </div>}
        body={<div style={{ padding: '4px 18px 20px' }}>
        {editingName ?
        <input autoFocus value={name} onChange={(e) => setName(e.target.value)}
          onBlur={() => setEditingName(false)}
          onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); setEditingName(false); } }}
          maxLength={12}
          style={{ width: '100%', boxSizing: 'border-box', fontFamily: T.sans, fontSize: 21, fontWeight: 700, color: T.ink, background: T.field, border: 'none', boxShadow: 'inset 0 0 0 1.5px ' + T.ink, borderRadius: 6, padding: '6px 12px', outline: 'none' }} /> :
        <button onClick={() => setEditingName(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'none', border: 'none', padding: 0, cursor: 'pointer', WebkitTapHighlightColor: 'transparent' }}>
          <span style={{ fontFamily: T.sans, fontSize: 21, fontWeight: 700, color: T.ink }}>{name}</span>
          <CIcon name="edit" size={17} color={T.faint} stroke={1.8} />
        </button>}
        <div style={{ fontFamily: T.sans, fontSize: 12.5, color: T.muted, marginTop: 9, lineHeight: 1.65 }}>此人像在机位拍摄的照片将作为基础定制素材。你可继续上传该人物不同角度的照片，以达到最佳定制效果。</div>

        <AngleBlock title="正面" photos={front} onAdd={() => addTo(setFront)} onRemove={rmFrom(setFront)} />
        <AngleBlock title="侧面" note="非必选，建议上传以达到最佳效果" photos={side} onAdd={() => addTo(setSide)} onRemove={rmFrom(setSide)} />
        <AngleBlock title="背面" note="非必选，建议上传以达到最佳效果" photos={back} onAdd={() => addTo(setBack)} onRemove={rmFrom(setBack)} />

        {saved &&
          <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 18, fontFamily: T.sans, fontSize: 12.5, color: '#1F8A5B', fontWeight: 600 }}>
          <CIcon name="check" size={15} color="#1F8A5B" stroke={2.4} />已保存
        </div>}
      </div>} />

    {del &&
      <div onClick={() => setDel(false)} style={{ position: 'absolute', inset: 0, zIndex: 250, 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' }}>删除「{name}」后，该人像归集的照片将不再用于定制，确定删除？</div>
        <div style={{ display: 'flex', borderTop: '1px solid ' + T.line }}>
          <button onClick={() => setDel(false)} 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' }}>取消</button>
          <button onClick={() => {setDel(false);nav.pop();}} style={{ flex: 1, padding: '14px 0', fontFamily: T.sans, fontSize: 15, fontWeight: 600, color: '#C0492E', background: 'none', border: 'none', cursor: 'pointer' }}>删除</button>
        </div>
      </div>
    </div>}
    </>);
}

// ════════════════════════════════════════════════════════════════
// 4b) 人像管理 —— 人像列表
// ════════════════════════════════════════════════════════════════
function PortraitListScreen({ nav }) {
  const T = CT;
  const portraits = C_PORTRAITS;
  return (
    <CScreen
      nav={<CNav title="人像管理" onBack={() => nav.pop()} />}
      body={<div style={{ padding: '8px 18px 24px' }}>
        <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 }}>
          {portraits.map((p) => {
            const cover = p.front && p.front[0] || p.side && p.side[0] || null;
            const total = (p.front || []).length + (p.side || []).length + (p.back || []).length;
            return (
              <button key={p.id} onClick={() => nav.push('portraitManage', { portrait: p })} style={{ display: 'flex', alignItems: 'center', gap: 13, width: '100%', textAlign: 'left', background: T.card, border: 'none', cursor: 'pointer', borderRadius: 8, padding: 12, boxShadow: '0 1px 2px rgba(26,25,22,0.04), 0 8px 22px rgba(26,25,22,0.05)' }}>
                <div style={{ width: 56, height: 56, borderRadius: 999, overflow: 'hidden', flexShrink: 0, background: T.field, boxShadow: 'inset 0 0 0 1px ' + T.line }}>
                  {cover ?
                  <img src={window.lvAsset(cover)} alt="" draggable="false" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> :
                  <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><CIcon name="user" size={24} color={T.faint} stroke={1.7} /></div>}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: T.sans, fontSize: 15, fontWeight: 700, color: T.ink }}>{p.name}{p.group ? ' · 合影' : ''}</div>
                  <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 4 }}>已归集 {total} 张照片</div>
                </div>
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 2, fontFamily: T.sans, fontSize: 12.5, color: T.muted }}>管理<CIcon name="chevR" size={15} color={T.faint} stroke={1.8} /></span>
              </button>);
          })}
        </div>
        <button onClick={() => nav.push('portraitManage', { portrait: { id: 'new-' + Date.now(), name: '新人像', front: [], side: [], back: [], isNew: true } })} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, width: '100%', marginTop: 14, background: 'none', border: '1.5px dashed ' + T.line, borderRadius: 8, padding: '14px 0', cursor: 'pointer', fontFamily: T.sans, fontSize: 14, fontWeight: 600, color: T.inkSoft }}>
          <CIcon name="plus" size={18} color={T.inkSoft} stroke={2} />新增人像 / 合影
        </button>
      </div>} />);

}

// ════════════════════════════════════════════════════════════════
// 5a) 选择照片 —— 从「创意摄影 / 高清底片」中挑选（带筛选栏）
// ════════════════════════════════════════════════════════════════
// 高清底片机位（与作品页机位一致）
const PICK_POS = [
  { no: 1, name: '蓝月谷入口', count: 4 },
  { no: 2, name: '蓝月谷出口', count: 3 },
  { no: 3, name: '云杉坪观景台', count: 4 },
  { no: 4, name: '玉液湖栈道', count: 5 },
  { no: 5, name: '听涛台', count: 4 },
];

function PhotoPickerOverlay({ open, onClose, onConfirm, addedIds }) {
  const T = CT;
  const themes = window.LV_DATA.THEMES || [];
  const [cat, setCat] = React.useState('cy'); // cy = 创意摄影, yp = 高清底片
  const [sub, setSub] = React.useState(0);     // 0 = 全部
  const [sel, setSel] = React.useState(() => new Set());
  const pools = React.useMemo(() => {
    const cy = themes.flatMap((t) => Array.from({ length: 6 }).map((_, i) => ({
      id: 'sel-cy-' + t.id + '-' + i, group: t.id,
      src: i === 0 ? t.src : window.pickPhoto(window.lvSeed('cz-' + t.id + '-' + i)) })));
    const yp = PICK_POS.flatMap((p) => Array.from({ length: p.count }).map((_, i) => ({
      id: 'sel-yp-' + p.no + '-' + i, group: p.no,
      src: window.pickPhoto(window.lvSeed('pos' + p.no + '-' + i)) })));
    return { cy, yp, all: [...cy, ...yp] };
  }, []);
  React.useEffect(() => { if (open) { setSel(new Set(addedIds || [])); setCat('cy'); setSub(0); } }, [open]);
  if (!open) return null;

  const list = (cat === 'cy' ? pools.cy : pools.yp).filter((x) => sub === 0 || x.group === sub);
  const subs = cat === 'cy' ? themes.map((t) => ({ key: t.id, label: t.name })) : PICK_POS.map((p) => ({ key: p.no, label: p.no + ' 号机位' }));
  const toggle = (id) => setSel((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const confirm = () => { onConfirm(pools.all.filter((x) => sel.has(x.id))); onClose(); };

  const tab = (k, label) => (
    <button key={k} onClick={() => { setCat(k); setSub(0); }} style={{ flex: 1, fontFamily: T.sans, fontSize: 14, fontWeight: cat === k ? 700 : 500,
      color: cat === k ? T.ink : T.muted, background: cat === k ? T.card : 'transparent', border: 'none', padding: '10px 0', borderRadius: 6, cursor: 'pointer',
      boxShadow: cat === k ? '0 1px 4px rgba(26,25,22,0.10)' : 'none', transition: 'all .15s' }}>{label}</button>);
  const chip = (active) => ({ flexShrink: 0, cursor: 'pointer', whiteSpace: 'nowrap', border: 'none', fontFamily: T.sans, fontSize: 12.5,
    fontWeight: active ? 700 : 500, color: active ? '#fff' : T.inkSoft, background: active ? T.ink : T.card,
    boxShadow: active ? 'none' : 'inset 0 0 0 1px ' + T.line, borderRadius: 4, padding: '7px 14px' });

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 210, background: T.paper, display: 'flex', flexDirection: 'column', animation: 'lvPush .26s cubic-bezier(.2,.8,.2,1)' }}>
      <CNav title="选择照片" onBack={onClose} />
      {/* 筛选栏：创意摄影 / 高清底片 */}
      <div style={{ flexShrink: 0, padding: '2px 18px 0' }}>
        <div style={{ display: 'flex', background: T.field, borderRadius: 7, padding: 4, boxShadow: 'inset 0 0 0 1px ' + T.line }}>
          {tab('cy', '创意摄影')}{tab('yp', '高清底片')}
        </div>
      </div>
      <div className="lv-hscroll" style={{ flexShrink: 0, display: 'flex', gap: 7, overflowX: 'auto', padding: '12px 18px' }}>
        <button style={chip(sub === 0)} onClick={() => setSub(0)}>全部</button>
        {subs.map((s) => <button key={s.key} style={chip(sub === s.key)} onClick={() => setSub(s.key)}>{s.label}</button>)}
      </div>
      {/* 照片网格 */}
      <div className="lv-scroll" style={{ flex: 1, overflowY: 'auto', padding: '0 18px 16px' }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 8 }}>
          {list.map((p) => {
            const on = sel.has(p.id);
            return (
              <button key={p.id} onClick={() => toggle(p.id)} style={{ position: 'relative', border: 'none', padding: 0, background: 'none', cursor: 'pointer', borderRadius: 6, overflow: 'hidden', boxShadow: on ? '0 0 0 2.5px ' + CGOLD.deep : 'inset 0 0 0 1px ' + T.line, display: 'block' }}>
                <CPhoto src={p.src} ratio="1 / 1" radius={6} />
                {on &&
                  <div style={{ position: 'absolute', inset: 0, background: 'rgba(20,22,20,0.28)' }} />}
                <div style={{ position: 'absolute', right: 6, top: 6, width: 20, height: 20, borderRadius: 999, background: on ? CGOLD.solid : 'rgba(20,22,20,0.35)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: on ? '0 1px 3px rgba(0,0,0,0.3)' : 'inset 0 0 0 1.5px rgba(255,255,255,0.85)' }}>
                  {on && <CIcon name="checkSm" size={12} color={CGOLD.text} stroke={2.6} />}
                </div>
              </button>);
          })}
        </div>
      </div>
      {/* 底部确认 */}
      <div style={{ flexShrink: 0, background: T.paper, borderTop: '1px solid ' + T.line, padding: '12px 18px calc(14px + env(safe-area-inset-bottom))' }}>
        <CBtn full kind="dark" disabled={sel.size === 0} onClick={confirm} style={{ fontSize: 16, fontWeight: 700, padding: '15px 0', opacity: sel.size === 0 ? 0.4 : 1 }}>添加 {sel.size} 张</CBtn>
      </div>
    </div>);
}

// ════════════════════════════════════════════════════════════════
// 5b) 文创定制页 —— 定制的商品（可看详情）+ 选择/上传照片（可拖拽排序）
// ════════════════════════════════════════════════════════════════
function WenchuangCustomizeScreen({ store, nav, gift, orderId, mode }) {
  const T = CT;
  const readOnly = mode === 'view';
  // 查看 / 修改：预填已提交的定制内容（按 orderId/gift 确定，稳定可复现）
  const initialPhotos = () => {
    if (mode !== 'view' && mode !== 'edit') return [];
    const key = orderId || (gift && gift.id) || 'g';
    return Array.from({ length: 4 }).map((_, i) => ({
      id: 'seed-' + key + '-' + i,
      url: window.lvAsset(window.pickPhoto(window.lvSeed(key + '-wc-' + i))),
      kind: 'select' }));
  };
  const [photos, setPhotos] = React.useState(initialPhotos); // { id, url, kind: 'upload' | 'select' }
  const [done, setDone] = React.useState(false);
  const [pickOpen, setPickOpen] = React.useState(false);
  const fileRef = React.useRef(null);
  const dragFrom = React.useRef(null);
  const [dragOver, setDragOver] = React.useState(null);
  const canSubmit = photos.length > 0;

  const onFiles = (e) => {
    const files = [...(e.target.files || [])];
    const added = files.map((f, i) => ({ id: 'u' + Date.now() + '-' + i, url: URL.createObjectURL(f), kind: 'upload' }));
    setPhotos((prev) => [...prev, ...added]);
    e.target.value = '';
  };
  // 合并「选择照片」结果：保留本地上传，同步当前选中的应用内照片
  const addedSelIds = photos.filter((p) => p.kind === 'select').map((p) => p.id);
  const applySelection = (items) => {
    setPhotos((prev) => {
      const keep = new Set(items.map((it) => it.id));
      const kept = prev.filter((p) => p.kind !== 'select' || keep.has(p.id));
      const have = new Set(kept.map((p) => p.id));
      const add = items.filter((it) => !have.has(it.id)).map((it) => ({ id: it.id, url: window.lvAsset(it.src), kind: 'select' }));
      return [...kept, ...add];
    });
  };
  const removePhoto = (id) => setPhotos((prev) => prev.filter((p) => p.id !== id));
  const reorder = (from, to) => setPhotos((prev) => {
    if (from == null || to == null || from === to) return prev;
    const n = [...prev];const [m] = n.splice(from, 1);n.splice(to, 0, m);return n;
  });

  const submit = () => {
    const patch = {
      state: '等待制作', group: 'going', need: false, created: '刚刚', gift,
      statusSub: '内容已提交，等待文创供应商接单，接单前你仍可修改内容',
      eta: '随套餐赠送 · 预计 24–48h 定制',
      timeline: [{ t: '已上传内容', done: true, time: '刚刚' }, { t: '等待制作', done: true, time: '刚刚', cur: true }, { t: '正在制作', done: false }, { t: '等待领取', done: false }, { t: '已完成', done: false }]
    };
    if (orderId && store.updateOrder) {
      // 直接将原有赠品订单置为“等待制作”，不新增记录
      store.updateOrder(orderId, { ...patch, action: undefined });
    } else if (store.updateOrder && Array.isArray(store.orders) && store.orders.find((o) => o.cat === '文创' && (o.state === '待定制' || o.state === '等待制作'))) {
      // 客户下单时已创建过文创赠品订单，直接改其状态（不新增一条）
      const ex = store.orders.find((o) => o.cat === '文创' && (o.state === '待定制' || o.state === '等待制作'));
      store.updateOrder(ex.id, { ...patch, sub: gift.name + '(赠品) x 1', action: undefined });
    } else {
      store.addOrder({ cat: '文创', name: gift.name, tone: 'gold', amount: 0, ...patch });
    }
    setDone(true);
  };

  return (
    <>
    <CScreen
        nav={<CNav title={readOnly ? '查看文创内容' : mode === 'edit' ? '修改文创内容' : '定制我的文创'} onBack={() => nav.pop()} />}
        footer={
        readOnly ?
        <div style={{ flexShrink: 0, background: T.paper, padding: '12px 18px calc(28px + env(safe-area-inset-bottom))' }}>
          <CBtn full kind="dark" onClick={() => orderId ? nav.push('orderDetail', { orderId }) : nav.pop()} style={{ fontSize: 16, fontWeight: 700, padding: '15px 0' }}>查看订单进度</CBtn>
        </div> :
        <div style={{ flexShrink: 0, background: T.paper, padding: '12px 18px calc(28px + env(safe-area-inset-bottom))' }}>
          <CBtn full kind="dark" disabled={!canSubmit} onClick={submit} style={{ fontSize: 16, fontWeight: 700, padding: '15px 0', opacity: canSubmit ? 1 : 0.4 }}>{mode === 'edit' ? '保存修改' : '提交'}</CBtn>
        </div>}
        body={<div style={{ paddingBottom: 20 }}>

        {/* 定制的商品（点击进入商品详情） */}
        <div style={{ padding: '24px 18px 0' }}>
          <button onClick={() => nav.push('wenchuangDetail', { gift })} style={{ display: 'flex', gap: 13, width: '100%', textAlign: 'left', background: T.card, border: 'none', borderRadius: 8, padding: 12, cursor: 'pointer', boxShadow: '0 0 0 2px ' + CGOLD.solid }}>
            <div style={{ width: 96, flexShrink: 0, borderRadius: 7, overflow: 'hidden' }}>
              <CPhoto src={gift.src} ratio="1 / 1" radius={7} />
            </div>
            <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <div style={{ fontFamily: T.sans, fontSize: 15.5, fontWeight: 700, color: T.ink }}>{gift.name}</div>
              {gift.spec && <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 5 }}>{gift.spec}</div>}
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 2, marginTop: 8, fontFamily: T.sans, fontSize: 12, fontWeight: 600, color: CGOLD.deep }}>查看商品详情 <CIcon name="chevR" size={13} color={CGOLD.deep} stroke={2.2} /></span>
            </div>
          </button>
        </div>

        {/* 添加照片 */}
        <div style={{ padding: '26px 18px 0' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 800, color: T.ink }}>{readOnly ? '定制内容' : '添加照片'}</span>
          </div>
          <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.55 }}>{readOnly ? '以下为你已提交的定制内容，制作中的订单不可修改。' : <>从你的雪山影像中选择，或上传本地照片，制作时将按你指定的顺序列印。<span style={{ color: T.faint }}>长按缩略图可拖拽调整顺序。</span></>}</div>
        </div>
        {/* 选择照片 入口 */}
        {!readOnly &&
        <div style={{ padding: '14px 18px 0' }}>
          <button onClick={() => setPickOpen(true)} style={{ display: 'flex', alignItems: 'center', gap: 13, width: '100%', textAlign: 'left', background: T.card, border: 'none', borderRadius: 8, padding: '13px 15px', cursor: 'pointer', boxShadow: 'inset 0 0 0 1px ' + T.line }}>
            <div style={{ width: 40, height: 40, flexShrink: 0, borderRadius: 6, background: T.field, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: 'inset 0 0 0 1px ' + T.line }}>
              <CIcon name="album" 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 }}>选择照片</div>
              <div style={{ fontFamily: T.sans, fontSize: 12, color: T.muted, marginTop: 2 }}>从创意摄影 · 高清底片中挑选</div>
            </div>
            <CIcon name="chevR" size={16} color={T.faint} stroke={2} />
          </button>
        </div>}
        <input ref={fileRef} type="file" accept="image/*" multiple style={{ display: 'none' }} onChange={onFiles} />
        <div style={{ padding: '16px 18px 6px' }}>
          {photos.length > 0 && <div style={{ fontFamily: T.sans, fontSize: 12.5, fontWeight: 600, color: T.inkSoft, margin: '0 2px 11px' }}>{readOnly ? '共 ' + photos.length + ' 张' : '已添加 ' + photos.length + ' 张 · 长按拖拽可排序'}</div>}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
            {photos.map((p, i) =>
              <div key={p.id} draggable={!readOnly}
                onDragStart={() => {if (readOnly) return;dragFrom.current = i;}}
                onDragEnter={() => {if (readOnly) return;setDragOver(i);}}
                onDragOver={(e) => e.preventDefault()}
                onDrop={() => {if (readOnly) return;reorder(dragFrom.current, i);dragFrom.current = null;setDragOver(null);}}
                onDragEnd={() => {if (readOnly) return;dragFrom.current = null;setDragOver(null);}}
                style={{ position: 'relative', aspectRatio: '1 / 1', borderRadius: 7, overflow: 'hidden', cursor: readOnly ? 'default' : 'grab', background: T.field,
                  boxShadow: dragOver === i ? '0 0 0 2.5px ' + CGOLD.deep : 'inset 0 0 0 1px ' + T.line,
                  opacity: dragFrom.current === i ? 0.4 : 1, transition: 'box-shadow .12s, opacity .12s' }}>
                <img src={p.url} draggable={false} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', pointerEvents: 'none' }} />
                <span style={{ position: 'absolute', top: 6, left: 6, minWidth: 18, height: 18, padding: '0 5px', borderRadius: 4, background: 'rgba(0,0,0,0.55)', color: '#fff', fontFamily: T.sans, fontSize: 11, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{i + 1}</span>
                {!readOnly &&
                <button onClick={() => removePhoto(p.id)}
                  style={{ position: 'absolute', top: 5, right: 5, width: 20, height: 20, borderRadius: 999, border: 'none', background: 'rgba(0,0,0,0.55)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0 }}>
                  <CIcon name="close" size={12} color="#fff" stroke={2.4} />
                </button>}
              </div>)}
            {!readOnly &&
            <button onClick={() => fileRef.current && fileRef.current.click()}
              style={{ aspectRatio: '1 / 1', borderRadius: 7, border: '1.5px dashed ' + T.faint, background: T.field, cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
              <CIcon name="plus" size={24} color={T.muted} stroke={2} />
              <span style={{ fontFamily: T.sans, fontSize: 11.5, fontWeight: 500, color: T.muted }}>本地上传</span>
            </button>}
          </div>
        </div>
      </div>} />

    <PhotoPickerOverlay open={pickOpen} onClose={() => setPickOpen(false)} onConfirm={applySelection} addedIds={addedSelIds} />

    {done &&
      <div onClick={() => {setDone(false);nav.reset('works');}} style={{ position: 'absolute', inset: 0, zIndex: 250, 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, padding: '26px 24px 20px', textAlign: 'center', boxShadow: '0 24px 56px rgba(0,0,0,0.32)' }}>
        <div style={{ width: 52, height: 52, borderRadius: 999, margin: '0 auto', background: CGOLD.tint, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <CIcon name="check" size={26} color={CGOLD.deep} stroke={2.4} />
        </div>
        <div style={{ fontFamily: T.sans, fontSize: 17, fontWeight: 700, color: T.ink, marginTop: 14 }}>已提交定制</div>
        <div style={{ fontFamily: T.sans, fontSize: 13, color: T.muted, marginTop: 7, lineHeight: 1.6 }}>「{gift.name}」已进入定制队列，可在订单中查看进度，完成后到自提点领取。</div>
        <div style={{ display: 'flex', gap: 10, marginTop: 18 }}>
          <CBtn kind="soft" onClick={() => {setDone(false);nav.reset('works');}} style={{ flex: 1, padding: '12px 0' }}>返回作品</CBtn>
          <CBtn kind="dark" onClick={() => {setDone(false);nav.reset('orders');}} style={{ flex: 1, padding: '12px 0' }}>查看订单</CBtn>
        </div>
      </div>
    </div>}
    </>);
}

// ── 微信风格·二次确认弹框 ────────────────────────────────────────
function CChDialog({ open, body, cancelText = '取消', okText = '确定', onCancel, onOk }) {
  const T = CT;
  if (!open) return null;
  return (
    <div onClick={onCancel} style={{ position: 'absolute', inset: 0, zIndex: 250, 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: 700, color: '#E0463D', background: 'none', border: 'none', cursor: 'pointer' }}>{okText}</button>
        </div>
      </div>
    </div>);

}

Object.assign(window, { MyChuangyiSection, CustomizedSection, CustomizeScreen, CustomizeDetailScreen, PortraitManageScreen, PortraitListScreen, WenchuangCustomizeScreen, LV_PORTRAITS: C_PORTRAITS });